Scholomance: replace kg's Cata 4-floor map with classic single-room layout

kg only ships the post-Cataclysm Scholomance (4 floors split as you ascend the school) but Ascension uses the classic single-floor layout where every wing branches off the central rooms.

Fix: new dungeon_replacements section in ascension_overrides.json. When a tile_key is listed there, build_data skips kg entirely for that dungeon and synthesises a single-floor entry from a manually-supplied map (Atlas-addon Scholomance.blp upscaled to 2048x2048) and AtlasLoot's per-dungeon boss list.

Coords come from AtlasLoot's 0..100 percent space scaled to the new map's pixel space directly. Pin classification: dungeonskull → boss (cls=3), None+name-contains-Rare → rare elite (cls=5), other interactives → cls=2.
This commit is contained in:
2026-04-25 23:56:40 +02:00
parent 65ae0158f3
commit 5b1c198757
3 changed files with 252 additions and 8362 deletions
+13
View File
@@ -1,5 +1,18 @@
{
"_comment": "Ascension-specific corrections to the upstream kg / AtlasLoot data. Each entry overrides where an enemy/extra is rendered. Match by tile_key + name (case-insensitive substring). pos is in kg pixel space (z=4 stitched image, 6144x4096). kg_floor_id picks the floor (omit if the dungeon is single-floor).",
"_dungeon_replacements_comment": "When the kg map is the wrong layout for Ascension (e.g. retail/Cata layout vs classic), replace the entire dungeon's map with one we author manually. Coords are in the new map's pixel space (0..width, 0..height). Bosses come from AtlasLoot's per-dungeon entries with cords; pinType=dungeonskull → cls=3, pinType=None → cls=5 (rare).",
"dungeon_replacements": {
"scholomance": {
"image": "maps/scholomance.webp",
"width": 2048,
"height": 2048,
"atlasloot_id": "Scholomance",
"label": "Scholomance",
"note": "kg ships only the Cata 4-floor layout; Ascension uses the classic single-room layout. Map is Atlas-addon BLP upscaled to 2048."
}
},
"overrides": [
{
"tile_key": "stratholme",
+81 -1
View File
@@ -225,8 +225,83 @@ def main() -> int:
extras_db = json.loads(EXTRAS_PATH.read_text()).get("extras", {})
overrides = []
dungeon_replacements = {}
if OVERRIDES_PATH.exists():
overrides = json.loads(OVERRIDES_PATH.read_text()).get("overrides", [])
ov_doc = json.loads(OVERRIDES_PATH.read_text())
overrides = ov_doc.get("overrides", [])
dungeon_replacements = ov_doc.get("dungeon_replacements", {})
# Load AtlasLoot map data on demand for dungeon replacements.
atlasloot_data = None
al_path = Path("/tmp/atlasloot_maps.json")
if al_path.exists():
try:
atlasloot_data = json.loads(al_path.read_text())
except Exception:
atlasloot_data = None
def replacement_entry(tile_key, repl, registry_entry):
"""Build a complete dungeon record from a manual-override map +
AtlasLoot bosses. Bypasses kg entirely for this dungeon."""
al_id = repl["atlasloot_id"]
al = (atlasloot_data or {}).get("OriginalWoW", {}).get(al_id, {})
W, H = repl["width"], repl["height"]
enemies = []
seen = set()
for k, v in al.items():
if not k.isdigit() or not isinstance(v, list):
continue
for ent in v:
if not isinstance(ent, dict):
continue
if ent.get("SubZone"):
continue
cords = ent.get("cords")
name = ent.get("1")
if not (isinstance(cords, list) and len(cords) == 2 and name):
continue
key = (name, cords[0], cords[1])
if key in seen:
continue
seen.add(key)
pin = ent.get("pinType")
lname = name.lower()
if pin == "dungeonskull":
cls = 3
elif pin is None and "rare" in lname:
cls = 5
else:
cls = 2 # quest item, summon spot, lever, etc.
enemies.append({
"id": None,
"npc_id": None,
"name": name,
"pos": [round(cords[0] / 100 * W, 1), round(cords[1] / 100 * H, 1)],
"classification": cls,
"skippable": False, "required": False,
"kill_priority": None, "pack_id": None, "patrol_id": None,
})
map_obj = {
"image": repl["image"],
"width": W, "height": H,
"label": repl.get("label", tile_key),
"kg_floor_id": None,
"enemies": enemies,
"packs": [], "patrols": [], "icons": [],
}
return {
"id": tile_key,
"expansion": "OriginalWoW",
"name": registry_entry.get("name", tile_key),
"acronym": registry_entry.get("acronym"),
"tile_key": tile_key,
"data_slug": registry_entry.get("data_slug"),
"mapping_id": registry_entry.get("mapping_id"),
"maps": [map_obj],
"ascension_replaced": True,
"replacement_note": repl.get("note"),
}
def apply_overrides(tile_key, name, pos, floor_id):
"""Return (pos, floor_id) possibly replaced by an Ascension override."""
@@ -268,6 +343,11 @@ def main() -> int:
except Exception:
npc_index = {}
# Dungeon replacement: skip kg entirely for this one.
if d["tile_key"] in dungeon_replacements:
dungeons.append(replacement_entry(d["tile_key"], dungeon_replacements[d["tile_key"]], d))
continue
entry = build_one(d, summary, npc_index, icon_index)
if entry:
extras = extras_db.get(d["tile_key"], [])
+222 -8425
View File
File diff suppressed because it is too large Load Diff