1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 11:34:18 +00:00

Second commit

This commit is contained in:
Steffo 2019-11-11 10:34:05 +01:00
parent 15a3cb84eb
commit 1bd40ef9cd
22 changed files with 987 additions and 23 deletions

View file

@ -0,0 +1,4 @@
royalnet>=5.0a1
aiohttp>=3.6.2
dice>=2.4.2
sortedcontainers>=2.1.0

View file

@ -1,6 +1,6 @@
# This is a template Pack __init__. You can use this without changing anything in other packages too!
from . import commands, tables, stars
from . import commands, tables, stars, version
from .commands import available_commands
from .tables import available_tables
from .stars import available_page_stars, available_exception_stars
@ -9,9 +9,9 @@ __all__ = [
"commands",
"tables",
"stars",
"version",
"available_commands",
"available_tables",
"available_page_stars",
"available_exception_stars",
]

View file

@ -0,0 +1,26 @@
# Imports go here!
from .roll import RollCommand
from .dice import DiceCommand
from .dndactive import DndactiveCommand
from .dndinfo import DndinfoCommand
from .dndnew import DndnewCommand
from .dndedit import DndeditCommand
from .dndroll import DndrollCommand
from .dnditem import DnditemCommand
from .dndspell import DndspellCommand
# Enter the commands of your Pack here!
available_commands = [
RollCommand,
DiceCommand,
DndactiveCommand,
DndinfoCommand,
DndnewCommand,
DndeditCommand,
DndrollCommand,
DnditemCommand,
DndspellCommand,
]
# Don't change this, it should automatically generate __all__
__all__ = [command.__name__ for command in available_commands]

40
rpgpack/commands/dice.py Normal file
View file

@ -0,0 +1,40 @@
import dice
from royalnet.commands import *
class DiceCommand(Command):
name: str = "dice"
description: str = "Roll a dice, using 'dice'."
syntax = "{dice}"
aliases = ["d"]
async def run(self, args: CommandArgs, data: CommandData) -> None:
dice_str = args.joined(require_at_least=1)
try:
roll = dice.roll(dice_str)
except dice.DiceFatalException as e:
raise CommandError(e.msg)
except dice.DiceException as e:
raise CommandError(e.msg)
except dice.DiceBaseException as e:
raise CommandError(str(e))
try:
result = list(roll)
except TypeError:
result = [roll]
message = f"🎲 {dice_str}"
total = 0
if len(result) > 1:
message += f" = "
for index, die in enumerate(result):
message += f"{die}"
total += int(die)
if (index + 1) < len(result):
message += "+"
else:
total += int(result[0])
message += f" = [b]{total}[/b]"
await data.reply(message)

View file

@ -0,0 +1,56 @@
from royalnet.commands import *
from royalnet.utils import asyncify
from ..tables import DndCharacter, DndActiveCharacter
class DndactiveCommand(Command):
name: str = "dndactive"
description: str = "Set a DnD character as active."
aliases = ["da", "dnda", "active", "dactive"]
syntax = "{name|id}"
tables = {DndCharacter, DndActiveCharacter}
async def run(self, args: CommandArgs, data: CommandData) -> None:
identifier = args.optional(0)
author = await data.get_author(error_if_none=True)
if identifier is None:
# Display the active character
if author.dnd_active_character is None:
await data.reply(" You have no active characters.")
else:
await data.reply(f" You currently active character is [b]{author.dnd_active_character}[/b].")
return
try:
identifier = int(identifier)
except ValueError:
# Find the character by name
chars = await asyncify(data.session.query(self.alchemy.DndCharacter).filter_by(name=identifier).all)
if len(chars) >= 2:
char_string = "\n".join([f"[c]{char.character_id}[/c] (LV {char.level}) by {char.creator})" for char in chars])
raise CommandError(f"Multiple characters share the name {identifier}, "
f"please activate them using their id:\n{char_string}")
elif len(chars) == 1:
char = chars[0]
else:
char = None
else:
# Find the character by id
char = await asyncify(data.session.query(self.alchemy.DndCharacter)
.filter_by(character_id=identifier)
.one_or_none)
if char is None:
raise CommandError("No character found.")
# Check if the player already has an active character
if author.dnd_active_character is None:
# Create a new active character
achar = self.alchemy.DndActiveCharacter(character=char, user=author)
data.session.add(achar)
else:
# Change the active character
author.dnd_active_character.character = char
await data.session_commit()
await data.reply(f"✅ Active character set to [b]{char}[/b]!")

