Add player profile and appearance foundation
This commit is contained in:
parent
e53ad1ac38
commit
0b5b78a553
25 changed files with 1424 additions and 76 deletions
|
|
@ -118,6 +118,7 @@ func setup(
|
|||
spawn_service: PlayerSpawnService,
|
||||
network_mail_service: NetworkMailService,
|
||||
reservations: PlayerAssetReservationService,
|
||||
network_profile_service: NetworkProfileService,
|
||||
) -> void:
|
||||
_fishing_spot = fishing_spot
|
||||
_item_effects = item_effects
|
||||
|
|
@ -153,7 +154,8 @@ func setup(
|
|||
network_session,
|
||||
network_sale_service,
|
||||
network_mail_service,
|
||||
reservations
|
||||
reservations,
|
||||
network_profile_service
|
||||
)
|
||||
_hotbar_ui.setup(hotbar, bag, item_catalog, fishing_spot)
|
||||
_fishing_shop.setup(
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ enum Section {
|
|||
BAG,
|
||||
LOGBOOK,
|
||||
MAIL,
|
||||
PROFILE,
|
||||
}
|
||||
|
||||
enum SortMode {
|
||||
|
|
@ -156,6 +157,7 @@ enum CloseReason {
|
|||
@onready var _bag_sprite_detail_data: Label = %BagSpriteDetailData
|
||||
@onready var _logbook_page: Control = %LogbookPage
|
||||
@onready var _mail_page: MailPage = %MailPage
|
||||
@onready var _profile_page: ProfilePage = %ProfilePage
|
||||
@onready var _book_backing: PanelContainer = %BookBacking
|
||||
@onready var _book_spread: BoxContainer = %BookSpread
|
||||
@onready var _left_page: PanelContainer = %LeftPage
|
||||
|
|
@ -173,6 +175,7 @@ enum CloseReason {
|
|||
@onready var _bag_tab: BubbleButtonType = %BagTab
|
||||
@onready var _logbook_tab: BubbleButtonType = %LogbookTab
|
||||
@onready var _mail_tab: BubbleButtonType = %MailTab
|
||||
@onready var _profile_tab: BubbleButtonType = %ProfileTab
|
||||
@onready var _mail_unread_badge: Label = %MailUnreadBadge
|
||||
@onready var _close_button: BubbleButtonType = %CloseButton
|
||||
@onready var _content_shell: BubbleContentShellType = %MenuPanel
|
||||
|
|
@ -233,6 +236,7 @@ var _sale_service: FishSaleServiceType
|
|||
var _network_session: NetworkSessionType
|
||||
var _network_sale_service: NetworkSaleService
|
||||
var _network_mail_service: NetworkMailService
|
||||
var _network_profile_service: NetworkProfileService
|
||||
var _default_buyer: FishBuyerProfileType
|
||||
var _catalog: FishPoolType
|
||||
var _fishing_spot: FishingSpotType
|
||||
|
|
@ -268,6 +272,7 @@ var _cooler_rest_position: Vector2 = Vector2.ZERO
|
|||
var _bag_rest_position: Vector2 = Vector2.ZERO
|
||||
var _logbook_rest_position: Vector2 = Vector2.ZERO
|
||||
var _mail_rest_position: Vector2 = Vector2.ZERO
|
||||
var _profile_rest_position: Vector2 = Vector2.ZERO
|
||||
var _page_outgoing_root: Control
|
||||
var _page_incoming_root: Control
|
||||
var _page_hosts_shared: bool = false
|
||||
|
|
@ -294,6 +299,7 @@ func _ready() -> void:
|
|||
_show_section.bind(Section.LOGBOOK)
|
||||
)
|
||||
_mail_tab.pressed.connect(_show_section.bind(Section.MAIL))
|
||||
_profile_tab.pressed.connect(_show_section.bind(Section.PROFILE))
|
||||
_close_button.pressed.connect(close_menu)
|
||||
_sort_option.item_selected.connect(_on_sort_selected.bind(_sort_option))
|
||||
_sort_direction.pressed.connect(_on_sort_direction_pressed)
|
||||
|
|
@ -320,6 +326,7 @@ func _ready() -> void:
|
|||
_bag_tab,
|
||||
_logbook_tab,
|
||||
_mail_tab,
|
||||
_profile_tab,
|
||||
_close_button,
|
||||
])
|
||||
_configure_navigation_focus()
|
||||
|
|
@ -368,6 +375,7 @@ func setup(
|
|||
network_sale_service: NetworkSaleService,
|
||||
network_mail_service: NetworkMailService,
|
||||
reservations: PlayerAssetReservationService,
|
||||
network_profile_service: NetworkProfileService,
|
||||
) -> void:
|
||||
_player = player
|
||||
_inventory = inventory
|
||||
|
|
@ -384,9 +392,11 @@ func setup(
|
|||
_network_session = network_session
|
||||
_network_sale_service = network_sale_service
|
||||
_network_mail_service = network_mail_service
|
||||
_network_profile_service = network_profile_service
|
||||
_mail_page.setup(
|
||||
network_mail_service, reservations, inventory, wallet, bag, item_catalog
|
||||
)
|
||||
_profile_page.setup(network_profile_service)
|
||||
_network_mail_service.unread_count_changed.connect(
|
||||
_on_mail_unread_count_changed
|
||||
)
|
||||
|
|
@ -448,6 +458,8 @@ func consume_escape() -> bool:
|
|||
_close_sale_confirmation()
|
||||
elif _current_section == Section.MAIL and _mail_page.consume_escape():
|
||||
pass
|
||||
elif _current_section == Section.PROFILE and _profile_page.consume_escape():
|
||||
pass
|
||||
else:
|
||||
close_menu()
|
||||
return true
|
||||
|
|
@ -488,6 +500,12 @@ func close_menu(
|
|||
) -> void:
|
||||
if not visible:
|
||||
return
|
||||
if (
|
||||
reason == CloseReason.USER
|
||||
and _current_section == Section.PROFILE
|
||||
and _profile_page.request_close_confirmation()
|
||||
):
|
||||
return
|
||||
if _transitioning:
|
||||
if reason != CloseReason.USER:
|
||||
_finish_close(reason, restore_controls, _menu_generation)
|
||||
|
|
@ -572,6 +590,11 @@ func _show_section(section: Section) -> void:
|
|||
or get_viewport().gui_is_dragging()
|
||||
):
|
||||
return
|
||||
if (
|
||||
_current_section == Section.PROFILE
|
||||
and _profile_page.request_close_confirmation()
|
||||
):
|
||||
return
|
||||
_begin_page_transition(section)
|
||||
|
||||
|
||||
|
|
@ -584,6 +607,7 @@ func _show_section_immediate(section: Section) -> void:
|
|||
_bag_page.visible = section == Section.BAG
|
||||
_logbook_page.visible = section == Section.LOGBOOK
|
||||
_mail_page.visible = section == Section.MAIL
|
||||
_profile_page.visible = section == Section.PROFILE
|
||||
_content_shell.visible = false
|
||||
_inventory_section.visible = false
|
||||
_bag_section.visible = false
|
||||
|
|
@ -606,10 +630,15 @@ func _show_section_immediate(section: Section) -> void:
|
|||
_mail_page.activate()
|
||||
else:
|
||||
_mail_page.deactivate()
|
||||
if section == Section.PROFILE:
|
||||
_profile_page.activate()
|
||||
else:
|
||||
_profile_page.deactivate()
|
||||
_inventory_tab.button_pressed = section == Section.COOLER
|
||||
_bag_tab.button_pressed = section == Section.BAG
|
||||
_logbook_tab.button_pressed = section == Section.LOGBOOK
|
||||
_mail_tab.button_pressed = section == Section.MAIL
|
||||
_profile_tab.button_pressed = section == Section.PROFILE
|
||||
_update_navigation_selection()
|
||||
_configure_active_page_focus()
|
||||
|
||||
|
|
@ -621,8 +650,10 @@ func _focus_current_section() -> void:
|
|||
_bag_tab.grab_focus()
|
||||
elif _current_section == Section.LOGBOOK:
|
||||
_logbook_tab.grab_focus()
|
||||
else:
|
||||
elif _current_section == Section.MAIL:
|
||||
_mail_tab.grab_focus()
|
||||
else:
|
||||
_profile_tab.grab_focus()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
|
|
@ -650,6 +681,7 @@ func _configure_navigation_focus() -> void:
|
|||
_bag_tab,
|
||||
_logbook_tab,
|
||||
_mail_tab,
|
||||
_profile_tab,
|
||||
_close_button,
|
||||
]
|
||||
for index: int in navigation.size():
|
||||
|
|
@ -707,6 +739,8 @@ func _configure_active_page_focus() -> void:
|
|||
_configure_logbook_focus()
|
||||
Section.MAIL:
|
||||
_mail_page.activate()
|
||||
Section.PROFILE:
|
||||
_profile_page.activate()
|
||||
|
||||
|
||||
func _apply_cooler_control_styles() -> void:
|
||||
|
|
@ -800,6 +834,7 @@ func _apply_navigation_styles() -> void:
|
|||
_bag_tab,
|
||||
_logbook_tab,
|
||||
_mail_tab,
|
||||
_profile_tab,
|
||||
_close_button,
|
||||
]:
|
||||
bubble.add_theme_stylebox_override("normal", normal)
|
||||
|
|
@ -830,6 +865,7 @@ func _apply_navigation_selection_presentation() -> void:
|
|||
_bag_tab,
|
||||
_logbook_tab,
|
||||
_mail_tab,
|
||||
_profile_tab,
|
||||
]:
|
||||
if not bubble.button_pressed:
|
||||
continue
|
||||
|
|
@ -938,6 +974,7 @@ func _set_navigation_target(section: Section) -> void:
|
|||
_bag_tab.button_pressed = section == Section.BAG
|
||||
_logbook_tab.button_pressed = section == Section.LOGBOOK
|
||||
_mail_tab.button_pressed = section == Section.MAIL
|
||||
_profile_tab.button_pressed = section == Section.PROFILE
|
||||
|
||||
|
||||
func _update_shell_layout() -> void:
|
||||
|
|
@ -954,7 +991,7 @@ func _update_shell_layout() -> void:
|
|||
_content_shell.size = Vector2(612.0, 286.0) if compact else Vector2(1196.0, 478.0)
|
||||
_presentation_rest_position = _content_shell.position
|
||||
var navigation_size := (
|
||||
Vector2(720.0, 75.0) if compact else Vector2(720.0, 100.0)
|
||||
Vector2(840.0, 75.0) if compact else Vector2(840.0, 100.0)
|
||||
)
|
||||
_navigation_cluster.position = NAVIGATION_CANONICAL_POSITION
|
||||
_navigation_cluster.size = navigation_size
|
||||
|
|
@ -1126,6 +1163,10 @@ func _update_shell_layout() -> void:
|
|||
_mail_page.size = reference_size
|
||||
_mail_page.position = Vector2.ZERO
|
||||
_mail_rest_position = Vector2.ZERO
|
||||
_profile_page.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||
_profile_page.position = Vector2(42.0, 104.0)
|
||||
_profile_page.size = Vector2(1196.0, 608.0)
|
||||
_profile_rest_position = _profile_page.position
|
||||
_book_backing.position = (
|
||||
Vector2(14.0, 164.0) if compact else Vector2(58.0, 128.0)
|
||||
)
|
||||
|
|
@ -1187,10 +1228,12 @@ func _update_shell_layout() -> void:
|
|||
_bag_page.position = _bag_rest_position
|
||||
_logbook_page.position = _logbook_rest_position
|
||||
_mail_page.position = _mail_rest_position
|
||||
_profile_page.position = _profile_rest_position
|
||||
_cooler_page.modulate.a = 1.0
|
||||
_bag_page.modulate.a = 1.0
|
||||
_logbook_page.modulate.a = 1.0
|
||||
_mail_page.modulate.a = 1.0
|
||||
_profile_page.modulate.a = 1.0
|
||||
if not _page_transitioning:
|
||||
_content_stage.position = _content_rest_position
|
||||
_layout_cooler_fish(false)
|
||||
|
|
@ -1274,6 +1317,7 @@ func _begin_menu_entry() -> void:
|
|||
_bag_page.position = _bag_rest_position + Vector2.DOWN * start_offset
|
||||
_logbook_page.position = _logbook_rest_position + Vector2.DOWN * start_offset
|
||||
_mail_page.position = _mail_rest_position + Vector2.DOWN * start_offset
|
||||
_profile_page.position = _profile_rest_position + Vector2.DOWN * start_offset
|
||||
var navigation_rest: Vector2 = _navigation_cluster.position
|
||||
_navigation_cluster.position = navigation_rest + Vector2.DOWN * start_offset
|
||||
_presentation_tween = create_tween().set_parallel(true)
|
||||
|
|
@ -1307,6 +1351,12 @@ func _begin_menu_entry() -> void:
|
|||
_mail_rest_position,
|
||||
MENU_ENTER_DURATION
|
||||
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_presentation_tween.tween_property(
|
||||
_profile_page,
|
||||
"position",
|
||||
_profile_rest_position,
|
||||
MENU_ENTER_DURATION
|
||||
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_presentation_tween.tween_property(
|
||||
_navigation_cluster,
|
||||
"position",
|
||||
|
|
@ -1373,6 +1423,12 @@ func _begin_menu_exit(reason: CloseReason, restore_controls: bool) -> void:
|
|||
-_mail_page.size.y - TRANSITION_SAFE_MARGIN,
|
||||
MENU_EXIT_DURATION
|
||||
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_presentation_tween.tween_property(
|
||||
_profile_page,
|
||||
"position:y",
|
||||
-_profile_page.size.y - TRANSITION_SAFE_MARGIN,
|
||||
MENU_EXIT_DURATION
|
||||
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_presentation_tween.tween_property(
|
||||
_navigation_cluster,
|
||||
"position:y",
|
||||
|
|
@ -1436,8 +1492,10 @@ func _get_section_root(section: Section) -> Control:
|
|||
return _bag_page
|
||||
Section.LOGBOOK:
|
||||
return _logbook_page
|
||||
_:
|
||||
Section.MAIL:
|
||||
return _mail_page
|
||||
_:
|
||||
return _profile_page
|
||||
|
||||
|
||||
func _get_section_rest_position(section: Section) -> Vector2:
|
||||
|
|
@ -1448,8 +1506,10 @@ func _get_section_rest_position(section: Section) -> Vector2:
|
|||
return _bag_rest_position
|
||||
Section.LOGBOOK:
|
||||
return _logbook_rest_position
|
||||
_:
|
||||
Section.MAIL:
|
||||
return _mail_rest_position
|
||||
_:
|
||||
return _profile_rest_position
|
||||
|
||||
|
||||
func _begin_page_transition(section: Section) -> void:
|
||||
|
|
@ -1530,6 +1590,7 @@ func _set_shell_interactive(interactive: bool) -> void:
|
|||
_bag_tab,
|
||||
_logbook_tab,
|
||||
_mail_tab,
|
||||
_profile_tab,
|
||||
_close_button,
|
||||
]:
|
||||
bubble.focus_mode = Control.FOCUS_ALL if interactive else Control.FOCUS_NONE
|
||||
|
|
@ -1564,6 +1625,9 @@ func _set_content_interactive(interactive: bool) -> void:
|
|||
_mail_page.set_interactive(
|
||||
interactive and _current_section == Section.MAIL
|
||||
)
|
||||
_profile_page.set_interactive(
|
||||
interactive and _current_section == Section.PROFILE
|
||||
)
|
||||
var cooler_interactive: bool = (
|
||||
interactive and _current_section == Section.COOLER
|
||||
)
|
||||
|
|
@ -1636,6 +1700,7 @@ func _cancel_page_tween() -> void:
|
|||
func _reset_page_transition_visuals() -> void:
|
||||
for section: Section in [
|
||||
Section.COOLER, Section.BAG, Section.LOGBOOK, Section.MAIL,
|
||||
Section.PROFILE,
|
||||
]:
|
||||
var page: Control = _get_section_root(section)
|
||||
page.position = _get_section_rest_position(section)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=20 format=3]
|
||||
[gd_scene load_steps=21 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/player_menu.gd" id="1_menu"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
[ext_resource type="Texture2D" path="res://ui/assets/title/bubbles/bubble2.png" id="16_bubble2"]
|
||||
[ext_resource type="Texture2D" path="res://ui/assets/title/bubbles/bubble3.png" id="17_bubble3"]
|
||||
[ext_resource type="Script" path="res://ui/mail_page.gd" id="18_mail_page"]
|
||||
[ext_resource type="PackedScene" path="res://ui/profile_page.tscn" id="19_profile_page"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_collection"]
|
||||
|
||||
|
|
@ -583,6 +584,14 @@ offset_bottom = 720.0
|
|||
mouse_filter = 1
|
||||
script = ExtResource("18_mail_page")
|
||||
|
||||
[node name="ProfilePage" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot" instance=ExtResource("19_profile_page")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_top = 112.0
|
||||
offset_right = 1280.0
|
||||
offset_bottom = 710.0
|
||||
|
||||
[node name="BookBacking" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
|
|
@ -1206,15 +1215,15 @@ custom_minimum_size = Vector2(170, 64)
|
|||
[node name="NavigationCluster" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
offset_left = 390.0
|
||||
offset_left = 330.0
|
||||
offset_top = 44.0
|
||||
offset_right = 1110.0
|
||||
offset_right = 1170.0
|
||||
offset_bottom = 144.0
|
||||
mouse_filter = 1
|
||||
script = ExtResource("5_cluster")
|
||||
profile = ExtResource("4_profile")
|
||||
desktop_reference_size = Vector2(720, 100)
|
||||
compact_reference_size = Vector2(720, 100)
|
||||
desktop_reference_size = Vector2(840, 100)
|
||||
compact_reference_size = Vector2(840, 100)
|
||||
|
||||
[node name="InventoryTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/NavigationCluster"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -1224,8 +1233,8 @@ text = "cooler"
|
|||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
neutral_size = Vector2(124, 120)
|
||||
desktop_anchor = Vector2(116, 60)
|
||||
compact_anchor = Vector2(118, 73.3333)
|
||||
desktop_anchor = Vector2(92, 60)
|
||||
compact_anchor = Vector2(92, 73.3333)
|
||||
compact_minimum_size = Vector2(84, 82)
|
||||
minimum_font_size = 25
|
||||
maximum_font_size = 25
|
||||
|
|
@ -1244,8 +1253,8 @@ text = "bag"
|
|||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
neutral_size = Vector2(124, 120)
|
||||
desktop_anchor = Vector2(244, 60)
|
||||
compact_anchor = Vector2(251.333, 73.3333)
|
||||
desktop_anchor = Vector2(220, 60)
|
||||
compact_anchor = Vector2(220, 73.3333)
|
||||
compact_minimum_size = Vector2(84, 82)
|
||||
minimum_font_size = 25
|
||||
maximum_font_size = 25
|
||||
|
|
@ -1264,8 +1273,8 @@ text = "logbook"
|
|||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
neutral_size = Vector2(124, 120)
|
||||
desktop_anchor = Vector2(372, 60)
|
||||
compact_anchor = Vector2(384.667, 73.3333)
|
||||
desktop_anchor = Vector2(348, 60)
|
||||
compact_anchor = Vector2(348, 73.3333)
|
||||
compact_minimum_size = Vector2(84, 82)
|
||||
minimum_font_size = 25
|
||||
maximum_font_size = 25
|
||||
|
|
@ -1284,8 +1293,8 @@ text = "mail"
|
|||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
neutral_size = Vector2(124, 120)
|
||||
desktop_anchor = Vector2(500, 60)
|
||||
compact_anchor = Vector2(501, 73.3333)
|
||||
desktop_anchor = Vector2(476, 60)
|
||||
compact_anchor = Vector2(476, 73.3333)
|
||||
compact_minimum_size = Vector2(84, 82)
|
||||
minimum_font_size = 25
|
||||
maximum_font_size = 25
|
||||
|
|
@ -1312,6 +1321,26 @@ text = "1"
|
|||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="ProfileTab" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/NavigationCluster"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
toggle_mode = true
|
||||
text = "profile"
|
||||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
neutral_size = Vector2(124, 120)
|
||||
desktop_anchor = Vector2(604, 60)
|
||||
compact_anchor = Vector2(604, 73.3333)
|
||||
compact_minimum_size = Vector2(84, 82)
|
||||
minimum_font_size = 23
|
||||
maximum_font_size = 25
|
||||
horizontal_amplitude = 1.2
|
||||
vertical_amplitude = 2.1
|
||||
motion_period = 5.7
|
||||
motion_phase = 4.05
|
||||
deformation_amplitude = 0.011
|
||||
deformation_period = 6.5
|
||||
|
||||
[node name="CloseButton" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/NavigationCluster"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
|
|
@ -1319,8 +1348,8 @@ text = "close"
|
|||
script = ExtResource("6_bubble")
|
||||
profile = ExtResource("4_profile")
|
||||
neutral_size = Vector2(96, 94)
|
||||
desktop_anchor = Vector2(626, 57)
|
||||
compact_anchor = Vector2(616, 73.3333)
|
||||
desktop_anchor = Vector2(744, 57)
|
||||
compact_anchor = Vector2(744, 73.3333)
|
||||
compact_minimum_size = Vector2(76, 74)
|
||||
minimum_font_size = 23
|
||||
maximum_font_size = 23
|
||||
|
|
|
|||
414
ui/profile_page.gd
Normal file
414
ui/profile_page.gd
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
class_name ProfilePage
|
||||
extends Control
|
||||
|
||||
const CHECK_DEBOUNCE_SECONDS: float = 0.4
|
||||
|
||||
var _service: NetworkProfileService
|
||||
var _draft_name: String = ""
|
||||
var _draft_appearance: Dictionary = {}
|
||||
var _persisted_name: String = ""
|
||||
var _persisted_appearance: Dictionary = {}
|
||||
var _category_id: String = "species"
|
||||
var _dirty: bool = false
|
||||
var _allow_duplicate: bool = false
|
||||
|
||||
var _name_edit: LineEdit
|
||||
var _name_status: Label
|
||||
var _suggestions: HBoxContainer
|
||||
var _category_list: VBoxContainer
|
||||
var _option_list: VBoxContainer
|
||||
var _preview: ProfilePreview
|
||||
var _apply_button: Button
|
||||
var _revert_button: Button
|
||||
var _discard_confirmation: PanelContainer
|
||||
var _confirmation_label: Label
|
||||
var _confirmation_confirm: Button
|
||||
var _confirmation_action: String = ""
|
||||
var _debounce: Timer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
_build_ui()
|
||||
_debounce = Timer.new()
|
||||
_debounce.one_shot = true
|
||||
_debounce.wait_time = CHECK_DEBOUNCE_SECONDS
|
||||
_debounce.timeout.connect(_request_conflict_check)
|
||||
add_child(_debounce)
|
||||
|
||||
|
||||
func setup(service: NetworkProfileService) -> void:
|
||||
_service = service
|
||||
if not _service.conflict_result.is_connected(_on_conflict_result):
|
||||
_service.conflict_result.connect(_on_conflict_result)
|
||||
_service.apply_finished.connect(_on_apply_finished)
|
||||
_load_persisted()
|
||||
|
||||
|
||||
func activate() -> void:
|
||||
visible = true
|
||||
if _service != null:
|
||||
_load_persisted()
|
||||
_name_edit.grab_focus()
|
||||
|
||||
|
||||
func deactivate() -> void:
|
||||
visible = false
|
||||
_debounce.stop()
|
||||
_preview.reset_view()
|
||||
|
||||
|
||||
func set_interactive(interactive: bool) -> void:
|
||||
mouse_filter = (
|
||||
Control.MOUSE_FILTER_PASS if interactive else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
|
||||
|
||||
func consume_escape() -> bool:
|
||||
if _discard_confirmation.visible:
|
||||
_discard_confirmation.visible = false
|
||||
_confirmation_action = ""
|
||||
_name_edit.grab_focus()
|
||||
return true
|
||||
if _name_edit.has_focus():
|
||||
_name_edit.release_focus()
|
||||
return true
|
||||
if _dirty:
|
||||
_show_confirmation("discard")
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func has_unsaved_changes() -> bool:
|
||||
return _dirty
|
||||
|
||||
|
||||
func request_close_confirmation() -> bool:
|
||||
if not _dirty:
|
||||
return false
|
||||
_show_confirmation("discard")
|
||||
return true
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
var paper := PanelContainer.new()
|
||||
paper.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
paper.offset_left = 42.0
|
||||
paper.offset_top = 14.0
|
||||
paper.offset_right = -42.0
|
||||
paper.offset_bottom = -14.0
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color("f2ead3")
|
||||
style.border_color = Color("4a4238")
|
||||
style.set_border_width_all(4)
|
||||
style.set_corner_radius_all(18)
|
||||
paper.add_theme_stylebox_override("panel", style)
|
||||
add_child(paper)
|
||||
|
||||
var margin := MarginContainer.new()
|
||||
for side: String in ["left", "right", "top", "bottom"]:
|
||||
margin.add_theme_constant_override("margin_%s" % side, 28)
|
||||
paper.add_child(margin)
|
||||
var layout := VBoxContainer.new()
|
||||
layout.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(layout)
|
||||
|
||||
var heading := Label.new()
|
||||
heading.text = "player profile"
|
||||
heading.add_theme_font_size_override("font_size", 28)
|
||||
heading.add_theme_color_override("font_color", Color("302b27"))
|
||||
layout.add_child(heading)
|
||||
|
||||
var name_row := HBoxContainer.new()
|
||||
name_row.add_theme_constant_override("separation", 10)
|
||||
layout.add_child(name_row)
|
||||
var name_stack := VBoxContainer.new()
|
||||
name_stack.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
name_row.add_child(name_stack)
|
||||
var name_label := Label.new()
|
||||
name_label.text = "player name"
|
||||
name_label.add_theme_color_override("font_color", Color("302b27"))
|
||||
name_stack.add_child(name_label)
|
||||
_name_edit = LineEdit.new()
|
||||
_name_edit.max_length = NetworkProtocol.MAX_DISPLAY_NAME_LENGTH
|
||||
_name_edit.placeholder_text = "Player"
|
||||
_name_edit.custom_minimum_size = Vector2(360, 42)
|
||||
_name_edit.text_changed.connect(_on_name_changed)
|
||||
name_stack.add_child(_name_edit)
|
||||
var helper := Label.new()
|
||||
helper.text = "Shown to other players in multiplayer."
|
||||
helper.add_theme_color_override("font_color", Color("665e52"))
|
||||
name_stack.add_child(helper)
|
||||
_apply_button = Button.new()
|
||||
_apply_button.text = "apply"
|
||||
_apply_button.custom_minimum_size = Vector2(118, 50)
|
||||
_apply_button.pressed.connect(_apply)
|
||||
name_row.add_child(_apply_button)
|
||||
_revert_button = Button.new()
|
||||
_revert_button.text = "revert"
|
||||
_revert_button.custom_minimum_size = Vector2(108, 50)
|
||||
_revert_button.pressed.connect(_revert)
|
||||
name_row.add_child(_revert_button)
|
||||
var defaults_button := Button.new()
|
||||
defaults_button.text = "defaults"
|
||||
defaults_button.custom_minimum_size = Vector2(108, 50)
|
||||
defaults_button.pressed.connect(_show_confirmation.bind("defaults"))
|
||||
name_row.add_child(defaults_button)
|
||||
|
||||
_name_status = Label.new()
|
||||
_name_status.add_theme_color_override("font_color", Color("704c36"))
|
||||
layout.add_child(_name_status)
|
||||
_suggestions = HBoxContainer.new()
|
||||
_suggestions.add_theme_constant_override("separation", 8)
|
||||
layout.add_child(_suggestions)
|
||||
|
||||
var body := HBoxContainer.new()
|
||||
body.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
body.add_theme_constant_override("separation", 18)
|
||||
layout.add_child(body)
|
||||
_category_list = VBoxContainer.new()
|
||||
_category_list.custom_minimum_size = Vector2(170, 0)
|
||||
body.add_child(_category_list)
|
||||
_option_list = VBoxContainer.new()
|
||||
_option_list.custom_minimum_size = Vector2(230, 0)
|
||||
body.add_child(_option_list)
|
||||
var preview_stack := VBoxContainer.new()
|
||||
preview_stack.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
preview_stack.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
body.add_child(preview_stack)
|
||||
_preview = preload("res://ui/profile_preview.tscn").instantiate()
|
||||
preview_stack.add_child(_preview)
|
||||
var preview_note := Label.new()
|
||||
preview_note.text = "capsule preview • drag or use left / right"
|
||||
preview_note.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
preview_note.add_theme_color_override("font_color", Color("514a42"))
|
||||
preview_stack.add_child(preview_note)
|
||||
var reset_view := Button.new()
|
||||
reset_view.text = "reset view"
|
||||
reset_view.pressed.connect(_preview.reset_view)
|
||||
preview_stack.add_child(reset_view)
|
||||
|
||||
_discard_confirmation = PanelContainer.new()
|
||||
_discard_confirmation.visible = false
|
||||
_discard_confirmation.set_anchors_and_offsets_preset(Control.PRESET_CENTER)
|
||||
_discard_confirmation.custom_minimum_size = Vector2(430, 190)
|
||||
add_child(_discard_confirmation)
|
||||
var confirm_stack := VBoxContainer.new()
|
||||
confirm_stack.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
confirm_stack.add_theme_constant_override("separation", 16)
|
||||
_discard_confirmation.add_child(confirm_stack)
|
||||
_confirmation_label = Label.new()
|
||||
_confirmation_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
confirm_stack.add_child(_confirmation_label)
|
||||
var confirm_buttons := HBoxContainer.new()
|
||||
confirm_buttons.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
confirm_stack.add_child(confirm_buttons)
|
||||
_confirmation_confirm = Button.new()
|
||||
_confirmation_confirm.pressed.connect(_confirm_pending_action)
|
||||
confirm_buttons.add_child(_confirmation_confirm)
|
||||
var keep := Button.new()
|
||||
keep.name = "KeepEditing"
|
||||
keep.unique_name_in_owner = true
|
||||
keep.text = "keep editing"
|
||||
keep.pressed.connect(func() -> void:
|
||||
_discard_confirmation.visible = false
|
||||
_name_edit.grab_focus()
|
||||
)
|
||||
confirm_buttons.add_child(keep)
|
||||
|
||||
_build_categories()
|
||||
|
||||
|
||||
func _build_categories() -> void:
|
||||
for child: Node in _category_list.get_children():
|
||||
child.queue_free()
|
||||
for category_id: String in CharacterCustomizationCatalog.CATEGORY_IDS:
|
||||
var button := Button.new()
|
||||
button.text = CharacterCustomizationCatalog.category_label(category_id)
|
||||
button.toggle_mode = true
|
||||
button.custom_minimum_size = Vector2(0, 34)
|
||||
button.button_pressed = category_id == _category_id
|
||||
button.pressed.connect(_select_category.bind(category_id))
|
||||
_category_list.add_child(button)
|
||||
_refresh_options()
|
||||
|
||||
|
||||
func _select_category(category_id: String) -> void:
|
||||
_category_id = category_id
|
||||
for child: Node in _category_list.get_children():
|
||||
var button := child as Button
|
||||
if button != null:
|
||||
button.button_pressed = button.text == (
|
||||
CharacterCustomizationCatalog.category_label(category_id)
|
||||
)
|
||||
_refresh_options()
|
||||
|
||||
|
||||
func _refresh_options() -> void:
|
||||
for child: Node in _option_list.get_children():
|
||||
child.queue_free()
|
||||
var title := Label.new()
|
||||
title.text = CharacterCustomizationCatalog.category_label(_category_id)
|
||||
title.add_theme_font_size_override("font_size", 22)
|
||||
title.add_theme_color_override("font_color", Color("302b27"))
|
||||
_option_list.add_child(title)
|
||||
var options := CharacterCustomizationCatalog.options_for(_category_id)
|
||||
if options.is_empty():
|
||||
var empty := Label.new()
|
||||
empty.text = "More options coming later."
|
||||
empty.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_option_list.add_child(empty)
|
||||
return
|
||||
for option: Dictionary in options:
|
||||
var option_id := str(option["id"])
|
||||
var button := Button.new()
|
||||
button.text = str(option["label"])
|
||||
button.toggle_mode = true
|
||||
button.custom_minimum_size = Vector2(0, 34)
|
||||
button.button_pressed = _draft_appearance.get(_category_id) == option_id
|
||||
button.pressed.connect(_select_option.bind(_category_id, option_id))
|
||||
_option_list.add_child(button)
|
||||
|
||||
|
||||
func _select_option(category_id: String, option_id: String) -> void:
|
||||
_draft_appearance[category_id] = option_id
|
||||
_preview.apply_appearance_profile(_draft_appearance)
|
||||
_dirty = _draft_differs()
|
||||
_refresh_options()
|
||||
_refresh_actions()
|
||||
|
||||
|
||||
func _on_name_changed(value: String) -> void:
|
||||
_draft_name = value
|
||||
_allow_duplicate = false
|
||||
_dirty = _draft_differs()
|
||||
_refresh_actions()
|
||||
_clear_suggestions()
|
||||
if NetworkProfilePreferences.is_valid_display_name(value):
|
||||
_name_status.text = "Checking this game…"
|
||||
_debounce.start()
|
||||
else:
|
||||
_name_status.text = "Use 1–24 plain-text characters."
|
||||
|
||||
|
||||
func _request_conflict_check() -> void:
|
||||
if _service != null:
|
||||
_service.request_name_check(_draft_name)
|
||||
|
||||
|
||||
func _on_conflict_result(
|
||||
_request_id: String,
|
||||
has_conflict: bool,
|
||||
suggestion_names: PackedStringArray,
|
||||
) -> void:
|
||||
_clear_suggestions()
|
||||
if not has_conflict:
|
||||
_name_status.text = "Name is available in this game."
|
||||
return
|
||||
_name_status.text = "That name is already in use in this game."
|
||||
for suggestion: String in suggestion_names:
|
||||
var button := Button.new()
|
||||
button.text = suggestion
|
||||
button.pressed.connect(_use_suggestion.bind(suggestion))
|
||||
_suggestions.add_child(button)
|
||||
var anyway := Button.new()
|
||||
anyway.text = "use anyway"
|
||||
anyway.pressed.connect(func() -> void:
|
||||
_allow_duplicate = true
|
||||
_name_status.text = "Duplicate name allowed for this apply."
|
||||
)
|
||||
_suggestions.add_child(anyway)
|
||||
|
||||
|
||||
func _use_suggestion(value: String) -> void:
|
||||
_name_edit.text = value
|
||||
_draft_name = value
|
||||
_allow_duplicate = false
|
||||
_dirty = _draft_differs()
|
||||
_clear_suggestions()
|
||||
_request_conflict_check()
|
||||
|
||||
|
||||
func _apply() -> void:
|
||||
if _service == null:
|
||||
return
|
||||
_apply_button.disabled = true
|
||||
_service.apply_profile(_draft_name, _draft_appearance, _allow_duplicate)
|
||||
|
||||
|
||||
func _on_apply_finished(accepted: bool, message: String) -> void:
|
||||
_apply_button.disabled = false
|
||||
_name_status.text = message
|
||||
if accepted:
|
||||
_load_persisted()
|
||||
|
||||
|
||||
func _load_persisted() -> void:
|
||||
if _service == null:
|
||||
return
|
||||
_persisted_name = _service.get_persisted_name()
|
||||
_persisted_appearance = _service.get_persisted_appearance()
|
||||
_draft_name = _persisted_name
|
||||
_draft_appearance = _persisted_appearance.duplicate(true)
|
||||
_name_edit.text = _draft_name
|
||||
_preview.apply_appearance_profile(_draft_appearance)
|
||||
_dirty = false
|
||||
_allow_duplicate = false
|
||||
_name_status.text = ""
|
||||
_clear_suggestions()
|
||||
_refresh_options()
|
||||
_refresh_actions()
|
||||
|
||||
|
||||
func _revert() -> void:
|
||||
_load_persisted()
|
||||
|
||||
|
||||
func _confirm_discard() -> void:
|
||||
_discard_confirmation.visible = false
|
||||
_load_persisted()
|
||||
|
||||
|
||||
func _show_confirmation(action: String) -> void:
|
||||
_confirmation_action = action
|
||||
_discard_confirmation.visible = true
|
||||
if action == "defaults":
|
||||
_confirmation_label.text = "Reset the profile draft to defaults?"
|
||||
_confirmation_confirm.text = "reset draft"
|
||||
else:
|
||||
_confirmation_label.text = "Discard unsaved profile changes?"
|
||||
_confirmation_confirm.text = "discard changes"
|
||||
_discard_confirmation.get_node("%KeepEditing").grab_focus()
|
||||
|
||||
|
||||
func _confirm_pending_action() -> void:
|
||||
_discard_confirmation.visible = false
|
||||
if _confirmation_action == "defaults":
|
||||
_draft_appearance = CharacterCustomizationCatalog.default_snapshot()
|
||||
_preview.apply_appearance_profile(_draft_appearance)
|
||||
_dirty = _draft_differs()
|
||||
_refresh_options()
|
||||
_refresh_actions()
|
||||
else:
|
||||
_confirm_discard()
|
||||
_confirmation_action = ""
|
||||
|
||||
|
||||
func _draft_differs() -> bool:
|
||||
return (
|
||||
_draft_name.strip_edges() != _persisted_name
|
||||
or _draft_appearance != _persisted_appearance
|
||||
)
|
||||
|
||||
|
||||
func _refresh_actions() -> void:
|
||||
_apply_button.disabled = (
|
||||
not _dirty
|
||||
or not NetworkProfilePreferences.is_valid_display_name(_draft_name)
|
||||
)
|
||||
_revert_button.disabled = not _dirty
|
||||
|
||||
|
||||
func _clear_suggestions() -> void:
|
||||
for child: Node in _suggestions.get_children():
|
||||
child.queue_free()
|
||||
1
ui/profile_page.gd.uid
Normal file
1
ui/profile_page.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b6c2de8orrwfj
|
||||
8
ui/profile_page.tscn
Normal file
8
ui/profile_page.tscn
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/profile_page.gd" id="1"]
|
||||
|
||||
[node name="ProfilePage" type="Control"]
|
||||
custom_minimum_size = Vector2(320, 280)
|
||||
layout_mode = 0
|
||||
script = ExtResource("1")
|
||||
54
ui/profile_preview.gd
Normal file
54
ui/profile_preview.gd
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
class_name ProfilePreview
|
||||
extends SubViewportContainer
|
||||
|
||||
@export_range(0.1, 2.0, 0.05) var drag_sensitivity: float = 0.012
|
||||
@export_range(0.1, 4.0, 0.1) var keyboard_speed: float = 1.8
|
||||
|
||||
@onready var _preview_root: Node3D = %PreviewRoot
|
||||
|
||||
var _dragging: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
focus_mode = Control.FOCUS_ALL
|
||||
gui_input.connect(_on_gui_input)
|
||||
|
||||
|
||||
func apply_appearance_profile(_profile: Dictionary) -> void:
|
||||
# The capsule is deliberately neutral until modular character assets exist.
|
||||
pass
|
||||
|
||||
|
||||
func reset_view() -> void:
|
||||
_preview_root.rotation.y = 0.0
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not has_focus():
|
||||
return
|
||||
var axis := Input.get_axis("ui_left", "ui_right")
|
||||
var right_stick := Input.get_joy_axis(0, JOY_AXIS_RIGHT_X)
|
||||
if absf(right_stick) > 0.2:
|
||||
axis = right_stick
|
||||
if absf(axis) > 0.1:
|
||||
_rotate(axis * keyboard_speed * delta)
|
||||
|
||||
|
||||
func _on_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
_dragging = event.pressed
|
||||
if event.pressed:
|
||||
grab_focus()
|
||||
accept_event()
|
||||
elif event is InputEventMouseMotion and _dragging:
|
||||
_rotate(event.relative.x * drag_sensitivity)
|
||||
accept_event()
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_VISIBILITY_CHANGED and not is_visible_in_tree():
|
||||
_dragging = false
|
||||
|
||||
|
||||
func _rotate(amount: float) -> void:
|
||||
_preview_root.rotation.y = fposmod(_preview_root.rotation.y + amount, TAU)
|
||||
1
ui/profile_preview.gd.uid
Normal file
1
ui/profile_preview.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://3iu5mxqgsjrx
|
||||
57
ui/profile_preview.tscn
Normal file
57
ui/profile_preview.tscn
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
[gd_scene load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/profile_preview.gd" id="1"]
|
||||
|
||||
[sub_resource type="CapsuleMesh" id="Capsule"]
|
||||
radius = 0.62
|
||||
height = 2.25
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material"]
|
||||
albedo_color = Color(0.38, 0.67, 0.74, 1)
|
||||
roughness = 0.72
|
||||
|
||||
[sub_resource type="Environment" id="Environment"]
|
||||
background_mode = 1
|
||||
background_color = Color(0.075, 0.13, 0.16, 1)
|
||||
ambient_light_source = 3
|
||||
ambient_light_color = Color(0.72, 0.82, 0.86, 1)
|
||||
ambient_light_energy = 0.65
|
||||
|
||||
[sub_resource type="World3D" id="PreviewWorld"]
|
||||
environment = SubResource("Environment")
|
||||
|
||||
[node name="ProfilePreview" type="SubViewportContainer"]
|
||||
custom_minimum_size = Vector2(360, 250)
|
||||
stretch = true
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Viewport" type="SubViewport" parent="."]
|
||||
transparent_bg = false
|
||||
size = Vector2i(360, 390)
|
||||
render_target_update_mode = 4
|
||||
world_3d = SubResource("PreviewWorld")
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="Viewport"]
|
||||
environment = SubResource("Environment")
|
||||
|
||||
[node name="KeyLight" type="DirectionalLight3D" parent="Viewport"]
|
||||
rotation_degrees = Vector3(-38, -32, 0)
|
||||
light_energy = 1.15
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="FillLight" type="OmniLight3D" parent="Viewport"]
|
||||
position = Vector3(-1.8, 1.6, 2.2)
|
||||
light_color = Color(0.72, 0.84, 1, 1)
|
||||
omni_range = 5.0
|
||||
light_energy = 1.3
|
||||
|
||||
[node name="PreviewRoot" type="Node3D" parent="Viewport"]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="Capsule" type="MeshInstance3D" parent="Viewport/PreviewRoot"]
|
||||
mesh = SubResource("Capsule")
|
||||
material_override = SubResource("Material")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="Viewport"]
|
||||
position = Vector3(0, 0.15, 4.25)
|
||||
current = true
|
||||
|
|
@ -38,7 +38,6 @@ enum PresentationMode {
|
|||
@onready var _controls_page: SettingsBubblePage = %ControlsPage
|
||||
@onready var _accessibility_page: SettingsBubblePage = %AccessibilityPage
|
||||
@onready var _feedback: Label = %SettingsFeedback
|
||||
@onready var _display_name_edit: LineEdit = %DisplayNameEdit
|
||||
|
||||
@onready var _world_value: BubbleButton = %WorldValue
|
||||
@onready var _ui_value: BubbleButton = %UIValue
|
||||
|
|
@ -101,10 +100,6 @@ func _ready() -> void:
|
|||
%DisplayBackButton.gui_input.connect(_on_back_bubble_gui_input)
|
||||
%ControlsBackButton.gui_input.connect(_on_back_bubble_gui_input)
|
||||
%AccessibilityBackButton.gui_input.connect(_on_back_bubble_gui_input)
|
||||
_display_name_edit.text_submitted.connect(
|
||||
func(_value: String) -> void: _apply_display_name()
|
||||
)
|
||||
_display_name_edit.focus_exited.connect(_apply_display_name)
|
||||
for index: int in _world_options.size():
|
||||
_world_options[index].pressed.connect(
|
||||
_set_world_pixelation.bind(index + 1)
|
||||
|
|
@ -156,25 +151,6 @@ func setup_network_profile(
|
|||
) -> void:
|
||||
_network_profile = profile
|
||||
_network_session = session
|
||||
if _network_profile != null:
|
||||
_display_name_edit.text = _network_profile.display_name
|
||||
|
||||
|
||||
func _apply_display_name() -> void:
|
||||
if _network_profile == null:
|
||||
return
|
||||
var value := _display_name_edit.text.strip_edges()
|
||||
var accepted := (
|
||||
_network_session.update_local_display_name(value)
|
||||
if _network_session != null
|
||||
else _network_profile.set_display_name(value)
|
||||
)
|
||||
if accepted:
|
||||
_display_name_edit.text = _network_profile.display_name
|
||||
_feedback.text = "display name saved."
|
||||
else:
|
||||
_display_name_edit.text = _network_profile.display_name
|
||||
_feedback.text = "display name must be 1–24 plain-text characters."
|
||||
|
||||
|
||||
func open_panel(
|
||||
|
|
@ -192,8 +168,6 @@ func open_panel(
|
|||
)
|
||||
_settings_manager = settings_manager
|
||||
_load_controls()
|
||||
if _network_profile != null:
|
||||
_display_name_edit.text = _network_profile.display_name
|
||||
_feedback.text = ""
|
||||
show()
|
||||
_page_stack.clear()
|
||||
|
|
|
|||
|
|
@ -144,31 +144,6 @@ minimum_font_size = 14
|
|||
maximum_font_size = 17
|
||||
motion_phase = 4.8
|
||||
|
||||
[node name="DisplayNamePanel" type="PanelContainer" parent="RootPage"]
|
||||
layout_mode = 0
|
||||
offset_left = 318.0
|
||||
offset_top = 430.0
|
||||
offset_right = 618.0
|
||||
offset_bottom = 510.0
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="RootPage/DisplayNamePanel"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="RootPage/DisplayNamePanel/VBox"]
|
||||
layout_mode = 2
|
||||
text = "display name"
|
||||
|
||||
[node name="DisplayNameEdit" type="LineEdit" parent="RootPage/DisplayNamePanel/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
max_length = 24
|
||||
placeholder_text = "Player"
|
||||
|
||||
[node name="Help" type="Label" parent="RootPage/DisplayNamePanel/VBox"]
|
||||
layout_mode = 2
|
||||
text = "Shown to other players in multiplayer."
|
||||
theme_override_font_sizes/font_size = 12
|
||||
|
||||
[node name="DisplayPage" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue