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 9d1e77c3..1c00954c 100644 --- a/src/game/engine/scenes/world_map/world_map_arena.gd +++ b/src/game/engine/scenes/world_map/world_map_arena.gd @@ -129,20 +129,32 @@ func _apply_arena_camera_fit() -> void: 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 var viewport: Viewport = _world_map.get_viewport() if viewport == null: return - var view_size: Vector2 = viewport.get_visible_rect().size - cam.fit_map_to_viewport(int(game_map.width), int(game_map.height), view_size) + # Use the WINDOW size for the fit calculation, not the root viewport's + # get_visible_rect (which is fixed at the project's base 1920x1080). + var win_size_f: Vector2 = Vector2(DisplayServer.window_get_size()) + cam.fit_map_to_viewport(int(game_map.width), int(game_map.height), win_size_f) print( ( - "[AI ARENA] camera fit: map=%dx%d viewport=%dx%d zoom=%.3f" + "[AI ARENA] camera fit: map=%dx%d window=%dx%d zoom=%.3f" % [ int(game_map.width), int(game_map.height), - int(view_size.x), - int(view_size.y), + int(win_size_f.x), + int(win_size_f.y), cam.zoom.x, ] ) @@ -252,6 +264,14 @@ func _capture_viewport_screenshot_async() -> void: await tree.process_frame await tree.process_frame + # Re-apply the camera fit right before capture so we know the camera + # is at the fit zoom regardless of what else may have touched it. + _apply_arena_camera_fit() + for _i: int in range(2): + await tree.process_frame + RenderingServer.force_draw() + await tree.process_frame + var viewport: Viewport = _world_map.get_viewport() if viewport == null: return @@ -267,10 +287,16 @@ func _capture_viewport_screenshot_async() -> void: if err != OK: push_warning("[AI ARENA] screenshot save failed: %s" % error_string(err)) return + var diag_zoom: float = -1.0 + 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 print( ( - "[AI ARENA] screenshot saved: %s (%dx%d)" - % [path, image.get_width(), image.get_height()] + "[AI ARENA] screenshot saved: %s (%dx%d) cam_zoom=%.3f" + % [path, image.get_width(), image.get_height(), diag_zoom] ) )