From c0c6c8aeba5bbfca6ad270dc1e52b5180ebd92fe Mon Sep 17 00:00:00 2001 From: Dash Date: Sat, 17 Oct 2026 10:49:10 +0200 Subject: [PATCH] mechanic: move word lists to data files The word lists (monsters, spells, equipment, races, classes, titles and so on) now live in pqcli/data/*.json instead of pqcli/config.py. A GameData object loaded at startup is passed to the simulation and UIs. Users can replace any list by putting NAME.json in $XDG_CONFIG_HOME/pqcli/data/ or a directory given with --data-dir. --dump-data DIR copies the built-in files out for editing. Malformed files are reported and pqcli exits before starting the UI. Combat task descriptions come from the new combat_verbs list, with {target} standing for the enemy. The built-in list holds only "Executing {target}", and a single verb consumes no random roll, so stock gameplay is unchanged. Recorded traces in tests/fixtures pin this down. Monster, Race and Class stay in pqcli.config so existing saves still load. Resolves #3 --- README.md | 43 + pqcli/__main__.py | 49 +- pqcli/config.py | 679 +----- pqcli/data/armors.json | 22 + pqcli/data/boring_items.json | 44 + pqcli/data/classes.json | 20 + pqcli/data/combat_verbs.json | 3 + pqcli/data/defense_attrib.json | 11 + pqcli/data/defense_bad.json | 16 + pqcli/data/impressive_titles.json | 13 + pqcli/data/item_attrib.json | 35 + pqcli/data/item_ofs.json | 54 + pqcli/data/monsters.json | 234 +++ pqcli/data/offense_attrib.json | 13 + pqcli/data/offense_bad.json | 11 + pqcli/data/races.json | 23 + pqcli/data/shields.json | 18 + pqcli/data/specials.json | 39 + pqcli/data/spells.json | 49 + pqcli/data/titles.json | 11 + pqcli/data/weapons.json | 41 + pqcli/game_data.py | 249 +++ pqcli/mechanic.py | 161 +- pqcli/ui/base.py | 3 + pqcli/ui/basic/__init__.py | 14 +- pqcli/ui/curses/__init__.py | 19 +- .../choose_character_class_view.py | 11 +- .../choose_character_race_view.py | 13 +- pqcli/ui/curses/views/game_view/__init__.py | 4 +- pyproject.toml | 2 +- tests/conftest.py | 4 + tests/fixtures/parity_alpha.txt | 1871 +++++++++++++++++ tests/fixtures/parity_beta.txt | 1857 ++++++++++++++++ tests/fixtures/parity_gamma.txt | 1867 ++++++++++++++++ tests/game_trace.py | 69 + tests/test_game_data.py | 182 ++ tests/test_parity.py | 14 + uv.lock | 153 +- 38 files changed, 7143 insertions(+), 778 deletions(-) create mode 100644 pqcli/data/armors.json create mode 100644 pqcli/data/boring_items.json create mode 100644 pqcli/data/classes.json create mode 100644 pqcli/data/combat_verbs.json create mode 100644 pqcli/data/defense_attrib.json create mode 100644 pqcli/data/defense_bad.json create mode 100644 pqcli/data/impressive_titles.json create mode 100644 pqcli/data/item_attrib.json create mode 100644 pqcli/data/item_ofs.json create mode 100644 pqcli/data/monsters.json create mode 100644 pqcli/data/offense_attrib.json create mode 100644 pqcli/data/offense_bad.json create mode 100644 pqcli/data/races.json create mode 100644 pqcli/data/shields.json create mode 100644 pqcli/data/specials.json create mode 100644 pqcli/data/spells.json create mode 100644 pqcli/data/titles.json create mode 100644 pqcli/data/weapons.json create mode 100644 pqcli/game_data.py create mode 100644 tests/conftest.py create mode 100644 tests/fixtures/parity_alpha.txt create mode 100644 tests/fixtures/parity_beta.txt create mode 100644 tests/fixtures/parity_gamma.txt create mode 100644 tests/game_trace.py create mode 100644 tests/test_game_data.py create mode 100644 tests/test_parity.py diff --git a/README.md b/README.md index 6fbdf1b..7bb5bf7 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,49 @@ For server usage, run first-time setup without a forced slot (`pqcli-init`), then run the long-lived detached service (`pqcli`) that loads slot `1` by default. +## Customizing the adventure + +All word lists (monsters, spells, equipment, races, classes, titles and so on) +live in JSON files. To change them, copy the built-in files somewhere and edit +them: + +```console +$ pqcli --dump-data ~/.config/pqcli/data +``` + +On startup, pqcli reads `$XDG_CONFIG_HOME/pqcli/data/` (or the directory +given with `--data-dir DIR`). Each `NAME.json` there replaces the built-in +list of the same name. Files you leave out fall back to the built-in lists, +so you can delete the copies you don't change. If a file is malformed, pqcli +prints what is wrong and refuses to start. + +The available files are `spells`, `offense_attrib`, `defense_attrib`, +`offense_bad`, `defense_bad`, `shields`, `armors`, `weapons`, `specials`, +`item_attrib`, `item_ofs`, `boring_items`, `monsters`, `races`, `classes`, +`titles`, `impressive_titles` and `combat_verbs`. + +For example, to vary the combat descriptions, create `combat_verbs.json`. +`{target}` is replaced with the enemy: + +```json +[ + "Executing {target}", + "In a fierce battle to thwart {target}", + "Fighting bravely against {target}", + "Kicking the butt of {target}" +] +``` + +A few things to keep in mind: + +- Order matters in `spells.json`: spells are learned roughly from the top + down, so keep weaker spells first. +- Titles near the top of `titles.json` show up more often. +- Stats in `races.json` and `classes.json` are written as `STR`, `CON`, + `DEX`, `INT`, `WIS`, `CHA`, `HP Max` or `MP Max`. +- Existing characters keep their race and class even if you remove them from + the lists. + ## Contributing ```sh diff --git a/pqcli/__main__.py b/pqcli/__main__.py index d2b2302..0729c3a 100644 --- a/pqcli/__main__.py +++ b/pqcli/__main__.py @@ -5,12 +5,19 @@ from pathlib import Path from xdg_base_dirs import xdg_config_home +from pqcli.game_data import ( + DataError, + GameData, + dump_builtin_data, + load_game_data, +) from pqcli.mechanic import Player from pqcli.roster import Roster from pqcli.ui.basic import BasicUserInterface from pqcli.ui.curses import CursesUserInterface SAVE_PATH = Path(xdg_config_home()) / "pqcli" / "save.dat" +DATA_PATH = Path(xdg_config_home()) / "pqcli" / "data" def parse_args() -> argparse.Namespace: @@ -58,6 +65,22 @@ def parse_args() -> argparse.Namespace: metavar="NUM", help="play chosen character", ) + parser.add_argument( + "--data-dir", + type=Path, + default=DATA_PATH, + metavar="DIR", + help=( + "directory with custom word lists; each NAME.json replaces " + f"the built-in list of that name (default: {DATA_PATH})" + ), + ) + parser.add_argument( + "--dump-data", + type=Path, + metavar="DIR", + help="copy the built-in word lists to DIR for editing and exit", + ) return parser.parse_args() @@ -66,12 +89,14 @@ def list_players(roster: Roster, file: T.Optional[T.Any] = None) -> None: print(f"{i}. {player.name}", file=file) -def bootstrap_first_run(roster: Roster, args: argparse.Namespace) -> None: +def bootstrap_first_run( + roster: Roster, args: argparse.Namespace, data: GameData +) -> None: if roster.players or args.list_saves or args.load_save: return print("No saved characters found. Starting first-run character creation.") - ui = BasicUserInterface(roster, None, args) + ui = BasicUserInterface(roster, None, args, data) while not roster.players: player = ui.create_player(auto_play=False) if player: @@ -83,6 +108,22 @@ def bootstrap_first_run(roster: Roster, args: argparse.Namespace) -> None: def main() -> None: args = parse_args() + + if args.dump_data: + try: + paths = dump_builtin_data(args.dump_data) + except DataError as ex: + print(f"pqcli: {ex}", file=sys.stderr) + exit(1) + print(f"Wrote {len(paths)} files to {args.dump_data}") + exit(0) + + try: + data = load_game_data(args.data_dir) + except DataError as ex: + print(f"pqcli: {ex}", file=sys.stderr) + exit(1) + roster = Roster.load(SAVE_PATH) if args.list_saves: @@ -98,10 +139,10 @@ def main() -> None: list_players(roster, file=sys.stderr) exit(1) - bootstrap_first_run(roster, args) + bootstrap_first_run(roster, args, data) try: - ui = args.ui(roster, player, args) + ui = args.ui(roster, player, args, data) ui.run() finally: if args.use_saves: diff --git a/pqcli/config.py b/pqcli/config.py index addd556..27cdbff 100644 --- a/pqcli/config.py +++ b/pqcli/config.py @@ -39,56 +39,9 @@ PRIME_STATS = [ ALL_STATS = PRIME_STATS + [StatType.hp_max, StatType.mp_max] - -SPELLS = [ - "Slime Finger", - "Rabbit Punch", - "Hastiness", - "Good Move", - "Sadness", - "Seasick", - "Shoelaces", - "Inoculate", - "Cone of Annoyance", - "Magnetic Orb", - "Invisible Hands", - "Revolting Cloud", - "Aqueous Humor", - "Spectral Miasma", - "Clever Fellow", - "Lockjaw", - "History Lesson", - "Hydrophobia", - "Big Sister", - "Cone of Paste", - "Mulligan", - "Nestor's Bright Idea", - "Holy Batpole", - "Tumor (Benign)", - "Braingate", - "Summon a Bitch", - "Nonplus", - "Animate Nightstand", - "Eye of the Troglodyte", - "Curse Name", - "Dropsy", - "Vitreous Humor", - "Roger's Grand Illusion", - "Covet", - "Black Idaho", - "Astral Miasma", - "Spectral Oyster", - "Acrid Hands", - "Angioplasty", - "Grognor's Big Day Off", - "Tumor (Malignant)", - "Animate Tunic", - "Ursine Armor", - "Holy Roller", - "Tonsillectomy", - "Curse Family", - "Infinite Confusion", -] +# The word lists live in pqcli/data/*.json (see pqcli.game_data). +# Saves pickle Monster, Race and Class instances by import path, so these +# classes must stay in this module. @dataclass @@ -97,329 +50,12 @@ class Modifier: quality: int -OFFENSE_ATTRIB = [ - Modifier("Polished", 1), - Modifier("Serrated", 1), - Modifier("Heavy", 1), - Modifier("Pronged", 2), - Modifier("Steely", 2), - Modifier("Vicious", 3), - Modifier("Venomed", 4), - Modifier("Stabbity", 4), - Modifier("Dancing", 5), - Modifier("Invisible", 6), - Modifier("Vorpal", 7), -] - -DEFENSE_ATTRIB = [ - Modifier("Studded", 1), - Modifier("Banded", 2), - Modifier("Gilded", 2), - Modifier("Festooned", 3), - Modifier("Holy", 4), - Modifier("Cambric", 1), - Modifier("Fine", 4), - Modifier("Impressive", 5), - Modifier("Custom", 3), -] - -OFFENSE_BAD = [ - Modifier("Dull", -2), - Modifier("Tarnished", -1), - Modifier("Rusty", -3), - Modifier("Padded", -5), - Modifier("Bent", -4), - Modifier("Mini", -4), - Modifier("Rubber", -6), - Modifier("Nerf", -7), - Modifier("Unbalanced", -2), -] - -DEFENSE_BAD = [ - Modifier("Holey", -1), - Modifier("Patched", -1), - Modifier("Threadbare", -2), - Modifier("Faded", -1), - Modifier("Rusty", -3), - Modifier("Motheaten", -3), - Modifier("Mildewed", -2), - Modifier("Torn", -3), - Modifier("Dented", -3), - Modifier("Cursed", -5), - Modifier("Plastic", -4), - Modifier("Cracked", -4), - Modifier("Warped", -3), - Modifier("Corroded", -3), -] - - @dataclass class EquipmentPreset: name: str quality: int -SHIELDS = [ - EquipmentPreset("Parasol", 0), - EquipmentPreset("Pie Plate", 1), - EquipmentPreset("Garbage Can Lid", 2), - EquipmentPreset("Buckler", 3), - EquipmentPreset("Plexiglass", 4), - EquipmentPreset("Fender", 4), - EquipmentPreset("Round Shield", 5), - EquipmentPreset("Carapace", 5), - EquipmentPreset("Scutum", 6), - EquipmentPreset("Propugner", 6), - EquipmentPreset("Kite Shield", 7), - EquipmentPreset("Pavise", 8), - EquipmentPreset("Tower Shield", 9), - EquipmentPreset("Baroque Shield", 11), - EquipmentPreset("Aegis", 12), - EquipmentPreset("Magnetic Field", 18), -] - -ARMORS = [ - EquipmentPreset("Lace", 1), - EquipmentPreset("Macrame", 2), - EquipmentPreset("Burlap", 3), - EquipmentPreset("Canvas", 4), - EquipmentPreset("Flannel", 5), - EquipmentPreset("Chamois", 6), - EquipmentPreset("Pleathers", 7), - EquipmentPreset("Leathers", 8), - EquipmentPreset("Bearskin", 9), - EquipmentPreset("Ringmail", 10), - EquipmentPreset("Scale Mail", 12), - EquipmentPreset("Chainmail", 14), - EquipmentPreset("Splint Mail", 15), - EquipmentPreset("Platemail", 16), - EquipmentPreset("ABS", 17), - EquipmentPreset("Kevlar", 18), - EquipmentPreset("Titanium", 19), - EquipmentPreset("Mithril Mail", 20), - EquipmentPreset("Diamond Mail", 25), - EquipmentPreset("Plasma", 30), -] - -WEAPONS = [ - EquipmentPreset("Stick", 0), - EquipmentPreset("Broken Bottle", 1), - EquipmentPreset("Shiv", 1), - EquipmentPreset("Sprig", 1), - EquipmentPreset("Oxgoad", 1), - EquipmentPreset("Eelspear", 2), - EquipmentPreset("Bowie Knife", 2), - EquipmentPreset("Claw Hammer", 2), - EquipmentPreset("Handpeen", 2), - EquipmentPreset("Andiron", 3), - EquipmentPreset("Hatchet", 3), - EquipmentPreset("Tomahawk", 3), - EquipmentPreset("Hackbarm", 3), - EquipmentPreset("Crowbar", 4), - EquipmentPreset("Mace", 4), - EquipmentPreset("Battleadze", 4), - EquipmentPreset("Leafmace", 5), - EquipmentPreset("Shortsword", 5), - EquipmentPreset("Longiron", 5), - EquipmentPreset("Poachard", 5), - EquipmentPreset("Baselard", 5), - EquipmentPreset("Whinyard", 6), - EquipmentPreset("Blunderbuss", 6), - EquipmentPreset("Longsword", 6), - EquipmentPreset("Crankbow", 6), - EquipmentPreset("Blibo", 7), - EquipmentPreset("Broadsword", 7), - EquipmentPreset("Kreen", 7), - EquipmentPreset("Warhammer", 7), - EquipmentPreset("Morning Star", 8), - EquipmentPreset("Pole-adze", 8), - EquipmentPreset("Spontoon", 8), - EquipmentPreset("Bastard Sword", 9), - EquipmentPreset("Peen-arm", 9), - EquipmentPreset("Culverin", 10), - EquipmentPreset("Lance", 10), - EquipmentPreset("Halberd", 11), - EquipmentPreset("Poleax", 12), - EquipmentPreset("Bandyclef", 15), -] - -SPECIALS = [ - "Diadem", - "Festoon", - "Gemstone", - "Phial", - "Tiara", - "Scabbard", - "Arrow", - "Lens", - "Lamp", - "Hymnal", - "Fleece", - "Laurel", - "Brooch", - "Gimlet", - "Cobble", - "Albatross", - "Brazier", - "Bandolier", - "Tome", - "Garnet", - "Amethyst", - "Candelabra", - "Corset", - "Sphere", - "Sceptre", - "Ankh", - "Talisman", - "Orb", - "Gammel", - "Ornament", - "Brocade", - "Galoon", - "Bijou", - "Spangle", - "Gimcrack", - "Hood", - "Vulpeculum", -] - -ITEM_ATTRIB = [ - "Golden", - "Gilded", - "Spectral", - "Astral", - "Garlanded", - "Precious", - "Crafted", - "Dual", - "Filigreed", - "Cruciate", - "Arcane", - "Blessed", - "Reverential", - "Lucky", - "Enchanted", - "Gleaming", - "Grandiose", - "Sacred", - "Legendary", - "Mythic", - "Crystalline", - "Austere", - "Ostentatious", - "One True", - "Proverbial", - "Fearsome", - "Deadly", - "Benevolent", - "Unearthly", - "Magnificent", - "Iron", - "Ormolu", - "Puissant", -] - -ITEM_OFS = [ - "Foreboding", - "Foreshadowing", - "Nervousness", - "Happiness", - "Torpor", - "Danger", - "Craft", - "Silence", - "Invisibility", - "Rapidity", - "Pleasure", - "Practicality", - "Hurting", - "Joy", - "Petulance", - "Intrusion", - "Chaos", - "Suffering", - "Extroversion", - "Frenzy", - "Sisu", - "Solitude", - "Punctuality", - "Efficiency", - "Comfort", - "Patience", - "Internment", - "Incarceration", - "Misapprehension", - "Loyalty", - "Envy", - "Acrimony", - "Worry", - "Fear", - "Awe", - "Guile", - "Prurience", - "Fortune", - "Perspicacity", - "Domination", - "Submission", - "Fealty", - "Hunger", - "Despair", - "Cruelty", - "Grob", - "Dignard", - "Ra", - "the Bone", - "Diamonique", - "Electrum", - "Hydragyrum", -] - -BORING_ITEMS = [ - "nail", - "lunchpail", - "sock", - "I.O.U.", - "cookie", - "pint", - "toothpick", - "writ", - "newspaper", - "letter", - "plank", - "hat", - "egg", - "coin", - "needle", - "bucket", - "ladder", - "chicken", - "twig", - "dirtclod", - "counterpane", - "vest", - "teratoma", - "bunny", - "rock", - "pole", - "carrot", - "canoe", - "inkwell", - "hoe", - "bandage", - "trowel", - "towel", - "planter box", - "anvil", - "axle", - "tuppence", - "casket", - "nosegay", - "trinket", - "credenza", - "writ", -] - - @dataclass class Monster: name: str @@ -427,322 +63,13 @@ class Monster: item: T.Optional[str] -MONSTERS = [ - Monster("Anhkheg", 6, "chitin"), - Monster("Ant", 0, "antenna"), - Monster("Ape", 4, "ass"), - Monster("Baluchitherium", 14, "ear"), - Monster("Beholder", 10, "eyestalk"), - Monster("Black Pudding", 10, "saliva"), - Monster("Blink Dog", 4, "eyelid"), - Monster("Cub Scout", 1, "neckerchief"), - Monster("Girl Scout", 2, "cookie"), - Monster("Boy Scout", 3, "merit badge"), - Monster("Eagle Scout", 4, "merit badge"), - Monster("Bugbear", 3, "skin"), - Monster("Bugboar", 3, "tusk"), - Monster("Boogie", 3, "slime"), - Monster("Camel", 2, "hump"), - Monster("Carrion Crawler", 3, "egg"), - Monster("Catoblepas", 6, "neck"), - Monster("Centaur", 4, "rib"), - Monster("Centipede", 0, "leg"), - Monster("Cockatrice", 5, "wattle"), - Monster("Couatl", 9, "wing"), - Monster("Crayfish", 0, "antenna"), - Monster("Demogorgon", 53, "tentacle"), - Monster("Jubilex", 17, "gel"), - Monster("Manes", 1, "tooth"), - Monster("Orcus", 27, "wand"), - Monster("Succubus", 6, "bra"), - Monster("Vrock", 8, "neck"), - Monster("Hezrou", 9, "leg"), - Monster("Glabrezu", 10, "collar"), - Monster("Nalfeshnee", 11, "tusk"), - Monster("Marilith", 7, "arm"), - Monster("Balor", 8, "whip"), - Monster("Yeenoghu", 25, "flail"), - Monster("Asmodeus", 52, "leathers"), - Monster("Baalzebul", 43, "pants"), - Monster("Barbed Devil", 8, "flame"), - Monster("Bone Devil", 9, "hook"), - Monster("Dispater", 30, "matches"), - Monster("Erinyes", 6, "thong"), - Monster("Geryon", 30, "cornucopia"), - Monster("Malebranche", 5, "fork"), - Monster("Ice Devil", 11, "snow"), - Monster("Lemure", 3, "blob"), - Monster("Pit Fiend", 13, "seed"), - Monster("Ankylosaurus", 9, "tail"), - Monster("Brontosaurus", 30, "brain"), - Monster("Diplodocus", 24, "fin"), - Monster("Elasmosaurus", 15, "neck"), - Monster("Gorgosaurus", 13, "arm"), - Monster("Iguanadon", 6, "thumb"), - Monster("Megalosaurus", 12, "jaw"), - Monster("Monoclonius", 8, "horn"), - Monster("Pentasaurus", 12, "head"), - Monster("Stegosaurus", 18, "plate"), - Monster("Triceratops", 16, "horn"), - Monster("Tyrannosaurus Rex", 18, "forearm"), - Monster("Djinn", 7, "lamp"), - Monster("Doppelganger", 4, "face"), - Monster("Black Dragon", 7, None), - Monster("Plaid Dragon", 7, "sporrin"), - Monster("Blue Dragon", 9, None), - Monster("Beige Dragon", 9, None), - Monster("Brass Dragon", 7, "pole"), - Monster("Tin Dragon", 8, None), - Monster("Bronze Dragon", 9, "medal"), - Monster("Chromatic Dragon", 16, "scale"), - Monster("Copper Dragon", 8, "loafer"), - Monster("Gold Dragon", 8, "filling"), - Monster("Green Dragon", 8, None), - Monster("Platinum Dragon", 21, None), - Monster("Red Dragon", 10, "cocktail"), - Monster("Silver Dragon", 10, None), - Monster("White Dragon", 6, "tooth"), - Monster("Dragon Turtle", 13, "shell"), - Monster("Dryad", 2, "acorn"), - Monster("Dwarf", 1, "drawers"), - Monster("Eel", 2, "sashimi"), - Monster("Efreet", 10, "cinder"), - Monster("Sand Elemental", 8, "glass"), - Monster("Bacon Elemental", 10, "bit"), - Monster("Porn Elemental", 12, "lube"), - Monster("Cheese Elemental", 14, "curd"), - Monster("Hair Elemental", 16, "follicle"), - Monster("Swamp Elf", 1, "lilypad"), - Monster("Brown Elf", 1, "tusk"), - Monster("Sea Elf", 1, "jerkin"), - Monster("Ettin", 10, "fur"), - Monster("Frog", 0, "leg"), - Monster("Violet Fungi", 3, "spore"), - Monster("Gargoyle", 4, "gravel"), - Monster("Gelatinous Cube", 4, "jam"), - Monster("Ghast", 4, "vomit"), - Monster("Ghost", 10, None), - Monster("Ghoul", 2, "muscle"), - Monster("Humidity Giant", 12, "drops"), - Monster("Beef Giant", 11, "steak"), - Monster("Quartz Giant", 10, "crystal"), - Monster("Porcelain Giant", 9, "fixture"), - Monster("Rice Giant", 8, "grain"), - Monster("Cloud Giant", 12, "condensation"), - Monster("Fire Giant", 11, "cigarettes"), - Monster("Frost Giant", 10, "snowman"), - Monster("Hill Giant", 8, "corpse"), - Monster("Stone Giant", 9, "hatchling"), - Monster("Storm Giant", 15, "barometer"), - Monster("Mini Giant", 4, "pompadour"), - Monster("Gnoll", 2, "collar"), - Monster("Gnome", 1, "hat"), - Monster("Goblin", 1, "ear"), - Monster("Grid Bug", 1, "carapace"), - Monster("Jellyrock", 9, "seedling"), - Monster("Beer Golem", 15, "foam"), - Monster("Oxygen Golem", 17, "platelet"), - Monster("Cardboard Golem", 14, "recycling"), - Monster("Rubber Golem", 16, "ball"), - Monster("Leather Golem", 15, "fob"), - Monster("Gorgon", 8, "testicle"), - Monster("Gray Ooze", 3, "gravy"), - Monster("Green Slime", 2, "sample"), - Monster("Griffon", 7, "nest"), - Monster("Banshee", 7, "larynx"), - Monster("Harpy", 3, "mascara"), - Monster("Hell Hound", 5, "tongue"), - Monster("Hippocampus", 4, "mane"), - Monster("Hippogriff", 3, "egg"), - Monster("Hobgoblin", 1, "patella"), - Monster("Homunculus", 2, "fluid"), - Monster("Hydra", 8, "gyrum"), - Monster("Imp", 2, "tail"), - Monster("Invisible Stalker", 8, None), - Monster("Iron Peasant", 3, "chaff"), - Monster("Jumpskin", 3, "shin"), - Monster("Kobold", 1, "penis"), - Monster("Leprechaun", 1, "wallet"), - Monster("Leucrotta", 6, "hoof"), - Monster("Lich", 11, "crown"), - Monster("Lizard Man", 2, "tail"), - Monster("Lurker", 10, "sac"), - Monster("Manticore", 6, "spike"), - Monster("Mastodon", 12, "tusk"), - Monster("Medusa", 6, "eye"), - Monster("Multicell", 2, "dendrite"), - Monster("Pirate", 1, "booty"), - Monster("Berserker", 1, "shirt"), - Monster("Caveman", 2, "club"), - Monster("Dervish", 1, "robe"), - Monster("Merman", 1, "trident"), - Monster("Mermaid", 1, "gills"), - Monster("Mimic", 9, "hinge"), - Monster("Mind Flayer", 8, "tentacle"), - Monster("Minotaur", 6, "map"), - Monster("Yellow Mold", 1, "spore"), - Monster("Morkoth", 7, "teeth"), - Monster("Mummy", 6, "gauze"), - Monster("Naga", 9, "rattle"), - Monster("Nebbish", 1, "belly"), - Monster("Neo-Otyugh", 11, "organ "), - Monster("Nixie", 1, "webbing"), - Monster("Nymph", 3, "hanky"), - Monster("Ochre Jelly", 6, "nucleus"), - Monster("Octopus", 2, "beak"), - Monster("Ogre", 4, "talon"), - Monster("Ogre Mage", 5, "apparel"), - Monster("Orc", 1, "snout"), - Monster("Otyugh", 7, "organ"), - Monster("Owlbear", 5, "feather"), - Monster("Pegasus", 4, "aileron"), - Monster("Peryton", 4, "antler"), - Monster("Piercer", 3, "tip"), - Monster("Pixie", 1, "dust"), - Monster("Man-o-war", 3, "tentacle"), - Monster("Purple Worm", 15, "dung"), - Monster("Quasit", 3, "tail"), - Monster("Rakshasa", 7, "pajamas"), - Monster("Rat", 0, "tail"), - Monster("Remorhaz", 11, "protrusion"), - Monster("Roc", 18, "wing"), - Monster("Roper", 11, "twine"), - Monster("Rot Grub", 1, "eggsac"), - Monster("Rust Monster", 5, "shavings"), - Monster("Satyr", 5, "hoof"), - Monster("Sea Hag", 3, "wart"), - Monster("Silkie", 3, "fur"), - Monster("Shadow", 3, "silhouette"), - Monster("Shambling Mound", 10, "mulch"), - Monster("Shedu", 9, "hoof"), - Monster("Shrieker", 3, "stalk"), - Monster("Skeleton", 1, "clavicle"), - Monster("Spectre", 7, "vestige"), - Monster("Sphinx", 10, "paw"), - Monster("Spider", 0, "web"), - Monster("Sprite", 1, "can"), - Monster("Stirge", 1, "proboscis"), - Monster("Stun Bear", 5, "tooth"), - Monster("Stun Worm", 2, "trode"), - Monster("Su-monster", 5, "tail"), - Monster("Sylph", 3, "thigh"), - Monster("Titan", 20, "sandal"), - Monster("Trapper", 12, "shag"), - Monster("Treant", 10, "acorn"), - Monster("Triton", 3, "scale"), - Monster("Troglodyte", 2, "tail"), - Monster("Troll", 6, "hide"), - Monster("Umber Hulk", 8, "claw"), - Monster("Unicorn", 4, "blood"), - Monster("Vampire", 8, "pancreas"), - Monster("Wight", 4, "lung"), - Monster("Will-o'-the-Wisp", 9, "wisp"), - Monster("Wraith", 5, "finger"), - Monster("Wyvern", 7, "wing"), - Monster("Xorn", 7, "jaw"), - Monster("Yeti", 4, "fur"), - Monster("Zombie", 2, "forehead"), - Monster("Wasp", 0, "stinger"), - Monster("Rat", 1, "tail"), - Monster("Bunny", 0, "ear"), - Monster("Moth", 0, "dust"), - Monster("Beagle", 0, "collar"), - Monster("Midge", 0, "corpse"), - Monster("Ostrich", 1, "beak"), - Monster("Billy Goat", 1, "beard"), - Monster("Bat", 1, "wing"), - Monster("Koala", 2, "heart"), - Monster("Wolf", 2, "paw"), - Monster("Whippet", 2, "collar"), - Monster("Uruk", 2, "boot"), - Monster("Poroid", 4, "node"), - Monster("Moakum", 8, "frenum"), - Monster("Fly", 0, None), - Monster("Hogbird", 3, "curl"), - Monster("Wolog", 4, "lemma"), -] - - @dataclass class Race: name: str attr: T.List[StatType] -RACES = [ - Race("Half Orc", [StatType.hp_max]), - Race("Half Man", [StatType.charisma]), - Race("Half Halfling", [StatType.dexterity]), - Race("Double Hobbit", [StatType.strength]), - Race("Hob-Hobbit", [StatType.dexterity, StatType.condition]), - Race("Low Elf", [StatType.condition]), - Race("Dung Elf", [StatType.wisdom]), - Race("Talking Pony", [StatType.mp_max, StatType.intelligence]), - Race("Gyrognome", [StatType.dexterity]), - Race("Lesser Dwarf", [StatType.condition]), - Race("Crested Dwarf", [StatType.charisma]), - Race("Eel Man", [StatType.dexterity]), - Race("Panda Man", [StatType.condition, StatType.strength]), - Race("Trans-Kobold", [StatType.wisdom]), - Race("Enchanted Motorcycle", [StatType.mp_max]), - Race("Will o' the Wisp", [StatType.wisdom]), - Race("Battle-Finch", [StatType.dexterity, StatType.intelligence]), - Race("Double Wookiee", [StatType.strength]), - Race("Skraeling", [StatType.wisdom]), - Race("Demicanadian", [StatType.condition]), - Race("Land Squid", [StatType.strength, StatType.hp_max]), -] - - @dataclass class Class: name: str attr: T.List[StatType] - - -CLASSES = [ - Class("Ur-Paladin", [StatType.wisdom, StatType.condition]), - Class("Voodoo Princess", [StatType.intelligence, StatType.charisma]), - Class("Robot Monk", [StatType.strength]), - Class("Mu-Fu Monk", [StatType.dexterity]), - Class("Mage Illusioner", [StatType.intelligence, StatType.mp_max]), - Class("Shiv-Knight", [StatType.dexterity]), - Class("Inner Mason", [StatType.condition]), - Class("Fighter/Organist", [StatType.charisma, StatType.strength]), - Class("Puma Burgular", [StatType.dexterity]), - Class("Runeloremaster", [StatType.wisdom]), - Class("Hunter Strangler", [StatType.dexterity, StatType.intelligence]), - Class("Battle-Felon", [StatType.strength]), - Class("Tickle-Mimic", [StatType.wisdom, StatType.intelligence]), - Class("Slow Poisoner", [StatType.condition]), - Class("Bastard Lunatic", [StatType.condition]), - Class("Lowling", [StatType.wisdom]), - Class("Birdrider", [StatType.wisdom]), - Class("Vermineer", [StatType.intelligence]), -] - -TITLES = [ - "Mr.", - "Mrs.", - "Sir", - "Sgt.", - "Ms.", - "Captain", - "Chief", - "Admiral", - "Saint", -] - -IMPRESSIVE_TITLES = [ - "King", - "Queen", - "Lord", - "Lady", - "Viceroy", - "Mayor", - "Prince", - "Princess", - "Chief", - "Boss", - "Archbishop", -] diff --git a/pqcli/data/armors.json b/pqcli/data/armors.json new file mode 100644 index 0000000..acb9e5c --- /dev/null +++ b/pqcli/data/armors.json @@ -0,0 +1,22 @@ +[ + {"name": "Lace", "quality": 1}, + {"name": "Macrame", "quality": 2}, + {"name": "Burlap", "quality": 3}, + {"name": "Canvas", "quality": 4}, + {"name": "Flannel", "quality": 5}, + {"name": "Chamois", "quality": 6}, + {"name": "Pleathers", "quality": 7}, + {"name": "Leathers", "quality": 8}, + {"name": "Bearskin", "quality": 9}, + {"name": "Ringmail", "quality": 10}, + {"name": "Scale Mail", "quality": 12}, + {"name": "Chainmail", "quality": 14}, + {"name": "Splint Mail", "quality": 15}, + {"name": "Platemail", "quality": 16}, + {"name": "ABS", "quality": 17}, + {"name": "Kevlar", "quality": 18}, + {"name": "Titanium", "quality": 19}, + {"name": "Mithril Mail", "quality": 20}, + {"name": "Diamond Mail", "quality": 25}, + {"name": "Plasma", "quality": 30} +] diff --git a/pqcli/data/boring_items.json b/pqcli/data/boring_items.json new file mode 100644 index 0000000..ba32f16 --- /dev/null +++ b/pqcli/data/boring_items.json @@ -0,0 +1,44 @@ +[ + "nail", + "lunchpail", + "sock", + "I.O.U.", + "cookie", + "pint", + "toothpick", + "writ", + "newspaper", + "letter", + "plank", + "hat", + "egg", + "coin", + "needle", + "bucket", + "ladder", + "chicken", + "twig", + "dirtclod", + "counterpane", + "vest", + "teratoma", + "bunny", + "rock", + "pole", + "carrot", + "canoe", + "inkwell", + "hoe", + "bandage", + "trowel", + "towel", + "planter box", + "anvil", + "axle", + "tuppence", + "casket", + "nosegay", + "trinket", + "credenza", + "writ" +] diff --git a/pqcli/data/classes.json b/pqcli/data/classes.json new file mode 100644 index 0000000..08fa73d --- /dev/null +++ b/pqcli/data/classes.json @@ -0,0 +1,20 @@ +[ + {"name": "Ur-Paladin", "attr": ["WIS", "CON"]}, + {"name": "Voodoo Princess", "attr": ["INT", "CHA"]}, + {"name": "Robot Monk", "attr": ["STR"]}, + {"name": "Mu-Fu Monk", "attr": ["DEX"]}, + {"name": "Mage Illusioner", "attr": ["INT", "MP Max"]}, + {"name": "Shiv-Knight", "attr": ["DEX"]}, + {"name": "Inner Mason", "attr": ["CON"]}, + {"name": "Fighter/Organist", "attr": ["CHA", "STR"]}, + {"name": "Puma Burgular", "attr": ["DEX"]}, + {"name": "Runeloremaster", "attr": ["WIS"]}, + {"name": "Hunter Strangler", "attr": ["DEX", "INT"]}, + {"name": "Battle-Felon", "attr": ["STR"]}, + {"name": "Tickle-Mimic", "attr": ["WIS", "INT"]}, + {"name": "Slow Poisoner", "attr": ["CON"]}, + {"name": "Bastard Lunatic", "attr": ["CON"]}, + {"name": "Lowling", "attr": ["WIS"]}, + {"name": "Birdrider", "attr": ["WIS"]}, + {"name": "Vermineer", "attr": ["INT"]} +] diff --git a/pqcli/data/combat_verbs.json b/pqcli/data/combat_verbs.json new file mode 100644 index 0000000..af129b0 --- /dev/null +++ b/pqcli/data/combat_verbs.json @@ -0,0 +1,3 @@ +[ + "Executing {target}" +] diff --git a/pqcli/data/defense_attrib.json b/pqcli/data/defense_attrib.json new file mode 100644 index 0000000..72965d7 --- /dev/null +++ b/pqcli/data/defense_attrib.json @@ -0,0 +1,11 @@ +[ + {"name": "Studded", "quality": 1}, + {"name": "Banded", "quality": 2}, + {"name": "Gilded", "quality": 2}, + {"name": "Festooned", "quality": 3}, + {"name": "Holy", "quality": 4}, + {"name": "Cambric", "quality": 1}, + {"name": "Fine", "quality": 4}, + {"name": "Impressive", "quality": 5}, + {"name": "Custom", "quality": 3} +] diff --git a/pqcli/data/defense_bad.json b/pqcli/data/defense_bad.json new file mode 100644 index 0000000..ab35e6c --- /dev/null +++ b/pqcli/data/defense_bad.json @@ -0,0 +1,16 @@ +[ + {"name": "Holey", "quality": -1}, + {"name": "Patched", "quality": -1}, + {"name": "Threadbare", "quality": -2}, + {"name": "Faded", "quality": -1}, + {"name": "Rusty", "quality": -3}, + {"name": "Motheaten", "quality": -3}, + {"name": "Mildewed", "quality": -2}, + {"name": "Torn", "quality": -3}, + {"name": "Dented", "quality": -3}, + {"name": "Cursed", "quality": -5}, + {"name": "Plastic", "quality": -4}, + {"name": "Cracked", "quality": -4}, + {"name": "Warped", "quality": -3}, + {"name": "Corroded", "quality": -3} +] diff --git a/pqcli/data/impressive_titles.json b/pqcli/data/impressive_titles.json new file mode 100644 index 0000000..9197770 --- /dev/null +++ b/pqcli/data/impressive_titles.json @@ -0,0 +1,13 @@ +[ + "King", + "Queen", + "Lord", + "Lady", + "Viceroy", + "Mayor", + "Prince", + "Princess", + "Chief", + "Boss", + "Archbishop" +] diff --git a/pqcli/data/item_attrib.json b/pqcli/data/item_attrib.json new file mode 100644 index 0000000..b2a1fb2 --- /dev/null +++ b/pqcli/data/item_attrib.json @@ -0,0 +1,35 @@ +[ + "Golden", + "Gilded", + "Spectral", + "Astral", + "Garlanded", + "Precious", + "Crafted", + "Dual", + "Filigreed", + "Cruciate", + "Arcane", + "Blessed", + "Reverential", + "Lucky", + "Enchanted", + "Gleaming", + "Grandiose", + "Sacred", + "Legendary", + "Mythic", + "Crystalline", + "Austere", + "Ostentatious", + "One True", + "Proverbial", + "Fearsome", + "Deadly", + "Benevolent", + "Unearthly", + "Magnificent", + "Iron", + "Ormolu", + "Puissant" +] diff --git a/pqcli/data/item_ofs.json b/pqcli/data/item_ofs.json new file mode 100644 index 0000000..3b3927e --- /dev/null +++ b/pqcli/data/item_ofs.json @@ -0,0 +1,54 @@ +[ + "Foreboding", + "Foreshadowing", + "Nervousness", + "Happiness", + "Torpor", + "Danger", + "Craft", + "Silence", + "Invisibility", + "Rapidity", + "Pleasure", + "Practicality", + "Hurting", + "Joy", + "Petulance", + "Intrusion", + "Chaos", + "Suffering", + "Extroversion", + "Frenzy", + "Sisu", + "Solitude", + "Punctuality", + "Efficiency", + "Comfort", + "Patience", + "Internment", + "Incarceration", + "Misapprehension", + "Loyalty", + "Envy", + "Acrimony", + "Worry", + "Fear", + "Awe", + "Guile", + "Prurience", + "Fortune", + "Perspicacity", + "Domination", + "Submission", + "Fealty", + "Hunger", + "Despair", + "Cruelty", + "Grob", + "Dignard", + "Ra", + "the Bone", + "Diamonique", + "Electrum", + "Hydragyrum" +] diff --git a/pqcli/data/monsters.json b/pqcli/data/monsters.json new file mode 100644 index 0000000..ab07854 --- /dev/null +++ b/pqcli/data/monsters.json @@ -0,0 +1,234 @@ +[ + {"name": "Anhkheg", "level": 6, "item": "chitin"}, + {"name": "Ant", "level": 0, "item": "antenna"}, + {"name": "Ape", "level": 4, "item": "ass"}, + {"name": "Baluchitherium", "level": 14, "item": "ear"}, + {"name": "Beholder", "level": 10, "item": "eyestalk"}, + {"name": "Black Pudding", "level": 10, "item": "saliva"}, + {"name": "Blink Dog", "level": 4, "item": "eyelid"}, + {"name": "Cub Scout", "level": 1, "item": "neckerchief"}, + {"name": "Girl Scout", "level": 2, "item": "cookie"}, + {"name": "Boy Scout", "level": 3, "item": "merit badge"}, + {"name": "Eagle Scout", "level": 4, "item": "merit badge"}, + {"name": "Bugbear", "level": 3, "item": "skin"}, + {"name": "Bugboar", "level": 3, "item": "tusk"}, + {"name": "Boogie", "level": 3, "item": "slime"}, + {"name": "Camel", "level": 2, "item": "hump"}, + {"name": "Carrion Crawler", "level": 3, "item": "egg"}, + {"name": "Catoblepas", "level": 6, "item": "neck"}, + {"name": "Centaur", "level": 4, "item": "rib"}, + {"name": "Centipede", "level": 0, "item": "leg"}, + {"name": "Cockatrice", "level": 5, "item": "wattle"}, + {"name": "Couatl", "level": 9, "item": "wing"}, + {"name": "Crayfish", "level": 0, "item": "antenna"}, + {"name": "Demogorgon", "level": 53, "item": "tentacle"}, + {"name": "Jubilex", "level": 17, "item": "gel"}, + {"name": "Manes", "level": 1, "item": "tooth"}, + {"name": "Orcus", "level": 27, "item": "wand"}, + {"name": "Succubus", "level": 6, "item": "bra"}, + {"name": "Vrock", "level": 8, "item": "neck"}, + {"name": "Hezrou", "level": 9, "item": "leg"}, + {"name": "Glabrezu", "level": 10, "item": "collar"}, + {"name": "Nalfeshnee", "level": 11, "item": "tusk"}, + {"name": "Marilith", "level": 7, "item": "arm"}, + {"name": "Balor", "level": 8, "item": "whip"}, + {"name": "Yeenoghu", "level": 25, "item": "flail"}, + {"name": "Asmodeus", "level": 52, "item": "leathers"}, + {"name": "Baalzebul", "level": 43, "item": "pants"}, + {"name": "Barbed Devil", "level": 8, "item": "flame"}, + {"name": "Bone Devil", "level": 9, "item": "hook"}, + {"name": "Dispater", "level": 30, "item": "matches"}, + {"name": "Erinyes", "level": 6, "item": "thong"}, + {"name": "Geryon", "level": 30, "item": "cornucopia"}, + {"name": "Malebranche", "level": 5, "item": "fork"}, + {"name": "Ice Devil", "level": 11, "item": "snow"}, + {"name": "Lemure", "level": 3, "item": "blob"}, + {"name": "Pit Fiend", "level": 13, "item": "seed"}, + {"name": "Ankylosaurus", "level": 9, "item": "tail"}, + {"name": "Brontosaurus", "level": 30, "item": "brain"}, + {"name": "Diplodocus", "level": 24, "item": "fin"}, + {"name": "Elasmosaurus", "level": 15, "item": "neck"}, + {"name": "Gorgosaurus", "level": 13, "item": "arm"}, + {"name": "Iguanadon", "level": 6, "item": "thumb"}, + {"name": "Megalosaurus", "level": 12, "item": "jaw"}, + {"name": "Monoclonius", "level": 8, "item": "horn"}, + {"name": "Pentasaurus", "level": 12, "item": "head"}, + {"name": "Stegosaurus", "level": 18, "item": "plate"}, + {"name": "Triceratops", "level": 16, "item": "horn"}, + {"name": "Tyrannosaurus Rex", "level": 18, "item": "forearm"}, + {"name": "Djinn", "level": 7, "item": "lamp"}, + {"name": "Doppelganger", "level": 4, "item": "face"}, + {"name": "Black Dragon", "level": 7, "item": null}, + {"name": "Plaid Dragon", "level": 7, "item": "sporrin"}, + {"name": "Blue Dragon", "level": 9, "item": null}, + {"name": "Beige Dragon", "level": 9, "item": null}, + {"name": "Brass Dragon", "level": 7, "item": "pole"}, + {"name": "Tin Dragon", "level": 8, "item": null}, + {"name": "Bronze Dragon", "level": 9, "item": "medal"}, + {"name": "Chromatic Dragon", "level": 16, "item": "scale"}, + {"name": "Copper Dragon", "level": 8, "item": "loafer"}, + {"name": "Gold Dragon", "level": 8, "item": "filling"}, + {"name": "Green Dragon", "level": 8, "item": null}, + {"name": "Platinum Dragon", "level": 21, "item": null}, + {"name": "Red Dragon", "level": 10, "item": "cocktail"}, + {"name": "Silver Dragon", "level": 10, "item": null}, + {"name": "White Dragon", "level": 6, "item": "tooth"}, + {"name": "Dragon Turtle", "level": 13, "item": "shell"}, + {"name": "Dryad", "level": 2, "item": "acorn"}, + {"name": "Dwarf", "level": 1, "item": "drawers"}, + {"name": "Eel", "level": 2, "item": "sashimi"}, + {"name": "Efreet", "level": 10, "item": "cinder"}, + {"name": "Sand Elemental", "level": 8, "item": "glass"}, + {"name": "Bacon Elemental", "level": 10, "item": "bit"}, + {"name": "Porn Elemental", "level": 12, "item": "lube"}, + {"name": "Cheese Elemental", "level": 14, "item": "curd"}, + {"name": "Hair Elemental", "level": 16, "item": "follicle"}, + {"name": "Swamp Elf", "level": 1, "item": "lilypad"}, + {"name": "Brown Elf", "level": 1, "item": "tusk"}, + {"name": "Sea Elf", "level": 1, "item": "jerkin"}, + {"name": "Ettin", "level": 10, "item": "fur"}, + {"name": "Frog", "level": 0, "item": "leg"}, + {"name": "Violet Fungi", "level": 3, "item": "spore"}, + {"name": "Gargoyle", "level": 4, "item": "gravel"}, + {"name": "Gelatinous Cube", "level": 4, "item": "jam"}, + {"name": "Ghast", "level": 4, "item": "vomit"}, + {"name": "Ghost", "level": 10, "item": null}, + {"name": "Ghoul", "level": 2, "item": "muscle"}, + {"name": "Humidity Giant", "level": 12, "item": "drops"}, + {"name": "Beef Giant", "level": 11, "item": "steak"}, + {"name": "Quartz Giant", "level": 10, "item": "crystal"}, + {"name": "Porcelain Giant", "level": 9, "item": "fixture"}, + {"name": "Rice Giant", "level": 8, "item": "grain"}, + {"name": "Cloud Giant", "level": 12, "item": "condensation"}, + {"name": "Fire Giant", "level": 11, "item": "cigarettes"}, + {"name": "Frost Giant", "level": 10, "item": "snowman"}, + {"name": "Hill Giant", "level": 8, "item": "corpse"}, + {"name": "Stone Giant", "level": 9, "item": "hatchling"}, + {"name": "Storm Giant", "level": 15, "item": "barometer"}, + {"name": "Mini Giant", "level": 4, "item": "pompadour"}, + {"name": "Gnoll", "level": 2, "item": "collar"}, + {"name": "Gnome", "level": 1, "item": "hat"}, + {"name": "Goblin", "level": 1, "item": "ear"}, + {"name": "Grid Bug", "level": 1, "item": "carapace"}, + {"name": "Jellyrock", "level": 9, "item": "seedling"}, + {"name": "Beer Golem", "level": 15, "item": "foam"}, + {"name": "Oxygen Golem", "level": 17, "item": "platelet"}, + {"name": "Cardboard Golem", "level": 14, "item": "recycling"}, + {"name": "Rubber Golem", "level": 16, "item": "ball"}, + {"name": "Leather Golem", "level": 15, "item": "fob"}, + {"name": "Gorgon", "level": 8, "item": "testicle"}, + {"name": "Gray Ooze", "level": 3, "item": "gravy"}, + {"name": "Green Slime", "level": 2, "item": "sample"}, + {"name": "Griffon", "level": 7, "item": "nest"}, + {"name": "Banshee", "level": 7, "item": "larynx"}, + {"name": "Harpy", "level": 3, "item": "mascara"}, + {"name": "Hell Hound", "level": 5, "item": "tongue"}, + {"name": "Hippocampus", "level": 4, "item": "mane"}, + {"name": "Hippogriff", "level": 3, "item": "egg"}, + {"name": "Hobgoblin", "level": 1, "item": "patella"}, + {"name": "Homunculus", "level": 2, "item": "fluid"}, + {"name": "Hydra", "level": 8, "item": "gyrum"}, + {"name": "Imp", "level": 2, "item": "tail"}, + {"name": "Invisible Stalker", "level": 8, "item": null}, + {"name": "Iron Peasant", "level": 3, "item": "chaff"}, + {"name": "Jumpskin", "level": 3, "item": "shin"}, + {"name": "Kobold", "level": 1, "item": "penis"}, + {"name": "Leprechaun", "level": 1, "item": "wallet"}, + {"name": "Leucrotta", "level": 6, "item": "hoof"}, + {"name": "Lich", "level": 11, "item": "crown"}, + {"name": "Lizard Man", "level": 2, "item": "tail"}, + {"name": "Lurker", "level": 10, "item": "sac"}, + {"name": "Manticore", "level": 6, "item": "spike"}, + {"name": "Mastodon", "level": 12, "item": "tusk"}, + {"name": "Medusa", "level": 6, "item": "eye"}, + {"name": "Multicell", "level": 2, "item": "dendrite"}, + {"name": "Pirate", "level": 1, "item": "booty"}, + {"name": "Berserker", "level": 1, "item": "shirt"}, + {"name": "Caveman", "level": 2, "item": "club"}, + {"name": "Dervish", "level": 1, "item": "robe"}, + {"name": "Merman", "level": 1, "item": "trident"}, + {"name": "Mermaid", "level": 1, "item": "gills"}, + {"name": "Mimic", "level": 9, "item": "hinge"}, + {"name": "Mind Flayer", "level": 8, "item": "tentacle"}, + {"name": "Minotaur", "level": 6, "item": "map"}, + {"name": "Yellow Mold", "level": 1, "item": "spore"}, + {"name": "Morkoth", "level": 7, "item": "teeth"}, + {"name": "Mummy", "level": 6, "item": "gauze"}, + {"name": "Naga", "level": 9, "item": "rattle"}, + {"name": "Nebbish", "level": 1, "item": "belly"}, + {"name": "Neo-Otyugh", "level": 11, "item": "organ "}, + {"name": "Nixie", "level": 1, "item": "webbing"}, + {"name": "Nymph", "level": 3, "item": "hanky"}, + {"name": "Ochre Jelly", "level": 6, "item": "nucleus"}, + {"name": "Octopus", "level": 2, "item": "beak"}, + {"name": "Ogre", "level": 4, "item": "talon"}, + {"name": "Ogre Mage", "level": 5, "item": "apparel"}, + {"name": "Orc", "level": 1, "item": "snout"}, + {"name": "Otyugh", "level": 7, "item": "organ"}, + {"name": "Owlbear", "level": 5, "item": "feather"}, + {"name": "Pegasus", "level": 4, "item": "aileron"}, + {"name": "Peryton", "level": 4, "item": "antler"}, + {"name": "Piercer", "level": 3, "item": "tip"}, + {"name": "Pixie", "level": 1, "item": "dust"}, + {"name": "Man-o-war", "level": 3, "item": "tentacle"}, + {"name": "Purple Worm", "level": 15, "item": "dung"}, + {"name": "Quasit", "level": 3, "item": "tail"}, + {"name": "Rakshasa", "level": 7, "item": "pajamas"}, + {"name": "Rat", "level": 0, "item": "tail"}, + {"name": "Remorhaz", "level": 11, "item": "protrusion"}, + {"name": "Roc", "level": 18, "item": "wing"}, + {"name": "Roper", "level": 11, "item": "twine"}, + {"name": "Rot Grub", "level": 1, "item": "eggsac"}, + {"name": "Rust Monster", "level": 5, "item": "shavings"}, + {"name": "Satyr", "level": 5, "item": "hoof"}, + {"name": "Sea Hag", "level": 3, "item": "wart"}, + {"name": "Silkie", "level": 3, "item": "fur"}, + {"name": "Shadow", "level": 3, "item": "silhouette"}, + {"name": "Shambling Mound", "level": 10, "item": "mulch"}, + {"name": "Shedu", "level": 9, "item": "hoof"}, + {"name": "Shrieker", "level": 3, "item": "stalk"}, + {"name": "Skeleton", "level": 1, "item": "clavicle"}, + {"name": "Spectre", "level": 7, "item": "vestige"}, + {"name": "Sphinx", "level": 10, "item": "paw"}, + {"name": "Spider", "level": 0, "item": "web"}, + {"name": "Sprite", "level": 1, "item": "can"}, + {"name": "Stirge", "level": 1, "item": "proboscis"}, + {"name": "Stun Bear", "level": 5, "item": "tooth"}, + {"name": "Stun Worm", "level": 2, "item": "trode"}, + {"name": "Su-monster", "level": 5, "item": "tail"}, + {"name": "Sylph", "level": 3, "item": "thigh"}, + {"name": "Titan", "level": 20, "item": "sandal"}, + {"name": "Trapper", "level": 12, "item": "shag"}, + {"name": "Treant", "level": 10, "item": "acorn"}, + {"name": "Triton", "level": 3, "item": "scale"}, + {"name": "Troglodyte", "level": 2, "item": "tail"}, + {"name": "Troll", "level": 6, "item": "hide"}, + {"name": "Umber Hulk", "level": 8, "item": "claw"}, + {"name": "Unicorn", "level": 4, "item": "blood"}, + {"name": "Vampire", "level": 8, "item": "pancreas"}, + {"name": "Wight", "level": 4, "item": "lung"}, + {"name": "Will-o'-the-Wisp", "level": 9, "item": "wisp"}, + {"name": "Wraith", "level": 5, "item": "finger"}, + {"name": "Wyvern", "level": 7, "item": "wing"}, + {"name": "Xorn", "level": 7, "item": "jaw"}, + {"name": "Yeti", "level": 4, "item": "fur"}, + {"name": "Zombie", "level": 2, "item": "forehead"}, + {"name": "Wasp", "level": 0, "item": "stinger"}, + {"name": "Rat", "level": 1, "item": "tail"}, + {"name": "Bunny", "level": 0, "item": "ear"}, + {"name": "Moth", "level": 0, "item": "dust"}, + {"name": "Beagle", "level": 0, "item": "collar"}, + {"name": "Midge", "level": 0, "item": "corpse"}, + {"name": "Ostrich", "level": 1, "item": "beak"}, + {"name": "Billy Goat", "level": 1, "item": "beard"}, + {"name": "Bat", "level": 1, "item": "wing"}, + {"name": "Koala", "level": 2, "item": "heart"}, + {"name": "Wolf", "level": 2, "item": "paw"}, + {"name": "Whippet", "level": 2, "item": "collar"}, + {"name": "Uruk", "level": 2, "item": "boot"}, + {"name": "Poroid", "level": 4, "item": "node"}, + {"name": "Moakum", "level": 8, "item": "frenum"}, + {"name": "Fly", "level": 0, "item": null}, + {"name": "Hogbird", "level": 3, "item": "curl"}, + {"name": "Wolog", "level": 4, "item": "lemma"} +] diff --git a/pqcli/data/offense_attrib.json b/pqcli/data/offense_attrib.json new file mode 100644 index 0000000..65c01a2 --- /dev/null +++ b/pqcli/data/offense_attrib.json @@ -0,0 +1,13 @@ +[ + {"name": "Polished", "quality": 1}, + {"name": "Serrated", "quality": 1}, + {"name": "Heavy", "quality": 1}, + {"name": "Pronged", "quality": 2}, + {"name": "Steely", "quality": 2}, + {"name": "Vicious", "quality": 3}, + {"name": "Venomed", "quality": 4}, + {"name": "Stabbity", "quality": 4}, + {"name": "Dancing", "quality": 5}, + {"name": "Invisible", "quality": 6}, + {"name": "Vorpal", "quality": 7} +] diff --git a/pqcli/data/offense_bad.json b/pqcli/data/offense_bad.json new file mode 100644 index 0000000..0fba6ba --- /dev/null +++ b/pqcli/data/offense_bad.json @@ -0,0 +1,11 @@ +[ + {"name": "Dull", "quality": -2}, + {"name": "Tarnished", "quality": -1}, + {"name": "Rusty", "quality": -3}, + {"name": "Padded", "quality": -5}, + {"name": "Bent", "quality": -4}, + {"name": "Mini", "quality": -4}, + {"name": "Rubber", "quality": -6}, + {"name": "Nerf", "quality": -7}, + {"name": "Unbalanced", "quality": -2} +] diff --git a/pqcli/data/races.json b/pqcli/data/races.json new file mode 100644 index 0000000..bda87dc --- /dev/null +++ b/pqcli/data/races.json @@ -0,0 +1,23 @@ +[ + {"name": "Half Orc", "attr": ["HP Max"]}, + {"name": "Half Man", "attr": ["CHA"]}, + {"name": "Half Halfling", "attr": ["DEX"]}, + {"name": "Double Hobbit", "attr": ["STR"]}, + {"name": "Hob-Hobbit", "attr": ["DEX", "CON"]}, + {"name": "Low Elf", "attr": ["CON"]}, + {"name": "Dung Elf", "attr": ["WIS"]}, + {"name": "Talking Pony", "attr": ["MP Max", "INT"]}, + {"name": "Gyrognome", "attr": ["DEX"]}, + {"name": "Lesser Dwarf", "attr": ["CON"]}, + {"name": "Crested Dwarf", "attr": ["CHA"]}, + {"name": "Eel Man", "attr": ["DEX"]}, + {"name": "Panda Man", "attr": ["CON", "STR"]}, + {"name": "Trans-Kobold", "attr": ["WIS"]}, + {"name": "Enchanted Motorcycle", "attr": ["MP Max"]}, + {"name": "Will o' the Wisp", "attr": ["WIS"]}, + {"name": "Battle-Finch", "attr": ["DEX", "INT"]}, + {"name": "Double Wookiee", "attr": ["STR"]}, + {"name": "Skraeling", "attr": ["WIS"]}, + {"name": "Demicanadian", "attr": ["CON"]}, + {"name": "Land Squid", "attr": ["STR", "HP Max"]} +] diff --git a/pqcli/data/shields.json b/pqcli/data/shields.json new file mode 100644 index 0000000..7da975d --- /dev/null +++ b/pqcli/data/shields.json @@ -0,0 +1,18 @@ +[ + {"name": "Parasol", "quality": 0}, + {"name": "Pie Plate", "quality": 1}, + {"name": "Garbage Can Lid", "quality": 2}, + {"name": "Buckler", "quality": 3}, + {"name": "Plexiglass", "quality": 4}, + {"name": "Fender", "quality": 4}, + {"name": "Round Shield", "quality": 5}, + {"name": "Carapace", "quality": 5}, + {"name": "Scutum", "quality": 6}, + {"name": "Propugner", "quality": 6}, + {"name": "Kite Shield", "quality": 7}, + {"name": "Pavise", "quality": 8}, + {"name": "Tower Shield", "quality": 9}, + {"name": "Baroque Shield", "quality": 11}, + {"name": "Aegis", "quality": 12}, + {"name": "Magnetic Field", "quality": 18} +] diff --git a/pqcli/data/specials.json b/pqcli/data/specials.json new file mode 100644 index 0000000..55cbbeb --- /dev/null +++ b/pqcli/data/specials.json @@ -0,0 +1,39 @@ +[ + "Diadem", + "Festoon", + "Gemstone", + "Phial", + "Tiara", + "Scabbard", + "Arrow", + "Lens", + "Lamp", + "Hymnal", + "Fleece", + "Laurel", + "Brooch", + "Gimlet", + "Cobble", + "Albatross", + "Brazier", + "Bandolier", + "Tome", + "Garnet", + "Amethyst", + "Candelabra", + "Corset", + "Sphere", + "Sceptre", + "Ankh", + "Talisman", + "Orb", + "Gammel", + "Ornament", + "Brocade", + "Galoon", + "Bijou", + "Spangle", + "Gimcrack", + "Hood", + "Vulpeculum" +] diff --git a/pqcli/data/spells.json b/pqcli/data/spells.json new file mode 100644 index 0000000..4bd0267 --- /dev/null +++ b/pqcli/data/spells.json @@ -0,0 +1,49 @@ +[ + "Slime Finger", + "Rabbit Punch", + "Hastiness", + "Good Move", + "Sadness", + "Seasick", + "Shoelaces", + "Inoculate", + "Cone of Annoyance", + "Magnetic Orb", + "Invisible Hands", + "Revolting Cloud", + "Aqueous Humor", + "Spectral Miasma", + "Clever Fellow", + "Lockjaw", + "History Lesson", + "Hydrophobia", + "Big Sister", + "Cone of Paste", + "Mulligan", + "Nestor's Bright Idea", + "Holy Batpole", + "Tumor (Benign)", + "Braingate", + "Summon a Bitch", + "Nonplus", + "Animate Nightstand", + "Eye of the Troglodyte", + "Curse Name", + "Dropsy", + "Vitreous Humor", + "Roger's Grand Illusion", + "Covet", + "Black Idaho", + "Astral Miasma", + "Spectral Oyster", + "Acrid Hands", + "Angioplasty", + "Grognor's Big Day Off", + "Tumor (Malignant)", + "Animate Tunic", + "Ursine Armor", + "Holy Roller", + "Tonsillectomy", + "Curse Family", + "Infinite Confusion" +] diff --git a/pqcli/data/titles.json b/pqcli/data/titles.json new file mode 100644 index 0000000..7c84caf --- /dev/null +++ b/pqcli/data/titles.json @@ -0,0 +1,11 @@ +[ + "Mr.", + "Mrs.", + "Sir", + "Sgt.", + "Ms.", + "Captain", + "Chief", + "Admiral", + "Saint" +] diff --git a/pqcli/data/weapons.json b/pqcli/data/weapons.json new file mode 100644 index 0000000..049aeea --- /dev/null +++ b/pqcli/data/weapons.json @@ -0,0 +1,41 @@ +[ + {"name": "Stick", "quality": 0}, + {"name": "Broken Bottle", "quality": 1}, + {"name": "Shiv", "quality": 1}, + {"name": "Sprig", "quality": 1}, + {"name": "Oxgoad", "quality": 1}, + {"name": "Eelspear", "quality": 2}, + {"name": "Bowie Knife", "quality": 2}, + {"name": "Claw Hammer", "quality": 2}, + {"name": "Handpeen", "quality": 2}, + {"name": "Andiron", "quality": 3}, + {"name": "Hatchet", "quality": 3}, + {"name": "Tomahawk", "quality": 3}, + {"name": "Hackbarm", "quality": 3}, + {"name": "Crowbar", "quality": 4}, + {"name": "Mace", "quality": 4}, + {"name": "Battleadze", "quality": 4}, + {"name": "Leafmace", "quality": 5}, + {"name": "Shortsword", "quality": 5}, + {"name": "Longiron", "quality": 5}, + {"name": "Poachard", "quality": 5}, + {"name": "Baselard", "quality": 5}, + {"name": "Whinyard", "quality": 6}, + {"name": "Blunderbuss", "quality": 6}, + {"name": "Longsword", "quality": 6}, + {"name": "Crankbow", "quality": 6}, + {"name": "Blibo", "quality": 7}, + {"name": "Broadsword", "quality": 7}, + {"name": "Kreen", "quality": 7}, + {"name": "Warhammer", "quality": 7}, + {"name": "Morning Star", "quality": 8}, + {"name": "Pole-adze", "quality": 8}, + {"name": "Spontoon", "quality": 8}, + {"name": "Bastard Sword", "quality": 9}, + {"name": "Peen-arm", "quality": 9}, + {"name": "Culverin", "quality": 10}, + {"name": "Lance", "quality": 10}, + {"name": "Halberd", "quality": 11}, + {"name": "Poleax", "quality": 12}, + {"name": "Bandyclef", "quality": 15} +] diff --git a/pqcli/game_data.py b/pqcli/game_data.py new file mode 100644 index 0000000..3ec8e07 --- /dev/null +++ b/pqcli/game_data.py @@ -0,0 +1,249 @@ +import json +import sys +import typing as T +from dataclasses import dataclass, fields +from importlib import resources +from pathlib import Path + +from pqcli.config import ( + Class, + EquipmentPreset, + Modifier, + Monster, + Race, + StatType, +) + +TARGET_PLACEHOLDER = "{target}" + + +class DataError(Exception): + pass + + +@dataclass(frozen=True) +class GameData: + spells: T.Tuple[str, ...] + offense_attrib: T.Tuple[Modifier, ...] + defense_attrib: T.Tuple[Modifier, ...] + offense_bad: T.Tuple[Modifier, ...] + defense_bad: T.Tuple[Modifier, ...] + shields: T.Tuple[EquipmentPreset, ...] + armors: T.Tuple[EquipmentPreset, ...] + weapons: T.Tuple[EquipmentPreset, ...] + specials: T.Tuple[str, ...] + item_attrib: T.Tuple[str, ...] + item_ofs: T.Tuple[str, ...] + boring_items: T.Tuple[str, ...] + monsters: T.Tuple[Monster, ...] + races: T.Tuple[Race, ...] + classes: T.Tuple[Class, ...] + titles: T.Tuple[str, ...] + impressive_titles: T.Tuple[str, ...] + combat_verbs: T.Tuple[str, ...] + + +DATA_FILES = tuple(field.name for field in fields(GameData)) + + +class _Context: + def __init__(self, path: str) -> None: + self.path = path + + def error(self, message: str) -> DataError: + return DataError(f"{self.path}: {message}") + + +def _check_keys( + ctx: _Context, where: str, obj: T.Any, keys: T.Sequence[str] +) -> T.Dict[str, T.Any]: + if not isinstance(obj, dict): + raise ctx.error(f"{where} must be an object") + missing = [key for key in keys if key not in obj] + if missing: + raise ctx.error(f"{where} is missing {', '.join(map(repr, missing))}") + extra = [key for key in obj if key not in keys] + if extra: + raise ctx.error(f"{where} has unknown {', '.join(map(repr, extra))}") + return obj + + +def _str(ctx: _Context, where: str, value: T.Any) -> str: + if not isinstance(value, str): + raise ctx.error(f"{where} must be a string") + return value + + +def _int(ctx: _Context, where: str, value: T.Any) -> int: + if isinstance(value, bool) or not isinstance(value, int): + raise ctx.error(f"{where} must be an integer") + return value + + +def _stat(ctx: _Context, where: str, value: T.Any) -> StatType: + _str(ctx, where, value) + for stat in StatType: + if value.lower() in (stat.value.lower(), stat.name): + return stat + allowed = ", ".join(stat.value for stat in StatType) + raise ctx.error(f"{where}: unknown stat {value!r} (use one of {allowed})") + + +def _parse_str(ctx: _Context, where: str, item: T.Any) -> str: + return _str(ctx, where, item) + + +def _parse_verb(ctx: _Context, where: str, item: T.Any) -> str: + verb = _str(ctx, where, item) + if TARGET_PLACEHOLDER not in verb: + raise ctx.error(f"{where} must contain {TARGET_PLACEHOLDER}") + return verb + + +def _parse_modifier(ctx: _Context, where: str, item: T.Any) -> Modifier: + obj = _check_keys(ctx, where, item, ["name", "quality"]) + return Modifier( + _str(ctx, f"{where}.name", obj["name"]), + _int(ctx, f"{where}.quality", obj["quality"]), + ) + + +def _parse_preset(ctx: _Context, where: str, item: T.Any) -> EquipmentPreset: + obj = _check_keys(ctx, where, item, ["name", "quality"]) + return EquipmentPreset( + _str(ctx, f"{where}.name", obj["name"]), + _int(ctx, f"{where}.quality", obj["quality"]), + ) + + +def _parse_monster(ctx: _Context, where: str, item: T.Any) -> Monster: + obj = _check_keys(ctx, where, item, ["name", "level", "item"]) + loot = obj["item"] + return Monster( + _str(ctx, f"{where}.name", obj["name"]), + _int(ctx, f"{where}.level", obj["level"]), + None if loot is None else _str(ctx, f"{where}.item", loot), + ) + + +def _parse_attrs(ctx: _Context, where: str, value: T.Any) -> T.List[StatType]: + if not isinstance(value, list): + raise ctx.error(f"{where} must be a list of stats") + return [_stat(ctx, f"{where}[{i}]", stat) for i, stat in enumerate(value)] + + +def _parse_race(ctx: _Context, where: str, item: T.Any) -> Race: + obj = _check_keys(ctx, where, item, ["name", "attr"]) + return Race( + _str(ctx, f"{where}.name", obj["name"]), + _parse_attrs(ctx, f"{where}.attr", obj["attr"]), + ) + + +def _parse_class(ctx: _Context, where: str, item: T.Any) -> Class: + obj = _check_keys(ctx, where, item, ["name", "attr"]) + return Class( + _str(ctx, f"{where}.name", obj["name"]), + _parse_attrs(ctx, f"{where}.attr", obj["attr"]), + ) + + +_PARSERS: T.Dict[str, T.Callable[[_Context, str, T.Any], T.Any]] = { + "spells": _parse_str, + "offense_attrib": _parse_modifier, + "defense_attrib": _parse_modifier, + "offense_bad": _parse_modifier, + "defense_bad": _parse_modifier, + "shields": _parse_preset, + "armors": _parse_preset, + "weapons": _parse_preset, + "specials": _parse_str, + "item_attrib": _parse_str, + "item_ofs": _parse_str, + "boring_items": _parse_str, + "monsters": _parse_monster, + "races": _parse_race, + "classes": _parse_class, + "titles": _parse_str, + "impressive_titles": _parse_str, + "combat_verbs": _parse_verb, +} +assert set(_PARSERS) == set(DATA_FILES) + + +def _parse(name: str, path: str, text: str) -> T.Tuple[T.Any, ...]: + ctx = _Context(path) + try: + value = json.loads(text) + except json.JSONDecodeError as ex: + raise ctx.error(f"invalid JSON: {ex}") from ex + if not isinstance(value, list): + raise ctx.error("must be a list") + if not value: + raise ctx.error("must not be empty") + parser = _PARSERS[name] + return tuple(parser(ctx, f"[{i}]", item) for i, item in enumerate(value)) + + +def _builtin_dir() -> T.Any: + return resources.files("pqcli") / "data" + + +def _read_text(path: T.Any, display: str) -> str: + try: + return T.cast(str, path.read_text(encoding="utf-8")) + except UnicodeDecodeError as ex: + raise DataError(f"{display}: not valid UTF-8: {ex}") from ex + except OSError as ex: + raise DataError(f"{display}: cannot read: {ex}") from ex + + +def load_game_data(override_dir: T.Optional[Path] = None) -> GameData: + """Load the built-in word lists, replacing any that override_dir has. + + A missing override_dir is fine. Each .json in it replaces the + whole built-in list of that name. + """ + overrides: T.Set[str] = set() + if override_dir is not None and override_dir.exists(): + if not override_dir.is_dir(): + raise DataError(f"{override_dir}: not a directory") + for path in sorted(override_dir.glob("*.json")): + if path.stem in DATA_FILES: + overrides.add(path.stem) + else: + print( + f"pqcli: ignoring {path}: not a known data file " + f"(known: {', '.join(DATA_FILES)})", + file=sys.stderr, + ) + + values: T.Dict[str, T.Tuple[T.Any, ...]] = {} + for name in DATA_FILES: + if name in overrides: + assert override_dir is not None + source: T.Any = override_dir / f"{name}.json" + display = str(source) + else: + source = _builtin_dir() / f"{name}.json" + display = f"/{name}.json" + values[name] = _parse(name, display, _read_text(source, display)) + return GameData(**values) + + +def dump_builtin_data(target_dir: Path) -> T.List[Path]: + """Copy the built-in data files to target_dir, never overwriting.""" + targets = [target_dir / f"{name}.json" for name in DATA_FILES] + existing = [path for path in targets if path.exists()] + if existing: + raise DataError( + "refusing to overwrite " + + ", ".join(str(path) for path in existing) + ) + target_dir.mkdir(parents=True, exist_ok=True) + for name, path in zip(DATA_FILES, targets): + source = _builtin_dir() / f"{name}.json" + path.write_text( + _read_text(source, f"/{name}.json"), encoding="utf-8" + ) + return targets diff --git a/pqcli/mechanic.py b/pqcli/mechanic.py index cadf235..bb7807b 100644 --- a/pqcli/mechanic.py +++ b/pqcli/mechanic.py @@ -1,4 +1,5 @@ import datetime +import functools import itertools import logging import math @@ -8,24 +9,7 @@ from dataclasses import dataclass from pqcli import random from pqcli.config import ( - ARMORS, - BORING_ITEMS, - CLASSES, - DEFENSE_ATTRIB, - DEFENSE_BAD, - IMPRESSIVE_TITLES, - ITEM_ATTRIB, - ITEM_OFS, - MONSTERS, - OFFENSE_ATTRIB, - OFFENSE_BAD, PRIME_STATS, - RACES, - SHIELDS, - SPECIALS, - SPELLS, - TITLES, - WEAPONS, Class, EquipmentPreset, EquipmentType, @@ -34,6 +18,7 @@ from pqcli.config import ( Race, StatType, ) +from pqcli.game_data import TARGET_PLACEHOLDER, GameData from pqcli.lingo import ( act_name, big, @@ -388,7 +373,7 @@ class Player(SignalMixin): def equip_price(self) -> int: return 5 * (self.level**2) + 10 * self.level + 20 - def level_up(self) -> None: + def level_up(self, data: GameData) -> None: self.level += 1 logger.info("Leveled up to level %d!", self.level) self.stats.increment( @@ -401,7 +386,7 @@ class Player(SignalMixin): ) self.win_stat() self.win_stat() - self.win_spell() + self.win_spell(data) self.exp_bar.reset(level_up_time(self.level)) self.emit("level_up") @@ -427,31 +412,34 @@ class Player(SignalMixin): self.inventory.set_capacity(10 + self.stats[StatType.strength]) return True - def win_spell(self) -> None: + def win_spell(self, data: GameData) -> None: + spells = data.spells self.spell_book.add( - SPELLS[ + spells[ random.below_low( - min(self.stats[StatType.wisdom] + self.level, len(SPELLS)) + min(self.stats[StatType.wisdom] + self.level, len(spells)) ) ], 1, ) - def win_equipment(self) -> None: + def win_equipment(self, data: GameData) -> None: choice = random.choice(list(EquipmentType)) - stuff: T.List[EquipmentPreset] - better: T.List[Modifier] - worse: T.List[Modifier] + stuff: T.Sequence[EquipmentPreset] + better: T.Sequence[Modifier] + worse: T.Sequence[Modifier] if choice == EquipmentType.weapon: - stuff = WEAPONS - better = OFFENSE_ATTRIB - worse = OFFENSE_BAD + stuff = data.weapons + better = data.offense_attrib + worse = data.offense_bad else: - stuff = SHIELDS if choice == EquipmentType.shield else ARMORS - better = DEFENSE_ATTRIB - worse = DEFENSE_BAD + stuff = ( + data.shields if choice == EquipmentType.shield else data.armors + ) + better = data.defense_attrib + worse = data.defense_bad equipment = pick_equipment(stuff, self.level) name = equipment.name @@ -479,13 +467,14 @@ class Player(SignalMixin): logger.info("Gained %s %s", name, choice.value) self.equipment.put(choice, name) - def win_item(self) -> None: - self.inventory.add(special_item(), 1) + def win_item(self, data: GameData) -> None: + self.inventory.add(special_item(data), 1) class Simulation: - def __init__(self, player: Player) -> None: + def __init__(self, player: Player, data: GameData) -> None: self.player = player + self.data = data self.last_tick = datetime.datetime.now() def tick(self, elapsed: float = 100.0) -> None: @@ -526,7 +515,7 @@ class Simulation: gain = self.player.task and isinstance(self.player.task, KillTask) if gain: if self.player.exp_bar.done: - self.player.level_up() + self.player.level_up(self.data) else: self.player.exp_bar.increment(self.player.task_bar.max_ / 1000) @@ -561,7 +550,7 @@ class Simulation: or self.player.task.monster.item is None ): # npc - self.player.win_item() + self.player.win_item(self.data) elif self.player.task.monster.item: self.player.inventory.add( ( @@ -575,7 +564,7 @@ class Simulation: elif isinstance(self.player.task, BuyTask): # buy some equipment self.player.inventory.add_gold(-self.player.equip_price()) - self.player.win_equipment() + self.player.win_equipment(self.data) elif isinstance(self.player.task, (HeadingToMarketTask, SellTask)): if isinstance(self.player.task, SellTask): @@ -623,7 +612,9 @@ class Simulation: else: self.player.set_task( monster_task( - self.player.level, self.player.quest_book.monster + self.data, + self.player.level, + self.player.quest_book.monster, ) ) @@ -633,8 +624,8 @@ class Simulation: 60 * 60 * (1 + 5 * self.player.quest_book.act) ) if self.player.quest_book.act > 1: - self.player.win_item() - self.player.win_equipment() + self.player.win_item(self.data) + self.player.win_equipment(self.data) def complete_quest(self) -> None: self.player.quest_book.quest_bar.reset(50 + random.below_low(1000)) @@ -644,10 +635,10 @@ class Simulation: ) random.choice( [ - self.player.win_spell, - self.player.win_equipment, + functools.partial(self.player.win_spell, self.data), + functools.partial(self.player.win_equipment, self.data), self.player.win_stat, - self.player.win_item, + functools.partial(self.player.win_item, self.data), ] )() @@ -656,19 +647,21 @@ class Simulation: choice = random.below(5) if choice == 0: self.player.quest_book.monster = unnamed_monster( - self.player.level, iterations=3 + self.data, self.player.level, iterations=3 ) caption = "Exterminate " + definite( self.player.quest_book.monster.name, 2 ) elif choice == 1: - caption = "Seek " + definite(interesting_item(), 1) + caption = "Seek " + definite(interesting_item(self.data), 1) elif choice == 2: - caption = "Deliver this " + boring_item() + caption = "Deliver this " + boring_item(self.data) elif choice == 3: - caption = "Fetch me " + indefinite(boring_item(), 1) + caption = "Fetch me " + indefinite(boring_item(self.data), 1) elif choice == 4: - monster = unnamed_monster(self.player.level, iterations=1) + monster = unnamed_monster( + self.data, self.player.level, iterations=1 + ) caption = "Placate " + definite(monster.name, 2) else: raise AssertionError @@ -711,7 +704,7 @@ class Simulation: ) ) - nemesis = named_monster(self.player.level + 3) + nemesis = named_monster(self.data, self.player.level + 3) enqueue( RegularTask( @@ -760,7 +753,7 @@ class Simulation: ) elif choice == 2: - nemesis = impressive_guy() + nemesis = impressive_guy(self.data) enqueue( RegularTask( "Oh sweet relief! " @@ -777,7 +770,8 @@ class Simulation: ) enqueue( RegularTask( - f"You forget your {boring_item()} and go back to get it", + f"You forget your {boring_item(self.data)} " + "and go back to get it", 2000, ) ) @@ -807,46 +801,50 @@ class Simulation: ) -def special_item() -> str: - return interesting_item() + " of " + T.cast(str, random.choice(ITEM_OFS)) - - -def interesting_item() -> str: +def special_item(data: GameData) -> str: return ( - T.cast(str, random.choice(ITEM_ATTRIB)) - + " " - + T.cast(str, random.choice(SPECIALS)) + interesting_item(data) + + " of " + + T.cast(str, random.choice(data.item_ofs)) ) -def boring_item() -> str: - return T.cast(str, random.choice(BORING_ITEMS)) +def interesting_item(data: GameData) -> str: + return ( + T.cast(str, random.choice(data.item_attrib)) + + " " + + T.cast(str, random.choice(data.specials)) + ) -def impressive_guy() -> str: - return T.cast(str, random.choice(IMPRESSIVE_TITLES)) + ( - " of the " + T.cast(Race, random.choice(RACES)).name +def boring_item(data: GameData) -> str: + return T.cast(str, random.choice(data.boring_items)) + + +def impressive_guy(data: GameData) -> str: + return T.cast(str, random.choice(data.impressive_titles)) + ( + " of the " + T.cast(Race, random.choice(data.races)).name if random.below(2) else " of " + generate_name() ) -def unnamed_monster(level: int, iterations: int) -> Monster: - result = T.cast(Monster, random.choice(MONSTERS)) +def unnamed_monster(data: GameData, level: int, iterations: int) -> Monster: + result = T.cast(Monster, random.choice(data.monsters)) for _ in range(iterations): - alternative = T.cast(Monster, random.choice(MONSTERS)) + alternative = T.cast(Monster, random.choice(data.monsters)) if abs(level - alternative.level) < abs(level - result.level): result = alternative return result -def named_monster(level: int) -> str: - monster = unnamed_monster(level, iterations=4) +def named_monster(data: GameData, level: int) -> str: + monster = unnamed_monster(data, level, iterations=4) return generate_name() + " the " + monster.name def pick_equipment( - source: T.List[EquipmentPreset], goal: int + source: T.Sequence[EquipmentPreset], goal: int ) -> EquipmentPreset: result = T.cast(EquipmentPreset, random.choice(source)) for _ in range(5): @@ -857,7 +855,7 @@ def pick_equipment( def monster_task( - player_level: int, quest_monster: T.Optional[Monster] + data: GameData, player_level: int, quest_monster: T.Optional[Monster] ) -> KillTask: level = player_level for _ in range(level): @@ -870,12 +868,14 @@ def monster_task( monster: T.Optional[Monster] = None if random.odds(1, 25): # use an NPC every once in a while - race = random.choice(RACES) + race = random.choice(data.races) if random.odds(1, 2): - result = "passing " + race.name + " " + random.choice(CLASSES).name + result = ( + "passing " + race.name + " " + random.choice(data.classes).name + ) else: result = ( - random.choice_low(TITLES) + random.choice_low(data.titles) + " " + generate_name() + " the " @@ -890,7 +890,7 @@ def monster_task( lev = monster.level else: # pick the monster out of so many random ones closest to the level we want - monster = unnamed_monster(level, iterations=5) + monster = unnamed_monster(data, level, iterations=5) result = monster.name lev = monster.level @@ -929,7 +929,14 @@ def monster_task( result = indefinite(result, qty) duration = (2 * 3 * level * 1000) // player_level - return KillTask(f"Executing {result}", duration, monster=monster) + # with a single verb, skip the roll so the stock RNG stream is unchanged + verb = ( + data.combat_verbs[0] + if len(data.combat_verbs) == 1 + else T.cast(str, random.choice(data.combat_verbs)) + ) + description = verb.replace(TARGET_PLACEHOLDER, result) + return KillTask(description, duration, monster=monster) class StatsBuilder: diff --git a/pqcli/ui/base.py b/pqcli/ui/base.py index 6f06f18..2129cf2 100644 --- a/pqcli/ui/base.py +++ b/pqcli/ui/base.py @@ -2,6 +2,7 @@ import abc import argparse import typing as T +from pqcli.game_data import GameData from pqcli.mechanic import Player from pqcli.roster import Roster @@ -12,8 +13,10 @@ class BaseUserInterface(abc.ABC): roster: Roster, player: T.Optional[Player], args: argparse.Namespace, + data: GameData, ) -> None: self.roster = roster + self.data = data self.args = args self.player = player diff --git a/pqcli/ui/basic/__init__.py b/pqcli/ui/basic/__init__.py index c5a06ce..59bc531 100644 --- a/pqcli/ui/basic/__init__.py +++ b/pqcli/ui/basic/__init__.py @@ -8,7 +8,8 @@ import typing as T from datetime import datetime, timedelta from pqcli import lingo -from pqcli.config import CLASSES, PRIME_STATS, RACES +from pqcli.config import PRIME_STATS +from pqcli.game_data import GameData from pqcli.mechanic import Player, Simulation, StatsBuilder, create_player from pqcli.roster import Roster from pqcli.ui.base import BaseUserInterface @@ -37,8 +38,9 @@ class BasicUserInterface(BaseUserInterface): roster: Roster, player: T.Optional[Player], args: argparse.Namespace, + data: GameData, ) -> None: - super().__init__(roster, player, args) + super().__init__(roster, player, args, data) def signal_handler(sig: T.Any, frame: T.Any) -> None: logging.info("Quitting") @@ -107,8 +109,10 @@ class BasicUserInterface(BaseUserInterface): print("Cancelled.") return None - race = self.menu([(race, race.name) for race in RACES]) - class_ = self.menu([(class_, class_.name) for class_ in CLASSES]) + race = self.menu([(race, race.name) for race in self.data.races]) + class_ = self.menu( + [(class_, class_.name) for class_ in self.data.classes] + ) stats_builder = StatsBuilder() while True: @@ -131,7 +135,7 @@ class BasicUserInterface(BaseUserInterface): def play(self, player: Player) -> None: print(f"Playing as {player.name}") - simulation = Simulation(player) + simulation = Simulation(player, self.data) last_tick = datetime.now() last_level = 0 while True: diff --git a/pqcli/ui/curses/__init__.py b/pqcli/ui/curses/__init__.py index 1ed79e7..1682900 100644 --- a/pqcli/ui/curses/__init__.py +++ b/pqcli/ui/curses/__init__.py @@ -7,6 +7,7 @@ import typing as T from pqcli import lingo from pqcli.config import Class, Race +from pqcli.game_data import GameData from pqcli.mechanic import Player, Stats, create_player from pqcli.roster import Roster from pqcli.ui.base import BaseUserInterface @@ -40,9 +41,13 @@ class StopMainLoop(Exception): class CursesUserInterface(BaseUserInterface): def __init__( - self, roster: Roster, player: Player, args: argparse.Namespace + self, + roster: Roster, + player: Player, + args: argparse.Namespace, + data: GameData, ) -> None: - super().__init__(roster, player, args) + super().__init__(roster, player, args, data) os.environ.setdefault("ESCDELAY", "25") self._screen = curses.initscr() self._screen.nodelay(True) @@ -119,7 +124,7 @@ class CursesUserInterface(BaseUserInterface): def _switch_to_create_char_race_view( self, player_name: str, race: T.Optional[Race] = None ) -> None: - view = ChooseCharacterRaceView(self._screen, race) + view = ChooseCharacterRaceView(self._screen, self.data.races, race) view.on_cancel += lambda: self._switch_to_create_char_name_view( player_name ) @@ -131,7 +136,9 @@ class CursesUserInterface(BaseUserInterface): def _switch_to_create_char_class_view( self, player_name: str, race: Race, class_: T.Optional[Class] = None ) -> None: - view = ChooseCharacterClassView(self._screen, class_) + view = ChooseCharacterClassView( + self._screen, self.data.classes, class_ + ) view.on_cancel += lambda: self._switch_to_create_char_race_view( player_name, race ) @@ -177,7 +184,9 @@ class CursesUserInterface(BaseUserInterface): self._switch_view(view) def _switch_to_game_view(self, player: Player) -> None: - view = GameView(self._screen, self.roster, player, self.args) + view = GameView( + self._screen, self.roster, player, self.args, self.data + ) view.on_exit += self._switch_to_roster_view self._switch_view(view) diff --git a/pqcli/ui/curses/views/create_character_view/choose_character_class_view.py b/pqcli/ui/curses/views/create_character_view/choose_character_class_view.py index 72584ec..e80202e 100644 --- a/pqcli/ui/curses/views/create_character_view/choose_character_class_view.py +++ b/pqcli/ui/curses/views/create_character_view/choose_character_class_view.py @@ -1,22 +1,25 @@ import functools import typing as T -from pqcli.config import CLASSES, Class +from pqcli.config import Class from pqcli.ui.curses.util import KEYS_CANCEL, Choice from pqcli.ui.curses.views.menu_view import MenuView class ChooseCharacterClassView(MenuView): def __init__( - self, screen: T.Any, class_: T.Optional[Class] = None + self, + screen: T.Any, + classes: T.Sequence[Class], + class_: T.Optional[Class] = None, ) -> None: super().__init__( screen, "Choose character class", - CLASSES.index(class_) if class_ is not None else 0, + classes.index(class_) if class_ is not None else 0, ) - for y, class_ in enumerate(CLASSES, 1): + for y, class_ in enumerate(classes, 1): key: T.Optional[str] = str(y % 10) if y <= 10 else None self._choices.append( Choice( diff --git a/pqcli/ui/curses/views/create_character_view/choose_character_race_view.py b/pqcli/ui/curses/views/create_character_view/choose_character_race_view.py index 5293e67..134a8ec 100644 --- a/pqcli/ui/curses/views/create_character_view/choose_character_race_view.py +++ b/pqcli/ui/curses/views/create_character_view/choose_character_race_view.py @@ -1,20 +1,25 @@ import functools import typing as T -from pqcli.config import RACES, Race +from pqcli.config import Race from pqcli.ui.curses.util import KEYS_CANCEL, Choice from pqcli.ui.curses.views.menu_view import MenuView class ChooseCharacterRaceView(MenuView): - def __init__(self, screen: T.Any, race: T.Optional[Race] = None) -> None: + def __init__( + self, + screen: T.Any, + races: T.Sequence[Race], + race: T.Optional[Race] = None, + ) -> None: super().__init__( screen, "Choose character race", - RACES.index(race) if race is not None else 0, + races.index(race) if race is not None else 0, ) - for y, race in enumerate(RACES, 1): + for y, race in enumerate(races, 1): key: T.Optional[str] = str(y % 10) if y <= 10 else None self._choices.append( Choice( diff --git a/pqcli/ui/curses/views/game_view/__init__.py b/pqcli/ui/curses/views/game_view/__init__.py index b3d1993..5568272 100644 --- a/pqcli/ui/curses/views/game_view/__init__.py +++ b/pqcli/ui/curses/views/game_view/__init__.py @@ -3,6 +3,7 @@ import curses import datetime import typing as T +from pqcli.game_data import GameData from pqcli.mechanic import Player, SignalMixin, Simulation from pqcli.roster import Roster from pqcli.ui.curses.event_handler import EventHandler @@ -33,6 +34,7 @@ class GameView(BaseView): roster: Roster, player: Player, args: argparse.Namespace, + data: GameData, ) -> None: super().__init__(screen) self.on_exit = EventHandler() @@ -41,7 +43,7 @@ class GameView(BaseView): self._player = player self._args = args - self._simulation = Simulation(player) + self._simulation = Simulation(player, data) self._last_tick = datetime.datetime.now() def keypress(self, key: int) -> None: diff --git a/pyproject.toml b/pyproject.toml index db2318a..5e95293 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.uv] -dev-dependencies = ["pre-commit>=2.16.0"] +dev-dependencies = ["pre-commit>=2.16.0", "pytest>=8.0"] [tool.black] line-length = 79 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..1da1dcf --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,4 @@ +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) diff --git a/tests/fixtures/parity_alpha.txt b/tests/fixtures/parity_alpha.txt new file mode 100644 index 0000000..9c75704 --- /dev/null +++ b/tests/fixtures/parity_alpha.txt @@ -0,0 +1,1871 @@ +task: Loading +task: Experiencing an enigmatic and foreboding night vision +task: Much is revealed about that wise old bastard you'd underestimated +task: A shocking series of events leaves you alone and bewildered, but resolute +task: Drawing upon an unrealized reserve of determination, you set out on a long and dangerous journey +task: Loading Act I +task: Heading to the killing fields +task: Executing a Dryad +task: Executing a Homunculus +task: Executing a Berserker +task: Executing a greater Ant +task: Executing a Battle-Frog +task: Executing an Imp +task: Executing an Orc +task: Executing an undernourished Multicell +task: Executing a sick Lemure +task: Executing a Stirge +task: Executing an undernourished Lemure +task: Executing an Uruk +task: Executing a Battle-Fly +task: Executing a passing Hob-Hobbit Tickle-Mimic +task: Executing an undernourished Multicell +task: Executing a Hobgoblin +task: Executing a Battle-Pixie +task: Executing a greater Spider +task: Executing a sick Harpy +task: Executing Admiral Unreck the Half Man +task: Executing a Nebbish +task: Executing an underage Wolf +task: Heading to market to sell loot +task: Selling a dryad acorn +task: Selling a homunculus fluid +task: Selling a berserker shirt +task: Selling an ant antenna +task: Selling a frog leg +task: Selling an imp tail +task: Selling an orc snout +task: Selling 2 multicell dendrites +task: Selling 2 lemure blobs +task: Selling a stirge proboscis +task: Selling an uruk boot +task: Selling an One True Scabbard of Fealty +task: Selling a Grandiose Hood of Cruelty +task: Selling a hobgoblin patella +task: Selling a pixie dust +task: Selling a spider web +task: Selling a harpy mascara +task: Selling a Deadly Galoon of Joy +task: Selling a nebbish belly +task: Selling a wolf paw +task: Heading to the killing fields +task: Executing an Uruk +task: Executing a Multicell +task: Executing an undernourished Uruk +task: Executing an underage Whippet +task: Executing an underage Caveman +task: Executing a Manes +task: Executing a sick Boogie +task: Executing a sick Centaur +task: Executing a Manes +task: Executing an undernourished Koala +task: Executing a Manes +task: Executing a Mermaid +task: Executing a Gnome +task: Executing a teenage Hogbird +task: Executing an undernourished Harpy +task: Executing a greater Ant +task: Executing a greater Spider +task: Executing a greater Swamp Elf +task: Executing an undernourished Uruk +task: Executing a Sea Elf +task: Executing an underage Troglodyte +task: Executing a Grid Bug +task: Heading to market to sell loot +task: Selling 3 uruk boots +task: Selling a multicell dendrite +task: Selling a whippet collar +task: Selling a caveman club +task: Selling 3 manes tooths +task: Selling a boogie slime +task: Selling a centaur rib +task: Selling a koala heart +task: Selling a mermaid gills +task: Selling a gnome hat +task: Selling a hogbird curl +task: Selling a harpy mascara +task: Selling an ant antenna +task: Selling a spider web +task: Selling a swamp elf lilypad +task: Selling a sea elf jerkin +task: Selling a troglodyte tail +task: Selling a grid bug carapace +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an Ostrich +task: Executing a Dwarf +task: Executing an undernourished Dryad +task: Executing a Sprite +task: Executing a preadolescent Poroid +task: Executing a greater Spider +task: Executing a Rat +task: Executing Mr. Bronyon the Dung Elf +task: Executing a Sea Elf +task: Executing a Goblin +task: Executing a greater Fly +task: Executing a Brown Elf +task: Executing a Pixie +task: Executing a Nebbish +task: Executing a Girl Scout +task: Executing a Dwarf +task: Executing a Brown Elf +task: Executing a Stirge +task: Executing a sick Mini Giant +task: Executing a Billy Goat +task: Executing a sick Silkie +task: Executing a Camel +task: Heading to market to sell loot +task: Selling an ostrich beak +task: Selling 2 dwarf drawerses +task: Selling a dryad acorn +task: Selling a sprite can +task: Selling a poroid node +task: Selling a spider web +task: Selling a rat tail +task: Selling a Spectral Lens of Hurting +task: Selling a sea elf jerkin +task: Selling a goblin ear +task: Selling a Dual Orb of Diamonique +task: Selling 2 brown elf tusks +task: Selling a pixie dust +task: Selling a nebbish belly +task: Selling a girl scout cookie +task: Selling a stirge proboscis +task: Selling a mini giant pompadour +task: Selling a billy goat beard +task: Selling a silkie fur +task: Selling a camel hump +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Manes +task: Executing a Goblin +task: Executing a greater Ant +task: Executing a passing Double Hobbit Bastard Lunatic +task: Executing a Rot Grub +task: Executing a Skeleton +task: Executing a Nebbish +task: Executing a Grid Bug +task: Executing a Swamp Elf +task: Executing a baby Catoblepas +task: Executing a Berserker +task: Executing a teenage Sylph +task: Executing an undernourished Camel +task: Executing a Rot Grub +task: Executing a Grid Bug +task: Executing a Rot Grub +task: Executing a Swamp Elf +task: Executing a Leprechaun +task: Executing a Zombie +task: Executing an undernourished Green Slime +task: Executing a Brown Elf +task: Heading to market to sell loot +task: Selling a manes tooth +task: Selling a goblin ear +task: Selling an ant antenna +task: Selling a Sacred Brocade of Frenzy +task: Selling 3 rot grub eggsacs +task: Selling a skeleton clavicle +task: Selling a nebbish belly +task: Selling 2 grid bug carapaces +task: Selling 2 swamp elf lilypads +task: Selling an Arcane Gemstone of Pleasure +task: Selling a catoblepas neck +task: Selling a berserker shirt +task: Selling a sylph thigh +task: Selling a camel hump +task: Selling a leprechaun wallet +task: Selling a zombie forehead +task: Selling a green slime sample +task: Selling a brown elf tusk +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Skeleton +task: Executing a Merman +task: Executing an underage Troglodyte +task: Executing an underage Ghoul +task: Executing a Dervish +task: Executing a greater Bunny +task: Executing a crippled preadolescent Griffon +task: Executing a Battle-Bunny +task: Executing a passing Talking Pony Fighter/Organist +task: Executing a Swamp Elf +task: Executing a Leprechaun +task: Executing a Pirate +task: Executing a Sea Elf +task: Executing an underage Lizard Man +task: Executing a Mermaid +task: Executing a Gnome +task: Executing a Swamp Elf +task: Executing a Rat +task: Executing an undernourished Bugboar +task: Executing a teenage Harpy +task: Executing a Dwarf +task: Executing a Cub Scout +task: Heading to market to sell loot +task: Selling a skeleton clavicle +task: Selling a merman trident +task: Selling a troglodyte tail +task: Selling a ghoul muscle +task: Selling a dervish robe +task: Selling 2 bunny ears +task: Selling a griffon nest +task: Selling an Astral Ankh of the Bone +task: Selling 2 swamp elf lilypads +task: Selling a leprechaun wallet +task: Selling a pirate booty +task: Selling a sea elf jerkin +task: Selling a lizard man tail +task: Selling a mermaid gills +task: Selling a gnome hat +task: Selling a rat tail +task: Selling a bugboar tusk +task: Selling a harpy mascara +task: Selling a dwarf drawers +task: Selling a cub scout neckerchief +task: Heading to the killing fields +task: Executing a Gnome +task: Executing a Cub Scout +task: Executing a Nebbish +task: Executing Mrs. Whivbrick the Dung Elf +task: Executing a Manes +task: Executing a Stun Worm +task: Executing a Skeleton +task: Executing a comatose baby Bone Devil +task: Executing an undernourished Carrion Crawler +task: Executing a greater Beagle +task: Executing a Caveman +task: Executing a passing Talking Pony Robot Monk +task: Executing a teenage Harpy +task: Executing a Dervish +task: Executing a sick Iron Peasant +task: Executing a sick Bugboar +task: Executing a greater Pirate +task: Executing a greater Beagle +task: Executing a Dwarf +task: Executing a preadolescent Pegasus +task: Executing a Stirge +task: Executing a Leprechaun +task: Heading to market to sell loot +task: Selling a gnome hat +task: Selling a cub scout neckerchief +task: Selling a nebbish belly +task: Selling a Dual Tome of Efficiency +task: Selling a manes tooth +task: Selling a stun worm trode +task: Selling a skeleton clavicle +task: Selling a bone devil hook +task: Selling a carrion crawler egg +task: Selling 2 beagle collars +task: Selling a caveman club +task: Selling an Ostentatious Phial of Worry +task: Selling a harpy mascara +task: Selling a dervish robe +task: Selling an iron peasant chaff +task: Selling a bugboar tusk +task: Selling a pirate booty +task: Selling a dwarf drawers +task: Selling a pegasus aileron +task: Selling a stirge proboscis +task: Selling a leprechaun wallet +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Berserker +task: Executing a Merman +task: Executing an undernourished Multicell +task: Executing an undernourished Eel +task: Executing an underage Silkie +task: Executing a Berserker +task: Executing a Battle-Fly +task: Executing a Nixie +task: Executing an underage Troglodyte +task: Executing a Grid Bug +task: Executing a Yellow Mold +task: Executing a Kobold +task: Executing a teenage Bugbear +task: Executing a Sprite +task: Executing a Dervish +task: Executing a Bat +task: Executing a sick Boy Scout +task: Executing a Goblin +task: Executing a preadolescent Pegasus +task: Executing a sick Shrieker +task: Executing a Skeleton +task: Executing a sick Hippogriff +task: Heading to market to sell loot +task: Selling 2 berserker shirts +task: Selling a merman trident +task: Selling a multicell dendrite +task: Selling an eel sashimi +task: Selling a silkie fur +task: Selling a Golden Tome of Electrum +task: Selling a nixie webbing +task: Selling a troglodyte tail +task: Selling a grid bug carapace +task: Selling a yellow mold spore +task: Selling a kobold penis +task: Selling a bugbear skin +task: Selling a sprite can +task: Selling a dervish robe +task: Selling a bat wing +task: Selling a boy scout merit badge +task: Selling a goblin ear +task: Selling a pegasus aileron +task: Selling a shrieker stalk +task: Selling a skeleton clavicle +task: Selling a hippogriff egg +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Stun Worm +task: Executing a greater Bunny +task: Executing an underage Eel +task: Executing an underage Homunculus +task: Executing a Stirge +task: Executing a Cub Scout +task: Executing an undernourished Wolf +task: Executing an underage Lizard Man +task: Executing a Manes +task: Executing a Berserker +task: Executing a greater Moth +task: Executing an Ostrich +task: Executing Saint Kremuv the Half Halfling +task: Executing Sir Crouvwhob the Crested Dwarf +task: Executing a Koala +task: Executing a Skeleton +task: Executing an underage Hogbird +task: Executing an underage Quasit +task: Executing a baby Owlbear +task: Executing a Dervish +task: Executing a Brown Elf +task: Executing a greater Gnome +task: Heading to market to sell loot +task: Selling a stun worm trode +task: Selling a bunny ear +task: Selling an eel sashimi +task: Selling a homunculus fluid +task: Selling a stirge proboscis +task: Selling a cub scout neckerchief +task: Selling a wolf paw +task: Selling a lizard man tail +task: Selling a manes tooth +task: Selling a berserker shirt +task: Selling a moth dust +task: Selling an ostrich beak +task: Selling an One True Phial of Intrusion +task: Selling a Crafted Candelabra of Punctuality +task: Selling a koala heart +task: Selling a skeleton clavicle +task: Selling a hogbird curl +task: Selling a quasit tail +task: Selling an owlbear feather +task: Selling a dervish robe +task: Selling a brown elf tusk +task: Selling a gnome hat +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a greater Cub Scout +task: Executing a teenage Peryton +task: Executing an undernourished Multicell +task: Executing an undernourished Ogre +task: Executing a Hippogriff +task: Executing a Battle-Stirge +task: Executing a greater Beagle +task: Executing Mrs. Liexdrok the Panda Man +task: Executing an undernourished Eel +task: Executing a greater Billy Goat +task: Executing an Octopus +task: Executing a cursed Imp +task: Executing a Hippogriff +task: Executing a Zombie +task: Executing an underage Eagle Scout +task: Executing an undernourished Wolog +task: Executing an undernourished Violet Fungi +task: Executing a Battle-Midge +task: Executing a massive Midge +task: Executing a veteran Sea Elf +task: Executing an underage Dryad +task: Executing a Man-o-war +task: Heading to market to sell loot +task: Selling a cub scout neckerchief +task: Selling a peryton antler +task: Selling a multicell dendrite +task: Selling an ogre talon +task: Selling 2 hippogriff eggs +task: Selling a stirge proboscis +task: Selling a beagle collar +task: Selling a Puissant Amethyst of Chaos +task: Selling an eel sashimi +task: Selling a billy goat beard +task: Selling an octopus beak +task: Selling an imp tail +task: Selling a zombie forehead +task: Selling an eagle scout merit badge +task: Selling a wolog lemma +task: Selling a violet fungi spore +task: Selling 2 midge corpses +task: Selling a sea elf jerkin +task: Selling a dryad acorn +task: Selling a man-o-war tentacle +task: Heading to the killing fields +task: Executing an undernourished Nymph +task: Executing a Troglodyte +task: Executing a Green Slime +task: Executing a Wight +task: Executing a Troglodyte +task: Executing a greater Harpy +task: Executing a passing Hob-Hobbit Birdrider +task: Executing a cursed Imp +task: Executing an Iron Peasant +task: Executing a dead underage Umber Hulk +task: Executing a Battle-Bat +task: Executing a Girl Scout +task: Executing an undernourished Sylph +task: Executing a Caveman +task: Executing a greater Hobgoblin +task: Executing a veteran Grid Bug +task: Executing a massive Frog +task: Executing a Battle-Orc +task: Executing a teenage Wight +task: Executing an undernourished Hogbird +task: Executing a sick Unicorn +task: Executing a cursed Swamp Elf +task: Heading to market to sell loot +task: Selling a nymph hanky +task: Selling 2 troglodyte tails +task: Selling a green slime sample +task: Selling 2 wight lungs +task: Selling a harpy mascara +task: Selling a Blessed Hood of Envy +task: Selling an imp tail +task: Selling an iron peasant chaff +task: Selling an umber hulk claw +task: Selling a bat wing +task: Selling a girl scout cookie +task: Selling a sylph thigh +task: Selling a caveman club +task: Selling a hobgoblin patella +task: Selling a grid bug carapace +task: Selling a frog leg +task: Selling an orc snout +task: Selling a hogbird curl +task: Selling an unicorn blood +task: Selling a swamp elf lilypad +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Multicell +task: Executing a Boogie +task: Executing an undernourished Imp +task: Executing Mr. Xavgrum the Gyrognome +task: Executing a sick Sylph +task: Executing an underage Harpy +task: Executing a crippled Ape +task: Executing an undernourished Owlbear +task: Executing a Billy Goat +task: Executing an underage Gelatinous Cube +task: Executing an Eel +task: Executing a Sea Elf +task: Executing a greater Gray Ooze +task: Executing a Sea Elf +task: Executing a Merman +task: Executing a greater Sea Elf +task: Executing a Green Slime +task: Executing a greater Imp +task: Executing a cursed Uruk +task: Executing a Zombie +task: Executing a foetal Hydra +task: Executing a Jumpskin +task: Heading to market to sell loot +task: Selling a multicell dendrite +task: Selling a boogie slime +task: Selling 2 imp tails +task: Selling a Fearsome Hymnal of Patience +task: Selling a sylph thigh +task: Selling a harpy mascara +task: Selling an ape ass +task: Selling an owlbear feather +task: Selling a billy goat beard +task: Selling a gelatinous cube jam +task: Selling an eel sashimi +task: Selling 3 sea elf jerkins +task: Selling a gray ooze gravy +task: Selling a merman trident +task: Selling a green slime sample +task: Selling an uruk boot +task: Selling a zombie forehead +task: Selling a hydra gyrum +task: Selling a jumpskin shin +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a greater Yellow Mold +task: Executing a Battle-Midge +task: Executing a Battle-Kobold +task: Executing a greater Yellow Mold +task: Executing a Wolf +task: Executing an underage Blink Dog +task: Executing a Troglodyte +task: Executing a greater Girl Scout +task: Executing a Boy Scout +task: Executing a greater Spider +task: Executing a passing Panda Man Bastard Lunatic +task: Executing a Stirge +task: Executing a Battle-Kobold +task: Executing a Nixie +task: Executing a Wolf +task: Executing a Battle-Wolf +task: Executing an undernourished Carrion Crawler +task: Executing a massive Yellow Mold +task: Executing an Octopus +task: Executing a Nebbish +task: Executing a Battle-Eel +task: Executing a Gnome +task: Heading to market to sell loot +task: Selling 3 yellow mold spores +task: Selling a midge corpse +task: Selling 2 kobold penises +task: Selling 3 wolf paws +task: Selling a blink dog eyelid +task: Selling a troglodyte tail +task: Selling a girl scout cookie +task: Selling a boy scout merit badge +task: Selling a spider web +task: Selling a Dual Bijou of Torpor +task: Selling a stirge proboscis +task: Selling a nixie webbing +task: Selling a carrion crawler egg +task: Selling an octopus beak +task: Selling a nebbish belly +task: Selling an eel sashimi +task: Selling a gnome hat +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Battle-Uruk +task: Executing Captain Yikyaek the Gyrognome +task: Executing a Mermaid +task: Executing a Girl Scout +task: Executing a Man-o-war +task: Executing an Octopus +task: Executing a sick Gelatinous Cube +task: Executing a greater Nebbish +task: Executing an undernourished Boogie +task: Executing a Jumpskin +task: Executing a greater Multicell +task: Executing an Octopus +task: Executing a Hobgoblin +task: Executing a Hobgoblin +task: Executing an undernourished Piercer +task: Executing an undernourished Bugbear +task: Executing a veteran Rot Grub +task: Executing a Jumpskin +task: Executing a Green Slime +task: Executing a teenage Malebranche +task: Executing a Hobgoblin +task: Executing a greater Merman +task: Heading to market to sell loot +task: Selling an uruk boot +task: Selling an Unearthly Scabbard of Submission +task: Selling a mermaid gills +task: Selling a girl scout cookie +task: Selling a man-o-war tentacle +task: Selling 2 octopus beaks +task: Selling a gelatinous cube jam +task: Selling a nebbish belly +task: Selling a boogie slime +task: Selling 2 jumpskin shins +task: Selling a multicell dendrite +task: Selling 3 hobgoblin patellas +task: Selling a piercer tip +task: Selling a bugbear skin +task: Selling a rot grub eggsac +task: Selling a green slime sample +task: Selling a malebranche fork +task: Selling a merman trident +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Yellow Mold +task: Executing a Mermaid +task: Executing a Sprite +task: Executing a Leprechaun +task: Executing a Hogbird +task: Executing a Rot Grub +task: Executing a Hobgoblin +task: Executing a Battle-Leprechaun +task: Executing a Wolog +task: Executing a Merman +task: Executing an underage Green Slime +task: Executing an underage Imp +task: Executing a massive Manes +task: Executing a Jumpskin +task: Executing a greater Centipede +task: Executing an undernourished Man-o-war +task: Executing a teenage Harpy +task: Executing a passing Double Hobbit Vermineer +task: Executing a Carrion Crawler +task: Executing a Wolf +task: Executing a Boy Scout +task: Executing an underage Sylph +task: Heading to market to sell loot +task: Selling a yellow mold spore +task: Selling a mermaid gills +task: Selling a sprite can +task: Selling 2 leprechaun wallets +task: Selling a hogbird curl +task: Selling a rot grub eggsac +task: Selling a hobgoblin patella +task: Selling a wolog lemma +task: Selling a merman trident +task: Selling a green slime sample +task: Selling an imp tail +task: Selling a manes tooth +task: Selling a jumpskin shin +task: Selling a centipede leg +task: Selling a man-o-war tentacle +task: Selling a harpy mascara +task: Selling a Puissant Sceptre of Comfort +task: Selling a carrion crawler egg +task: Selling a wolf paw +task: Selling a boy scout merit badge +task: Selling a sylph thigh +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Bat +task: Executing a massive Green Slime +task: Executing a Green Slime +task: Executing an underage Hippogriff +task: Executing an undernourished Violet Fungi +task: Executing a greater Rot Grub +task: Executing a Green Slime +task: Executing a greater Green Slime +task: Executing an Octopus +task: Executing an undernourished Mini Giant +task: Executing a greater Lizard Man +task: Executing an underage Peryton +task: Executing a Stun Worm +task: Executing a sick Gargoyle +task: Executing an undernourished Hippogriff +task: Executing Saint Predcroup the Lesser Dwarf +task: Executing an Eel +task: Executing a Boy Scout +task: Executing a baby Leucrotta +task: Executing an undernourished Piercer +task: Executing an Orc +task: Executing an Ostrich +task: Heading to market to sell loot +task: Selling a bat wing +task: Selling 4 green slime samples +task: Selling 2 hippogriff eggs +task: Selling a violet fungi spore +task: Selling a rot grub eggsac +task: Selling an octopus beak +task: Selling a mini giant pompadour +task: Selling a lizard man tail +task: Selling a peryton antler +task: Selling a stun worm trode +task: Selling a gargoyle gravel +task: Selling an One True Ankh of Nervousness +task: Selling an eel sashimi +task: Selling a boy scout merit badge +task: Selling a leucrotta hoof +task: Selling a piercer tip +task: Selling an orc snout +task: Selling an ostrich beak +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an undernourished Violet Fungi +task: Executing an underage Iron Peasant +task: Executing an undernourished Poroid +task: Executing a Harpy +task: Executing an undernourished Caveman +task: Executing a sick Man-o-war +task: Executing an underage Doppelganger +task: Executing a Triton +task: Executing a Bat +task: Executing an Uruk +task: Executing a Hogbird +task: Executing Mr. Laznick the Half Man +task: Executing a foetal Balor +task: Executing a Quasit +task: Executing a Green Slime +task: Executing an underage Bugbear +task: Executing a greater Dwarf +task: Executing a preadolescent Wolog +task: Executing an undernourished Dryad +task: Executing a Stun Worm +task: Executing an Uruk +task: Executing a Were-Midge +task: Heading to market to sell loot +task: Selling a violet fungi spore +task: Selling an iron peasant chaff +task: Selling a poroid node +task: Selling a harpy mascara +task: Selling a caveman club +task: Selling a man-o-war tentacle +task: Selling a doppelganger face +task: Selling a triton scale +task: Selling a bat wing +task: Selling 2 uruk boots +task: Selling a hogbird curl +task: Selling a Garlanded Hood of Nervousness +task: Selling a balor whip +task: Selling a quasit tail +task: Selling a green slime sample +task: Selling a bugbear skin +task: Selling a dwarf drawers +task: Selling a wolog lemma +task: Selling a dryad acorn +task: Selling a stun worm trode +task: Selling a midge corpse +task: Heading to the killing fields +task: Executing a Sea Elf +task: Executing a sick Gelatinous Cube +task: Executing a Battle-Dwarf +task: Executing a Boogie +task: Executing a Battle-Merman +task: Executing a Merman +task: Executing an underage Gray Ooze +task: Executing a Lizard Man +task: Executing a passing Will o' the Wisp Fighter/Organist +task: Executing a cursed Gnoll +task: Executing a Troglodyte +task: Executing a Pixie +task: Executing a Multicell +task: Executing a Nixie +task: Executing a Homunculus +task: Executing an undernourished Pegasus +task: Executing a Hippogriff +task: Executing a Koala +task: Executing a Yellow Mold +task: Executing a Sylph +task: Executing a Battle-Rat +task: Executing an undernourished Caveman +task: Heading to market to sell loot +task: Selling a sea elf jerkin +task: Selling a gelatinous cube jam +task: Selling a dwarf drawers +task: Selling a boogie slime +task: Selling 2 merman tridents +task: Selling a gray ooze gravy +task: Selling a lizard man tail +task: Selling an Ormolu Talisman of Cruelty +task: Selling a gnoll collar +task: Selling a troglodyte tail +task: Selling a pixie dust +task: Selling a multicell dendrite +task: Selling a nixie webbing +task: Selling a homunculus fluid +task: Selling a pegasus aileron +task: Selling a hippogriff egg +task: Selling a koala heart +task: Selling a yellow mold spore +task: Selling a sylph thigh +task: Selling a rat tail +task: Selling a caveman club +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Berserker +task: Executing a Battle-Stirge +task: Executing a comatose Su-monster +task: Executing a Battle-Ostrich +task: Executing a Triton +task: Executing a Camel +task: Executing a Merman +task: Executing a Battle-Eel +task: Executing an undernourished Wolog +task: Executing an undernourished Sea Hag +task: Executing a Homunculus +task: Executing an undernourished Boy Scout +task: Executing a Ghoul +task: Executing a Sea Elf +task: Executing an underage Sylph +task: Executing a Cub Scout +task: Executing a Dwarf +task: Executing a Shrieker +task: Executing a massive Dwarf +task: Executing a Multicell +task: Executing a Stun Worm +task: Executing a greater Orc +task: Heading to market to sell loot +task: Selling a berserker shirt +task: Selling a stirge proboscis +task: Selling a su-monster tail +task: Selling an ostrich beak +task: Selling a triton scale +task: Selling a camel hump +task: Selling a merman trident +task: Selling an eel sashimi +task: Selling a wolog lemma +task: Selling a sea hag wart +task: Selling a homunculus fluid +task: Selling a boy scout merit badge +task: Selling a ghoul muscle +task: Selling a sea elf jerkin +task: Selling a sylph thigh +task: Selling a cub scout neckerchief +task: Selling 2 dwarf drawerses +task: Selling a shrieker stalk +task: Selling a multicell dendrite +task: Selling a stun worm trode +task: Selling an orc snout +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Jumpskin +task: Executing a Gnome +task: Executing a Gelatinous Cube +task: Executing an undernourished Shadow +task: Executing an underage Hogbird +task: Executing a Skeleton +task: Executing a teenage Sea Hag +task: Executing a greater Gnoll +task: Executing an underage Zombie +task: Executing a Koala +task: Executing a Swamp Elf +task: Executing a Camel +task: Executing a sick Triton +task: Executing a greater Skeleton +task: Executing a Battle-Nixie +task: Executing Ms. Shemwhaz the Enchanted Motorcycle +task: Executing a sick Doppelganger +task: Executing a Gnome +task: Executing a sick Harpy +task: Executing a Girl Scout +task: Executing a veteran Violet Fungi +task: Executing an Orc +task: Heading to market to sell loot +task: Selling a jumpskin shin +task: Selling 2 gnome hats +task: Selling a gelatinous cube jam +task: Selling a shadow silhouette +task: Selling a hogbird curl +task: Selling 2 skeleton clavicles +task: Selling a sea hag wart +task: Selling a gnoll collar +task: Selling a zombie forehead +task: Selling a koala heart +task: Selling a swamp elf lilypad +task: Selling a camel hump +task: Selling a triton scale +task: Selling a nixie webbing +task: Selling a Precious Vulpeculum of Rapidity +task: Selling a doppelganger face +task: Selling a harpy mascara +task: Selling a girl scout cookie +task: Selling a violet fungi spore +task: Selling an orc snout +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Homunculus +task: Executing an Imp +task: Executing a Battle-Ghoul +task: Executing an underage Green Slime +task: Executing a passing Panda Man Mage Illusioner +task: Executing a passing Demicanadian Vermineer +task: Executing a Bat +task: Executing a Battle-Homunculus +task: Executing a teenage Blink Dog +task: Executing a massive Yellow Mold +task: Executing a Jumpskin +task: Executing an undernourished Ape +task: Executing an undernourished Gray Ooze +task: Executing an underage Jumpskin +task: Executing a Billy Goat +task: Executing an underage Troglodyte +task: Executing a baby Plaid Dragon +task: Executing an Imp +task: Executing a Caveman +task: Executing a Battle-Eel +task: Executing a Gnome +task: Executing a Man-o-war +task: Heading to market to sell loot +task: Selling 2 homunculus fluids +task: Selling 2 imp tails +task: Selling a ghoul muscle +task: Selling a green slime sample +task: Selling an Ormolu Bijou of Solitude +task: Selling a Fearsome Vulpeculum of Comfort +task: Selling a bat wing +task: Selling a blink dog eyelid +task: Selling a yellow mold spore +task: Selling 2 jumpskin shins +task: Selling an ape ass +task: Selling a gray ooze gravy +task: Selling a billy goat beard +task: Selling a troglodyte tail +task: Selling a plaid dragon sporrin +task: Selling a caveman club +task: Selling an eel sashimi +task: Selling a gnome hat +task: Selling a man-o-war tentacle +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an undernourished Silkie +task: Executing an underage Su-monster +task: Executing a Stun Worm +task: Executing a Leprechaun +task: Executing a Battle-Dryad +task: Executing a greater Merman +task: Executing an undernourished Gargoyle +task: Executing a Dervish +task: Executing a Skeleton +task: Executing a Battle-Camel +task: Executing a veteran Gray Ooze +task: Executing a greater Kobold +task: Executing a veteran Stun Worm +task: Executing a massive Wasp +task: Executing a greater Orc +task: Executing an undernourished Stun Worm +task: Executing a comatose Troll +task: Executing a Triton +task: Executing a sick baby Hydra +task: Executing an Eel +task: Executing a sick Doppelganger +task: Executing an underage Malebranche +task: Heading to market to sell loot +task: Selling a silkie fur +task: Selling a su-monster tail +task: Selling 3 stun worm trodes +task: Selling a leprechaun wallet +task: Selling a dryad acorn +task: Selling a merman trident +task: Selling a gargoyle gravel +task: Selling a dervish robe +task: Selling a skeleton clavicle +task: Selling a camel hump +task: Selling a gray ooze gravy +task: Selling a kobold penis +task: Selling a wasp stinger +task: Selling an orc snout +task: Selling a troll hide +task: Selling a triton scale +task: Selling a hydra gyrum +task: Selling an eel sashimi +task: Selling a doppelganger face +task: Selling a malebranche fork +task: Heading to the killing fields +task: Executing a Caveman +task: Executing a teenage Mini Giant +task: Executing an Orc +task: Executing a Nebbish +task: Executing a preadolescent Plaid Dragon +task: Executing an underage Boogie +task: Executing a Sea Elf +task: Executing a baby Leucrotta +task: Executing an undernourished Koala +task: Executing a greater Billy Goat +task: Executing an underage Dryad +task: Executing a Ghoul +task: Executing an undernourished Carrion Crawler +task: Executing a Manes +task: Executing a Carrion Crawler +task: Executing a Pixie +task: Executing a passing Hob-Hobbit Runeloremaster +task: Executing an Octopus +task: Executing a greater Skeleton +task: Executing a Goblin +task: Executing a Brown Elf +task: Heading to market to sell loot +task: Selling a Legendary Laurel of Ra +task: Selling a caveman club +task: Selling a mini giant pompadour +task: Selling an orc snout +task: Selling a nebbish belly +task: Selling a plaid dragon sporrin +task: Selling a boogie slime +task: Selling a sea elf jerkin +task: Selling a leucrotta hoof +task: Selling a koala heart +task: Selling a billy goat beard +task: Selling a dryad acorn +task: Selling a ghoul muscle +task: Selling 2 carrion crawler eggs +task: Selling a manes tooth +task: Selling a pixie dust +task: Selling a Lucky Amethyst of Petulance +task: Selling an octopus beak +task: Selling a skeleton clavicle +task: Selling a goblin ear +task: Selling a brown elf tusk +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Hippogriff +task: Executing Captain Krinjiet the Half Orc +task: Executing a Lemure +task: Executing a Doppelganger +task: Executing a Battle-Pirate +task: Executing a Nymph +task: Executing a Kobold +task: Executing a Berserker +task: Executing a Silkie +task: Executing a Manes +task: Executing a cursed Rat +task: Executing a Girl Scout +task: Executing a greater Merman +task: Executing an underage Gnoll +task: Executing an underage Sea Hag +task: Executing a Sylph +task: Executing a Harpy +task: Executing a veteran Cub Scout +task: Executing a Cub Scout +task: Executing a Dwarf +task: Executing a preadolescent Hippocampus +task: Executing a cursed Goblin +task: Heading to market to sell loot +task: Selling a hippogriff egg +task: Selling a Sacred Galoon of Worry +task: Selling a lemure blob +task: Selling a doppelganger face +task: Selling a pirate booty +task: Selling a nymph hanky +task: Selling a kobold penis +task: Selling a berserker shirt +task: Selling a silkie fur +task: Selling a manes tooth +task: Selling a rat tail +task: Selling a girl scout cookie +task: Selling a merman trident +task: Selling a gnoll collar +task: Selling a sea hag wart +task: Selling a sylph thigh +task: Selling a harpy mascara +task: Selling 2 cub scout neckerchieves +task: Selling a dwarf drawers +task: Selling a hippocampus mane +task: Selling a goblin ear +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an Eel +task: Executing a sick Nymph +task: Executing an undernourished Camel +task: Executing an underage Octopus +task: Executing an undernourished Sea Hag +task: Executing a Koala +task: Executing a Brown Elf +task: Executing an Uruk +task: Executing a greater Whippet +task: Executing an undernourished Lemure +task: Executing a Gnoll +task: Executing a Battle-Nixie +task: Executing a Hippogriff +task: Executing a cursed Spider +task: Executing a sick Ogre +task: Executing an undernourished Sylph +task: Executing a Dwarf +task: Executing an undernourished Sea Hag +task: Executing a greater Eel +task: Executing a greater Pirate +task: Executing an underage Wolog +task: Executing a Manes +task: Heading to market to sell loot +task: Selling 2 eel sashimis +task: Selling a nymph hanky +task: Selling a camel hump +task: Selling an octopus beak +task: Selling 2 sea hag warts +task: Selling a koala heart +task: Selling a brown elf tusk +task: Selling an uruk boot +task: Selling a whippet collar +task: Selling a lemure blob +task: Selling a gnoll collar +task: Selling a nixie webbing +task: Selling a hippogriff egg +task: Selling a spider web +task: Selling an ogre talon +task: Selling a sylph thigh +task: Selling a dwarf drawers +task: Selling a pirate booty +task: Selling a wolog lemma +task: Selling a manes tooth +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Sylph +task: Executing a veteran Brown Elf +task: Executing a greater Grid Bug +task: Executing an underage Shrieker +task: Executing a veteran Swamp Elf +task: Executing a greater Nixie +task: Executing a Girl Scout +task: Executing a Stun Worm +task: Executing a greater Fly +task: Executing a Zombie +task: Executing a sick Gargoyle +task: Executing an Orc +task: Executing an Eel +task: Executing an underage Lizard Man +task: Executing a Man-o-war +task: Executing a Nixie +task: Executing a teenage Ghast +task: Executing a Rot Grub +task: Executing a comatose Stun Bear +task: Executing an undernourished Mini Giant +task: Executing a Green Slime +task: Executing an undernourished Violet Fungi +task: Heading to market to sell loot +task: Selling a sylph thigh +task: Selling a brown elf tusk +task: Selling a grid bug carapace +task: Selling a shrieker stalk +task: Selling a swamp elf lilypad +task: Selling 2 nixie webbings +task: Selling a girl scout cookie +task: Selling a stun worm trode +task: Selling a Gleaming Ankh of Envy +task: Selling a zombie forehead +task: Selling a gargoyle gravel +task: Selling an orc snout +task: Selling an eel sashimi +task: Selling a lizard man tail +task: Selling a man-o-war tentacle +task: Selling a ghast vomit +task: Selling a rot grub eggsac +task: Selling a stun bear tooth +task: Selling a mini giant pompadour +task: Selling a green slime sample +task: Selling a violet fungi spore +task: Heading to the killing fields +task: Executing a sick Hippogriff +task: Executing a teenage Hippogriff +task: Executing a Whippet +task: Executing a Centaur +task: Executing a greater Triton +task: Executing a veteran Cub Scout +task: Executing a Multicell +task: Executing an Uruk +task: Executing a Camel +task: Executing a Camel +task: Executing a Kobold +task: Executing a Jumpskin +task: Executing an undernourished Troglodyte +task: Executing a Pixie +task: Executing a Multicell +task: Executing an underage Violet Fungi +task: Executing Chief Frogmiek the Half Halfling +task: Executing an underage Ghast +task: Executing a Ghoul +task: Executing a massive Imp +task: Executing an undernourished Pegasus +task: Executing a greater Sprite +task: Heading to market to sell loot +task: Selling 2 hippogriff eggs +task: Selling a whippet collar +task: Selling a centaur rib +task: Selling a triton scale +task: Selling a cub scout neckerchief +task: Selling 2 multicell dendrites +task: Selling an uruk boot +task: Selling 2 camel humps +task: Selling a kobold penis +task: Selling a jumpskin shin +task: Selling a troglodyte tail +task: Selling a pixie dust +task: Selling a violet fungi spore +task: Selling a Blessed Arrow of Fear +task: Selling a ghast vomit +task: Selling a ghoul muscle +task: Selling an imp tail +task: Selling a pegasus aileron +task: Selling a sprite can +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an undernourished Jumpskin +task: Executing an undernourished Boogie +task: Executing an underage Eel +task: Executing a greater Ostrich +task: Executing an Iron Peasant +task: Executing a Centaur +task: Executing an undernourished Sea Hag +task: Executing an underage Mini Giant +task: Executing a greater Gray Ooze +task: Executing a Battle-Nebbish +task: Executing a Rust Monster +task: Executing a Shadow +task: Executing a Carrion Crawler +task: Executing an undernourished Rust Monster +task: Executing an undernourished Wight +task: Executing an underage Boogie +task: Executing a Gray Ooze +task: Executing a cursed Quasit +task: Executing an Iron Peasant +task: Executing a Hogbird +task: Executing a Poroid +task: Executing an underage Wraith +task: Heading to market to sell loot +task: Selling a jumpskin shin +task: Selling 2 boogie slimes +task: Selling an eel sashimi +task: Selling an ostrich beak +task: Selling 2 iron peasant chafves +task: Selling a centaur rib +task: Selling a sea hag wart +task: Selling a mini giant pompadour +task: Selling 2 gray ooze gravies +task: Selling a nebbish belly +task: Selling 2 rust monster shavingses +task: Selling a shadow silhouette +task: Selling a carrion crawler egg +task: Selling a wight lung +task: Selling a quasit tail +task: Selling a hogbird curl +task: Selling a poroid node +task: Selling a wraith finger +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Gelatinous Cube +task: Executing an enormous Kobold +task: Executing an undernourished Hippocampus +task: Executing a teenage Otyugh +task: Executing a Battle-Triton +task: Executing a Ghast +task: Executing a Bugbear +task: Executing a Bugbear +task: Executing a Bugbear +task: Executing a cursed Green Slime +task: Executing an underage Bugbear +task: Executing a veteran Stun Worm +task: Executing an underage Silkie +task: Executing a Sea Elf +task: Executing a sick Bugbear +task: Executing a Bugbear +task: Executing a Kobold +task: Executing a Bugbear +task: Executing an underage Sea Hag +task: Executing a greater Kobold +task: Executing a Piercer +task: Executing a Goblin +task: Heading to market to sell loot +task: Selling a gelatinous cube jam +task: Selling 3 kobold penises +task: Selling a hippocampus mane +task: Selling an otyugh organ +task: Selling a triton scale +task: Selling a ghast vomit +task: Selling 7 bugbear skins +task: Selling a green slime sample +task: Selling a stun worm trode +task: Selling a silkie fur +task: Selling a sea elf jerkin +task: Selling a sea hag wart +task: Selling a piercer tip +task: Selling a goblin ear +task: Heading to the killing fields +task: Executing a Wolog +task: Executing Mrs. Akprap the Half Man +task: Executing a greater Violet Fungi +task: Executing a Battle-Bugbear +task: Executing a Bugbear +task: Executing a greater Jumpskin +task: Executing a Shadow +task: Executing a Koala +task: Executing a Battle-Homunculus +task: Executing an Ape +task: Executing a Battle-Hippogriff +task: Executing a Rat +task: Executing a Bugbear +task: Executing a Man-o-war +task: Executing a teenage Bugbear +task: Executing a Harpy +task: Executing an Imp +task: Executing a Triton +task: Executing a Battle-Centipede +task: Executing an Iron Peasant +task: Executing an Iron Peasant +task: Executing a cursed Moth +task: Heading to market to sell loot +task: Selling a wolog lemma +task: Selling a Gilded Ornament of Joy +task: Selling a violet fungi spore +task: Selling 4 bugbear skins +task: Selling a jumpskin shin +task: Selling a shadow silhouette +task: Selling a koala heart +task: Selling a homunculus fluid +task: Selling an ape ass +task: Selling a hippogriff egg +task: Selling a rat tail +task: Selling a man-o-war tentacle +task: Selling a harpy mascara +task: Selling an imp tail +task: Selling a triton scale +task: Selling a centipede leg +task: Selling 2 iron peasant chafves +task: Selling a moth dust +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a preadolescent Succubus +task: Executing an enormous Multicell +task: Executing a Bugbear +task: Executing a Hell Hound +task: Executing a greater Stun Worm +task: Executing a Rust Monster +task: Executing an underage Bugbear +task: Executing a Battle-Gnome +task: Executing a teenage Bugbear +task: Executing a passing Demicanadian Shiv-Knight +task: Executing a Battle-Bugbear +task: Executing an underage Silkie +task: Executing an undernourished Centaur +task: Executing a Gray Ooze +task: Executing a Multicell +task: Executing an undernourished Bugbear +task: Executing a sick Manticore +task: Executing a passing Half Orc Battle-Felon +task: Executing a Peryton +task: Executing a Stun Bear +task: Executing an underage Bugbear +task: Executing an undernourished Lemure +task: Heading to market to sell loot +task: Selling a succubus bra +task: Selling 2 multicell dendrites +task: Selling 6 bugbear skins +task: Selling a hell hound tongue +task: Selling a stun worm trode +task: Selling a rust monster shavings +task: Selling a gnome hat +task: Selling a Cruciate Fleece of Submission +task: Selling a silkie fur +task: Selling a centaur rib +task: Selling a gray ooze gravy +task: Selling a manticore spike +task: Selling a Proverbial Tome of Happiness +task: Selling a peryton antler +task: Selling a stun bear tooth +task: Selling a lemure blob +task: Negotiating purchase of better equipment +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Stun Bear +task: Executing a passing Talking Pony Ur-Paladin +task: Executing an underage Bugbear +task: Executing a greater Stun Worm +task: Executing a Caveman +task: Executing a Boy Scout +task: Executing a Carrion Crawler +task: Executing an undernourished Boy Scout +task: Executing a Caveman +task: Executing a comatose Hell Hound +task: Executing an underage Bugbear +task: Executing a greater Boy Scout +task: Executing a Hippogriff +task: Executing a Battle-Hippogriff +task: Executing an Imp +task: Executing a massive Imp +task: Executing a Battle-Caveman +task: Executing an underage Man-o-war +task: Executing an undernourished Bugbear +task: Executing a teenage Bugbear +task: Executing a Battle-Berserker +task: Executing a Dervish +task: Heading to market to sell loot +task: Selling a stun bear tooth +task: Selling a Dual Brocade of Rapidity +task: Selling 4 bugbear skins +task: Selling a stun worm trode +task: Selling 3 caveman clubs +task: Selling 3 boy scout merit badges +task: Selling a carrion crawler egg +task: Selling a hell hound tongue +task: Selling 2 hippogriff eggs +task: Selling 2 imp tails +task: Selling a man-o-war tentacle +task: Selling a berserker shirt +task: Selling a dervish robe +task: Heading to the killing fields +task: Executing an underage Ogre +task: Executing a greater Carrion Crawler +task: Executing an Owlbear +task: Executing a greater Pixie +task: Executing a Shadow +task: Executing an undernourished Bugbear +task: Executing a Were-Stirge +task: Executing a Bugboar +task: Executing a Bugbear +task: Executing an undernourished Stun Bear +task: Executing a Sea Hag +task: Executing a Stun Worm +task: Executing an underage Boogie +task: Executing a Battle-Triton +task: Executing a Multicell +task: Executing a Bugbear +task: Executing a greater Cub Scout +task: Executing a Nymph +task: Executing a Battle-Ghast +task: Executing a Homunculus +task: Executing an underage Bugbear +task: Executing a Gray Ooze +task: Executing a Gray Ooze +task: Heading to market to sell loot +task: Selling an ogre talon +task: Selling a carrion crawler egg +task: Selling an owlbear feather +task: Selling a pixie dust +task: Selling a shadow silhouette +task: Selling 4 bugbear skins +task: Selling a stirge proboscis +task: Selling a bugboar tusk +task: Selling a stun bear tooth +task: Selling a sea hag wart +task: Selling a stun worm trode +task: Selling a boogie slime +task: Selling a triton scale +task: Selling a multicell dendrite +task: Selling a cub scout neckerchief +task: Selling a nymph hanky +task: Selling a ghast vomit +task: Selling a homunculus fluid +task: Selling 2 gray ooze gravies +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Ghoul +task: Executing Sgt. Modfrock the Battle-Finch +task: Executing a greater Hobgoblin +task: Executing a Camel +task: Executing an undernourished Ghast +task: Executing a Battle-Moth +task: Executing an enormous Dwarf +task: Executing a Rust Monster +task: Executing an underage Uruk +task: Executing a Centaur +task: Executing a Battle-Imp +task: Executing an undernourished Centaur +task: Executing a greater Gnoll +task: Executing Sir Krudmov the Eel Man +task: Executing a passing Gyrognome Slow Poisoner +task: Executing an underage Piercer +task: Executing a Ghoul +task: Executing an undernourished Man-o-war +task: Executing an underage Rust Monster +task: Executing a preadolescent Medusa +task: Executing a teenage Blink Dog +task: Executing an Imp +task: Executing a Battle-Stirge +task: Heading to market to sell loot +task: Selling 2 ghoul muscles +task: Selling an Astral Brazier of Silence +task: Selling a hobgoblin patella +task: Selling a camel hump +task: Selling a ghast vomit +task: Selling a moth dust +task: Selling a dwarf drawers +task: Selling 2 rust monster shavingses +task: Selling an uruk boot +task: Selling 2 centaur ribs +task: Selling 2 imp tails +task: Selling a gnoll collar +task: Selling a Reverential Hood of Suffering +task: Selling a Reverential Gimcrack of Intrusion +task: Selling a piercer tip +task: Selling a man-o-war tentacle +task: Selling a medusa eye +task: Selling a blink dog eyelid +task: Selling a stirge proboscis +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Poroid +task: Executing an Iron Peasant +task: Executing a massive Manes +task: Executing a Bat +task: Executing a greater Octopus +task: Executing a Battle-Manes +task: Executing an Uruk +task: Executing a sick Mummy +task: Executing a Shrieker +task: Executing a Stun Worm +task: Executing an undernourished Quasit +task: Executing a massive Hogbird +task: Executing a Mermaid +task: Executing a Battle-Orc +task: Executing a sick Stun Bear +task: Executing a Centaur +task: Executing a Boogie +task: Executing a crippled Medusa +task: Executing a cursed Yellow Mold +task: Executing a Battle-Zombie +task: Executing a Su-monster +task: Executing a Wight +task: Executing a Wolog +task: Heading to market to sell loot +task: Selling a poroid node +task: Selling an iron peasant chaff +task: Selling 2 manes tooths +task: Selling a bat wing +task: Selling an octopus beak +task: Selling an uruk boot +task: Selling a mummy gauze +task: Selling a shrieker stalk +task: Selling a stun worm trode +task: Selling a quasit tail +task: Selling a hogbird curl +task: Selling a mermaid gills +task: Selling an orc snout +task: Selling a stun bear tooth +task: Selling a centaur rib +task: Selling a boogie slime +task: Selling a medusa eye +task: Selling a yellow mold spore +queued: Oh sweet relief! You've reached the protection of the good Queen of the Talking Pony +queued: There is rejoicing, and an unnerving encounter with Queen of the Talking Pony in private +queued: You forget your chicken and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of the Talking Pony be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Princess of Whuvdrum +queued: There is rejoicing, and an unnerving encounter with Princess of Whuvdrum in private +queued: You forget your twig and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Princess of Whuvdrum be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Archbishop of the Half Halfling +queued: There is rejoicing, and an unnerving encounter with Archbishop of the Half Halfling in private +queued: You forget your writ and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Archbishop of the Half Halfling be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Princess of the Panda Man +queued: There is rejoicing, and an unnerving encounter with Princess of the Panda Man in private +queued: You forget your vest and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Princess of the Panda Man be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Ripyick the Wight +queued: Locked in grim combat with Ripyick the Wight +queued: Victory! Ripyick the Wight is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Frooxzoot the Vrock +queued: Victory! Frooxzoot the Vrock is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Boss of the Crested Dwarf +queued: There is rejoicing, and an unnerving encounter with Boss of the Crested Dwarf in private +queued: You forget your letter and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Boss of the Crested Dwarf be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Prince of Whopbrat +queued: There is rejoicing, and an unnerving encounter with Prince of Whopbrat in private +queued: You forget your credenza and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Prince of Whopbrat be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Lord of the Lesser Dwarf +queued: There is rejoicing, and an unnerving encounter with Lord of the Lesser Dwarf in private +queued: You forget your bunny and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Lord of the Lesser Dwarf be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Princess of Ocknip +queued: There is rejoicing, and an unnerving encounter with Princess of Ocknip in private +queued: You forget your writ and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Princess of Ocknip be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Frimyoot the Copper Dragon +queued: Locked in grim combat with Frimyoot the Copper Dragon +queued: Victory! Frimyoot the Copper Dragon is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Lord of Yodven +queued: There is rejoicing, and an unnerving encounter with Lord of Yodven in private +queued: You forget your needle and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Lord of Yodven be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Zanvoz the Black Pudding +queued: You seem to gain the advantage over Zanvoz the Black Pudding +queued: Victory! Zanvoz the Black Pudding is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Triezmug the Medusa +queued: Victory! Triezmug the Medusa is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Trobyip the Hydra +queued: Victory! Trobyip the Hydra is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Kraepid the Violet Fungi +queued: You seem to gain the advantage over Kraepid the Violet Fungi +queued: Victory! Kraepid the Violet Fungi is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Princess of the Hob-Hobbit +queued: There is rejoicing, and an unnerving encounter with Princess of the Hob-Hobbit in private +queued: You forget your writ and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Princess of the Hob-Hobbit be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Viceroy of the Eel Man +queued: There is rejoicing, and an unnerving encounter with Viceroy of the Eel Man in private +queued: You forget your tuppence and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Viceroy of the Eel Man be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Viceroy of the Hob-Hobbit +queued: There is rejoicing, and an unnerving encounter with Viceroy of the Hob-Hobbit in private +queued: You forget your toothpick and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Viceroy of the Hob-Hobbit be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of Meptrad +queued: There is rejoicing, and an unnerving encounter with Queen of Meptrad in private +queued: You forget your cookie and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of Meptrad be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +monster: Executing a Swamp Elf 6000 +monster: Executing an underage Koala 3000 +monster: Executing a Centaur 8000 +monster: Executing an Iguanadon 9000 +monster: Executing an underage Gelatinous Cube 3600 +monster: Executing a Mind Flayer 8000 +monster: Executing a Vrock 6857 +monster: Executing a Minotaur 4500 +monster: Executing a veteran Tin Dragon 6000 +monster: Executing an undernourished Ghost 5400 +monster: Executing a Bone Devil 4909 +monster: Executing a giant Were-Marilith 7000 +monster: Executing a sick Purple Worm 6000 +monster: Executing a giant Pentasaurus 6857 +monster: Executing an enormous Gorgosaurus 6400 +monster: Executing a greater Stegosaurus 7125 +monster: Executing 3 sick Balors 6352 +monster: Executing a greater demon Vrock 4666 +monster: Executing an undead Pentasaurus 5052 +monster: Executing 3 undernourished Spectres 5400 +monster: Executing a giant Ice Devil 4285 +monster: Executing a titanic Were-Baluchitherium 6000 +monster: Executing a messianic Black Pudding 5217 +monster: Executing 4 undernourished Plaid Dragons 6000 +monster: Executing 4 undernourished Hill Giants 6720 +monster: Executing a messianic Leather Golem 5769 +monster: Executing 2 greater Beef Giants 5333 +monster: Executing 3 cursed Copper Dragons 6428 +monster: Executing 3 underage Shambling Mounds 5586 +monster: Executing 5 Xorns 7000 +monster: Executing 3 Neo-Otyughs 6387 +monster: Executing 4 Bronze Dragons 6750 +monster: Executing 3 greater Efreets 6000 +monster: Executing a massive Dispater 5647 +monster: Executing an imaginary Demogorgon 6171 +monster: Executing a titanic Brontosaurus 5833 +monster: Executing 2 Jubilexes 5513 +monster: Executing 2 preadolescent Yeenoghus 6947 +monster: Executing 3 baby Rocs 6461 +monster: Executing a messianic Brontosaurus 6000 +monster: Executing 3 undernourished Cardboard Golems 5707 +monster: Executing a messianic Orcus 5714 +monster: Executing 8 undernourished White Dragons 5581 +monster: Executing 3 Triceratopses 6545 +monster: Executing 4 undernourished Beef Giants 5333 +monster: Executing a demon Baalzebul 6260 +monster: Executing 2 giant Platinum Dragons 6382 +monster: Executing an undernourished foetal Asmodeus 5750 +monster: Executing 2 sick baby Brontosauri 5877 +monster: Executing 2 cursed Platinum Dragons 5520 +monster: Executing 3 Pit Fiends 4588 +monster: Executing 5 Megalosauri 6923 +monster: Executing 13 undernourished Ogres 4415 +monster: Executing 3 Oxygen Golems 5666 +monster: Executing 5 Bacon Elementals 5454 +monster: Executing a messianic Orcus 5571 +monster: Executing 7 undernourished Porcelain Giants 5894 +monster: Executing 4 baby Oxygen Golems 5379 +monster: Executing 5 undernourished Trappers 5593 +monster: Executing 5 Fire Giants 5500 +monster: Executing 3 crippled preadolescent Yeenoghus 5606 +monster: Executing 6 underage Black Puddings 5225 +monster: Executing 2 imaginary Baalzebuls 5904 +monster: Executing 4 preadolescent Rocs 5625 +monster: Executing 7 Bronze Dragons 5815 +monster: Executing Chief Yietnouck the Talking Pony 6000 +monster: Executing 3 underage Diplodoci 6179 +monster: Executing 4 cursed Purple Worms 6000 +monster: Executing 4 veteran Leather Golems 5565 +monster: Executing 4 sick Titans 6171 +monster: Executing 6 undernourished Pentasauri 5577 +monster: Executing 7 Porcelain Giants 5250 +monster: Executing a messianic Asmodeus 5506 +monster: Executing 5 greater Mastodons 5270 +monster: Executing 8 Black Puddings 6400 +monster: Executing 4 undernourished Platinum Dragons 6315 +monster: Executing 7 undernourished Lurkers 4909 +monster: Executing 4 greater Tyrannosaurus Rexes 5846 +monster: Executing 8 Ghosts 6075 +monster: Executing 2 demon Geryons 5250 +monster: Executing a passing Enchanted Motorcycle Lowling 6222 +monster: Executing 3 preadolescent Geryons 5926 +monster: Executing 5 teenage Rocs 5783 +monster: Executing 9 Glabrezus 6428 +monster: Executing 7 Mastodons 5929 +monster: Executing 12 undernourished Umber Hulks 5860 +monster: Executing 3 undead Yeenoghus 6000 +monster: Executing 7 Humidity Giants 5727 +monster: Executing 3 enormous Orci 6067 +monster: Executing 5 underage Titans 6333 +monster: Executing 7 undernourished Beer Golems 6461 +monster: Executing 6 undernourished Oxygen Golems 6260 +monster: Executing a messianic Asmodeus 6258 +monster: Executing 7 underage Pentasauri 4914 +monster: Executing 7 teenage Storm Giants 5747 +monster: Executing 6 Elasmosauri 5625 +monster: Executing 6 veteran Chromatic Dragons 6309 +monster: Executing 4 comatose Brontosauri 6367 +monster: Executing 2 underage Asmodei 6181 +level: 3 +act: 1 +gold: 99 +stat: STR 13 +stat: CON 12 +stat: DEX 11 +stat: INT 13 +stat: WIS 15 +stat: CHA 9 +stat: HP Max 20 +stat: MP Max 21 +equipment: Weapon Andiron +equipment: Hauberk -1 Macrame +equipment: Brassairts Rusty Corroded Bearskin +equipment: Helm +1 Macrame +equipment: Cuisses Threadbare Flannel +equipment: Shield +1 Garbage Can Lid +equipment: Sollerets -1 Canvas +equipment: Greaves -1 Canvas +equipment: Gambeson Gilded Lace +equipment: Vambraces -2 Flannel +equipment: Gauntlets Macrame +inventory: yellow mold spore 1 +inventory: zombie forehead 1 +inventory: su-monster tail 1 +inventory: wight lung 1 +inventory: wolog lemma 1 +inventory: Gilded Diadem of Fortune 1 +inventory: Gilded Bijou of Silence 1 +inventory: Magnificent Tome of Punctuality 1 +inventory: Crafted Gammel of Acrimony 1 +inventory: Legendary Albatross of Torpor 1 +inventory: Dual Scabbard of Joy 1 +inventory: Enchanted Orb of Suffering 1 +inventory: Austere Gammel of Efficiency 1 +inventory: Spectral Gemstone of Efficiency 1 +inventory: Deadly Tome of Guile 1 +inventory: Crystalline Sceptre of Joy 1 +inventory: Magnificent Brocade of Guile 1 +spell: Magnetic Orb 1 +spell: Slime Finger 3 +spell: Shoelaces 2 +spell: Hastiness 1 +spell: Spectral Miasma 1 +spell: Seasick 1 +spell: Good Move 3 +spell: Hydrophobia 1 +spell: Lockjaw 1 +quest: Fetch me a teratoma +quest: Deliver this nail +quest: Deliver this egg +quest: Placate the Ogre Mages +quest: Seek the Filigreed Talisman +quest: Exterminate the Green Slimes +quest: Placate the Medusas +quest: Deliver this trowel +quest: Placate the Malebranches +quest: Fetch me a counterpane +quest: Deliver this plank +quest: Exterminate the Bugbears +quest: Placate the Sprites +quest: Deliver this vest +quest: Placate the Piercers +quest: Placate the Ghouls +quest: Deliver this rock +quest: Deliver this anvil +quest: Exterminate the Shadows +quest: Deliver this dirtclod +quest: Placate the Imps +quest: Exterminate the Octopi +quest: Deliver this newspaper +quest: Seek the Astral Orb +quest: Seek the One True Brocade +quest: Fetch me a teratoma +quest: Placate the Beholders +quest: Seek the Precious Cobble +quest: Seek the Ormolu Lamp +quest: Deliver this trowel +quest: Seek the Enchanted Fleece +quest: Placate the Whippets +quest: Deliver this bunny +quest: Exterminate the Malebranches +quest: Placate the Medusas +quest: Fetch me a sock +quest: Placate the Porcelain Giants +quest: Placate the Mini Giants +quest: Placate the Mind Flayers +quest: Seek the Puissant Hood +quest: Placate the Pit Fiends +quest: Placate the Catoblepases +quest: Fetch me a trowel diff --git a/tests/fixtures/parity_beta.txt b/tests/fixtures/parity_beta.txt new file mode 100644 index 0000000..e5d610a --- /dev/null +++ b/tests/fixtures/parity_beta.txt @@ -0,0 +1,1857 @@ +task: Loading +task: Experiencing an enigmatic and foreboding night vision +task: Much is revealed about that wise old bastard you'd underestimated +task: A shocking series of events leaves you alone and bewildered, but resolute +task: Drawing upon an unrealized reserve of determination, you set out on a long and dangerous journey +task: Loading Act I +task: Heading to the killing fields +task: Executing a greater Moth +task: Executing an underage Zombie +task: Executing a Cub Scout +task: Executing a Rot Grub +task: Executing a teenage Hippocampus +task: Executing a Dervish +task: Executing an undernourished Quasit +task: Executing a Battle-Ant +task: Executing a Manes +task: Executing an underage Girl Scout +task: Executing a passing Skraeling Slow Poisoner +task: Executing an Orc +task: Executing a Manes +task: Executing a Battle-Skeleton +task: Executing a Battle-Ant +task: Executing a sick Quasit +task: Executing an Ostrich +task: Executing a greater Moth +task: Executing an underage Green Slime +task: Executing a Yellow Mold +task: Executing a sick baby Hydra +task: Executing a Goblin +task: Heading to market to sell loot +task: Selling 2 moth dusts +task: Selling a zombie forehead +task: Selling a cub scout neckerchief +task: Selling a rot grub eggsac +task: Selling a hippocampus mane +task: Selling a dervish robe +task: Selling 2 quasit tails +task: Selling 2 ant antennas +task: Selling 2 manes tooths +task: Selling a girl scout cookie +task: Selling a Legendary Bijou of Fear +task: Selling an orc snout +task: Selling a skeleton clavicle +task: Selling an ostrich beak +task: Selling a green slime sample +task: Selling a yellow mold spore +task: Selling a hydra gyrum +task: Selling a goblin ear +task: Heading to the killing fields +task: Executing a comatose Minotaur +task: Executing a Bat +task: Executing a Cub Scout +task: Executing a Hobgoblin +task: Executing a passing Low Elf Runeloremaster +task: Executing a Stirge +task: Executing a Merman +task: Executing a Manes +task: Executing a Stirge +task: Executing an Eel +task: Executing a sick Shrieker +task: Executing an undernourished Troglodyte +task: Executing an undernourished Caveman +task: Executing a Pirate +task: Executing a Sprite +task: Executing a Stirge +task: Executing a Yellow Mold +task: Executing a crippled Poroid +task: Executing an Eel +task: Executing an underage Whippet +task: Executing a Rot Grub +task: Executing a Yellow Mold +task: Heading to market to sell loot +task: Selling a minotaur map +task: Selling a bat wing +task: Selling a cub scout neckerchief +task: Selling a hobgoblin patella +task: Selling a Precious Gimlet of Intrusion +task: Selling 3 stirge proboscises +task: Selling a merman trident +task: Selling a manes tooth +task: Selling 2 eel sashimis +task: Selling a shrieker stalk +task: Selling a troglodyte tail +task: Selling a caveman club +task: Selling a pirate booty +task: Selling a sprite can +task: Selling 2 yellow mold spores +task: Selling a poroid node +task: Selling a whippet collar +task: Selling a rot grub eggsac +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing Mr. Zickloun the Panda Man +task: Executing a Gnome +task: Executing a teenage Boy Scout +task: Executing Admiral Rivxieg the Land Squid +task: Executing an undernourished Triton +task: Executing an undernourished Homunculus +task: Executing an Ostrich +task: Executing an undernourished Girl Scout +task: Executing a greater Moth +task: Executing a preadolescent Hippocampus +task: Executing a passing Low Elf Ur-Paladin +task: Executing a Troglodyte +task: Executing a Yellow Mold +task: Executing a Skeleton +task: Executing an underage Boogie +task: Executing a Dwarf +task: Executing a Dervish +task: Executing a Gnome +task: Executing an Uruk +task: Executing a greater Centipede +task: Executing a Cub Scout +task: Executing an undernourished Caveman +task: Heading to market to sell loot +task: Selling a Gilded Corset of Loyalty +task: Selling 2 gnome hats +task: Selling a boy scout merit badge +task: Selling an Arcane Ornament of Submission +task: Selling a triton scale +task: Selling a homunculus fluid +task: Selling an ostrich beak +task: Selling a girl scout cookie +task: Selling a moth dust +task: Selling a hippocampus mane +task: Selling an Austere Fleece of Misapprehension +task: Selling a troglodyte tail +task: Selling a yellow mold spore +task: Selling a skeleton clavicle +task: Selling a boogie slime +task: Selling a dwarf drawers +task: Selling a dervish robe +task: Selling an uruk boot +task: Selling a centipede leg +task: Selling a cub scout neckerchief +task: Selling a caveman club +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Cub Scout +task: Executing a sick Harpy +task: Executing an undernourished Ghoul +task: Executing a Billy Goat +task: Executing a Swamp Elf +task: Executing a Goblin +task: Executing a Merman +task: Executing a greater Ostrich +task: Executing a teenage Blink Dog +task: Executing a Battle-Frog +task: Executing a greater Spider +task: Executing a Berserker +task: Executing an undernourished Gray Ooze +task: Executing a Merman +task: Executing a Hobgoblin +task: Executing a teenage Shrieker +task: Executing a sick Quasit +task: Executing a Hobgoblin +task: Executing a Stirge +task: Executing a Goblin +task: Executing a Battle-Fly +task: Executing a teenage Nymph +task: Heading to market to sell loot +task: Selling a cub scout neckerchief +task: Selling a harpy mascara +task: Selling a ghoul muscle +task: Selling a billy goat beard +task: Selling a swamp elf lilypad +task: Selling 2 goblin ears +task: Selling 2 merman tridents +task: Selling an ostrich beak +task: Selling a blink dog eyelid +task: Selling a frog leg +task: Selling a spider web +task: Selling a berserker shirt +task: Selling a gray ooze gravy +task: Selling 2 hobgoblin patellas +task: Selling a shrieker stalk +task: Selling a quasit tail +task: Selling a stirge proboscis +task: Selling a Blessed Gammel of Despair +task: Selling a nymph hanky +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Camel +task: Executing a Pirate +task: Executing a preadolescent Yeti +task: Executing a Sprite +task: Executing a Pixie +task: Executing a greater Rat +task: Executing a Girl Scout +task: Executing a Battle-Merman +task: Executing a Nebbish +task: Executing a Billy Goat +task: Executing a Gnome +task: Executing a Grid Bug +task: Executing a crippled preadolescent Spectre +task: Executing a teenage Harpy +task: Executing a veteran Yellow Mold +task: Executing a sick baby Brass Dragon +task: Executing an Imp +task: Executing a Yellow Mold +task: Executing a Zombie +task: Executing a greater Merman +task: Executing a sick Harpy +task: Executing an underage Dryad +task: Heading to market to sell loot +task: Selling a camel hump +task: Selling a pirate booty +task: Selling a yeti fur +task: Selling a sprite can +task: Selling a pixie dust +task: Selling a rat tail +task: Selling a girl scout cookie +task: Selling 2 merman tridents +task: Selling a nebbish belly +task: Selling a billy goat beard +task: Selling a gnome hat +task: Selling a grid bug carapace +task: Selling a spectre vestige +task: Selling 2 harpy mascaras +task: Selling 2 yellow mold spores +task: Selling a brass dragon pole +task: Selling an imp tail +task: Selling a zombie forehead +task: Selling a dryad acorn +task: Heading to the killing fields +task: Executing a Nebbish +task: Executing a greater Ostrich +task: Executing a Leprechaun +task: Executing a Bat +task: Executing a preadolescent Centaur +task: Executing a Cub Scout +task: Executing a Rat +task: Executing a Brown Elf +task: Executing an underage Girl Scout +task: Executing a sick Iron Peasant +task: Executing a Battle-Nebbish +task: Executing a Battle-Moth +task: Executing an underage Gnoll +task: Executing a Hobgoblin +task: Executing a Nebbish +task: Executing a Battle-Moth +task: Executing a Manes +task: Executing a Battle-Bat +task: Executing an undernourished Carrion Crawler +task: Executing an undernourished Uruk +task: Executing an underage Iron Peasant +task: Executing a Yellow Mold +task: Heading to market to sell loot +task: Selling 3 nebbish bellies +task: Selling an ostrich beak +task: Selling a leprechaun wallet +task: Selling 2 bat wings +task: Selling a centaur rib +task: Selling a cub scout neckerchief +task: Selling a rat tail +task: Selling a brown elf tusk +task: Selling a girl scout cookie +task: Selling 2 iron peasant chafves +task: Selling 2 moth dusts +task: Selling a gnoll collar +task: Selling a hobgoblin patella +task: Selling a manes tooth +task: Selling a carrion crawler egg +task: Selling an uruk boot +task: Selling a yellow mold spore +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a crippled Wraith +task: Executing a teenage Ghast +task: Executing a Mermaid +task: Executing a Merman +task: Executing an underage Wolf +task: Executing a Homunculus +task: Executing a Skeleton +task: Executing a Merman +task: Executing a Bat +task: Executing a Battle-Bunny +task: Executing a Swamp Elf +task: Executing a Mermaid +task: Executing an undernourished Green Slime +task: Executing a Dwarf +task: Executing an underage Violet Fungi +task: Executing a Cub Scout +task: Executing a Battle-Beagle +task: Executing a Manes +task: Executing a Goblin +task: Executing an Imp +task: Executing an underage Lizard Man +task: Executing an underage Green Slime +task: Heading to market to sell loot +task: Selling a wraith finger +task: Selling a ghast vomit +task: Selling 2 mermaid gillses +task: Selling 2 merman tridents +task: Selling a wolf paw +task: Selling a homunculus fluid +task: Selling a skeleton clavicle +task: Selling a bat wing +task: Selling a bunny ear +task: Selling a swamp elf lilypad +task: Selling 2 green slime samples +task: Selling a dwarf drawers +task: Selling a violet fungi spore +task: Selling a cub scout neckerchief +task: Selling a beagle collar +task: Selling a manes tooth +task: Selling a goblin ear +task: Selling an imp tail +task: Selling a lizard man tail +task: Heading to the killing fields +task: Executing an underage Green Slime +task: Executing an undernourished Green Slime +task: Executing a Leprechaun +task: Executing an underage Shrieker +task: Executing an undernourished Green Slime +task: Executing a Nebbish +task: Executing a sick Silkie +task: Executing a Merman +task: Executing a Rot Grub +task: Executing a preadolescent Pegasus +task: Executing a Stirge +task: Executing a teenage Triton +task: Executing an underage Green Slime +task: Executing an undernourished Green Slime +task: Executing a teenage Eagle Scout +task: Executing a Battle-Goblin +task: Executing a Green Slime +task: Executing a veteran Green Slime +task: Executing a Sprite +task: Executing an undernourished Girl Scout +task: Executing a Lizard Man +task: Executing a Pirate +task: Heading to market to sell loot +task: Selling 7 green slime samples +task: Selling a leprechaun wallet +task: Selling a shrieker stalk +task: Selling a nebbish belly +task: Selling a silkie fur +task: Selling a merman trident +task: Selling a rot grub eggsac +task: Selling a pegasus aileron +task: Selling a stirge proboscis +task: Selling a triton scale +task: Selling an eagle scout merit badge +task: Selling a goblin ear +task: Selling a sprite can +task: Selling a girl scout cookie +task: Selling a lizard man tail +task: Selling a pirate booty +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Green Slime +task: Executing a Stun Worm +task: Executing a Green Slime +task: Executing a greater Green Slime +task: Executing a Brown Elf +task: Executing an undernourished Boy Scout +task: Executing a Homunculus +task: Executing a teenage Quasit +task: Executing a comatose Succubus +task: Executing a Shadow +task: Executing a sick Boogie +task: Executing a Sprite +task: Executing a Lemure +task: Executing a veteran Green Slime +task: Executing a Pirate +task: Executing an underage Yeti +task: Executing a greater Manes +task: Executing a Stun Worm +task: Executing a greater Green Slime +task: Executing a Green Slime +task: Executing an undernourished Carrion Crawler +task: Executing an underage Shadow +task: Heading to market to sell loot +task: Selling 6 green slime samples +task: Selling 2 stun worm trodes +task: Selling a brown elf tusk +task: Selling a boy scout merit badge +task: Selling a homunculus fluid +task: Selling a quasit tail +task: Selling a succubus bra +task: Selling 2 shadow silhouettes +task: Selling a boogie slime +task: Selling a sprite can +task: Selling a lemure blob +task: Selling a pirate booty +task: Selling a yeti fur +task: Selling a manes tooth +task: Selling a carrion crawler egg +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Bat +task: Executing a Jumpskin +task: Executing a veteran Green Slime +task: Executing a Hogbird +task: Executing a massive Dervish +task: Executing an undernourished Lizard Man +task: Executing a veteran Lizard Man +task: Executing a Multicell +task: Executing a Green Slime +task: Executing an undernourished Doppelganger +task: Executing a Battle-Bunny +task: Executing a Violet Fungi +task: Executing an undernourished Green Slime +task: Executing a Green Slime +task: Executing an underage Man-o-war +task: Executing a greater Pixie +task: Executing a cursed Zombie +task: Executing an undernourished Boogie +task: Executing a veteran Green Slime +task: Executing a Girl Scout +task: Executing a Rot Grub +task: Executing an underage Green Slime +task: Heading to market to sell loot +task: Selling a bat wing +task: Selling a jumpskin shin +task: Selling 6 green slime samples +task: Selling a hogbird curl +task: Selling a dervish robe +task: Selling 2 lizard man tails +task: Selling a multicell dendrite +task: Selling a doppelganger face +task: Selling a bunny ear +task: Selling a violet fungi spore +task: Selling a man-o-war tentacle +task: Selling a pixie dust +task: Selling a zombie forehead +task: Selling a boogie slime +task: Selling a girl scout cookie +task: Selling a rot grub eggsac +task: Heading to the killing fields +task: Executing a greater Wasp +task: Executing a Battle-Dryad +task: Executing a Ghoul +task: Executing an underage Shadow +task: Executing a greater Sea Elf +task: Executing a greater Hobgoblin +task: Executing an undernourished Green Slime +task: Executing a Lemure +task: Executing a Jumpskin +task: Executing a Swamp Elf +task: Executing a crippled baby Beige Dragon +task: Executing an underage Caveman +task: Executing an underage Wight +task: Executing an underage Green Slime +task: Executing an undernourished Lizard Man +task: Executing a greater Green Slime +task: Executing a Battle-Ghoul +task: Executing a greater Green Slime +task: Executing a Homunculus +task: Executing an Uruk +task: Executing a Hobgoblin +task: Executing a massive Rat +task: Heading to market to sell loot +task: Selling a wasp stinger +task: Selling a dryad acorn +task: Selling 2 ghoul muscles +task: Selling a shadow silhouette +task: Selling a sea elf jerkin +task: Selling 2 hobgoblin patellas +task: Selling 4 green slime samples +task: Selling a lemure blob +task: Selling a jumpskin shin +task: Selling a swamp elf lilypad +task: Selling a Reverential Bijou of Suffering +task: Selling a caveman club +task: Selling a wight lung +task: Selling a lizard man tail +task: Selling a homunculus fluid +task: Selling an uruk boot +task: Selling a rat tail +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Zombie +task: Executing a Green Slime +task: Executing a greater Troglodyte +task: Executing an undernourished Bugbear +task: Executing a Green Slime +task: Executing a sick Ghast +task: Executing a massive Stirge +task: Executing an undernourished Green Slime +task: Executing an underage Green Slime +task: Executing a greater Ant +task: Executing a greater Swamp Elf +task: Executing Sir Ixkrin the Half Orc +task: Executing a Battle-Dwarf +task: Executing a Green Slime +task: Executing a greater Sea Elf +task: Executing an Ogre +task: Executing a Dwarf +task: Executing a Nebbish +task: Executing a massive Nebbish +task: Executing a Green Slime +task: Executing a Piercer +task: Executing a Green Slime +task: Heading to market to sell loot +task: Selling a zombie forehead +task: Selling 7 green slime samples +task: Selling a troglodyte tail +task: Selling a bugbear skin +task: Selling a ghast vomit +task: Selling a stirge proboscis +task: Selling an ant antenna +task: Selling a swamp elf lilypad +task: Selling a Cruciate Candelabra of Intrusion +task: Selling 2 dwarf drawerses +task: Selling a sea elf jerkin +task: Selling an ogre talon +task: Selling 2 nebbish bellies +task: Selling a piercer tip +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Shrieker +task: Executing a Green Slime +task: Executing Sgt. Grogog the Land Squid +task: Executing a greater Nebbish +task: Executing an undernourished Boy Scout +task: Executing a Green Slime +task: Executing an underage Harpy +task: Executing a passing Half Man Runeloremaster +task: Executing an underage Homunculus +task: Executing an undernourished Lemure +task: Executing a Berserker +task: Executing a Shadow +task: Executing a Battle-Ostrich +task: Executing an underage Iron Peasant +task: Executing a Green Slime +task: Executing a Bat +task: Executing an undernourished Green Slime +task: Executing a Pixie +task: Executing an Imp +task: Executing a massive Nebbish +task: Executing a Lizard Man +task: Executing a Harpy +task: Heading to market to sell loot +task: Selling a shrieker stalk +task: Selling 4 green slime samples +task: Selling a Garlanded Scabbard of Ra +task: Selling 2 nebbish bellies +task: Selling a boy scout merit badge +task: Selling 2 harpy mascaras +task: Selling a Magnificent Sceptre of Torpor +task: Selling a homunculus fluid +task: Selling a lemure blob +task: Selling a berserker shirt +task: Selling a shadow silhouette +task: Selling an ostrich beak +task: Selling an iron peasant chaff +task: Selling a bat wing +task: Selling a pixie dust +task: Selling an imp tail +task: Selling a lizard man tail +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an undernourished Eagle Scout +task: Executing a teenage Sea Hag +task: Executing a Mermaid +task: Executing an undernourished Ghoul +task: Executing a Ghoul +task: Executing a Leprechaun +task: Executing an undernourished Sea Hag +task: Executing a Gnoll +task: Executing a teenage Sea Hag +task: Executing an undernourished Sea Hag +task: Executing a Piercer +task: Executing a veteran Grid Bug +task: Executing a massive Frog +task: Executing a Wolf +task: Executing a Multicell +task: Executing an underage Sea Hag +task: Executing an undernourished Lizard Man +task: Executing a Battle-Uruk +task: Executing an underage Bugboar +task: Executing an underage Sea Hag +task: Executing a passing Skraeling Puma Burgular +task: Executing a Sea Elf +task: Heading to market to sell loot +task: Selling an eagle scout merit badge +task: Selling 6 sea hag warts +task: Selling a mermaid gills +task: Selling 2 ghoul muscles +task: Selling a leprechaun wallet +task: Selling a gnoll collar +task: Selling a piercer tip +task: Selling a grid bug carapace +task: Selling a frog leg +task: Selling a wolf paw +task: Selling a multicell dendrite +task: Selling a lizard man tail +task: Selling an uruk boot +task: Selling a bugboar tusk +task: Selling an Unearthly Candelabra of Grob +task: Selling a sea elf jerkin +task: Heading to the killing fields +task: Executing a Violet Fungi +task: Executing an underage Sea Hag +task: Executing a preadolescent Centaur +task: Executing an undernourished Gray Ooze +task: Executing a teenage Triton +task: Executing an underage Sea Hag +task: Executing a Battle-Wasp +task: Executing a Yellow Mold +task: Executing a Ghoul +task: Executing a comatose Stun Bear +task: Executing an undernourished Nymph +task: Executing an underage Ghast +task: Executing a Stun Worm +task: Executing an undernourished Sea Hag +task: Executing a Girl Scout +task: Executing a Boogie +task: Executing a teenage Sea Hag +task: Executing a massive Lizard Man +task: Executing an underage Girl Scout +task: Executing an underage Triton +task: Executing an undernourished Violet Fungi +task: Executing an undernourished Piercer +task: Heading to market to sell loot +task: Selling 2 violet fungi spores +task: Selling 4 sea hag warts +task: Selling a centaur rib +task: Selling a gray ooze gravy +task: Selling 2 triton scales +task: Selling a wasp stinger +task: Selling a yellow mold spore +task: Selling a ghoul muscle +task: Selling a stun bear tooth +task: Selling a nymph hanky +task: Selling a ghast vomit +task: Selling a stun worm trode +task: Selling 2 girl scout cookies +task: Selling a boogie slime +task: Selling a lizard man tail +task: Selling a piercer tip +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a greater Sprite +task: Executing a Skeleton +task: Executing a passing Lesser Dwarf Inner Mason +task: Executing a Gnoll +task: Executing a Girl Scout +task: Executing a Battle-Manes +task: Executing an undernourished Quasit +task: Executing a Skeleton +task: Executing an undernourished Ghoul +task: Executing an underage Eel +task: Executing an undernourished Green Slime +task: Executing Sgt. Nonjock the Battle-Finch +task: Executing an undernourished Boy Scout +task: Executing a Sea Hag +task: Executing a Triton +task: Executing an undernourished Harpy +task: Executing an underage Multicell +task: Executing a Leprechaun +task: Executing a Silkie +task: Executing a sick Boy Scout +task: Executing a Battle-Leprechaun +task: Executing Sgt. Tramtrib the Enchanted Motorcycle +task: Heading to market to sell loot +task: Selling a sprite can +task: Selling 2 skeleton clavicles +task: Selling an Ostentatious Candelabra of Sisu +task: Selling a gnoll collar +task: Selling a girl scout cookie +task: Selling a manes tooth +task: Selling a quasit tail +task: Selling a ghoul muscle +task: Selling an eel sashimi +task: Selling a green slime sample +task: Selling a Dual Gimlet of Ra +task: Selling 2 boy scout merit badges +task: Selling a sea hag wart +task: Selling a triton scale +task: Selling a harpy mascara +task: Selling a multicell dendrite +task: Selling 2 leprechaun wallets +task: Selling a silkie fur +task: Selling an Enchanted Gammel of Nervousness +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a teenage Ogre +task: Executing Sgt. Trukjig the Talking Pony +task: Executing an underage Hippogriff +task: Executing an Octopus +task: Executing a Koala +task: Executing an underage Gelatinous Cube +task: Executing a Caveman +task: Executing an Uruk +task: Executing a preadolescent Troll +task: Executing a Brown Elf +task: Executing Sir Zoomlux the Enchanted Motorcycle +task: Executing a Troglodyte +task: Executing an underage Lizard Man +task: Executing a Battle-Midge +task: Executing Mr. Epvib the Skraeling +task: Executing a Billy Goat +task: Executing an underage Hippogriff +task: Executing an undernourished Wolf +task: Executing a massive Beagle +task: Executing a passing Land Squid Slow Poisoner +task: Executing a Grid Bug +task: Executing a greater Imp +task: Heading to market to sell loot +task: Selling an ogre talon +task: Selling a Garlanded Laurel of Intrusion +task: Selling 2 hippogriff eggs +task: Selling an octopus beak +task: Selling a koala heart +task: Selling a gelatinous cube jam +task: Selling a caveman club +task: Selling an uruk boot +task: Selling a troll hide +task: Selling a brown elf tusk +task: Selling a Puissant Galoon of Fortune +task: Selling a troglodyte tail +task: Selling a lizard man tail +task: Selling a midge corpse +task: Selling a Benevolent Candelabra of Envy +task: Selling a billy goat beard +task: Selling a wolf paw +task: Selling a beagle collar +task: Selling an Arcane Brocade of Danger +task: Selling a grid bug carapace +task: Selling an imp tail +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an undernourished Ape +task: Executing Captain Yevtrok the Lesser Dwarf +task: Executing a Battle-Koala +task: Executing an underage Sylph +task: Executing a Harpy +task: Executing a greater Zombie +task: Executing a massive Brown Elf +task: Executing a Bat +task: Executing a Pixie +task: Executing a Kobold +task: Executing a greater Dervish +task: Executing an undernourished Bugbear +task: Executing a cursed Ostrich +task: Executing a Battle-Uruk +task: Executing a Lizard Man +task: Executing a sick Rust Monster +task: Executing a Leprechaun +task: Executing a greater Leprechaun +task: Executing a teenage Mini Giant +task: Executing an Unicorn +task: Executing an enormous Fly +task: Executing a Manes +task: Heading to market to sell loot +task: Selling an ape ass +task: Selling an Ormolu Gimcrack of Extroversion +task: Selling a koala heart +task: Selling a sylph thigh +task: Selling a harpy mascara +task: Selling a zombie forehead +task: Selling a brown elf tusk +task: Selling a bat wing +task: Selling a pixie dust +task: Selling a kobold penis +task: Selling a dervish robe +task: Selling a bugbear skin +task: Selling an ostrich beak +task: Selling an uruk boot +task: Selling a lizard man tail +task: Selling a rust monster shavings +task: Selling 2 leprechaun wallets +task: Selling a mini giant pompadour +task: Selling an unicorn blood +task: Selling a Crafted Garnet of Patience +task: Selling a manes tooth +task: Negotiating purchase of better equipment +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Lizard Man +task: Executing an underage Koala +task: Executing a cursed Pixie +task: Executing a Gnome +task: Executing a crippled Pegasus +task: Executing an undernourished Gray Ooze +task: Executing a sick Peryton +task: Executing a Battle-Sprite +task: Executing a Pirate +task: Executing a Shadow +task: Executing a Sylph +task: Executing an Octopus +task: Executing a Pirate +task: Executing a Camel +task: Executing a Gnoll +task: Executing a Nixie +task: Executing a veteran Grid Bug +task: Executing an underage Gnoll +task: Executing an underage Dryad +task: Executing a Sprite +task: Executing an undernourished Hippogriff +task: Executing a greater Whippet +task: Heading to market to sell loot +task: Selling a lizard man tail +task: Selling a koala heart +task: Selling a pixie dust +task: Selling a gnome hat +task: Selling a pegasus aileron +task: Selling a gray ooze gravy +task: Selling a peryton antler +task: Selling 2 sprite cans +task: Selling 2 pirate booties +task: Selling a shadow silhouette +task: Selling a sylph thigh +task: Selling an octopus beak +task: Selling a camel hump +task: Selling 2 gnoll collars +task: Selling a nixie webbing +task: Selling a grid bug carapace +task: Selling a dryad acorn +task: Selling a hippogriff egg +task: Selling a whippet collar +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a sick Wraith +task: Executing a Jumpskin +task: Executing a Troglodyte +task: Executing a Hogbird +task: Executing a greater Goblin +task: Executing a Battle-Bat +task: Executing a Yellow Mold +task: Executing a Billy Goat +task: Executing a Girl Scout +task: Executing a Battle-Gnoll +task: Executing a greater Yellow Mold +task: Executing a greater Berserker +task: Executing Sgt. Uxxoom the Hob-Hobbit +task: Executing a greater Pixie +task: Executing an underage Nymph +task: Executing a greater Cub Scout +task: Executing a Multicell +task: Executing a Man-o-war +task: Executing an Orc +task: Executing a greater Orc +task: Executing a passing Crested Dwarf Vermineer +task: Executing a greater Cub Scout +task: Heading to market to sell loot +task: Selling a wraith finger +task: Selling a jumpskin shin +task: Selling a troglodyte tail +task: Selling a hogbird curl +task: Selling a goblin ear +task: Selling a bat wing +task: Selling 2 yellow mold spores +task: Selling a billy goat beard +task: Selling a girl scout cookie +task: Selling a gnoll collar +task: Selling a berserker shirt +task: Selling an Ostentatious Orb of Despair +task: Selling a pixie dust +task: Selling a nymph hanky +task: Selling 2 cub scout neckerchieves +task: Selling a multicell dendrite +task: Selling a man-o-war tentacle +task: Selling 2 orc snouts +task: Selling a Mythic Cobble of Fortune +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a preadolescent Satyr +task: Executing a Blink Dog +task: Executing a greater Orc +task: Executing a Bat +task: Executing Sir Proozproun the Gyrognome +task: Executing a greater Manes +task: Executing an underage Harpy +task: Executing a Pixie +task: Executing an undernourished Lizard Man +task: Executing a cursed Cub Scout +task: Executing a Troglodyte +task: Executing an undernourished Whippet +task: Executing a Pixie +task: Executing a Grid Bug +task: Executing a Multicell +task: Executing a teenage Gray Ooze +task: Executing an underage Violet Fungi +task: Executing an undernourished Violet Fungi +task: Executing Sgt. Ickat the Half Orc +task: Executing a Leprechaun +task: Executing an undernourished Sylph +task: Executing a Battle-Goblin +task: Heading to market to sell loot +task: Selling a satyr hoof +task: Selling a blink dog eyelid +task: Selling an orc snout +task: Selling a bat wing +task: Selling a Sacred Fleece of Incarceration +task: Selling a manes tooth +task: Selling a harpy mascara +task: Selling 2 pixie dusts +task: Selling a lizard man tail +task: Selling a cub scout neckerchief +task: Selling a troglodyte tail +task: Selling a whippet collar +task: Selling a grid bug carapace +task: Selling a multicell dendrite +task: Selling a gray ooze gravy +task: Selling 2 violet fungi spores +task: Selling a Garlanded Albatross of Domination +task: Selling a leprechaun wallet +task: Selling a sylph thigh +task: Selling a goblin ear +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Camel +task: Executing a Were-Rat +task: Executing a greater Shrieker +task: Executing a Harpy +task: Executing a Lemure +task: Executing a Battle-Triton +task: Executing an undernourished Hippogriff +task: Executing a greater Frog +task: Executing a Girl Scout +task: Executing a Swamp Elf +task: Executing a Stirge +task: Executing a Hogbird +task: Executing an undernourished Jumpskin +task: Executing a Skeleton +task: Executing an underage Yeti +task: Executing an underage Sea Hag +task: Executing a Zombie +task: Executing an undernourished Eel +task: Executing a Battle-Multicell +task: Executing a Manes +task: Executing an underage Triton +task: Executing a sick Bugbear +task: Heading to market to sell loot +task: Selling a camel hump +task: Selling a rat tail +task: Selling a shrieker stalk +task: Selling a harpy mascara +task: Selling a lemure blob +task: Selling 2 triton scales +task: Selling a hippogriff egg +task: Selling a frog leg +task: Selling a girl scout cookie +task: Selling a swamp elf lilypad +task: Selling a stirge proboscis +task: Selling a hogbird curl +task: Selling a jumpskin shin +task: Selling a skeleton clavicle +task: Selling a yeti fur +task: Selling a sea hag wart +task: Selling a zombie forehead +task: Selling an eel sashimi +task: Selling a multicell dendrite +task: Selling a manes tooth +task: Selling a bugbear skin +task: Heading to the killing fields +task: Executing a Battle-Shadow +task: Executing a teenage Lemure +task: Executing an underage Koala +task: Executing an underage Wolog +task: Executing a passing Lesser Dwarf Ur-Paladin +task: Executing a Green Slime +task: Executing a greater Hobgoblin +task: Executing a Gnoll +task: Executing an underage Sea Hag +task: Executing a Lizard Man +task: Executing a teenage Blink Dog +task: Executing a sick Yeti +task: Executing an undernourished Gray Ooze +task: Executing an underage Ghoul +task: Executing a Shadow +task: Executing a Cub Scout +task: Executing a Boogie +task: Executing a Girl Scout +task: Executing an undernourished Homunculus +task: Executing a Homunculus +task: Executing a Sea Elf +task: Executing a teenage Yeti +task: Heading to market to sell loot +task: Selling 2 shadow silhouettes +task: Selling a lemure blob +task: Selling a koala heart +task: Selling a wolog lemma +task: Selling a Crystalline Lamp of Foreshadowing +task: Selling a green slime sample +task: Selling a hobgoblin patella +task: Selling a gnoll collar +task: Selling a sea hag wart +task: Selling a lizard man tail +task: Selling a blink dog eyelid +task: Selling 2 yeti furs +task: Selling a gray ooze gravy +task: Selling a ghoul muscle +task: Selling a cub scout neckerchief +task: Selling a boogie slime +task: Selling a girl scout cookie +task: Selling 2 homunculus fluids +task: Selling a sea elf jerkin +task: Negotiating purchase of better equipment +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Bugboar +task: Executing a Girl Scout +task: Executing an underage Imp +task: Executing a greater Centipede +task: Executing a massive Manes +task: Executing an Ostrich +task: Executing an Ostrich +task: Executing a Harpy +task: Executing a Rot Grub +task: Executing a cursed Bunny +task: Executing an underage Bugbear +task: Executing a Troglodyte +task: Executing a greater Bat +task: Executing an Orc +task: Executing an undernourished Boy Scout +task: Executing a Battle-Wasp +task: Executing a Caveman +task: Executing an Imp +task: Executing an undernourished Gray Ooze +task: Executing an Octopus +task: Executing a Manes +task: Executing a Blink Dog +task: Heading to market to sell loot +task: Selling a bugboar tusk +task: Selling a girl scout cookie +task: Selling 2 imp tails +task: Selling a centipede leg +task: Selling 2 manes tooths +task: Selling 2 ostrich beaks +task: Selling a harpy mascara +task: Selling a rot grub eggsac +task: Selling a bunny ear +task: Selling a bugbear skin +task: Selling a troglodyte tail +task: Selling a bat wing +task: Selling an orc snout +task: Selling a boy scout merit badge +task: Selling a wasp stinger +task: Selling a caveman club +task: Selling a gray ooze gravy +task: Selling an octopus beak +task: Selling a blink dog eyelid +task: Heading to the killing fields +task: Executing an underage Caveman +task: Executing a Stun Worm +task: Executing a Ghast +task: Executing a Nixie +task: Executing an undernourished Camel +task: Executing a greater Sprite +task: Executing an underage Harpy +task: Executing an undernourished Piercer +task: Executing a teenage Peryton +task: Executing an Eel +task: Executing a Man-o-war +task: Executing a greater Whippet +task: Executing a cursed Imp +task: Executing a Bat +task: Executing a massive Brown Elf +task: Executing a Dryad +task: Executing a Yellow Mold +task: Executing a greater Ant +task: Executing a greater Mermaid +task: Executing a greater Pixie +task: Executing a greater Crayfish +task: Executing Ms. Grugwhov the Low Elf +task: Heading to market to sell loot +task: Selling a caveman club +task: Selling a stun worm trode +task: Selling a ghast vomit +task: Selling a nixie webbing +task: Selling a camel hump +task: Selling a sprite can +task: Selling a harpy mascara +task: Selling a piercer tip +task: Selling a peryton antler +task: Selling an eel sashimi +task: Selling a man-o-war tentacle +task: Selling a whippet collar +task: Selling an imp tail +task: Selling a bat wing +task: Selling a brown elf tusk +task: Selling a dryad acorn +task: Selling a yellow mold spore +task: Selling an ant antenna +task: Selling a mermaid gills +task: Selling a pixie dust +task: Selling a crayfish antenna +task: Selling an Arcane Spangle of Foreboding +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Rat +task: Executing an undernourished Multicell +task: Executing a Whippet +task: Executing a greater Brown Elf +task: Executing a veteran Sea Elf +task: Executing a Jumpskin +task: Executing a preadolescent Yeti +task: Executing a greater Jumpskin +task: Executing a Stirge +task: Executing a greater Camel +task: Executing a Lizard Man +task: Executing a Poroid +task: Executing a Violet Fungi +task: Executing a Rot Grub +task: Executing a Boy Scout +task: Executing a Battle-Hobgoblin +task: Executing a Sylph +task: Executing a Battle-Spider +task: Executing a cursed Moth +task: Executing an undernourished Shrieker +task: Executing a Silkie +task: Executing a Leprechaun +task: Heading to market to sell loot +task: Selling a rat tail +task: Selling a multicell dendrite +task: Selling a whippet collar +task: Selling a brown elf tusk +task: Selling a sea elf jerkin +task: Selling 2 jumpskin shins +task: Selling a yeti fur +task: Selling a stirge proboscis +task: Selling a camel hump +task: Selling a lizard man tail +task: Selling a poroid node +task: Selling a violet fungi spore +task: Selling a rot grub eggsac +task: Selling a boy scout merit badge +task: Selling a hobgoblin patella +task: Selling a sylph thigh +task: Selling a spider web +task: Selling a moth dust +task: Selling a shrieker stalk +task: Selling a silkie fur +task: Selling a leprechaun wallet +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Wight +task: Executing a Pegasus +task: Executing a passing Enchanted Motorcycle Fighter/Organist +task: Executing a Wight +task: Executing a Man-o-war +task: Executing a Hogbird +task: Executing a Hogbird +task: Executing a Centaur +task: Executing a Gelatinous Cube +task: Executing an underage Stun Bear +task: Executing an undernourished Dryad +task: Executing a Boy Scout +task: Executing a Hippogriff +task: Executing a teenage Hogbird +task: Executing a teenage Hogbird +task: Executing a Dwarf +task: Executing a greater Mermaid +task: Executing a Swamp Elf +task: Executing a Lemure +task: Executing a crippled Cockatrice +task: Executing a Hogbird +task: Executing a Sea Hag +task: Heading to market to sell loot +task: Selling 2 wight lungs +task: Selling a pegasus aileron +task: Selling a Crafted Ankh of Rapidity +task: Selling a man-o-war tentacle +task: Selling 5 hogbird curls +task: Selling a centaur rib +task: Selling a gelatinous cube jam +task: Selling a stun bear tooth +task: Selling a dryad acorn +task: Selling a boy scout merit badge +task: Selling a hippogriff egg +task: Selling a dwarf drawers +task: Selling a mermaid gills +task: Selling a swamp elf lilypad +task: Selling a lemure blob +task: Selling a cockatrice wattle +task: Selling a sea hag wart +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a massive Leprechaun +task: Executing a Hogbird +task: Executing a greater Dryad +task: Executing a passing Low Elf Puma Burgular +task: Executing a Battle-Harpy +task: Executing a greater Stun Worm +task: Executing a cursed Bat +task: Executing a veteran Billy Goat +task: Executing Mrs. Zuxouck the Skraeling +task: Executing a Stun Worm +task: Executing a Girl Scout +task: Executing a Gnome +task: Executing a sick Iguanadon +task: Executing a greater Berserker +task: Executing an Eel +task: Executing a greater Ogre Mage +task: Executing a Battle-Harpy +task: Executing a Battle-Hippocampus +task: Executing a Hippocampus +task: Executing an undernourished Doppelganger +task: Executing a sick Djinn +task: Executing an underage Triton +task: Executing a Man-o-war +task: Heading to market to sell loot +task: Selling a leprechaun wallet +task: Selling a hogbird curl +task: Selling a dryad acorn +task: Selling a Crystalline Lamp of Suffering +task: Selling 2 harpy mascaras +task: Selling 2 stun worm trodes +task: Selling a bat wing +task: Selling a billy goat beard +task: Selling an Iron Tome of Hurting +task: Selling a girl scout cookie +task: Selling a gnome hat +task: Selling an iguanadon thumb +task: Selling a berserker shirt +task: Selling an eel sashimi +task: Selling an ogre mage apparel +task: Selling 2 hippocampus manes +task: Selling a doppelganger face +task: Selling a djinn lamp +task: Selling a triton scale +task: Selling a man-o-war tentacle +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a cursed Mermaid +task: Executing a Bugboar +task: Executing an undernourished Harpy +task: Executing a Sea Elf +task: Executing an undernourished Wight +task: Executing a greater Violet Fungi +task: Executing an Ogre Mage +task: Executing a cursed Stun Worm +task: Executing a Battle-Boogie +task: Executing a greater Iron Peasant +task: Executing a greater Homunculus +task: Executing an underage Centaur +task: Executing a Stun Worm +task: Executing an undernourished Ape +task: Executing a Ghast +task: Executing a Man-o-war +task: Executing a passing Eel Man Lowling +task: Executing a Battle-Yeti +task: Executing an undernourished Carrion Crawler +task: Executing an Iron Peasant +task: Executing an Ape +task: Executing a Ghoul +task: Executing an undernourished Boogie +task: Heading to market to sell loot +task: Selling a mermaid gills +task: Selling a bugboar tusk +task: Selling a harpy mascara +task: Selling a sea elf jerkin +task: Selling a wight lung +task: Selling a violet fungi spore +task: Selling an ogre mage apparel +task: Selling 2 stun worm trodes +task: Selling 2 boogie slimes +task: Selling 2 iron peasant chafves +task: Selling a homunculus fluid +task: Selling a centaur rib +task: Selling 2 ape asses +task: Selling a ghast vomit +task: Selling a man-o-war tentacle +task: Selling an Ormolu Gammel of Fortune +task: Selling a yeti fur +task: Selling a carrion crawler egg +task: Selling a ghoul muscle +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a sick Troll +task: Executing a Battle-Imp +task: Executing a Battle-Quasit +task: Executing a Violet Fungi +task: Executing a greater Yeti +task: Executing a teenage Owlbear +task: Executing a Wolf +task: Executing a Centaur +task: Executing a Bugboar +task: Executing a Stun Worm +task: Executing an Octopus +task: Executing an undernourished Malebranche +task: Executing a veteran Swamp Elf +task: Executing a Battle-Dervish +task: Executing a Hogbird +task: Executing a veteran Green Slime +task: Executing an underage Hogbird +task: Executing a Were-Midge +task: Executing a Battle-Multicell +task: Executing a Battle-Mermaid +task: Executing a Gray Ooze +task: Executing Chief Brammooz the Low Elf +task: Executing a massive Manes +task: Heading to market to sell loot +task: Selling a troll hide +task: Selling an imp tail +task: Selling a quasit tail +task: Selling a violet fungi spore +task: Selling a yeti fur +task: Selling an owlbear feather +task: Selling a wolf paw +task: Selling a centaur rib +task: Selling a bugboar tusk +task: Selling a stun worm trode +task: Selling an octopus beak +task: Selling a malebranche fork +task: Selling a swamp elf lilypad +task: Selling a dervish robe +task: Selling 2 hogbird curls +task: Selling a green slime sample +task: Selling a midge corpse +task: Selling a multicell dendrite +task: Selling a mermaid gills +task: Selling a gray ooze gravy +task: Selling a Golden Gimlet of Chaos +task: Selling a manes tooth +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Lizard Man +task: Executing an enormous Kobold +task: Executing an undernourished Hogbird +task: Executing an undernourished Doppelganger +task: Executing a teenage Troll +task: Executing a veteran Stun Worm +task: Executing an undernourished Owlbear +task: Executing a Carrion Crawler +task: Executing a veteran Boy Scout +task: Executing a greater Octopus +task: Executing a Mini Giant +task: Executing a Nymph +task: Executing a sick Man-o-war +task: Executing an undernourished Green Slime +task: Executing a Ghast +task: Executing Captain Crikyim the Dung Elf +task: Executing an enormous Bunny +task: Executing a veteran Cub Scout +task: Executing an undernourished Lemure +task: Executing a cursed Mermaid +task: Executing a greater Boy Scout +task: Executing an Uruk +task: Executing a Jumpskin +task: Heading to market to sell loot +task: Selling a lizard man tail +task: Selling a kobold penis +task: Selling a hogbird curl +task: Selling a doppelganger face +task: Selling a troll hide +task: Selling a stun worm trode +task: Selling an owlbear feather +task: Selling a carrion crawler egg +task: Selling 2 boy scout merit badges +task: Selling an octopus beak +task: Selling a mini giant pompadour +task: Selling a nymph hanky +task: Selling a man-o-war tentacle +task: Selling a green slime sample +task: Selling a ghast vomit +task: Selling a Sacred Gimcrack of Nervousness +task: Selling a bunny ear +task: Selling a cub scout neckerchief +task: Selling a lemure blob +task: Selling a mermaid gills +task: Selling an uruk boot +task: Selling a jumpskin shin +task: Heading to the killing fields +task: Executing a Carrion Crawler +task: Executing an underage Su-monster +task: Executing Mrs. Proockdriez the Talking Pony +task: Executing an underage Hippogriff +task: Executing an underage Ogre +task: Executing a greater Quasit +task: Executing a greater Grid Bug +task: Executing a passing Hob-Hobbit Slow Poisoner +task: Executing a passing Double Wookiee Tickle-Mimic +task: Executing a Ghast +task: Executing a Sylph +task: Executing a greater Eel +task: Executing a Boy Scout +task: Executing a Boogie +task: Executing a Wolf +task: Executing a Manes +task: Executing a crippled Ankylosaurus +task: Executing a veteran Gray Ooze +task: Executing a greater Centaur +task: Executing a teenage Lemure +task: Executing a cursed Octopus +task: Executing a Lizard Man +task: Executing a Camel +task: Heading to market to sell loot +task: Selling a carrion crawler egg +task: Selling a su-monster tail +task: Selling a Fearsome Tome of Cruelty +task: Selling a hippogriff egg +task: Selling an ogre talon +task: Selling a quasit tail +task: Selling a grid bug carapace +task: Selling a Puissant Corset of Perspicacity +task: Selling an Austere Lens of Prurience +task: Selling a ghast vomit +task: Selling a sylph thigh +task: Selling an eel sashimi +task: Selling a boy scout merit badge +task: Selling a boogie slime +task: Selling a wolf paw +task: Selling a manes tooth +task: Selling an ankylosaurus tail +task: Selling a gray ooze gravy +task: Selling a centaur rib +task: Selling a lemure blob +task: Selling an octopus beak +task: Selling a lizard man tail +task: Selling a camel hump +task: Negotiating purchase of better equipment +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Su-monster +task: Executing a Piercer +task: Executing a Stun Worm +task: Executing a Harpy +task: Executing a Gray Ooze +task: Executing a Gnoll +task: Executing a teenage Lemure +task: Executing a Harpy +task: Executing an underage Iron Peasant +task: Executing a greater Merman +task: Executing a greater Sea Elf +task: Executing a Bugbear +task: Executing an underage Catoblepas +task: Executing a Yeti +task: Executing a crippled Xorn +task: Executing an undernourished Wraith +task: Executing a Leprechaun +task: Executing a passing Dung Elf Lowling +task: Executing an underage Wraith +task: Executing an Uruk +task: Executing a teenage Su-monster +task: Executing an undernourished Triton +task: Executing a Hippogriff +task: Heading to market to sell loot +task: Selling 2 su-monster tails +task: Selling a piercer tip +task: Selling a stun worm trode +task: Selling 2 harpy mascaras +task: Selling a gray ooze gravy +task: Selling a gnoll collar +task: Selling a lemure blob +task: Selling an iron peasant chaff +task: Selling a merman trident +task: Selling a sea elf jerkin +task: Selling a bugbear skin +task: Selling a catoblepas neck +task: Selling a yeti fur +task: Selling a xorn jaw +task: Selling 2 wraith fingers +task: Selling a leprechaun wallet +task: Selling a Magnificent Orb of Submission +task: Selling an uruk boot +task: Selling a triton scale +task: Selling a hippogriff egg +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Boy Scout +task: Executing an underage Silkie +task: Executing an undernourished Iron Peasant +task: Executing a Battle-Rat +task: Executing a greater Koala +task: Executing an underage Wyvern +task: Executing a Wolog +task: Executing a veteran Girl Scout +task: Executing a Peryton +task: Executing an undernourished Yeti +task: Executing a Triton +task: Executing an undernourished Ogre +task: Executing a Homunculus +task: Executing a Nymph +task: Executing an underage Stun Bear +task: Executing a greater Hogbird +task: Executing a greater Green Slime +task: Executing a Ghast +task: Executing a greater Octopus +task: Executing an underage Silkie +task: Executing a Battle-Mermaid +task: Executing an undernourished Gargoyle +task: Executing a Hippogriff +task: Heading to market to sell loot +task: Selling a boy scout merit badge +task: Selling 2 silkie furs +task: Selling an iron peasant chaff +task: Selling a rat tail +task: Selling a koala heart +task: Selling a wyvern wing +task: Selling a wolog lemma +task: Selling a girl scout cookie +task: Selling a peryton antler +task: Selling a yeti fur +task: Selling a triton scale +task: Selling an ogre talon +task: Selling a homunculus fluid +task: Selling a nymph hanky +task: Selling a stun bear tooth +task: Selling a hogbird curl +task: Selling a green slime sample +task: Selling a ghast vomit +task: Selling an octopus beak +task: Selling a mermaid gills +task: Selling a gargoyle gravel +task: Selling a hippogriff egg +queued: Oh sweet relief! You've reached the protection of the good Chief of Whodock +queued: There is rejoicing, and an unnerving encounter with Chief of Whodock in private +queued: You forget your needle and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Chief of Whodock be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Archbishop of the Demicanadian +queued: There is rejoicing, and an unnerving encounter with Archbishop of the Demicanadian in private +queued: You forget your planter box and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Archbishop of the Demicanadian be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Voutxot the Medusa +queued: Locked in grim combat with Voutxot the Medusa +queued: Victory! Voutxot the Medusa is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Mayor of Prikprov +queued: There is rejoicing, and an unnerving encounter with Mayor of Prikprov in private +queued: You forget your sock and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Mayor of Prikprov be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Prougjack the Ochre Jelly +queued: Victory! Prougjack the Ochre Jelly is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Muntrix the Mind Flayer +queued: Victory! Muntrix the Mind Flayer is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Princess of the Skraeling +queued: There is rejoicing, and an unnerving encounter with Princess of the Skraeling in private +queued: You forget your tuppence and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Princess of the Skraeling be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Kraekgreck the Gorgon +queued: Locked in grim combat with Kraekgreck the Gorgon +queued: Victory! Kraekgreck the Gorgon is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Aebtruz the Otyugh +queued: Aebtruz the Otyugh seems to have the upper hand +queued: You seem to gain the advantage over Aebtruz the Otyugh +queued: Victory! Aebtruz the Otyugh is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of Vieklum +queued: There is rejoicing, and an unnerving encounter with Queen of Vieklum in private +queued: You forget your inkwell and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of Vieklum be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good King of the Talking Pony +queued: There is rejoicing, and an unnerving encounter with King of the Talking Pony in private +queued: You forget your pole and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could King of the Talking Pony be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Prince of the Eel Man +queued: There is rejoicing, and an unnerving encounter with Prince of the Eel Man in private +queued: You forget your dirtclod and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Prince of the Eel Man be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Rubug the Mini Giant +queued: You seem to gain the advantage over Rubug the Mini Giant +queued: Victory! Rubug the Mini Giant is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Nivwhaep the Sea Hag +queued: You seem to gain the advantage over Nivwhaep the Sea Hag +queued: Victory! Nivwhaep the Sea Hag is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Mebdrob the Unicorn +queued: Mebdrob the Unicorn seems to have the upper hand +queued: Locked in grim combat with Mebdrob the Unicorn +queued: Victory! Mebdrob the Unicorn is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Briedet the Mummy +queued: Victory! Briedet the Mummy is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Jevdraet the Unicorn +queued: Victory! Jevdraet the Unicorn is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of the Double Hobbit +queued: There is rejoicing, and an unnerving encounter with Queen of the Double Hobbit in private +queued: You forget your I.O.U. and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of the Double Hobbit be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +monster: Executing a sick Sea Hag 6000 +monster: Executing a Skeleton 3000 +monster: Executing a teenage Catoblepas 8000 +monster: Executing a Bugboar 4500 +monster: Executing an enormous Doppelganger 8400 +monster: Executing a teenage Shambling Mound 8000 +monster: Executing a Stone Giant 7714 +monster: Executing a Gold Dragon 6000 +monster: Executing a veteran White Dragon 4666 +monster: Executing a giant Humidity Giant 9600 +monster: Executing a preadolescent Chromatic Dragon 7090 +monster: Executing a greater Mastodon 6500 +monster: Executing an enormous Were-Lich 7846 +monster: Executing a giant undead Iguanadon 6000 +monster: Executing a greater Pentasaurus 5200 +monster: Executing a greater Oxygen Golem 6750 +monster: Executing a messianic Roper 7411 +monster: Executing a greater Chromatic Dragon 5666 +monster: Executing a teenage Platinum Dragon 6000 +monster: Executing 2 enormous Umber Hulks 6600 +monster: Executing a greater demon Purple Worm 6000 +monster: Executing a crippled foetal Orcus 5181 +monster: Executing 2 Beef Giants 5739 +monster: Executing 2 Trappers 6000 +monster: Executing 3 preadolescent Megalosauri 6480 +monster: Executing 2 warrior Ice Devils 6461 +monster: Executing an undernourished Geryon 6444 +monster: Executing 2 massive Mastodons 6000 +monster: Executing a massive undead Yeenoghu 6413 +monster: Executing 2 Battle-Trappers 5200 +monster: Executing 4 sick Nagas 5419 +monster: Executing 3 underage Red Dragons 5062 +monster: Executing 2 Chromatic Dragons 5818 +monster: Executing 3 baby Purple Worms 5823 +monster: Executing 5 underage Rice Giants 6000 +monster: Executing Sir Retev the Land Squid 6000 +monster: Executing 5 teenage Black Puddings 6486 +monster: Executing 3 sick Pentasauri 4736 +monster: Executing 4 Beholders 6153 +monster: Executing 3 Elasmosauri 6750 +monster: Executing 3 veteran Pit Fiends 6146 +monster: Executing 5 undernourished Stone Giants 5714 +monster: Executing 4 Ghosts 5581 +monster: Executing 5 underage Tin Dragons 4772 +monster: Executing 9 Hell Hounds 6000 +monster: Executing 3 Leather Golems 5869 +monster: Executing 5 veteran Bronze Dragons 6382 +monster: Executing 3 undernourished Tyrannosaurus Rexes 6375 +monster: Executing 6 teenage Bone Devils 5142 +monster: Executing 5 Ettins 6000 +monster: Executing a Battle-Baalzebul 5176 +monster: Executing 5 teenage Nalfeshnees 5192 +monster: Executing 4 Cardboard Golems 6339 +monster: Executing 5 Frost Giants 5555 +monster: Executing 2 teenage Brontosauri 6109 +monster: Executing 5 Beef Giants 5892 +monster: Executing Captain Edbreg the Enchanted Motorcycle 5684 +monster: Executing 4 Purple Worms 6206 +monster: Executing 7 Rice Giants 5694 +monster: Executing 3 sick Platinum Dragons 5700 +monster: Executing 5 greater Couatls 4918 +monster: Executing 5 Megalosauri 5806 +monster: Executing 3 crippled foetal Brontosauri 6285 +monster: Executing 4 Triceratopses 6000 +monster: Executing 7 Battle-Mimics 6461 +monster: Executing Captain Triedzack the Talking Pony 6181 +monster: Executing 4 underage Jubilexes 5731 +monster: Executing 2 enormous Brontosauri 5823 +monster: Executing 6 underage Humidity Giants 5739 +monster: Executing 6 Trappers 6171 +monster: Executing 5 undernourished Beer Golems 5915 +monster: Executing 5 teenage Oxygen Golems 6250 +monster: Executing 10 Umber Hulks 6575 +monster: Executing 2 demon Brontosauri 5675 +monster: Executing 3 warrior Platinum Dragons 5760 +monster: Executing 4 massive Rocs 6315 +monster: Executing 10 Gold Dragons 6233 +monster: Executing 5 Battle-Gorgosauri 5384 +monster: Executing 2 titanic undead Brontosauri 5924 +monster: Executing 8 undernourished Neo-Otyughs 6000 +monster: Executing a messianic Demogorgon 5481 +monster: Executing 10 Vrocks 5853 +monster: Executing Chief Yiptrok the Gyrognome 5710 +monster: Executing 7 Liches 5500 +monster: Executing 8 Glabrezus 5647 +monster: Executing 5 undernourished Rocs 5930 +monster: Executing 3 Geryons 6206 +monster: Executing 3 greater Dispaters 6340 +monster: Executing Sgt. Azek the Land Squid 5730 +monster: Executing a passing Double Hobbit Robot Monk 6466 +monster: Executing 10 underage Ankylosauri 5274 +monster: Executing 5 greater Storm Giants 5217 +monster: Executing 3 greater Geryons 6000 +monster: Executing 8 Silver Dragons 5106 +monster: Executing 6 Rubber Golems 6063 +monster: Executing 10 underage Lurkers 5625 +monster: Executing 12 Gorgons 5938 +monster: Executing 11 Balors 5387 +monster: Executing 6 massive Rubber Golems 6545 +level: 3 +act: 1 +gold: 75 +stat: STR 14 +stat: CON 11 +stat: DEX 11 +stat: INT 15 +stat: WIS 15 +stat: CHA 11 +stat: HP Max 19 +stat: MP Max 19 +equipment: Weapon Unbalanced Poachard +equipment: Hauberk +1 Macrame +equipment: Vambraces Burlap +equipment: Shield Studded Garbage Can Lid +equipment: Sollerets -1 Canvas +equipment: Gauntlets -2 Flannel +equipment: Helm Burlap +equipment: Cuisses +1 Lace +equipment: Greaves Macrame +equipment: Brassairts +1 Macrame +inventory: hippogriff egg 1 +inventory: Ostentatious Garnet of Craft 1 +inventory: Spectral Hymnal of Fortune 1 +inventory: Fearsome Laurel of Frenzy 1 +inventory: Dual Gimcrack of Internment 1 +inventory: Gleaming Gammel of Worry 1 +inventory: Filigreed Brazier of Guile 1 +inventory: Arcane Diadem of Guile 1 +inventory: Lucky Sphere of Petulance 1 +inventory: Deadly Hood of Grob 1 +inventory: Cruciate Gimcrack of Petulance 1 +inventory: Gleaming Cobble of Intrusion 1 +inventory: Crystalline Brooch of Nervousness 1 +spell: Spectral Miasma 2 +spell: Good Move 1 +spell: Seasick 1 +spell: Aqueous Humor 2 +spell: Rabbit Punch 2 +spell: Slime Finger 1 +spell: Sadness 1 +spell: Inoculate 1 +quest: Seek the Spectral Fleece +quest: Seek the Austere Bandolier +quest: Seek the Spectral Ornament +quest: Exterminate the Green Slimes +quest: Exterminate the Sea Hags +quest: Placate the Eels +quest: Deliver this tuppence +quest: Exterminate the Hogbirds +quest: Fetch me a carrot +quest: Seek the Iron Lamp +quest: Seek the Ormolu Sceptre +quest: Deliver this ladder +quest: Exterminate the Dryads +quest: Exterminate the Homunculi +quest: Exterminate the Hogbirds +quest: Placate the Troglodytes +quest: Exterminate the Bugboars +quest: Seek the Austere Phial +quest: Deliver this bunny +quest: Placate the Bacon Elementals +quest: Exterminate the Cavemen +quest: Placate the Wologs +quest: Seek the Iron Spangle +quest: Fetch me an I.O.U. +quest: Deliver this chicken +quest: Deliver this vest +quest: Deliver this vest +quest: Seek the Garlanded Brocade +quest: Placate the Iguanadons +quest: Placate the Tritons +quest: Exterminate the Bugboars +quest: Fetch me a writ +quest: Exterminate the Frogs +quest: Placate the Dervishes +quest: Placate the Harpies +quest: Deliver this casket +quest: Placate the Iron Peasants +quest: Seek the Astral Laurel +quest: Fetch me a nail +quest: Deliver this newspaper +quest: Deliver this writ diff --git a/tests/fixtures/parity_gamma.txt b/tests/fixtures/parity_gamma.txt new file mode 100644 index 0000000..959b3da --- /dev/null +++ b/tests/fixtures/parity_gamma.txt @@ -0,0 +1,1867 @@ +task: Loading +task: Experiencing an enigmatic and foreboding night vision +task: Much is revealed about that wise old bastard you'd underestimated +task: A shocking series of events leaves you alone and bewildered, but resolute +task: Drawing upon an unrealized reserve of determination, you set out on a long and dangerous journey +task: Loading Act I +task: Heading to the killing fields +task: Executing an undernourished Bugboar +task: Executing an undernourished Homunculus +task: Executing a baby Minotaur +task: Executing a Brown Elf +task: Executing an undernourished Iron Peasant +task: Executing a Cub Scout +task: Executing a dead Minotaur +task: Executing a Manes +task: Executing a greater Rat +task: Executing an undernourished Troglodyte +task: Executing a dead preadolescent Jellyrock +task: Executing a dead White Dragon +task: Executing a Stun Worm +task: Executing a foetal Minotaur +task: Executing an underage Girl Scout +task: Executing a dead underage Griffon +task: Executing an underage Stun Worm +task: Executing a Nixie +task: Executing a Sprite +task: Executing an underage Green Slime +task: Executing a Battle-Frog +task: Executing a Battle-Bunny +task: Heading to market to sell loot +task: Selling a bugboar tusk +task: Selling a homunculus fluid +task: Selling 3 minotaur maps +task: Selling a brown elf tusk +task: Selling an iron peasant chaff +task: Selling a cub scout neckerchief +task: Selling a manes tooth +task: Selling a rat tail +task: Selling a troglodyte tail +task: Selling a jellyrock seedling +task: Selling a white dragon tooth +task: Selling 2 stun worm trodes +task: Selling a girl scout cookie +task: Selling a griffon nest +task: Selling a nixie webbing +task: Selling a sprite can +task: Selling a green slime sample +task: Selling a frog leg +task: Selling a bunny ear +task: Heading to the killing fields +task: Executing a Goblin +task: Executing a teenage Piercer +task: Executing a Sea Elf +task: Executing a Pixie +task: Executing a Stirge +task: Executing a greater Dervish +task: Executing an underage Hogbird +task: Executing a greater Beagle +task: Executing an Orc +task: Executing a Merman +task: Executing a passing Low Elf Voodoo Princess +task: Executing a Battle-Ant +task: Executing a Pirate +task: Executing an undernourished Eel +task: Executing an Orc +task: Executing a Swamp Elf +task: Executing an undernourished Troglodyte +task: Executing a Sea Elf +task: Executing a Pixie +task: Executing an undernourished Ghoul +task: Executing an undernourished Gnoll +task: Executing an undernourished Koala +task: Heading to market to sell loot +task: Selling a goblin ear +task: Selling a piercer tip +task: Selling 2 sea elf jerkins +task: Selling 2 pixie dusts +task: Selling a stirge proboscis +task: Selling a dervish robe +task: Selling a hogbird curl +task: Selling a beagle collar +task: Selling 2 orc snouts +task: Selling a merman trident +task: Selling a Filigreed Gimlet of Incarceration +task: Selling an ant antenna +task: Selling a pirate booty +task: Selling an eel sashimi +task: Selling a swamp elf lilypad +task: Selling a troglodyte tail +task: Selling a ghoul muscle +task: Selling a gnoll collar +task: Selling a koala heart +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Mermaid +task: Executing a Gnome +task: Executing a greater Cub Scout +task: Executing a Pixie +task: Executing a Battle-Bunny +task: Executing a massive Beagle +task: Executing a Swamp Elf +task: Executing a Nixie +task: Executing an underage Ghoul +task: Executing a Grid Bug +task: Executing a greater Wasp +task: Executing a veteran Brown Elf +task: Executing a passing Skraeling Battle-Felon +task: Executing a Goblin +task: Executing a Bat +task: Executing an underage Bugbear +task: Executing a Sea Elf +task: Executing a crippled Malebranche +task: Executing an underage Green Slime +task: Executing a Battle-Moth +task: Executing an underage Carrion Crawler +task: Executing a teenage Violet Fungi +task: Heading to market to sell loot +task: Selling a mermaid gills +task: Selling a gnome hat +task: Selling a cub scout neckerchief +task: Selling a pixie dust +task: Selling a bunny ear +task: Selling a beagle collar +task: Selling a swamp elf lilypad +task: Selling a nixie webbing +task: Selling a ghoul muscle +task: Selling a grid bug carapace +task: Selling a wasp stinger +task: Selling a brown elf tusk +task: Selling an Arcane Amethyst of Fear +task: Selling a goblin ear +task: Selling a bat wing +task: Selling a bugbear skin +task: Selling a sea elf jerkin +task: Selling a malebranche fork +task: Selling a green slime sample +task: Selling a moth dust +task: Selling a carrion crawler egg +task: Selling a violet fungi spore +task: Heading to the killing fields +task: Executing a Sprite +task: Executing a Billy Goat +task: Executing a Battle-Fly +task: Executing a Skeleton +task: Executing an underage Dryad +task: Executing a Rat +task: Executing an underage Ghoul +task: Executing an underage Multicell +task: Executing a Goblin +task: Executing Mr. Evtrag the Double Wookiee +task: Executing a Cub Scout +task: Executing a Battle-Centipede +task: Executing a greater Kobold +task: Executing a Gnome +task: Executing a Pirate +task: Executing a Battle-Beagle +task: Executing a Swamp Elf +task: Executing an undernourished Zombie +task: Executing an underage Caveman +task: Executing a Cub Scout +task: Executing a Pirate +task: Executing a Brown Elf +task: Heading to market to sell loot +task: Selling a sprite can +task: Selling a billy goat beard +task: Selling a Spectral Arrow of Submission +task: Selling a skeleton clavicle +task: Selling a dryad acorn +task: Selling a rat tail +task: Selling a ghoul muscle +task: Selling a multicell dendrite +task: Selling a goblin ear +task: Selling an Unearthly Hymnal of Dignard +task: Selling 2 cub scout neckerchieves +task: Selling a centipede leg +task: Selling a kobold penis +task: Selling a gnome hat +task: Selling 2 pirate booties +task: Selling a beagle collar +task: Selling a swamp elf lilypad +task: Selling a zombie forehead +task: Selling a caveman club +task: Selling a brown elf tusk +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an undernourished Imp +task: Executing an underage Homunculus +task: Executing a sick Hogbird +task: Executing an undernourished Green Slime +task: Executing a greater Gnome +task: Executing a Rat +task: Executing Captain Ooxzab the Trans-Kobold +task: Executing a Cub Scout +task: Executing a Berserker +task: Executing a Green Slime +task: Executing a dead teenage Vampire +task: Executing an undernourished Koala +task: Executing a Ghoul +task: Executing a Sea Elf +task: Executing an undernourished Imp +task: Executing a Swamp Elf +task: Executing a greater Crayfish +task: Executing a Nixie +task: Executing a Cub Scout +task: Executing a Rat +task: Executing a Merman +task: Executing a sick Hogbird +task: Heading to market to sell loot +task: Selling 2 imp tails +task: Selling a homunculus fluid +task: Selling 2 hogbird curls +task: Selling 2 green slime samples +task: Selling a gnome hat +task: Selling 2 rat tails +task: Selling a Gilded Albatross of Submission +task: Selling 2 cub scout neckerchieves +task: Selling a berserker shirt +task: Selling a vampire pancreas +task: Selling a koala heart +task: Selling a ghoul muscle +task: Selling a sea elf jerkin +task: Selling a swamp elf lilypad +task: Selling a crayfish antenna +task: Selling a nixie webbing +task: Selling a merman trident +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a sick Hogbird +task: Executing a Whippet +task: Executing a teenage Hogbird +task: Executing a Manes +task: Executing a passing Double Hobbit Ur-Paladin +task: Executing a Wolf +task: Executing a teenage Bugbear +task: Executing a crippled Peryton +task: Executing an undernourished Gnoll +task: Executing a greater Gnome +task: Executing a teenage Hogbird +task: Executing a Rat +task: Executing a Dwarf +task: Executing an Orc +task: Executing Captain Nevcruz the Battle-Finch +task: Executing a sick Hogbird +task: Executing a Brown Elf +task: Executing a Bat +task: Executing an Ostrich +task: Executing Chief Mapjoog the Panda Man +task: Executing a Battle-Crayfish +task: Heading to market to sell loot +task: Selling 4 hogbird curls +task: Selling a whippet collar +task: Selling a manes tooth +task: Selling an Iron Hymnal of Joy +task: Selling a wolf paw +task: Selling a bugbear skin +task: Selling a peryton antler +task: Selling a gnoll collar +task: Selling a gnome hat +task: Selling a rat tail +task: Selling a dwarf drawers +task: Selling an orc snout +task: Selling a Reverential Candelabra of Grob +task: Selling a brown elf tusk +task: Selling a Precious Lens of Fortune +task: Selling a bat wing +task: Selling an ostrich beak +task: Selling a Gilded Albatross of Despair +task: Selling a crayfish antenna +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Eel +task: Executing a Bat +task: Executing a crippled Wolog +task: Executing a sick Sylph +task: Executing a teenage Iron Peasant +task: Executing an undernourished Girl Scout +task: Executing a Sprite +task: Executing a Pixie +task: Executing a baby Owlbear +task: Executing a Grid Bug +task: Executing a Sprite +task: Executing a Berserker +task: Executing a Battle-Bunny +task: Executing a Gnome +task: Executing an underage Sea Hag +task: Executing a Merman +task: Executing a crippled Ghast +task: Executing a Cub Scout +task: Executing a Merman +task: Executing a crippled Wraith +task: Executing a Leprechaun +task: Executing a foetal Leucrotta +task: Heading to market to sell loot +task: Selling an eel sashimi +task: Selling a bat wing +task: Selling a wolog lemma +task: Selling a sylph thigh +task: Selling an iron peasant chaff +task: Selling a girl scout cookie +task: Selling 2 sprite cans +task: Selling a pixie dust +task: Selling an owlbear feather +task: Selling a grid bug carapace +task: Selling a berserker shirt +task: Selling a bunny ear +task: Selling a gnome hat +task: Selling a sea hag wart +task: Selling 2 merman tridents +task: Selling a ghast vomit +task: Selling a cub scout neckerchief +task: Selling a wraith finger +task: Selling a leprechaun wallet +task: Selling a leucrotta hoof +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a cursed Moth +task: Executing a Battle-Centipede +task: Executing a greater Spider +task: Executing a Gnoll +task: Executing a Grid Bug +task: Executing a Stirge +task: Executing a greater Midge +task: Executing a Cub Scout +task: Executing a sick Wight +task: Executing a teenage Carrion Crawler +task: Executing a Dwarf +task: Executing a Pirate +task: Executing a greater Centipede +task: Executing a crippled Doppelganger +task: Executing a Dryad +task: Executing a Battle-Spider +task: Executing a preadolescent Yeti +task: Executing a Battle-Dwarf +task: Executing a Rot Grub +task: Executing a Berserker +task: Executing a cursed Goblin +task: Executing an Orc +task: Heading to market to sell loot +task: Selling a moth dust +task: Selling 2 centipede legs +task: Selling 2 spider webs +task: Selling a gnoll collar +task: Selling a grid bug carapace +task: Selling a stirge proboscis +task: Selling a midge corpse +task: Selling a cub scout neckerchief +task: Selling a wight lung +task: Selling a carrion crawler egg +task: Selling 2 dwarf drawerses +task: Selling a pirate booty +task: Selling a doppelganger face +task: Selling a dryad acorn +task: Selling a yeti fur +task: Selling a rot grub eggsac +task: Selling a berserker shirt +task: Selling a goblin ear +task: Selling an orc snout +task: Heading to the killing fields +task: Executing a Ghoul +task: Executing a Homunculus +task: Executing a veteran Rot Grub +task: Executing a passing Lesser Dwarf Puma Burgular +task: Executing an undernourished Bugbear +task: Executing a Goblin +task: Executing an undernourished Multicell +task: Executing a Battle-Bugbear +task: Executing a Battle-Hobgoblin +task: Executing an underage Wolog +task: Executing a passing Enchanted Motorcycle Slow Poisoner +task: Executing a greater Frog +task: Executing a Sea Elf +task: Executing an underage Lizard Man +task: Executing a greater Brown Elf +task: Executing a Swamp Elf +task: Executing a veteran Lizard Man +task: Executing an undernourished Green Slime +task: Executing an undernourished Harpy +task: Executing an underage Lizard Man +task: Executing a Lizard Man +task: Heading to market to sell loot +task: Selling a ghoul muscle +task: Selling a homunculus fluid +task: Selling a rot grub eggsac +task: Selling a Legendary Arrow of Chaos +task: Selling 2 bugbear skins +task: Selling a goblin ear +task: Selling a multicell dendrite +task: Selling a hobgoblin patella +task: Selling an Ormolu Spangle of Efficiency +task: Selling a wolog lemma +task: Selling a Puissant Orb of Solitude +task: Selling a frog leg +task: Selling a sea elf jerkin +task: Selling 4 lizard man tails +task: Selling a brown elf tusk +task: Selling a swamp elf lilypad +task: Selling a green slime sample +task: Selling a harpy mascara +task: Negotiating purchase of better equipment +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an Imp +task: Executing an undernourished Lizard Man +task: Executing a Lizard Man +task: Executing a Lizard Man +task: Executing a Carrion Crawler +task: Executing a Zombie +task: Executing an undernourished Lemure +task: Executing a greater Yellow Mold +task: Executing an Octopus +task: Executing a Brown Elf +task: Executing a Battle-Merman +task: Executing a Multicell +task: Executing a Billy Goat +task: Executing an undernourished Piercer +task: Executing an underage Camel +task: Executing a Dryad +task: Executing a Troglodyte +task: Executing an Iron Peasant +task: Executing a Battle-Midge +task: Executing a teenage Bugboar +task: Executing a greater Green Slime +task: Executing a greater Berserker +task: Heading to market to sell loot +task: Selling an imp tail +task: Selling 3 lizard man tails +task: Selling a carrion crawler egg +task: Selling a zombie forehead +task: Selling a lemure blob +task: Selling a yellow mold spore +task: Selling an octopus beak +task: Selling a brown elf tusk +task: Selling a merman trident +task: Selling a multicell dendrite +task: Selling a billy goat beard +task: Selling a piercer tip +task: Selling a camel hump +task: Selling a dryad acorn +task: Selling a troglodyte tail +task: Selling an iron peasant chaff +task: Selling a midge corpse +task: Selling a bugboar tusk +task: Selling a green slime sample +task: Selling a berserker shirt +task: Heading to the killing fields +task: Executing a Lizard Man +task: Executing a sick Harpy +task: Executing a Koala +task: Executing an underage Iron Peasant +task: Executing an underage Violet Fungi +task: Executing an underage Poroid +task: Executing Ms. Droockbrin the Low Elf +task: Executing a Lizard Man +task: Executing a Dryad +task: Executing a Yellow Mold +task: Executing a greater Hobgoblin +task: Executing a Sea Hag +task: Executing a Stirge +task: Executing a greater Lizard Man +task: Executing a cursed Lizard Man +task: Executing a Lizard Man +task: Executing a veteran Rot Grub +task: Executing a Brown Elf +task: Executing a greater Leprechaun +task: Executing a Lizard Man +task: Executing an undernourished Lizard Man +task: Executing a Sea Hag +task: Heading to market to sell loot +task: Selling 7 lizard man tails +task: Selling a harpy mascara +task: Selling a koala heart +task: Selling an iron peasant chaff +task: Selling a violet fungi spore +task: Selling a poroid node +task: Selling a Lucky Ornament of Incarceration +task: Selling a dryad acorn +task: Selling a yellow mold spore +task: Selling a hobgoblin patella +task: Selling 2 sea hag warts +task: Selling a stirge proboscis +task: Selling a rot grub eggsac +task: Selling a brown elf tusk +task: Selling a leprechaun wallet +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Leprechaun +task: Executing an underage Imp +task: Executing an undernourished Lizard Man +task: Executing a greater Ant +task: Executing a Lizard Man +task: Executing an Uruk +task: Executing an underage Imp +task: Executing an underage Lemure +task: Executing a Hippogriff +task: Executing an undernourished Silkie +task: Executing a Kobold +task: Executing a Lizard Man +task: Executing a massive Lizard Man +task: Executing a Lizard Man +task: Executing a Lizard Man +task: Executing a Battle-Caveman +task: Executing a Man-o-war +task: Executing a Ghoul +task: Executing a Gnome +task: Executing a greater Lizard Man +task: Executing Mrs. Yieklick the Double Hobbit +task: Executing a greater Lizard Man +task: Heading to market to sell loot +task: Selling a leprechaun wallet +task: Selling 2 imp tails +task: Selling 8 lizard man tails +task: Selling an ant antenna +task: Selling an uruk boot +task: Selling a lemure blob +task: Selling a hippogriff egg +task: Selling a silkie fur +task: Selling a kobold penis +task: Selling a caveman club +task: Selling a man-o-war tentacle +task: Selling a ghoul muscle +task: Selling a gnome hat +task: Selling a Lucky Arrow of Envy +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Doppelganger +task: Executing an undernourished Bugboar +task: Executing a veteran Brown Elf +task: Executing an undernourished Pegasus +task: Executing a Multicell +task: Executing an enormous Moth +task: Executing a passing Skraeling Runeloremaster +task: Executing a Lizard Man +task: Executing an undernourished Triton +task: Executing a Triton +task: Executing a greater Lizard Man +task: Executing a Lizard Man +task: Executing a Lizard Man +task: Executing a greater Manes +task: Executing a greater Dervish +task: Executing a Battle-Shrieker +task: Executing a greater Stirge +task: Executing a Lizard Man +task: Executing a Sea Elf +task: Executing a sick Ogre Mage +task: Executing a veteran Lizard Man +task: Executing a Quasit +task: Heading to market to sell loot +task: Selling a doppelganger face +task: Selling a bugboar tusk +task: Selling a brown elf tusk +task: Selling a pegasus aileron +task: Selling a multicell dendrite +task: Selling a moth dust +task: Selling a Deadly Fleece of Prurience +task: Selling 6 lizard man tails +task: Selling 2 triton scales +task: Selling a manes tooth +task: Selling a dervish robe +task: Selling a shrieker stalk +task: Selling a stirge proboscis +task: Selling a sea elf jerkin +task: Selling an ogre mage apparel +task: Selling a quasit tail +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Battle-Berserker +task: Executing a massive Gnome +task: Executing a Dervish +task: Executing a sick Poroid +task: Executing a Zombie +task: Executing an Orc +task: Executing a Homunculus +task: Executing a crippled Unicorn +task: Executing an Uruk +task: Executing a Lizard Man +task: Executing an underage Triton +task: Executing a Lemure +task: Executing a Dervish +task: Executing an undernourished Triton +task: Executing a Girl Scout +task: Executing a teenage Shadow +task: Executing a Berserker +task: Executing a Billy Goat +task: Executing an undernourished Gargoyle +task: Executing an underage Boogie +task: Executing a Boy Scout +task: Executing a Lizard Man +task: Heading to market to sell loot +task: Selling 2 berserker shirts +task: Selling a gnome hat +task: Selling 2 dervish robes +task: Selling a poroid node +task: Selling a zombie forehead +task: Selling an orc snout +task: Selling a homunculus fluid +task: Selling an unicorn blood +task: Selling an uruk boot +task: Selling 2 lizard man tails +task: Selling 2 triton scales +task: Selling a lemure blob +task: Selling a girl scout cookie +task: Selling a shadow silhouette +task: Selling a billy goat beard +task: Selling a gargoyle gravel +task: Selling a boogie slime +task: Selling a boy scout merit badge +task: Heading to the killing fields +task: Executing a Troglodyte +task: Executing a Green Slime +task: Executing a teenage Eagle Scout +task: Executing a Silkie +task: Executing a greater Bunny +task: Executing a cursed Midge +task: Executing a Sea Elf +task: Executing an undernourished Hippogriff +task: Executing a cursed Sprite +task: Executing an underage Stun Worm +task: Executing a Camel +task: Executing a Multicell +task: Executing a Dryad +task: Executing a Caveman +task: Executing a Girl Scout +task: Executing a Battle-Hippogriff +task: Executing a Gnoll +task: Executing an undernourished Wolog +task: Executing Admiral Grotlap the Low Elf +task: Executing a Pixie +task: Executing an underage Sea Hag +task: Executing a teenage Ogre Mage +task: Heading to market to sell loot +task: Selling a troglodyte tail +task: Selling a green slime sample +task: Selling an eagle scout merit badge +task: Selling a silkie fur +task: Selling a bunny ear +task: Selling a midge corpse +task: Selling a sea elf jerkin +task: Selling 2 hippogriff eggs +task: Selling a sprite can +task: Selling a stun worm trode +task: Selling a camel hump +task: Selling a multicell dendrite +task: Selling a dryad acorn +task: Selling a caveman club +task: Selling a girl scout cookie +task: Selling a gnoll collar +task: Selling a wolog lemma +task: Selling an Ormolu Tome of Practicality +task: Selling a pixie dust +task: Selling a sea hag wart +task: Selling an ogre mage apparel +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Carrion Crawler +task: Executing an underage Gelatinous Cube +task: Executing a crippled Hippocampus +task: Executing a massive Brown Elf +task: Executing a veteran Yellow Mold +task: Executing a Yellow Mold +task: Executing a Battle-Mermaid +task: Executing an underage Centaur +task: Executing a Brown Elf +task: Executing a Battle-Bunny +task: Executing an undernourished Triton +task: Executing an undernourished Wolf +task: Executing an undernourished Camel +task: Executing a greater Leprechaun +task: Executing a greater Leprechaun +task: Executing an Imp +task: Executing a Battle-Pixie +task: Executing an Uruk +task: Executing an underage Blink Dog +task: Executing a cursed Multicell +task: Executing a crippled Banshee +task: Executing a Battle-Frog +task: Heading to market to sell loot +task: Selling a carrion crawler egg +task: Selling a gelatinous cube jam +task: Selling a hippocampus mane +task: Selling 2 brown elf tusks +task: Selling 2 yellow mold spores +task: Selling a mermaid gills +task: Selling a centaur rib +task: Selling a bunny ear +task: Selling a triton scale +task: Selling a wolf paw +task: Selling a camel hump +task: Selling 2 leprechaun wallets +task: Selling an imp tail +task: Selling a pixie dust +task: Selling an uruk boot +task: Selling a blink dog eyelid +task: Selling a multicell dendrite +task: Selling a banshee larynx +task: Selling a frog leg +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Violet Fungi +task: Executing a Battle-Merman +task: Executing an undernourished Peryton +task: Executing a Battle-Nebbish +task: Executing an undernourished Carrion Crawler +task: Executing Sir Zoopex the Double Hobbit +task: Executing a massive Nebbish +task: Executing an Uruk +task: Executing a sick Gray Ooze +task: Executing a Dryad +task: Executing a comatose Leucrotta +task: Executing a Kobold +task: Executing a preadolescent Mini Giant +task: Executing a Troglodyte +task: Executing a Piercer +task: Executing a passing Enchanted Motorcycle Voodoo Princess +task: Executing a Swamp Elf +task: Executing a Ghoul +task: Executing a Berserker +task: Executing an underage Eel +task: Executing a Skeleton +task: Executing a greater Midge +task: Heading to market to sell loot +task: Selling a violet fungi spore +task: Selling a merman trident +task: Selling a peryton antler +task: Selling 2 nebbish bellies +task: Selling a carrion crawler egg +task: Selling a Legendary Festoon of Silence +task: Selling an uruk boot +task: Selling a gray ooze gravy +task: Selling a dryad acorn +task: Selling a leucrotta hoof +task: Selling a kobold penis +task: Selling a mini giant pompadour +task: Selling a troglodyte tail +task: Selling a piercer tip +task: Selling a Golden Ornament of Electrum +task: Selling a swamp elf lilypad +task: Selling a ghoul muscle +task: Selling a berserker shirt +task: Selling an eel sashimi +task: Selling a skeleton clavicle +task: Selling a midge corpse +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a comatose teenage Hydra +task: Executing a Wolf +task: Executing a Battle-Dwarf +task: Executing an underage Uruk +task: Executing a Carrion Crawler +task: Executing an underage Stun Worm +task: Executing a Stun Worm +task: Executing a Triton +task: Executing a Brown Elf +task: Executing a Boy Scout +task: Executing a Wolf +task: Executing an undernourished Ape +task: Executing a Green Slime +task: Executing a Pirate +task: Executing a veteran Billy Goat +task: Executing a Green Slime +task: Executing a Girl Scout +task: Executing a Gnoll +task: Executing a Shadow +task: Executing a veteran Sea Elf +task: Executing a Zombie +task: Executing a cursed Whippet +task: Heading to market to sell loot +task: Selling a hydra gyrum +task: Selling 2 wolf paws +task: Selling a dwarf drawers +task: Selling an uruk boot +task: Selling a carrion crawler egg +task: Selling 2 stun worm trodes +task: Selling a triton scale +task: Selling a brown elf tusk +task: Selling a boy scout merit badge +task: Selling an ape ass +task: Selling 2 green slime samples +task: Selling a pirate booty +task: Selling a billy goat beard +task: Selling a girl scout cookie +task: Selling a gnoll collar +task: Selling a shadow silhouette +task: Selling a sea elf jerkin +task: Selling a zombie forehead +task: Selling a whippet collar +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing Sgt. Vuzprit the Low Elf +task: Executing a sick Malebranche +task: Executing a Hogbird +task: Executing a Dervish +task: Executing an Octopus +task: Executing a Battle-Piercer +task: Executing a Swamp Elf +task: Executing a Triton +task: Executing a passing Low Elf Voodoo Princess +task: Executing Sir Grotdron the Trans-Kobold +task: Executing an Iron Peasant +task: Executing a massive Pixie +task: Executing a Man-o-war +task: Executing a Battle-Gnome +task: Executing a Stun Worm +task: Executing an underage Girl Scout +task: Executing a massive Rat +task: Executing an underage Jumpskin +task: Executing a Mermaid +task: Executing a crippled Rust Monster +task: Executing a Nixie +task: Executing a greater Whippet +task: Heading to market to sell loot +task: Selling an Iron Fleece of Ra +task: Selling a malebranche fork +task: Selling a hogbird curl +task: Selling a dervish robe +task: Selling an octopus beak +task: Selling a piercer tip +task: Selling a swamp elf lilypad +task: Selling a triton scale +task: Selling an Enchanted Bandolier of Practicality +task: Selling a Benevolent Brocade of Danger +task: Selling an iron peasant chaff +task: Selling a pixie dust +task: Selling a man-o-war tentacle +task: Selling a gnome hat +task: Selling a stun worm trode +task: Selling a girl scout cookie +task: Selling a rat tail +task: Selling a jumpskin shin +task: Selling a mermaid gills +task: Selling a rust monster shavings +task: Selling a nixie webbing +task: Selling a whippet collar +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Rot Grub +task: Executing a Battle-Skeleton +task: Executing an underage Peryton +task: Executing a Man-o-war +task: Executing an Octopus +task: Executing a baby Succubus +task: Executing an Uruk +task: Executing an undernourished Sylph +task: Executing a Gray Ooze +task: Executing an undernourished Poroid +task: Executing an undernourished Dryad +task: Executing a greater Bunny +task: Executing a Kobold +task: Executing a Wolf +task: Executing a Battle-Imp +task: Executing a Triton +task: Executing an Imp +task: Executing a Triton +task: Executing a Harpy +task: Executing a teenage Hogbird +task: Executing a sick Man-o-war +task: Heading to market to sell loot +task: Selling a rot grub eggsac +task: Selling a skeleton clavicle +task: Selling a peryton antler +task: Selling 2 man-o-war tentacles +task: Selling an octopus beak +task: Selling a succubus bra +task: Selling an uruk boot +task: Selling a sylph thigh +task: Selling a gray ooze gravy +task: Selling a poroid node +task: Selling a dryad acorn +task: Selling a bunny ear +task: Selling a kobold penis +task: Selling a wolf paw +task: Selling 2 imp tails +task: Selling 2 triton scales +task: Selling a Garlanded Garnet of Hurting +task: Selling a harpy mascara +task: Selling a hogbird curl +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Battle-Koala +task: Executing a Dervish +task: Executing an undernourished Unicorn +task: Executing an undernourished Satyr +task: Executing a Battle-Goblin +task: Executing a Silkie +task: Executing an undernourished Dryad +task: Executing a crippled Iguanadon +task: Executing a greater Nebbish +task: Executing an undernourished Sea Hag +task: Executing a Leprechaun +task: Executing a Battle-Ostrich +task: Executing a Harpy +task: Executing a Camel +task: Executing an undernourished Gray Ooze +task: Executing an underage Poroid +task: Executing a Battle-Moth +task: Executing a Battle-Berserker +task: Executing a Brown Elf +task: Executing a Lizard Man +task: Executing a Boogie +task: Executing a Dwarf +task: Heading to market to sell loot +task: Selling a koala heart +task: Selling a dervish robe +task: Selling an unicorn blood +task: Selling a satyr hoof +task: Selling a goblin ear +task: Selling a silkie fur +task: Selling a dryad acorn +task: Selling an iguanadon thumb +task: Selling a nebbish belly +task: Selling a sea hag wart +task: Selling a leprechaun wallet +task: Selling an ostrich beak +task: Selling a harpy mascara +task: Selling a camel hump +task: Selling a gray ooze gravy +task: Selling a poroid node +task: Selling a moth dust +task: Selling a berserker shirt +task: Selling a brown elf tusk +task: Selling a lizard man tail +task: Selling a boogie slime +task: Selling a dwarf drawers +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Yeti +task: Executing a Battle-Zombie +task: Executing a teenage Eagle Scout +task: Executing a greater Kobold +task: Executing a Battle-Wasp +task: Executing a Homunculus +task: Executing a cursed Billy Goat +task: Executing an undernourished Ogre +task: Executing a cursed Leprechaun +task: Executing a Were-Wasp +task: Executing a Leprechaun +task: Executing a Battle-Leprechaun +task: Executing a Leprechaun +task: Executing a Leprechaun +task: Executing a Leprechaun +task: Executing a Hobgoblin +task: Executing a Koala +task: Executing an Octopus +task: Executing a veteran Girl Scout +task: Executing a Nymph +task: Executing a Gray Ooze +task: Executing a Wolf +task: Heading to market to sell loot +task: Selling a yeti fur +task: Selling a zombie forehead +task: Selling an eagle scout merit badge +task: Selling a kobold penis +task: Selling 2 wasp stingers +task: Selling a homunculus fluid +task: Selling a billy goat beard +task: Selling an ogre talon +task: Selling 6 leprechaun wallets +task: Selling a hobgoblin patella +task: Selling a koala heart +task: Selling an octopus beak +task: Selling a girl scout cookie +task: Selling a nymph hanky +task: Selling a gray ooze gravy +task: Selling a wolf paw +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a massive Leprechaun +task: Executing a cursed Leprechaun +task: Executing an underage Shadow +task: Executing a Leprechaun +task: Executing a Koala +task: Executing a veteran Sea Elf +task: Executing a greater Grid Bug +task: Executing a greater Leprechaun +task: Executing an underage Gray Ooze +task: Executing a sick Sea Hag +task: Executing a Boogie +task: Executing a teenage Mini Giant +task: Executing an undernourished Lizard Man +task: Executing an underage Ogre +task: Executing a greater Leprechaun +task: Executing a teenage Carrion Crawler +task: Executing an undernourished Harpy +task: Executing a Battle-Pixie +task: Executing a greater Cub Scout +task: Executing an underage Octopus +task: Executing an undernourished Gray Ooze +task: Executing an underage Nymph +task: Heading to market to sell loot +task: Selling 5 leprechaun wallets +task: Selling a shadow silhouette +task: Selling a koala heart +task: Selling a sea elf jerkin +task: Selling a grid bug carapace +task: Selling 2 gray ooze gravies +task: Selling a sea hag wart +task: Selling a boogie slime +task: Selling a mini giant pompadour +task: Selling a lizard man tail +task: Selling an ogre talon +task: Selling a carrion crawler egg +task: Selling a harpy mascara +task: Selling a pixie dust +task: Selling a cub scout neckerchief +task: Selling an octopus beak +task: Selling a nymph hanky +task: Heading to the killing fields +task: Executing a Whippet +task: Executing a Grid Bug +task: Executing a Lemure +task: Executing an Uruk +task: Executing a crippled Griffon +task: Executing an Uruk +task: Executing a Gnome +task: Executing a Hobgoblin +task: Executing a veteran Billy Goat +task: Executing a passing Talking Pony Hunter Strangler +task: Executing a Carrion Crawler +task: Executing an Orc +task: Executing a Battle-Midge +task: Executing a cursed Lizard Man +task: Executing a Yellow Mold +task: Executing an undernourished Piercer +task: Executing a Sprite +task: Executing an underage Violet Fungi +task: Executing a greater Merman +task: Executing a Manes +task: Executing a greater Gnome +task: Executing a Shrieker +task: Heading to market to sell loot +task: Selling a whippet collar +task: Selling a grid bug carapace +task: Selling a lemure blob +task: Selling 2 uruk boots +task: Selling a griffon nest +task: Selling 2 gnome hats +task: Selling a hobgoblin patella +task: Selling a billy goat beard +task: Selling a Precious Brazier of Prurience +task: Selling a carrion crawler egg +task: Selling an orc snout +task: Selling a midge corpse +task: Selling a lizard man tail +task: Selling a yellow mold spore +task: Selling a piercer tip +task: Selling a sprite can +task: Selling a violet fungi spore +task: Selling a merman trident +task: Selling a manes tooth +task: Selling a shrieker stalk +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an underage Ghast +task: Executing a crippled Su-monster +task: Executing a Hogbird +task: Executing an undernourished Iron Peasant +task: Executing a Stun Worm +task: Executing an underage Shadow +task: Executing a Kobold +task: Executing a Quasit +task: Executing a Merman +task: Executing a Bat +task: Executing a cursed Dervish +task: Executing a Battle-Multicell +task: Executing a greater Caveman +task: Executing an Eel +task: Executing a Caveman +task: Executing a sick Rust Monster +task: Executing an Uruk +task: Executing a Berserker +task: Executing a passing Will o' the Wisp Slow Poisoner +task: Executing a sick Hell Hound +task: Executing a greater Skeleton +task: Heading to market to sell loot +task: Selling a ghast vomit +task: Selling a su-monster tail +task: Selling a hogbird curl +task: Selling an iron peasant chaff +task: Selling a stun worm trode +task: Selling a shadow silhouette +task: Selling a kobold penis +task: Selling a quasit tail +task: Selling a merman trident +task: Selling a bat wing +task: Selling a dervish robe +task: Selling a multicell dendrite +task: Selling an One True Diadem of Ra +task: Selling 2 caveman clubs +task: Selling an eel sashimi +task: Selling a rust monster shavings +task: Selling an uruk boot +task: Selling a berserker shirt +task: Selling an Enchanted Lens of Sisu +task: Selling a hell hound tongue +task: Selling a skeleton clavicle +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Battle-Orc +task: Executing an underage Iron Peasant +task: Executing a Homunculus +task: Executing a Caveman +task: Executing a Pirate +task: Executing a Lizard Man +task: Executing a Hogbird +task: Executing a Sea Elf +task: Executing a greater Gnoll +task: Executing a Gnome +task: Executing a Battle-Imp +task: Executing a Camel +task: Executing a Skeleton +task: Executing a cursed Mermaid +task: Executing a Nixie +task: Executing a Battle-Dryad +task: Executing a greater Frog +task: Executing a sick Mummy +task: Executing an undernourished Eagle Scout +task: Executing a veteran Sea Elf +task: Executing a Malebranche +task: Executing a Hogbird +task: Heading to market to sell loot +task: Selling an orc snout +task: Selling an iron peasant chaff +task: Selling a homunculus fluid +task: Selling a caveman club +task: Selling a pirate booty +task: Selling a lizard man tail +task: Selling 2 hogbird curls +task: Selling 2 sea elf jerkins +task: Selling a gnoll collar +task: Selling a gnome hat +task: Selling an imp tail +task: Selling a camel hump +task: Selling a skeleton clavicle +task: Selling a mermaid gills +task: Selling a nixie webbing +task: Selling a dryad acorn +task: Selling a frog leg +task: Selling a mummy gauze +task: Selling an eagle scout merit badge +task: Selling a malebranche fork +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Wolf +task: Executing a Quasit +task: Executing a cursed Troglodyte +task: Executing an undernourished Bugboar +task: Executing Mrs. Tragtrieck the Eel Man +task: Executing an underage Jumpskin +task: Executing an undernourished Satyr +task: Executing a teenage Spectre +task: Executing an Eel +task: Executing a Mini Giant +task: Executing a Battle-Pegasus +task: Executing a Battle-Ghoul +task: Executing a Pegasus +task: Executing an undernourished Poroid +task: Executing a Jumpskin +task: Executing a Green Slime +task: Executing a greater Bunny +task: Executing a Bat +task: Executing a Battle-Ostrich +task: Executing an undernourished Cockatrice +task: Executing a passing Gyrognome Birdrider +task: Executing a Bugbear +task: Heading to market to sell loot +task: Selling a wolf paw +task: Selling a quasit tail +task: Selling a troglodyte tail +task: Selling a bugboar tusk +task: Selling a Legendary Tome of Dignard +task: Selling 2 jumpskin shins +task: Selling a satyr hoof +task: Selling a spectre vestige +task: Selling an eel sashimi +task: Selling a mini giant pompadour +task: Selling 2 pegasus ailerons +task: Selling a ghoul muscle +task: Selling a poroid node +task: Selling a green slime sample +task: Selling a bunny ear +task: Selling a bat wing +task: Selling an ostrich beak +task: Selling a cockatrice wattle +task: Selling a Legendary Tiara of Cruelty +task: Selling a bugbear skin +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Shrieker +task: Executing an underage Ghast +task: Executing an undernourished Violet Fungi +task: Executing a Rot Grub +task: Executing a greater Boy Scout +task: Executing a Girl Scout +task: Executing an underage Gelatinous Cube +task: Executing a Hippogriff +task: Executing a Battle-Ogre +task: Executing a Wolog +task: Executing a sick Gray Ooze +task: Executing a Ghast +task: Executing an Orc +task: Executing a Nymph +task: Executing a Stun Worm +task: Executing a Jumpskin +task: Executing a massive Dervish +task: Executing a Battle-Lemure +task: Executing an Ogre +task: Executing a Wight +task: Executing a Piercer +task: Executing a Troglodyte +task: Heading to market to sell loot +task: Selling a shrieker stalk +task: Selling 2 ghast vomits +task: Selling a violet fungi spore +task: Selling a rot grub eggsac +task: Selling a boy scout merit badge +task: Selling a girl scout cookie +task: Selling a gelatinous cube jam +task: Selling a hippogriff egg +task: Selling 2 ogre talons +task: Selling a wolog lemma +task: Selling a gray ooze gravy +task: Selling an orc snout +task: Selling a nymph hanky +task: Selling a stun worm trode +task: Selling a jumpskin shin +task: Selling a dervish robe +task: Selling a lemure blob +task: Selling a wight lung +task: Selling a piercer tip +task: Selling a troglodyte tail +task: Heading to the killing fields +task: Executing a cursed Multicell +task: Executing a Bugboar +task: Executing a Yellow Mold +task: Executing a Grid Bug +task: Executing an undernourished Yeti +task: Executing a Lemure +task: Executing a baby Manticore +task: Executing an Unicorn +task: Executing a veteran Grid Bug +task: Executing a Gnome +task: Executing a Mini Giant +task: Executing a Sylph +task: Executing a Jumpskin +task: Executing a Malebranche +task: Executing a sick Ogre +task: Executing a greater Shrieker +task: Executing a massive Rot Grub +task: Executing a greater Ogre +task: Executing a Bat +task: Executing a passing Half Halfling Shiv-Knight +task: Executing a preadolescent Iguanadon +task: Executing a Berserker +task: Heading to market to sell loot +task: Selling a multicell dendrite +task: Selling a bugboar tusk +task: Selling a yellow mold spore +task: Selling 2 grid bug carapaces +task: Selling a yeti fur +task: Selling a lemure blob +task: Selling a manticore spike +task: Selling an unicorn blood +task: Selling a gnome hat +task: Selling a mini giant pompadour +task: Selling a sylph thigh +task: Selling a jumpskin shin +task: Selling a malebranche fork +task: Selling 2 ogre talons +task: Selling a shrieker stalk +task: Selling a rot grub eggsac +task: Selling a bat wing +task: Selling a Garlanded Scabbard of Rapidity +task: Selling an iguanadon thumb +task: Selling a berserker shirt +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a greater Green Slime +task: Executing a Hippogriff +task: Executing an undernourished Ogre +task: Executing a greater Stun Worm +task: Executing a teenage Iguanadon +task: Executing a Battle-Octopus +task: Executing a cursed Brown Elf +task: Executing a greater Peryton +task: Executing a Battle-Dryad +task: Executing a Billy Goat +task: Executing a greater Green Slime +task: Executing an underage Man-o-war +task: Executing a greater Multicell +task: Executing a greater Harpy +task: Executing an Eel +task: Executing a massive Girl Scout +task: Executing a Pegasus +task: Executing an undernourished Manticore +task: Executing a sick Gelatinous Cube +task: Executing a crippled Owlbear +task: Executing a Bugbear +task: Heading to market to sell loot +task: Selling 2 green slime samples +task: Selling a hippogriff egg +task: Selling an ogre talon +task: Selling a stun worm trode +task: Selling an iguanadon thumb +task: Selling an octopus beak +task: Selling a brown elf tusk +task: Selling a peryton antler +task: Selling a dryad acorn +task: Selling a billy goat beard +task: Selling a man-o-war tentacle +task: Selling a multicell dendrite +task: Selling a harpy mascara +task: Selling an eel sashimi +task: Selling a girl scout cookie +task: Selling a pegasus aileron +task: Selling a manticore spike +task: Selling a Dual Albatross of Suffering +task: Selling a gelatinous cube jam +task: Selling an owlbear feather +task: Selling a bugbear skin +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Centaur +task: Executing a passing Talking Pony Ur-Paladin +task: Executing a Bugboar +task: Executing a greater Jumpskin +task: Executing an undernourished Ogre Mage +task: Executing Captain Shekmun the Hob-Hobbit +task: Executing Mrs. Nibzaed the Battle-Finch +task: Executing a Bugboar +task: Executing a cursed Sylph +task: Executing a Dervish +task: Executing a Goblin +task: Executing a Brown Elf +task: Executing a teenage Spectre +task: Executing an underage Gargoyle +task: Executing a Carrion Crawler +task: Executing a Stun Worm +task: Executing a veteran Yellow Mold +task: Executing an underage Ochre Jelly +task: Executing a Poroid +task: Executing a Shadow +task: Executing a cursed Shrieker +task: Executing a cursed Brown Elf +task: Heading to market to sell loot +task: Selling a centaur rib +task: Selling a Reverential Scabbard of Awe +task: Selling 2 bugboar tusks +task: Selling a jumpskin shin +task: Selling an ogre mage apparel +task: Selling a Golden Tome of Hunger +task: Selling a Proverbial Albatross of Patience +task: Selling a sylph thigh +task: Selling a dervish robe +task: Selling a goblin ear +task: Selling 2 brown elf tusks +task: Selling a spectre vestige +task: Selling a gargoyle gravel +task: Selling a carrion crawler egg +task: Selling a stun worm trode +task: Selling a yellow mold spore +task: Selling an ochre jelly nucleus +task: Selling a poroid node +task: Selling a shadow silhouette +task: Selling a shrieker stalk +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Bugbear +task: Executing a greater Camel +task: Executing a Battle-Camel +task: Executing a greater Whippet +task: Executing a greater Camel +task: Executing a Battle-Camel +task: Executing an undernourished Yeti +task: Executing a Harpy +task: Executing a Zombie +task: Executing a greater Grid Bug +task: Executing a massive Mermaid +task: Executing a cursed Nymph +task: Executing an underage Harpy +task: Executing a foetal White Dragon +task: Executing an underage Peryton +task: Executing a Stun Worm +task: Executing Mr. Whidkrum the Double Wookiee +task: Executing a Boy Scout +task: Executing a greater Boy Scout +task: Executing a preadolescent Hell Hound +task: Executing a veteran Cub Scout +task: Executing an undernourished Stun Bear +task: Heading to market to sell loot +task: Selling a bugbear skin +task: Selling 4 camel humps +task: Selling a whippet collar +task: Selling a yeti fur +task: Selling 2 harpy mascaras +task: Selling a zombie forehead +task: Selling a grid bug carapace +task: Selling a mermaid gills +task: Selling a nymph hanky +task: Selling a white dragon tooth +task: Selling a peryton antler +task: Selling a stun worm trode +task: Selling a Sacred Diadem of Incarceration +task: Selling 2 boy scout merit badges +task: Selling a hell hound tongue +task: Selling a cub scout neckerchief +task: Selling a stun bear tooth +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing an Eagle Scout +task: Executing an underage Mini Giant +task: Executing a sick Cockatrice +task: Executing a Gray Ooze +task: Executing a cursed Green Slime +task: Executing a Battle-Troglodyte +task: Executing an underage Ogre +task: Executing an undernourished Boy Scout +task: Executing an Unicorn +task: Executing a Homunculus +task: Executing a greater Mermaid +task: Executing a Gargoyle +task: Executing a preadolescent Wyvern +task: Executing a Battle-Manes +task: Executing a Dwarf +task: Executing a Battle-Triton +task: Executing a Nymph +task: Executing an Uruk +task: Executing a teenage Manticore +task: Executing a massive Triton +task: Executing an enormous Manes +task: Heading to market to sell loot +task: Selling an eagle scout merit badge +task: Selling a mini giant pompadour +task: Selling a cockatrice wattle +task: Selling a gray ooze gravy +task: Selling a green slime sample +task: Selling a troglodyte tail +task: Selling an ogre talon +task: Selling a boy scout merit badge +task: Selling an unicorn blood +task: Selling a Gilded Ornament of Electrum +task: Selling a homunculus fluid +task: Selling a mermaid gills +task: Selling a gargoyle gravel +task: Selling a wyvern wing +task: Selling 2 manes tooths +task: Selling a dwarf drawers +task: Selling 2 triton scales +task: Selling a nymph hanky +task: Selling an uruk boot +task: Selling a manticore spike +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a Triton +task: Executing an underage Triton +task: Executing Captain Moopaet the Trans-Kobold +task: Executing a Hippogriff +task: Executing a Wight +task: Executing a greater Troglodyte +task: Executing an Imp +task: Executing a greater Jumpskin +task: Executing a Sylph +task: Executing a Yeti +task: Executing a Bugboar +task: Executing a Centaur +task: Executing a Violet Fungi +task: Executing a Yeti +task: Executing an undernourished Bugboar +task: Executing a Sea Elf +task: Executing a Piercer +task: Executing a Zombie +task: Executing a greater Goblin +task: Executing a Battle-Nymph +task: Executing a sick foetal Bone Devil +task: Heading to market to sell loot +task: Selling 2 triton scales +task: Selling an Austere Hymnal of the Bone +task: Selling a Garlanded Brooch of Rapidity +task: Selling a hippogriff egg +task: Selling a wight lung +task: Selling a troglodyte tail +task: Selling an imp tail +task: Selling a jumpskin shin +task: Selling a sylph thigh +task: Selling 2 yeti furs +task: Selling 2 bugboar tusks +task: Selling a centaur rib +task: Selling a violet fungi spore +task: Selling a sea elf jerkin +task: Selling a piercer tip +task: Selling a zombie forehead +task: Selling a goblin ear +task: Selling a nymph hanky +task: Selling a bone devil hook +task: Negotiating purchase of better equipment +task: Heading to the killing fields +task: Executing a greater Billy Goat +task: Executing a Battle-Imp +task: Executing a sick Iguanadon +task: Executing a Troglodyte +task: Executing a Pegasus +task: Executing a cursed Merman +task: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Jimyab the Doppelganger +queued: Victory! Jimyab the Doppelganger is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Princess of Lavtreg +queued: There is rejoicing, and an unnerving encounter with Princess of Lavtreg in private +queued: You forget your writ and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Princess of Lavtreg be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of Edoock +queued: There is rejoicing, and an unnerving encounter with Queen of Edoock in private +queued: You forget your counterpane and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of Edoock be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Nuxock the Gorgon +queued: You seem to gain the advantage over Nuxock the Gorgon +queued: Victory! Nuxock the Gorgon is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Credyoom the Cockatrice +queued: Credyoom the Cockatrice seems to have the upper hand +queued: Victory! Credyoom the Cockatrice is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Ukmox the Erinyes +queued: Ukmox the Erinyes seems to have the upper hand +queued: Victory! Ukmox the Erinyes is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Chief of Uvjav +queued: There is rejoicing, and an unnerving encounter with Chief of Uvjav in private +queued: You forget your towel and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Chief of Uvjav be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Krickrov the Mummy +queued: Victory! Krickrov the Mummy is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Prince of the Low Elf +queued: There is rejoicing, and an unnerving encounter with Prince of the Low Elf in private +queued: You forget your I.O.U. and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Prince of the Low Elf be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good King of Whubun +queued: There is rejoicing, and an unnerving encounter with King of Whubun in private +queued: You forget your counterpane and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could King of Whubun be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of Whietbrag +queued: There is rejoicing, and an unnerving encounter with Queen of Whietbrag in private +queued: You forget your casket and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of Whietbrag be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Your quarry is in sight, but a mighty enemy bars your path! +queued: A desperate struggle commences with Naxxoock the Su-monster +queued: You seem to gain the advantage over Naxxoock the Su-monster +queued: Victory! Naxxoock the Su-monster is slain! Exhausted, you lose consciousness +queued: You awake in a friendly place, but the road awaits +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Archbishop of the Double Wookiee +queued: There is rejoicing, and an unnerving encounter with Archbishop of the Double Wookiee in private +queued: You forget your chicken and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Archbishop of the Double Wookiee be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of the Panda Man +queued: There is rejoicing, and an unnerving encounter with Queen of the Panda Man in private +queued: You forget your planter box and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of the Panda Man be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Chief of Ogdruk +queued: There is rejoicing, and an unnerving encounter with Chief of Ogdruk in private +queued: You forget your hat and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Chief of Ogdruk be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Exhausted, you arrive at a friendly oasis in a hostile land +queued: You greet old friends and meet new allies +queued: You are privy to a council of powerful do-gooders +queued: There is much to be done. You are chosen! +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Prince of Xiekraeb +queued: There is rejoicing, and an unnerving encounter with Prince of Xiekraeb in private +queued: You forget your twig and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Prince of Xiekraeb be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Queen of Lanshox +queued: There is rejoicing, and an unnerving encounter with Queen of Lanshox in private +queued: You forget your pint and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Queen of Lanshox be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +queued: Oh sweet relief! You've reached the protection of the good Boss of Zumpried +queued: There is rejoicing, and an unnerving encounter with Boss of Zumpried in private +queued: You forget your cookie and go back to get it +queued: What's this!? You overhear something shocking! +queued: Could Boss of Zumpried be a dirty double-dealer? +queued: Who can possibly be trusted with this news!? ... Oh yes, of course +queued: Loading Act II +monster: Executing a Sea Elf 6000 +monster: Executing an underage Blink Dog 9000 +monster: Executing a Battle-Mermaid 4000 +monster: Executing a teenage Anhkheg 6000 +monster: Executing an Unicorn 4800 +monster: Executing a Mummy 6000 +monster: Executing an underage Otyugh 5142 +monster: Executing a cursed Morkoth 6750 +monster: Executing a Cardboard Golem 9333 +monster: Executing an underage Baluchitherium 7800 +monster: Executing an undernourished Fire Giant 5454 +monster: Executing a greater demon Blue Dragon 7500 +monster: Executing an enormous Gold Dragon 5076 +monster: Executing a greater Hair Elemental 7285 +monster: Executing a Jubilex 6800 +monster: Executing Sgt. Mocklux the Double Hobbit 5250 +monster: Executing a greater Cheese Elemental 5294 +monster: Executing a messianic Ogre Mage 5000 +monster: Executing a preadolescent Yeenoghu 6947 +monster: Executing a passing Demicanadian Battle-Felon 6600 +monster: Executing 3 undernourished Djinns 5142 +monster: Executing a messianic Purple Worm 6818 +monster: Executing 2 Battle-Shedus 5217 +monster: Executing a Yeenoghu 6250 +monster: Executing 4 Satyrs 4800 +monster: Executing an enormous Yeenoghu 6461 +monster: Executing a messianic Oxygen Golem 6222 +monster: Executing a messianic Beer Golem 5571 +monster: Executing a giant demon Platinum Dragon 6206 +monster: Executing 2 Battle-Megalosauri 5200 +monster: Executing 4 teenage Ghosts 6193 +monster: Executing 3 undernourished Ice Devils 5625 +monster: Executing 3 underage Trappers 6000 +monster: Executing 3 greater Beholders 5823 +monster: Executing a giant demon Titan 4971 +monster: Executing 4 greater Beige Dragons 6666 +monster: Executing 5 underage Invisible Stalkers 5675 +monster: Executing 3 Pit Fiends 6157 +monster: Executing 2 enormous Rocs 6461 +monster: Executing a passing Double Hobbit Voodoo Princess 6300 +monster: Executing 4 undernourished Stone Giants 4682 +monster: Executing 6 undernourished Rice Giants 6000 +monster: Executing a messianic Geryon 7395 +monster: Executing 3 Dragon Turtles 5318 +monster: Executing 5 sick Ettins 5333 +monster: Executing 2 crippled Orci 6260 +monster: Executing 3 warrior Purple Worms 6893 +monster: Executing 2 imaginary Dispaters 5000 +monster: Executing 3 enormous Cheese Elementals 6244 +monster: Executing 3 Battle-Elasmosauri 5760 +monster: Executing 3 dead underage Yeenoghus 6705 +monster: Executing 3 greater Leather Golems 5538 +monster: Executing 4 underage Cardboard Golems 5886 +monster: Executing 6 underage Bone Devils 5333 +monster: Executing 4 veteran Dragon Turtles 6109 +monster: Executing 4 teenage Baluchitheriums 5142 +monster: Executing 6 Sand Elementals 5052 +monster: Executing 4 Chromatic Dragons 6620 +monster: Executing 3 crippled foetal Orci 5796 +monster: Executing 5 Trappers 6000 +monster: Executing 6 undernourished Neo-Otyughs 5901 +monster: Executing 2 greater demon Diplodoci 5806 +monster: Executing 6 Quartz Giants 5714 +monster: Executing 4 undernourished Oxygen Golems 6000 +monster: Executing 7 undernourished Couatls 5169 +monster: Executing 7 Sphinxes 6363 +monster: Executing Mr. Mogzub the Skraeling 5641 +monster: Executing 4 undernourished Tyrannosaurus Rexes 6000 +monster: Executing 3 sick Yeenoghus 6000 +monster: Executing 5 Battle-Baluchitheriums 6428 +monster: Executing 6 undernourished Pit Fiends 6084 +monster: Executing 3 massive Platinum Dragons 5750 +monster: Executing 8 undernourished Red Dragons 5917 +monster: Executing Admiral Revnien the Low Elf 5675 +monster: Executing a messianic Demogorgon 5280 +monster: Executing 8 Bacon Elementals 6315 +monster: Executing 8 undernourished Bone Devils 4987 +monster: Executing 5 underage Chromatic Dragons 5769 +monster: Executing 3 undernourished Yeenoghus 5468 +monster: Executing 3 demon Platinum Dragons 5850 +monster: Executing 5 veteran Pit Fiends 5185 +monster: Executing 7 greater Nalfeshnees 6146 +monster: Executing a passing Lesser Dwarf Mu-Fu Monk 6650 +monster: Executing 9 underage Quartz Giants 5785 +monster: Executing 7 Remorhazs 5435 +monster: Executing 9 Shedus 5651 +monster: Executing 8 Ettins 5517 +monster: Executing 8 greater Shambling Mounds 6000 +monster: Executing 7 Megalosauri 5662 +monster: Executing 2 imaginary Demogorgons 5733 +monster: Executing 5 Battle-Triceratopses 5604 +monster: Executing 3 greater Geryons 6065 +monster: Executing 9 undernourished Porcelain Giants 4645 +monster: Executing 14 Mariliths 6255 +monster: Executing 10 Bronze Dragons 5684 +monster: Executing 9 Silver Dragons 5625 +monster: Executing 2 imaginary Demogorgons 5195 +monster: Executing 10 Bone Devils 5510 +monster: Executing 10 undernourished Quartz Giants 5454 +level: 3 +act: 1 +gold: 10 +stat: STR 13 +stat: CON 12 +stat: DEX 11 +stat: INT 15 +stat: WIS 14 +stat: CHA 10 +stat: HP Max 20 +stat: MP Max 20 +equipment: Weapon Claw Hammer +equipment: Hauberk -1 Faded Flannel +equipment: Gambeson -1 Canvas +equipment: Shield -2 Round Shield +equipment: Helm +1 Macrame +equipment: Cuisses -3 Chamois +equipment: Greaves Motheaten Holey Pleathers +equipment: Gauntlets +1 Macrame +equipment: Sollerets Burlap +equipment: Brassairts -1 Patched Flannel +equipment: Vambraces Burlap +inventory: billy goat beard 1 +inventory: imp tail 1 +inventory: iguanadon thumb 1 +inventory: troglodyte tail 1 +inventory: pegasus aileron 1 +inventory: merman trident 1 +inventory: Proverbial Phial of Torpor 1 +inventory: Crafted Sphere of Fear 1 +inventory: Austere Bijou of Fortune 1 +inventory: Legendary Brooch of the Bone 1 +inventory: Arcane Tiara of Ra 1 +inventory: Gleaming Galoon of Ra 1 +inventory: Gleaming Gemstone of Misapprehension 1 +inventory: Unearthly Garnet of Internment 1 +spell: Seasick 1 +spell: Rabbit Punch 3 +spell: Revolting Cloud 1 +spell: Good Move 2 +spell: Cone of Annoyance 3 +spell: Hastiness 2 +spell: Sadness 2 +spell: Slime Finger 1 +quest: Exterminate the Minotaurs +quest: Fetch me a twig +quest: Exterminate the Hogbirds +quest: Deliver this bucket +quest: Exterminate the Lizard Men +quest: Fetch me a coin +quest: Deliver this hoe +quest: Seek the Gilded Arrow +quest: Fetch me an anvil +quest: Exterminate the Leprechauns +quest: Fetch me a writ +quest: Seek the Lucky Arrow +quest: Seek the Astral Laurel +quest: Placate the Centipedes +quest: Exterminate the Tritons +quest: Deliver this tuppence +quest: Exterminate the Black Dragons +quest: Exterminate the Green Slimes +quest: Fetch me an inkwell +quest: Seek the Spectral Gammel +quest: Fetch me an I.O.U. +quest: Seek the Austere Amethyst +quest: Exterminate the Hippocampi +quest: Placate the Ogres +quest: Fetch me a canoe +quest: Seek the Proverbial Scabbard +quest: Deliver this bandage +quest: Exterminate the Poroids +quest: Deliver this toothpick +quest: Deliver this toothpick +quest: Fetch me a ladder +quest: Placate the Yellow Molds +quest: Seek the Ostentatious Bandolier +quest: Fetch me a bunny +quest: Seek the One True Bijou +quest: Seek the Arcane Galoon +quest: Deliver this bunny +quest: Placate the Anhkhegs +quest: Fetch me a twig +quest: Exterminate the Ogre Mages +quest: Placate the Piercers +quest: Fetch me a hat +quest: Exterminate the Ants +quest: Seek the Legendary Tiara +quest: Seek the Austere Gimcrack +quest: Placate the Stun Bears diff --git a/tests/game_trace.py b/tests/game_trace.py new file mode 100644 index 0000000..131607d --- /dev/null +++ b/tests/game_trace.py @@ -0,0 +1,69 @@ +import datetime +import typing as T + +from pqcli import random +from pqcli.config import StatType +from pqcli.game_data import load_game_data +from pqcli.mechanic import Player, Simulation, Stats, monster_task + +STATS = { + StatType.strength: 12, + StatType.condition: 11, + StatType.dexterity: 10, + StatType.intelligence: 13, + StatType.wisdom: 14, + StatType.charisma: 9, + StatType.hp_max: 6, + StatType.mp_max: 5, +} + + +def run_trace(seed: str, ticks: int = 3000) -> T.List[str]: + """Play a deterministic game and return everything that happened.""" + data = load_game_data() + player = Player( + name="Tester", + birthday=datetime.datetime(2000, 1, 1), + race=data.races[0], + class_=data.classes[0], + stats=Stats(dict(STATS)), + ) + random.seed(seed) + simulation = Simulation(player, data) + + lines: T.List[str] = [] + player.connect( + "new_task", + lambda: lines.append(f"task: {player.task.description}"), + ) + for _ in range(ticks): + simulation.tick(100000.0) + + # plot cinematics and quests are rare in a short run, so force them + for _ in range(30): + simulation.interplot_cinematic() + simulation.complete_quest() + lines.extend(f"queued: {task.description}" for task in player.queue) + player.queue.clear() + + # high level monsters are unreachable in a short run + for level in range(1, 100): + task = monster_task(data, level, player.quest_book.monster) + lines.append(f"monster: {task.description} {task.duration}") + + lines.append(f"level: {player.level}") + lines.append(f"act: {player.quest_book.act}") + lines.append(f"gold: {player.inventory.gold}") + for stat, value in player.stats: + lines.append(f"stat: {stat.value} {value}") + for equipment_type, item in player.equipment: + lines.append(f"equipment: {equipment_type.value} {item}") + for inventory_item in player.inventory: + lines.append( + f"inventory: {inventory_item.name} {inventory_item.quantity}" + ) + for spell in player.spell_book: + lines.append(f"spell: {spell.name} {spell.level}") + for quest in player.quest_book.quests: + lines.append(f"quest: {quest}") + return lines diff --git a/tests/test_game_data.py b/tests/test_game_data.py new file mode 100644 index 0000000..b652fda --- /dev/null +++ b/tests/test_game_data.py @@ -0,0 +1,182 @@ +import datetime +import json +import pickle +import typing as T +from pathlib import Path + +import pytest + +from pqcli import random +from pqcli.config import Class, Monster, Race, StatType +from pqcli.game_data import ( + DATA_FILES, + DataError, + GameData, + dump_builtin_data, + load_game_data, +) +from pqcli.mechanic import Player, Stats, monster_task + + +def write(directory: Path, name: str, value: T.Any) -> Path: + path = directory / f"{name}.json" + path.write_text(json.dumps(value), encoding="utf-8") + return path + + +def test_builtin_data_loads() -> None: + data = load_game_data() + assert data.spells[0] == "Slime Finger" + assert data.monsters[0] == Monster("Anhkheg", 6, "chitin") + assert data.races[0] == Race("Half Orc", [StatType.hp_max]) + assert data.classes[-1] == Class("Vermineer", [StatType.intelligence]) + assert data.combat_verbs == ("Executing {target}",) + assert all(getattr(data, name) for name in DATA_FILES) + + +def test_builtin_data_keeps_quirks() -> None: + data = load_game_data() + names = [monster.name for monster in data.monsters] + assert names.count("Rat") == 2 + assert Monster("Neo-Otyugh", 11, "organ ") in data.monsters + assert any(monster.item is None for monster in data.monsters) + + +def test_missing_override_dir_uses_builtin(tmp_path: Path) -> None: + assert load_game_data(tmp_path / "nope") == load_game_data() + + +def test_override_replaces_only_that_list(tmp_path: Path) -> None: + write(tmp_path, "spells", ["Zap", "Bonk"]) + write( + tmp_path, + "races", + [{"name": "Robot", "attr": ["STR", "hp_max", "Wisdom"]}], + ) + builtin = load_game_data() + data = load_game_data(tmp_path) + assert data.spells == ("Zap", "Bonk") + assert data.races == ( + Race("Robot", [StatType.strength, StatType.hp_max, StatType.wisdom]), + ) + assert data.monsters == builtin.monsters + assert data.classes == builtin.classes + + +def test_override_accepts_all_shapes(tmp_path: Path) -> None: + write(tmp_path, "monsters", [{"name": "Bob", "level": 0, "item": None}]) + write(tmp_path, "weapons", [{"name": "Spoon", "quality": -1}]) + write(tmp_path, "offense_bad", [{"name": "Soggy", "quality": -2}]) + data = load_game_data(tmp_path) + assert data.monsters == (Monster("Bob", 0, None),) + assert data.weapons[0].name == "Spoon" + assert data.offense_bad[0].quality == -2 + + +def test_unknown_file_warns( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + write(tmp_path, "monster", []) + load_game_data(tmp_path) + assert "monster.json" in capsys.readouterr().err + + +@pytest.mark.parametrize( + "name,value,message", + [ + ("spells", [], "must not be empty"), + ("spells", {"a": 1}, "must be a list"), + ("spells", ["ok", 3], "[1]"), + ("monsters", [{"name": "Imp", "level": "2", "item": "x"}], "level"), + ("monsters", [{"name": "Imp", "level": True, "item": "x"}], "level"), + ("monsters", [{"name": "Imp", "level": 2}], "item"), + ("monsters", [{"name": "Imp", "level": 2, "item": 1}], "item"), + ("weapons", [{"name": "Spoon", "quality": 1, "x": 1}], "x"), + ("races", [{"name": "Elf", "attr": ["LUCK"]}], "LUCK"), + ("races", [{"name": "Elf", "attr": "STR"}], "attr"), + ("combat_verbs", ["Hitting"], "{target}"), + ], +) +def test_invalid_override_raises( + tmp_path: Path, name: str, value: T.Any, message: str +) -> None: + write(tmp_path, name, value) + with pytest.raises(DataError) as excinfo: + load_game_data(tmp_path) + assert f"{name}.json" in str(excinfo.value) + assert message in str(excinfo.value) + + +def test_malformed_json_raises(tmp_path: Path) -> None: + (tmp_path / "spells.json").write_text("[oops", encoding="utf-8") + with pytest.raises(DataError, match="spells.json"): + load_game_data(tmp_path) + + +def test_non_utf8_raises(tmp_path: Path) -> None: + (tmp_path / "spells.json").write_bytes(b'["\xff"]') + with pytest.raises(DataError, match="spells.json"): + load_game_data(tmp_path) + + +def test_override_dir_must_be_directory(tmp_path: Path) -> None: + path = write(tmp_path, "spells", ["Zap"]) + with pytest.raises(DataError, match="not a directory"): + load_game_data(path) + + +def test_combat_verbs_are_used(tmp_path: Path) -> None: + verbs = [ + "Fighting bravely against {target}", + "Kicking the butt of {target}", + ] + write(tmp_path, "combat_verbs", verbs) + data = load_game_data(tmp_path) + random.seed("verbs") + seen = set() + for _ in range(200): + description = monster_task(data, 5, None).description + prefix = description.split(" ")[0] + assert prefix in {"Fighting", "Kicking"} + assert "{target}" not in description + seen.add(prefix) + assert seen == {"Fighting", "Kicking"} + + +def test_dump_builtin_data_round_trips(tmp_path: Path) -> None: + written = dump_builtin_data(tmp_path / "out") + assert sorted(path.stem for path in written) == sorted(DATA_FILES) + assert load_game_data(tmp_path / "out") == load_game_data() + + +def test_dump_builtin_data_refuses_to_overwrite(tmp_path: Path) -> None: + existing = write(tmp_path, "spells", ["Mine"]) + with pytest.raises(DataError, match="spells.json"): + dump_builtin_data(tmp_path) + assert json.loads(existing.read_text()) == ["Mine"] + assert list(tmp_path.iterdir()) == [existing] + + +def test_saved_player_survives_removed_race(tmp_path: Path) -> None: + data = load_game_data() + player = Player( + name="Saved", + birthday=datetime.datetime(2000, 1, 1), + race=data.races[3], + class_=data.classes[2], + stats=Stats({stat: 10 for stat in StatType}), + ) + blob = pickle.dumps(player) + + write(tmp_path, "races", [{"name": "Robot", "attr": ["STR"]}]) + load_game_data(tmp_path) + restored = pickle.loads(blob) + assert restored.race == Race("Double Hobbit", [StatType.strength]) + assert type(restored.race).__module__ == "pqcli.config" + + +def test_game_data_is_immutable() -> None: + data = load_game_data() + assert isinstance(data, GameData) + with pytest.raises(AttributeError): + data.spells = () # type: ignore[misc] diff --git a/tests/test_parity.py b/tests/test_parity.py new file mode 100644 index 0000000..88010c1 --- /dev/null +++ b/tests/test_parity.py @@ -0,0 +1,14 @@ +from pathlib import Path + +import pytest +from game_trace import run_trace + +FIXTURES = Path(__file__).parent / "fixtures" + + +@pytest.mark.parametrize("seed", ["alpha", "beta", "gamma"]) +def test_gameplay_matches_recorded_trace(seed: str) -> None: + # The fixtures were recorded before the word lists moved to data files; + # stock data must reproduce them exactly, RNG stream included. + expected = (FIXTURES / f"parity_{seed}.txt").read_text().splitlines() + assert run_trace(seed) == expected diff --git a/uv.lock b/uv.lock index 3c26d05..a462ee8 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, ] +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -20,6 +29,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + [[package]] name = "filelock" version = "3.20.0" @@ -38,6 +59,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, ] +[[package]] +name = "iniconfig" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" }, +] + [[package]] name = "nodeenv" version = "1.9.1" @@ -47,6 +77,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + [[package]] name = "platformdirs" version = "4.5.1" @@ -56,6 +95,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] +[[package]] +name = "pluggy" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" }, +] + [[package]] name = "pqcli" version = "1.0.4" @@ -75,6 +123,7 @@ windows = [ [package.dev-dependencies] dev = [ { name = "pre-commit" }, + { name = "pytest" }, ] [package.metadata] @@ -88,7 +137,10 @@ requires-dist = [ provides-extras = ["windows"] [package.metadata.requires-dev] -dev = [{ name = "pre-commit", specifier = ">=2.16.0" }] +dev = [ + { name = "pre-commit", specifier = ">=2.16.0" }, + { name = "pytest", specifier = ">=8.0" }, +] [[package]] name = "pre-commit" @@ -106,6 +158,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" }, ] +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -170,6 +249,78 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "tomli" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/78/9ad63712633ed3ab5cc1a648d863d7e7da371e9425e209555a0fe711b695/tomli-2.5.0.tar.gz", hash = "sha256:264507556cd8b8c8e7c6ee037cdf443a463f03f4c958e57195e3d369711b8ff6", size = 17662, upload-time = "2026-10-07T12:23:37.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/ab99b60ee52acd949684febabc3005d0045d0f66bebd9cdebd67372d26dd/tomli-2.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4dc1c1781f2f716de763d1e9a7b34c6a894e167e291c7c5d16c72f7a9538545", size = 163901, upload-time = "2026-10-07T12:22:15.601Z" }, + { url = "https://files.pythonhosted.org/packages/bc/00/ee01b7ed4579180fff07142d290257f25ba786f23f3ec6005f620933c2f5/tomli-2.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eff8babca5a7999bc137acbc7482a8b7e17ffca5075ab41f5d770ab408c7bfef", size = 163756, upload-time = "2026-10-07T12:22:16.957Z" }, + { url = "https://files.pythonhosted.org/packages/72/c2/4efebf65372f6583185f79799312109dddb61102d47e5c33dcfd1a297aca/tomli-2.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86665cee9c4835b7a7f1e8ec2c719b5258d4dc782887aded5a8ae7352a96843b", size = 268038, upload-time = "2026-10-07T12:22:18.135Z" }, + { url = "https://files.pythonhosted.org/packages/53/07/5850468e925d898abb36038666f9c333a94d2a223e802a8ba5b6d319d23f/tomli-2.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e369fd63331746182360977b1892bfc215476a30d61612d732425311639f56", size = 276422, upload-time = "2026-10-07T12:22:19.567Z" }, + { url = "https://files.pythonhosted.org/packages/b4/87/f293984cdcf83c054196d4fd3dad44fc68ae55b4b8c44bc76cef360c3150/tomli-2.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ad1ea345759240d6463efa0ed1c704402752e49aa21476620738d74d72d8aa1", size = 272616, upload-time = "2026-10-07T12:22:20.794Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ce/db582886b3c1219d3fec93ebd669332482e5aee7a91e0f7838d84f2d1759/tomli-2.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96243987194634bd411066ce40c952e108f86af04db533ecd8ac3ff2a85b1885", size = 276593, upload-time = "2026-10-07T12:22:22.12Z" }, + { url = "https://files.pythonhosted.org/packages/bf/72/7619b87dea4261fc27dd7b54c4461c129c1f7d9bb7ba3aec89c797a431b8/tomli-2.5.0-cp311-cp311-win32.whl", hash = "sha256:610b27d99f28ec5f191c7064a48f3ddb179a1fe6ca73d571483ae859f57b605e", size = 101830, upload-time = "2026-10-07T12:22:23.651Z" }, + { url = "https://files.pythonhosted.org/packages/1e/74/220106da34502304b6751a2a9b8a9fbca6c3fd47e737a2e2e3da7c61c9db/tomli-2.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:c804ae44fe7b4bab5da295e4f980a1ff04670bca9d23fe0a4e887e08ebd741a8", size = 112742, upload-time = "2026-10-07T12:22:24.972Z" }, + { url = "https://files.pythonhosted.org/packages/27/99/7d9c8b41837a7773613e169504147375c157a290167aa59ad74a085f521f/tomli-2.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:cfac177ebd6236003846ea339981f71457cb6eb748f23381eb257e45092e3980", size = 109332, upload-time = "2026-10-07T12:22:26.117Z" }, + { url = "https://files.pythonhosted.org/packages/52/ed/7baa86f87493646a594de388c7c1c40a39dd0461f7e9c0359cbeefc91fe8/tomli-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f4a40d03fb9f63424f0979855bdeaf44dd7696b8d59501822c10ed30ba532df", size = 164854, upload-time = "2026-10-07T12:22:27.444Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b1/44c0341f2224397855723c7a8a39f718ea6fcbcc3dacc66e5aeca0f334e3/tomli-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9ebf8d19b17bd0daeb7b7dec81a946a439b753942fd0210d6e96c532249eea6b", size = 164074, upload-time = "2026-10-07T12:22:28.679Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e2d5b7d3fba47adedb23de616c16d428ea076c79a3d8e1d95d649ffe197e/tomli-2.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf0b5e8e0f68ebb494356e577c06c139161efd8d3b9050f93b39b7c26cc54ff0", size = 274274, upload-time = "2026-10-07T12:22:29.804Z" }, + { url = "https://files.pythonhosted.org/packages/43/90/6090e706ff27a6f89f4a40578e3324b95c3cd8c4150868aabf33a8f414c3/tomli-2.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cf74416bdc94ae458b14e37286c1073081850ac8459a00d0c5efef5d44294c6", size = 286435, upload-time = "2026-10-07T12:22:31.297Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9e/a2c40768df16c408f22430afb0a73e9d7e5f79c950884954649d1146b74d/tomli-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:61ea1ebe1e55a34ea8199cc8dbff398d35027b82271c8ac4802fd3a1fd5b1bcc", size = 278119, upload-time = "2026-10-07T12:22:32.601Z" }, + { url = "https://files.pythonhosted.org/packages/12/25/3c0cb485b98e9cfac495629b1c93c87ccf0b72fbe9d2689fd8fe62c6d5a3/tomli-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed53f7e89bb04f6d9e8e7799112360b0c4d5cbff067de0814c98c37c39b920f7", size = 286177, upload-time = "2026-10-07T12:22:33.745Z" }, + { url = "https://files.pythonhosted.org/packages/77/8b/0144c65f0e37e51c18d04ae15c21b19431c165002d0131fe9aa8b0b8b1e8/tomli-2.5.0-cp312-cp312-win32.whl", hash = "sha256:e7ad033e27a516a233bea839cdb77b80146facb3b4f40bf02cd0cac165cdd5c2", size = 102760, upload-time = "2026-10-07T12:22:34.887Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/5d6d8f42fc9a05fce69354e00ff256484192f5f2fc9a2165718fa0de61ec/tomli-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd05de8c1698f8413dd7d869492693a0bf2211543b787ac78cd5e7536af1a6d7", size = 112722, upload-time = "2026-10-07T12:22:36.162Z" }, + { url = "https://files.pythonhosted.org/packages/30/65/df18032218db0fb9b769fb23c8039a051f15c811993995ea04c350273a32/tomli-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:069435bd5480429b98c5e5afb02ab21c219b6f0064680671c6dc0d46817346ea", size = 109534, upload-time = "2026-10-07T12:22:37.296Z" }, + { url = "https://files.pythonhosted.org/packages/42/e5/51736d70da209350969e15aca5c5ab6e2ce1ea87a0a892a6c13aec172a86/tomli-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:943276cf269e0071948d9ff697159c1735e623c1151d88abb09b74659ef0cbea", size = 163328, upload-time = "2026-10-07T12:22:38.373Z" }, + { url = "https://files.pythonhosted.org/packages/ec/55/086f80dab4ab497602644274e6dea7ec5dd0b4e262e443a8ad3bb7edee2d/tomli-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:463b16086865b97facd8d0b3fb4cb7c544e3f58d2a69dc3113d6db9653fdb043", size = 162246, upload-time = "2026-10-07T12:22:39.673Z" }, + { url = "https://files.pythonhosted.org/packages/aa/eb/3ecc94459f3635c92321f4e7bde571323fdb2267c50e19e3188a281eae3b/tomli-2.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1245a6638fc4bb0a60af38a7d45413db34a13842027c77597c712c998c62fdf0", size = 272655, upload-time = "2026-10-07T12:22:41.08Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d7/494fd1f0c37a621f1ad9975c2efadb523e8101f144ed6edb2e7fe64738f2/tomli-2.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d8bac3d603c97e6854424e5b2b5b741bdbde387e09f162fb0446812b4a8362b", size = 283595, upload-time = "2026-10-07T12:22:42.222Z" }, + { url = "https://files.pythonhosted.org/packages/70/51/bb8d62b1317e6640866f6949b2d5855e5300f2c99d46de1cd245570bba65/tomli-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:21e4cae4114aba25aa0d4f85cdf486d290fb35c0954d7bba536248da64d43066", size = 276253, upload-time = "2026-10-07T12:22:43.625Z" }, + { url = "https://files.pythonhosted.org/packages/66/f4/f46bd7f0763cd47de2db697dca9257c6a4adfd1a93b018cc75c8190ed5a8/tomli-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bbaefc84548d754be821bba7c4141c4787dda182f9e77f2f87b71213529efa7b", size = 283582, upload-time = "2026-10-07T12:22:44.983Z" }, + { url = "https://files.pythonhosted.org/packages/ac/03/70f2bcb2923a6db37818d917e124270a7f4cfd38ea576f5aa753a91c0ef5/tomli-2.5.0-cp313-cp313-win32.whl", hash = "sha256:abdbf6313b8d9efe157edeb7ab6eae4de064b1300ad31abf73755154b30abe68", size = 102628, upload-time = "2026-10-07T12:22:46.508Z" }, + { url = "https://files.pythonhosted.org/packages/dc/98/d52024bb5b0ff68b4f0d276d867f634c84a67319a7e9f6b7708a37742333/tomli-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd4dc129784e0c5335bd4e61dfcc4487499a013419e655cf2da1d091b7e0efdc", size = 113301, upload-time = "2026-10-07T12:22:47.647Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f2/540db3a70572a8c23a28aba3e9c358ce0ffffbafc990905c1343aa265b31/tomli-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:69491c143d2fe063046e0301e62a810bed338fa4d1ce0fd870c27dc1e09b0d84", size = 109744, upload-time = "2026-10-07T12:22:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/e4/49/caf6b307766eb9567664a8707e9d6be5fcc0e8903f18781c6677a60d80c7/tomli-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d3182ee2d887e507bd67319a0a61105d1dd33facc111329559a233b772c1a105", size = 162899, upload-time = "2026-10-07T12:22:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/d3/c8/68cfce773a2733a49c74f99d627fb461bd990756860099eac25617889585/tomli-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:521345fd1f19d45b8df87657aaa38b6f2ca3800059fadf428e7ebf479a383646", size = 162080, upload-time = "2026-10-07T12:22:51.558Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b2/e5bb8651fdad593f670501a7d718b1a7f73f064d44dea15e04c04dfef45d/tomli-2.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e95c7614e705bfe2b04b27aa124adec59752d15813df37e2156747cab3a006b", size = 273380, upload-time = "2026-10-07T12:22:52.918Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d2/9e2d7f8b1dfe0e2b34c245986ebd55c4c553ea4ce6c47c443b332673253f/tomli-2.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac2027d37c3afbdf4bdd377f2676f6f1d2122a5be1f1137b49dced590b37e75", size = 283228, upload-time = "2026-10-07T12:22:54.173Z" }, + { url = "https://files.pythonhosted.org/packages/ba/df/ec7b876b7b1a2718bd74a3743c076fff565b04029ba33e8f61fac262739f/tomli-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c414be4ed9d3cac80c42e348fa5a956117d1a48227f48026e31f59cb4a7671eb", size = 277189, upload-time = "2026-10-07T12:22:55.342Z" }, + { url = "https://files.pythonhosted.org/packages/7d/7b/e192d9eed0b9cb80da799f4d77052297fb9a2c3cc9b19f571f56ea88add6/tomli-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9b03d7dc168353b4132965bde20feceabaa470e570c6f59660dfae59b1f9eeb3", size = 283632, upload-time = "2026-10-07T12:22:56.735Z" }, + { url = "https://files.pythonhosted.org/packages/84/50/ff94454e75461d75623e47401ed323d65c10aab8fe9033242c20cd2fdf32/tomli-2.5.0-cp314-cp314-win32.whl", hash = "sha256:6f041843c4d3a37245c0c056fd955b186bf8b1fb85690cbe40b81230891dc34b", size = 103535, upload-time = "2026-10-07T12:22:58.084Z" }, + { url = "https://files.pythonhosted.org/packages/54/0b/bdacf05f963bd6026ebf6eeb0beda847d1d60e03e440725c64a4e08a0afd/tomli-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:f4b653094e18f9031102d3a1da5c729c8f222d85225b18037dac621695e46e1a", size = 114621, upload-time = "2026-10-07T12:22:59.2Z" }, + { url = "https://files.pythonhosted.org/packages/61/99/53f438fa6ae4f9d4ed0ddde3e7242b3bdc34b48c8f9948b72b9e9b127676/tomli-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3f89d10c1ff6a38d992c27fc8a4816af71a909e08a40ec66934240b1e74347c3", size = 111572, upload-time = "2026-10-07T12:23:00.479Z" }, + { url = "https://files.pythonhosted.org/packages/b9/20/1f88f19427d380a40e90a770e087489eaafe4aeee070ae88ed2bbec00acd/tomli-2.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:e9e15b4a6c7dd6b85b5fbab29488a73f1f70de516942308daa266bf0e0aeb0d4", size = 171814, upload-time = "2026-10-07T12:23:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d0/56/cbe5079c9f9a54b9b3e27fc82f08f3cb36edee75561679f53d2380c801d6/tomli-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e12bbcd32897272fb05929110362ae9ff4c1b9bb26bd9e971e71dcd3275b4c3d", size = 171324, upload-time = "2026-10-07T12:23:03.18Z" }, + { url = "https://files.pythonhosted.org/packages/2b/30/1d53fd3b0f1cb3ba542e345ec32c26aefdddc4e829e4f3429af8a4f27782/tomli-2.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20aa36de8f2cf87237143bc1fa1aae8d6612c09118f4da21c6a684db5dd1f6f9", size = 297441, upload-time = "2026-10-07T12:23:04.345Z" }, + { url = "https://files.pythonhosted.org/packages/66/d9/0800acb6a111686f764c1b91ef15cc42a20a66a46013bb42220f1d2c61c1/tomli-2.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22185fad8a1e622f064e78008018a0dd3323550dcb479cb7a1d296888d74024f", size = 307476, upload-time = "2026-10-07T12:23:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/e8/63/30a8f3cd51b5bec37f04744bad0b0dc6160df84aad4f27b0e9283d66f221/tomli-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:984012f71908165449a951de2050d52f276bfe3aa5d5f570f63ddad814370374", size = 296113, upload-time = "2026-10-07T12:23:07.202Z" }, + { url = "https://files.pythonhosted.org/packages/ab/18/0b9ffc597e69c5a1e20a7823cb60d54b39a9f54e91edcb8574f022186758/tomli-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f79203b3965b4000e91808aaa7c040206093f2b8bf86f455982f2274c9ccf442", size = 307725, upload-time = "2026-10-07T12:23:08.508Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c7/18f8baae0b5607a60e8e19b4a7fedee43a8ff6458e3896dcbbadeeac9c22/tomli-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:91294a9fb94a75542f6e46e4a2ae709bd8d9b51134098cae5cf3bea5478b6d03", size = 108546, upload-time = "2026-10-07T12:23:09.956Z" }, + { url = "https://files.pythonhosted.org/packages/72/34/4cca9739254130627bde87500b3f2b512154fe2f278efa7e2a5e10ad4bcb/tomli-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f15e3e0b835a6d68b10c86bf80a3149780498d6911c93c3ffd1861d19f9200f1", size = 117814, upload-time = "2026-10-07T12:23:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/afa530d47dd80a78fce43beac6bc6e00f84558eafcffbc6f37b21e80d056/tomli-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6664b7ae7af7294256c53960a6103077f4914cec8ff98479c352f622c6f6b2f0", size = 115188, upload-time = "2026-10-07T12:23:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/66/98/316fdc00f8c0939e6fe50461dd343c162d3ad51d1286eb25b7db54361d50/tomli-2.5.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a525685c2f97da40762b8695eb7aa0af4c8344ca1905c73e4e29cb04d34607dc", size = 162775, upload-time = "2026-10-07T12:23:13.941Z" }, + { url = "https://files.pythonhosted.org/packages/c5/22/7b10fa5bb01c9539f53f69b619361b19350acc73657772ea7ac70ba309a8/tomli-2.5.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:9dbb18c1cfb2f6517942fc9314437f66aa06d94436ffb1f06102ef3572f35276", size = 161406, upload-time = "2026-10-07T12:23:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e7/1a069d86dfd20f1f84f71c63faed9f83c1d890bc06c27d82dc7d888fb573/tomli-2.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:752e8b1aa6a4367ef8bf6a1a1e005540f7ed055ba36d7193796812ca5404eb52", size = 273855, upload-time = "2026-10-07T12:23:16.471Z" }, + { url = "https://files.pythonhosted.org/packages/ae/83/d1ef43d1687d092ab9c235455c76e6e709483b346b056f086095c7c263a5/tomli-2.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47300f9bf791808f77d82747691c4bb09cb14bdf3060cca99b42cdc4361d5a7", size = 284910, upload-time = "2026-10-07T12:23:18.166Z" }, + { url = "https://files.pythonhosted.org/packages/cc/05/f4d9cf7de61822ece0c3873f30d291e324911c71a378b8bfe5ced13fd9f5/tomli-2.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:19b0dd8749f4ea2f112c5fcfb3c5248390c899d7e2e173f1d91abee1fa0ff391", size = 277723, upload-time = "2026-10-07T12:23:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/42/28/78262493141fa543151cf005760c3cb01d09fc28a11f993c05109902cb8c/tomli-2.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:57b1c3b01fab802e2899bc3d168dca320e14165e2fd9fd584760fb4ca5826859", size = 285115, upload-time = "2026-10-07T12:23:20.698Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b9/e1dab9a30bcb677b5cc5cee810609cfd64f24306a3055767dd3fda00b1e0/tomli-2.5.0-cp315-cp315-win32.whl", hash = "sha256:667e521b37a6c5ccaa044202c235b530f90177ffe2cd4a64ecc213c7dd535feb", size = 103475, upload-time = "2026-10-07T12:23:21.941Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bd/31a3790c11d6ea95fcf5e6022ac0f8d0543c9b61120b730fc481bd43d3b4/tomli-2.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:d747252933c8a65ef6bd8da0fbb7ce28a90eb6119d8cd00772cd528aa07b68d5", size = 114589, upload-time = "2026-10-07T12:23:23.098Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/4f6310fa699364f0e3af7ee3af88dddd9af066d33e716a0265bbe2b3ea84/tomli-2.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:75dbcde8751b0a960aa3de173aa5e894d590755c6d7758b7e774c06f1dc3cbdd", size = 111493, upload-time = "2026-10-07T12:23:24.233Z" }, + { url = "https://files.pythonhosted.org/packages/68/14/00853f0b396d8971107ae1921bb5b322fdee1650d2f16bf06c20adb532e5/tomli-2.5.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2419c2a189551987b59d80e63ec355671283336f41c6b9b89462df679c7d0c57", size = 171380, upload-time = "2026-10-07T12:23:25.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/ad/fa6949321dadee46b27363974fb197b94c911c3b0f7a5fd26d7dc18fc2a0/tomli-2.5.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:0dc598040da8d42cf20f0be588ed7004f46db12a0ac6c32e03a59dccedaaadcd", size = 170553, upload-time = "2026-10-07T12:23:26.855Z" }, + { url = "https://files.pythonhosted.org/packages/53/aa/3056c919eb3e084df3752b2cf5f865dcc04af0b27dba2f66d7b28af4633a/tomli-2.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49096930c8d886c9bbdab62d2d0d17ce823ddeea522309a190b36245d5b49e01", size = 294428, upload-time = "2026-10-07T12:23:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/faeeb5d8769ea3832021d73e892c8391eae7b4b4f8b55a789127bd8b18a9/tomli-2.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8ade5023067f99fe72b88accd30d0ea05a158e9e32a11f124e731ea9695313f", size = 304909, upload-time = "2026-10-07T12:23:29.381Z" }, + { url = "https://files.pythonhosted.org/packages/f6/52/f094c09e73fb654b621716d019acb5d29bdfd1be01df80c281d552bda48d/tomli-2.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b69564772b5c8f22ea5f498dff08cfa825045b4d4c4400529000bdf818aa3b2a", size = 293220, upload-time = "2026-10-07T12:23:30.608Z" }, + { url = "https://files.pythonhosted.org/packages/86/f5/0c30541078ca4b505ce3bd76ed931facbfec524dd018535d691d1af0a6d2/tomli-2.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8ff3a2ca028c7eee0c777f9a092038d0a594a9fa04e215f929a22c329e2cb142", size = 305705, upload-time = "2026-10-07T12:23:32.181Z" }, + { url = "https://files.pythonhosted.org/packages/05/74/590e7d19d6a118fc5cc5704ff358e21d95b8573f6b9443b1519f29ca8825/tomli-2.5.0-cp315-cp315t-win32.whl", hash = "sha256:62fc1bc8eb03e3a9cadfca713d65614ed8e09d974a283295ffe3a831976b4dc5", size = 108432, upload-time = "2026-10-07T12:23:33.496Z" }, + { url = "https://files.pythonhosted.org/packages/1c/b8/63a75cfb27a17c38550e44025d3a6e7be64516fd8608a3b75703bf37d81b/tomli-2.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f3fcbc57b1791fa6cbe5d8434179d51de12be1a4811469529f47f6e7487a2571", size = 117281, upload-time = "2026-10-07T12:23:34.648Z" }, + { url = "https://files.pythonhosted.org/packages/72/01/e8c1debb2173973372934c68fc8e46170ab60ef23ed4592dff4dec6e8993/tomli-2.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d2ba24db8a9376921b5e87b4762b9adb0f3f1deaea68f2b8b0bb2c11efb9c3e7", size = 115069, upload-time = "2026-10-07T12:23:35.77Z" }, + { url = "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl", hash = "sha256:32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b", size = 14765, upload-time = "2026-10-07T12:23:36.875Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0"