331 lines
9.4 KiB
GDScript
331 lines
9.4 KiB
GDScript
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 = "自定义外观(暂未开放)"
|