#include "character/CharacterObject.h" #include namespace frostbite2D { bool CharacterObject::Construction(int jobId) { EnableEventReceive(); RemoveAllChildren(); animationManager_ = nullptr; config_.reset(); currentAction_.clear(); actionLibrary_ = CharacterActionLibrary(); commandBuffer_.Clear(); currentIntent_ = CharacterIntent(); stateMachine_.Reset(); motor_ = CharacterMotor(); status_ = CharacterStatus(); lastDeltaTime_ = 0.0f; auto config = character::loadCharacterConfig(jobId); if (!config) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CharacterObject: failed to load job %d config", jobId); return false; } jobId_ = jobId; growType_ = -1; direction_ = 1; config_ = *config; equipmentManager_.Init(config_->baseJobConfig); if (auto actionLibrary = loadCharacterActionLibrary(*config_)) { actionLibrary_ = *actionLibrary; } animationManager_ = MakePtr(); if (!animationManager_->Init(this, *config_, equipmentManager_)) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "CharacterObject: no layered avatar animations available, character will stay empty"); } AddChild(animationManager_); if (const auto* idleAction = actionLibrary_.GetDefaultAction()) { PlayAnimationTag(idleAction->animationTag); } else if (config_->animationPath.count("rest") > 0) { SetAction("rest"); } else if (!config_->animationPath.empty()) { SetAction(config_->animationPath.begin()->first); } SetWorldPosition({}); SetFacing(1); return true; } void CharacterObject::SetAction(const std::string& actionName) { currentAction_ = actionName; if (animationManager_) { animationManager_->SetAction(actionName); } } void CharacterObject::SetDirection(int direction) { direction_ = direction >= 0 ? 1 : -1; if (animationManager_) { animationManager_->SetDirection(direction_); } } void CharacterObject::SetCharacterPosition(const Vec2& pos) { motor_.SetGroundPosition(pos); SetWorldPosition(motor_.position); } void CharacterObject::SetWorldPosition(const CharacterWorldPosition& pos) { motor_.position = pos; // 逻辑世界坐标是 x/y/z,真正渲染时再投影到屏幕坐标。 SetPosition(pos.ToScreenPosition()); // 地面 y 继续决定同层对象前后遮挡顺序。 SetZOrder(static_cast(pos.y)); } void CharacterObject::PushCommand(const CharacterCommand& command) { commandBuffer_.Submit(command); } void CharacterObject::ApplyHit(const HitContext& hit) { if (status_.invincible) { return; } if (status_.superArmor && !hit.ignoreInterrupt) { return; } if (!hit.ignoreInterrupt && !stateMachine_.CanBeInterrupted()) { return; } const CharacterActionDefinition* hurtAction = FindAction(hit.hurtAction); if (!hurtAction) { hurtAction = FindAction("hurt_light"); } motor_.verticalVelocity = hit.launchZVelocity; if (hit.launchZVelocity > 0.0f) { motor_.grounded = false; } stateMachine_.ForceHurt(hurtAction); PlayAnimationTag(hurtAction ? hurtAction->animationTag : ResolveAnimationTag("hurt_light", "rest")); } void CharacterObject::OnUpdate(float deltaTime) { // 角色主循环顺序固定为: // 输入采样 -> 缓冲推进 -> 意图生成 -> 状态机决策 -> motor 推进 -> 投影到屏幕。 lastDeltaTime_ = deltaTime; commandBuffer_.Advance(deltaTime); inputRouter_.EmitCommands(commandBuffer_); currentIntent_ = commandBuffer_.BuildIntent(); stateMachine_.Update(*this, commandBuffer_, currentIntent_, deltaTime); motor_.Update(deltaTime); SetWorldPosition(motor_.position); SetFacing(motor_.facing); } bool CharacterObject::OnKeyDown(const KeyEvent& event) { if (event.isRepeat()) { return false; } return inputRouter_.OnKeyDown(event); } bool CharacterObject::OnKeyUp(const KeyEvent& event) { return inputRouter_.OnKeyUp(event.getKeyCode()); } bool CharacterObject::OnJoystickAxis(const JoystickEvent& event) { return inputRouter_.OnJoystickAxis(static_cast(event)); } bool CharacterObject::OnJoystickButtonDown(const JoystickEvent& event) { return inputRouter_.OnJoystickButtonDown( static_cast(event)); } bool CharacterObject::OnJoystickButtonUp(const JoystickEvent& event) { return inputRouter_.OnJoystickButtonUp( static_cast(event)); } const CharacterActionDefinition* CharacterObject::FindAction( const std::string& actionId) const { return actionLibrary_.FindAction(actionId); } std::string CharacterObject::ResolveAnimationTag( const std::string& preferred, const std::string& fallback) const { // 逻辑动作与资源动作是两层名字:优先选动作定义里的标签,不存在再回退。 if (animationManager_ && animationManager_->HasAction(preferred)) { return preferred; } if (animationManager_ && animationManager_->HasAction(fallback)) { return fallback; } if (config_ && !config_->animationPath.empty()) { return config_->animationPath.begin()->first; } return preferred; } void CharacterObject::PlayAnimationTag(const std::string& actionTag) { SetAction(actionTag); } void CharacterObject::SetFacing(int direction) { motor_.facing = direction >= 0 ? 1 : -1; SetDirection(motor_.facing); } } // namespace frostbite2D