#include "Character.h" #include "Animator.h" #include "tFunctions.h" #include #include #include // ------------------------------------------------------- // BodyComponent // ------------------------------------------------------- BodyComponent* CharacterBody::GetComponentOfName(std::string name) { for (BodyComponent* C : components) { if (C->name == name) return C; } return nullptr; } void CharacterBody::ExecuteAction(InputAction* action) { for (BodyComponent* C : components) { if (C->name == action->BodyComponentName) { for (Action* a : C->Actions) { if (a->Name == action->ActionName) { a->FunctionPointer(this, C); break; } } break; } } } // ------------------------------------------------------- // CharacterMind // ------------------------------------------------------- InputAction* CharacterMind::ProduceAction() { // TODO: implement return nullptr; } // ------------------------------------------------------- // Character // ------------------------------------------------------- void Character::PressInput(char letter) { letter = (char)std::toupper((unsigned char)letter); if (letter == 'W' || letter == 'A' || letter == 'S' || letter == 'D') inputLetters.insert(letter); } void Character::ReleaseInput(char letter) { letter = (char)std::toupper((unsigned char)letter); inputLetters.erase(letter); } void Character::ClearInput() { inputLetters.clear(); } glm::vec3 Character::GetMovementDirection(const glm::vec3& forward, const glm::vec3& right) const { const glm::vec3 flatForward = glm::normalize(glm::vec3(forward.x, 0.0f, forward.z)); const glm::vec3 flatRight = glm::normalize(glm::vec3(right.x, 0.0f, right.z)); glm::vec3 direction(0.0f); if (inputLetters.count('W')) direction += flatForward; if (inputLetters.count('S')) direction -= flatForward; if (inputLetters.count('A')) direction += flatRight; if (inputLetters.count('D')) direction -= flatRight; if (glm::length(direction) > 0.0f) direction = glm::normalize(direction); return direction; } void Character::SetTurnDirection(const glm::vec3& direction) { const glm::vec3 flatDirection(direction.x, 0.0f, direction.z); if (glm::length(flatDirection) > 0.0001f) turnDirection = glm::normalize(flatDirection); } void Character::TurnToDirection(float dt) { if (glm::length(turnDirection) <= 0.0001f) return; const glm::quat target = LookAt(-turnDirection); const glm::quat current = body->t.GetRotationQuaternion(); const float blend = 1.0f - std::exp(-turnSpeed * dt); body->t.RotateToQuaternion(glm::normalize(glm::slerp(current, target, blend))); } void Character::SetPunchAnimation(Animation* animation) { if (!body || !animation) return; if (!punchAnimator) { punchAnimator = body->AddAnimator(10); punchAnimator->blendDuration = 0.08f; } punchAnimation = animation; punchAnimator->SetAnimation(animation); punchAnimator->loops = false; punchAnimator->playbackSpeed = punchAnimationSpeed; punchAnimator->onAnimationEnd = [this]() { OnPunchAnimationEnd(); }; punchAnimator->StopAnimation(); punchHitbox.active = false; punchHitbox.hitTargets.clear(); } void Character::SetPunchLooping(bool looping) { if (punchAnimator) punchAnimator->loops = looping; } void Character::Punch(const glm::vec3& lookDirection) { if (!punchAnimation || !punchAnimator || punchAnimator->isPlaying) return; const glm::vec3 flatLook(lookDirection.x, 0.0f, lookDirection.z); punchLookLocked = glm::length(flatLook) > 0.0001f; if (punchLookLocked) { punchLookDirection = glm::normalize(flatLook); SetTurnDirection(punchLookDirection); } punchHitbox.hitTargets.clear(); punchAnimator->playbackSpeed = punchAnimationSpeed; punchAnimator->PlayAnimation(); } void Character::SetWalking(bool walking) { if (body && body->animator) body->animator->playbackSpeed = walking ? 1.0f : 0.0f; } glm::vec3 Character::GetForwardDirection() const { if (!body) return glm::vec3(0.0f, 0.0f, -1.0f); glm::vec3 forward = -body->t.GetFrontVector(); forward.y = 0.0f; const float length = glm::length(forward); if (length <= 0.0001f) return glm::vec3(0.0f, 0.0f, -1.0f); return forward / length; } void Character::OnPunchAnimationEnd() { if (!body) return; punchHitbox.hitTargets.clear(); glm::vec3 forward = -body->t.GetFrontVector(); forward.y = 0.0f; if (glm::length(forward) <= 0.0001f) return; forward = glm::normalize(forward); punchHitbox.transform.TranslateTo(body->t.GetTranslation() + forward * punchReach); punchHitbox.transform.RotateToQuaternion(LookAt(forward)); punchHitbox.transform.ScaleTo(punchHitboxSize); punchHitbox.damage = (int)punchDamage; punchHitbox.knockback = 3.5f; punchHitbox.active = true; } Character::Character() { body = new CharacterBody(); mind = nullptr; shadertype = ShaderType::RigShader; } void Character::Render(Shader& shader) { if (!body || !body->rig || !body->animator) return; if (body->animator->m_CurrentAnimation == nullptr) { for (int i = 0; i < 100; i++) shader.SetMat4(("finalBonesMatrices[" + std::to_string(i) + "]").c_str(), glm::mat4(1.0f)); } else { std::vector transforms = body->GetBlendedBoneMatrices(); for (int i = 0; i < transforms.size(); i++) shader.SetMat4(("finalBonesMatrices[" + std::to_string(i) + "]").c_str(), transforms[i]); } shader.Set4F("colorOverride", colorOverride); shader.SetInt("useTexture", 0); shader.SetInt("tex", 1); shader.Set1F("metallic", metallic); shader.Set1F("roughness", roughness); shader.Set1F("ao", ao); // body->t's scale is the hitbox only; the rig is drawn at visualScale so it fills that hitbox body->rig->Render(shader, body->t.GetTranslationMatrix() * body->t.GetRotationMatrix() * glm::scale(glm::mat4(1.0f), glm::vec3(visualScale))); shader.Set4F("colorOverride", glm::vec4(0.0f)); // don't leak into other rig draws }