195 lines
9.5 KiB
C++
195 lines
9.5 KiB
C++
#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
|
|
// Hyper armor: hits still hurt, but don't flinch, stun, knock back or knock down the user, so the move carries on
|
|
bool hyperArmor = false;
|
|
float hyperArmorStartFrame = 0.0f;
|
|
float hyperArmorEndFrame = 0.0f; // 0 keeps it to the end
|
|
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 the ability it's in has hyper armor right now
|
|
bool HasHyperArmor() 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
|