mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
Second commit
This commit is contained in:
parent
15a3cb84eb
commit
1bd40ef9cd
22 changed files with 987 additions and 23 deletions
|
@ -0,0 +1,4 @@
|
||||||
|
royalnet>=5.0a1
|
||||||
|
aiohttp>=3.6.2
|
||||||
|
dice>=2.4.2
|
||||||
|
sortedcontainers>=2.1.0
|
|
@ -1,6 +1,6 @@
|
||||||
# This is a template Pack __init__. You can use this without changing anything in other packages too!
|
# 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 .commands import available_commands
|
||||||
from .tables import available_tables
|
from .tables import available_tables
|
||||||
from .stars import available_page_stars, available_exception_stars
|
from .stars import available_page_stars, available_exception_stars
|
||||||
|
@ -9,9 +9,9 @@ __all__ = [
|
||||||
"commands",
|
"commands",
|
||||||
"tables",
|
"tables",
|
||||||
"stars",
|
"stars",
|
||||||
|
"version",
|
||||||
"available_commands",
|
"available_commands",
|
||||||
"available_tables",
|
"available_tables",
|
||||||
"available_page_stars",
|
"available_page_stars",
|
||||||
"available_exception_stars",
|
"available_exception_stars",
|
||||||
]
|
]
|
||||||
|
|
26
rpgpack/commands/__init__.py
Normal file
26
rpgpack/commands/__init__.py
Normal 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
40
rpgpack/commands/dice.py
Normal 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)
|
56
rpgpack/commands/dndactive.py
Normal file
56
rpgpack/commands/dndactive.py
Normal 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]!")
|
35
rpgpack/commands/dndedit.py
Normal file
35
rpgpack/commands/dndedit.py
Normal 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!")
|
19
rpgpack/commands/dndinfo.py
Normal file
19
rpgpack/commands/dndinfo.py
Normal 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())
|
56
rpgpack/commands/dnditem.py
Normal file
56
rpgpack/commands/dnditem.py
Normal 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)
|
71
rpgpack/commands/dndnew.py
Normal file
71
rpgpack/commands/dndnew.py
Normal 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
146
rpgpack/commands/dndroll.py
Normal 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]")
|
114
rpgpack/commands/dndspell.py
Normal file
114
rpgpack/commands/dndspell.py
Normal 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
28
rpgpack/commands/roll.py
Normal 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]")
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
# Enter the PageStars of your Pack here!
|
# Enter the PageStars of your Pack here!
|
||||||
available_page_stars = [
|
available_page_stars = [
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Enter the ExceptionStars of your Pack here!
|
# Enter the ExceptionStars of your Pack here!
|
||||||
|
@ -12,5 +12,4 @@ available_exception_stars = [
|
||||||
]
|
]
|
||||||
|
|
||||||
# Don't change this, it should automatically generate __all__
|
# 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]]
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
# Imports go here!
|
# Imports go here!
|
||||||
|
from .dndactivecharacters import DndActiveCharacter
|
||||||
|
from .dndcharacters import DndCharacter
|
||||||
|
|
||||||
# Enter the tables of your Pack here!
|
# Enter the tables of your Pack here!
|
||||||
available_tables = [
|
available_tables = [
|
||||||
|
DndActiveCharacter,
|
||||||
|
DndCharacter,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Don't change this, it should automatically generate __all__
|
# Don't change this, it should automatically generate __all__
|
26
rpgpack/tables/dndactivecharacters.py
Normal file
26
rpgpack/tables/dndactivecharacters.py
Normal 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}>"
|
301
rpgpack/tables/dndcharacters.py
Normal file
301
rpgpack/tables/dndcharacters.py
Normal 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
|
4
rpgpack/utils/__init__.py
Normal file
4
rpgpack/utils/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from .dndproficiencytype import DndProficiencyType
|
||||||
|
from .parse5etoolsentry import parse_5etools_entry
|
||||||
|
|
||||||
|
__all__ = ["DndProficiencyType", "parse_5etools_entry"]
|
11
rpgpack/utils/dndproficiencytype.py
Normal file
11
rpgpack/utils/dndproficiencytype.py
Normal 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)
|
31
rpgpack/utils/parse5etoolsentry.py
Normal file
31
rpgpack/utils/parse5etoolsentry.py
Normal 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
4
rpgpack/version.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
semantic = "5.0a93"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(semantic)
|
13
setup.py
13
setup.py
|
@ -1,4 +1,5 @@
|
||||||
import setuptools
|
import setuptools
|
||||||
|
import rpgpack.version
|
||||||
|
|
||||||
with open("README.md", "r") as f:
|
with open("README.md", "r") as f:
|
||||||
long_description = f.read()
|
long_description = f.read()
|
||||||
|
@ -7,14 +8,14 @@ with open("requirements.txt", "r") as f:
|
||||||
install_requires = f.readlines()
|
install_requires = f.readlines()
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="{packname}",
|
name="rpgpack",
|
||||||
version="0.1",
|
version=rpgpack.version.semantic,
|
||||||
author="{packauthorname}",
|
author="Stefano Pigozzi",
|
||||||
author_email="{packauthoremail}",
|
author_email="ste.pigozzi@gmail.com",
|
||||||
description="{packdescription}",
|
description="A Royalnet pack to play D&D by-chat",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="{packgithublink}",
|
url="https://github.com/Steffo99/rpgpack",
|
||||||
packages=setuptools.find_packages(),
|
packages=setuptools.find_packages(),
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
python_requires=">=3.7",
|
python_requires=">=3.7",
|
||||||
|
|
|
@ -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]
|
|
Loading…
Reference in a new issue