extends CharacterBody2D class_name CharacterController ## 角色控制器 ## 处理角色的移动、动画和状态 # 角色属性 var character_id: String = "" var character_name: String = "" var is_online: bool = true var move_speed: float = preload("res://scripts/GameConfig.gd").get_character_move_speed() # 个性化属性 var character_level: int = 1 var character_experience: int = 0 var character_status: String = "active" var character_mood: String = "neutral" var character_appearance: Dictionary = {} var character_personality: Dictionary = {} var character_attributes: Dictionary = {} var character_skills: Dictionary = {} var character_achievements: Array = [] # 状态管理器 var status_manager: CharacterStatusManager # 角色状态枚举 enum CharacterState { IDLE, WALKING, TALKING } var current_state: CharacterState = CharacterState.IDLE # 移动相关 var current_direction: Vector2 = Vector2.ZERO var target_position: Vector2 = Vector2.ZERO var is_moving_smooth: bool = false var position_tween: Tween = null # 交互相关 var interaction_range: float = preload("res://scripts/GameConfig.gd").get_interaction_range() var nearby_character: CharacterController = null # 信号 signal state_changed(old_state: CharacterState, new_state: CharacterState) signal position_updated(new_position: Vector2) signal interaction_available(target_character: CharacterController) signal interaction_unavailable() func _ready(): """初始化角色控制器""" # 设置碰撞层 collision_layer = 4 # Layer 3 (2^2 = 4): 角色层 collision_mask = 3 # Layers 1-2: 墙壁和家具 # 确保有碰撞形状 _ensure_collision_shape() # 初始化位置和速度 target_position = global_position velocity = Vector2.ZERO # 确保初始速度为零 # 初始化状态管理器 status_manager = CharacterStatusManager.new() add_child(status_manager) # 连接状态管理器信号 status_manager.status_changed.connect(_on_status_changed) status_manager.mood_changed.connect(_on_mood_changed) ## 确保角色有碰撞形状 func _ensure_collision_shape() -> void: """ 确保角色有碰撞形状,如果没有则创建默认的 """ var collision_shape = get_node_or_null("CollisionShape2D") if not collision_shape: # 创建默认碰撞形状 collision_shape = CollisionShape2D.new() var shape = CircleShape2D.new() shape.radius = 16.0 # 默认半径 collision_shape.shape = shape add_child(collision_shape) collision_shape.name = "CollisionShape2D" func _physics_process(delta: float): """物理帧更新""" if is_moving_smooth: _update_smooth_movement(delta) else: # 总是更新直接移动,即使方向为零(这样可以停止角色) _update_direct_movement(delta) ## 直接移动(用于本地玩家输入) func move_to(direction: Vector2) -> void: """ 根据方向向量移动角色 @param direction: 移动方向(已归一化) """ current_direction = direction if direction != Vector2.ZERO: _change_state(CharacterState.WALKING) else: _change_state(CharacterState.IDLE) velocity = Vector2.ZERO # 立即停止移动 ## 平滑移动到目标位置(用于网络同步) func set_position_smooth(target_pos: Vector2, use_tween: bool = false) -> void: """ 平滑移动到目标位置(用于网络延迟补偿) @param target_pos: 目标位置 @param use_tween: 是否使用 Tween 动画(更平滑但可能有延迟) """ target_position = target_pos if use_tween: # 使用 Tween 实现更平滑的移动 if position_tween: position_tween.kill() position_tween = create_tween() var distance = global_position.distance_to(target_pos) var duration = distance / move_speed position_tween.tween_property(self, "global_position", target_pos, duration) position_tween.finished.connect(_on_tween_finished) _change_state(CharacterState.WALKING) else: # 使用物理移动(更适合实时网络同步) is_moving_smooth = true _change_state(CharacterState.WALKING) ## Tween 完成回调 func _on_tween_finished() -> void: """Tween 动画完成时调用""" _change_state(CharacterState.IDLE) position_updated.emit(global_position) ## 直接设置位置(瞬移) func set_position_immediate(pos: Vector2) -> void: """ 立即设置角色位置(无动画) @param pos: 新位置 """ global_position = pos target_position = pos is_moving_smooth = false position_updated.emit(pos) ## 播放动画 func play_animation(anim_name: String) -> void: """ 播放指定动画 @param anim_name: 动画名称(idle, walking, talking) """ # 查找 AnimationPlayer 节点 var anim_player = get_node_or_null("AnimationPlayer") if anim_player and anim_player is AnimationPlayer: # 检查动画是否存在 if anim_player.has_animation(anim_name): anim_player.play(anim_name) # 如果没有 AnimationPlayer,使用简单的视觉反馈 _update_animation_state(anim_name) ## 设置在线状态 func set_online_status(online: bool) -> void: """ 设置角色在线/离线状态 @param online: 是否在线 """ is_online = online _update_visual_status() ## 获取当前状态 func get_current_state() -> CharacterState: """ 获取当前角色状态 @return: 当前状态 """ return current_state ## 初始化角色数据 func initialize(data: Dictionary) -> void: """ 使用角色数据初始化控制器 @param data: 角色数据字典 """ print("CharacterController.initialize() called with data: ", data) if data.has(CharacterData.FIELD_ID): character_id = data[CharacterData.FIELD_ID] if data.has(CharacterData.FIELD_NAME): character_name = data[CharacterData.FIELD_NAME] if data.has(CharacterData.FIELD_IS_ONLINE): is_online = data[CharacterData.FIELD_IS_ONLINE] # 加载个性化数据 _load_personalization_data(data) # 设置初始位置 var pos = CharacterData.get_position(data) print("Setting character position from data: ", pos) set_position_immediate(pos) # 完全重置所有移动状态 _reset_movement_state() # 设置状态管理器数据 if status_manager: status_manager.set_character_data(data) # 更新名称标签 _update_name_label() _update_visual_status() _update_appearance() ## 重置移动状态 func _reset_movement_state() -> void: """完全重置角色的移动状态""" velocity = Vector2.ZERO current_direction = Vector2.ZERO target_position = global_position is_moving_smooth = false # 停止任何正在进行的Tween if position_tween: position_tween.kill() position_tween = null # 设置为空闲状态 _change_state(CharacterState.IDLE) print("Character movement state completely reset") ## 更新名称标签 func _update_name_label() -> void: """更新角色名称标签""" var name_label = get_node_or_null("NameLabel") if not name_label: # 使用工具类创建带阴影的标签 name_label = preload("res://scripts/Utils.gd").create_label_with_shadow("", 14) name_label.name = "NameLabel" add_child(name_label) # 设置名称文本(包含等级和心情) var display_name = character_name if not character_name.is_empty() else "Unknown" var personalization = preload("res://scripts/CharacterPersonalization.gd") var mood_emoji = personalization.get_mood_emoji(character_mood) var level_text = "Lv.%d" % character_level if character_level > 1 else "" var full_text = display_name if not level_text.is_empty(): full_text += " " + level_text full_text += " " + mood_emoji name_label.text = full_text # 调整位置(在角色上方) name_label.position = Vector2(-60, -40) # 居中并在角色上方 name_label.size = Vector2(120, 20) ## 私有方法:更新直接移动 func _update_direct_movement(_delta: float) -> void: """ 更新基于输入的直接移动 @param _delta: 帧时间(保留用于未来扩展) """ # 计算速度 if current_direction == Vector2.ZERO: velocity = Vector2.ZERO else: velocity = current_direction.normalized() * move_speed # 执行移动并处理碰撞 var previous_position = global_position move_and_slide() # 检查是否发生了碰撞 if get_slide_collision_count() > 0: # 发生碰撞,角色被阻挡 pass # 发射位置更新信号(只在位置改变时) if global_position != previous_position: target_position = global_position position_updated.emit(global_position) ## 私有方法:更新平滑移动 func _update_smooth_movement(_delta: float) -> void: """ 更新平滑移动到目标位置 @param _delta: 帧时间(未使用,保留用于未来扩展) """ var distance = global_position.distance_to(target_position) # 如果已经很接近目标位置,停止移动 if distance < 1.0: global_position = target_position is_moving_smooth = false _change_state(CharacterState.IDLE) position_updated.emit(global_position) return # 计算移动方向和速度 var direction = (target_position - global_position).normalized() velocity = direction * move_speed # 执行移动 move_and_slide() position_updated.emit(global_position) ## 私有方法:改变状态 func _change_state(new_state: CharacterState) -> void: """ 改变角色状态 @param new_state: 新状态 """ if current_state == new_state: return var old_state = current_state current_state = new_state state_changed.emit(old_state, new_state) # 根据状态播放动画 match new_state: CharacterState.IDLE: play_animation("idle") CharacterState.WALKING: play_animation("walking") CharacterState.TALKING: play_animation("talking") ## 私有方法:更新动画状态 func _update_animation_state(_anim_name: String) -> void: """ 更新动画状态的视觉反馈(当没有 AnimationPlayer 时) @param _anim_name: 动画名称(未使用,保留用于未来扩展) """ # 这是一个占位实现,用于在没有实际动画资源时提供反馈 # 实际的精灵动画将在任务 11 中添加 pass ## 检测附近角色 func check_nearby_characters(all_characters: Array) -> void: """ 检测附近是否有可交互的角色 @param all_characters: 所有角色的数组 """ var closest_character: CharacterController = null var closest_distance: float = interaction_range for character in all_characters: if character == self or not character is CharacterController: continue var distance = global_position.distance_to(character.global_position) if distance < closest_distance: closest_distance = distance closest_character = character # 检查是否有变化 if closest_character != nearby_character: nearby_character = closest_character if nearby_character: interaction_available.emit(nearby_character) else: interaction_unavailable.emit() ## 获取附近角色 func get_nearby_character() -> CharacterController: """ 获取当前附近的角色 @return: 附近的角色,如果没有则返回 null """ return nearby_character ## 私有方法:更新视觉状态 func _update_visual_status() -> void: """ 更新角色的视觉状态(在线/离线) """ # 查找或创建状态指示器 var status_indicator = get_node_or_null("StatusIndicator") if not status_indicator: # 使用工具类创建状态指示器 status_indicator = preload("res://scripts/Utils.gd").create_status_indicator(is_online) status_indicator.name = "StatusIndicator" status_indicator.position = Vector2(-4, -24) # 在角色上方 add_child(status_indicator) else: # 更新现有指示器的颜色 var personalization = preload("res://scripts/CharacterPersonalization.gd") var color = personalization.get_status_color(character_status) if is_online else Color.GRAY status_indicator.color = color ## 加载个性化数据 func _load_personalization_data(data: Dictionary) -> void: """ 从角色数据中加载个性化信息 @param data: 角色数据字典 """ character_level = data.get(CharacterData.FIELD_LEVEL, 1) character_experience = data.get(CharacterData.FIELD_EXPERIENCE, 0) character_status = data.get(CharacterData.FIELD_STATUS, "active") character_mood = data.get(CharacterData.FIELD_MOOD, "neutral") character_appearance = data.get(CharacterData.FIELD_APPEARANCE, {}) character_personality = data.get(CharacterData.FIELD_PERSONALITY, {}) character_attributes = data.get(CharacterData.FIELD_ATTRIBUTES, {}) character_skills = data.get(CharacterData.FIELD_SKILLS, {}) character_achievements = data.get(CharacterData.FIELD_ACHIEVEMENTS, []) ## 更新外观 func _update_appearance() -> void: """更新角色外观""" var sprite = get_node_or_null("CharacterSprite") if sprite and sprite is CharacterSprite: var personalization = preload("res://scripts/CharacterPersonalization.gd") personalization.apply_appearance_to_sprite(sprite, character_appearance) ## 状态变化回调 func _on_status_changed(old_status: String, new_status: String) -> void: """状态变化时的回调""" character_status = new_status _update_visual_status() print("Character %s status changed: %s -> %s" % [character_name, old_status, new_status]) ## 心情变化回调 func _on_mood_changed(old_mood: String, new_mood: String) -> void: """心情变化时的回调""" character_mood = new_mood _update_name_label() # 更新名称标签以显示心情 print("Character %s mood changed: %s -> %s" % [character_name, old_mood, new_mood]) ## 获取角色信息摘要 func get_character_summary() -> String: """ 获取角色信息摘要 @return: 角色摘要文本 """ var summary = [] summary.append("等级 %d" % character_level) if character_achievements.size() > 0: summary.append("%d 个成就" % character_achievements.size()) var personalization = preload("res://scripts/CharacterPersonalization.gd") var mood_emoji = personalization.get_mood_emoji(character_mood) summary.append("心情 %s" % mood_emoji) return " | ".join(summary) ## 设置角色状态 func set_character_status(status: String) -> void: """ 设置角色状态 @param status: 新状态 """ if status_manager: status_manager.set_status(status) ## 设置角色心情 func set_character_mood(mood: String) -> void: """ 设置角色心情 @param mood: 新心情 """ if status_manager: status_manager.set_mood(mood) ## 触发活动事件 func trigger_activity_event(event_type: String, data: Dictionary = {}) -> void: """ 触发角色活动事件 @param event_type: 事件类型 @param data: 事件数据 """ if status_manager: status_manager.handle_activity_event(event_type, data) ## 增加经验值 func add_experience(experience: int) -> bool: """ 增加经验值 @param experience: 经验值 @return: 是否升级 """ character_experience += experience # 检查升级 var required_exp = CharacterData.get_required_experience(character_level) if character_experience >= required_exp: character_level += 1 character_experience -= required_exp # 触发升级事件 trigger_activity_event("level_up") print("Character %s leveled up to %d!" % [character_name, character_level]) return true return false ## 添加成就 func add_achievement(achievement: Dictionary) -> void: """ 添加成就 @param achievement: 成就数据 """ # 检查是否已有此成就 for existing in character_achievements: if existing.get("id") == achievement.get("id"): return character_achievements.append(achievement) trigger_activity_event("achievement_earned") print("Character %s earned achievement: %s" % [character_name, achievement.get("name", "Unknown")]) ## 获取个性化数据 func get_personalization_data() -> Dictionary: """ 获取当前的个性化数据 @return: 个性化数据字典 """ return { CharacterData.FIELD_LEVEL: character_level, CharacterData.FIELD_EXPERIENCE: character_experience, CharacterData.FIELD_STATUS: character_status, CharacterData.FIELD_MOOD: character_mood, CharacterData.FIELD_APPEARANCE: character_appearance, CharacterData.FIELD_PERSONALITY: character_personality, CharacterData.FIELD_ATTRIBUTES: character_attributes, CharacterData.FIELD_SKILLS: character_skills, CharacterData.FIELD_ACHIEVEMENTS: character_achievements }