Integrate per-save identity and interaction UI

This commit is contained in:
Alexander Sellite 2026-09-01 17:30:50 -04:00
parent ac82a28f3b
commit 4fda6e97f6
58 changed files with 3471 additions and 461 deletions

View file

@ -409,7 +409,17 @@ func _run() -> void:
# not the inactive normal gameplay camera.
var normal_camera := player.get_gameplay_camera()
var normal_camera_transform: Transform3D = normal_camera.global_transform
var chat_anchor: Vector3 = player.get_chat_anchor_position()
var chat_anchor: Vector3 = player.get_nameplate_anchor_position()
assert(player.get_chat_anchor_position().is_equal_approx(chat_anchor))
var held_gear_probe := MeshInstance3D.new()
var held_gear_mesh := BoxMesh.new()
held_gear_mesh.size = Vector3(0.5, 4.0, 0.5)
held_gear_probe.mesh = held_gear_mesh
held_gear_probe.position = Vector3(0.0, 6.0, 0.0)
player.get_node("%Visuals").add_child(held_gear_probe)
assert(player.get_nameplate_anchor_position().is_equal_approx(chat_anchor))
assert(player.get_chat_anchor_position().is_equal_approx(chat_anchor))
held_gear_probe.free()
normal_camera.look_at(
normal_camera.global_position
- (chat_anchor - normal_camera.global_position),
@ -417,6 +427,120 @@ func _run() -> void:
)
assert(normal_camera.is_position_behind(chat_anchor))
assert(not free_camera.is_position_behind(chat_anchor))
free_camera.look_at(chat_anchor, Vector3.UP)
chat_ui.call("_update_nameplates")
var nameplate_layer := chat_ui.get("_nameplate_layer") as Control
var nameplates: Dictionary = chat_ui.get("_nameplates")
var local_nameplate: Dictionary = nameplates.get(
session.get_local_peer_id(), {}
)
var nameplate_control := local_nameplate.get("control") as Control
var nameplate_name := local_nameplate.get("name") as Label
var nameplate_title := local_nameplate.get("title") as Label
var local_record := session.get_peer_record(session.get_local_peer_id())
assert(
nameplate_control != null and nameplate_control.visible,
(
"local nameplate must be visible (available=%s world=%s layer=%s "
+ "shared=%s remote_visible=%s behind=%s occluded=%s screen=%s viewport=%s)"
)
% [
bool(chat_ui.get("_available")),
chat_ui.is_world_speech_visible(),
nameplate_layer.visible,
bool(chat_ui.call(
"_speaker_shares_local_space", session.get_local_peer_id()
)),
player.is_remote_presentation_visible(),
free_camera.is_position_behind(chat_anchor),
bool(chat_ui.call(
"_is_speech_world_occluded", free_camera, player, chat_anchor
)),
free_camera.unproject_position(chat_anchor)
/ float(chat_ui.get("_output_scale")),
chat_ui.get_viewport_rect().size,
],
)
assert(nameplate_name != null and nameplate_title != null)
assert(nameplate_name.text == local_record.display_name)
assert(nameplate_title.text == local_record.title)
assert(
nameplate_name.get_theme_font_size("font_size")
== ChatUI.NAMEPLATE_NAME_FONT_SIZE
)
assert(
nameplate_title.get_theme_font_size("font_size")
== ChatUI.NAMEPLATE_TITLE_FONT_SIZE
)
assert(
nameplate_name.get_theme_font_size("font_size")
> nameplate_title.get_theme_font_size("font_size")
)
assert(ChatUI.NAMEPLATE_NAME_FONT_SIZE == 20)
assert(ChatUI.NAMEPLATE_TITLE_TOP < ChatUI.NAMEPLATE_NAME_HEIGHT)
assert(ChatUI.NAMEPLATE_HEIGHT < 42.0)
assert(nameplate_title.position.y == ChatUI.NAMEPLATE_TITLE_TOP)
for flat_label: Label in [nameplate_name, nameplate_title]:
assert(flat_label.get_theme_color("font_color") == Color.WHITE)
assert(flat_label.get_theme_constant("outline_size") == 0)
assert(flat_label.get_theme_color("font_outline_color").a == 0.0)
assert(flat_label.get_theme_color("font_shadow_color").a == 0.0)
# Persistent remote labels obey both the authoritative home-space boundary
# and the avatar presentation mask. They return only when the avatar shares
# the local space and its world presentation is visible.
var spawn_service := main.get_node(
"%PlayerSpawnService"
) as PlayerSpawnService
var registry := session.get("_registry") as PeerRegistry
const NAMEPLATE_TEST_PEER_ID: int = 9191
assert(registry.add_peer(
NAMEPLATE_TEST_PEER_ID,
"nameplate-test-profile",
"remote stray",
NetworkProtocol.PROTOCOL_VERSION,
"",
"",
PackedStringArray(),
"trailblazer",
))
var remote_transform: Transform3D = player.global_transform
remote_transform.origin += Vector3(0.8, 0.0, 0.0)
var remote_avatar := spawn_service.spawn_remote_player(
NAMEPLATE_TEST_PEER_ID,
remote_transform,
)
assert(remote_avatar != null)
assert(session.set_peer_space(NAMEPLATE_TEST_PEER_ID, &"rv:privacy-test"))
remote_avatar.set_remote_presentation_visible(true)
chat_ui.call("_update_nameplates")
var remote_nameplate: Dictionary = nameplates.get(
NAMEPLATE_TEST_PEER_ID, {}
)
var remote_plate := remote_nameplate.get("control") as Control
assert(remote_plate != null and not remote_plate.visible)
assert(session.set_peer_space(
NAMEPLATE_TEST_PEER_ID,
session.get_peer_space(session.get_local_peer_id()),
))
remote_avatar.set_remote_presentation_visible(false)
chat_ui.call("_update_nameplates")
assert(not remote_plate.visible)
remote_avatar.set_remote_presentation_visible(true)
free_camera.look_at(
remote_avatar.get_nameplate_anchor_position(), Vector3.UP
)
chat_ui.call("_update_nameplates")
assert(remote_plate.visible)
assert((remote_nameplate.get("name") as Label).text == "remote stray")
assert((remote_nameplate.get("title") as Label).text == "trailblazer")
spawn_service.remove_peer(NAMEPLATE_TEST_PEER_ID)
registry.remove_peer(NAMEPLATE_TEST_PEER_ID)
chat_ui.call("_update_nameplates")
assert(not nameplates.has(NAMEPLATE_TEST_PEER_ID))
free_camera.look_at(chat_anchor, Vector3.UP)
chat_ui.call("_update_nameplates")
assert(nameplate_control.visible)
var shop_prompt := game_ui.get_node("%ShopPrompt") as Control
var storage_prompt := game_ui.get_node("%StoragePrompt") as Control
game_ui.set_shop_prompt_visible(true, chat_anchor)
@ -432,11 +556,19 @@ func _run() -> void:
assert(not first_speech_mouth.is_empty())
assert(not second_speech_mouth.is_empty())
assert(first_speech_mouth != second_speech_mouth)
free_camera.look_at(chat_anchor, Vector3.UP)
chat_ui.call("_update_nameplates")
chat_ui.call("_update_speech")
var speech: Dictionary = chat_ui.get("_speech")
var local_speech: Dictionary = speech.get(session.get_local_peer_id(), {})
var speech_bubble := local_speech.get("bubble") as PanelContainer
assert(speech_bubble != null and speech_bubble.visible)
assert(
speech_bubble.position.y
+ speech_bubble.size.y
+ ChatUI.SPEECH_POINTER_HEIGHT
<= nameplate_control.position.y
)
normal_camera.global_transform = normal_camera_transform
await create_timer(0.6).timeout
assert(not bool(player.get("_speech_mouth_active")))
@ -448,6 +580,7 @@ func _run() -> void:
await process_frame
assert(game_ui.is_gameplay_hud_hidden())
assert(chat_ui.is_hud_hidden())
assert(not nameplate_layer.visible)
assert(not (
game_ui.get_node("%GameplayTransientHUD") as Control
).visible)
@ -479,6 +612,8 @@ func _run() -> void:
chat_ui.open_chat()
await process_frame
assert(chat_ui.is_open() and chat_panel.visible)
assert(not chat_ui.is_hud_hidden())
assert(not nameplate_layer.visible)
chat_ui.refocus_gameplay()
assert(chat_ui.is_hud_hidden())
var cancel_button := InputEventJoypadButton.new()
@ -503,6 +638,7 @@ func _run() -> void:
await process_frame
assert(not game_ui.is_gameplay_hud_hidden())
assert(not chat_ui.is_hud_hidden())
assert(nameplate_layer.visible)
assert(not service.can_activate())
assert(not service.is_active() and not toolbar.visible)
assert(player.bag.add_item(ArtShopStock.ART_KIT_ITEM_ID, 1))