Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roman namelist fixes #181

Merged
merged 3 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mod/stellaris_dashboard/descriptor.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name="Stellaris Dashboard"
version="v6.5.2"
version="v6.5.3"
tags={
"Utilities"
"Gameplay"
Expand Down
2 changes: 1 addition & 1 deletion mod/stellaris_dashboard/interface/main_bottom.gui
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ guiTypes = {
pdx_tooltip = "STELLARIS_DASHBOARD_TOOLTIP"
pdx_tooltip_anchor_offset = { x= 0 y = @tt_offset_y }
pdx_tooltip_anchor_orientation = lower_left
web_link="http://127.0.0.1:28053/checkversion/v6.5.2"
web_link="http://127.0.0.1:28053/checkversion/v6.5.3"
}

iconType = {
Expand Down
2 changes: 1 addition & 1 deletion stellarisdashboard/dashboard_app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

logger = logging.getLogger(__name__)

VERSION = "v6.5.2"
VERSION = "v6.5.3"


def parse_version(version: str):
Expand Down
59 changes: 52 additions & 7 deletions stellarisdashboard/game_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def render_from_json(self, name_json: str):
logger.warning(f"Failed name: {name_json!r}")
return rendered

def render_from_dict(self, name_dict: dict) -> str:
def render_from_dict(self, name_dict: dict, tags: set[str] | None = None) -> str:
if not isinstance(name_dict, dict):
logger.warning(f"Expected name template dictionary, received {name_dict}")
return str(name_dict)
Expand All @@ -92,23 +92,31 @@ def render_from_dict(self, name_dict: dict) -> str:
render_template = self.name_mapping.get(key, key)
render_template = self._preprocess_template(render_template, name_dict)

# this handles tags and variants as documented in the Stellaris file localisation/99_README_GRAMMAR.txt
# note: this does not fully implement tag forwarding (section 6) or tag-sensitive text (section 7)
# but this seems to be "good enough" for our purposes of rendering names
if tags is None:
tags = set()
render_template, added_tags = self._process_tags_and_variants(render_template, tags)
tags.update(added_tags)

if "value" in name_dict:
return self.render_from_dict(name_dict["value"])
return self.render_from_dict(name_dict["value"], tags)

substitution_values = self._collect_substitution_values(name_dict)
substitution_values = self._collect_substitution_values(name_dict, tags)
render_template = self._substitute_variables(
render_template, substitution_values
)
render_template = self._handle_unresolved_variables(render_template)
return render_template

def _collect_substitution_values(self, name_dict):
def _collect_substitution_values(self, name_dict, tags: set[str]):
substitution_values = []
for var in name_dict.get("variables", []):
if "key" in var and "value" in var:
var_key = var.get("key")
substitution_values.append(
(var_key, self.render_from_dict(var["value"]))
(var_key, self.render_from_dict(var["value"], tags))
)
return substitution_values

Expand All @@ -132,6 +140,28 @@ def _preprocess_template(self, render_template, name_dict):
render_template = "$fmt$"

return render_template

def _process_tags_and_variants(self, render_template: str, tags: set[str]):
raw_variants = render_template.split("|||")
raw_variants.reverse() # variants are checked right-to-left
for variant in raw_variants:
# tags added
if "&!" in variant:
added_tags = set(variant[variant.index("&!") + 2:].split(","))
variant = variant[0:variant.index("&!")]
else:
added_tags = set()

# variants (other than first) have a comma-separated list of required tags, followed by a colon
if ":" in variant and variant != raw_variants[-1]:
required_tags = set(variant[0:variant.index(":")].split(","))
variant = variant[variant.index(":") + 1:]
else:
required_tags = set()

# all required tags must be present to use a variant
if tags.issuperset(required_tags):
return variant, added_tags

def _substitute_variables(self, render_template, substitution_values):
if render_template == "%ACRONYM%":
Expand All @@ -152,7 +182,9 @@ def _substitute_variables(self, render_template, substitution_values):
if subst_key == "num":
try:
render_template = render_template.replace(
f"$ORD$", self._fmt_ord_number(int(subst_value))
"$ORD$", self._fmt_ord_number(int(subst_value))
).replace(
"$R$", self._fmt_roman_number(int(subst_value))
)
except ValueError:
...
Expand Down Expand Up @@ -188,7 +220,7 @@ def _handle_unresolved_variables(self, render_template):

# Find variables that were not resolved so far:
for match in var_re.findall(render_template):
if match == "ORD":
if match == "ORD" or match == "R":
continue
resolved = lookup_key(match)
render_template = re.sub(rf"\${match}\$", resolved, render_template)
Expand All @@ -204,6 +236,19 @@ def _fmt_ord_number(self, num: int):
return f"{num}rd"
return f"{num}th"

def _fmt_roman_number(self, num: int):
ONES_SYMBOL = ("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX")
TENS_SYMBOL = ("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC")
HUNDREDS_SYMBOL = ("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM")
thousands = int(num / 1000)
num -= thousands * 1000
hundreds = int(num / 100)
num -= hundreds * 100
tens = int(num / 10)
num -= tens * 10
ones = num
return "M" * thousands + HUNDREDS_SYMBOL[hundreds] + TENS_SYMBOL[tens] + ONES_SYMBOL[ones]

def _fmt_adjective(self, noun: str) -> str:
# {'i': '*ian $1$', 'r': '*ran $1$', 'a': '*an $1$', 'e': '*an $1$', 'us': '*an $1$',
# 'is': '*an $1$', 'es': '*an $1$', 'ss': '*an $1$', 'id': '*an $1$', 'ed': '*an $1$',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
l_english:
HUMAN3_CHR_Aulus:1 "Aulus&!masc"
HUMAN3_CHR_Aula:0 "Aula&!fem"
HUMAN3_CHR_Aufidius:1 "$1$ Aufidia|||masc:$1$ Aufidius"

22 changes: 22 additions & 0 deletions test/names_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,28 @@ def test_name_rendering_with_game_files(test_case: NameTestcase):
expected="Quetzan Consolidated Worlds",
description="Nested %ADJECTIVE% and %ADJ% test",
),
NameTestcase(
dict(
key="%LEADER_2%",
variables=[
{"key": "1", "value": {"key": "HUMAN3_CHR_Aula"}},
{"key": "2", "value": {"key": "HUMAN3_CHR_Aufidius"}},
]
),
expected="Aula Aufidia",
description="Female tagged name"
),
NameTestcase(
dict(
key="%LEADER_2%",
variables=[
{"key": "1", "value": {"key": "HUMAN3_CHR_Aulus"}},
{"key": "2", "value": {"key": "HUMAN3_CHR_Aufidius"}},
]
),
expected="Aulus Aufidius",
description="Male tagged name"
)
],
ids=lambda tc: tc.description,
)
Expand Down
Loading