From d6694b585bc703867428a7c15a97f91d6f48b3d4 Mon Sep 17 00:00:00 2001 From: Dominik Roth Date: Sun, 9 Aug 2026 20:06:44 +0200 Subject: [PATCH] Fold COASTAL_BATTERY into HOSTILE_ARTILLERY, it's the same thing Same pattern already established for AMMO_CACHE -> SUPPLY_CACHE: a 'Coastal Battery' is just a heavy fixed-emplacement HostileArtillery under a different name in the game's own text, not a meaningfully different unit type worth its own enum member. Removed the TargetType.COASTAL_BATTERY member, added 'CoastalBattery' to ocr.py's _TYPE_WORD_ALIASES (so 'Coastal Battery#2:' still parses, now as a HostileArtillery), and a models.py migration entry so any save file written before this change still loads correctly. Verified the migration directly (COASTAL_BATTERY -> HOSTILE_ARTILLERY, existing AMMO_CACHE -> SUPPLY_CACHE unaffected) and updated the one test that asserted the old type. --- src/fenigma/models.py | 8 ++++---- src/fenigma/ocr.py | 6 ++++-- tests/test_ocr.py | 7 +++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/fenigma/models.py b/src/fenigma/models.py index ce20d77..ef2a0f9 100644 --- a/src/fenigma/models.py +++ b/src/fenigma/models.py @@ -41,10 +41,9 @@ class TargetType(Enum): FDC = "FDC" # Fire Direction Center, coordinates enemy counter-battery fire INFANTRY = "Infantry" # hostile ground troops MECHANIZED = "Mechanized" # hostile armored/vehicle unit - HOSTILE_ARTILLERY = "Hostile Artillery" + HOSTILE_ARTILLERY = "Hostile Artillery" # "Coastal Battery" is just this, see _TYPE_WORD_ALIASES in ocr.py HOSTILE_TANK = "Hostile Tank" PILLBOX = "Pillbox" # armoured emplacement, fixed position - COASTAL_BATTERY = "Coastal Battery" # heavy fixed emplacement, reported by listening posts MARINE_GARRISON = "Marine Garrison" # allied unit, requests fire support (see Target.requested_time) ENEMY = "Enemy" # ad-hoc installation named directly in the intel text # ("Enemy Signal Station", "Enemy Field Command"), not one of the @@ -60,8 +59,9 @@ class TargetType(Enum): # Renamed/removed enum members, for loading save files written before the # rename. AMMO_CACHE turned out to be a misreading of the game's actual -# "SupplyCache" name and was dropped in favor of it. -_TARGET_TYPE_MIGRATIONS = {"AMMO_CACHE": "SUPPLY_CACHE"} +# "SupplyCache" name and was dropped in favor of it. COASTAL_BATTERY +# turned out to just be a HOSTILE_ARTILLERY under a different name. +_TARGET_TYPE_MIGRATIONS = {"AMMO_CACHE": "SUPPLY_CACHE", "COASTAL_BATTERY": "HOSTILE_ARTILLERY"} def _migrate_target_type(name: str) -> TargetType: diff --git a/src/fenigma/ocr.py b/src/fenigma/ocr.py index d0139b2..c6654aa 100644 --- a/src/fenigma/ocr.py +++ b/src/fenigma/ocr.py @@ -515,8 +515,10 @@ _BARE_CLUE_VALUE_RE = re.compile( _TYPE_BY_SHORT = {t.short: t for t in TargetType} # The game's typewriter has used "AmmoCache" for what's now modeled as -# SupplyCache, treat it as the same type rather than dropping the target. -_TYPE_WORD_ALIASES = {"AmmoCache": "SupplyCache"} +# SupplyCache, and "CoastalBattery" for what's just a HostileArtillery +# under a different name, treat both as the same type rather than +# dropping the target or inventing a redundant enum member for it. +_TYPE_WORD_ALIASES = {"AmmoCache": "SupplyCache", "CoastalBattery": "HostileArtillery"} _REF_NAMED_RE = re.compile(rf"^{_TYPE_ID_FRAGMENT}") diff --git a/tests/test_ocr.py b/tests/test_ocr.py index 1f2d243..abe824b 100644 --- a/tests/test_ocr.py +++ b/tests/test_ocr.py @@ -94,14 +94,17 @@ def test_enemy_destroyed_report(): def test_listening_post_and_coastal_battery(): + """'Coastal Battery' is an alias for HostileArtillery (see + _TYPE_WORD_ALIASES), not its own TargetType, it's just the + fixed-emplacement flavor of the same thing.""" text = """Listening Post#1 at K6 7:8 audio reports on: Coastal Battery#2: Distance 6.28km South-East from Listening Post#1 """ info = ocr.parse_text(text) assert info.reference_points["ListeningPost#1"][2] == Coord("K", 6, 7, 8) - assert (TargetType.COASTAL_BATTERY, "2") in info.targets - _, clues, *_ = info.targets[(TargetType.COASTAL_BATTERY, "2")] + assert (TargetType.HOSTILE_ARTILLERY, "2") in info.targets + _, clues, *_ = info.targets[(TargetType.HOSTILE_ARTILLERY, "2")] assert clues == [ocr.Clue(reference="ListeningPost#1", bearing_deg=135.0, distance_km=6.28)]