From 4527ca77378f9deae31491e4c14c45d31fbde610 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sat, 11 Apr 2026 06:49:11 -0700 Subject: [PATCH] =?UTF-8?q?fix(world-map):=20=F0=9F=90=9B=20Fix=20viewport?= =?UTF-8?q?=20sizing=20and=20scaling=20in=20arena=20mode=20to=20prevent=20?= =?UTF-8?q?layout=20race=20conditions=20and=20correct=20hex=20map=20render?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../scenes/world_map/world_map_arena.gd | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/game/engine/scenes/world_map/world_map_arena.gd b/src/game/engine/scenes/world_map/world_map_arena.gd index 1c00954c..1a9a6d0e 100644 --- a/src/game/engine/scenes/world_map/world_map_arena.gd +++ b/src/game/engine/scenes/world_map/world_map_arena.gd @@ -124,22 +124,33 @@ func _apply_arena_camera_fit() -> void: if cam == null or not cam.has_method("fit_map_to_viewport"): return + # Force the ViewportWindowManager Control to the window size. + # Normally Control nodes inside a Node2D parent get sized via their + # anchors-relative-to-the-root-viewport, but in arena mode that + # resize never seems to fire — Control.size stays (0,0), so the + # SubViewport ends up at Godot's minimum (2,2), and the + # SubViewportContainer with stretch=true scales that 2x2 image up + # to the entire window (~500x scale). The hex map then looks like + # it's at zoom 0.45 even though Camera2D.zoom is 0.126. Setting + # both sizes explicitly side-steps the layout race condition. + var win_size: Vector2i = DisplayServer.window_get_size() + var win_size_v: Vector2 = Vector2(win_size) + if vwm is Control: + (vwm as Control).size = win_size_v + var bg_vp: SubViewport = null if vwm.has_method("get_background_viewport"): bg_vp = vwm.call("get_background_viewport") as SubViewport if bg_vp != null: bg_vp.render_target_update_mode = SubViewport.UPDATE_ALWAYS - # Force the SubViewport to match the actual window size. The default - # layout-driven _sync_background_size in viewport_window_manager.gd - # can leave the SubViewport at its initial ~240x180 if the Control's - # layout hasn't propagated. With stretch=true on the container, that - # tiny SubViewport gets blown up 5x to fill the window — making the - # hex map look like it's at zoom 0.45 even though the Camera2D.zoom - # is 0.126. Setting the size explicitly here side-steps the layout - # race condition. - var win_size: Vector2i = DisplayServer.window_get_size() bg_vp.size = win_size + # Also resize the SubViewportContainer (the immediate parent of the + # SubViewport) so the stretch math doesn't compound the wrong ratio. + var bg_container: Node = vwm.get_node_or_null("BackgroundViewport") + if bg_container is Control: + (bg_container as Control).size = win_size_v + var viewport: Viewport = _world_map.get_viewport() if viewport == null: return @@ -288,15 +299,33 @@ func _capture_viewport_screenshot_async() -> void: push_warning("[AI ARENA] screenshot save failed: %s" % error_string(err)) return var diag_zoom: float = -1.0 + var diag_subvp_size: Vector2i = Vector2i.ZERO + var diag_vwm_size: Vector2 = Vector2.ZERO var vwm_diag: Node = _world_map.get_node_or_null("ViewportWindowManager") - if vwm_diag != null and vwm_diag.has_method("get_background_camera"): - var diag_cam: Camera2D = vwm_diag.get_background_camera() - if diag_cam != null: - diag_zoom = diag_cam.zoom.x + if vwm_diag != null: + if vwm_diag is Control: + diag_vwm_size = (vwm_diag as Control).size + if vwm_diag.has_method("get_background_camera"): + var diag_cam: Camera2D = vwm_diag.get_background_camera() + if diag_cam != null: + diag_zoom = diag_cam.zoom.x + if vwm_diag.has_method("get_background_viewport"): + var diag_vp: SubViewport = vwm_diag.call("get_background_viewport") as SubViewport + if diag_vp != null: + diag_subvp_size = diag_vp.size print( ( - "[AI ARENA] screenshot saved: %s (%dx%d) cam_zoom=%.3f" - % [path, image.get_width(), image.get_height(), diag_zoom] + "[AI ARENA] screenshot saved: %s (%dx%d) cam_zoom=%.3f subvp=%dx%d vwm=%dx%d" + % [ + path, + image.get_width(), + image.get_height(), + diag_zoom, + diag_subvp_size.x, + diag_subvp_size.y, + int(diag_vwm_size.x), + int(diag_vwm_size.y), + ] ) )