Added CLI options for controlling text output

This commit is contained in:
evilchili 2025-08-17 19:29:09 -07:00
parent 2cddc902ab
commit ae3d40e91e
3 changed files with 108 additions and 34 deletions

View File

@ -1,13 +1,12 @@
# import logging
import tomllib
from dataclasses import dataclass
from functools import cached_property
from io import StringIO
from pathlib import Path
from textwrap import indent
from typing import List, Union, get_type_hints
from types import SimpleNamespace
import tomllib
from typing import List, Union, get_type_hints
from PIL import Image
@ -40,6 +39,13 @@ class BattleMap(BattleMapType):
map_string: str = ""
metadata: SimpleNamespace = None
tileset: TileSet = None
render_header: bool = True
render_border: bool = True
render_coordinates: bool = True
render_legend: bool = True
render_notes: bool = True
width: int = 0
height: int = 0
@ -62,7 +68,7 @@ class BattleMap(BattleMapType):
line = line.lstrip(" ")
if in_map:
if line and line[0] == "#":
map_data += line[1:] + "\n"
map_data += line[1:].lstrip() + "\n"
continue
in_map = False
metadata += line + "\n"
@ -82,6 +88,8 @@ class BattleMap(BattleMapType):
del self.legend
if hasattr(self, "coordinates"):
del self.coordinates
if hasattr(self, "notes"):
del self.notes
return self.grid
def validate_map_string(self, data: str) -> bool:
@ -127,31 +135,46 @@ class BattleMap(BattleMapType):
@cached_property
def header(self) -> str:
lines = [f"BattleMap: {self.metadata.map['name']} ({self.width}x{self.height})"]
lines.append(('-' * len(lines[0])))
lines = ["", f"BattleMap: {self.metadata.map['name']} ({self.width}x{self.height})"]
lines.append(("-" * len(lines[0])))
for key, value in self.metadata.map.items():
if key in ("name", "description"):
continue
lines.append(f"{key.title()}: {value}")
lines.append("")
return "\n".join(lines)
@cached_property
def footer(self) -> str:
lines = []
if 'description' in self.metadata.map:
if "description" in self.metadata.map:
lines.append(f"{self.metadata.map['description']}")
lines.append("")
lines.append("Legend:")
lines.append(self.legend)
return "\n".join(lines)
@cached_property
def notes(self) -> str:
lines = []
for number in dir(self.metadata):
if not number.isdigit():
continue
location = getattr(self.metadata, number)
lines.append(f"[{number}]{location.get('name', '')}")
if hasattr(location, "notes"):
for note in location.notes:
lines.append(f" {note}")
lines.append("")
return "\n".join(lines)
@cached_property
def legend(self) -> str:
output = ""
locations = False
for char in sorted(set(list(self.map_string)), key=str.lower):
if char in self.tileset.config.legend:
if char in "0123456789":
if char.isdigit():
locations = True
continue
output += f"{char} - {self.tileset.config.legend[char]}\n"
@ -182,24 +205,42 @@ class BattleMap(BattleMapType):
return self.as_string()
def __repr__(self) -> str:
lines = ""
left_coords = self.left_coordinates
i = 0
for line in str(self).splitlines():
lines += f"{left_coords[i].rjust(2, ' ')}{line}".ljust(self.width, " ") + "\n"
i = i + 1
top_break = "" + ("" * (self.width + 2)) + ""
bot_break = "" + ("" * (self.width + 2)) + ""
return "\n".join(
[
"",
self.header,
"",
indent(self.top_coordinates, " " * 5),
indent(top_break, " " * 3),
lines.rstrip(),
indent(bot_break, " " * 3),
"",
indent(self.footer, " ")
]
)
lines = [""]
map_lines = ""
if self.render_coordinates:
left_coords = self.left_coordinates
i = 0
border = "" if self.render_border else " "
for line in str(self).splitlines():
map_lines += f"{left_coords[i].rjust(2, ' ')}{border}{line}".ljust(self.width, " ") + f"{border}\n"
i = i + 1
elif self.render_border:
for line in str(self).splitlines():
map_lines += f"{line}".ljust(self.width, " ") + "\n"
else:
map_lines = str(self)
top_break = bot_break = ""
if self.render_border:
top_break = "" + ("" * self.width) + ""
bot_break = "" + ("" * self.width) + ""
lines = [self.header] if self.render_header else []
if self.render_coordinates:
lines.append(indent(self.top_coordinates, " " * 3))
if top_break:
lines.append(indent(top_break, " " * 2) if self.render_coordinates else top_break)
lines.append(map_lines.rstrip())
if bot_break:
lines.append(indent(bot_break, " " * 2) if self.render_coordinates else bot_break)
lines.append("")
if self.render_legend:
lines.append(indent(self.footer, " "))
lines.append("")
if self.render_notes:
lines.append(self.notes)
return indent("\n".join(lines), " ")

