From 991b2fad188e8d4f4d9ab7ff1e6580b4d2604ffd Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 01:51:01 +0100 Subject: [PATCH 1/9] Add knaverats --- create.py | 9 + knaverats.py | 619 +++++++++++++++++++++++++++++++++++++++ templates/knaverats.html | 48 +++ 3 files changed, 676 insertions(+) create mode 100644 knaverats.py create mode 100644 templates/knaverats.html diff --git a/create.py b/create.py index 6e07b3f..c608f1d 100644 --- a/create.py +++ b/create.py @@ -9,6 +9,7 @@ import demoncity import dice import fifth +import knaverats import mazerats import silent_titans import troika @@ -100,6 +101,14 @@ def make_mazerats_char(number): characters = [mazerats.Character() for _ in range(number)] return render_template("mazerats.html", characters=characters) +@app.route('/knaverats/', defaults={'number': 1}) +@app.route('/knaverats//') +def make_knaverats_char(number): + if number >= 20: + number = 20 + characters = [knaverats.Character() for _ in range(number)] + return render_template("knaverats.html", characters=characters) + @app.route('/npcs/', defaults={'number': 10}) @app.route('/npcs//') def generate_npcs(number): diff --git a/knaverats.py b/knaverats.py new file mode 100644 index 0000000..4a2b50f --- /dev/null +++ b/knaverats.py @@ -0,0 +1,619 @@ +import itertools +import random + +import mixins +import dice + + +class Character(object): + + def __init__(self, *args, **kwargs): + super(Character, self).__init__(*args, **kwargs) + + # Attributes + self.STR = self.attribute() + self.DEX = self.attribute() + self.CON = self.attribute() + self.INT = self.attribute() + self.WIS = self.attribute() + self.CHA = self.attribute() + + self.health = 4 + int(self.CON) + self.shield, self.light_armour = self.armour() + armour_bonus = 1 if self.light_armour else 0 + self.armour = 6 + armour_bonus + + self.attack = 0 # The default attack bonus is 0 + self.skill = None # No starting skill + self.spell = None # No starting spell. + + self.equipment = ', '.join(self.get_equipment()) + + self.get_feature() + + def attribute(self): + return random.choice(["+2", "+1", "+1", "+1", "0", "0"]) + + def armour(self): + return random.choice([ + (True, True), + (True, True), + (True, True), + (False, True), + (False, True), + (False, False), + ]) + + def get_equipment(self): + items = [ + "Animal Scent", + "Bear Trap", + "Bedroll", + "Caltrops", + "Chain (10 ft.)", + "Chalk", + "Chisel", + "Crowbar", + "Fishing Net", + "Glass Marbles", + "Glue", + "Grappling Hook", + "Grease", + "Hacksaw", + "Hammer", + "Hand drill", + "Horn", + "Iron spikes", + "Iron tongs", + "Lantern and Oil", + "Large Sack", + "Lockpicks (3)", + "Manacles", + "Medicine (3)", + "Metal file", + "Rope (50 ft.)", + "Steel wire", + "Shovel", + "Steel mirror", + "Ten Foot Pole", + "Tinderbox", + "Torch", + "Vial of Acid", + "Vial of Poison", + "Waterskin", + ] + + weapons = [ + 'Axe', 'Dagger', 'Mace', 'Short Sword', 'Flail', + 'One-Handed Spear', 'Spears', 'Halberd', 'Long Sword', 'Warhammer', + 'Bow', 'Cross Bow', 'Sling' + ] + + equip = ['Rations (2)'] + if self.shield: + equip += ['Shield'] + if self.light_armour: + equip += ['Light Armour'] + + return equip + random.sample(items, 6) + random.sample(weapons, 2) + + def get_feature(self): + feature = random.choice(['fighter', 'wizard', 'specialist']) + if feature == 'fighter': + self.attack = "+1" + elif feature == 'wizard': + self.spell = self.get_spell() + elif feature == 'specialist': + self.skill = self.get_skills() + + def get_spell(self): + return 'MAGIC' + + def get_skills(self): + return random.choice([ + "Briarborn (tracking, foraging, survival)", + "Fingersmith (Tinkering, picking locks or pockets)", + "Roofrunner (climbing, leaping, balancing)", + "Shadowjack (moving silently, hiding in shadows)", + ]) + + @property + def appearance(self): + return random.choice([ + "Aquiline", + "Athletic", + "Barrel-Chested", + "Boney", + "Brawny", + "Brutish", + "Bullnecked", + "Chiseled", + "Coltish", + "Corpulent", + "Craggy", + "Delicate", + "Furrowed", + "Gaunt", + "Gorgeous", + "Grizzled", + "Haggard", + "Handsome", + "Hideous", + "Lanky", + "Pudgy", + "Ripped", + "Rosy", + "Scrawny", + "Sinewy", + "Slender", + "Slumped", + "Solid", + "Square-Jawed", + "Statuesque", + "Towering", + "Trim", + "Weathered", + "Willowy", + "Wiry", + "Wrinkled", + ]) + + @property + def physical_detail(self): + return random.choice([ + "Acid scars", + "Battle scars", + "Birthmark", + "Braided hair", + "Brand mark", + "Broken nose", + "Bronze skinned", + "Burn scars ", + "Bushy eyebrows", + "Curly hair", + "Dark skinned", + "Dreadlocks", + "Exotic accent", + "Flogging scars", + "Freckles", + "Gold tooth", + "Hoarse voice", + "Huge beard", + "Long hair", + "Matted hair", + "Missing ear", + "Missing teeth", + "Moustache", + "Muttonchops", + "Nine fingers", + "Oiled hair", + "One-eyed", + "Pale skinned", + "Piercings ", + "Ritual scars", + "Sallow skin", + "Shaved head", + "Sunburned", + "Tangled hair", + "Tattoos", + "Topknot", + ]) + + @property + def background(self): + return random.choice([ + "Alchemist", + "Beggar-prince", + "Blackmailer", + "Bounty-hunter", + "Chimney sweep", + "Coin-clipper", + "Contortionist", + "Counterfeiter", + "Cultist", + "Cutpurse", + "Debt-collector", + "Deserter", + "Fence", + "Fortuneteller", + "Galley slave", + "Gambler", + "Gravedigger", + "Headsman", + "Hedge knight", + "Highwayman", + "Housebreaker", + "Kidnapper", + "Mad prophet", + "Mountebank", + "Peddler", + "Pit-fighter", + "Poisoner", + "Rat-catcher", + "Scrivener", + "Sellsword", + "Slave", + "Smuggler", + "Street performer", + "Tattooist", + "Urchin", + "Usurer", + ]) + + @property + def clothing(self): + return random.choice([ + "Antique", + "Battle-torn", + "Bedraggled", + "Blood-stained", + "Ceremonial", + "Dated", + "Decaying", + "Eccentric", + "Elegant", + "Embroidered", + "Exotic", + "Fashionable", + "Flamboyant", + "Food-stained", + "Formal", + "Frayed", + "Frumpy", + "Garish", + "Grimy", + "Haute couture", + "Lacey", + "Livery", + "Mud-stained", + "Ostentatious", + "Oversized", + "Patched", + "Patterned", + "Perfumed", + "Practical", + "Rumpled", + "Sigils", + "Singed", + "Tasteless", + "Undersized", + "Wine-stained", + "Worn out", + ]) + + @property + def personality(self): + return random.choice([ + "Bitter", + "Brave", + "Cautious", + "Chipper", + "Contrary", + "Cowardly", + "Cunning", + "Driven", + "Entitled", + "Gregarious", + "Grumpy", + "Heartless", + "Honor-bound", + "Hotheaded", + "Inquisitive", + "Irascible", + "Jolly", + "Know-it-all", + "Lazy", + "Loyal", + "Menacing", + "Mopey", + "Nervous", + "Protective", + "Righteous", + "Rude", + "Sarcastic", + "Savage", + "Scheming", + "Serene", + "Spacey", + "Stoic", + "Stubborn", + "Stuck-up", + "Suspicious", + "Wisecracking", + ]) + + @property + def mannerism(self): + return random.choice([ + "Anecdotes", + "Breathy", + "Chuckles", + "Clipped", + "Cryptic", + "Deep voice", + "Drawl", + "Enunciates", + "Flowery speech", + "Gravelly voice", + "Highly formal", + "Hypnotic", + "Interrupts", + "Laconic", + "Laughs", + "Long pauses", + "Melodious", + "Monotone", + "Mumbles", + "Narrates", + "Overly casual", + "Quaint sayings", + "Rambles", + "Random facts", + "Rapid-fire", + "Rhyming", + "Robotic", + "Slow speech", + "Speechifies", + "Squeaky", + "Street slang", + "Stutters", + "Talks to self", + "Trails off", + "Very loud", + "Whispers", + ]) + + def get_spell(self): + physical_element = [ + "Animating", + "Attracting", + "Binding", + "Blossoming", + "Consuming", + "Creeping", + "Crushing", + "Diminishing", + "Dividing", + "Duplicating", + "Enveloping", + "Expanding", + "Fusing", + "Grasping", + "Hastening", + "Hindering", + "Illuminating", + "Imprisoning", + "Levitating", + "Opening ", + "Petrifying", + "Phasing", + "Piercing", + "Pursuing", + "Reflecting ", + "Regenerating", + "Rending", + "Repelling", + "Resurrecting", + "Screaming", + "Sealing", + "Shapeshifting", + "Shielding", + "Spawning", + "Transmuting", + "Transporting", + ] + + physical_effect = [ + "Acid", + "Amber", + "Bark", + "Blood", + "Bone", + "Brine", + "Clay ", + "Crow", + "Crystal", + "Ember", + "Flesh", + "Fungus", + "Glass", + "Honey", + "Ice", + "Insect", + "Wood ", + "Lava", + "Moss", + "Obsidian", + "Oil", + "Poison", + "Rat ", + "Salt", + "Sand", + "Sap", + "Serpent", + "Slime", + "Stone", + "Tar", + "Thorn", + "Vine", + "Water", + "Wine", + "Wood", + "Worm", + ] + + physical_form = [ + "Altar", + "Armor", + "Arrow ", + "Beast", + "Blade", + "Cauldron", + "Chain", + "Chariot", + "Claw", + "Cloak", + "Colossus", + "Crown", + "Elemental", + "Eye", + "Fountain", + "Gate", + "Golem", + "Hammer", + "Horn", + "Key", + "Mask", + "Monolith", + "Pit", + "Prison", + "Sentinel", + "Servant", + "Shield", + "Spear", + "Steed", + "Swarm", + "Tentacle", + "Throne", + "Torch", + "Trap", + "Wall", + "Web", + + ] + + ethereal_element = [ + "Avenging", + "Banishing", + "Bewildering", + "Blinding", + "Charming", + "Communicating", + "Compelling", + "Concealing", + "Deafening", + "Deceiving", + "Deciphering", + "Disguising", + "Dispelling", + "Emboldening", + "Encoding", + "Energizing", + "Enlightening", + "Enraging", + "Excruciating", + "Foreseeing", + "Intoxicating", + "Maddening", + "Mesmerizing", + "Mindreading", + "Nullifying", + "Paralyzing", + "Revealing", + "Revolting", + "Scrying", + "Silencing", + "Soothing", + "Summoning", + "Terrifying", + "Warding", + "Wearying", + "Withering", + ] + + ethereal_effect = [ + "Ash", + "Chaos", + "Distortion", + "Dream", + "Dust", + "Echo", + "Ectoplasm", + "Fire", + "Fog", + "Ghost", + "Harmony", + "Heat", + "Light", + "Lightning", + "Memory", + "Mind", + "Mutation", + "Negation", + "Plague", + "Plasma", + "Probability", + "Rain", + "Rot", + "Shadow", + "Smoke", + "Snow", + "Soul", + "Star", + "Stasis", + "Steam", + "Thunder", + "Time", + "Void", + "Warp", + "Whisper", + "Wind", + ] + + ethereal_form = [ + "Aura", + "Beacon", + "Beam", + "Blast", + "Blob", + "Bolt", + "Bubble", + "Call", + "Cascade", + "Circle", + "Cloud", + "Coil", + "Cone ", + "Cube", + "Dance", + "Disk", + "Field", + "Form", + "Gaze", + "Loop", + "Moment", + "Nexus", + "Portal", + "Pulse", + "Pyramid", + "Ray", + "Shard", + "Sphere", + "Spray", + "Storm", + "Swarm", + "Torrent", + "Touch", + "Vortex", + "Wave", + "Word", + ] + + spell_form = [ + (physical_effect, physical_form), + (ethereal_element, physical_form), + (physical_effect, ethereal_form), + (ethereal_element, ethereal_form), + (ethereal_effect, physical_form), + (physical_effect, physical_element), + (ethereal_effect, ethereal_form), + (physical_effect, ethereal_element), + (physical_element, physical_form), + (ethereal_effect, physical_element), + (physical_element, ethereal_form), + (ethereal_effect, ethereal_element), + ] + + return ' '.join([random.choice(component) for component in random.choice(spell_form)]) + + diff --git a/templates/knaverats.html b/templates/knaverats.html new file mode 100644 index 0000000..73ee27b --- /dev/null +++ b/templates/knaverats.html @@ -0,0 +1,48 @@ +{% extends "base.html" %} + +{% block page_class %}Maze Rats{% endblock %} + +{% block title %}A Random Maze Rat{% endblock %} + +{% block content %} +

