2026-07-31 21:20:23 -04:00
|
|
|
extends SceneTree
|
|
|
|
|
|
|
|
|
|
const UIPixelationPresenterType = preload(
|
|
|
|
|
"res://ui/ui_pixelation_presenter.gd"
|
|
|
|
|
)
|
|
|
|
|
const UIReferencePresentationType = preload(
|
|
|
|
|
"res://ui/ui_reference_presentation.gd"
|
|
|
|
|
)
|
|
|
|
|
const GameUIScene := preload("res://ui/game_ui.tscn")
|
|
|
|
|
const TARGET_SIZES: Array[Vector2i] = [
|
|
|
|
|
Vector2i(1280, 720),
|
|
|
|
|
Vector2i(1920, 1080),
|
|
|
|
|
Vector2i(2560, 1440),
|
|
|
|
|
Vector2i(3840, 2160),
|
|
|
|
|
Vector2i(640, 480),
|
|
|
|
|
Vector2i(1024, 768),
|
|
|
|
|
Vector2i(1729, 973),
|
|
|
|
|
Vector2i(2560, 1080),
|
|
|
|
|
Vector2i(3440, 1440),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _initialize() -> void:
|
|
|
|
|
call_deferred("_run")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _run() -> void:
|
|
|
|
|
var original_size: Vector2i = root.size
|
|
|
|
|
var presenter := UIPixelationPresenterType.new()
|
|
|
|
|
presenter.name = "UIPresentation"
|
|
|
|
|
presenter.stretch = true
|
|
|
|
|
var ui_viewport := SubViewport.new()
|
|
|
|
|
ui_viewport.name = "UIViewport"
|
|
|
|
|
var game_ui := GameUIScene.instantiate() as GameUI
|
|
|
|
|
var ui_root := game_ui.get_node("UIRoot") as Control
|
|
|
|
|
var canonical_stage := game_ui.get_node(
|
|
|
|
|
"UIRoot/CanonicalStage"
|
|
|
|
|
) as Control
|
|
|
|
|
var chat_ui := game_ui.get_node("UIRoot/ChatUI") as ChatUI
|
|
|
|
|
var player_menu := game_ui.get_node(
|
|
|
|
|
"UIRoot/CanonicalStage/PlayerMenu"
|
|
|
|
|
) as PlayerMenu
|
|
|
|
|
var hotbar := game_ui.get_node(
|
|
|
|
|
"UIRoot/CanonicalStage/Hotbar"
|
|
|
|
|
) as HotbarUI
|
|
|
|
|
var screen_fade := game_ui.get_node("UIRoot/ScreenFade") as Control
|
|
|
|
|
var title_screen := game_ui.get_node("UIRoot/TitleScreen") as TitleScreen
|
|
|
|
|
var title_content_stage := title_screen.get_node(
|
|
|
|
|
"ResponsiveTitleStage"
|
|
|
|
|
) as Control
|
|
|
|
|
var decorative_fish_layer := title_screen.get_node(
|
|
|
|
|
"DecorativeFishLayer"
|
|
|
|
|
) as Control
|
|
|
|
|
var decorative_bubble_layer := title_screen.get_node(
|
|
|
|
|
"DecorativeBubbleLayer"
|
|
|
|
|
) as Control
|
|
|
|
|
ui_viewport.add_child(game_ui)
|
|
|
|
|
presenter.add_child(ui_viewport)
|
|
|
|
|
root.add_child(presenter)
|
|
|
|
|
await process_frame
|
|
|
|
|
chat_ui.set_available(true)
|
|
|
|
|
print(
|
|
|
|
|
"UI viewport embedding: root=%s subviewport=%s"
|
|
|
|
|
% [root.gui_embed_subwindows, ui_viewport.gui_embed_subwindows]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for target_size: Vector2i in TARGET_SIZES:
|
|
|
|
|
root.size = target_size
|
|
|
|
|
await process_frame
|
|
|
|
|
await process_frame
|
|
|
|
|
var actual_size: Vector2i = root.size
|
|
|
|
|
var display_size := Vector2(actual_size)
|
|
|
|
|
var expected_scale: float = UIReferencePresentationType.get_scale(
|
|
|
|
|
display_size
|
|
|
|
|
)
|
|
|
|
|
var expected_stage_offset: Vector2 = UIReferencePresentationType.get_offset(
|
|
|
|
|
display_size
|
|
|
|
|
)
|
|
|
|
|
assert(presenter.position.is_equal_approx(Vector2.ZERO))
|
|
|
|
|
assert(ui_root.position.is_equal_approx(Vector2.ZERO))
|
|
|
|
|
var expected_visible_size: Vector2 = (
|
|
|
|
|
UIReferencePresentationType.get_visible_reference_size(display_size)
|
|
|
|
|
)
|
|
|
|
|
assert(ui_root.size.is_equal_approx(expected_visible_size))
|
|
|
|
|
assert(canonical_stage.size.is_equal_approx(
|
|
|
|
|
UIReferencePresentationType.REFERENCE_SIZE
|
|
|
|
|
))
|
|
|
|
|
assert(canonical_stage.position.is_equal_approx(
|
|
|
|
|
UIReferencePresentationType.get_stage_position(display_size)
|
|
|
|
|
))
|
|
|
|
|
assert(chat_ui.size.is_equal_approx(expected_visible_size))
|
|
|
|
|
assert(screen_fade.size.is_equal_approx(expected_visible_size))
|
|
|
|
|
assert(player_menu.position.is_equal_approx(Vector2.ZERO))
|
|
|
|
|
assert(player_menu.size.is_equal_approx(
|
|
|
|
|
UIReferencePresentationType.REFERENCE_SIZE
|
|
|
|
|
))
|
2026-08-08 14:23:28 -04:00
|
|
|
assert(hotbar.position.is_equal_approx(Vector2(
|
|
|
|
|
0.0,
|
|
|
|
|
canonical_stage.position.y,
|
|
|
|
|
)))
|
2026-07-31 21:20:23 -04:00
|
|
|
assert(hotbar.size.is_equal_approx(
|
|
|
|
|
UIReferencePresentationType.REFERENCE_SIZE
|
|
|
|
|
))
|
|
|
|
|
assert(title_screen.size.is_equal_approx(expected_visible_size))
|
|
|
|
|
assert(title_content_stage.size.is_equal_approx(
|
|
|
|
|
UIReferencePresentationType.REFERENCE_SIZE
|
|
|
|
|
))
|
|
|
|
|
assert(title_content_stage.position.is_equal_approx(
|
|
|
|
|
canonical_stage.position
|
|
|
|
|
))
|
|
|
|
|
assert(decorative_fish_layer.size.is_equal_approx(
|
|
|
|
|
expected_visible_size
|
|
|
|
|
))
|
|
|
|
|
assert(decorative_bubble_layer.size.is_equal_approx(
|
|
|
|
|
expected_visible_size
|
|
|
|
|
))
|
|
|
|
|
var left_to_right_bounds: Vector2 = (
|
|
|
|
|
TitleScreen.get_decorative_fish_crossing_bounds(
|
|
|
|
|
expected_visible_size.x, 160.0, 1.0
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
var right_to_left_bounds: Vector2 = (
|
|
|
|
|
TitleScreen.get_decorative_fish_crossing_bounds(
|
|
|
|
|
expected_visible_size.x, 160.0, -1.0
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
assert(left_to_right_bounds.x < -160.0)
|
|
|
|
|
assert(left_to_right_bounds.y > expected_visible_size.x)
|
|
|
|
|
assert(right_to_left_bounds.x > expected_visible_size.x)
|
|
|
|
|
assert(right_to_left_bounds.y < -160.0)
|
|
|
|
|
assert(is_equal_approx(presenter.scale.x, presenter.scale.y))
|
|
|
|
|
assert(is_equal_approx(ui_root.scale.x, ui_root.scale.y))
|
|
|
|
|
var effective_scale: float = presenter.scale.x * ui_root.scale.x
|
|
|
|
|
assert(is_equal_approx(effective_scale, expected_scale))
|
|
|
|
|
assert((
|
|
|
|
|
canonical_stage.position * effective_scale
|
|
|
|
|
).distance_to(expected_stage_offset) <= 0.01)
|
|
|
|
|
var expected_render_height: int = mini(
|
|
|
|
|
roundi(UIReferencePresentationType.REFERENCE_SIZE.y * expected_scale),
|
|
|
|
|
PlayerSettings.get_ui_render_height(
|
|
|
|
|
PlayerSettings.DEFAULT_UI_PIXEL_SIZE,
|
|
|
|
|
roundi(UIReferencePresentationType.REFERENCE_SIZE.y),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
var expected_render_scale: float = (
|
|
|
|
|
float(expected_render_height)
|
|
|
|
|
/ UIReferencePresentationType.REFERENCE_SIZE.y
|
|
|
|
|
)
|
|
|
|
|
var expected_viewport_size := Vector2i(
|
|
|
|
|
roundi(expected_visible_size.x * expected_render_scale),
|
|
|
|
|
roundi(expected_visible_size.y * expected_render_scale),
|
|
|
|
|
)
|
|
|
|
|
assert(presenter.get_ui_viewport_size() == expected_viewport_size)
|
|
|
|
|
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
|
|
|
|
|
var chat_panel := chat_ui.get_node("ChatPanel") as Control
|
2026-08-02 13:31:10 -04:00
|
|
|
var clock_panel := chat_ui.get_node("WorldClockPanel") as Control
|
|
|
|
|
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as Control
|
2026-07-31 21:20:23 -04:00
|
|
|
assert(chat_panel != null)
|
2026-08-02 13:31:10 -04:00
|
|
|
assert(clock_panel != null)
|
|
|
|
|
assert(weather_icon != null)
|
2026-07-31 21:20:23 -04:00
|
|
|
assert(chat_panel.visible)
|
|
|
|
|
assert(is_equal_approx(chat_panel.position.x, 0.0))
|
|
|
|
|
assert(is_equal_approx(
|
|
|
|
|
(chat_panel.position.y + chat_panel.size.y) * effective_scale,
|
|
|
|
|
display_size.y - ChatUI.BOTTOM_MARGIN * effective_scale,
|
|
|
|
|
))
|
2026-08-02 13:31:10 -04:00
|
|
|
assert(clock_panel.position.is_equal_approx(Vector2(
|
|
|
|
|
ChatUI.CLOCK_EDGE_MARGIN,
|
|
|
|
|
ChatUI.CLOCK_EDGE_MARGIN,
|
|
|
|
|
)))
|
|
|
|
|
assert(is_equal_approx(
|
|
|
|
|
weather_icon.position.y, ChatUI.CLOCK_EDGE_MARGIN
|
|
|
|
|
))
|
|
|
|
|
assert(weather_icon.position.x > clock_panel.position.x)
|
2026-07-31 21:20:23 -04:00
|
|
|
print(
|
|
|
|
|
(
|
|
|
|
|
"UI resize: requested=%s actual=%s viewport=%s container_scale=%.6f "
|
|
|
|
|
+ "root_scale=%.6f effective_scale=%.6f stage_offset=%s "
|
|
|
|
|
+ "chat_x=%.3f"
|
|
|
|
|
)
|
|
|
|
|
% [
|
|
|
|
|
target_size,
|
|
|
|
|
actual_size,
|
|
|
|
|
presenter.get_ui_viewport_size(),
|
|
|
|
|
presenter.scale.x,
|
|
|
|
|
ui_root.scale.x,
|
|
|
|
|
effective_scale,
|
|
|
|
|
expected_stage_offset,
|
|
|
|
|
chat_panel.position.x * effective_scale,
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
if OS.has_environment("NETFISHING_UI_SCALE_CAPTURE"):
|
|
|
|
|
await process_frame
|
|
|
|
|
var capture: Image = root.get_texture().get_image()
|
|
|
|
|
var capture_path: String = (
|
|
|
|
|
"%s-requested-%dx%d-actual-%dx%d.png"
|
|
|
|
|
) % [
|
|
|
|
|
OS.get_environment("NETFISHING_UI_SCALE_CAPTURE"),
|
|
|
|
|
target_size.x,
|
|
|
|
|
target_size.y,
|
|
|
|
|
actual_size.x,
|
|
|
|
|
actual_size.y,
|
|
|
|
|
]
|
|
|
|
|
assert(capture.save_png(capture_path) == OK)
|
|
|
|
|
|
|
|
|
|
presenter.queue_free()
|
|
|
|
|
root.size = original_size
|
|
|
|
|
await process_frame
|
|
|
|
|
print("UI scaling runtime validation: PASS")
|
|
|
|
|
quit()
|