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

750 lines
23 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 Node
## Main scene script
## 主场景脚本,负责初始化和协调各个管理器
# 引用各个管理器节点
@onready var network_manager = $NetworkManager
@onready var game_state_manager = $GameStateManager
@onready var ui_layer = $UILayer
@onready var game_world = $GameWorld
@onready var error_notification: ErrorNotification = null
@onready var loading_indicator: LoadingIndicator = null
# 游戏场景和角色
var office_scene: Node2D = null
var world_manager: Node = null
var input_handler: Node = null
var player_character: CharacterBody2D = null
# 测试管理器
var dialogue_test_manager = null
# 社交系统
var social_manager: SocialManager = null
# 服务器配置
var server_url: String = "ws://localhost:8080"
func _ready():
"""主场景初始化"""
print("[MAIN] AI Town Game - Main scene loaded")
print("[MAIN] Godot version: ", Engine.get_version_info())
# 初始化游戏
_initialize_game()
func _initialize_game():
"""初始化游戏系统"""
print("[MAIN] Initializing game systems...")
# 初始化安全系统
_initialize_security()
# 初始化社交系统
_initialize_social_systems()
# 初始化UI组件
_setup_ui_components()
# 连接信号
_connect_all_signals()
# 显示登录界面
ui_layer.show_screen("login")
print("[MAIN] Game initialization complete")
## 初始化安全系统
func _initialize_security():
"""初始化安全配置和系统"""
print("[SECURITY] Initializing security systems...")
# 初始化安全配置
SecurityConfig.initialize()
# 验证安全配置
if not SecurityConfig.validate_config():
print("[ERROR] Security configuration validation failed")
if error_notification:
error_notification.show_error("安全系统初始化失败")
var security_level = SecurityConfig.get_security_level()
print("[SECURITY] Security level: ", security_level)
# 如果是高安全级别,记录安全事件
if security_level == "high":
print("[INFO] [SYSTEM] High security mode activated")
## 初始化社交系统
func _initialize_social_systems():
"""初始化社交系统"""
print("[SOCIAL] Initializing social systems...")
# 创建社交管理器
social_manager = SocialManager.new()
social_manager.name = "SocialManager"
add_child(social_manager)
# 设置网络管理器引用
social_manager.set_network_manager(network_manager)
# 连接社交系统信号
social_manager.social_notification.connect(_on_social_notification)
social_manager.friend_activity.connect(_on_friend_activity)
print("[SOCIAL] Social systems initialized")
## 设置UI组件
func _setup_ui_components():
"""设置UI组件错误通知和加载指示器"""
_setup_error_notification()
_setup_loading_indicator()
## 连接所有信号
func _connect_all_signals():
"""连接所有必要的信号"""
# 游戏状态管理器信号
if not game_state_manager.state_changed.is_connected(_on_game_state_changed):
game_state_manager.state_changed.connect(_on_game_state_changed)
# 网络管理器信号
_connect_network_signals()
# UI信号延迟连接
_connect_ui_signals()
## 连接网络信号
func _connect_network_signals():
"""连接网络管理器的所有信号"""
var signals_to_connect = [
["connected_to_server", _on_connected_to_server],
["disconnected_from_server", _on_disconnected_from_server],
["connection_error", _on_connection_error],
["message_received", _on_message_received]
]
for signal_data in signals_to_connect:
var signal_name = signal_data[0]
var callback = signal_data[1]
var signal_obj = network_manager.get(signal_name)
if signal_obj and not signal_obj.is_connected(callback):
signal_obj.connect(callback)
## 设置错误通知系统
func _setup_error_notification():
"""设置错误通知系统"""
var error_scene = load("res://scenes/ErrorNotification.tscn")
if error_scene:
error_notification = error_scene.instantiate()
ui_layer.add_child(error_notification)
print("[MAIN] Error notification system initialized")
else:
print("[ERROR] Failed to load ErrorNotification scene")
## 设置加载指示器
func _setup_loading_indicator():
"""设置加载指示器"""
var loading_scene = load("res://scenes/LoadingIndicator.tscn")
if loading_scene:
loading_indicator = loading_scene.instantiate()
ui_layer.add_child(loading_indicator)
print("[MAIN] Loading indicator initialized")
else:
print("[ERROR] Failed to load LoadingIndicator scene")
## 连接 UI 信号
func _connect_ui_signals():
"""连接 UI 层的信号"""
# 延迟一帧,确保 UI 元素已创建
await get_tree().process_frame
# 连接登录界面信号
if ui_layer.login_screen:
ui_layer.login_screen.login_requested.connect(login_to_server)
ui_layer.login_screen.create_character_requested.connect(_on_create_character_ui_requested)
print("Login screen signals connected")
# 注意:角色创建界面会在切换到 CHARACTER_CREATION 状态时创建和连接
## 处理创建角色 UI 请求
func _on_create_character_ui_requested():
"""处理创建角色 UI 请求"""
# 检查是否已连接并认证
if not network_manager.is_server_connected():
# 未连接,显示错误提示
if error_notification:
error_notification.show_error("请先登录后再创建角色")
return
# 已连接,切换到角色创建界面
game_state_manager.change_state(GameStateManager.GameState.CHARACTER_CREATION)
## 处理返回登录请求
func _on_back_to_login_requested():
"""处理返回登录请求"""
game_state_manager.change_state(GameStateManager.GameState.LOGIN)
## 游戏状态变化处理
func _on_game_state_changed(old_state, new_state):
"""
处理游戏状态变化
@param old_state: 旧状态
@param new_state: 新状态
"""
print("Game state changed: ", GameStateManager.GameState.keys()[old_state], " -> ", GameStateManager.GameState.keys()[new_state])
# 根据状态切换 UI 和场景
match new_state:
GameStateManager.GameState.LOGIN:
ui_layer.show_screen("login")
_cleanup_game_world()
GameStateManager.GameState.CHARACTER_CREATION:
ui_layer.show_screen("character_creation")
# 连接角色创建界面的信号(如果还没连接)
_connect_character_creation_signals()
GameStateManager.GameState.IN_GAME:
ui_layer.show_screen("hud")
_load_game_world()
# 更新HUD的网络状态
_update_hud_network_status()
GameStateManager.GameState.DISCONNECTED:
ui_layer.show_screen("login")
_cleanup_game_world()
## 连接角色创建界面信号
func _connect_character_creation_signals():
"""连接角色创建界面的信号"""
if ui_layer.character_creation and not ui_layer.character_creation.character_created.is_connected(create_character):
ui_layer.character_creation.character_created.connect(create_character)
ui_layer.character_creation.back_requested.connect(_on_back_to_login_requested)
print("Character creation signals connected")
## 网络连接成功
func _on_connected_to_server():
"""服务器连接成功"""
print("Connected to server successfully")
if loading_indicator:
loading_indicator.hide_loading()
if error_notification:
error_notification.show_success("连接成功!")
# 更新HUD网络状态
_update_hud_network_status()
## 更新HUD网络状态
func _update_hud_network_status():
"""更新HUD的网络状态显示"""
if ui_layer.hud:
ui_layer.hud.update_network_status(network_manager.is_server_connected())
## 网络断开连接
func _on_disconnected_from_server():
"""服务器断开连接"""
print("Disconnected from server")
if loading_indicator:
loading_indicator.hide_loading()
# 根据当前状态决定如何处理断线
var current_state = game_state_manager.current_state
if current_state == GameStateManager.GameState.IN_GAME:
# 在游戏中断线,显示警告但不退出游戏
if error_notification:
error_notification.show_warning("与服务器断开连接,正在尝试重连...", 3.0)
# 不改变游戏状态,让玩家继续游戏(离线模式)
else:
# 在登录或角色创建时断线,返回登录界面
if error_notification:
error_notification.show_warning("与服务器断开连接")
game_state_manager.change_state(GameStateManager.GameState.DISCONNECTED)
## 网络连接错误
func _on_connection_error(error: String):
"""网络连接错误"""
print("Connection error: ", error)
if loading_indicator:
loading_indicator.hide_loading()
if error_notification:
error_notification.show_network_error(error)
## 接收到网络消息
func _on_message_received(message: Dictionary):
"""
处理接收到的网络消息
@param message: 消息字典
"""
if not message.has("type"):
print("Warning: Received message without type")
return
var msg_type = message["type"]
print("Received message: ", msg_type)
# 根据消息类型处理
match msg_type:
"auth_response":
_handle_auth_response(message)
"character_create":
_handle_character_create_response(message)
"character_move":
_handle_character_move(message)
"character_state":
_handle_character_state(message)
"world_state":
_handle_world_state(message)
"friend_request", "friend_response", "private_message", "event_invitation", "social_update":
# 社交相关消息由SocialManager处理
pass
_:
print("Unknown message type: ", msg_type)
## 处理认证响应
func _handle_auth_response(message: Dictionary):
"""处理认证响应"""
var data = message.get("data", {})
if data.get("success", false):
print("Authentication successful")
# 进入角色创建或游戏
game_state_manager.change_state(GameStateManager.GameState.CHARACTER_CREATION)
else:
var error_msg = data.get("error", "Unknown error")
print("Authentication failed: ", error_msg)
if error_notification:
error_notification.show_error("登录失败: " + error_msg)
## 处理角色创建响应
func _handle_character_create_response(message: Dictionary):
"""处理角色创建响应"""
var data = message.get("data", {})
if data.get("success", false):
print("Character created successfully")
var character_data = data.get("character", {})
game_state_manager.player_data = character_data
# 隐藏加载指示器
if loading_indicator:
loading_indicator.hide_loading()
game_state_manager.change_state(GameStateManager.GameState.IN_GAME)
else:
var error_msg = data.get("error", "Unknown error")
print("Character creation failed: ", error_msg)
# 隐藏加载指示器并显示错误
if loading_indicator:
loading_indicator.hide_loading()
if error_notification:
error_notification.show_error("创建角色失败: " + error_msg)
## 处理角色移动消息
func _handle_character_move(message: Dictionary):
"""处理其他角色的移动"""
if not world_manager:
return
var data = message.get("data", {})
var character_id = data.get("characterId", "")
var position = data.get("position", {})
print("Received character_move message - ID: ", character_id, " Position: ", position)
# 检查是否是玩家自己的角色
var player_id = game_state_manager.player_data.get("id", "") if game_state_manager.player_data else ""
if character_id == player_id:
print("IGNORED: Received move message for player's own character - preventing auto-movement")
return # 忽略玩家自己的移动消息,避免网络回环导致自动移动
if character_id and position:
var pos = Vector2(position.get("x", 0), position.get("y", 0))
world_manager.update_character_position(character_id, pos)
## 处理角色状态更新
func _handle_character_state(message: Dictionary):
"""处理角色状态更新"""
if not world_manager:
return
var data = message.get("data", {})
var character_id = data.get("characterId", "")
var character_name = data.get("name", "")
var position = data.get("position", {})
var is_online = data.get("isOnline", false)
# 检查是否是玩家自己的角色,避免处理自己的状态更新
var player_id = game_state_manager.player_data.get("id", "") if game_state_manager.player_data else ""
if character_id == player_id:
print("Ignoring state update for player's own character: ", character_id)
return
if character_id:
var state = {
"id": character_id,
"name": character_name,
"position": position,
"isOnline": is_online
}
print("Processing character state update - ID: ", character_id, " Online: ", is_online)
world_manager.update_character_state(character_id, state)
## 处理世界状态同步
func _handle_world_state(message: Dictionary):
"""处理世界状态同步"""
print("Received world state")
var data = message.get("data", {})
var characters = data.get("characters", [])
if world_manager:
# 更新所有角色
for character_data in characters:
var char_id = character_data.get("id", "")
if char_id and char_id != game_state_manager.player_data.get("id", ""):
# 不是玩家自己,生成或更新远程角色
world_manager.spawn_or_update_character(character_data)
## 加载游戏世界
func _load_game_world():
"""加载游戏世界场景"""
print("Loading game world...")
# 加载 Datawhale 办公室场景
var office_scene_path = "res://scenes/DatawhaleOffice.tscn"
var office_packed = load(office_scene_path)
if office_packed:
office_scene = office_packed.instantiate()
game_world.add_child(office_scene)
# 获取世界管理器
world_manager = office_scene.get_node_or_null("WorldManager")
if not world_manager:
# 如果场景中没有,创建一个
world_manager = preload("res://scripts/WorldManager.gd").new()
world_manager.name = "WorldManager"
office_scene.add_child(world_manager)
# 设置角色容器
var characters_container = office_scene.get_node_or_null("Characters")
if characters_container:
world_manager.set_character_container(characters_container)
print("Character container set for WorldManager")
else:
print("Warning: Characters container not found in office scene")
# 创建玩家角色
_spawn_player_character()
# 创建输入处理器
_setup_input_handler()
# 初始化对话测试管理器
_setup_dialogue_test_manager()
print("Game world loaded")
else:
print("Error: Failed to load office scene")
## 生成玩家角色
func _spawn_player_character():
"""生成玩家角色"""
print("Spawning player character...")
var player_scene_path = "res://scenes/PlayerCharacter.tscn"
var player_packed = load(player_scene_path)
if player_packed and office_scene:
player_character = player_packed.instantiate()
# 获取角色容器
var characters_container = office_scene.get_characters_container()
if characters_container:
characters_container.add_child(player_character)
else:
office_scene.add_child(player_character)
# 初始化玩家角色
var player_data = game_state_manager.player_data
print("Player data from game_state_manager: ", player_data)
if player_data:
player_character.initialize(player_data)
# 设置初始位置(场景中央)- 覆盖服务器位置
print("Setting player position to scene center: Vector2(1000, 750)")
player_character.global_position = Vector2(1000, 750)
# 确保角色完全静止 - 延迟执行确保所有系统都已初始化
_ensure_player_character_stopped()
# 设置相机跟随
if office_scene.has_method("set_camera_target"):
office_scene.set_camera_target(player_character)
# 连接角色信号
player_character.position_updated.connect(_on_player_position_updated)
print("Player character spawned")
else:
print("Error: Failed to load player character scene")
## 确保玩家角色停止移动
func _ensure_player_character_stopped():
"""确保玩家角色完全停止移动"""
if player_character and player_character.has_method("_reset_movement_state"):
# 延迟执行,确保所有初始化完成
await get_tree().process_frame
player_character._reset_movement_state()
print("Player character movement state ensured to be stopped")
# 同时清除输入处理器的状态
if input_handler and input_handler.has_method("_clear_all_movement_state"):
input_handler._clear_all_movement_state()
print("Input handler movement state cleared")
## 设置输入处理器
func _setup_input_handler():
"""设置输入处理器"""
print("Setting up input handler...")
# 创建输入处理器
input_handler = preload("res://scripts/InputHandler.gd").new()
input_handler.name = "InputHandler"
add_child(input_handler)
# 连接输入信号
input_handler.move_input.connect(_on_move_input)
input_handler.interact_input.connect(_on_interact_input)
print("Input handler ready")
## 玩家移动输入
func _on_move_input(direction: Vector2):
"""
处理玩家移动输入
@param direction: 移动方向
"""
if direction != Vector2.ZERO:
print("Player input received - Direction: ", direction)
if player_character:
player_character.move_to(direction)
## 玩家交互输入
func _on_interact_input():
"""处理玩家交互输入"""
print("Interact key pressed (E)")
if not player_character:
print("No player character")
return
# 获取附近的NPC
if dialogue_test_manager:
var player_pos = player_character.global_position
var nearby_npcs = dialogue_test_manager.get_nearby_npcs(player_pos, 100.0)
if nearby_npcs.size() > 0:
var closest_npc = nearby_npcs[0]
print("Starting dialogue with: ", closest_npc.name)
# 开始对话
dialogue_test_manager.start_dialogue_with_npc(closest_npc.id)
# 显示对话框
if ui_layer and ui_layer.dialogue_box:
ui_layer.show_dialogue(closest_npc.name)
else:
print("Dialogue box not available")
else:
print("No NPCs nearby to interact with")
# 显示提示
if ui_layer and ui_layer.hud:
ui_layer.hud.show_interaction_hint("附近没有可对话的角色")
# 2秒后隐藏提示
await get_tree().create_timer(2.0).timeout
ui_layer.hud.hide_interaction_hint()
else:
print("Dialogue test manager not available")
## 对话消息接收处理
func _on_dialogue_message_received(sender: String, message: String):
"""处理接收到的对话消息显示在UI中"""
if ui_layer and ui_layer.dialogue_box:
# 只显示NPC的消息玩家消息已经在DialogueBox中直接显示了
if sender != "player":
var display_name = sender
ui_layer.dialogue_box.add_message(display_name, message)
else:
# 玩家消息已经显示,这里只做日志记录
print("Player message processed: ", message)
## 玩家位置更新
func _on_player_position_updated(new_position: Vector2):
"""
玩家位置更新时发送到服务器
@param new_position: 新位置
"""
if network_manager.is_server_connected():
var message = MessageProtocol.create_character_move("", new_position, Vector2.ZERO)
network_manager.send_message(message)
## 清理游戏世界
func _cleanup_game_world():
"""清理游戏世界"""
print("Cleaning up game world...")
if input_handler:
input_handler.queue_free()
input_handler = null
if office_scene:
office_scene.queue_free()
office_scene = null
player_character = null
world_manager = null
print("Game world cleaned up")
## 登录到服务器
func login_to_server(username: String):
"""
登录到服务器
@param username: 用户名
"""
print("Attempting to login as: ", username)
# 显示加载状态
if loading_indicator:
loading_indicator.show_connecting()
# 如果已经连接,直接发送认证请求
if network_manager.is_server_connected():
_send_auth_request(username)
return
# 否则先连接到服务器
network_manager.connect_to_server(server_url)
# 等待连接成功
await network_manager.connected_to_server
# 连接成功后发送认证请求
_send_auth_request(username)
## 发送认证请求
func _send_auth_request(username: String):
"""发送认证请求到服务器"""
print("Sending auth request for: ", username)
var auth_message = MessageProtocol.create_auth_request(username)
network_manager.send_message(auth_message)
## 创建角色
func create_character(character_name: String, personalization_data: Dictionary = {}):
"""
创建新角色
@param character_name: 角色名称
@param personalization_data: 个性化数据(外观、个性等)
"""
print("Creating character: ", character_name)
print("Personalization data: ", personalization_data)
# 检查是否已连接
if not network_manager.is_server_connected():
if error_notification:
error_notification.show_error("未连接到服务器,无法创建角色")
# 返回登录界面
game_state_manager.change_state(GameStateManager.GameState.LOGIN)
return
# 显示加载状态
if loading_indicator:
loading_indicator.show_creating_character()
# 创建包含个性化数据的角色创建消息
var create_message = MessageProtocol.create_character_create(character_name, personalization_data)
network_manager.send_message(create_message)
## 设置对话测试管理器
func _setup_dialogue_test_manager():
"""设置对话测试管理器"""
print("Setting up dialogue test manager...")
# 创建简单对话测试脚本
var test_script = load("res://scripts/SimpleDialogueTest.gd")
dialogue_test_manager = test_script.new()
dialogue_test_manager.name = "SimpleDialogueTest"
add_child(dialogue_test_manager)
# 获取对话系统引用
var dialogue_system = office_scene.get_node_or_null("DialogueSystem")
if not dialogue_system:
# 如果场景中没有,创建一个
var dialogue_script = load("res://scripts/DialogueSystem.gd")
dialogue_system = dialogue_script.new()
dialogue_system.name = "DialogueSystem"
office_scene.add_child(dialogue_system)
# 设置测试环境
dialogue_test_manager.setup_test(world_manager, dialogue_system)
# 连接对话系统信号
if dialogue_system.message_received.connect(dialogue_test_manager.handle_player_message) != OK:
print("Failed to connect dialogue signal")
# 连接对话系统消息到UI
if dialogue_system.message_received.connect(_on_dialogue_message_received) != OK:
print("Failed to connect dialogue UI signal")
# 设置对话系统的社交管理器引用
if social_manager:
dialogue_system.set_social_manager(social_manager)
# 延迟生成测试NPC确保所有系统都已初始化
if player_character:
var player_pos = player_character.global_position
# 使用call_deferred确保在下一帧执行
dialogue_test_manager.call_deferred("spawn_test_npcs", player_pos)
print("Dialogue test manager setup complete")
## 处理社交通知
func _on_social_notification(notification_type: String, title: String, message: String, _data: Dictionary):
"""
处理社交系统通知
@param notification_type: 通知类型
@param title: 通知标题
@param message: 通知消息
@param _data: 通知数据 (暂未使用)
"""
print("[SOCIAL] Notification: ", notification_type, " - ", title, ": ", message)
# 显示通知给用户
if error_notification:
match notification_type:
"friend_request", "friend_accepted", "event_invitation":
error_notification.show_success(message, 5.0)
"friend_online":
error_notification.show_info(message, 3.0)
"milestone":
error_notification.show_success(message, 8.0)
_:
error_notification.show_info(message, 4.0)
## 处理好友活动
func _on_friend_activity(activity_type: String, data: Dictionary):
"""
处理好友活动事件
@param activity_type: 活动类型
@param data: 活动数据
"""
print("[SOCIAL] Friend activity: ", activity_type, " - ", data)
# 这里可以更新UI显示好友活动
# 比如更新好友列表、活动日志等
func _process(_delta):
# 主循环处理
pass