Maze Rats

+{% for c in characters %} +

Maze Rat!

+

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothing }} Clothing, {{ c.personality }}, {{ c.mannerism }}

+ + + + + + + + + + + + + + + + + +
STR: {{ c.STR }}DEX: {{ c.DEX }}CON: {{ c.CON }}
INT: {{ c.INT }}WIS: {{ c.WIS }}CHA: {{ c.CHA }}
Attack: {{ c.attack }}Armour: {{ c.armour }}Health: {{ c.health }}
+ +

+ You were once a {{ c.background}}. Now you are a Maze Rat, a grubby adventurer! + {% if c.spell %}You can cast {{ c.spell }}, a wondrous spell{% endif %} + {% if c.skill %}You possess the skills of a {{ c.skill }}.{% endif %} +

+ +

+ Equipment: {{ c.equipment }} +

+{% endfor %} + + +{% endblock %} + From 05ffcdab4009e2f90272ae0791568fbd16aaaeb2 Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 01:52:03 +0100 Subject: [PATCH 2/9] Add knaverats link --- knaverats.py | 1 + 1 file changed, 1 insertion(+) diff --git a/knaverats.py b/knaverats.py index 4a2b50f..0b51d68 100644 --- a/knaverats.py +++ b/knaverats.py @@ -4,6 +4,7 @@ import mixins import dice +# From https://www.reddit.com/r/rpg/comments/9smybh/knave_rats_my_favorite_parts_of_knave_and_maze/ class Character(object): From 1c8063f2d2a4005e4d3862e396c13afe12bd24cb Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 01:58:29 +0100 Subject: [PATCH 3/9] Add attribution --- templates/knaverats.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/knaverats.html b/templates/knaverats.html index 73ee27b..a681658 100644 --- a/templates/knaverats.html +++ b/templates/knaverats.html @@ -41,7 +41,7 @@

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothin {% endblock %} From ac0265e09ad0fdb582994728b23c18914e7101fa Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 02:00:23 +0100 Subject: [PATCH 4/9] Fix headers --- templates/knaverats.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/knaverats.html b/templates/knaverats.html index a681658..273d86a 100644 --- a/templates/knaverats.html +++ b/templates/knaverats.html @@ -1,13 +1,13 @@ {% extends "base.html" %} -{% block page_class %}Maze Rats{% endblock %} +{% block page_class %}Knave Rats{% endblock %} -{% block title %}A Random Maze Rat{% endblock %} +{% block title %}A Random Knave Rat{% endblock %} {% block content %} -

