185 lines
6.9 KiB
C++
185 lines
6.9 KiB
C++
#pragma once
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <Eigen/Core>
|
|
#include <Eigen/Geometry>
|
|
#include "external/nlohmann/json.hpp"
|
|
#include "ISaveable.h"
|
|
|
|
namespace FRG {
|
|
|
|
constexpr float VISIBLE_RANGE = 6.f;
|
|
|
|
struct CharacterUpdateContext;
|
|
struct CharacterUpdateEvents;
|
|
struct LocationNetworkState;
|
|
|
|
enum class AnimationState {
|
|
STAND = 0,
|
|
WALK = 1,
|
|
STAND_TO_ACTION = 2,
|
|
ACTION_ATTACK = 3,
|
|
ACTION_ATTACK_2 = 4,
|
|
ACTION_IDLE = 5,
|
|
ACTION_TO_STAND = 6,
|
|
ACTION_TO_DEATH = 7,
|
|
DEATH_IDLE = 8,
|
|
};
|
|
|
|
|
|
|
|
// Paths and fixed parameters needed to recreate a Character's visual assets
|
|
// from scratch (e.g. after loading a saved game).
|
|
struct CharacterCreationInfo {
|
|
// Each entry is (AnimationState, filepath). Binary .anim format is auto-detected.
|
|
std::vector<std::pair<AnimationState, std::string>> animationPaths;
|
|
// Per-mesh texture paths (mesh name → path); takes priority over singleTexturePath.
|
|
std::unordered_map<std::string, std::string> meshTexturePaths;
|
|
// Single shared texture path (used when setTexture(shared_ptr) was called on all meshes).
|
|
std::string singleTexturePath;
|
|
// Weapon asset paths and attachment setup
|
|
std::string weaponMeshPath;
|
|
std::string weaponTexturePath;
|
|
std::string weaponAttachBoneName = "RightHand";
|
|
Eigen::Vector3f weaponInitialPosition = Eigen::Vector3f::Zero();
|
|
// Weapon orientation stored as a quaternion (serializable as 4 floats).
|
|
Eigen::Quaternionf weaponInitialRotation = Eigen::Quaternionf::Identity();
|
|
// Optional mesh-space transformations applied after loading the weapon mesh.
|
|
Eigen::Vector3f weaponMeshMove = Eigen::Vector3f::Zero();
|
|
float weaponMeshScale = 1.0f;
|
|
};
|
|
|
|
// All serializable authoritative state for a character. GameState or
|
|
// LocationGameState owns it; rendering-only data lives in Character.
|
|
class CharacterState : public ISaveable {
|
|
public:
|
|
// Distance below which the character is considered to have reached its target.
|
|
static constexpr float kWalkThreshold = 0.05f;
|
|
|
|
// --- Identity ---
|
|
std::string npcName;
|
|
bool isPlayer = false;
|
|
bool enabled = true;
|
|
bool canAttack = false;
|
|
|
|
// --- Position / orientation ---
|
|
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
|
float facingAngle = 0.0f;
|
|
float targetFacingAngle = 0.0f;
|
|
|
|
// --- Health ---
|
|
float hp = 200.f;
|
|
float initialHp = 200.f;
|
|
|
|
float visibleRange = VISIBLE_RANGE;
|
|
|
|
// --- Combat flags ---
|
|
int battle_state = 0;
|
|
bool resetAnim = false;
|
|
int attack = 0;
|
|
AnimationState currentState = AnimationState::STAND;
|
|
float attack_cooldown = 0.f;
|
|
|
|
// Weapon visibility toggle (changes during animation)
|
|
bool showWeapon = true;
|
|
|
|
// --- Stable character references ---
|
|
// Empty means no target. "player" is the campaign player; NPC IDs are
|
|
// location-local and validated while loading location content.
|
|
std::string attackTargetId;
|
|
std::string faceTargetId;
|
|
|
|
// --- Home position for drift recovery ---
|
|
Eigen::Vector3f homePosition = Eigen::Vector3f::Zero();
|
|
float maxHomeDrift = 3.0f;
|
|
|
|
// --- Slow-motion effect ---
|
|
float slowMoTimeRemaining = 0.0f;
|
|
float slowMoActiveTime = 6.0f;
|
|
float slowMoTransitionTime = 0.5f;
|
|
|
|
// --- Authoritative logical animation clock ---
|
|
// Frame counts are immutable metadata copied from loaded clips during
|
|
// character construction. Simulation advances animationFrame itself.
|
|
float animationFrame = 0.0f;
|
|
std::array<int, 9> animationStartFrames = {{0, 0, 0, 0, 0, 0, 0, 0, 0}};
|
|
std::array<int, 9> animationFrameCounts = {{1, 1, 1, 1, 1, 1, 1, 1, 1}};
|
|
|
|
// --- Movement / visual parameters ---
|
|
float walkSpeed = 3.0f;
|
|
float rotationSpeed = 4.0f;
|
|
float modelScale = 0.12f;
|
|
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
|
bool useGpuSkinning = true;
|
|
float collisionRadius = 0.45f;
|
|
float interactionRadius = 0.0f;
|
|
|
|
// --- Pathfinding data (serializable parts) ---
|
|
Eigen::Vector3f walkTarget = Eigen::Vector3f::Zero();
|
|
Eigen::Vector3f requestedWalkTarget = Eigen::Vector3f::Zero();
|
|
std::vector<Eigen::Vector3f> pathWaypoints;
|
|
size_t currentWaypointIndex = 0;
|
|
|
|
// Internal drift-check timer (serializable so it survives save/load).
|
|
float homeDriftCheckTimer = 5.0f;
|
|
|
|
// Global Lua function called when the character reaches the final waypoint.
|
|
// The name is serialized, so no runtime callback needs to be rehydrated.
|
|
std::string onArrivedCallbackName;
|
|
|
|
// --- Asset / creation info (used to reload visuals from a saved state) ---
|
|
CharacterCreationInfo creationInfo;
|
|
|
|
// --- Pure-state methods ---
|
|
void configureAsPlayer();
|
|
bool isMoving() const;
|
|
const Eigen::Vector3f& getRequestedWalkTarget() const { return requestedWalkTarget; }
|
|
Eigen::Vector3f getCurrentNavigationTarget() const;
|
|
void stopInPlace();
|
|
void setTarget(const Eigen::Vector3f& target,
|
|
std::string callbackName,
|
|
const LocationNetworkState& location,
|
|
const std::string& entityId);
|
|
void forceReplan(const LocationNetworkState& location,
|
|
const std::string& entityId);
|
|
void stopAndRotateToTarget(std::string characterId);
|
|
void resetPlayerAfterDeath();
|
|
void tryAttackCombo();
|
|
float getHp() const { return hp; }
|
|
void setHp(float newHp);
|
|
CharacterUpdateEvents update(std::int64_t deltaMs, const CharacterUpdateContext& context);
|
|
void setAnimationMetadata(AnimationState state, int startFrame, int frameCount);
|
|
// --- Serialisation (runtime mutable fields only; creation info is reloaded from config) ---
|
|
void save(nlohmann::json& out) const override;
|
|
void load(const nlohmann::json& in) override;
|
|
};
|
|
|
|
using PlayerStateMap =
|
|
std::unordered_map<std::string, std::shared_ptr<CharacterState>>;
|
|
|
|
struct CharacterObservation {
|
|
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
|
float hp = 0.f;
|
|
};
|
|
|
|
struct CharacterUpdateContext {
|
|
const CharacterObservation* attackTarget = nullptr;
|
|
const CharacterObservation* faceTarget = nullptr;
|
|
};
|
|
|
|
struct CharacterUpdateEvents {
|
|
bool attackImpact = false;
|
|
bool deathAnimationComplete = false;
|
|
bool navigationRequested = false;
|
|
Eigen::Vector3f navigationTarget = Eigen::Vector3f::Zero();
|
|
std::string arrivedCallbackName;
|
|
std::string attackTargetId;
|
|
Eigen::Vector3f attackDirection = Eigen::Vector3f::Zero();
|
|
};
|
|
|
|
} // namespace FRG
|