maybeeee fix more stuff for gob videos (#1262)

This commit is contained in:
NotSoToothless
2026-05-19 17:37:41 -07:00
committed by GitHub
parent 2c9e89eee2
commit f511cdc083
3 changed files with 112 additions and 8 deletions
+108 -2
View File
@@ -710,13 +710,18 @@ def _battle_type_candidates(battle_type: str) -> list[str]:
mode = bt.split("_")[-1]
out: list[str] = []
if mode:
out.append(f"{mode}_battle_area_realistic")
out.append(f"{mode}_battle_area_arcade")
out.append(f"{mode}_battle_area_hardcore")
out.append(f"{mode}_battle_area")
if "dom" in bt:
out.append("dom_battle_area_realistic")
out.append("dom_battle_area_arcade")
out.append("dom_battle_area_hardcore")
if "conq" in bt:
out.append("conq_battle_area_realistic")
out.append("conq_battle_area_arcade")
out.append("conq_battle_area_hardcore")
dedup: list[str] = []
seen: set[str] = set()
for name in out:
@@ -726,6 +731,19 @@ def _battle_type_candidates(battle_type: str) -> list[str]:
return dedup
def _battle_area_variant_priority(name: str) -> int:
ln = name.lower()
if "realistic" in ln:
return 0
if "hardcore" in ln:
return 1
if "arcade" in ln:
return 2
if "briefing" in ln or "brief_" in ln:
return 3
return 4
def _select_battle_area_name(mission_def: dict | None,
areas: dict[str, dict],
battle_type: str) -> str:
@@ -795,6 +813,80 @@ def resolve_world_bounds(level_def: dict,
return c0, c1, f"battleArea:{battle_name}"
def _ground_points_for_bounds_fit(ground_entities: list[dict]) -> list[tuple[float, float]]:
pts: list[tuple[float, float]] = []
for ent in ground_entities:
for sample in ent.get("Path", []):
try:
pts.append((float(sample["X"]), float(sample["Z"])))
except Exception:
continue
return pts
def _bounds_coverage(points: list[tuple[float, float]], c0: list[float], c1: list[float]) -> float:
if not points:
return 0.0
x_lo = min(float(c0[0]), float(c1[0]))
x_hi = max(float(c0[0]), float(c1[0]))
z_lo = min(float(c0[1]), float(c1[1]))
z_hi = max(float(c0[1]), float(c1[1]))
inside = 0
for x, z in points:
if x_lo <= x <= x_hi and z_lo <= z <= z_hi:
inside += 1
return inside / float(len(points))
def _fit_world_bounds_to_ground_activity(c0: list[float], c1: list[float], coord_src: str,
mission_def: dict | None, mission_def_path: Path | None,
battle_type: str, ground_entities: list[dict],
) -> tuple[list[float], list[float], str]:
pts = _ground_points_for_bounds_fit(ground_entities)
if not pts:
return c0, c1, coord_src
areas = _collect_mission_areas(mission_def, mission_def_path)
if not areas:
return c0, c1, coord_src
names: list[str] = []
seen: set[str] = set()
for name in _mission_battle_area_targets(mission_def):
if name in areas and name not in seen:
seen.add(name)
names.append(name)
for name in _battle_type_candidates(battle_type):
if name in areas and name not in seen:
seen.add(name)
names.append(name)
if not names:
return c0, c1, coord_src
candidates: list[tuple[str, list[float], list[float], float, float]] = []
for name in names:
area = areas.get(name)
if not isinstance(area, dict):
continue
bounds = _box_bounds_from_tm(area)
if bounds is None:
continue
bc0, bc1 = bounds
cov = _bounds_coverage(pts, bc0, bc1)
area_sz = abs((float(bc1[0]) - float(bc0[0])) * (float(bc1[1]) - float(bc0[1])))
candidates.append((name, bc0, bc1, cov, area_sz))
if not candidates:
return c0, c1, coord_src
cur_cov = _bounds_coverage(pts, c0, c1)
best_name, best_c0, best_c1, best_cov, _ = sorted(
candidates, key=lambda t: (-t[3], _battle_area_variant_priority(t[0]), t[4])
)[0]
# Switch when current bounds miss noticeable movement.
if best_cov > cur_cov + 0.02:
return best_c0, best_c1, f"battleArea:{best_name}|fit={best_cov:.3f}"
return c0, c1, coord_src
def _capture_mode_prefixes(battle_type: str) -> list[str]:
bt = _clean_map_key(battle_type).lower()
if "conq" in bt:
@@ -1201,7 +1293,7 @@ def _draw_capture_areas(img: Image.Image, capture_areas: list[dict],
icon_size = 18
rp = 8
if len(outline_points) >= 3:
outline = _pick_contrast_outline_color(out, outline_points)
outline = (255, 255, 255)
draw.line(outline_points + [outline_points[0]], fill=outline, width=stroke_w)
# Keep icon area at ~1/4 of cap area (diamond area ~= s^2/2).
area2 = 0.0
@@ -1226,7 +1318,7 @@ def _draw_capture_areas(img: Image.Image, capture_areas: list[dict],
(px + int(rp * 0.707), py - int(rp * 0.707)),
(px - int(rp * 0.707), py - int(rp * 0.707)),
]
outline = _pick_contrast_outline_color(out, ring_samples)
outline = (255, 255, 255)
draw.ellipse((px - rp, py - rp, px + rp, py + rp), outline=outline, width=stroke_w)
cap_area = math.pi * float(rp * rp)
icon_size = max(10, min(canvas, int(round(math.sqrt(cap_area / 2.0)))))
@@ -2532,6 +2624,11 @@ def render_gob(
mission_def_path,
battle_type,
)
tc0, tc1, coord_src = _fit_world_bounds_to_ground_activity(
tc0, tc1, coord_src,
mission_def, mission_def_path, battle_type,
ground_ents,
)
tc0, tc1 = _expand_bounds_by_pixels(tc0, tc1, canvas=canvas, pad_px=MAP_PAD_PX)
tc0, tc1 = _clamp_bounds_to_base(tc0, tc1, base_tc0, base_tc1)
print(f"ok ({coord_src}) X=[{tc0[0]}, {tc1[0]}] Z=[{tc0[1]}, {tc1[1]}]")
@@ -2988,6 +3085,10 @@ def export_replay_json(gob_path: Path) -> dict:
level_path = level_override
session_id = d.get("SessionID", 0)
level_data = load_level_coords(level_path, session_id=session_id)
ground_entities_for_bounds = [e for e in d.get("Entities", [])
if e.get("PlayerID", 0) != 0
and e.get("ModelName", "").startswith("tankModels/")
and e.get("Path")]
capture_areas = resolve_capture_areas(mission_def, mission_def_path, battle_type)
if level_data:
c0, c1, _ = resolve_world_bounds(
@@ -2997,6 +3098,11 @@ def export_replay_json(gob_path: Path) -> dict:
mission_def_path,
battle_type,
)
c0, c1, _ = _fit_world_bounds_to_ground_activity(
c0, c1, "json",
mission_def, mission_def_path, battle_type,
ground_entities_for_bounds,
)
c0, c1 = _expand_bounds_by_pixels(c0, c1, canvas=CANVAS_MIN, pad_px=MAP_PAD_PX)
base_c0, base_c1, _ = select_tank_coords(level_data, use_alt_map_coord)
c0, c1 = _clamp_bounds_to_base(c0, c1, base_c0, base_c1)