Initial setup of dmsh
This commit is contained in:
parent
b70e416a3f
commit
cbba29012b
|
@ -30,6 +30,7 @@ dnd-npcs = { git = "https://github.com/evilchili/dnd-npcs", branch = 'main'
|
||||||
elethis-cipher= { git = "https://github.com/evilchili/elethis-cipher", branch = 'main' }
|
elethis-cipher= { git = "https://github.com/evilchili/elethis-cipher", branch = 'main' }
|
||||||
#dnd-rolltable = { git = "https://github.com/evilchili/dnd-rolltable", branch = 'main' }
|
#dnd-rolltable = { git = "https://github.com/evilchili/dnd-rolltable", branch = 'main' }
|
||||||
dnd-rolltable = { file = "../../dnd-rolltable/dist/dnd_rolltable-1.1.9-py3-none-any.whl" }
|
dnd-rolltable = { file = "../../dnd-rolltable/dist/dnd_rolltable-1.1.9-py3-none-any.whl" }
|
||||||
|
prompt-toolkit = "^3.0.38"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
site = "site_tools.cli:app"
|
site = "site_tools.cli:app"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import asyncio
|
||||||
import click
|
import click
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -15,8 +16,11 @@ from livereload.watcher import INotifyWatcher
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pelican import main as pelican_main
|
from pelican import main as pelican_main
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from typing_extensions import Annotated
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from site_tools.content_manager import create
|
from site_tools.content_manager import create
|
||||||
|
from site_tools.shell.interactive_shell import InteractiveShell
|
||||||
from rolltable.tables import RollTable
|
from rolltable.tables import RollTable
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +43,8 @@ CONFIG = {
|
||||||
'import_path': 'imports',
|
'import_path': 'imports',
|
||||||
# where new asseets will be made available
|
# where new asseets will be made available
|
||||||
'production_host': 'deadsands.froghat.club',
|
'production_host': 'deadsands.froghat.club',
|
||||||
|
# where to find roll table sources
|
||||||
|
'table_sources_path': 'sources',
|
||||||
}
|
}
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
@ -53,6 +59,15 @@ class ContentType(str, Enum):
|
||||||
page = 'page'
|
page = 'page'
|
||||||
|
|
||||||
|
|
||||||
|
class Die(str, Enum):
|
||||||
|
d100 = '100'
|
||||||
|
d20 = '20'
|
||||||
|
d12 = '12'
|
||||||
|
d10 = '10'
|
||||||
|
d6 = '6'
|
||||||
|
d4 = '4'
|
||||||
|
|
||||||
|
|
||||||
def pelican_run(cmd: list = [], publish=False) -> None:
|
def pelican_run(cmd: list = [], publish=False) -> None:
|
||||||
settings = CONFIG['settings_publish' if publish else 'settings_base']
|
settings = CONFIG['settings_publish' if publish else 'settings_base']
|
||||||
pelican_main(['-s', settings] + cmd)
|
pelican_main(['-s', settings] + cmd)
|
||||||
|
@ -165,16 +180,24 @@ def restock(source: str = typer.Argument(
|
||||||
...,
|
...,
|
||||||
help='The source file for the store.'
|
help='The source file for the store.'
|
||||||
),
|
),
|
||||||
frequency: str = typer.Option(
|
frequency: str = Annotated[
|
||||||
'default',
|
str,
|
||||||
help='use the specified frequency from the source file'),
|
typer.Option(
|
||||||
die: int = typer.Option(
|
'default',
|
||||||
|
help='use the specified frequency from the source file'
|
||||||
|
)
|
||||||
|
],
|
||||||
|
die: Die = typer.Option(
|
||||||
20,
|
20,
|
||||||
help='The size of the die for which to create a table'),
|
help='The size of the die for which to create a table'
|
||||||
template_dir: str = typer.Argument(
|
),
|
||||||
CONFIG['templates_path'],
|
template_dir: str = Annotated[
|
||||||
help="Override the default location for markdown templates.",
|
str,
|
||||||
)
|
typer.Argument(
|
||||||
|
CONFIG['templates_path'],
|
||||||
|
help="Override the default location for markdown templates.",
|
||||||
|
)
|
||||||
|
],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
rt = RollTable(
|
rt = RollTable(
|
||||||
|
@ -240,5 +263,25 @@ def new(
|
||||||
category, template or content_type.value))
|
category, template or content_type.value))
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def dmsh():
|
||||||
|
import termios, sys
|
||||||
|
|
||||||
|
session = defaultdict(dict)
|
||||||
|
prompt = InteractiveShell(
|
||||||
|
[
|
||||||
|
"[title]DM's Shell.[/title]",
|
||||||
|
'dmsh'
|
||||||
|
], config=CONFIG, session=session
|
||||||
|
)
|
||||||
|
|
||||||
|
# ensure the terminal is restored on exit.
|
||||||
|
old_attrs = termios.tcgetattr(sys.stdin)
|
||||||
|
try:
|
||||||
|
asyncio.run(prompt.start())
|
||||||
|
finally:
|
||||||
|
termios.tcsetattr(sys.stdin, termios.TCSANOW, old_attrs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app()
|
app()
|
||||||
|
|
170
deadsands/site_tools/console.py
Normal file
170
deadsands/site_tools/console.py
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from pathlib import Path
|
||||||
|
from textwrap import dedent
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
import rich.repr
|
||||||
|
|
||||||
|
from rich.console import Console as _Console
|
||||||
|
from rich.markdown import Markdown
|
||||||
|
from rich.theme import Theme
|
||||||
|
from rich.table import Table, Column
|
||||||
|
|
||||||
|
from prompt_toolkit import PromptSession
|
||||||
|
from prompt_toolkit.patch_stdout import patch_stdout
|
||||||
|
from prompt_toolkit.formatted_text import ANSI
|
||||||
|
from prompt_toolkit.styles import Style
|
||||||
|
from prompt_toolkit.output import ColorDepth
|
||||||
|
|
||||||
|
|
||||||
|
BASE_STYLE = {
|
||||||
|
'help': 'cyan',
|
||||||
|
'bright': 'white',
|
||||||
|
'repr.str': 'dim',
|
||||||
|
'repr.brace': 'dim',
|
||||||
|
'repr.url': 'blue',
|
||||||
|
'table.header': 'white',
|
||||||
|
'toolbar.fg': '#888888',
|
||||||
|
'toolbar.bg': '#111111',
|
||||||
|
'toolbar.bold': '#FFFFFF',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def console_theme(theme_name: Union[str, None] = None) -> dict:
|
||||||
|
"""
|
||||||
|
Return a console theme as a dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
theme_name (str):
|
||||||
|
"""
|
||||||
|
cfg = ConfigParser()
|
||||||
|
cfg.read_dict({'styles': BASE_STYLE})
|
||||||
|
|
||||||
|
if theme_name:
|
||||||
|
theme_path = theme_name if theme_name else os.environ.get('DEFAULT_THEME', 'blue_train')
|
||||||
|
cfg.read(Theme(
|
||||||
|
Path(theme_path) / Path('console.cfg')
|
||||||
|
))
|
||||||
|
return cfg['styles']
|
||||||
|
|
||||||
|
|
||||||
|
@rich.repr.auto
|
||||||
|
class Console(_Console):
|
||||||
|
"""
|
||||||
|
SYNOPSIS
|
||||||
|
|
||||||
|
Subclasses a rich.console.Console to provide an instance with a
|
||||||
|
reconfigured themes, and convenience methods and attributes.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
|
||||||
|
Console([ARGS])
|
||||||
|
|
||||||
|
ARGS
|
||||||
|
|
||||||
|
theme The name of a theme to load. Defaults to DEFAULT_THEME.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
|
||||||
|
Console().print("Can I kick it?")
|
||||||
|
>>> Can I kick it?
|
||||||
|
|
||||||
|
INSTANCE ATTRIBUTES
|
||||||
|
|
||||||
|
theme The current theme
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._console_theme = console_theme(kwargs.get('theme', None))
|
||||||
|
self._overflow = 'ellipsis'
|
||||||
|
kwargs['theme'] = Theme(self._console_theme, inherit=False)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._session = PromptSession()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def theme(self) -> Theme:
|
||||||
|
return self._console_theme
|
||||||
|
|
||||||
|
def prompt(self, lines: List, **kwargs) -> str:
|
||||||
|
"""
|
||||||
|
Print a list of lines, using the final line as a prompt.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
Console().prompt(["Can I kick it?", "[Y/n] ")
|
||||||
|
>>> Can I kick it?
|
||||||
|
[Y/n]>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
prompt_style = Style.from_dict({
|
||||||
|
# 'bottom-toolbar': f"{self.theme['toolbar.fg']} bg:{self.theme['toolbar.bg']}",
|
||||||
|
# 'toolbar-bold': f"{self.theme['toolbar.bold']}"
|
||||||
|
})
|
||||||
|
|
||||||
|
for line in lines[:-1]:
|
||||||
|
super().print(line)
|
||||||
|
with self.capture() as capture:
|
||||||
|
super().print(f"[prompt bold]{lines[-1]}>[/] ", end='')
|
||||||
|
text = ANSI(capture.get())
|
||||||
|
|
||||||
|
# This is required to intercept key bindings and not mess up the
|
||||||
|
# prompt. Both the prompt and bottom_toolbar params must be functions
|
||||||
|
# for this to correctly regenerate the prompt after the interrupt.
|
||||||
|
with patch_stdout(raw=True):
|
||||||
|
return self._session.prompt(
|
||||||
|
lambda: text,
|
||||||
|
style=prompt_style,
|
||||||
|
color_depth=ColorDepth.TRUE_COLOR,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def mdprint(self, txt: str, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Like print(), but support markdown. Text will be dedented.
|
||||||
|
"""
|
||||||
|
self.print(Markdown(dedent(txt), justify='left'), **kwargs)
|
||||||
|
|
||||||
|
def print(self, txt: str, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Print text to the console, possibly truncated with an ellipsis.
|
||||||
|
"""
|
||||||
|
super().print(txt, overflow=self._overflow, **kwargs)
|
||||||
|
|
||||||
|
def debug(self, txt: str, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Print text to the console with the current theme's debug style applied, if debugging is enabled.
|
||||||
|
"""
|
||||||
|
if os.environ.get('DEBUG', None):
|
||||||
|
self.print(dedent(txt), style='debug')
|
||||||
|
|
||||||
|
def error(self, txt: str, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Print text to the console with the current theme's error style applied.
|
||||||
|
"""
|
||||||
|
self.print(dedent(txt), style='error')
|
||||||
|
|
||||||
|
def table(self, *cols: List[Column], **params) -> None:
|
||||||
|
"""
|
||||||
|
Print a rich table to the console with theme elements and styles applied.
|
||||||
|
parameters and keyword arguments are passed to rich.table.Table.
|
||||||
|
"""
|
||||||
|
background_style = f"on {self.theme['background']}"
|
||||||
|
params.update(
|
||||||
|
header_style=background_style,
|
||||||
|
title_style=background_style,
|
||||||
|
border_style=background_style,
|
||||||
|
row_styles=[background_style],
|
||||||
|
caption_style=background_style,
|
||||||
|
style=background_style,
|
||||||
|
)
|
||||||
|
params['min_width'] = 80
|
||||||
|
width = os.environ.get('CONSOLE_WIDTH', 'auto')
|
||||||
|
if width == 'expand':
|
||||||
|
params['expand'] = True
|
||||||
|
elif width != 'auto':
|
||||||
|
params['width'] = int(width)
|
||||||
|
return Table(*cols, **params)
|
35
deadsands/site_tools/shell.py
Normal file
35
deadsands/site_tools/shell.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
from prompt_toolkit import PromptSession
|
||||||
|
from prompt_toolkit.completion import NestedCompleter
|
||||||
|
from prompt_toolkit.completion import FuzzyWordCompleter
|
||||||
|
from site_tools.cli import app
|
||||||
|
from enum import EnumMeta
|
||||||
|
from inspect import signature
|
||||||
|
from rich import print
|
||||||
|
|
||||||
|
|
||||||
|
def dmsh():
|
||||||
|
|
||||||
|
session = PromptSession()
|
||||||
|
|
||||||
|
def cmd2dict(cmd):
|
||||||
|
sig = signature(cmd)
|
||||||
|
if not sig.parameters:
|
||||||
|
return None
|
||||||
|
cmds = {}
|
||||||
|
for (k, v) in list(sig.parameters.items()):
|
||||||
|
print(v, dir(v))
|
||||||
|
if v.annotation.__class__ == EnumMeta:
|
||||||
|
cmds[k] = FuzzyWordCompleter([e.value for e in v.annotation])
|
||||||
|
else:
|
||||||
|
cmds[k] = None
|
||||||
|
return cmds
|
||||||
|
|
||||||
|
commands = dict(
|
||||||
|
site=dict((c.callback.__name__, cmd2dict(c.callback)) for c in app.registered_commands)
|
||||||
|
)
|
||||||
|
print(commands)
|
||||||
|
completer = NestedCompleter.from_nested_dict(commands)
|
||||||
|
text = session.prompt("DM> ", completer=completer, complete_while_typing=True, enable_history_search=False)
|
||||||
|
words = text.split()
|
||||||
|
|
||||||
|
print(f"You said {words}")
|
1
deadsands/site_tools/shell/__init__.py
Normal file
1
deadsands/site_tools/shell/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .base import BasePrompt
|
125
deadsands/site_tools/shell/base.py
Normal file
125
deadsands/site_tools/shell/base.py
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import functools
|
||||||
|
from collections import namedtuple, defaultdict
|
||||||
|
|
||||||
|
from prompt_toolkit.completion import NestedCompleter
|
||||||
|
|
||||||
|
|
||||||
|
from site_tools.console import Console
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
COMMANDS = defaultdict(dict)
|
||||||
|
|
||||||
|
Command = namedtuple('Commmand', 'prompt,handler,usage,completer')
|
||||||
|
|
||||||
|
|
||||||
|
def register_command(handler, usage, completer=None):
|
||||||
|
prompt = handler.__qualname__.split('.', -1)[0]
|
||||||
|
cmd = handler.__name__
|
||||||
|
if cmd not in COMMANDS[prompt]:
|
||||||
|
COMMANDS[prompt][cmd] = Command(
|
||||||
|
prompt=prompt,
|
||||||
|
handler=handler,
|
||||||
|
usage=usage,
|
||||||
|
completer=completer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def command(usage, completer=None, binding=None):
|
||||||
|
def decorator(func):
|
||||||
|
register_command(func, usage, completer)
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class BasePrompt(NestedCompleter):
|
||||||
|
|
||||||
|
def __init__(self, console=None):
|
||||||
|
super(BasePrompt, self).__init__(self._nested_completer_map())
|
||||||
|
|
||||||
|
self._prompt = ''
|
||||||
|
self._autocomplete_values = []
|
||||||
|
self._console = None
|
||||||
|
self._theme = None
|
||||||
|
|
||||||
|
def _nested_completer_map(self):
|
||||||
|
return dict(
|
||||||
|
(cmd_name, cmd.completer) for (cmd_name, cmd) in COMMANDS[self.__class__.__name__].items()
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_help(self, cmd=None):
|
||||||
|
try:
|
||||||
|
return dedent(COMMANDS[self.__class__.__name__][cmd].usage)
|
||||||
|
except KeyError:
|
||||||
|
return self.usage
|
||||||
|
|
||||||
|
def default_completer(self, document, complete_event):
|
||||||
|
raise NotImplementedError(f"Implement the 'default_completer' method of {self.__class__.__name__}")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def usage(self):
|
||||||
|
text = dedent("""
|
||||||
|
[title]dmsh[/title]
|
||||||
|
|
||||||
|
Available commands are listed below. Try 'help COMMAND' for detailed help.
|
||||||
|
|
||||||
|
[title]COMMANDS[/title]
|
||||||
|
|
||||||
|
""")
|
||||||
|
for (name, cmd) in sorted(self.commands.items()):
|
||||||
|
text += f" [b]{name:10s}[/b] {cmd.handler.__doc__.strip()}\n"
|
||||||
|
return text
|
||||||
|
|
||||||
|
@property
|
||||||
|
def commands(self):
|
||||||
|
return COMMANDS[self.__class__.__name__]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def console(self):
|
||||||
|
if not self._console:
|
||||||
|
self._console = Console(color_system='truecolor')
|
||||||
|
return self._console
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prompt(self):
|
||||||
|
return self._prompt
|
||||||
|
|
||||||
|
@property
|
||||||
|
def autocomplete_values(self):
|
||||||
|
return self._autocomplete_values
|
||||||
|
|
||||||
|
@property
|
||||||
|
def toolbar(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_bindings(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def help(self, parts):
|
||||||
|
attr = None
|
||||||
|
if parts:
|
||||||
|
attr = parts[0]
|
||||||
|
self.console.print(self._get_help(attr))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def process(self, cmd, *parts):
|
||||||
|
if cmd in self.commands:
|
||||||
|
return self.commands[cmd].handler(self, parts)
|
||||||
|
self.console.error(f"Command {cmd} not understood.")
|
||||||
|
|
||||||
|
def start(self, cmd=None):
|
||||||
|
while True:
|
||||||
|
if not cmd:
|
||||||
|
cmd = self.console.prompt(
|
||||||
|
self.prompt,
|
||||||
|
completer=self,
|
||||||
|
bottom_toolbar=self.toolbar,
|
||||||
|
key_bindings=self.key_bindings)
|
||||||
|
if cmd:
|
||||||
|
cmd, *parts = cmd.split()
|
||||||
|
self.process(cmd, *parts)
|
||||||
|
cmd = None
|
191
deadsands/site_tools/shell/interactive_shell.py
Normal file
191
deadsands/site_tools/shell/interactive_shell.py
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
from site_tools.shell.base import BasePrompt, command
|
||||||
|
from rolltable.tables import RollTable
|
||||||
|
from rich.table import Table
|
||||||
|
from pathlib import Path
|
||||||
|
from prompt_toolkit.completion import WordCompleter
|
||||||
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
|
from prompt_toolkit.application import get_app
|
||||||
|
|
||||||
|
|
||||||
|
bindings = KeyBindings()
|
||||||
|
|
||||||
|
|
||||||
|
class InteractiveShell(BasePrompt):
|
||||||
|
|
||||||
|
def __init__(self, prompt=[], config={}, session={}):
|
||||||
|
super().__init__()
|
||||||
|
self._prompt = prompt
|
||||||
|
self._config = config
|
||||||
|
self._wmt = ""
|
||||||
|
self._subshells = {}
|
||||||
|
self._register_subshells()
|
||||||
|
self._register_keybindings()
|
||||||
|
self._session = session
|
||||||
|
|
||||||
|
def _register_keybindings(self):
|
||||||
|
|
||||||
|
@bindings.add('c-q')
|
||||||
|
@bindings.add('c-d')
|
||||||
|
def quit(event):
|
||||||
|
self.quit()
|
||||||
|
|
||||||
|
@bindings.add('c-h')
|
||||||
|
def help(event):
|
||||||
|
self.help()
|
||||||
|
|
||||||
|
@bindings.add('c-w')
|
||||||
|
def wmt(event):
|
||||||
|
self.wmt()
|
||||||
|
|
||||||
|
def _register_subshells(self):
|
||||||
|
for subclass in BasePrompt.__subclasses__():
|
||||||
|
if subclass.__name__ == self.__class__.__name__:
|
||||||
|
continue
|
||||||
|
self._subshells[subclass.__name__] = subclass(parent=self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_bindings(self):
|
||||||
|
return bindings
|
||||||
|
|
||||||
|
@property
|
||||||
|
def toolbar(self):
|
||||||
|
return [
|
||||||
|
('class:bold', ' DMSH '),
|
||||||
|
('', " [H]elp "),
|
||||||
|
('', " [W]mt "),
|
||||||
|
('', " [Q]uit "),
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def session(self):
|
||||||
|
return self._session
|
||||||
|
|
||||||
|
@property
|
||||||
|
def autocomplete_values(self):
|
||||||
|
return list(self.commands.keys())
|
||||||
|
|
||||||
|
def default_completer(self, document, complete_event): # pragma: no cover
|
||||||
|
word = document.current_line_before_cursor
|
||||||
|
raise Exception(word)
|
||||||
|
|
||||||
|
def process(self, cmd, *parts):
|
||||||
|
if cmd in self.commands:
|
||||||
|
return self.commands[cmd].handler(self, parts)
|
||||||
|
return "Unknown Command; try help."
|
||||||
|
|
||||||
|
@command(usage="""
|
||||||
|
[title]QUIT[/title]
|
||||||
|
|
||||||
|
The [b]quit[/b] command exits dmsh.
|
||||||
|
|
||||||
|
[title]USAGE[/title]
|
||||||
|
|
||||||
|
[link]> quit|^D|<ENTER>[/link]
|
||||||
|
""")
|
||||||
|
def quit(self, *parts):
|
||||||
|
"""
|
||||||
|
Quit dmsh.
|
||||||
|
"""
|
||||||
|
get_app().exit()
|
||||||
|
raise SystemExit("Okay BYEEEE")
|
||||||
|
|
||||||
|
@command(usage="""
|
||||||
|
[title]HELP FOR THE HELP LORD[/title]
|
||||||
|
|
||||||
|
The [b]help[/b] command will print usage information for whatever you're currently
|
||||||
|
doing. You can also ask for help on any command currently available.
|
||||||
|
|
||||||
|
[title]USAGE[/title]
|
||||||
|
|
||||||
|
[link]> help [COMMAND][/link]
|
||||||
|
""")
|
||||||
|
def help(self, *parts):
|
||||||
|
"""
|
||||||
|
Display the help message.
|
||||||
|
"""
|
||||||
|
super().help(parts)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@command(usage="""
|
||||||
|
[title]INCREMENT DATE[/title]
|
||||||
|
|
||||||
|
[b]id[/b] Increments the calendar date by one day.
|
||||||
|
|
||||||
|
[title]USAGE[/title]
|
||||||
|
|
||||||
|
[link]id[/link]
|
||||||
|
""")
|
||||||
|
def id(self, *parts):
|
||||||
|
"""
|
||||||
|
Increment the date by one day.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@command(usage="""
|
||||||
|
[title]LOCATION[/title]
|
||||||
|
|
||||||
|
[b]loc[/b] sets the party's location to the specified region of the Sahwat Desert.
|
||||||
|
|
||||||
|
[title]USAGE[/title]
|
||||||
|
|
||||||
|
[link]loc LOCATION[/link]
|
||||||
|
""",
|
||||||
|
completer=WordCompleter([
|
||||||
|
"The Blooming Wastes",
|
||||||
|
"Dust River Canyon",
|
||||||
|
"Gopher Gulch",
|
||||||
|
"Calamity Ridge"
|
||||||
|
]))
|
||||||
|
def loc(self, *parts):
|
||||||
|
"""
|
||||||
|
Move the party to a new region of the Sahwat Desert.
|
||||||
|
"""
|
||||||
|
if parts:
|
||||||
|
self.session['location'] = (' '.join(parts))
|
||||||
|
self.console.print(f"The party is in {self.session['location']}.")
|
||||||
|
|
||||||
|
@command(usage="""
|
||||||
|
[title]OVERLAND TRAVEL[/title]
|
||||||
|
|
||||||
|
[b]ot[/b]
|
||||||
|
|
||||||
|
[title]USAGE[/title]
|
||||||
|
|
||||||
|
[link]ot in[/link]
|
||||||
|
""")
|
||||||
|
def ot(self, *parts):
|
||||||
|
"""
|
||||||
|
Increment the date by one day and record
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@command(usage="""
|
||||||
|
[title]WILD MAGIC TABLE[/title]
|
||||||
|
|
||||||
|
[b]wmt[/b] Generates a d20 wild magic surge roll table. The table will be cached for the session.
|
||||||
|
|
||||||
|
[title]USAGE[/title]
|
||||||
|
|
||||||
|
[link]> wmt[/link]
|
||||||
|
|
||||||
|
[title]CLI[/title]
|
||||||
|
|
||||||
|
[link]roll-table \\
|
||||||
|
sources/sahwat_magic_table.yaml \\
|
||||||
|
--frequency default --die 20[/link]
|
||||||
|
""")
|
||||||
|
def wmt(self, *parts, source='sahwat_magic_table.yaml'):
|
||||||
|
"""
|
||||||
|
Generate a Wild Magic Table for resolving spell effects.
|
||||||
|
"""
|
||||||
|
if not self._wmt:
|
||||||
|
rt = RollTable(
|
||||||
|
[Path(f"{self._config['table_sources_path']}/{source}").read_text()],
|
||||||
|
frequency='default',
|
||||||
|
die=20,
|
||||||
|
)
|
||||||
|
table = Table(*rt.expanded_rows[0])
|
||||||
|
for row in rt.expanded_rows[1:]:
|
||||||
|
table.add_row(*row)
|
||||||
|
self._wmt = table
|
||||||
|
self.console.print(self._wmt)
|
Loading…
Reference in New Issue
Block a user