View File

@ -54,31 +54,60 @@ def convert(source: typer.FileText = typer.Argument(help="The donjon.sh .json fi
@app.command()
def render(
source: typer.FileText = typer.Argument(
help="The battle map text file to load.", default=INSTALL_DIR / "examples" / "five_room_dungeon.txt"
help="The battle map text file to load.", default=INSTALL_DIR / "examples" / "five_room_dungeon.toml"
),
outfile: Path = typer.Option(help="The file to create. If not specified, print to STDOUT", default=None),
tileset: str = typer.Option(
help="The name of the tile set to use (run mapper list to see what's available).", default="colorized"
),
header: bool = typer.Option(help="If True, include the map header.", default=True),
border: bool = typer.Option(help="If True, draw the map border.", default=True),
coordinates: bool = typer.Option(help="If True, draw the coordinate outside the border.", default=True),
legend: bool = typer.Option(help="If True, include the legend.", default=True),
notes: bool = typer.Option(help="If True, include the notes.", default=True),
map_only: bool = typer.Option(
help="Equivalent to --no-header --no-border --no-coordinates --no-legend --no-notes", default=False
),
):
"""
Create a rendered battle map using a tile set. Will generate a PNG file if the tile set supports it,
otherwise text output.
"""
render_map(source, outfile, tileset)
render_map(
source=source,
outfile=outfile,
tileset=tileset,
header=header and not map_only,
border=border and not map_only,
coordinates=coordinates and not map_only,
legend=legend and not map_only,
notes=notes and not map_only,
)
def render_map(
source: typer.FileText = INSTALL_DIR / "examples" / "five_room_dungeon.txt",
outfile: Union[Path, None] = None,
tileset: str = "colorized",
header: bool = True,
border: bool = True,
coordinates: bool = True,
legend: bool = True,
notes: bool = True,
):
manager = app_state["tileset_manager"]
if tileset not in manager.available:
raise RuntimeError(f"Could not locate the tile set {tileset} in {manager.config_dir}.")
render_options = {
"render_header": header,
"render_border": border,
"render_coordinates": coordinates,
"render_notes": notes,
"render_legend": legend,
}
try:
bmap = battlemap.BattleMap(source=source, tileset=manager.load(tileset))
bmap = battlemap.BattleMap(source=source, tileset=manager.load(tileset), **render_options)
bmap.load()
except battlemap.UnsupportedTileException as e:
logging.error(e)

View File

@ -35,6 +35,10 @@ def sample_map():
[1]
name = "room 1"
description = "A large empty room."
notes = [
"Locked door DC 15"
]
[2]
name = "room 2"
[3]
@ -62,13 +66,13 @@ def test_renderer(manager, sample_map):
srclines = sample_map.splitlines()[1:]
strlines = str(test_map).splitlines()
for i in range(len(strlines)):
assert strlines[i] == srclines[i].lstrip(' #').ljust(21, " ")
assert strlines[i] == srclines[i].lstrip(" #").ljust(21, " ")
def test_grid_coordiates(manager):
coord_length = len(X_COORDS)
map_size = 2 * coord_length + 1
bigmap = StringIO((' # ' + ("." * map_size) + "\n") * map_size)
bigmap = StringIO((" # " + ("." * map_size) + "\n") * map_size)
test_map = battlemap.BattleMap(source=bigmap, tileset=manager.load("ascii"))
test_map.load()
assert test_map.grid.data[-1][-1].coordinates == (f"{map_size - 1}", X_COORDS[0] * 3)