View file

@ -0,0 +1,35 @@
import re
from royalnet.commands import *
from .dndnew import DndnewCommand
from ..tables import DndCharacter, DndActiveCharacter
class DndeditCommand(DndnewCommand):
name: str = "dndedit"
description: str = "Edit the active DnD character."
aliases = ["de", "dnde", "edit", "dedit"]
tables = {DndCharacter, DndActiveCharacter}
async def run(self, args: CommandArgs, data: CommandData) -> None:
character_sheet = args.joined()
if character_sheet == "":
await data.reply(self._syntax())
return
author = await data.get_author(error_if_none=True)
if author.dnd_active_character is None:
raise CommandError("You don't have an active character.")
char: DndCharacter = author.dnd_active_character.character
arguments = self._parse(character_sheet)
for key in arguments:
char.__setattr__(key, arguments[key])
await data.session_commit()
await data.reply(f"✅ Edit successful!")

View file

@ -0,0 +1,19 @@
from royalnet.commands import *
from royalnet.utils import asyncify
from ..tables import DndCharacter, DndActiveCharacter
class DndinfoCommand(Command):
name: str = "dndinfo"
description: str = "Display the character sheet of the active DnD character."
aliases = ["di", "dndi", "info", "dinfo"]
tables = {DndCharacter, DndActiveCharacter}
async def run(self, args: CommandArgs, data: CommandData) -> None:
author = await data.get_author(error_if_none=True)
if author.dnd_active_character is None:
raise CommandError("You don't have an active character.")
await data.reply(author.dnd_active_character.character.character_sheet())

View file

@ -0,0 +1,56 @@
import aiohttp
import sortedcontainers
from royalnet.commands import *
from ..utils import parse_5etools_entry
class DnditemCommand(Command):
name: str = "dnditem"
aliases = ["item"]
description: str = "Ottieni informazioni su un oggetto di D&D5e."
syntax = "{nomeoggetto}"
_dnddata: sortedcontainers.SortedKeyList = None
def __init__(self, interface: CommandInterface):
super().__init__(interface)
interface.loop.create_task(self._fetch_dnddata())
async def _fetch_dnddata(self):
self._dnddata = self._dnddata = sortedcontainers.SortedKeyList([], key=lambda i: i["name"].lower())
async with aiohttp.ClientSession() as session:
async with session.get("https://5e.tools/data/items.json") as response:
j = await response.json()
for item in j["item"]:
self._dnddata.add(item)
async with session.get("https://5e.tools/data/fluff-items.json") as response:
j = await response.json()
for item in j["item"]:
self._dnddata.add(item)
async with session.get("https://5e.tools/data/items-base.json") as response:
j = await response.json()
for item in j["baseitem"]:
self._dnddata.add(item)
async def run(self, args: CommandArgs, data: CommandData) -> None:
if self._dnddata is None:
await data.reply("⚠️ Il database degli oggetti di D&D non è ancora stato scaricato.")
return
search = args.joined().lower()
result = self._dnddata[self._dnddata.bisect_key_left(search)]
string = f'📦 [b]{result["name"]}[/b]\n'
if "source" in result:
string += f'[i]{result["source"]}, page {result["page"]}[/i]\n'
string += f'\n' \
f'Type: [b]{result.get("type", "None")}[/b]\n' \
f'Value: [b]{result.get("value", "-")}[/b]\n' \
f'Weight: [b]{result.get("weight", "0")} lb[/b]\n' \
f'Rarity: [b]{result["rarity"] if result.get("rarity", "None") != "None" else "Mundane"}[/b]\n' \
f'\n'
for entry in result.get("entries", []):
string += parse_5etools_entry(entry)
string += "\n\n"
await data.reply(string)

View file