Maze Rats

+

Knave Rats

{% for c in characters %} -

Maze Rat!

+

Knave Rat!

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothing }} Clothing, {{ c.personality }}, {{ c.mannerism }}

@@ -29,7 +29,7 @@

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothin

- You were once a {{ c.background}}. Now you are a Maze Rat, a grubby adventurer! + You were once a {{ c.background}}. Now you are a Knave Rat, a grubby adventurer! {% if c.spell %}You can cast {{ c.spell }}, a wondrous spell{% endif %} {% if c.skill %}You possess the skills of a {{ c.skill }}.{% endif %}

From 2c9e269f27e808b252b41888138867004546460c Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 02:03:22 +0100 Subject: [PATCH 5/9] Add equipment slots --- knaverats.py | 1 + templates/knaverats.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/knaverats.py b/knaverats.py index 0b51d68..67c6805 100644 --- a/knaverats.py +++ b/knaverats.py @@ -29,6 +29,7 @@ def __init__(self, *args, **kwargs): self.spell = None # No starting spell. self.equipment = ', '.join(self.get_equipment()) + self.equipment_slots = 10 + int(self.STR) self.get_feature() diff --git a/templates/knaverats.html b/templates/knaverats.html index 273d86a..5befede 100644 --- a/templates/knaverats.html +++ b/templates/knaverats.html @@ -35,7 +35,7 @@

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothin

