197 lines
6.3 KiB
C++
197 lines
6.3 KiB
C++
#include "Character.h"
|
|
#include "Animator.h"
|
|
#include "tFunctions.h"
|
|
#include <cctype>
|
|
#include <cmath>
|
|
#include <glm/gtc/quaternion.hpp>
|
|
|
|
// -------------------------------------------------------
|
|
// 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<glm::mat4> 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
|
|
} |