@ -0,0 +1,71 @@
import re
# noinspection PyUnresolvedReferences
from royalnet.commands import *
from ..tables import DndCharacter
from ..utils import DndProficiencyType
class DndnewCommand(Command):
name: str = "dndnew"
description: str = "Create a new DnD character."
aliases = ["dn", "dndn", "new", "dnew"]
syntax = "{name}\n{character_sheet}"
tables = {DndCharacter}
def _search_value(self, name: str, string: str):
return re.search(r"\s*" + name + r"\s*([0-9]+)\s*", string, re.IGNORECASE)
def _parse(self, character_sheet: str) -> dict:
columns = list(self.alchemy.DndCharacter.__table__.columns)
column_names = [column.name for column in columns if (not column.primary_key and
not column.foreign_keys and
column.name != "name")]
arguments = {}
for column_name in column_names:
match = self._search_value(column_name, character_sheet)
if match:
if column_name.endswith("_proficiency"):
arguments[column_name] = DndProficiencyType(float(match.group(1)))
else:
arguments[column_name] = match.group(1)
return arguments
def _syntax(self) -> str:
columns = list(self.alchemy.DndCharacter.__table__.columns)
column_names = [column.name for column in columns if (not column.primary_key and
not column.foreign_keys and
column.name != "name")]
message = " How to create a new character:\n[p]/dndnew YOUR_CHARACTER_NAME\n"
for column_name in column_names:
message += f"{column_name} _\n"
message += "[/p]"
return message
async def run(self, args: CommandArgs, data: CommandData) -> None:
character_sheet = args.joined()
if character_sheet == "":
await data.reply(self._syntax())
return
creator = await data.get_author()
name, rest = character_sheet.split("\n", 1)
character = self.alchemy.DndCharacter(name=name, creator=creator, **self._parse(rest))
data.session.add(character)
try:
await data.session_commit()
except Exception as err:
# THIS IS INTENDED
if err.__class__.__name__ == "IntegrityError":
param_name = re.search(r'in column "(\S+)"', err.args[0]).group(1)
raise CommandError(f"Mandatory parameter '{param_name}' is missing.")
raise
await data.reply(f"✅ Character [b]{character.name}[/b] created!")

146
rpgpack/commands/dndroll.py Normal file
View file

@ -0,0 +1,146 @@
import re
import random
from royalnet.commands import *
from ..tables import DndCharacter, DndActiveCharacter
from royalnet.utils import plusformat
class DndrollCommand(Command):
name: str = "dndroll"
description: str = "Roll dice as the active DnD character."
aliases = ["dr", "dndr", "roll", "droll"]
tables = {DndCharacter, DndActiveCharacter}
_skill_names = {
"str": "strength",
"for": "strength",
"dex": "dexterity",
"des": "dexterity",
"con": "constitution",
"cos": "constitution",
"inte": "intelligence",
"wis": "wisdom",
"sag": "wisdom",
"cha": "charisma",
"car": "charisma",
"ststr": "strength_save",
"stfor": "strength_save",
"stdex": "dexterity_save",
"stdes": "dexterity_save",
"stcon": "constitution_save",
"stcos": "constitution_save",
"stint": "intelligence_save",
"stwis": "wisdom_save",
"stsag": "wisdom_save",
"stcha": "charisma_save",
"stcar": "charisma_save",
"tsstr": "strength_save",
"tsfor": "strength_save",
"tsdex": "dexterity_save",
"tsdes": "dexterity_save",
"tscon": "constitution_save",
"tscos": "constitution_save",
"tsint": "intelligence_save",
"tswis": "wisdom_save",
"tssag": "wisdom_save",
"tscha": "charisma_save",
"tscar": "charisma_save",
"acr": "acrobatics",
"add": "animal_handling",
"ani": "animal_handling",
"arc": "arcana",
"ath": "athletics",
"dec": "deception",
"ing": "deception",
"his": "history",
"sto": "history",
"ins": "insight",
"intu": "insight",
"inti": "intimidation",
"inv": "investigation",
"med": "medicine",
"nat": "nature",
"perc": "perception",
"perf": "performance",
"pers": "persuasion",
"rel": "religion",
"sle": "sleight_of_hand",
"soh": "sleight_of_hand",
"rap": "sleight_of_hand",
"ste": "stealth",
"nas": "stealth",
"sur": "survival",
"sop": "sopravvivenza",
}
async def run(self, args: CommandArgs, data: CommandData) -> None:
author = await data.get_author(error_if_none=True)
if author.dnd_active_character is None:
raise CommandError("You don't have an active character.")
char: DndCharacter = author.dnd_active_character.character
first = args[0]
second = args.optional(1)
third = args.optional(2)
advantage = False
disadvantage = False
extra_modifier = 0
if third:
try:
extra_modifier = int(third)
except ValueError:
raise InvalidInputError("Invalid modifier value (third parameter).")
if second.startswith("a") or second.startswith("v"):
advantage = True
elif second.startswith("d") or second.startswith("d"):
disadvantage = True
else:
raise InvalidInputError("Invalid advantage string (second parameter).")
elif second:
try:
extra_modifier = int(second)
except ValueError:
if second.startswith("a") or second.startswith("v"):
advantage = True
elif second.startswith("d") or second.startswith("d"):
disadvantage = True
else:
raise InvalidInputError("Invalid modifier value or advantage string (second parameter).")
skill_short_name = first.lower()
for root in self._skill_names:
if skill_short_name.startswith(root):
skill_name = self._skill_names[root]
break
else:
raise CommandError("Invalid skill name (first parameter).")
skill_modifier = char.__getattribute__(skill_name)
modifier = skill_modifier + extra_modifier
modifier_str = plusformat(modifier, empty_if_zero=True)
if advantage:
roll_a = random.randrange(1, 21)
roll_b = random.randrange(1, 21)
roll = max([roll_a, roll_b])
total = roll + modifier
await data.reply(f"🎲 2d20h1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]")
elif disadvantage:
roll_a = random.randrange(1, 21)
roll_b = random.randrange(1, 21)
roll = min([roll_a, roll_b])
total = roll + modifier
await data.reply(f"🎲 2d20l1{modifier_str} = ({roll_a}|{roll_b}){modifier_str} = [b]{total}[/b]")
else:
roll = random.randrange(1, 21)
total = roll + modifier
await data.reply(f"🎲 1d20{modifier_str} = {roll}{modifier_str} = [b]{total}[/b]")

View file

@ -0,0 +1,114 @@
import aiohttp
import sortedcontainers
from royalnet.commands import *
from royalnet.utils import ordinalformat, andformat
from ..utils import parse_5etools_entry
class DndspellCommand(Command):
name: str = "dndspell"
aliases = ["spell"]
description: str = "Ottieni informazioni su una magia di D&D5e."
syntax = "{nomemagia}"
_dnddata: sortedcontainers.SortedKeyList = None
def __init__(self, interface: CommandInterface):
super().__init__(interface)
interface.loop.create_task(self._fetch_dnddata())
async def _fetch_dnddata(self):
self._dnddata = self._dnddata = sortedcontainers.SortedKeyList([], key=lambda i: i["name"].lower())
async with aiohttp.ClientSession() as session:
for url in [
"https://5e.tools/data/spells/spells-ai.json",
"https://5e.tools/data/spells/spells-ggr.json",
"https://5e.tools/data/spells/spells-llk.json",
"https://5e.tools/data/spells/spells-phb.json",
"https://5e.tools/data/spells/spells-scag.json",
"https://5e.tools/data/spells/spells-stream.json",
"https://5e.tools/data/spells/spells-ua-ar.json",
"https://5e.tools/data/spells/spells-ua-mm.json",
"https://5e.tools/data/spells/spells-ua-ss.json",
"https://5e.tools/data/spells/spells-ua-tobm.json",
"https://5e.tools/data/spells/spells-xge.json"
]:
async with session.get(url) as response:
j = await response.json()
for spell in j["spell"]:
self._dnddata.add(spell)
@staticmethod
def _parse_spell(spell: dict) -> str:
string = f'✨ [b]{spell["name"]}[/b]\n'
if "source" in spell:
string += f'[i]{spell["source"]}, page {spell["page"]}[/i]\n'
string += "\n"
if spell["level"] == 0:
string += f'[b]Cantrip[/b] {spell["school"]}\n'
else:
string += f'[b]{ordinalformat(spell["level"])}[/b] level {spell["school"]}\n'
if "time" in spell:
for time in spell["time"]:
string += f'Cast time: ⌛️ [b]{time["number"]} {time["unit"]}[/b]\n'
if "range" in spell:
if spell["range"]["distance"]["type"] == "touch":
string += "Range: 👉 [b]Touch[/b]\n"
elif spell["range"]["distance"]["type"] == "self":
string += "Range: 👤 [b]Self[/b]\n"
else:
string += f'Range: 🏹 [b]{spell["range"]["distance"]["amount"]} {spell["range"]["distance"]["type"]}[/b] ({spell["range"]["type"]})\n'
if "components" in spell:
string += f'Components: '
if spell["components"].get("v", False):
string += "👄 [b]Verbal[/b] | "
if spell["components"].get("s", False):
string += "🤙 [b]Somatic[/b] | "
if spell["components"].get("r", False):
# TODO: wtf is this
string += "❓ [b]R...?[/b] | "
if spell["components"].get("m", False):
if "text" in spell["components"]["m"]:
string += f'💎 [b]Material[/b] ([i]{spell["components"]["m"]["text"]}[/i]) | '
else:
string += f'💎 [b]Material[/b] ([i]{spell["components"]["m"]}[/i]) | '
string = string.rstrip(" ").rstrip("|")
string += "\n"
string += "\n"
if "duration" in spell:
for duration in spell["duration"]:
if duration["type"] == "timed":
string += f'Duration: 🕒 [b]{duration["duration"]["amount"]} {duration["duration"]["type"]}[/b]'
elif duration["type"] == "instant":
string += 'Duration: ☁️ [b]Instantaneous[/b]'
elif duration["type"] == "special":
string += 'Duration: ⭐️ [b]Special[/b]'
elif duration["type"] == "permanent":
string += f"Duration: ♾ [b]Permanent[/b] (ends on {andformat(duration['ends'], final=' or ')})"
else:
string += f'Duration: ⚠️[b]UNKNOWN[/b]'
if duration.get("concentration", False):
string += " (requires concentration)"
string += "\n"
if "meta" in spell:
if spell["meta"].get("ritual", False):
string += "🔮 Can be casted as ritual\n"
string += "\n"
for entry in spell.get("entries", []):
string += parse_5etools_entry(entry)
string += "\n\n"
for entry in spell.get("entriesHigherLevel", []):
string += parse_5etools_entry(entry)
string += "\n\n"
return string
async def run(self, args: CommandArgs, data: CommandData) -> None:
if self._dnddata is None:
await data.reply("⚠️ Il database degli oggetti di D&D non è ancora stato scaricato.")
return
search = args.joined().lower()
result = self._dnddata[self._dnddata.bisect_key_left(search)]
await data.reply(self._parse_spell(result))

