188 lines
5.4 KiB
C++
188 lines
5.4 KiB
C++
#ifndef CHARACTER_CLASS
|
|
#define CHARACTER_CLASS
|
|
#include <vector>
|
|
#include <string>
|
|
#include <functional>
|
|
#include <algorithm>
|
|
#include <set>
|
|
#include "t.h"
|
|
#include "Rig.h"
|
|
#include "Object.h"
|
|
#include "Animator.h"
|
|
|
|
class Character;
|
|
class CharacterManager;
|
|
class CharacterBody;
|
|
class BodyComponent;
|
|
|
|
struct Hitbox {
|
|
t transform;
|
|
int damage = 0;
|
|
float knockback = 0.0f;
|
|
bool active = false;
|
|
std::set<Character*> hitTargets;
|
|
|
|
bool CanHit(Character* target) const {
|
|
return target && hitTargets.find(target) == hitTargets.end();
|
|
}
|
|
|
|
void RegisterHit(Character* target) {
|
|
if (target) hitTargets.insert(target);
|
|
}
|
|
};
|
|
|
|
class Action {
|
|
public:
|
|
std::string Name;
|
|
std::function<void(CharacterBody*, BodyComponent*)> FunctionPointer;
|
|
};
|
|
|
|
class InputAction {
|
|
public:
|
|
std::string BodyComponentName;
|
|
std::string ActionName;
|
|
};
|
|
|
|
class BodyComponent {
|
|
public:
|
|
std::string name;
|
|
BodyComponent* ParentComponent = nullptr;
|
|
std::vector<Action*> Actions;
|
|
};
|
|
|
|
class CharacterBody : public t_package {
|
|
public:
|
|
int Health = 100;
|
|
Rig* rig = nullptr;
|
|
|
|
std::vector<Animator*> animators;
|
|
Animator* animator = nullptr; // back-compat: base animator at weight 0
|
|
|
|
std::vector<BodyComponent*> components;
|
|
BodyComponent* RootComponent = nullptr;
|
|
|
|
CharacterBody() {
|
|
animator = AddAnimator(0);
|
|
animator->blendWeight = 1.0f; // base always fully visible
|
|
}
|
|
|
|
Animator* AddAnimator(int weight) {
|
|
Animator* a = new Animator();
|
|
a->weight = weight;
|
|
if (rig) a->SetRig(rig);
|
|
animators.push_back(a);
|
|
std::sort(animators.begin(), animators.end(),
|
|
[](Animator* a, Animator* b) { return a->weight < b->weight; });
|
|
return a;
|
|
}
|
|
|
|
// Returns weighted blend of all playing animators.
|
|
// Base (weight 0) is always the foundation; higher-weight animators
|
|
// blend in on top based on their blendWeight (0?1).
|
|
std::vector<glm::mat4> GetBlendedBoneMatrices() const {
|
|
// Start from the base animator
|
|
std::vector<glm::mat4> result = animator->GetFinalBoneMatrices();
|
|
const int count = (int)result.size();
|
|
|
|
// Blend each higher-priority playing animator on top
|
|
for (int i = 1; i < (int)animators.size(); i++) {
|
|
Animator* a = animators[i];
|
|
if (!a->isPlaying || !a->m_CurrentAnimation || a->blendWeight <= 0.0f) continue;
|
|
const auto& matrices = a->GetFinalBoneMatrices();
|
|
for (int j = 0; j < count && j < (int)matrices.size(); j++)
|
|
result[j] = glm::mix(result[j], matrices[j], a->blendWeight);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Returns the highest-weight playing animator (for back-compat)
|
|
Animator* GetActiveAnimator() const {
|
|
for (int i = (int)animators.size() - 1; i >= 0; i--)
|
|
if (animators[i]->isPlaying && animators[i]->m_CurrentAnimation)
|
|
return animators[i];
|
|
return animator;
|
|
}
|
|
|
|
void SetRig(Rig* r) {
|
|
rig = r;
|
|
for (Animator* a : animators)
|
|
a->SetRig(r);
|
|
}
|
|
|
|
void SetAnimation(Animation* anim) {
|
|
animator->SetAnimation(anim);
|
|
animator->PlayAnimation();
|
|
animator->blendWeight = 1.0f; // base never blends � always instant
|
|
}
|
|
|
|
void LoadAnimation(Animation* anim) { SetAnimation(anim); }
|
|
|
|
void Step(float dt) {
|
|
for (Animator* a : animators)
|
|
a->Step(dt);
|
|
}
|
|
|
|
BodyComponent* GetComponentOfName(std::string name);
|
|
void ExecuteAction(InputAction* action);
|
|
};
|
|
|
|
class CharacterMind {
|
|
public:
|
|
InputAction* ProduceAction();
|
|
};
|
|
|
|
class Character : public Object, public Renderable {
|
|
public:
|
|
CharacterMind* mind = nullptr;
|
|
CharacterBody* body = nullptr;
|
|
glm::vec4 colorOverride = glm::vec4(0.0f); // rgb + strength (0 = keep the rig's own texture)
|
|
float visualScale = 1.4f; // rig is ~1.8 tall natively; hitbox is 2.5
|
|
float metallic = 0.0f;
|
|
float roughness = 0.7f;
|
|
float ao = 1.0f;
|
|
float turnSpeed = 12.0f;
|
|
float punchDamage = 12.0f;
|
|
float punchReach = 1.15f;
|
|
float punchAnimationSpeed = 0.6f;
|
|
glm::vec3 punchHitboxSize = glm::vec3(1.1f, 1.5f, 1.4f);
|
|
|
|
void PressInput(char letter);
|
|
void ReleaseInput(char letter);
|
|
void ClearInput();
|
|
glm::vec3 GetMovementDirection(const glm::vec3& forward, const glm::vec3& right) const;
|
|
void SetTurnDirection(const glm::vec3& direction);
|
|
const glm::vec3& GetTurnDirection() const { return turnDirection; }
|
|
void SetPunchAnimation(Animation* animation);
|
|
void SetPunchLooping(bool looping);
|
|
void Punch(const glm::vec3& lookDirection = glm::vec3(0.0f));
|
|
void SetWalking(bool walking);
|
|
glm::vec3 GetForwardDirection() const;
|
|
const Hitbox& GetPunchHitbox() const { return punchHitbox; }
|
|
|
|
Character();
|
|
void Render(Shader& shader) override;
|
|
|
|
void Step(float dt) {
|
|
if (punchLookLocked && punchAnimator && punchAnimator->isPlaying)
|
|
turnDirection = punchLookDirection;
|
|
TurnToDirection(dt);
|
|
punchHitbox.active = false;
|
|
body->Step(dt);
|
|
}
|
|
|
|
private:
|
|
friend class CharacterManager;
|
|
|
|
void TurnToDirection(float dt);
|
|
void OnPunchAnimationEnd();
|
|
|
|
std::set<char> inputLetters;
|
|
glm::vec3 turnDirection = glm::vec3(0.0f);
|
|
Animation* punchAnimation = nullptr;
|
|
Animator* punchAnimator = nullptr;
|
|
Hitbox punchHitbox;
|
|
glm::vec3 punchLookDirection = glm::vec3(0.0f);
|
|
bool punchLookLocked = false;
|
|
};
|
|
|
|
#endif |