Initial WhaleTown V2 frontend
This commit is contained in:
293
scenes/ui/WelcomeDialog.gd
Normal file
293
scenes/ui/WelcomeDialog.gd
Normal file
@@ -0,0 +1,293 @@
|
||||
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- 输入框获得焦点时,角色移动会暂停响应。",
|
||||
"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:
|
||||
_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 event.is_action_pressed("ui_cancel"):
|
||||
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
|
||||
Reference in New Issue
Block a user