28
rpgpack/commands/roll.py Normal file
View file

@ -0,0 +1,28 @@
import typing
import random
from royalnet.commands import *
class RollCommand(Command):
name: str = "roll"
description: str = "Roll a dice, from N to M (defaults to 1-100)."
syntax = "[min] [max]"
aliases = ["r", "random"]
async def run(self, args: CommandArgs, data: CommandData) -> None:
first: typing.Optional[str] = args.optional(0)
second: typing.Optional[str] = args.optional(1)
if second:
minimum = int(first)
maximum = int(second)
elif first:
minimum = 1
maximum = int(first)
else:
minimum = 1
maximum = 100
result = random.randrange(minimum, maximum+1)
await data.reply(f"🎲 Dice roll [{minimum}-{maximum}]: [b]{result}[/b]")

View file

@ -3,7 +3,7 @@
# Enter the PageStars of your Pack here!
available_page_stars = [
]
# Enter the ExceptionStars of your Pack here!
@ -12,5 +12,4 @@ available_exception_stars = [
]
# Don't change this, it should automatically generate __all__
__all__ = [command.__name__ for command in [*available_page_stars, *available_exception_stars]]
__all__ = [star.__name__ for star in [*available_page_stars, *available_exception_stars]]

View file

@ -1,9 +1,11 @@
# Imports go here!
from .dndactivecharacters import DndActiveCharacter
from .dndcharacters import DndCharacter
# Enter the tables of your Pack here!
available_tables = [
DndActiveCharacter,
DndCharacter,
]
# Don't change this, it should automatically generate __all__

View file

@ -0,0 +1,26 @@
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import *
class DndActiveCharacter:
__tablename__ = "dndactivecharacters"
@declared_attr
def character_id(self):
return Column(Integer, ForeignKey("dndcharacters.character_id"), primary_key=True)
@declared_attr
def user_id(self):
return Column(Integer, ForeignKey("users.uid"), primary_key=True)
@declared_attr
def character(self):
return relationship("DndCharacter", foreign_keys=self.character_id, backref="activated_by")
@declared_attr
def user(self):
return relationship("User", foreign_keys=self.user_id, backref=backref("dnd_active_character", uselist=False))
def __repr__(self):
return f"<{self.__class__.__qualname__} for {self.user_id}: {self.character_id}>"

