Files
whale-town/scripts/CharacterCreation.gd
2025-12-05 19:00:14 +08:00

331 lines
9.4 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Control
class_name CharacterCreation
## 角色创建界面
## 处理新角色的创建
# UI 元素
var character_name_input: LineEdit
var create_button: Button
var back_button: Button
var customize_button: Button
var status_label: Label
var container: VBoxContainer
var status_timer: Timer
# 个性化数据
var character_appearance: Dictionary = {}
var character_personality: Dictionary = {}
# 自定义界面
var customization_ui: Control
# 信号
signal character_created(character_name: String, personalization_data: Dictionary)
signal back_requested()
func _ready():
"""初始化角色创建界面"""
_create_ui()
_setup_timer()
_setup_animations()
_initialize_personalization()
update_layout()
## 设置动画效果
func _setup_animations():
"""设置界面动画效果"""
# 为移动设备优化触摸体验
TouchFeedbackManager.optimize_ui_for_touch(self)
# 界面入场动画
UIAnimationManager.fade_slide_in(container, "right", 0.4)
## 设置定时器
func _setup_timer():
"""设置状态标签自动清除定时器"""
status_timer = Timer.new()
status_timer.one_shot = true
status_timer.timeout.connect(_on_status_timer_timeout)
add_child(status_timer)
## 定时器超时
func _on_status_timer_timeout():
"""定时器超时,清除状态标签"""
clear_status()
## 创建 UI 元素
func _create_ui():
"""创建角色创建界面的所有 UI 元素"""
# 设置为全屏
anchor_right = 1.0
anchor_bottom = 1.0
# 创建中心容器
container = VBoxContainer.new()
container.name = "Container"
container.custom_minimum_size = Vector2(300, 0)
# 设置容器锚点为居中
container.anchor_left = 0.5
container.anchor_right = 0.5
container.anchor_top = 0.5
container.anchor_bottom = 0.5
container.offset_left = -150 # 容器宽度的一半
container.offset_right = 150
container.offset_top = -200 # 估算容器高度的一半
container.offset_bottom = 200
add_child(container)
# 标题
var title = Label.new()
title.text = "创建角色"
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title.add_theme_font_size_override("font_size", 32)
container.add_child(title)
# 间距
container.add_child(_create_spacer(20))
# 说明文本
var description = Label.new()
description.text = "请输入你的角色名称2-20个字符"
description.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
description.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
container.add_child(description)
# 间距
container.add_child(_create_spacer(10))
# 角色名称标签
var name_label = Label.new()
name_label.text = "角色名称:"
container.add_child(name_label)
# 角色名称输入框
character_name_input = LineEdit.new()
character_name_input.placeholder_text = "输入角色名称"
character_name_input.custom_minimum_size = Vector2(0, 40)
character_name_input.max_length = 20
character_name_input.text_changed.connect(_on_name_changed)
character_name_input.text_submitted.connect(_on_name_submitted)
container.add_child(character_name_input)
# 状态标签(放在输入框下方)
status_label = Label.new()
status_label.text = ""
status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
status_label.custom_minimum_size = Vector2(0, 30)
status_label.add_theme_font_size_override("font_size", 16)
container.add_child(status_label)
# 间距
container.add_child(_create_spacer(10))
# 自定义按钮(默认禁用)
customize_button = Button.new()
customize_button.text = "自定义外观(暂未开放)"
customize_button.custom_minimum_size = Vector2(0, 50)
customize_button.disabled = true # 默认禁用
customize_button.pressed.connect(_on_customize_pressed)
container.add_child(customize_button)
# 间距
container.add_child(_create_spacer(10))
# 创建按钮
create_button = Button.new()
create_button.text = "创建角色"
create_button.custom_minimum_size = Vector2(0, 50)
create_button.pressed.connect(_on_create_pressed)
container.add_child(create_button)
# 间距
container.add_child(_create_spacer(10))
# 返回按钮
back_button = Button.new()
back_button.text = "返回"
back_button.custom_minimum_size = Vector2(0, 50)
back_button.pressed.connect(_on_back_pressed)
container.add_child(back_button)
## 创建间距
func _create_spacer(height: float) -> Control:
"""创建垂直间距"""
var spacer = Control.new()
spacer.custom_minimum_size = Vector2(0, height)
return spacer
## 更新布局
func update_layout():
"""更新布局以适应窗口大小"""
if not container:
return
# 容器已经通过锚点设置为居中,无需手动计算位置
# 锚点会自动适应窗口大小变化
## 显示状态消息
func show_status(message: String, is_error: bool = false):
"""
显示状态消息
@param message: 消息文本
@param is_error: 是否为错误消息
"""
if status_label:
status_label.text = message
if is_error:
status_label.add_theme_color_override("font_color", Color(1, 0.3, 0.3))
else:
status_label.add_theme_color_override("font_color", Color(0.3, 1, 0.3))
# 启动定时器1秒后自动清除与ErrorNotification保持一致
if status_timer:
status_timer.start(1.0)
## 清除状态消息
func clear_status():
"""清除状态消息"""
if status_timer:
status_timer.stop()
if status_label:
status_label.text = ""
## 验证角色名称
func validate_character_name(char_name: String) -> String:
"""
验证角色名称
@param char_name: 角色名称
@return: 错误消息,如果有效则返回空字符串
"""
# 使用安全管理器进行验证
var validation_result = SecurityManager.validate_input(char_name, "character_name")
if not validation_result.valid:
return validation_result.error
# 使用 CharacterData 的验证函数作为额外检查
if not CharacterData.validate_name(validation_result.sanitized):
return "角色名称格式无效"
return ""
## 名称输入变化
func _on_name_changed(new_text: String):
"""名称输入框内容变化"""
# 实时验证
var error = validate_character_name(new_text)
if error.is_empty():
clear_status()
create_button.disabled = false
else:
show_status(error, true)
create_button.disabled = true
## 创建按钮点击
func _on_create_pressed():
"""创建按钮被点击"""
var char_name = character_name_input.text
# 验证名称
var error = validate_character_name(char_name)
if not error.is_empty():
show_status(error, true)
# 添加错误动画反馈
UIAnimationManager.shake_error(character_name_input, 8.0, 0.4)
return
# 获取清理后的名称
var validation_result = SecurityManager.validate_input(char_name, "character_name")
var sanitized_name = validation_result.sanitized
# 添加成功反馈动画
UIAnimationManager.button_press_feedback(create_button)
clear_status()
character_created.emit(sanitized_name, get_personalization_data())
## 返回按钮点击
func _on_back_pressed():
"""返回按钮被点击"""
back_requested.emit()
## 名称输入框回车
func _on_name_submitted(_text: String):
"""名称输入框按下回车"""
if not create_button.disabled:
_on_create_pressed()
## 初始化个性化数据
func _initialize_personalization():
"""初始化默认的个性化数据"""
var personalization = preload("res://scripts/CharacterPersonalization.gd")
character_appearance = personalization.generate_random_appearance()
character_personality = personalization.generate_random_personality()
## 自定义按钮点击
func _on_customize_pressed():
"""自定义按钮被点击"""
# 检查按钮是否被禁用
if customize_button.disabled:
show_status("自定义外观功能暂未开放", true)
return
if customization_ui:
customization_ui.queue_free()
var CharacterCustomizationClass = preload("res://scripts/CharacterCustomization.gd")
customization_ui = CharacterCustomizationClass.new()
get_tree().root.add_child(customization_ui)
# 创建临时角色数据用于自定义
var temp_data = CharacterData.create("临时角色", "temp")
CharacterData.set_appearance(temp_data, character_appearance)
temp_data[CharacterData.FIELD_PERSONALITY] = character_personality
customization_ui.load_character_data(temp_data)
customization_ui.customization_saved.connect(_on_customization_saved)
customization_ui.customization_cancelled.connect(_on_customization_cancelled)
## 自定义保存回调
func _on_customization_saved(data: Dictionary):
"""自定义数据保存回调"""
character_appearance = data.get(CharacterData.FIELD_APPEARANCE, {})
character_personality = data.get(CharacterData.FIELD_PERSONALITY, {})
show_status("外观和个性已自定义", false)
if customization_ui:
customization_ui.queue_free()
customization_ui = null
## 自定义取消回调
func _on_customization_cancelled():
"""自定义取消回调"""
if customization_ui:
customization_ui.queue_free()
customization_ui = null
## 获取个性化数据
func get_personalization_data() -> Dictionary:
"""
获取当前的个性化数据
@return: 个性化数据字典
"""
return {
CharacterData.FIELD_APPEARANCE: character_appearance,
CharacterData.FIELD_PERSONALITY: character_personality
}
## 启用/禁用自定义外观功能
func set_customization_enabled(enabled: bool):
"""
启用或禁用自定义外观功能
@param enabled: 是否启用
"""
if customize_button:
customize_button.disabled = not enabled
if enabled:
customize_button.text = "自定义外观"
else:
customize_button.text = "自定义外观(暂未开放)"