- Equipment: {{ c.equipment }} + Equipment ({{ c.equipment_slots }} max): {{ c.equipment }}

{% endfor %} From 1c6872061babd8d9ef0eee3b5f2a6152feaf57d2 Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 02:18:17 +0100 Subject: [PATCH 6/9] Add weapon classes --- knaverats.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/knaverats.py b/knaverats.py index 67c6805..1f4f035 100644 --- a/knaverats.py +++ b/knaverats.py @@ -21,10 +21,12 @@ def __init__(self, *args, **kwargs): self.health = 4 + int(self.CON) self.shield, self.light_armour = self.armour() + self.weapon_class = self.weapon() armour_bonus = 1 if self.light_armour else 0 - self.armour = 6 + armour_bonus + shield_bonus = 1 if self.shield else 0 + self.armour = 6 + armour_bonus + shield_bonus - self.attack = 0 # The default attack bonus is 0 + self.attack = 1 if self.weapon_class is "H" else 0 self.skill = None # No starting skill self.spell = None # No starting spell. @@ -46,6 +48,11 @@ def armour(self): (False, False), ]) + def weapon(self): + return random.choice([ + "L", "L", "L", "L", "H", "R", + ]) + def get_equipment(self): items = [ "Animal Scent", @@ -85,10 +92,17 @@ def get_equipment(self): "Waterskin", ] - weapons = [ - 'Axe', 'Dagger', 'Mace', 'Short Sword', 'Flail', - 'One-Handed Spear', 'Spears', 'Halberd', 'Long Sword', 'Warhammer', - 'Bow', 'Cross Bow', 'Sling' + weapons_light = [ + 'Axe', 'Dagger', 'Mace', 'Short Sword', + 'Flail', 'One-Handed Spear', + ] + + weapons_heavy = [ + 'Spear', 'Halberd', 'Long Sword', 'Warhammer', 'Zweihander' + ] + + weapons_ranged = [ + 'Bow', 'Cross Bow', 'Sling', 'Blowpipe', ] equip = ['Rations (2)'] @@ -97,6 +111,13 @@ def get_equipment(self): if self.light_armour: equip += ['Light Armour'] + if self.weapon_class == 'L': + weapons = weapons_light + if self.weapon_class == 'H': + weapons = weapons_heavy + if self.weapon_class == 'R': + weapons = weapons_ranged + return equip + random.sample(items, 6) + random.sample(weapons, 2) def get_feature(self): From f5545a8a3954d3749393c7ef9c07c689aeb5d8fd Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 02:23:59 +0100 Subject: [PATCH 7/9] Fix fighter --- create.py | 2 +- knaverats.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/create.py b/create.py index c608f1d..ee4d67e 100644 --- a/create.py +++ b/create.py @@ -11,7 +11,7 @@ import fifth import knaverats import mazerats -import silent_titans +#import silent_titans import troika import trophy diff --git a/knaverats.py b/knaverats.py index 1f4f035..3756d09 100644 --- a/knaverats.py +++ b/knaverats.py @@ -26,7 +26,7 @@ def __init__(self, *args, **kwargs): shield_bonus = 1 if self.shield else 0 self.armour = 6 + armour_bonus + shield_bonus - self.attack = 1 if self.weapon_class is "H" else 0 + self.attack = "+1" if self.weapon_class is "H" else "0" self.skill = None # No starting skill self.spell = None # No starting spell. @@ -123,7 +123,7 @@ def get_equipment(self): def get_feature(self): feature = random.choice(['fighter', 'wizard', 'specialist']) if feature == 'fighter': - self.attack = "+1" + self.attack = '+{}'.format((int(self.attack) + 1)) elif feature == 'wizard': self.spell = self.get_spell() elif feature == 'specialist': From 20596519415408dd142d166d5bcfb9c9570b0094 Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 02:24:39 +0100 Subject: [PATCH 8/9] Revert create --- create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create.py b/create.py index ee4d67e..c608f1d 100644 --- a/create.py +++ b/create.py @@ -11,7 +11,7 @@ import fifth import knaverats import mazerats -#import silent_titans +import silent_titans import troika import trophy From 891b82166f1967e777628499e1a9e2c263cd4390 Mon Sep 17 00:00:00 2001 From: pakoito Date: Mon, 30 Sep 2019 02:29:06 +0100 Subject: [PATCH 9/9] Typo --- templates/knaverats.html | 2 +- templates/mazerats.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/knaverats.html b/templates/knaverats.html index 5befede..e8fccd6 100644 --- a/templates/knaverats.html +++ b/templates/knaverats.html @@ -29,7 +29,7 @@

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothin

- You were once a {{ c.background}}. Now you are a Knave Rat, a grubby adventurer! + You were once a {{ c.background }}. Now you are a Knave Rat, a grubby adventurer! {% if c.spell %}You can cast {{ c.spell }}, a wondrous spell{% endif %} {% if c.skill %}You possess the skills of a {{ c.skill }}.{% endif %}

diff --git a/templates/mazerats.html b/templates/mazerats.html index ad2f16c..6545f62 100644 --- a/templates/mazerats.html +++ b/templates/mazerats.html @@ -24,7 +24,7 @@

{{ c.appearance }}, {{ c.physical_detail }}, {{ c.clothin

- You were once a {{ c.background}}. Now you are a Maze Rat, a grubby adventurer! + You were once a {{ c.background }}. Now you are a Maze Rat, a grubby adventurer! {% if c.spell %}You can cast {{ c.spell }}, a wondrous spell{% endif %} {% if c.skill %}You possess the skills of a {{ c.skill }}.{% endif %}