Compare commits
29 Commits
3de42211c2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1829184cd | ||
|
|
fbe69be2ac | ||
|
|
2173733463 | ||
|
|
78ba21227a | ||
|
|
2561b403bd | ||
|
|
ef5d3474a7 | ||
|
|
0149035c72 | ||
|
|
1ddeec0b6d | ||
|
|
72a33f22de | ||
|
|
8bcefa02a6 | ||
|
|
fbecd90c8d | ||
|
|
972e094466 | ||
|
|
f089cf0d2c | ||
|
|
0171a05c19 | ||
|
|
15342552f1 | ||
|
|
e6d19b1f69 | ||
|
|
dad1a4c588 | ||
|
|
0ca25f9f91 | ||
|
|
8a7f605a5f | ||
|
|
b7ddafddd4 | ||
|
|
f4d8511ab6 | ||
|
|
390f062e0a | ||
|
|
206a229895 | ||
|
|
feacb20551 | ||
|
|
c966c4f974 | ||
|
|
6b03659ec9 | ||
|
|
9323e7c5d5 | ||
|
|
74881a607c | ||
|
|
215361e4d9 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
__pycache__/
|
||||
flask_session/
|
||||
48
README.md
48
README.md
@@ -1,3 +1,47 @@
|
||||
# ose-character-gen
|
||||
# OSE Character Generator
|
||||
|
||||
This tool uses the Old School Essentials (OSE) ruleset for tabletop RPGs in order to randomly generate a character.
|
||||
|
||||
## Overview
|
||||
|
||||
### What is Dungeons & Dragons?
|
||||
Dungeons & Dragons, also known as D&D, is a pen and paper table top game. One player is the story teller, the "dungeon master" and the others interact with the story using their "adventurers." An adventurer is a playable character, who has a mixture of abilities and powers, the details of which are often kept on piece of paper called a "character sheet."
|
||||
|
||||
### How does the game work?
|
||||
Most settings are fantasy based, like game of thrones or lord of the rings. Players often pick up a quest in one of these fantasy worlds, guided by the "Dungeon Master" who narrates the world. The players will make decisions, take actions, and ultimate help write the story as it unfolds.</p>
|
||||
|
||||
### Why would I need a character generator?
|
||||
It takes time and effort to create a new character; there are many rules, phrases, and terminology you'll need to know to create a character. Most rule books provide an easy to follow guide on making player characters or "adventurers." However, if you are short on time, a generator such as this will get you in the game right away!
|
||||
|
||||
### About This App
|
||||
This app accomplishes a few things, but mainly it creates a character for you.
|
||||
|
||||
- It follows the rules precisely from the books to generate a legal character
|
||||
- All stats are randomly rolled, but a class is chosen per your best stats
|
||||
- Character equipment and spells are also randomly selected, within that character-class rules
|
||||
- You can generate a whole party of characters
|
||||
- Within that party, you can adjust their level, and the number of characters
|
||||
- You can create a single character to edit
|
||||
|
||||
|
||||
## Instructions
|
||||
|
||||
### Install
|
||||
This is a flask web application, to install:
|
||||
|
||||
1. Clone this repo
|
||||
2. Install requirements, `python3 pip install -r requirements.txt`
|
||||
3. To run the webserver execute `flask run` while in your cloned repository's top folder.
|
||||
|
||||
### To Use
|
||||
There are two main generators, one for a single character and another for a group, or a "party" of characters.
|
||||
1. Character Generator
|
||||
- You may select a class and a level
|
||||
- The re-roll button always starts the character back to level 1
|
||||
2. Party Generator
|
||||
- You may also select the level for this group
|
||||
- You may select the count, between 2 and 5.
|
||||
- If you want to have the party persist across refreshes, level and size changes, keep "cached" on
|
||||
- Otherwise, "not cached" will always generate a new party
|
||||
- If you find a single character interesting, you may "select" them and it will load **this** character in the "charater generator."
|
||||
|
||||
Web app for old school D&D character generation
|
||||
479
adventurers.py
Executable file
479
adventurers.py
Executable file
@@ -0,0 +1,479 @@
|
||||
#!/usr/bin/python3
|
||||
import json
|
||||
import random
|
||||
from equipment import *
|
||||
from spells import *
|
||||
|
||||
# functions
|
||||
def roll_dice(count, sides):
|
||||
return sum(random.randint(1,sides) for _ in range(count))
|
||||
|
||||
# Player Character Classes
|
||||
class Adventurer:
|
||||
ability_score_modifiers = {
|
||||
'strength' : { 3 : { 'melee' : -3, 'open doors': 1 }, 5 : { 'melee' : -2, 'open doors': 1 }, 8 : { 'melee' : -1, 'open doors': 1 },
|
||||
12 : { 'melee' : 0, 'open doors': 2 }, 15 : { 'melee' : 1, 'open doors': 3 }, 17 : { 'melee' : 2, 'open doors': 4 },
|
||||
18 : { 'melee' : 3, 'open doors': 5 } },
|
||||
'intelligence' : { 3 : { 'languages' : 'native, broken speech', 'literacy': 'illiterate' }, 5 : { 'languages' : 'native', 'literacy': 'illiterate' },
|
||||
8 : { 'languages' : 'native', 'literacy': 'basic' }, 12 : { 'languages' :'native', 'literacy': 'literate' },
|
||||
15 : { 'languages' : 'native +1 additional', 'literacy': 'literate'}, 17 : { 'languages' : 'native +2 additional', 'literacy': 'literate' },
|
||||
18 : { 'languages' : 'native +3 additional', 'literacy': 'literate' } },
|
||||
'wisdom' : { 3 : { 'magic saves' : -3 }, 5 : { 'magic saves' : -2 }, 8 : { 'magic saves' : -1 }, 12 : { 'magic saves' : 0,},
|
||||
15 : { 'magic saves' : 1 }, 17 : { 'magic saves' : 2 }, 18 : { 'magic saves' : 3 } },
|
||||
'dexterity' : { 3 : { 'AC' : -3, 'missile': -3, 'initiative' : -2 }, 5 : { 'AC' : -2, 'missile': -2, 'initiative' : -1},
|
||||
8 : { 'AC' : -1, 'missile': -1, 'initiative' : -1 }, 12 : { 'AC' : 0, 'missile': 0, 'initiative' : 0 },
|
||||
15 : { 'AC' : 1, 'missile': 1, 'initiative' : 1 }, 17 : { 'AC' : 2, 'missile': 2, 'initiative' : 1 },
|
||||
18 : { 'AC' : 3, 'missile': 3, 'initiative' : 2 } },
|
||||
'constitution' : { 3 : { 'hit points' : -3 }, 5 : { 'hit points' : -2 }, 8 : { 'hit points' : -1 }, 12 : { 'hit points' : 0 },
|
||||
15 : { 'hit points' : 1 }, 17 : { 'hit points' : 2 }, 18 : { 'hit points' : 3 } },
|
||||
'charisma' : { 3 : { 'npc reactions' : -2, 'max retainers': 1, 'retainer loyalty' : 4 }, 5 : { 'npc reactions' : -2, 'max retainers': 2, 'retainer loyalty' : 5 },
|
||||
8 : { 'npc reactions' : -1, 'max retainers': 3, 'retainer loyalty' : 6 }, 12 : { 'npc reactions' : 0, 'max retainers': 4, 'retainer loyalty' : 7 },
|
||||
15 : { 'npc reactions' : 1, 'max retainers': 5, 'retainer loyalty' : 8 }, 17 : { 'npc reactions' : 1, 'max retainers': 6, 'retainer loyalty' : 9 },
|
||||
18 : { 'npc reactions' : 2, 'max retainers': 7, 'retainer loyalty' : 10 } }
|
||||
}
|
||||
|
||||
def __init__(self, c_id: str, level=1, attributes={}) -> None:
|
||||
# using a get() method to pull an attribute else use a default value, https://python-academy.org/en/handbook/get
|
||||
self.identifier = c_id
|
||||
self.player_class = None
|
||||
self.level = level
|
||||
self.strength = attributes.get('strength', roll_dice(3,6))
|
||||
self.intelligence = attributes.get('intelligence', roll_dice(3,6))
|
||||
self.wisdom = attributes.get('wisdom', roll_dice(3,6))
|
||||
self.dexterity = attributes.get('dexterity', roll_dice(3,6))
|
||||
self.constitution = attributes.get('constitution', roll_dice(3,6))
|
||||
self.charisma = attributes.get('charisma', roll_dice(3,6))
|
||||
self.hp_rolls = []
|
||||
self.hp = 1
|
||||
self.atk = 0
|
||||
self.gold= roll_dice(3,6)
|
||||
self.torches = roll_dice(1,6)
|
||||
self.rations = roll_dice(1,6)
|
||||
self.equipment = self.set_equipment()
|
||||
# all armor, individual classes may have overrides
|
||||
self.possible_armor = list(armor.keys())
|
||||
# all weapons, individual classes may have overrides
|
||||
self.possible_weapons = weapons
|
||||
self.possible_melee_weapons = list(filter(lambda d: 'melee' in d['traits'], weapons))
|
||||
self.possible_missile_weapons = list(filter(lambda d: 'missile' in d['traits'], weapons))
|
||||
# each character should get 1 melee and 1 random (missle or melee)
|
||||
self.weapons = [ random.choice(self.possible_melee_weapons), random.choice(self.possible_weapons) ]
|
||||
self.armor = random.choice(self.possible_armor)
|
||||
# special abilities
|
||||
self.spells = None
|
||||
self.spell_book = None
|
||||
self.thief_skills = None
|
||||
self.turn_undead = None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.player_class}"
|
||||
|
||||
def get_subclass_dict() -> dict:
|
||||
subclasses = {}
|
||||
for subclass in Adventurer.__subclasses__():
|
||||
subclasses[subclass.adv_class] = subclass
|
||||
return subclasses
|
||||
|
||||
def get_json(self) -> dict:
|
||||
char_dict = self.__dict__
|
||||
char_json = json.dumps(char_dict)
|
||||
return char_dict
|
||||
|
||||
def vertical_sheet(self) -> list:
|
||||
sheet = []
|
||||
sheet.append('{0: <28}'.format(f"| {self.player_class.title()} - Level {self.level}"))
|
||||
for key, val in self.get_attributes().items():
|
||||
key_string = "| " + '{0:16}'.format(f"{key}").capitalize() + f" {val}"
|
||||
sheet += ['{0: <28}'.format(key_string)]
|
||||
sheet.append('| ------------------------- ')
|
||||
sheet.append('{0: <28}'.format(f"| HP: {self.hp} AC: {self.ac}"))
|
||||
sheet.append('{0: <28}'.format(f"| Torches: {self.torches}"))
|
||||
sheet.append('{0: <28}'.format(f"| Rations: {self.rations}"))
|
||||
sheet.append('{0: <28}'.format(f"| Armor: {self.armor}"))
|
||||
sheet.append('{0: <28}'.format(f"| Weapons:"))
|
||||
sheet.append('{0: <28}'.format(f"| {self.weapons[0]['name'].title()}"))
|
||||
sheet.append('{0: <28}'.format(f"| {self.weapons[1]['name'].title()}"))
|
||||
sheet.append('{0: <28}'.format(f"| Equipment:"))
|
||||
for item in self.equipment:
|
||||
item_string = "| " + '{0:22}'.format(f"{item}").title()
|
||||
sheet += ['{0: <28}'.format(item_string)]
|
||||
sheet.append('{0: <28}'.format(f"| "))
|
||||
sheet.append('{0: <28}'.format(f"| Gold: {self.gold}"))
|
||||
sheet.append('| ------------------------- ')
|
||||
for key, val in self.progression[self.level]['saves'].items():
|
||||
key_string = "| " + '{0:22}'.format(f"{key}").title() + f" {val}"
|
||||
sheet += ['{0: <28}'.format(key_string)]
|
||||
sheet.append('| ------------------------- ')
|
||||
#if self.spells:
|
||||
# sheet.append('{0: <28}'.format("| Spellbook: "))
|
||||
# sheet += ['{0: <28}'.format(f"| {spell.title()}") for spell in self.spell_book ]
|
||||
# append a | to each string, after the formatted whitespace
|
||||
sheet = [ line + "|" for line in sheet ]
|
||||
return sheet
|
||||
|
||||
def full_sheet(self) -> list:
|
||||
sheet = { 1: [], 2 : [] }
|
||||
sheet[1].append('{0: <60}'.format(f"| Character Name:"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Player Name :"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Class: {self.player_class.title()} Alignment:"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Title: Level {self.level}"))
|
||||
sheet[1].append('| ------------------------------------------ ')
|
||||
sheet[1].append('{0: <60}'.format(f"| ABILITY SCORES\t\t\tSAVING THROWS"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Strength:\t{self.strength} Death: {self.ac}"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Intelligence:{self.intelligence} Wands: {self.ac}"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Wisdom:\t {self.wisdom} Para: {self.ac}"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Dexterity:\t{self.dexterity} Breath: {self.ac}"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Constitution: {self.constitution} Spells: {self.ac}"))
|
||||
sheet[1].append('{0: <60}'.format(f"| Charisma:\t{self.charisma} SpellMod: {self.ac}"))
|
||||
return sheet
|
||||
|
||||
def get_attributes(self) -> dict:
|
||||
attribute_list = ['strength', 'intelligence', 'wisdom', 'dexterity', 'constitution', 'charisma']
|
||||
return {k: self.__dict__[k] for k in attribute_list}
|
||||
|
||||
def get_best_prime_attribute(self) -> str:
|
||||
attribute_list = [ 'strength', 'intelligence', 'wisdom', 'dexterity' ]
|
||||
prime_attributes = {k: self.__dict__[k] for k in attribute_list}
|
||||
# return highest, found this at https://stackoverflow.com/a/280156
|
||||
return max(prime_attributes, key=prime_attributes.get)
|
||||
|
||||
def set_equipment(self) -> list:
|
||||
equipment = [ 'backpack', 'tinderbox', 'waterskin' ]
|
||||
for i in range(2):
|
||||
item = ""
|
||||
while item not in equipment:
|
||||
item = random.choice(adventuring_gear)
|
||||
if item not in equipment:
|
||||
equipment.append(item)
|
||||
return equipment
|
||||
|
||||
def select_spells(self, spell_list: dict) -> list:
|
||||
spell_book = []
|
||||
for spell_level, count in self.spells.items():
|
||||
if count != "-":
|
||||
for i in range(count):
|
||||
new_spell = ""
|
||||
while new_spell not in spell_book:
|
||||
new_spell = random.choice(spell_list[spell_level])
|
||||
if new_spell not in spell_book:
|
||||
spell_book.append(new_spell)
|
||||
return spell_book
|
||||
|
||||
def roll_all_hit_points(self) -> list:
|
||||
hp_list = []
|
||||
prev_hit_dice = 0
|
||||
for i in range(self.__class__.max_level):
|
||||
new_hit_dice = self.__class__.progression[i]['hit-dice']
|
||||
hp_mod = self.__class__.progression[i]['hp-mod']
|
||||
hit_dice = new_hit_dice - prev_hit_dice
|
||||
prev_hit_dice = new_hit_dice
|
||||
# check for constitution bonus to hp
|
||||
if hp_mod == 0 and self.constitution >= 13: hp_mod = 1
|
||||
if hp_mod == 0 and self.constitution >= 16: hp_mod = 2
|
||||
if hp_mod == 0 and self.constitution >= 18: hp_mod = 3
|
||||
hp_roll = roll_dice(hit_dice,self.__class__.hit_die) + hp_mod
|
||||
hp_list.append(hp_roll)
|
||||
return hp_list
|
||||
|
||||
def get_class_progression_for_level(self) -> list:
|
||||
return list(filter(lambda d: d['level'] == self.level, self.__class__.progression))[0]
|
||||
|
||||
def set_level(self, new_level: int) -> None:
|
||||
self.level = new_level
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
if self.player_class in [ "magic user", "elf" ]:
|
||||
self.spells = MagicUser.spells[self.level]
|
||||
self.spell_book = self.select_spells(magic_user_spells)
|
||||
if self.player_class == "cleric":
|
||||
self.spells = Cleric.spells[self.level]
|
||||
self.spell_book = self.select_spells(cleric_spells)
|
||||
self.turn_undead = Cleric.turn_undead[self.level]
|
||||
|
||||
def set_attack_bonus(self) -> int:
|
||||
prog = self.get_class_progression_for_level()
|
||||
thac0 = prog['thac0']
|
||||
atk = 19 - thac0
|
||||
return atk
|
||||
|
||||
class Fighter(Adventurer):
|
||||
adv_class = "fighter"
|
||||
requirements = None
|
||||
prime_requisite = "strength"
|
||||
hit_die = 8
|
||||
max_level = 14
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 14, "breath attack" : 15, "spells / rods / staves" : 16 }},
|
||||
{ "level" : 2, "xp" : 2000, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 14, "breath attack" : 15, "spells / rods / staves" : 16 }},
|
||||
{ "level" : 3, "xp" : 4000, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 14, "breath attack" : 15, "spells / rods / staves" : 16 }},
|
||||
{ "level" : 4, "xp" : 8000, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 12, "breath attack" : 13, "spells / rods / staves" : 14 }},
|
||||
{ "level" : 5, "xp" : 16000, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 12, "breath attack" : 13, "spells / rods / staves" : 14 }},
|
||||
{ "level" : 6, "xp" : 32000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 12, "breath attack" : 13, "spells / rods / staves" : 14 }},
|
||||
{ "level" : 7, "xp" : 64000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 10, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 8, "xp" : 120000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 10, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 9, "xp" : 240000, "hit-dice" : 9, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 10, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 10, "xp" : 360000, "hit-dice" : 9, "hp-mod" : 2, "thac0" : 12, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 11, "xp" : 480000, "hit-dice" : 9, "hp-mod" : 4, "thac0" : 12, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 12, "xp" : 600000, "hit-dice" : 9, "hp-mod" : 6, "thac0" : 12, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 13, "xp" : 720000, "hit-dice" : 9, "hp-mod" : 8, "thac0" : 10, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 5, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 14, "xp" : 840000, "hit-dice" : 9, "hp-mod" : 10, "thac0" : 10, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 5, "spells / rods / staves" : 8 }}
|
||||
]
|
||||
def __init__(self, c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = Fighter.adv_class
|
||||
self.progression = Fighter.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.ac = armor[self.armor]
|
||||
self.atk = self.set_attack_bonus()
|
||||
|
||||
class MagicUser(Adventurer):
|
||||
adv_class = "magic user"
|
||||
requirements = None
|
||||
prime_requisite = "intelligence"
|
||||
hit_die = 4
|
||||
max_level = 14
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 2, "xp" : 2500, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 3, "xp" : 5000, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 4, "xp" : 10000, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 5, "xp" : 20000, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 6, "xp" : 40000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 11, "breath attack" : 11, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 7, "xp" : 80000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 11, "breath attack" : 11, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 8, "xp" : 150000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 11, "breath attack" : 11, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 9, "xp" : 300000, "hit-dice" : 9, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 11, "breath attack" : 11, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 10, "xp" : 450000, "hit-dice" : 9, "hp-mod" : 1, "thac0" : 17, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 11, "breath attack" : 11, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 11, "xp" : 600000, "hit-dice" : 9, "hp-mod" : 2, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 12, "xp" : 750000, "hit-dice" : 9, "hp-mod" : 3, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 13, "xp" : 900000, "hit-dice" : 9, "hp-mod" : 4, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 14, "xp" : 1050000, "hit-dice" : 9, "hp-mod" : 5, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 8 }}
|
||||
]
|
||||
spells = {
|
||||
1: { 1: 1, 2: '-', 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
2: { 1: 2, 2: '-', 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
3: { 1: 2, 2: 1, 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
4: { 1: 2, 2: 2, 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
5: { 1: 2, 2: 2, 3: 1, 4: '-', 5: '-', 6: '-'},
|
||||
6: { 1: 2, 2: 2, 3: 2, 4: '-', 5: '-', 6: '-'},
|
||||
7: { 1: 3, 2: 2, 3: 2, 4: 1, 5: '-', 6: '-'},
|
||||
8: { 1: 3, 2: 3, 3: 2, 4: 2, 5: '-', 6: '-'},
|
||||
9: { 1: 3, 2: 3, 3: 3, 4: 2, 5: 1, 6: '-'},
|
||||
10: { 1: 3, 2: 3, 3: 3, 4: 3, 5: 2, 6: '-'},
|
||||
11: { 1: 4, 2: 3, 3: 3, 4: 3, 5: 2, 6: 1 },
|
||||
12: { 1: 4, 2: 4, 3: 3, 4: 3, 5: 2, 6: 1 },
|
||||
13: { 1: 4, 2: 4, 3: 4, 4: 3, 5: 3, 6: 3 },
|
||||
14: { 1: 4, 2: 4, 3: 4, 4: 4, 5: 3, 6: 3 }
|
||||
}
|
||||
def __init__(self,c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = MagicUser.adv_class
|
||||
self.progression = MagicUser.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.armor = "None"
|
||||
self.ac = armor[self.armor]
|
||||
self.weapons = [ list(filter(lambda d: 'silver dagger' in d['name'],weapons))[0], { "name" : "" } ]
|
||||
self.spells = MagicUser.spells[self.level]
|
||||
self.spell_book = self.select_spells(magic_user_spells)
|
||||
self.atk = self.set_attack_bonus()
|
||||
|
||||
class Cleric(Adventurer):
|
||||
adv_class = "cleric"
|
||||
requirements = None
|
||||
prime_requisite = "wisdom"
|
||||
hit_die = 6
|
||||
max_level = 14
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 14, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 2, "xp" : 1500, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 14, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 3, "xp" : 3000, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 14, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 4, "xp" : 6000, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 11, "wands" : 12, "paralysis / petrify" : 14, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 5, "xp" : 12000, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 9, "wands" : 10, "paralysis / petrify" : 12, "breath attack" : 14, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 6, "xp" : 25000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 9, "wands" : 10, "paralysis / petrify" : 12, "breath attack" : 14, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 7, "xp" : 50000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 9, "wands" : 10, "paralysis / petrify" : 12, "breath attack" : 14, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 8, "xp" : 100000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 9, "wands" : 10, "paralysis / petrify" : 12, "breath attack" : 14, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 9, "xp" : 200000, "hit-dice" : 9, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 9, "breath attack" : 14, "spells / rods / staves" : 9 }},
|
||||
{ "level" : 10, "xp" : 300000, "hit-dice" : 9, "hp-mod" : 1, "thac0" : 14, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 9 }},
|
||||
{ "level" : 11, "xp" : 400000, "hit-dice" : 9, "hp-mod" : 2, "thac0" : 14, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 9 }},
|
||||
{ "level" : 12, "xp" : 500000, "hit-dice" : 9, "hp-mod" : 3, "thac0" : 14, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 9 }},
|
||||
{ "level" : 13, "xp" : 600000, "hit-dice" : 9, "hp-mod" : 4, "thac0" : 12, "saves" : { "death / poison" : 3, "wands" : 5, "paralysis / petrify" : 7, "breath attack" : 8, "spells / rods / staves" : 7 }},
|
||||
{ "level" : 14, "xp" : 700000, "hit-dice" : 9, "hp-mod" : 5, "thac0" : 12, "saves" : { "death / poison" : 3, "wands" : 5, "paralysis / petrify" : 7, "breath attack" : 8, "spells / rods / staves" : 7 }}
|
||||
]
|
||||
spells = {
|
||||
1: { 1: '-', 2: '-', 3: '-', 4: '-', 5: '-'},
|
||||
2: { 1: 1, 2: '-', 3: '-', 4: '-', 5: '-'},
|
||||
3: { 1: 2, 2: '-', 3: '-', 4: '-', 5: '-'},
|
||||
4: { 1: 2, 2: 1, 3: '-', 4: '-', 5: '-'},
|
||||
5: { 1: 2, 2: 2, 3: '-', 4: '-', 5: '-'},
|
||||
6: { 1: 2, 2: 2, 3: 1, 4: 1, 5: '-'},
|
||||
7: { 1: 2, 2: 2, 3: 2, 4: 1, 5: 1 },
|
||||
8: { 1: 3, 2: 3, 3: 2, 4: 2, 5: 1 },
|
||||
9: { 1: 3, 2: 3, 3: 3, 4: 2, 5: 2 },
|
||||
10: { 1: 4, 2: 4, 3: 3, 4: 3, 5: 2 },
|
||||
11: { 1: 4, 2: 4, 3: 4, 4: 3, 5: 3 },
|
||||
12: { 1: 5, 2: 5, 3: 4, 4: 4, 5: 3 },
|
||||
13: { 1: 5, 2: 5, 3: 4, 4: 4, 5: 4 },
|
||||
14: { 1: 6, 2: 5, 3: 5, 4: 5, 5: 4 }
|
||||
}
|
||||
turn_undead = {
|
||||
1: { '1': '7', '2': '9', '2*': '11', 3: '-', 4: '-', 5: '-', '6' : '-', '7-9' : '-' },
|
||||
2: { '1': 'T', '2': '7', '2*': '9', 3: '11', 4: '-', 5: '-', '6' : '-', '7-9' : '-' },
|
||||
3: { '1': 'T', '2': 'T', '2*': '7', 3: '9', 4: '11', 5: '-', '6' : '-', '7-9' : '-' },
|
||||
4: { '1': 'D', '2': 'T', '2*': 'T', 3: '7', 4: '9', 5: '11', '6' : '-', '7-9' : '-' },
|
||||
5: { '1': 'D', '2': 'D', '2*': 'T', 3: 'T', 4: '7', 5: '9', '6' : '11', '7-9' : '-' },
|
||||
6: { '1': 'D', '2': 'D', '2*': 'D', 3: 'T', 4: 'T', 5: '7', '6' : '9', '7-9' : '11'},
|
||||
7: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'T', 5: 'T', '6' : '7', '7-9' : '9' },
|
||||
8: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'D', 5: 'T', '6' : 'T', '7-9' : '7' },
|
||||
9: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'D', 5: 'D', '6' : 'D', '7-9' : 'T' },
|
||||
11: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'D', 5: 'D', '6' : 'D', '7-9' : 'T' },
|
||||
12: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'D', 5: 'D', '6' : 'D', '7-9' : 'D' },
|
||||
13: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'D', 5: 'D', '6' : 'D', '7-9' : 'D' },
|
||||
14: { '1': 'D', '2': 'D', '2*': 'D', 3: 'D', 4: 'D', 5: 'D', '6' : 'D', '7-9' : 'D' }
|
||||
}
|
||||
|
||||
def __init__(self,c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = Cleric.adv_class
|
||||
self.progression = Cleric.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.armor = random.choice(list(armor.keys()))
|
||||
self.ac = armor[self.armor]
|
||||
# clerics can only wield blunt weapons
|
||||
self.possible_melee_weapons = list(filter(lambda d: 'blunt' in d['traits'] and 'melee' in d['traits'], weapons))
|
||||
self.possible_weapons = list(filter(lambda d: 'blunt' in d['traits'], weapons))
|
||||
self.weapons = [ random.choice(self.possible_melee_weapons), random.choice(self.possible_weapons) ]
|
||||
self.spells = Cleric.spells[self.level]
|
||||
self.spell_book = self.select_spells(cleric_spells)
|
||||
self.atk = self.set_attack_bonus()
|
||||
self.turn_undead = Cleric.turn_undead[self.level]
|
||||
|
||||
class Thief(Adventurer):
|
||||
adv_class = "thief"
|
||||
prime_requisite = "dexterity"
|
||||
requirements = None
|
||||
hit_die = 4
|
||||
max_level = 14
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 2, "xp" : 1200, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 3, "xp" : 2400, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 4, "xp" : 4800, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 13, "wands" : 14, "paralysis / petrify" : 13, "breath attack" : 16, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 5, "xp" : 9600, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 11, "breath attack" : 14, "spells / rods / staves" : 13 }},
|
||||
{ "level" : 6, "xp" : 20000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 11, "breath attack" : 14, "spells / rods / staves" : 13 }},
|
||||
{ "level" : 7, "xp" : 40000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 11, "breath attack" : 14, "spells / rods / staves" : 13 }},
|
||||
{ "level" : 8, "xp" : 80000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 11, "breath attack" : 14, "spells / rods / staves" : 13 }},
|
||||
{ "level" : 9, "xp" : 160000, "hit-dice" : 9, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 10, "xp" : 280000, "hit-dice" : 9, "hp-mod" : 2, "thac0" : 14, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 11, "xp" : 400000, "hit-dice" : 9, "hp-mod" : 4, "thac0" : 14, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 12, "xp" : 520000, "hit-dice" : 9, "hp-mod" : 6, "thac0" : 14, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 9, "breath attack" : 11, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 13, "xp" : 640000, "hit-dice" : 9, "hp-mod" : 8, "thac0" : 12, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 7, "breath attack" : 10, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 14, "xp" : 760000, "hit-dice" : 9, "hp-mod" : 10, "thac0" : 12, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 7, "breath attack" : 10, "spells / rods / staves" : 8 }}
|
||||
]
|
||||
def __init__(self,c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = Thief.adv_class
|
||||
self.progression = Thief.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.armor = random.choice(list(armor.keys()))
|
||||
self.ac = armor[self.armor]
|
||||
self.atk = self.set_attack_bonus()
|
||||
|
||||
class Dwarf(Adventurer):
|
||||
adv_class = "dwarf"
|
||||
requirements = {'constitution' : 9 }
|
||||
prime_requisite = "strength"
|
||||
hit_die = 8
|
||||
max_level = 12
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 2, "xp" : 2200, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 3, "xp" : 4400, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 4, "xp" : 8800, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 5, "xp" : 17000, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 6, "xp" : 35000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 7, "xp" : 70000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 7, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 8, "xp" : 140000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 7, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 9, "xp" : 270000, "hit-dice" : 9, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 7, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 10, "xp" : 400000, "hit-dice" : 9, "hp-mod" : 3, "thac0" : 12, "saves" : { "death / poison" : 2, "wands" : 3, "paralysis / petrify" : 4, "breath attack" : 4, "spells / rods / staves" : 6 }},
|
||||
{ "level" : 11, "xp" : 530000, "hit-dice" : 9, "hp-mod" : 6, "thac0" : 12, "saves" : { "death / poison" : 2, "wands" : 3, "paralysis / petrify" : 4, "breath attack" : 4, "spells / rods / staves" : 6 }},
|
||||
{ "level" : 12, "xp" : 660000, "hit-dice" : 9, "hp-mod" : 9, "thac0" : 12, "saves" : { "death / poison" : 2, "wands" : 3, "paralysis / petrify" : 4, "breath attack" : 4, "spells / rods / staves" : 6 }}
|
||||
]
|
||||
def __init__(self,c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = Dwarf.adv_class
|
||||
self.progression = Dwarf.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.armor = random.choice(list(armor.keys()))
|
||||
self.ac = armor[self.armor]
|
||||
self.atk = self.set_attack_bonus()
|
||||
|
||||
class Elf(Adventurer):
|
||||
adv_class = "elf"
|
||||
requirements = {'intelligence' : 9 }
|
||||
prime_requisite = "intellgence"
|
||||
hit_die = 6
|
||||
max_level = 10
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 13, "breath attack" : 15, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 2, "xp" : 4000, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 13, "breath attack" : 15, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 3, "xp" : 8000, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 12, "wands" : 13, "paralysis / petrify" : 13, "breath attack" : 15, "spells / rods / staves" : 15 }},
|
||||
{ "level" : 4, "xp" : 16000, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 11, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 5, "xp" : 32000, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 11, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 6, "xp" : 64000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 10, "wands" : 11, "paralysis / petrify" : 11, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 7, "xp" : 120000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 9, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 8, "xp" : 250000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 9, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 9, "xp" : 400000, "hit-dice" : 9, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 9, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 10, "xp" : 600000, "hit-dice" : 9, "hp-mod" : 2, "thac0" : 12, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 8, "spells / rods / staves" : 8 }}
|
||||
]
|
||||
spells = {
|
||||
1: { 1: 1, 2: '-', 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
2: { 1: 2, 2: '-', 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
3: { 1: 2, 2: 1, 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
4: { 1: 2, 2: 2, 3: '-', 4: '-', 5: '-', 6: '-'},
|
||||
5: { 1: 2, 2: 2, 3: 1, 4: '-', 5: '-', 6: '-'},
|
||||
6: { 1: 2, 2: 2, 3: 2, 4: '-', 5: '-', 6: '-'},
|
||||
7: { 1: 3, 2: 2, 3: 2, 4: 1, 5: '-', 6: '-'},
|
||||
8: { 1: 3, 2: 3, 3: 2, 4: 2, 5: '-', 6: '-'},
|
||||
9: { 1: 3, 2: 3, 3: 3, 4: 2, 5: 1, 6: '-'},
|
||||
10: { 1: 3, 2: 3, 3: 3, 4: 3, 5: 2, 6: '-'}
|
||||
}
|
||||
def __init__(self,c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = Elf.adv_class
|
||||
self.progression = Elf.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.armor = random.choice(list(armor.keys()))
|
||||
self.ac = armor[self.armor]
|
||||
self.spells = Elf.spells[self.level]
|
||||
self.spell_book = self.select_spells(magic_user_spells)
|
||||
self.atk = self.set_attack_bonus()
|
||||
|
||||
class Halfling(Adventurer):
|
||||
adv_class = "halfling"
|
||||
requirements = {'constitution' : 9, 'dexterity' : 9 }
|
||||
prime_requisite = "dexterity"
|
||||
hit_die = 6
|
||||
max_level = 8
|
||||
progression = [
|
||||
{ "level" : 1, "xp" : 0, "hit-dice" : 1, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 2, "xp" : 2000, "hit-dice" : 2, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 3, "xp" : 4000, "hit-dice" : 3, "hp-mod" : 0, "thac0" : 19, "saves" : { "death / poison" : 8, "wands" : 9, "paralysis / petrify" : 10, "breath attack" : 13, "spells / rods / staves" : 12 }},
|
||||
{ "level" : 4, "xp" : 8000, "hit-dice" : 4, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 5, "xp" : 16000, "hit-dice" : 5, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 6, "xp" : 32000, "hit-dice" : 6, "hp-mod" : 0, "thac0" : 17, "saves" : { "death / poison" : 6, "wands" : 7, "paralysis / petrify" : 8, "breath attack" : 10, "spells / rods / staves" : 10 }},
|
||||
{ "level" : 7, "xp" : 64000, "hit-dice" : 7, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 7, "spells / rods / staves" : 8 }},
|
||||
{ "level" : 8, "xp" : 120000, "hit-dice" : 8, "hp-mod" : 0, "thac0" : 14, "saves" : { "death / poison" : 4, "wands" : 5, "paralysis / petrify" : 6, "breath attack" : 7, "spells / rods / staves" : 8 }},
|
||||
]
|
||||
def __init__(self,c_id, level, attributes={}) -> None:
|
||||
Adventurer.__init__(self, c_id, level, attributes)
|
||||
self.player_class = Halfling.adv_class
|
||||
self.progression = Halfling.progression
|
||||
self.hp_rolls = self.roll_all_hit_points()
|
||||
self.hp = sum(self.hp_rolls[:self.level])
|
||||
self.armor = random.choice(list(armor.keys()))
|
||||
self.ac = armor[self.armor]
|
||||
self.atk = self.set_attack_bonus()
|
||||
118
app.py
Normal file
118
app.py
Normal file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/python3
|
||||
import zlib
|
||||
from main import *
|
||||
from adventurers import *
|
||||
from flask import Flask, render_template, redirect, request, make_response
|
||||
from flask_session import Session
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# useful functions
|
||||
def compress_character(adventurer):
|
||||
cookie_string = adventurer.identifier
|
||||
adventurer_json = json.dumps(adventurer.get_json())
|
||||
compressed_data = zlib.compress(adventurer_json.encode())
|
||||
cookie_data = base64.urlsafe_b64encode(compressed_data).decode()
|
||||
return cookie_string, cookie_data
|
||||
|
||||
def return_character_from_cookie(cookie):
|
||||
cookie_decompressed = base64.urlsafe_b64decode(cookie)
|
||||
adventurer_dict = json.loads(zlib.decompress(cookie_decompressed).decode())
|
||||
adventurer = AdventurerGen.create_from_dict(adventurer_dict)
|
||||
return adventurer
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
tab = request.args.get("tab", default="what", type=str)
|
||||
# control argument inputs to tabs available on index.html
|
||||
if tab not in [ "what", "how", "why", "about" ]:
|
||||
tab = "what"
|
||||
return render_template("index.html", tab=tab)
|
||||
|
||||
@app.route('/party', methods = ["GET"])
|
||||
def party():
|
||||
# default values, modified if cookies exist
|
||||
stored_adv_party = []
|
||||
stored_count = None
|
||||
stored_level = None
|
||||
if request.method == "GET":
|
||||
count = request.args.get("count", default=4, type=int)
|
||||
level = request.args.get("level", default=1, type=int)
|
||||
cache = request.args.get("cache", default='true', type=str)
|
||||
# control inputs to prevent abuse
|
||||
if cache not in [ "true", "false" ]:
|
||||
cache = 'true'
|
||||
if level < 1 or level > 5:
|
||||
level = 1
|
||||
if count < 2 or count > 5:
|
||||
count = 4
|
||||
# generate an adventuring party
|
||||
adv_party = PartyGen.get_new_party(count, level)
|
||||
# check for cookies present
|
||||
if request.cookies:
|
||||
stored_count = len(request.cookies)
|
||||
for c in request.cookies:
|
||||
if c.startswith('adv'):
|
||||
new_char = return_character_from_cookie(request.cookies[c])
|
||||
stored_level = new_char.level
|
||||
stored_adv_party.append(new_char)
|
||||
# if request arguments match the adventure party count & level stored in the cookies, just use the cookies
|
||||
if cache == 'true':
|
||||
if level != stored_level:
|
||||
for adv in stored_adv_party:
|
||||
adv.set_level(level)
|
||||
if count == stored_count:
|
||||
adv_party.set_party(stored_adv_party, count, level)
|
||||
if count > stored_count:
|
||||
extension = count - stored_count
|
||||
more_party_members = PartyGen.get_new_party(extension,level).adventurers
|
||||
extended_party = stored_adv_party + more_party_members
|
||||
adv_party.set_party(extended_party, count, level)
|
||||
if count < stored_count:
|
||||
reduction = count - stored_count
|
||||
reduced_party = stored_adv_party[:reduction]
|
||||
adv_party.set_party(reduced_party, count, level)
|
||||
# generate page
|
||||
response = make_response(render_template("party.html", adv_party=adv_party, count=count, level=level, cache=cache))
|
||||
# make a cookie for each character
|
||||
for adv in adv_party.adventurers:
|
||||
cookie_string, cookie_data = compress_character(adv)
|
||||
response.set_cookie(cookie_string, cookie_data)
|
||||
return response
|
||||
|
||||
@app.route('/character', methods = ["GET","POST"])
|
||||
def character():
|
||||
if request.method == "GET":
|
||||
c_id = request.args.get("id", default="adv-1", type=str)
|
||||
level = request.args.get("level", default=1, type=int)
|
||||
role = request.args.get("role", default="fighter", type=str)
|
||||
cache = request.args.get("cache", default='true', type=str)
|
||||
# control inputs to prevent abuse
|
||||
if cache not in [ "true", "false" ]:
|
||||
cache = 'true'
|
||||
if not c_id.startswith('adv-'):
|
||||
c_id = 'adv-1'
|
||||
if level < 1 or level > 5:
|
||||
level = 1
|
||||
if role not in [ 'fighter','magic-user','cleric', 'thief', 'dwarf', 'elf', 'halfling']:
|
||||
role = 'fighter'
|
||||
# check cookies for characters
|
||||
if request.cookies and cache != "false":
|
||||
character = return_character_from_cookie(request.cookies[c_id])
|
||||
if character.level != level:
|
||||
character.set_level(level)
|
||||
# reroll until we get the class from the cookie, if it doesn't match
|
||||
while character.player_class != role.replace("-"," "):
|
||||
new_char = Adventurer(c_id, level)
|
||||
selected_class = AdventurerGen(new_char).selection()
|
||||
character = selected_class(new_char.identifier, new_char.level, new_char.get_attributes())
|
||||
else:
|
||||
new_char = Adventurer(c_id, level)
|
||||
selected_class = AdventurerGen(new_char).selection()
|
||||
character = selected_class(new_char.identifier, new_char.level, new_char.get_attributes())
|
||||
role = character.player_class
|
||||
level = character.level
|
||||
response = make_response(render_template("character.html", character=character, level=level, cache=cache,role=role))
|
||||
cookie_string, cookie_data = compress_character(character)
|
||||
response.set_cookie(cookie_string, cookie_data)
|
||||
return response
|
||||
25
equipment.py
Executable file
25
equipment.py
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
armor= { 'None' : 9, 'Leather' : 7, 'Leather, Shield' : 6, 'Chain' : 5, 'Chain, Shield' : 4, 'Plate' : 3, 'Plate, Shield' : 2 }
|
||||
|
||||
weapons = [
|
||||
{ 'name' : 'battle axe', 'damage-dice' : 8, 'traits' : [ 'melee', 'slow', 'two-handed' ] },
|
||||
{ 'name' : 'club', 'damage-dice' : 4, 'traits' : [ 'melee', 'blunt' ] },
|
||||
{ 'name' : 'crossbow', 'damage-dice' : 6, 'traits' : [ 'missile', 'reload','slow','two-handed'], 'ammo' : '20 bolts'},
|
||||
{ 'name' : 'hand axe', 'damage-dice' : 6, 'traits' : [ 'melee', 'missile'] },
|
||||
{ 'name' : 'mace', 'damage-dice' : 6, 'traits' : [ 'melee', 'blunt' ] },
|
||||
{ 'name' : 'pole arm', 'damage-dice' : 10, 'traits' : [ 'melee', 'brace', 'slow', 'two-handed' ] },
|
||||
{ 'name' : 'short bow', 'damage-dice' : 6, 'traits' : [ 'missile', 'two-handed' ], 'ammo' : '20 arrows'},
|
||||
{ 'name' : 'short sword', 'damage-dice' : 6, 'traits' : [ 'melee' ] },
|
||||
{ 'name' : 'silver dagger','damage-dice' : 4, 'traits' : [ 'melee', 'missile' ] },
|
||||
{ 'name' : 'sling', 'damage-dice' : 4, 'traits' : [ 'missile', 'blunt' ], 'ammo' : '20 stones'},
|
||||
{ 'name' : 'staff', 'damage-dice' : 4, 'traits' : [ 'melee', 'blunt', 'slow', 'two-handed' ] },
|
||||
{ 'name' : 'spear', 'damage-dice' : 6, 'traits' : [ 'melee', 'missile', 'brace' ] },
|
||||
{ 'name' : 'sword', 'damage-dice' : 8, 'traits' : [ 'melee' ] },
|
||||
{ 'name' : 'war hammer', 'damage-dice' : 6, 'traits' : [ 'melee', 'blunt' ] }
|
||||
]
|
||||
|
||||
adventuring_gear = [
|
||||
"crowbar", "hammer (small) + 12 iron spikes", "holy water", "lantern + 3 flasks of oil", "mirror (small, steel)", "pole (10' long, wooden)",
|
||||
"rope (50')", "rope (50') + grappling hook", "sack (large)", "sack (small)", "stakes (3) + mallet", "wolfsbane (1 bunch)"
|
||||
]
|
||||
181
main.py
181
main.py
@@ -1,83 +1,128 @@
|
||||
#!/usr/bin/python3
|
||||
from adventurers import *
|
||||
import base64
|
||||
import json
|
||||
import random
|
||||
|
||||
class PlayerCharacter:
|
||||
def __init__(self, attributes={}) -> None:
|
||||
self.strength = roll_dice(3,6) if not attributes else attributes['strength']
|
||||
self.intelligence = roll_dice(3,6) if not attributes else attributes['intelligence']
|
||||
self.wisdom = roll_dice(3,6) if not attributes else attributes['wisdom']
|
||||
self.dexterity = roll_dice(3,6) if not attributes else attributes['dexterity']
|
||||
self.constitution = roll_dice(3,6) if not attributes else attributes['constitution']
|
||||
self.charisma = roll_dice(3,6) if not attributes else attributes['charisma']
|
||||
# Player Class Selector
|
||||
class AdventurerGen():
|
||||
def __init__(self, player: Adventurer) -> None:
|
||||
self.player = player
|
||||
# https://stackoverflow.com/questions/3862310/how-to-find-all-the-subclasses-of-a-class-given-its-name
|
||||
# pull classes that do not have requirements
|
||||
self.available_classes = [cls for cls in Adventurer.__subclasses__() if not cls.requirements]
|
||||
# pull classes that do have requirements
|
||||
self.classes_with_reqs = [cls for cls in Adventurer.__subclasses__() if cls.requirements]
|
||||
# run function to randomly select an adventurer class
|
||||
self.selected_class = self.selection()
|
||||
|
||||
def class_selector(self):
|
||||
# take the 4 primary attribtues
|
||||
base_attr = self.__dict__.copy()
|
||||
del base_attr['constitution']
|
||||
del base_attr['charisma']
|
||||
# of those grab the highest, found this at https://stackoverflow.com/a/280156
|
||||
max_base_attr = max(base_attr, key=base_attr.get)
|
||||
# use that to pick the best possible base class
|
||||
best_class = { "strength" : "Fighter", "intelligence" : "Magic User", "wisdom" : "Cleric", "dexterity" : "Thief" }
|
||||
best_base_class = best_class[max_base_attr]
|
||||
# create a list of possible classes
|
||||
possible_classes = [ best_base_class ]
|
||||
# check if player qualifies for other classes
|
||||
if self.constitution >= 9 : possible_classes.append("Dwarf")
|
||||
if self.intelligence >= 9 : possible_classes.append("Elf")
|
||||
if self.constitution >= 9 and self.dexterity >= 9 : possible_classes.append("Halfling")
|
||||
def return_class_by_best_attribute(self) -> Adventurer:
|
||||
# for adventurer classes in available classes, return the one where that classes' prime requisite is equal to the players best attribute
|
||||
return [adv_class for adv_class in self.available_classes if adv_class.prime_requisite == self.player.get_best_prime_attribute()][0]
|
||||
|
||||
def return_classes_with_requirements(self) -> list:
|
||||
p_attrs = self.player.get_attributes()
|
||||
possible_classes = []
|
||||
for c in self.classes_with_reqs:
|
||||
match_count = 0
|
||||
for r in c.requirements:
|
||||
if p_attrs[r] >= c.requirements[r]:
|
||||
match_count += 1
|
||||
if match_count >= len(c.requirements):
|
||||
possible_classes.append(c)
|
||||
return possible_classes
|
||||
|
||||
def selection(self) -> Adventurer:
|
||||
best_prime_attribute = self.player.get_best_prime_attribute()
|
||||
# create an array of possible player classes, add the best choice per player's best core attributes
|
||||
possible_classes = [ self.return_class_by_best_attribute() ]
|
||||
possible_classes += self.return_classes_with_requirements()
|
||||
# randomly select class
|
||||
selected_class = random.choice(possible_classes)
|
||||
return f"{selected_class}"
|
||||
return selected_class
|
||||
|
||||
class Fighter(PlayerCharacter):
|
||||
prime_requisite = "strength"
|
||||
def __init__(self, attributes: dict) -> None:
|
||||
PlayerCharacter.__init__(self, attributes)
|
||||
self.player_class = "fighter"
|
||||
self.hp = roll_dice(1, 8)
|
||||
def create_from_dict(character_dict) -> Adventurer:
|
||||
chosen_class = character_dict['player_class']
|
||||
c_id = character_dict['identifier']
|
||||
level = character_dict['level']
|
||||
adv_dict = Adventurer.get_subclass_dict()
|
||||
chosen_class = adv_dict[chosen_class]
|
||||
new_char = chosen_class(c_id=c_id, level=level)
|
||||
for k, v in character_dict.items():
|
||||
setattr(new_char,k, v)
|
||||
return new_char
|
||||
|
||||
class MagicUser(PlayerCharacter):
|
||||
prime_requisite = "intelligence"
|
||||
def __init__(self, attributes: dict) -> None:
|
||||
PlayerCharacter.__init__(self, name, attributes)
|
||||
self.player_class = "magic-user"
|
||||
self.hp = roll_dice(1, 4)
|
||||
class PartyGen():
|
||||
def __init__(self, party_size: int, party_level: int) -> None:
|
||||
self.size = party_size
|
||||
self.level = party_level
|
||||
self.adventurers = []
|
||||
self.adventurer_types = []
|
||||
|
||||
class Cleric(PlayerCharacter):
|
||||
prime_requisite = "wisdom"
|
||||
def __init__(self, attributes: dict) -> None:
|
||||
PlayerCharacter.__init__(self, name, attributes)
|
||||
self.player_class = "cleric"
|
||||
self.hp = roll_dice(1, 6)
|
||||
|
||||
class Thief(PlayerCharacter):
|
||||
prime_requisite = "dexterity"
|
||||
def __init__(self, attributes: dict) -> None:
|
||||
PlayerCharacter.__init__(self, name, attributes)
|
||||
self.player_class = "thief"
|
||||
self.hp = roll_dice(1, 6)
|
||||
|
||||
# functions
|
||||
def roll_dice(count, sides):
|
||||
return sum(random.randint(1,sides) for _ in range(count))
|
||||
|
||||
def main():
|
||||
adventurer_party = []
|
||||
expected_party_size = 4
|
||||
while len(adventurer_party) < expected_party_size:
|
||||
new_player = PlayerCharacter()
|
||||
selected_class = None
|
||||
def gen_party(self) -> None:
|
||||
c_id = 1
|
||||
while len(self.adventurers) < self.size:
|
||||
identifier = "adv-" + str(c_id)
|
||||
new_player = Adventurer(identifier, self.level)
|
||||
attempts = 0
|
||||
while selected_class not in adventurer_party:
|
||||
while new_player.player_class not in self.adventurer_types:
|
||||
attempts += 1
|
||||
selected_class = new_player.class_selector()
|
||||
if selected_class not in adventurer_party:
|
||||
adventurer_party.append(selected_class)
|
||||
selected_class = AdventurerGen(new_player).selection()
|
||||
new_player = selected_class(new_player.identifier, new_player.level, new_player.get_attributes())
|
||||
# i couldnt randomly generate a scenario where a character couldn't be added, but it seems possible, so this is the hard cut off
|
||||
elif attempts > 10:
|
||||
adventurer_party.append(selected_class)
|
||||
if (new_player.player_class not in self.adventurer_types) or (attempts > 10):
|
||||
c_id += 1
|
||||
self.adventurers.append(new_player)
|
||||
self.adventurer_types.append(new_player.player_class)
|
||||
|
||||
print(f"{adventurer_party}")
|
||||
def set_party(self, adventurers: list, count: int, level: int):
|
||||
self.size = count
|
||||
self.level = level
|
||||
self.adventurers = adventurers
|
||||
self.adventurer_types = []
|
||||
|
||||
def get_new_party(party_size: int, party_level: int):
|
||||
# keep variables within expected ranges
|
||||
if party_size <= 0 or party_size > 5:
|
||||
party_size = 1
|
||||
if party_level < 1 or party_level > 5:
|
||||
party_level = 1
|
||||
# generate an aventuring party per size and level
|
||||
new_party = PartyGen(party_size, party_level)
|
||||
# for adventurers select classes
|
||||
new_party.gen_party()
|
||||
# return the created adventurer party
|
||||
return new_party
|
||||
|
||||
def get_character(self, identifer: str) -> Adventurer:
|
||||
for adv in self.adventurers:
|
||||
if adv.identifier == identifier:
|
||||
return adv
|
||||
|
||||
def get_character_sheets(self) -> list:
|
||||
sheet_string = ""
|
||||
character_sheets = []
|
||||
for c in self.adventurers:
|
||||
character_sheets.append(c.vertical_sheet())
|
||||
return character_sheets
|
||||
|
||||
def get_json(self) -> str:
|
||||
party_list = []
|
||||
for c in self.adventurers:
|
||||
party_list.append(c.get_json())
|
||||
party_json = json.dumps(party_list)
|
||||
return party_json
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.adventurers}"
|
||||
|
||||
# used for local testing
|
||||
def main():
|
||||
adv_dict = Adventurer.get_subclass_dict()
|
||||
test_class = adv_dict['cleric']
|
||||
new_char = test_class(c_id="adv-1",level=2)
|
||||
for line in new_char.vertical_sheet():
|
||||
print(line)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
Flask==3.1.3
|
||||
18
spells.py
Normal file
18
spells.py
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
magic_user_spells = {
|
||||
1 : [ "charm person", "detect magic", "floating disc", "hold portal", "light (darkness)", "magic missile", "protection from evil", "read languages", "shield", "sleep", "ventriloquism" ],
|
||||
2 : [ "continual light", "detect evil", "detect invisible", "ESP", "invisibility", "knock", "levitate", "locate object", "mirror image", "phantasmal force", "web", "wizard lock" ],
|
||||
3 : [ "clairvoyance", "dispel magic", "fire ball", "fly", "haste", "hold person", "infravision", "invisibility 10'", "lightning bolt", "protection from evil 10'", "protection from normal missiles", "water breathing" ],
|
||||
4 : [ "charm monster", "confusion", "dimension door", "growth of plants", "hallucinatory terrain", "massmorph", "polymorph others", "polymorph self", "remove curse", "wall of fire", "wall of ice", "wizard eye" ],
|
||||
5 : [ "animate dead", "cloudkill", "conjure elemental", "contact higher plane", "feeblemind", "hold monster", "magic jar", "pass-wall", "telekinesis", "teleport", "transmute rock to mud", "wall of stone" ],
|
||||
6 : [ "anti-magic shell", "control weather", "death spell", "disintegrate", "geas", "invisible stalker", "lower water", "move earth", "part water", "project image", "reincarnation", "stone to flesh" ]
|
||||
}
|
||||
|
||||
cleric_spells = {
|
||||
1 : [ "cure light wounds", "detect evil", "detect magic", "light (darkness)", "protection from evil", "purify food and water", "remove fear", "resist cold"],
|
||||
2 : [ "bless", "find traps", "hold person", "know alignment", "resist fire", "silence 15'", "snake charm", "speak with animals" ],
|
||||
3 : [ "continual light", "cure disease", "growth of animal", "locate object", "remove curse", "striking" ],
|
||||
4 : [ "create water", "cure serious wounds", "neutralize poison", "protection from evil 10'", "speak with plants", "sticks to snakes" ],
|
||||
5 : [ "commune", "create food", "dispel evil", "insect plague", "quest", "raise dead" ]
|
||||
}
|
||||
BIN
static/magic-user.png
Normal file
BIN
static/magic-user.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
164
templates/character.html
Normal file
164
templates/character.html
Normal file
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>D&D Characters</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css"
|
||||
>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">Home</a>
|
||||
<a class="navbar-item" href="character">Character Generator</a>
|
||||
<a class="navbar-item" href="party?cache=true">Party Generator</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="card">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<div class="block">
|
||||
<div class="block">
|
||||
<h1>Character Generator</h1>
|
||||
<p>The below lets you generate a character based on your preferences. If you change levels, there will be subtle changes, usually just the "saving throws" and "hp" will change. Some classes, like spellcasters, will gain new spells.</p>
|
||||
<hr>
|
||||
<div class="columns">
|
||||
<div class="column" >
|
||||
<p>Select a Class</p>
|
||||
<div class="dropdown is-hoverable">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
<span>{{role.title()}}</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
{%for c in ['fighter','magic-user', 'cleric', 'thief', 'dwarf', 'elf', 'halfling'] %}
|
||||
{% if role == c %}
|
||||
<a href="{{ '/character?level={}&role={}&cache=true'.format(level,c) }}" class="dropdown-item is-active">{{c.title()}}</a>
|
||||
{% else %}
|
||||
<a href="{{ '/character?level={}&role={}&cache=true'.format(level,c) }}" class="dropdown-item">{{c.title()}}</a>
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" >
|
||||
<p>Character Level</p>
|
||||
<div class="buttons">
|
||||
{%for j in range(1,6)%}
|
||||
{% if level|int() == j|int() %}
|
||||
<a class="button is-primary" href="{{ '/character?&level={}&role={}&cache=true'.format(j,role) }}">{{j}}</a>
|
||||
{% else %}
|
||||
<a class="button" href="{{ '/character?&level={}&role={}&cache=true'.format(j,role) }}">{{j}}</a>
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column" >
|
||||
<p>Generate new character sheet</p>
|
||||
<div class="buttons">
|
||||
<a class="button is-danger is-outlined" href="{{ '/character?&level={}&role={}&cache=false'.format(j,role,cache) }}">Re-Roll Character</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="card">
|
||||
<h3>{{character.adv_class.title()}} - Level {{character.level}}</h3>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<table class="table">
|
||||
<thead><tr><th>Ability Scores</th><th></th><th>Saving Throws</th><th></th></tr></thead>
|
||||
<tbody><tr><th>Strength</th><td>{{character.strength}}</td><th>Death / Poison</th><td>{{character.progression[character.level]['saves']['death / poison']}}</tr></tbody>
|
||||
<tbody><tr><th>Intelligence</th><td>{{character.intelligence}}</td><th>Magic Wands</th><td>{{character.progression[character.level]['saves']['wands']}}</tr></tbody>
|
||||
<tbody><tr><th>Wisdom</th><td>{{character.wisdom}}</td><th>Paralysis / Petrification</th><td>{{character.progression[character.level]['saves']['paralysis / petrify']}}</tr></tbody>
|
||||
<tbody><tr><th>Dexterity</th><td>{{character.dexterity}}</td><th>Breath Attacks</th><td>{{character.progression[character.level]['saves']['breath attack']}}</tr></tbody>
|
||||
<tbody><tr><th>Constitution</th><td>{{character.constitution}}</td><th>Spells, Rods, Staves</th><td>{{character.progression[character.level]['saves']['spells / rods / staves']}}</tr></tbody>
|
||||
<tbody><tr><th>Charisma</th><td>{{character.charisma}}</td><th>Wisdom Mod. to Saves v. Magic</th><td>+1</tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="column">
|
||||
<table class="table">
|
||||
<thead><tr><th>Combat</th><th></th><th>Dungeon Gear</th><th></th></thead>
|
||||
<tbody><tr><th>Hit Points</th><td>{{character.hp}}</td><th>Torches</th><td>{{character.torches}}</td></tr></tbody>
|
||||
<tbody><tr><th>Armor Class</th><td>{{character.ac}}</td><th>Rations</th><td>{{character.rations}}</td></tr></tbody>
|
||||
<tbody><tr><th>Attack Bonus</th><td>{{character.atk}}</td><th>Gold</th><td>{{character.gold}}</td></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<table class="table">
|
||||
<thead><tr><th>Equipment</th><th></th><th></th><th></th></tr></thead>
|
||||
{% for e in character.equipment %}
|
||||
<tbody><tr><th>{{e}}</th></tr></tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% if character.spells %}
|
||||
<div class="column">
|
||||
<table class="table">
|
||||
<thead><tr><th>Spellbook</th><th></th><th></th><th></th></thead>
|
||||
{%for spell in character.spell_book %}
|
||||
<tbody><tr><th>{{spell}}</th><td></td><th></th><td></td></tr></tbody>
|
||||
{%endfor%}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if character.turn_undead %}
|
||||
<div class="column">
|
||||
<h5>Turn Undead</h5>
|
||||
<table class="table">
|
||||
<thead><tr><th>Monster Hit Die</th><th>Roll to Turn</th><th></th><th></th></thead>
|
||||
{%for k,v in character.turn_undead.items() %}
|
||||
<tbody><tr><th>{{k}} Hit Die</th><td>{{v}}</td><th></th><td></td></tr></tbody>
|
||||
{%endfor%}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if character.thief_skills %}
|
||||
<div class="column">
|
||||
<h5>Thief Skills</h5>
|
||||
<table class="table">
|
||||
<thead><tr><th>Thief Skills</th><th></th><th></th><th></th></thead>
|
||||
{%for spell in character.spell_book %}
|
||||
<tbody><tr><th>{{spell}}</th><td></td><th></th><td></td></tr></tbody>
|
||||
{%endfor%}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
94
templates/index.html
Normal file
94
templates/index.html
Normal file
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>D&D Characters</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css"
|
||||
>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">Home</a>
|
||||
<a class="navbar-item" href="character">Character Generator</a>
|
||||
<a class="navbar-item" href="party?cache=true">Party Generator</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="card">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<div class="block">
|
||||
<h1>A Dungeons & Dragons Character Generator</h1>
|
||||
<div class="tabs"><ul>
|
||||
{% for t in ["what","how","why", "about"] %}
|
||||
{%if t == tab %}
|
||||
<li class="is-active"><a href="{{ '/?tab={}'.format(t) }}">{{t.title()}}</a></li>
|
||||
{%else%}
|
||||
<li><a href="{{ '/?tab={}'.format(t) }}">{{t.title()}}</a></li>
|
||||
{%endif%}
|
||||
{%endfor%}
|
||||
</ul></div>
|
||||
|
||||
{%if tab == "what" %}
|
||||
<h3>What is Dungeons & Dragons?</h3>
|
||||
<p>Dungeons & Dragons, also known as D&D, is a pen and paper table top game. One player is the story teller, the "dungeon master" and the others interact with the story using their "adventurers." An adventurer is a playable character, who has a mixture of abilities and powers, the details of which are often kept on piece of paper called a "character sheet."</p>
|
||||
<br>
|
||||
<img src="https://static0.colliderimages.com/wordpress/wp-content/uploads/2024/06/dungeons-and-dragons-in-stranger-things.jpg">
|
||||
{%endif%}
|
||||
{%if tab == "how" %}
|
||||
<h3>How does the game work?</h3>
|
||||
<p>Most settings are fantasy based, like game of thrones or lord of the rings. Players often pick up a quest in one of these fantasy worlds, guided by the "Dungeon Master" who narrates the world. The players will make decisions, take actions, and ultimate help write the story as it unfolds.</p>
|
||||
<br>
|
||||
<img src="https://www.baltimoremagazine.com/wp-content/uploads/2023/03/dungeons-and-dragons-honor-among-thieves-movie-review-2023.jpeg">
|
||||
{%endif%}
|
||||
{%if tab == "why" %}
|
||||
<h3>Why would I need a character generator?</h3>
|
||||
<p>It takes time and effort to create a new character; there are many rules, phrases, and terminology you'll need to know to create a character. Most rule books provide an easy to follow guide on making player characters or "adventurers." However, if you are short on time, a generator such as this will get you in the game right away!</p>
|
||||
<br>
|
||||
<img src="https://assetsio.reedpopcdn.com/dnd-character-sheet-dice-pencil.jpeg">
|
||||
{%endif%}
|
||||
{%if tab == "about" %}
|
||||
<h3>About This App</h3>
|
||||
<p>This app accomplishes a few things, but mainly it creates a character for you.</p>
|
||||
<ul>
|
||||
<li>It follows the rules precisely from the books to generate a legal character</li>
|
||||
<li>All stats are randomly rolled, but a class is chosen per your best stats</li>
|
||||
<li>Character equipment and spells are also randomly selected, within that character-class rules</li>
|
||||
<li>You can generate a whole party of characters</li>
|
||||
<li>Within that party, you can adjust their level, and the number of characters</li>
|
||||
<li>You can create a single character to edit</li>
|
||||
<br>
|
||||
<img src="static/magic-user.png">
|
||||
</il>
|
||||
|
||||
{%endif%}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
101
templates/party.html
Normal file
101
templates/party.html
Normal file
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>D&D Characters</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css"
|
||||
>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">Home</a>
|
||||
<a class="navbar-item" href="character">Character Generator</a>
|
||||
<a class="navbar-item" href="party?cache=true">Party Generator</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="card">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<h2>Welcome to the Dungeon</h2>
|
||||
<div class="block">
|
||||
<div class="columns">
|
||||
<div class="column" >
|
||||
<p>How many party members?</p>
|
||||
<div class="buttons">
|
||||
{%for i in range(2,6)%}
|
||||
{% if count|int() == i|int() %}
|
||||
<a class="button is-primary" href="{{ '/party?count={}&level={}&cache={}'.format(i,level,cache) }}">{{i}}</a>
|
||||
{% else %}
|
||||
<a class="button" href="{{ '/party?count={}&level={}&cache={}'.format(i,level,cache) }}">{{i}}</a>
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" >
|
||||
<p>What level for the party?</p>
|
||||
<div class="buttons">
|
||||
{%for j in range(1,6)%}
|
||||
{% if level|int() == j|int() %}
|
||||
<a class="button is-primary" href="{{ '/party?count={}&level={}&cache={}'.format(count,j,cache) }}">{{j}}</a>
|
||||
{% else %}
|
||||
<a class="button" href="{{ '/party?count={}&level={}&cache={}'.format(count,j,cache) }}">{{j}}</a>
|
||||
{% endif %}
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" >
|
||||
<p>Preserve adventurers?</p>
|
||||
<div class="buttons">
|
||||
{% if cache == 'true' %}
|
||||
<a class="button is-primary">cached</a>
|
||||
<a class="button is-danger is-outlined" href="{{ '/party?count={}&level={}&cache=false'.format(count,level) }}">not cached</a>
|
||||
{% else %}
|
||||
<a class="button is-primary is-outlined" href="{{ '/party?count={}&level={}&cache=true'.format(count,level) }}">are cached</a>
|
||||
<a class="button is-danger">not cached</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block has-text-centered">
|
||||
<div class="columns">
|
||||
{%for character in adv_party.adventurers%}
|
||||
<div class="column" >
|
||||
<pre>{{character.vertical_sheet() | join("\n")}}</pre>
|
||||
<div class="block has-text-centered">
|
||||
<a class="button is-focused" href="{{ '/character?id={}&level={}&role={}&cache=true'.format(character.identifier,character.level,character.player_class)}}">Select this {{character.player_class.title()}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user