Files
whale-town-front-v2/scenes/ui/WelcomeDialog.gd
2026-07-21 23:07:31 +08:00

307 lines
11 KiB
GDScript

extends CanvasLayer
class_name WelcomeDialog
const GUIDE_PAGES: Array[Dictionary] = [
{
"text": "欢迎来到 [color=#3399ff]Datawhale Town[/color]!\n\n这里是开源学习者的家园。在这里,我们一同探索知识,分享成长。\n\n[center]WhaleTown[/center]",
"image_path": "res://assets/maps/square/v1/props/bottom_entrance_left_task_props_v2_hd_clean.png",
},
{
"text": "最新活动:\n\n- 镇长在公会大厅附近接待新伙伴。\n- 码头区域已经开放,可以和码头 NPC 交流。\n- 广场公告板后续会同步线上新闻动态。",
"image_path": "res://assets/maps/square/v1/props/center_whale_fountain_v2_hd_clean.png",
},
{
"text": "操作提示:\n\n- 按 [color=#ffaa00]E[/color] 键可以与 NPC、公告板和信息板互动。\n- 靠近目标后面向它,再按互动键。\n- 按 [color=#ffaa00]Esc[/color] 可以关闭当前界面或结束当前互动。\n- 输入框获得焦点时,角色移动会暂停响应。",
"image_path": "res://assets/maps/square/v1/props/bottom_entrance_right_service_props_v2_hd_clean.png",
},
]
@onready var panelContainer: PanelContainer = $CenterContainer/PanelContainer
@onready var vboxContainer: VBoxContainer = $CenterContainer/PanelContainer/VBoxContainer
@onready var titleLabel: Label = $CenterContainer/PanelContainer/VBoxContainer/Header/Title
@onready var logoContainer: PanelContainer = $CenterContainer/PanelContainer/VBoxContainer/LogoContainer
@onready var bodyText: Label = $CenterContainer/PanelContainer/VBoxContainer/BodyText
@onready var actionContainer: CenterContainer = $CenterContainer/PanelContainer/VBoxContainer/ActionContainer
var _chatUi: Control
var _chatUiPrevMouseFilter: Control.MouseFilter = Control.MOUSE_FILTER_STOP
var _chatUiMouseDisabled: bool = false
var _guideContentContainer: HBoxContainer
var _guideImageRect: TextureRect
var _guideImageLabel: Label
var _guideContentLabel: RichTextLabel
var _guideFooter: HBoxContainer
var _guidePrevButton: Button
var _guideNextButton: Button
var _guideDotsContainer: HBoxContainer
var _guideCurrentPage: int = 0
var _guideTween: Tween
func _ready() -> void:
add_to_group("whaletown_escape_dismissible")
_disableChatUiMouseInput()
var closeButton := find_child("CloseButton", true, false) as Button
if closeButton != null:
closeButton.pressed.connect(_onClosePressed)
var startButton := find_child("StartButton", true, false) as Button
if startButton != null:
startButton.pressed.connect(_onStartPressed)
func _exit_tree() -> void:
_restoreChatUiMouseInput()
func _input(event: InputEvent) -> void:
if get_viewport().is_input_handled():
return
if event.is_action_pressed("ui_cancel"):
queue_free()
get_viewport().set_input_as_handled()
func is_escape_dismissible() -> bool:
return is_inside_tree()
func get_escape_priority() -> int:
return 980
func request_escape_close() -> void:
queue_free()
func _onClosePressed() -> void:
queue_free()
func _onStartPressed() -> void:
_showGuide()
func _showGuide() -> void:
panelContainer.custom_minimum_size = Vector2(720, 560)
titleLabel.text = "新人引导手册"
logoContainer.visible = false
bodyText.visible = false
actionContainer.visible = false
if _guideContentContainer == null:
_buildGuideUi()
_guideContentContainer.visible = true
_guideFooter.visible = true
_guideCurrentPage = 0
_setupGuideDots()
_updateGuideUi(false)
func _buildGuideUi() -> void:
_guideContentContainer = HBoxContainer.new()
_guideContentContainer.name = "GuideContentContainer"
_guideContentContainer.custom_minimum_size = Vector2(0, 360)
_guideContentContainer.size_flags_vertical = Control.SIZE_EXPAND_FILL
_guideContentContainer.add_theme_constant_override("separation", 22)
vboxContainer.add_child(_guideContentContainer)
var imagePanel := PanelContainer.new()
imagePanel.name = "ImagePanel"
imagePanel.custom_minimum_size = Vector2(322, 0)
imagePanel.size_flags_vertical = Control.SIZE_EXPAND_FILL
imagePanel.add_theme_stylebox_override("panel", _createImageStyle())
_guideContentContainer.add_child(imagePanel)
_guideImageRect = TextureRect.new()
_guideImageRect.name = "ImageRect"
_guideImageRect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
_guideImageRect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
imagePanel.add_child(_guideImageRect)
_guideImageLabel = Label.new()
_guideImageLabel.name = "ImageLabel"
_guideImageLabel.text = "暂无图片"
_guideImageLabel.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_guideImageLabel.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_guideImageLabel.add_theme_color_override("font_color", Color(0.43, 0.48, 0.45, 1.0))
_guideImageLabel.add_theme_font_size_override("font_size", 18)
imagePanel.add_child(_guideImageLabel)
var textPanel := PanelContainer.new()
textPanel.name = "TextPanel"
textPanel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
textPanel.size_flags_vertical = Control.SIZE_EXPAND_FILL
textPanel.add_theme_stylebox_override("panel", _createTextStyle())
_guideContentContainer.add_child(textPanel)
_guideContentLabel = RichTextLabel.new()
_guideContentLabel.name = "ContentLabel"
_guideContentLabel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_guideContentLabel.size_flags_vertical = Control.SIZE_EXPAND_FILL
_guideContentLabel.bbcode_enabled = true
_guideContentLabel.fit_content = true
_guideContentLabel.scroll_active = false
_guideContentLabel.add_theme_color_override("default_color", Color(0.18, 0.2, 0.18, 1.0))
_guideContentLabel.add_theme_font_size_override("normal_font_size", 21)
textPanel.add_child(_guideContentLabel)
_guideFooter = HBoxContainer.new()
_guideFooter.name = "GuideFooter"
_guideFooter.custom_minimum_size = Vector2(0, 58)
_guideFooter.alignment = BoxContainer.ALIGNMENT_CENTER
_guideFooter.add_theme_constant_override("separation", 22)
vboxContainer.add_child(_guideFooter)
_guidePrevButton = _createGuideButton("<")
_guidePrevButton.name = "PrevButton"
_guidePrevButton.pressed.connect(_onGuidePrevPressed)
_guideFooter.add_child(_guidePrevButton)
_guideDotsContainer = HBoxContainer.new()
_guideDotsContainer.name = "DotsContainer"
_guideDotsContainer.custom_minimum_size = Vector2(88, 0)
_guideDotsContainer.alignment = BoxContainer.ALIGNMENT_CENTER
_guideDotsContainer.add_theme_constant_override("separation", 10)
_guideFooter.add_child(_guideDotsContainer)
_guideNextButton = _createGuideButton(">")
_guideNextButton.name = "NextButton"
_guideNextButton.pressed.connect(_onGuideNextPressed)
_guideFooter.add_child(_guideNextButton)
func _createGuideButton(text: String) -> Button:
var button := Button.new()
button.custom_minimum_size = Vector2(56, 44)
button.text = text
button.add_theme_color_override("font_color", Color.WHITE)
button.add_theme_font_size_override("font_size", 22)
button.add_theme_stylebox_override("normal", _createButtonStyle(Color(0.2, 0.43, 0.48, 1.0)))
button.add_theme_stylebox_override("hover", _createButtonStyle(Color(0.28, 0.52, 0.55, 1.0)))
button.add_theme_stylebox_override("pressed", _createButtonStyle(Color(0.28, 0.52, 0.55, 1.0)))
button.add_theme_stylebox_override("disabled", _createButtonStyle(Color(0.72, 0.76, 0.72, 1.0)))
return button
func _createImageStyle() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.content_margin_left = 10.0
style.content_margin_top = 10.0
style.content_margin_right = 10.0
style.content_margin_bottom = 10.0
style.bg_color = Color(0.875, 0.93, 0.92, 1.0)
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.55, 0.68, 0.64, 1.0)
style.corner_radius_top_left = 10
style.corner_radius_top_right = 10
style.corner_radius_bottom_right = 10
style.corner_radius_bottom_left = 10
return style
func _createTextStyle() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.content_margin_left = 20.0
style.content_margin_top = 18.0
style.content_margin_right = 20.0
style.content_margin_bottom = 18.0
style.bg_color = Color(1.0, 0.985, 0.935, 1.0)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.78, 0.7, 0.55, 1.0)
style.corner_radius_top_left = 10
style.corner_radius_top_right = 10
style.corner_radius_bottom_right = 10
style.corner_radius_bottom_left = 10
return style
func _createButtonStyle(color: Color) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.content_margin_left = 16.0
style.content_margin_top = 8.0
style.content_margin_right = 16.0
style.content_margin_bottom = 8.0
style.bg_color = color
style.corner_radius_top_left = 8
style.corner_radius_top_right = 8
style.corner_radius_bottom_right = 8
style.corner_radius_bottom_left = 8
return style
func _setupGuideDots() -> void:
for child in _guideDotsContainer.get_children():
child.queue_free()
for index in range(GUIDE_PAGES.size()):
var dot := ColorRect.new()
dot.custom_minimum_size = Vector2(12, 12)
_guideDotsContainer.add_child(dot)
func _updateGuideUi(animate: bool = true) -> void:
_guidePrevButton.disabled = _guideCurrentPage == 0
_guideNextButton.disabled = _guideCurrentPage == GUIDE_PAGES.size() - 1
var dots := _guideDotsContainer.get_children()
for index in range(dots.size()):
var dot := dots[index] as ColorRect
if dot == null:
continue
if index == _guideCurrentPage:
dot.color = Color(0.15, 0.36, 0.44, 1.0)
dot.custom_minimum_size = Vector2(18, 12)
else:
dot.color = Color(0.72, 0.78, 0.72, 1.0)
dot.custom_minimum_size = Vector2(12, 12)
if animate:
_animateGuideContentChange()
else:
_setGuideContentImmediate()
func _setGuideContentImmediate() -> void:
var page := GUIDE_PAGES[_guideCurrentPage]
_guideContentLabel.text = page.get("text", "") as String
var imagePath := page.get("image_path", "") as String
if imagePath != "" and ResourceLoader.exists(imagePath):
_guideImageRect.texture = load(imagePath) as Texture2D
_guideImageLabel.visible = false
else:
_guideImageRect.texture = null
_guideImageLabel.visible = true
_guideImageLabel.text = "暂无图片"
func _animateGuideContentChange() -> void:
if _guideTween != null and _guideTween.is_valid():
_guideTween.kill()
_guideTween = create_tween()
_guideTween.tween_property(_guideContentContainer, "modulate:a", 0.0, 0.15)
_guideTween.tween_callback(_setGuideContentImmediate)
_guideTween.tween_property(_guideContentContainer, "modulate:a", 1.0, 0.15)
func _onGuidePrevPressed() -> void:
if _guideCurrentPage > 0:
_guideCurrentPage -= 1
_updateGuideUi()
func _onGuideNextPressed() -> void:
if _guideCurrentPage < GUIDE_PAGES.size() - 1:
_guideCurrentPage += 1
_updateGuideUi()
func _disableChatUiMouseInput() -> void:
var currentScene := get_tree().current_scene
if currentScene != null:
_chatUi = currentScene.get_node_or_null("UILayer/ChatUI") as Control
if _chatUi == null:
_chatUi = get_tree().root.find_child("ChatUI", true, false) as Control
if _chatUi != null:
_chatUiPrevMouseFilter = _chatUi.mouse_filter
_chatUi.mouse_filter = Control.MOUSE_FILTER_IGNORE
_chatUiMouseDisabled = true
func _restoreChatUiMouseInput() -> void:
if _chatUiMouseDisabled and is_instance_valid(_chatUi):
_chatUi.mouse_filter = _chatUiPrevMouseFilter
_chatUiMouseDisabled = false
_chatUi = null