117 lines
4.5 KiB
GDScript
117 lines
4.5 KiB
GDScript
# mobile_ui_test —— mobile presentation smoke test without a server or assets.
|
|
# godot --headless --path project --script mobile_ui_test.gd
|
|
extends SceneTree
|
|
|
|
const UiProfile := preload("res://ui/ui_profile.gd")
|
|
const MobileHud := preload("res://ui/mobile/mobile_hud.gd")
|
|
const MobileUiRoot := preload("res://ui/mobile/mobile_ui_root.gd")
|
|
const MobileMenuDrawer := preload("res://ui/mobile/mobile_menu_drawer.gd")
|
|
const MobileWindowHost := preload("res://ui/mobile/mobile_window_host.gd")
|
|
const PlayerCtl := preload("res://player_controller.gd")
|
|
|
|
class FakeClient extends Node:
|
|
signal party_changed
|
|
signal vitals_changed(vid: int)
|
|
var main := 7
|
|
var entity := {"vid": 7, "name": "测试角色", "race": 0, "level": 12}
|
|
var party := [{"vid": 7, "name": "测试角色", "hp_pct": 100, "leader": true}]
|
|
func get_main_vid() -> int: return main
|
|
func get_entity(_vid: int) -> Dictionary: return entity
|
|
func get_party() -> Array: return party
|
|
func set_target(_vid: int) -> bool: return true
|
|
|
|
var _fail := 0
|
|
|
|
func _ck(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
_fail += 1
|
|
printerr("FAIL: " + message)
|
|
|
|
func _init() -> void:
|
|
await _run()
|
|
if _fail == 0:
|
|
print("PASS: mobile_ui_test (profile / landscape HUD / menu / controls)")
|
|
quit(0)
|
|
else:
|
|
printerr("%d check(s) failed" % _fail)
|
|
quit(1)
|
|
|
|
func _run() -> void:
|
|
await process_frame
|
|
_ck(UiProfile.parse("mobile") == UiProfile.Mode.MOBILE, "profile parses mobile")
|
|
_ck(UiProfile.parse("desktop") == UiProfile.Mode.DESKTOP, "profile parses desktop")
|
|
_ck(UiProfile.parse("auto") == UiProfile.Mode.AUTO, "profile parses auto")
|
|
_ck(MobileUiRoot.requires_landscape(Vector2(844, 390)) == false,
|
|
"landscape design size is accepted")
|
|
_ck(MobileUiRoot.requires_landscape(Vector2(390, 844)),
|
|
"portrait viewport is rejected")
|
|
var safe := MobileWindowHost.safe_rect_for(Vector2(844, 390),
|
|
Rect2(20, 8, 804, 374))
|
|
_ck(safe == Rect2(20, 8, 804, 374), "reported safe area is preserved")
|
|
_ck(MobileWindowHost.safe_rect_for(Vector2(844, 390),
|
|
Rect2(0, 0, 1920, 1080)) == Rect2(0, 0, 844, 390),
|
|
"physical-screen safe area is rejected for a logical viewport")
|
|
|
|
var client := FakeClient.new()
|
|
get_root().add_child(client)
|
|
var root := MobileUiRoot.new()
|
|
get_root().add_child(root)
|
|
root.setup(null, null, client)
|
|
root.bind_controls({"client": client})
|
|
await process_frame
|
|
|
|
_ck(root.hud_view is MobileHud, "mobile root owns mobile HUD")
|
|
_ck(root.input_overlay != null, "mobile root owns touch overlay")
|
|
_ck(root.hud_view._player_name.text == "测试角色", "avatar card uses server player name")
|
|
root.set_vitals(50, 100, 20, 40)
|
|
_ck(root.hud_view._hp_bar.value == 50 and root.hud_view._mp_bar.value == 20,
|
|
"mobile HUD updates HP/SP through compatibility setters")
|
|
root.set_target("目标", 35)
|
|
_ck(root.hud_view._target_panel.visible and root.hud_view._target_bar.value == 35,
|
|
"mobile HUD shows target status")
|
|
|
|
root.menu_drawer.open()
|
|
_ck(root.menu_drawer.is_open(), "mobile menu opens")
|
|
_ck(MobileMenuDrawer.CATEGORIES.size() == 5, "mobile menu has five top-level categories")
|
|
_ck(not MobileMenuDrawer.ITEMS["社交"].has("队伍"), "social menu has no party entry")
|
|
root.menu_drawer.close()
|
|
|
|
var pc := PlayerCtl.new()
|
|
get_root().add_child(pc)
|
|
pc.player = Node3D.new()
|
|
get_root().add_child(pc.player)
|
|
root.input_overlay.bind_controls(pc, null, null, null)
|
|
await process_frame
|
|
var touch_pos: Vector2 = root.input_overlay._joystick.get_global_rect().get_center()
|
|
var ui_down := InputEventScreenTouch.new()
|
|
ui_down.index = 21
|
|
ui_down.pressed = true
|
|
ui_down.position = touch_pos
|
|
root.input_overlay._input(ui_down)
|
|
_ck(pc._mobile_ui_touches.has(21), "UI touch is registered by world-touch arbiter")
|
|
var world_down := InputEventScreenTouch.new()
|
|
world_down.index = 22
|
|
world_down.pressed = true
|
|
world_down.position = Vector2(600, 210)
|
|
pc._on_touch(world_down)
|
|
var world_up := InputEventScreenTouch.new()
|
|
world_up.index = 22
|
|
world_up.pressed = false
|
|
world_up.position = world_down.position
|
|
pc._on_touch(world_up)
|
|
_ck(pc._multi_gesture, "joystick plus world touch cannot become tap-to-move")
|
|
var ui_up := InputEventScreenTouch.new()
|
|
ui_up.index = 21
|
|
ui_up.pressed = false
|
|
ui_up.position = touch_pos
|
|
root.input_overlay._input(ui_up)
|
|
_ck(not pc._mobile_ui_touches.has(21), "UI touch arbiter clears on release")
|
|
pc.set_mobile_axis(Vector2(2, 0))
|
|
_ck(pc._mobile_axis == Vector2(1, 0), "mobile movement axis is normalized")
|
|
pc.clear_mobile_input()
|
|
_ck(pc._mobile_axis == Vector2.ZERO, "mobile movement axis clears on cancellation")
|
|
|
|
root.queue_free()
|
|
pc.queue_free()
|
|
client.queue_free()
|