View file

@ -0,0 +1,301 @@
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import *
from ..utils import DndProficiencyType
class DndCharacter:
__tablename__ = "dndcharacters"
@declared_attr
def character_id(self):
return Column(Integer, primary_key=True)
@declared_attr
def creator_id(self):
return Column(Integer, ForeignKey("users.uid"))
@declared_attr
def creator(self):
return relationship("User", foreign_keys=self.creator_id, backref="dndcharacters_created")
@declared_attr
def name(self):
return Column(String, nullable=False)
@declared_attr
def strength_score(self):
return Column(Integer, nullable=False)
@property
def strength(self):
return (self.strength_score - 10) // 2
@declared_attr
def dexterity_score(self):
return Column(Integer, nullable=False)
@property
def dexterity(self):
return (self.dexterity_score - 10) // 2
@declared_attr
def constitution_score(self):
return Column(Integer, nullable=False)
@property
def constitution(self):
return (self.constitution_score - 10) // 2
@declared_attr
def intelligence_score(self):
return Column(Integer, nullable=False)
@property
def intelligence(self):
return (self.intelligence_score - 10) // 2
@declared_attr
def wisdom_score(self):
return Column(Integer, nullable=False)
@property
def wisdom(self):
return (self.wisdom_score - 10) // 2
@declared_attr
def charisma_score(self):
return Column(Integer, nullable=False)
@property
def charisma(self):
return (self.charisma_score - 10) // 2
@declared_attr
def level(self):
return Column(Integer, nullable=False)
@property
def proficiency_bonus(self):
return ((self.level - 1) // 4) + 2
@declared_attr
def current_hp(self):
return Column(Integer, nullable=False)
@declared_attr
def max_hp(self):
return Column(Integer, nullable=False)
@declared_attr
def armor_class(self):
return Column(Integer, nullable=False)
@declared_attr
def strength_save_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def dexterity_save_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def constitution_save_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def intelligence_save_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def wisdom_save_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def charisma_save_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def acrobatics_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def animal_handling_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def arcana_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def athletics_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def deception_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def history_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def insight_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def intimidation_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def investigation_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def medicine_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def nature_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def perception_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def performance_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def persuasion_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def religion_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def sleight_of_hand_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def stealth_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@declared_attr
def survival_proficiency(self):
return Column(Enum(DndProficiencyType), nullable=False, default=DndProficiencyType.NONE)
@property
def strength_save(self):
return self.strength + self.proficiency_bonus * self.strength_save_proficiency.value
@property
def dexterity_save(self):
return self.dexterity + self.proficiency_bonus * self.dexterity_save_proficiency.value
@property
def constitution_save(self):
return self.constitution + self.proficiency_bonus * self.constitution_save_proficiency.value
@property
def intelligence_save(self):
return self.intelligence + self.proficiency_bonus * self.intelligence_save_proficiency.value
@property
def wisdom_save(self):
return self.wisdom + self.proficiency_bonus * self.wisdom_save_proficiency.value
@property
def charisma_save(self):
return self.charisma + self.proficiency_bonus * self.charisma_save_proficiency.value
@property
def acrobatics(self):
return self.dexterity + self.proficiency_bonus * self.acrobatics_proficiency.value
@property
def animal_handling(self):
return self.wisdom + self.proficiency_bonus * self.animal_handling_proficiency.value
@property
def arcana(self):
return self.intelligence + self.proficiency_bonus * self.arcana_proficiency.value
@property
def athletics(self):
return self.strength + self.proficiency_bonus * self.athletics_proficiency.value
@property
def deception(self):
return self.charisma + self.proficiency_bonus * self.deception_proficiency.value
@property
def history(self):
return self.intelligence + self.proficiency_bonus * self.history_proficiency.value
@property
def insight(self):
return self.wisdom + self.proficiency_bonus * self.insight_proficiency.value
@property
def intimidation(self):
return self.charisma + self.proficiency_bonus * self.intimidation_proficiency.value
@property
def investigation(self):
return self.intelligence + self.proficiency_bonus * self.investigation_proficiency.value
@property
def medicine(self):
return self.wisdom + self.proficiency_bonus * self.medicine_proficiency.value
@property
def nature(self):
return self.intelligence + self.proficiency_bonus * self.nature_proficiency.value
@property
def perception(self):
return self.wisdom + self.proficiency_bonus * self.perception_proficiency.value
@property
def performance(self):
return self.charisma + self.proficiency_bonus * self.performance_proficiency.value
@property
def persuasion(self):
return self.charisma + self.proficiency_bonus * self.persuasion_proficiency.value
@property
def religion(self):
return self.intelligence + self.proficiency_bonus * self.religion_proficiency.value
@property
def sleight_of_hand(self):
return self.dexterity + self.proficiency_bonus * self.sleight_of_hand_proficiency.value
@property
def stealth(self):
return self.dexterity + self.proficiency_bonus * self.stealth_proficiency.value
@property
def survival(self):
return self.wisdom + self.proficiency_bonus * self.survival_proficiency.value
def __repr__(self):
return f"<{self.__class__.__qualname__} {self.name}>"
def __str__(self):
return f"{self.name}"
def character_sheet(self) -> str:
columns = list(self.__class__.__table__.columns)
column_names = [column.name for column in columns if (not column.primary_key and
not column.foreign_keys and
column.name != "name")]
message = f"[b]{self.name}[/b]\n"
for column_name in column_names:
value = self.__getattribute__(column_name)
message += f"{column_name} {value}\n"
return message

View file

@ -0,0 +1,4 @@
from .dndproficiencytype import DndProficiencyType
from .parse5etoolsentry import parse_5etools_entry
__all__ = ["DndProficiencyType", "parse_5etools_entry"]

View file

@ -0,0 +1,11 @@
import enum
class DndProficiencyType(enum.Enum):
NONE = 0
HALF_PROFICIENCY = 0.5
FULL_PROFICIENCY = 1
EXPERTISE = 2
def __str__(self):
return str(self.value)

View file

@ -0,0 +1,31 @@
def parse_5etools_entry(entry) -> str:
if isinstance(entry, str):
return entry
elif isinstance(entry, dict):
string = ""
if entry["type"] == "entries":
string += f'[b]{entry.get("name", "")}[/b]\n'
for subentry in entry["entries"]:
string += parse_5etools_entry(subentry)
string += "\n\n"
elif entry["type"] == "table":
string += "[i][table hidden][/i]"
# for label in entry["colLabels"]:
# string += f"| {label} "
# string += "|"
# for row in entry["rows"]:
# for column in row:
# string += f"| {self._parse_entry(column)} "
# string += "|\n"
elif entry["type"] == "cell":
return parse_5etools_entry(entry["entry"])
elif entry["type"] == "list":
string = ""
for item in entry["items"]:
string += f"- {parse_5etools_entry(item)}\n"
string.rstrip("\n")
else:
string += "[i]⚠️ [unknown type][/i]"
else:
return "[/i]⚠️ [unknown data][/i]"
return string

4
rpgpack/version.py Normal file
View file

@ -0,0 +1,4 @@
semantic = "5.0a93"
if __name__ == "__main__":
print(semantic)

View file

@ -1,4 +1,5 @@
import setuptools
import rpgpack.version
with open("README.md", "r") as f:
long_description = f.read()
@ -7,14 +8,14 @@ with open("requirements.txt", "r") as f:
install_requires = f.readlines()
setuptools.setup(
name="{packname}",
version="0.1",
author="{packauthorname}",
author_email="{packauthoremail}",
description="{packdescription}",
name="rpgpack",
version=rpgpack.version.semantic,
author="Stefano Pigozzi",
author_email="ste.pigozzi@gmail.com",
description="A Royalnet pack to play D&D by-chat",
long_description=long_description,
long_description_content_type="text/markdown",
url="{packgithublink}",
url="https://github.com/Steffo99/rpgpack",
packages=setuptools.find_packages(),
install_requires=install_requires,
python_requires=">=3.7",

View file

@ -1,10 +0,0 @@
# Imports go here!
# Enter the commands of your Pack here!
available_commands = [
]
# Don't change this, it should automatically generate __all__
__all__ = [command.__name__ for command in available_commands]