checkpoint

This commit is contained in:
evilchili 2023-08-20 12:07:10 -07:00
parent 54c63834c7
commit ffc7be0f36
6 changed files with 159 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -32,10 +32,13 @@ dnd-npcs = { file = "../../dnd-npcs/dist/dnd_npcs-0.2.0-py3-none-any.whl" }
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" }
dnd-calendar = { path = "../../dnd-calendar" }
prompt-toolkit = "^3.0.38" prompt-toolkit = "^3.0.38"
[tool.poetry.scripts] [tool.poetry.scripts]
site = "site_tools.cli:app" site = "site_tools.cli:site_app"
roll-table = "rolltable.cli:app" roll-table = "rolltable.cli:app"
pelican = "site_tools.tasks:pelican_main" pelican = "site_tools.tasks:pelican_main"
dmsh = "site_tools.cli:dmsh" dmsh = "site_tools.cli:dmsh"

View File

@ -37,6 +37,11 @@ CONFIG.update(
"production_host": "deadsands.froghat.club", "production_host": "deadsands.froghat.club",
# where to find roll table sources # where to find roll table sources
"table_sources_path": "sources", "table_sources_path": "sources",
# where to store campaign state
"campaign_save_path": '~/.dnd',
"campaign_name": "deadsands",
# campaign start date
"campaign_start_date": "2.1125.5.25",
} }
) )

View File

@ -0,0 +1,82 @@
from collections import defaultdict
from pathlib import Path
import shutil
import yaml
from telisar.reckoning import telisaran
def _string_to_date(date):
return telisaran.datetime.from_expression(f"on {date}", timeline={})
def _date_to_string(date):
return date.numeric
def _rotate_backups(path, max_backups=10):
oldest = None
if not path.exists():
return oldest
# move file.000 to file.001, file.001 to file.002, etc...
for i in range(max_backups - 2, -1, -1):
source = Path(f"{path}.{i:03d}")
target = Path(f"{path}.{i+1:03d}")
if not source.exists():
continue
if oldest is None:
oldest = i
if i == max_backups:
source.unlink()
shutil.move(source, target)
return oldest
def save(campaign, path='.', name='dnd_campaign'):
savedir = Path(path).expanduser()
savepath = savedir / f"{name}.yaml"
savedir.mkdir(exist_ok=True)
backup_count = _rotate_backups(savepath)
if savepath.exists():
target = Path(f"{savepath}.000")
shutil.move(savepath, target)
campaign['date'] = _date_to_string(campaign['date'])
campaign['start_date'] = _date_to_string(campaign['start_date'])
savepath.write_text(yaml.safe_dump(dict(campaign)))
return savepath, (backup_count or 0) + 2
def load(path=".", name='dnd_campaign', start_date='', backup=None, console=None):
ext = "" if backup is None else f".{backup:03d}"
default_date = _string_to_date(start_date)
campaign = defaultdict(str)
campaign['start_date'] = default_date
campaign['date'] = default_date
if console:
console.print(f"Loading campaign {name} from {path}...")
try:
target = Path(path).expanduser() / f"{name}.yaml{ext}"
with open(target, 'rb') as f:
loaded = yaml.safe_load(f)
loaded['start_date'] = _string_to_date(loaded['start_date'])
loaded['date'] = _string_to_date(loaded['date'])
campaign.update(loaded)
if console:
console.print(f"Successfully loaded Campaign {name} from {target}!")
return campaign
except FileNotFoundError:
console.print(f"No existing campaigns found in {path}.")
return campaign
except yaml.parser.ParserError as e:
if console:
console.print(f"{e}\nWill try an older backup.")
return load(path, 0 if backup is None else backup+1)

View File

@ -7,8 +7,10 @@ from rich.table import Table
from rolltable.tables import RollTable from rolltable.tables import RollTable
from site_tools.shell.base import BasePrompt, command from site_tools.shell.base import BasePrompt, command
from site_tools import campaign
from npc.generator.base import generate_npc from npc.generator.base import generate_npc
from reckoning.calendar import TelisaranCalendar
BINDINGS = KeyBindings() BINDINGS = KeyBindings()
@ -23,12 +25,22 @@ class DMShell(BasePrompt):
self._register_subshells() self._register_subshells()
self._register_keybindings() self._register_keybindings()
self.cache['campaign'] = campaign.load(
path=self.cache['campaign_save_path'],
name=self.cache['campaign_name'],
start_date=self.cache['campaign_start_date'],
console=self.console
)
self._campaign = self.cache['campaign']
def _register_keybindings(self): def _register_keybindings(self):
self._toolbar.extend( self._toolbar.extend(
[ [
("", " [?] Help "), ("", " [?] Help "),
("", " [F2] Wild Magic Table "), ("", " [F2] Wild Magic Table "),
("", " [F3] NPC"), ("", " [F3] NPC"),
("", " [F4] Calendar"),
("", " [F8] Save"),
("", " [^Q] Quit "), ("", " [^Q] Quit "),
] ]
) )
@ -51,6 +63,61 @@ class DMShell(BasePrompt):
def npc(event): def npc(event):
self.npc() self.npc()
@self.key_bindings.add("f4")
def calendar(event):
self.calendar()
@self.key_bindings.add("f8")
def save(event):
self.save()
@command(usage="""
[title]Calendar[/title]
Print the Telisaran calendar, including the current date.
[title]calendar[/title]
[link]> calendar [season][/link]
""", completer=WordCompleter(
[
'season',
]
))
def calendar(self, parts=[]):
if not self.cache['calendar']:
self.cache['calendar'] = TelisaranCalendar(today=self._campaign['start_date'])
if not parts:
self.console.print(self.cache['calendar'].__doc__)
self.console.print(f"Today is {self._campaign['date'].short}")
return
if parts[0] == 'season':
self.console.print(self.cache['calendar'].season)
return
@command(usage="""
[title]Save[/title]
Save the campaign state.
[title]USAGE[/title]
[link]> save[/link]
""")
def save(self, parts=[]):
"""
Save the campaign state.
"""
path, count = campaign.save(
self.cache['campaign'],
path=self.cache['campaign_save_path'],
name=self.cache['campaign_name']
)
self.console.print(f"Saved {path}; {count} backups exist.")
@command(usage=""" @command(usage="""
[title]NPC[/title] [title]NPC[/title]
@ -104,6 +171,7 @@ class DMShell(BasePrompt):
""" """
Quit dmsh. Quit dmsh.
""" """
self.save()
try: try:
get_app().exit() get_app().exit()
finally: finally: