This commit is contained in:
2026-11-08 07:45:01 -05:00
commit 36e093f84c
1109 changed files with 254529 additions and 0 deletions
+187
View File
@@ -0,0 +1,187 @@
#ifndef COMBAT_CLASS
#define COMBAT_CLASS
#include <string>
#include "StatusEffect.h"
#include <vector>
#include <glm/glm.hpp>
class Animation;
class CharacterBody;
// One swing in a combo. Timings are frames of its animation, which has lastFrame of them.
struct AttackMove {
Animation* animation = nullptr;
float duration = 0.5f; // real seconds for the whole move, recovery and all. 0 plays the animation at its own speed.
float lastFrame = 120.0f;
float hitStartFrame = 40.0f; // the fist can connect from here
float hitEndFrame = 60.0f; // to here
float recoveryFrame = 60.0f; // from here it's just getting back to normal, and the next swing can cut in
float damage = 8.0f;
glm::vec3 hitboxSize = glm::vec3(0.9f, 1.2f, 1.0f); // meters across, tall and deep
glm::vec3 hitboxOffset = glm::vec3(0.0f, 0.3f, 0.85f); // its middle from the attacker's: right, up and forward. Starts at its front.
float knockback = 2.0f; // m/s away from the attacker
float knockbackUp = 0.0f; // m/s upward, enough of it and they leave the ground
float stun = 0.0f; // real seconds whoever it hits can't do anything
bool hitsKnockedDown = false; // can hit characters that are knocked down
float moveSpeedMultiplier = 0.0f; // of walking speed while it's going, recovery and all. 0 stands still.
float forwardVelocity = 4.0f; // m/s added along where it faces as the swing starts
};
// A move on a number key, like shooting. It plays on the action layer like a swing and goes off partway through,
// then can't be used again until its cooldown is up. Timings are frames of its animation, which has lastFrame of them,
// or with lastFrame 0, frames at 60 a second.
struct Ability {
enum class Effect {
Shot, // a long thin hitbox from the gun toward where it was aimed, out for the frame it fires
Stab, // a short hitbox in front of it the frame it fires. A hit leaves its prop stuck in them, bleeding them.
Sweep // a wide hitbox in front of it the frame it fires, catching everyone in it
};
std::string name;
std::string description; // what it does, shown hovering over it in the moves list
Effect effect = Effect::Shot;
Animation* animation = nullptr;
float duration = 0.0f; // real seconds for the whole move. 0 plays the animation at its own speed.
float lastFrame = 40.0f;
float fireFrame = 4.0f; // when it goes off
float hitExitFrame = 0.0f; // when it lands, it's over this far in rather than at lastFrame. 0 plays it all.
float missExitFrame = 0.0f; // when it doesn't land, it's over this far in. 0 plays it all.
float cooldown = 0.5f; // real seconds from it finishing until it can be used again
float moveSpeedMultiplier = 0.5f; // of walking speed while it's going
bool pivots = false; // keeps turning to face where the player looks while it's going, so it can be aimed
float pivotEndFrame = 0.0f; // stops pivoting here, 0 pivots all the way through
std::string sound; // played where it goes off, as it does. Empty plays nothing.
float screenshake = 0.0f; // how hard the camera shakes as it goes off, when it's the player's
float damage = 12.0f;
float range = 100.0f; // meters, how long the shot's hitbox reaches
glm::vec2 hitboxSize = glm::vec2(0.3f, 0.3f); // meters across and tall, around the line of the shot
float knockback = 1.0f; // m/s away from the shooter
float stun = 0.0f; // real seconds whoever it hits can't do anything
float knockdownTime = 0.0f; // real seconds whoever it hits is knocked down for, 0 doesn't
// Played on whoever it kills, instead of them just dropping. If it has a bone called Camera and the player is
// either of them, the camera follows that bone until it's over.
Animation* killAnimation = nullptr;
struct KillSound {
float frame = 0.0f; // into killAnimation, at 60 a second
std::string file;
};
std::vector<KillSound> killSounds; // played over the kill animation, heard when the camera's following it
bool hitsKnockedDown = false; // can hit characters that are knocked down
// A model shown on one of the animation's own bones while it's going, like a knife. Empty shows nothing.
std::string prop;
std::string propBone;
glm::vec3 propTurn = glm::vec3(0.0f); // degrees around x, y and z on top of the bone's rotation
// Stab only: how long the prop stays stuck in whoever it hits, hurting them bleedDamage a second meanwhile
float bleedTime = 0.0f; // real seconds
float bleedDamage = 0.0f;
// Carries it forward lungeDistance meters, at a steady pace from lungeStartFrame to lungeEndFrame. 0 stays put.
float lungeDistance = 0.0f;
float lungeStartFrame = 0.0f;
float lungeEndFrame = 0.0f;
// Whoever it hits gets status for statusTime real seconds, at statusFrame if it's still going then,
// or as they're hit with statusFrame 0
bool appliesStatus = false;
StatusEffect::Type status = StatusEffect::Type::HeavyGravity;
float statusTime = 0.0f;
float statusFrame = 0.0f;
};
// An ability that's just gone off, for the game to carry out
struct AbilityFired {
int index = -1; // into abilities
glm::vec3 aimPoint = glm::vec3(0.0f); // world space, what it was aimed at
bool status = false; // its statusFrame came rather than its fireFrame: whoever it hit gets its status now
};
// A character's attacks: a combo that goes through its moves in order, each click starting the next one
// while the last is recovering. Waiting out a recovery starts the combo over, so does finishing it.
// Abilities sit beside it, one per number key.
class CharacterCombat {
public:
std::vector<AttackMove> combo;
std::string comboName; // what the moves list calls the combo
std::vector<Ability> abilities; // abilities[0] is on 1, abilities[1] on 2 and so on
// Asks for the next swing, which starts as soon as the one going lets it
void QueueAttack() { queued = true; }
// Uses abilities[index] aimed at aimPoint, as soon as whatever's going lets it. Nothing while it's cooling down.
void UseAbility(int index, const glm::vec3& aimPoint);
// Stops whatever swing or ability is going, and starts the combo over. Cooldowns carry on.
void Reset();
// Once a frame, dt in game time. Starts queued swings on the body's action layer.
void Step(CharacterBody& body, float dt);
// The swing going now, nullptr between swings
const AttackMove* GetCurrentMove() const;
int GetCurrentIndex() const { return current; }
// The ability going now, nullptr if none is
const Ability* GetCurrentAbility() const;
// Different every time an ability starts, for telling one use from the next
unsigned int GetAbilityUse() const { return abilityUse; }
// The ability going now landed, so it ends early if it has a hitExitFrame
void AbilityLanded() { abilityLanded = ability >= 0; }
bool HasAbilityLanded() const { return abilityLanded; }
// Whether the ability it's in is lunging it forward right now, and if so how fast, in m/s of game time
bool GetAbilityLunge(float& speed) const;
// Whether the ability it's in turns it to face where it's aimed right now
bool IsAbilityPivoting() const;
// Whether abilities[index] can be used now, and how much of its cooldown is left, 1 just used to 0 ready
bool IsAbilityReady(int index) const;
float GetCooldownFraction(int index) const;
// Abilities that went off since the last call, emptied by it
std::vector<AbilityFired> TakeFired();
// Whether the current swing can connect right now, so its hitbox is out
bool IsHitting() const;
// Different for every swing anyone throws, so its hitbox only hits each character once
unsigned int GetSwingId() const { return swingId; }
// A swing id nothing else has, for anything else that puts out hitboxes
static unsigned int NewSwingId();
private:
float MoveTime(const AttackMove& move) const; // game seconds the whole move takes
float FrameTime(const AttackMove& move, float frame) const; // game seconds into the move
void Start(CharacterBody& body, int index);
void StartAbility(CharacterBody& body, int index);
void StepAbility(CharacterBody& body, float dt);
int current = -1; // index into combo, -1 between swings
int next = 0;
float time = 0.0f; // seconds into the current swing
bool queued = false;
unsigned int swingId = 0;
int ability = -1; // index into abilities going now, -1 if none
float abilityTime = 0.0f; // game seconds into it
bool abilityFired = false;
bool abilityStatusFired = false;
bool abilityLanded = false;
unsigned int abilityUse = 0;
int queuedAbility = -1;
glm::vec3 queuedAim = glm::vec3(0.0f);
glm::vec3 abilityAim = glm::vec3(0.0f);
std::vector<float> cooldowns; // game seconds left, by ability
std::vector<AbilityFired> fired;
};
#endif