cleaning up app and improving html

This commit is contained in:
Zachary Watts
2026-05-02 00:41:49 -04:00
parent 78ba21227a
commit 2173733463
5 changed files with 53 additions and 42 deletions

View File

@@ -1,3 +1,22 @@
# ose-character-gen
# OSE Character Generator
Web app for old school D&D character generation
This tool uses the Old School Essentials (OSE) ruleset for tabletop RPGs in order to randomly generate a character.
### 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

52
app.py
View File

@@ -7,47 +7,53 @@ from flask_session import Session
app = Flask(__name__)
# Configure session
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# 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 = createCharacterWithDict(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():
print("generating party")
# default values, modified if cookies exist
stored_adv_party = []
stored_count = None
stored_level = None
if request.method == "GET":
print('in get if')
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 = returnParty(count, level)
# check for cookies present
if request.cookies:
stored_count = len(request.cookies)
#print(request.cookies)
for c in request.cookies:
if c.startswith('adv'):
cookie_encoded = request.cookies[c]
cookie_compressed = base64.urlsafe_b64decode(cookie_encoded)
character_dict = json.loads(zlib.decompress(cookie_compressed).decode())
new_char = createCharacterWithDict(character_dict)
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
@@ -68,14 +74,9 @@ def party():
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))
print(response)
# make a cookie for each character
# this took a while to figure out, json was too large, b64 encoded json was too large, but it turns out you can compress json as a cookie
for character in adv_party.adventurers:
cookie_string = character.identifier
char_json = json.dumps(character.get_json())
compressed = zlib.compress(char_json.encode())
cookie_data = base64.urlsafe_b64encode(compressed).decode()
for adv in adv_party.adventurers:
cookie_string, cookie_data = compress_character(adv)
response.set_cookie(cookie_string, cookie_data)
return response
@@ -95,19 +96,17 @@ def character():
if role not in [ 'fighter','magic-user','cleric', 'thief', 'dwarf', 'elf', 'halfling']:
role = 'fighter'
if request.cookies and cache != "false":
cookie_encoded = request.cookies[c_id]
cookie_compressed = base64.urlsafe_b64decode(cookie_encoded)
character_dict = json.loads(zlib.decompress(cookie_compressed).decode())
character = createCharacterWithDict(character_dict)
character = return_character_from_cookie(request.cookies[c_id])
print(character.player_class)
if character.level != level:
character.set_level(level)
# reroll until we get our class
while character.player_class != role.replace("-"," "):
new_char = Adventurer(c_id, 1)
new_char = Adventurer(c_id, level)
selected_class = ClassSelector(new_char).selection()
character = selected_class(new_char.identifier, new_char.level, new_char.get_attributes())
else:
new_char = Adventurer(c_id, 1)
new_char = Adventurer(c_id, level)
selected_class = ClassSelector(new_char).selection()
character = selected_class(new_char.identifier, new_char.level, new_char.get_attributes())
role = character.player_class
@@ -116,9 +115,6 @@ def character():
if character.player_class == "cleric":
character.turn_undead = { int(k) : v for k,v in character.turn_undead.items() }
response = make_response(render_template("character.html", character=character, level=level, cache=cache,role=role))
cookie_string = character.identifier
char_json = json.dumps(character.get_json())
compressed = zlib.compress(char_json.encode())
cookie_data = base64.urlsafe_b64encode(compressed).decode()
cookie_string, cookie_data = compress_character(character)
response.set_cookie(cookie_string, cookie_data)
return response

View File

@@ -105,7 +105,7 @@ def returnCharacter(identifer):
if adv.identifier == identifier:
return adv
def createCharacterWithDict(character_dict):
def createCharacterWithDict(character_dict) -> Adventurer:
chosen_class = character_dict['player_class']
c_id = character_dict['identifier']
level = character_dict['level']
@@ -116,13 +116,8 @@ def createCharacterWithDict(character_dict):
setattr(new_char,k, v)
return new_char
# used for local testing
def main():
#adventurer_party = returnParty(party_size=2, party_level=2)
#for adv in adventurer_party.adventurers:
# print(adv.adv_class, adv.level)
# adv.set_level(10)
# print(adv.adv_class, adv.level)
adv_dict = Adventurer.get_subclass_dict()
test_class = adv_dict['cleric']
new_char = test_class(c_id="adv-1",level=2)

View File

@@ -36,6 +36,9 @@
<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>
@@ -81,6 +84,8 @@
</div>
</div>
</div>
<br>
<div class="card">
<h3>{{character.adv_class.title()}} - Level {{character.level}}</h3>
<div class="columns">
<div class="column">
@@ -114,9 +119,8 @@
</div>
{% if character.spells %}
<div class="column">
<h5>Spellbook</h5>
<table class="table">
<thead><tr><th>Spellname</th><th></th><th></th><th></th></thead>
<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%}

View File

@@ -90,9 +90,6 @@
</div>
</div>
<br>
<div class="block has-text-centered">
<button class="button is-primary is-fillwidth">Select this adventuring party!</button>
<div>
</div>
</div>
</div>