Switch to Vec3fp and fp variables for character
This commit is contained in:
1 parent
8df016c0a4
commit
889c6d465b
23 files changed
+383
-405
No files matched your search
+7
-1
@@ -28,6 +28,12 @@ namespace FRG {
|
|||||||
return Eigen::Vector3f(j[0].get<float>(), j[1].get<float>(), j[2].get<float>());
|
return Eigen::Vector3f(j[0].get<float>(), j[1].get<float>(), j[2].get<float>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Vec3fp readVec3fp(const json& j) {
|
||||||
|
if (!j.is_array() || j.size() != 3) return Vec3fp();
|
||||||
|
return Vec3fp(static_cast<fp>(j[0].get<double>()), static_cast<fp>(j[1].get<double>()), static_cast<fp>(j[2].get<double>()));
|
||||||
|
}
|
||||||
|
|
||||||
bool LocationConfig::fromFile(const std::string& path, const std::string& zipFile, LocationConfig& out) {
|
bool LocationConfig::fromFile(const std::string& path, const std::string& zipFile, LocationConfig& out) {
|
||||||
json j;
|
json j;
|
||||||
if (!parseJsonFile(path, zipFile, j)) return false;
|
if (!parseJsonFile(path, zipFile, j)) return false;
|
||||||
@@ -43,7 +49,7 @@ namespace FRG {
|
|||||||
out.teleportsJsonPath = j.value("teleports", "");
|
out.teleportsJsonPath = j.value("teleports", "");
|
||||||
out.triggerZonesJsonPath = j.value("triggerZones", "");
|
out.triggerZonesJsonPath = j.value("triggerZones", "");
|
||||||
out.lightsJsonPath = j.value("lights", "");
|
out.lightsJsonPath = j.value("lights", "");
|
||||||
if (j.contains("playerPosition")) out.playerPosition = readVec3(j["playerPosition"]);
|
if (j.contains("playerPosition")) out.playerPosition = readVec3fp(j["playerPosition"]);
|
||||||
out.lockInteractiveObjectsDuringTutorial = j.value("lockInteractiveObjectsDuringTutorial", false);
|
out.lockInteractiveObjectsDuringTutorial = j.value("lockInteractiveObjectsDuringTutorial", false);
|
||||||
out.loadingScreenPath = j.value("loadingScreen", "");
|
out.loadingScreenPath = j.value("loadingScreen", "");
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "navigation/PathFinder.h"
|
||||||
|
|
||||||
namespace FRG {
|
namespace FRG {
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ namespace FRG {
|
|||||||
std::string teleportsJsonPath;
|
std::string teleportsJsonPath;
|
||||||
std::string triggerZonesJsonPath;
|
std::string triggerZonesJsonPath;
|
||||||
std::string lightsJsonPath;
|
std::string lightsJsonPath;
|
||||||
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
|
Vec3fp playerPosition;
|
||||||
|
|
||||||
// Where the taxi drives in when the player calls it. No taxi when absent.
|
// Where the taxi drives in when the player calls it. No taxi when absent.
|
||||||
struct Taxi {
|
struct Taxi {
|
||||||
|
|||||||
+33
-43
@@ -42,19 +42,9 @@ bool Character::isMoving() const {
|
|||||||
return state && state->isMoving();
|
return state && state->isMoving();
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Vector3f Character::getRequestedWalkTarget() const {
|
|
||||||
auto state = stateSnapshot();
|
|
||||||
return state ? state->getRequestedWalkTarget() : Eigen::Vector3f::Zero();
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::Vector3f Character::getCurrentNavigationTarget() const {
|
|
||||||
auto state = stateSnapshot();
|
|
||||||
return state ? state->getCurrentNavigationTarget() : Eigen::Vector3f::Zero();
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::Vector3f Character::renderPosition() const {
|
Eigen::Vector3f Character::renderPosition() const {
|
||||||
auto state = stateSnapshot();
|
auto state = stateSnapshot();
|
||||||
return state ? interpolatedState(*state).position : Eigen::Vector3f::Zero();
|
return state ? interpolatedState(*state).position.toEigen() : Eigen::Vector3f::Zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Character::getHp() const {
|
float Character::getHp() const {
|
||||||
@@ -181,8 +171,8 @@ CharacterState Character::interpolatedState(const CharacterState& state) const {
|
|||||||
const float alpha = (std::max)(0.0f, (std::min)(1.0f, elapsedMs / kSimulationTickMs));
|
const float alpha = (std::max)(0.0f, (std::min)(1.0f, elapsedMs / kSimulationTickMs));
|
||||||
|
|
||||||
CharacterState result = state;
|
CharacterState result = state;
|
||||||
result.position = previousPresentationSample_.position +
|
result.position = Vec3fp::fromEigen(previousPresentationSample_.position +
|
||||||
alpha * (currentPresentationSample_.position - previousPresentationSample_.position);
|
alpha * (currentPresentationSample_.position - previousPresentationSample_.position));
|
||||||
|
|
||||||
constexpr float kPi = 3.14159265358979323846f;
|
constexpr float kPi = 3.14159265358979323846f;
|
||||||
constexpr float kTwoPi = 2.0f * kPi;
|
constexpr float kTwoPi = 2.0f * kPi;
|
||||||
@@ -190,7 +180,7 @@ CharacterState Character::interpolatedState(const CharacterState& state) const {
|
|||||||
previousPresentationSample_.facingAngle;
|
previousPresentationSample_.facingAngle;
|
||||||
while (angleDelta > kPi) angleDelta -= kTwoPi;
|
while (angleDelta > kPi) angleDelta -= kTwoPi;
|
||||||
while (angleDelta < -kPi) angleDelta += kTwoPi;
|
while (angleDelta < -kPi) angleDelta += kTwoPi;
|
||||||
result.facingAngle = previousPresentationSample_.facingAngle + alpha * angleDelta;
|
result.facingAngle = static_cast<fp>(previousPresentationSample_.facingAngle + alpha * angleDelta);
|
||||||
if (previousPresentationSample_.currentState == state.currentState &&
|
if (previousPresentationSample_.currentState == state.currentState &&
|
||||||
currentPresentationSample_.currentState == state.currentState) {
|
currentPresentationSample_.currentState == state.currentState) {
|
||||||
float previousFrame = previousPresentationSample_.animationFrame;
|
float previousFrame = previousPresentationSample_.animationFrame;
|
||||||
@@ -235,8 +225,8 @@ void Character::update(int64_t deltaMs, const CharacterUpdateEvents* events) {
|
|||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
PresentationSample sample;
|
PresentationSample sample;
|
||||||
sample.position = state.position;
|
sample.position = state.position.toEigen();
|
||||||
sample.facingAngle = state.facingAngle;
|
sample.facingAngle = static_cast<float>(state.facingAngle);
|
||||||
sample.animationFrame = state.animationFrame;
|
sample.animationFrame = state.animationFrame;
|
||||||
sample.currentState = state.currentState;
|
sample.currentState = state.currentState;
|
||||||
sample.enabled = state.enabled;
|
sample.enabled = state.enabled;
|
||||||
@@ -299,8 +289,8 @@ void Character::draw(Renderer& renderer) {
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -353,8 +343,8 @@ void Character::drawGpuSkinning(Renderer& renderer, const CharacterState& state)
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -417,8 +407,8 @@ void Character::drawAttachedWeapon(Renderer& renderer, const CharacterState& sta
|
|||||||
Eigen::Matrix4f currentBone = anim.gpuSkinningShaderData.skinningMatrices[boneIdx] * bindBone;
|
Eigen::Matrix4f currentBone = anim.gpuSkinningShaderData.skinningMatrices[boneIdx] * bindBone;
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
renderer.TranslateMatrix(Eigen::Vector3f(currentBone.block<3, 1>(0, 3)));
|
renderer.TranslateMatrix(Eigen::Vector3f(currentBone.block<3, 1>(0, 3)));
|
||||||
@@ -452,8 +442,8 @@ void Character::drawShadowDepthCpu(Renderer& renderer, const CharacterState& sta
|
|||||||
if (it == animations.end()) return;
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -488,8 +478,8 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer, const CharacterSt
|
|||||||
renderer.shaderManager.PushShader(shadowSkinningShader);
|
renderer.shaderManager.PushShader(shadowSkinningShader);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -561,8 +551,8 @@ void Character::drawCpuWithShadow(Renderer& renderer, const CharacterState& stat
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -623,8 +613,8 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const CharacterSta
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -726,8 +716,8 @@ void Character::drawNightGpuSkinning(Renderer& renderer,
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -798,8 +788,8 @@ void Character::drawNightCpu(Renderer& renderer,
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -900,8 +890,8 @@ void Character::drawNightGpuSkinningWithShadow(Renderer& renderer,
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -985,8 +975,8 @@ void Character::drawNightCpuWithShadow(Renderer& renderer,
|
|||||||
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
|
||||||
|
|
||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ state.position.x(), state.position.y(), state.position.z() });
|
renderer.TranslateMatrix({ static_cast<float>(state.position.x), static_cast<float>(state.position.y), static_cast<float>(state.position.z) });
|
||||||
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-state.facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-static_cast<float>(state.facingAngle), Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
renderer.ScaleMatrix(state.modelScale);
|
renderer.ScaleMatrix(state.modelScale);
|
||||||
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
renderer.RotateMatrix(state.modelCorrectionRotation.toRotationMatrix());
|
||||||
|
|
||||||
@@ -1027,7 +1017,7 @@ void Character::setupHitSparks(std::shared_ptr<Texture> sparkTexture) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Character::presentDamage(float hp, float maxHp,
|
void Character::presentDamage(float hp, float maxHp,
|
||||||
const Eigen::Vector3f& attackDirection,
|
const Vec3fp& attackDirection,
|
||||||
const std::string& attackerId) {
|
const std::string& attackerId) {
|
||||||
if (onHpChanged)
|
if (onHpChanged)
|
||||||
onHpChanged(hp, maxHp, attackerId);
|
onHpChanged(hp, maxHp, attackerId);
|
||||||
@@ -1037,9 +1027,9 @@ void Character::presentDamage(float hp, float maxHp,
|
|||||||
if (!stateSnapshot) return;
|
if (!stateSnapshot) return;
|
||||||
const CharacterState& state = *stateSnapshot;
|
const CharacterState& state = *stateSnapshot;
|
||||||
|
|
||||||
Eigen::Vector3f emitPos = state.position + Eigen::Vector3f(0.f, 1.f, 0.f);
|
Eigen::Vector3f emitPos = state.position.toEigen() + Eigen::Vector3f(0.f, 1.f, 0.f);
|
||||||
hitSparkEmitter.resetEmissionPoints({ emitPos });
|
hitSparkEmitter.resetEmissionPoints({ emitPos });
|
||||||
hitSparkEmitter.setEmissionDirection(attackDirection);
|
hitSparkEmitter.setEmissionDirection(attackDirection.toEigen());
|
||||||
|
|
||||||
static std::mt19937 gen(std::random_device{}());
|
static std::mt19937 gen(std::random_device{}());
|
||||||
std::uniform_int_distribution<int> countDist(10, 15);
|
std::uniform_int_distribution<int> countDist(10, 15);
|
||||||
@@ -1065,7 +1055,7 @@ static void drawCharacterNameLabel(
|
|||||||
if (!state.enabled || state.hp <= 0.f || displayName.empty()) return;
|
if (!state.enabled || state.hp <= 0.f || displayName.empty()) return;
|
||||||
|
|
||||||
Eigen::Vector4f worldPos(
|
Eigen::Vector4f worldPos(
|
||||||
state.position.x(), state.position.y() + 1.83f, state.position.z(), 1.f);
|
static_cast<float>(state.position.x), static_cast<float>(state.position.y) + 1.83f, static_cast<float>(state.position.z), 1.f);
|
||||||
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
|
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
|
||||||
if (clip.w() <= 0.f) return;
|
if (clip.w() <= 0.f) return;
|
||||||
|
|
||||||
@@ -1126,7 +1116,7 @@ void Character::drawHealthBar(Renderer& renderer,
|
|||||||
if (state.hp <= 0.f) return;
|
if (state.hp <= 0.f) return;
|
||||||
if (state.initialHp <= 0.f) return;
|
if (state.initialHp <= 0.f) return;
|
||||||
|
|
||||||
Eigen::Vector4f worldPos(state.position.x(), state.position.y() + 1.75f, state.position.z(), 1.f);
|
Eigen::Vector4f worldPos(static_cast<float>(state.position.x), static_cast<float>(state.position.y) + 1.75f, static_cast<float>(state.position.z), 1.f);
|
||||||
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
|
Eigen::Vector4f clip = projectionMatrix * cameraViewMatrix * worldPos;
|
||||||
if (clip.w() <= 0.f) return;
|
if (clip.w() <= 0.f) return;
|
||||||
float ndcX = clip.x() / clip.w();
|
float ndcX = clip.x() / clip.w();
|
||||||
|
|||||||
+1
-3
@@ -55,7 +55,7 @@ public:
|
|||||||
void update(int64_t deltaMs, const CharacterUpdateEvents* events = nullptr);
|
void update(int64_t deltaMs, const CharacterUpdateEvents* events = nullptr);
|
||||||
|
|
||||||
// ---- Damage ----
|
// ---- Damage ----
|
||||||
void presentDamage(float hp, float maxHp, const Eigen::Vector3f& attackDirection,
|
void presentDamage(float hp, float maxHp, const Vec3fp& attackDirection,
|
||||||
const std::string& attackerId);
|
const std::string& attackerId);
|
||||||
|
|
||||||
// ---- Spark setup ----
|
// ---- Spark setup ----
|
||||||
@@ -93,8 +93,6 @@ public:
|
|||||||
|
|
||||||
// ---- Query helpers (read immutable snapshots) ----
|
// ---- Query helpers (read immutable snapshots) ----
|
||||||
bool isMoving() const;
|
bool isMoving() const;
|
||||||
Eigen::Vector3f getRequestedWalkTarget() const;
|
|
||||||
Eigen::Vector3f getCurrentNavigationTarget() const;
|
|
||||||
Eigen::Vector3f renderPosition() const;
|
Eigen::Vector3f renderPosition() const;
|
||||||
float getHp() const;
|
float getHp() const;
|
||||||
|
|
||||||
|
|||||||
+114
-84
@@ -9,7 +9,13 @@ namespace {
|
|||||||
// Tick counts are authoritative and therefore independent of render rate.
|
// Tick counts are authoritative and therefore independent of render rate.
|
||||||
constexpr float ATTACK_COOLDOWN_TIME = 1.6f;
|
constexpr float ATTACK_COOLDOWN_TIME = 1.6f;
|
||||||
constexpr float HOME_DRIFT_CHECK_INTERVAL = 5.0f;
|
constexpr float HOME_DRIFT_CHECK_INTERVAL = 5.0f;
|
||||||
|
fp HOME_DRIFT_CHECK_INTERVAL_FP = static_cast<fp>(HOME_DRIFT_CHECK_INTERVAL);
|
||||||
constexpr float TARGET_REPLAN_THRESHOLD = 0.25f;
|
constexpr float TARGET_REPLAN_THRESHOLD = 0.25f;
|
||||||
|
|
||||||
|
fp TARGET_REPLAN_THRESHOLD_SQUARED = static_cast<fp>(TARGET_REPLAN_THRESHOLD * TARGET_REPLAN_THRESHOLD);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constexpr float PI = 3.14159265358979323846f;
|
constexpr float PI = 3.14159265358979323846f;
|
||||||
constexpr int BLOCKED_REPLAN_TICKS = 3;
|
constexpr int BLOCKED_REPLAN_TICKS = 3;
|
||||||
|
|
||||||
@@ -25,8 +31,8 @@ std::uint32_t stableIdHash(const std::string& value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CharacterState::configureAsPlayer() {
|
void CharacterState::configureAsPlayer() {
|
||||||
walkSpeed = 3.0f;
|
walkSpeed = static_cast<fp>(3.0f);
|
||||||
rotationSpeed = 8.0f;
|
rotationSpeed = static_cast < fp>(8.0f);
|
||||||
modelScale = 1.f;
|
modelScale = 1.f;
|
||||||
modelCorrectionRotation = Eigen::Quaternionf(
|
modelCorrectionRotation = Eigen::Quaternionf(
|
||||||
Eigen::AngleAxisf(PI, Eigen::Vector3f::UnitY()) *
|
Eigen::AngleAxisf(PI, Eigen::Vector3f::UnitY()) *
|
||||||
@@ -66,12 +72,12 @@ void CharacterState::scheduleNextNavigationRetry()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterState::isMoving() const {
|
bool CharacterState::isMoving() const {
|
||||||
Eigen::Vector3f toTarget = walkTarget - position;
|
Vec3fp toTarget = walkTarget - position;
|
||||||
toTarget.y() = 0.f;
|
toTarget.y = static_cast<fp>(0.f);
|
||||||
return !pathWaypoints.empty() || toTarget.norm() > kWalkThreshold;
|
return !pathWaypoints.empty() || toTarget.normSquared() > kWalkThreshold_SQUARED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Vector3f CharacterState::getCurrentNavigationTarget() const {
|
Vec3fp CharacterState::getCurrentNavigationTarget() const {
|
||||||
if (!pathWaypoints.empty() && currentWaypointIndex < pathWaypoints.size()) {
|
if (!pathWaypoints.empty() && currentWaypointIndex < pathWaypoints.size()) {
|
||||||
return pathWaypoints[currentWaypointIndex];
|
return pathWaypoints[currentWaypointIndex];
|
||||||
}
|
}
|
||||||
@@ -79,7 +85,7 @@ Eigen::Vector3f CharacterState::getCurrentNavigationTarget() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CharacterState::stopInPlace() {
|
void CharacterState::stopInPlace() {
|
||||||
walkTarget = Eigen::Vector3f(position.x(), 0.f, position.z());
|
walkTarget = Vec3fp(position.x, static_cast<fp>(0.f), position.z);
|
||||||
requestedWalkTarget = walkTarget;
|
requestedWalkTarget = walkTarget;
|
||||||
pathWaypoints.clear();
|
pathWaypoints.clear();
|
||||||
currentWaypointIndex = 0;
|
currentWaypointIndex = 0;
|
||||||
@@ -90,20 +96,20 @@ void CharacterState::stopInPlace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CharacterState::setTarget(
|
void CharacterState::setTarget(
|
||||||
const Eigen::Vector3f& target,
|
const Vec3fp& target,
|
||||||
std::string callbackName,
|
std::string callbackName,
|
||||||
const LocationNetworkState& location,
|
const LocationNetworkState& location,
|
||||||
const std::string& entityId)
|
const std::string& entityId)
|
||||||
{
|
{
|
||||||
const Eigen::Vector3f normalizedTarget(target.x(), 0.f, target.z());
|
const Vec3fp normalizedTarget(target.x, static_cast<fp>(0.f), target.z);
|
||||||
const bool sameRequestedTarget =
|
const bool sameRequestedTarget =
|
||||||
(normalizedTarget - requestedWalkTarget).norm() <= TARGET_REPLAN_THRESHOLD;
|
(normalizedTarget - requestedWalkTarget).normSquared() <= TARGET_REPLAN_THRESHOLD_SQUARED;
|
||||||
const bool alreadyMovingToTarget =
|
const bool alreadyMovingToTarget =
|
||||||
!pathWaypoints.empty() ||
|
!pathWaypoints.empty() ||
|
||||||
(walkTarget - normalizedTarget).norm() <= TARGET_REPLAN_THRESHOLD;
|
(walkTarget - normalizedTarget).normSquared() <= TARGET_REPLAN_THRESHOLD_SQUARED;
|
||||||
const bool stoppedAfterFailedPath =
|
const bool stoppedAfterFailedPath =
|
||||||
pathWaypoints.empty() &&
|
pathWaypoints.empty() &&
|
||||||
(walkTarget - position).norm() <= TARGET_REPLAN_THRESHOLD;
|
(walkTarget - position).normSquared() <= TARGET_REPLAN_THRESHOLD_SQUARED;
|
||||||
if (callbackName.empty() && sameRequestedTarget &&
|
if (callbackName.empty() && sameRequestedTarget &&
|
||||||
(alreadyMovingToTarget || stoppedAfterFailedPath)) {
|
(alreadyMovingToTarget || stoppedAfterFailedPath)) {
|
||||||
return;
|
return;
|
||||||
@@ -119,12 +125,15 @@ void CharacterState::setTarget(
|
|||||||
|
|
||||||
currentWaypointIndex = 0;
|
currentWaypointIndex = 0;
|
||||||
if (!pathWaypoints.empty()) {
|
if (!pathWaypoints.empty()) {
|
||||||
for (Eigen::Vector3f& waypoint : pathWaypoints) waypoint.y() = 0.f;
|
for (Vec3fp& waypoint : pathWaypoints)
|
||||||
|
{
|
||||||
|
waypoint.y = static_cast<fp>(0.f);
|
||||||
|
}
|
||||||
walkTarget = pathWaypoints.back();
|
walkTarget = pathWaypoints.back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
walkTarget = Eigen::Vector3f(position.x(), 0.f, position.z());
|
walkTarget = Vec3fp(position.x, static_cast<fp>(0.f), position.z);
|
||||||
if (!isPlayer) scheduleNextNavigationRetry();
|
if (!isPlayer) scheduleNextNavigationRetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,8 +141,8 @@ void CharacterState::forceReplan(
|
|||||||
const LocationNetworkState& location,
|
const LocationNetworkState& location,
|
||||||
const std::string& entityId)
|
const std::string& entityId)
|
||||||
{
|
{
|
||||||
const Eigen::Vector3f normalizedTarget(
|
const Vec3fp normalizedTarget(
|
||||||
requestedWalkTarget.x(), 0.f, requestedWalkTarget.z());
|
requestedWalkTarget.x, static_cast<fp>(0.f), requestedWalkTarget.z);
|
||||||
|
|
||||||
pathWaypoints = location.navigationMaps[location.activeNavigationIndex].findPathToNearest(this->position, normalizedTarget);
|
pathWaypoints = location.navigationMaps[location.activeNavigationIndex].findPathToNearest(this->position, normalizedTarget);
|
||||||
|
|
||||||
@@ -141,13 +150,16 @@ void CharacterState::forceReplan(
|
|||||||
movementBlockedTicks = 0;
|
movementBlockedTicks = 0;
|
||||||
navigationRetryTicks = 0;
|
navigationRetryTicks = 0;
|
||||||
if (!pathWaypoints.empty()) {
|
if (!pathWaypoints.empty()) {
|
||||||
for (Eigen::Vector3f& waypoint : pathWaypoints) waypoint.y() = 0.f;
|
for (Vec3fp& waypoint : pathWaypoints)
|
||||||
|
{
|
||||||
|
waypoint.y = static_cast<fp>(0.f);
|
||||||
|
}
|
||||||
walkTarget = pathWaypoints.back();
|
walkTarget = pathWaypoints.back();
|
||||||
blockingCharacterId.clear();
|
blockingCharacterId.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
walkTarget = Eigen::Vector3f(position.x(), 0.f, position.z());
|
walkTarget = Vec3fp(position.x, static_cast<fp>(0.f), position.z);
|
||||||
if (!isPlayer) scheduleNextNavigationRetry();
|
if (!isPlayer) scheduleNextNavigationRetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,16 +221,16 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
attack = 0;
|
attack = 0;
|
||||||
attackTargetId.clear();
|
attackTargetId.clear();
|
||||||
} else {
|
} else {
|
||||||
const float distance = (attackTarget->position - position).norm();
|
const fp distanceSquared = (attackTarget->position - position).normSquared();
|
||||||
if (distance >= visibleRange) {
|
if (distanceSquared >= visibleRange* visibleRange) {
|
||||||
if ((attackTarget->position - requestedWalkTarget).norm() >
|
if ((attackTarget->position - requestedWalkTarget).normSquared() >
|
||||||
TARGET_REPLAN_THRESHOLD) {
|
TARGET_REPLAN_THRESHOLD_SQUARED) {
|
||||||
events.navigationRequested = true;
|
events.navigationRequested = true;
|
||||||
events.navigationTarget = attackTarget->position;
|
events.navigationTarget = attackTarget->position;
|
||||||
}
|
}
|
||||||
battle_state = 0;
|
battle_state = 0;
|
||||||
} else if (distance >= 1.25f) {
|
} else if (distanceSquared >= static_cast<fp>(1.25f*1.25f)) {
|
||||||
if ((attackTarget->position - requestedWalkTarget).norm() > TARGET_REPLAN_THRESHOLD) {
|
if ((attackTarget->position - requestedWalkTarget).normSquared() > TARGET_REPLAN_THRESHOLD_SQUARED) {
|
||||||
events.navigationRequested = true;
|
events.navigationRequested = true;
|
||||||
events.navigationTarget = attackTarget->position;
|
events.navigationTarget = attackTarget->position;
|
||||||
}
|
}
|
||||||
@@ -228,21 +240,22 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Eigen::Vector3f activeTarget = getCurrentNavigationTarget();
|
const Vec3fp activeTarget = getCurrentNavigationTarget();
|
||||||
const Eigen::Vector3f lookTarget =
|
const Vec3fp lookTarget =
|
||||||
battle_state == 1 && attackTarget ? attackTarget->position : activeTarget;
|
battle_state == 1 && attackTarget ? attackTarget->position : activeTarget;
|
||||||
|
|
||||||
if (currentState == AnimationState::WALK || currentState == AnimationState::STAND) {
|
if (currentState == AnimationState::WALK || currentState == AnimationState::STAND) {
|
||||||
Eigen::Vector3f toTarget = activeTarget - position;
|
Vec3fp toTarget = activeTarget - position;
|
||||||
toTarget.y() = 0.f;
|
toTarget.y = static_cast<fp>(0.f);
|
||||||
const float distance = toTarget.norm();
|
const fp distanceSquared = toTarget.normSquared();
|
||||||
if (distance > kWalkThreshold) {
|
if (distanceSquared > kWalkThreshold_SQUARED()) {
|
||||||
const Eigen::Vector3f direction = toTarget / distance;
|
fp distance = sqrt(distanceSquared); //FP SQRT
|
||||||
const float moveAmount = walkSpeed * static_cast<float>(deltaMs) / 1000.f;
|
const Vec3fp direction = toTarget / distance;
|
||||||
Eigen::Vector3f candidate =
|
const fp moveAmount = walkSpeed * static_cast<fp>(deltaMs) / static_cast < fp>(1000.f);
|
||||||
|
Vec3fp candidate =
|
||||||
moveAmount >= distance ? activeTarget : position + direction * moveAmount;
|
moveAmount >= distance ? activeTarget : position + direction * moveAmount;
|
||||||
candidate.y() = 0.f;
|
candidate.y = static_cast<fp>(0.f);
|
||||||
const CharacterMovementObstacle* blocker = nullptr;
|
/*const CharacterMovementObstacle* blocker = nullptr;
|
||||||
if (context.movementObstacles) {
|
if (context.movementObstacles) {
|
||||||
for (const CharacterMovementObstacle& obstacle :
|
for (const CharacterMovementObstacle& obstacle :
|
||||||
*context.movementObstacles) {
|
*context.movementObstacles) {
|
||||||
@@ -296,15 +309,15 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
navigationPausedThisTick = true;
|
navigationPausedThisTick = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else*/{
|
||||||
movedLastTick =
|
movedLastTick =
|
||||||
(candidate - position).squaredNorm() > 1e-8f;
|
(candidate - position).normSquared() > static_cast<fp>(1e-8f);
|
||||||
position = candidate;
|
position = candidate;
|
||||||
movementBlockedTicks = 0;
|
movementBlockedTicks = 0;
|
||||||
blockingCharacterId.clear();
|
blockingCharacterId.clear();
|
||||||
}
|
}
|
||||||
targetFacingAngle = std::atan2(direction.x(), -direction.z());
|
targetFacingAngle = fpm::atan2(direction.x, -direction.z);
|
||||||
if (!blocker) currentState = AnimationState::WALK;
|
currentState = AnimationState::WALK;
|
||||||
} else {
|
} else {
|
||||||
currentState = AnimationState::STAND;
|
currentState = AnimationState::STAND;
|
||||||
const bool completedPath = !pathWaypoints.empty();
|
const bool completedPath = !pathWaypoints.empty();
|
||||||
@@ -316,8 +329,8 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
pathWaypoints.clear();
|
pathWaypoints.clear();
|
||||||
currentWaypointIndex = 0;
|
currentWaypointIndex = 0;
|
||||||
if (completedPath &&
|
if (completedPath &&
|
||||||
(requestedWalkTarget - position).norm() >
|
(requestedWalkTarget - position).normSquared() >
|
||||||
TARGET_REPLAN_THRESHOLD &&
|
TARGET_REPLAN_THRESHOLD_SQUARED &&
|
||||||
!isPlayer)
|
!isPlayer)
|
||||||
scheduleNextNavigationRetry();
|
scheduleNextNavigationRetry();
|
||||||
if (completedPath && !onArrivedCallbackName.empty()) {
|
if (completedPath && !onArrivedCallbackName.empty()) {
|
||||||
@@ -327,11 +340,11 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (faceTarget) {
|
if (faceTarget) {
|
||||||
Eigen::Vector3f toFace = faceTarget->position - position;
|
Vec3fp toFace = faceTarget->position - position;
|
||||||
toFace.y() = 0.f;
|
toFace.y = static_cast<fp>(0.f);
|
||||||
const float distanceToFace = toFace.norm();
|
const fp distanceToFace = sqrt(toFace.normSquared()); //FP SQRT
|
||||||
targetFacingAngle = distanceToFace > 1e-3f
|
targetFacingAngle = distanceToFace > static_cast<fp>(1e-3f)
|
||||||
? std::atan2(toFace.x() / distanceToFace, -toFace.z() / distanceToFace)
|
? fpm::atan2(toFace.x / distanceToFace, -toFace.z / distanceToFace)
|
||||||
: facingAngle;
|
: facingAngle;
|
||||||
} else if (!faceTargetId.empty()) {
|
} else if (!faceTargetId.empty()) {
|
||||||
faceTargetId.clear();
|
faceTargetId.clear();
|
||||||
@@ -340,7 +353,7 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isPlayer && !navigationPausedThisTick && pathWaypoints.empty() &&
|
if (!isPlayer && !navigationPausedThisTick && pathWaypoints.empty() &&
|
||||||
(requestedWalkTarget - position).norm() > TARGET_REPLAN_THRESHOLD &&
|
(requestedWalkTarget - position).normSquared() > TARGET_REPLAN_THRESHOLD_SQUARED &&
|
||||||
!events.navigationRequested) {
|
!events.navigationRequested) {
|
||||||
if (navigationRetryTicks > 0) --navigationRetryTicks;
|
if (navigationRetryTicks > 0) --navigationRetryTicks;
|
||||||
if (navigationRetryTicks == 0) {
|
if (navigationRetryTicks == 0) {
|
||||||
@@ -359,8 +372,8 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
attackTargetId.clear();
|
attackTargetId.clear();
|
||||||
}
|
}
|
||||||
} else if (battle_state == 1 && attackTarget) {
|
} else if (battle_state == 1 && attackTarget) {
|
||||||
targetFacingAngle = std::atan2(
|
targetFacingAngle = fpm::atan2(
|
||||||
lookTarget.x() - position.x(), -(lookTarget.z() - position.z()));
|
lookTarget.x - position.x, -(lookTarget.z - position.z));
|
||||||
if (currentState == AnimationState::STAND || currentState == AnimationState::WALK) {
|
if (currentState == AnimationState::STAND || currentState == AnimationState::WALK) {
|
||||||
currentState = AnimationState::STAND_TO_ACTION;
|
currentState = AnimationState::STAND_TO_ACTION;
|
||||||
resetAnim = true;
|
resetAnim = true;
|
||||||
@@ -384,24 +397,25 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
resetAnim = true;
|
resetAnim = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float angleDiff = targetFacingAngle - facingAngle;
|
fp angleDiff = targetFacingAngle - facingAngle;
|
||||||
while (angleDiff > PI) angleDiff -= 2.f * PI;
|
const fp PIfp = static_cast<fp>(PI);
|
||||||
while (angleDiff < -PI) angleDiff += 2.f * PI;
|
while (angleDiff > PIfp) angleDiff -= static_cast<fp>(2.f) * PIfp;
|
||||||
const float rotationStep = rotationSpeed * static_cast<float>(deltaMs) / 1000.f;
|
while (angleDiff < -PIfp) angleDiff += static_cast<fp>(2.f) * PIfp;
|
||||||
if (std::fabs(angleDiff) <= rotationStep) {
|
const fp rotationStep = rotationSpeed * static_cast<fp>(deltaMs) / static_cast<fp>(1000.f);
|
||||||
|
if (fpm::abs(angleDiff) <= rotationStep) {
|
||||||
facingAngle = targetFacingAngle;
|
facingAngle = targetFacingAngle;
|
||||||
faceTargetId.clear();
|
faceTargetId.clear();
|
||||||
} else {
|
} else {
|
||||||
facingAngle += angleDiff > 0.f ? rotationStep : -rotationStep;
|
facingAngle += angleDiff > static_cast<fp>(0.f) ? rotationStep : -rotationStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPlayer && hp > 0.f && battle_state == 0 && maxHomeDrift > 0.f) {
|
if (!isPlayer && hp > 0.f && battle_state == 0 && maxHomeDriftSquared > static_cast<fp>(0.f)) {
|
||||||
homeDriftCheckTimer -= static_cast<float>(deltaMs) / 1000.f;
|
homeDriftCheckTimer -= static_cast<fp>(deltaMs) / static_cast<fp>(1000.f);
|
||||||
if (homeDriftCheckTimer <= 0.f) {
|
if (homeDriftCheckTimer <= static_cast <fp>(0.f)) {
|
||||||
homeDriftCheckTimer = HOME_DRIFT_CHECK_INTERVAL;
|
homeDriftCheckTimer = HOME_DRIFT_CHECK_INTERVAL_FP;
|
||||||
Eigen::Vector3f toHome = homePosition - position;
|
Vec3fp toHome = homePosition - position;
|
||||||
toHome.y() = 0.f;
|
toHome.y = static_cast<fp>(0.f);
|
||||||
if (!isMoving() && toHome.norm() > maxHomeDrift) {
|
if (!isMoving() && toHome.normSquared() > maxHomeDriftSquared) {
|
||||||
events.navigationRequested = true;
|
events.navigationRequested = true;
|
||||||
events.navigationTarget = homePosition;
|
events.navigationTarget = homePosition;
|
||||||
}
|
}
|
||||||
@@ -424,10 +438,18 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
events.attackImpact = true;
|
events.attackImpact = true;
|
||||||
events.attackTargetId = attackTargetId;
|
events.attackTargetId = attackTargetId;
|
||||||
events.attackDirection = attackTarget->position - position;
|
events.attackDirection = attackTarget->position - position;
|
||||||
events.attackDirection.y() = 0.f;
|
events.attackDirection.y = static_cast<fp>(0.f);
|
||||||
const float length = events.attackDirection.norm();
|
const fp lengthSquared = fpm::sqrt(events.attackDirection.normSquared());
|
||||||
if (length > 1e-3f) events.attackDirection /= length;
|
if (lengthSquared > static_cast<fp>(1e-3f * 1e-3f))
|
||||||
else events.attackDirection = Eigen::Vector3f::Zero();
|
{
|
||||||
|
const fp length = fpm::sqrt(lengthSquared); //FP SQRT
|
||||||
|
|
||||||
|
events.attackDirection = events.attackDirection / length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
events.attackDirection = Vec3fp{};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showWeapon =
|
showWeapon =
|
||||||
@@ -463,9 +485,9 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
|
|
||||||
void CharacterState::save(nlohmann::json& out) const
|
void CharacterState::save(nlohmann::json& out) const
|
||||||
{
|
{
|
||||||
out["position"] = { position.x(), position.y(), position.z() };
|
out["position"] = { static_cast<double>(position.x), static_cast<double>(position.y), static_cast<double>(position.z) };
|
||||||
out["facingAngle"] = facingAngle;
|
out["facingAngle"] = static_cast<double>(facingAngle);
|
||||||
out["targetFacingAngle"] = targetFacingAngle;
|
out["targetFacingAngle"] = static_cast<double>(targetFacingAngle);
|
||||||
out["hp"] = hp;
|
out["hp"] = hp;
|
||||||
out["enabled"] = enabled;
|
out["enabled"] = enabled;
|
||||||
out["battle_state"] = battle_state;
|
out["battle_state"] = battle_state;
|
||||||
@@ -474,16 +496,16 @@ void CharacterState::save(nlohmann::json& out) const
|
|||||||
out["showWeapon"] = showWeapon;
|
out["showWeapon"] = showWeapon;
|
||||||
out["attackTargetId"] = attackTargetId;
|
out["attackTargetId"] = attackTargetId;
|
||||||
out["faceTargetId"] = faceTargetId;
|
out["faceTargetId"] = faceTargetId;
|
||||||
out["homePosition"] = { homePosition.x(), homePosition.y(), homePosition.z() };
|
out["homePosition"] = { static_cast<double>(homePosition.x), static_cast<double>(homePosition.y), static_cast<double>(homePosition.z) };
|
||||||
out["walkTarget"] = { walkTarget.x(), walkTarget.y(), walkTarget.z() };
|
out["walkTarget"] = { static_cast<double>(walkTarget.x), static_cast<double>(walkTarget.y), static_cast<double>(walkTarget.z) };
|
||||||
out["requestedWalkTarget"] = { requestedWalkTarget.x(), requestedWalkTarget.y(), requestedWalkTarget.z() };
|
out["requestedWalkTarget"] = { static_cast<double>(requestedWalkTarget.x), static_cast<double>(requestedWalkTarget.y), static_cast<double>(requestedWalkTarget.z) };
|
||||||
nlohmann::json wpArr = nlohmann::json::array();
|
nlohmann::json wpArr = nlohmann::json::array();
|
||||||
for (const auto& wp : pathWaypoints) {
|
for (const auto& wp : pathWaypoints) {
|
||||||
wpArr.push_back({ wp.x(), wp.y(), wp.z() });
|
wpArr.push_back({ static_cast<double>(wp.x), static_cast<double>(wp.y), static_cast<double>(wp.z) });
|
||||||
}
|
}
|
||||||
out["pathWaypoints"] = std::move(wpArr);
|
out["pathWaypoints"] = std::move(wpArr);
|
||||||
out["currentWaypointIndex"] = currentWaypointIndex;
|
out["currentWaypointIndex"] = currentWaypointIndex;
|
||||||
out["homeDriftCheckTimer"] = homeDriftCheckTimer;
|
out["homeDriftCheckTimer"] = static_cast<double>(homeDriftCheckTimer);
|
||||||
out["movementBlockedTicks"] = movementBlockedTicks;
|
out["movementBlockedTicks"] = movementBlockedTicks;
|
||||||
out["navigationRetryTicks"] = navigationRetryTicks;
|
out["navigationRetryTicks"] = navigationRetryTicks;
|
||||||
out["movedLastTick"] = movedLastTick;
|
out["movedLastTick"] = movedLastTick;
|
||||||
@@ -492,7 +514,7 @@ void CharacterState::save(nlohmann::json& out) const
|
|||||||
if (!blockingCharacterId.empty())
|
if (!blockingCharacterId.empty())
|
||||||
out["blockingCharacterId"] = blockingCharacterId;
|
out["blockingCharacterId"] = blockingCharacterId;
|
||||||
out["animationFrame"] = animationFrame;
|
out["animationFrame"] = animationFrame;
|
||||||
out["visibleRange"] = visibleRange;
|
out["visibleRange"] = static_cast<double>(visibleRange);
|
||||||
if (!onArrivedCallbackName.empty())
|
if (!onArrivedCallbackName.empty())
|
||||||
out["onArrivedCallbackName"] = onArrivedCallbackName;
|
out["onArrivedCallbackName"] = onArrivedCallbackName;
|
||||||
}
|
}
|
||||||
@@ -505,11 +527,19 @@ void CharacterState::load(const nlohmann::json& in)
|
|||||||
return def;
|
return def;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (in.contains("position")) position = readVec3(in["position"], position);
|
auto readVec3fp = [](const nlohmann::json& j, Vec3fp def) -> Vec3fp {
|
||||||
facingAngle = in.value("facingAngle", facingAngle);
|
if (j.is_array() && j.size() == 3)
|
||||||
targetFacingAngle = in.value("targetFacingAngle", targetFacingAngle);
|
return Vec3fp{ static_cast<fp>(j[0].get<double>()), static_cast<fp>(j[1].get<double>()), static_cast<fp>(j[2].get<double>()) };
|
||||||
|
return def;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (in.contains("position")) {
|
||||||
|
position = readVec3fp(in["position"], position);
|
||||||
|
}
|
||||||
|
facingAngle = static_cast<fp>(in.value("facingAngle", static_cast<double>(facingAngle)));
|
||||||
|
targetFacingAngle = static_cast<fp>(in.value("targetFacingAngle", static_cast<double>(targetFacingAngle)));
|
||||||
hp = in.value("hp", hp);
|
hp = in.value("hp", hp);
|
||||||
visibleRange = in.value("visibleRange", visibleRange);
|
visibleRange = static_cast<fp>(in.value("visibleRange", static_cast<double>(visibleRange)));
|
||||||
enabled = in.value("enabled", enabled);
|
enabled = in.value("enabled", enabled);
|
||||||
battle_state = in.value("battle_state", battle_state);
|
battle_state = in.value("battle_state", battle_state);
|
||||||
currentState = static_cast<AnimationState>(in.value("currentState", static_cast<int>(currentState)));
|
currentState = static_cast<AnimationState>(in.value("currentState", static_cast<int>(currentState)));
|
||||||
@@ -517,17 +547,17 @@ void CharacterState::load(const nlohmann::json& in)
|
|||||||
showWeapon = in.value("showWeapon", showWeapon);
|
showWeapon = in.value("showWeapon", showWeapon);
|
||||||
attackTargetId = in.value("attackTargetId", attackTargetId);
|
attackTargetId = in.value("attackTargetId", attackTargetId);
|
||||||
faceTargetId = in.value("faceTargetId", faceTargetId);
|
faceTargetId = in.value("faceTargetId", faceTargetId);
|
||||||
if (in.contains("homePosition")) homePosition = readVec3(in["homePosition"], homePosition);
|
if (in.contains("homePosition")) homePosition = readVec3fp(in["homePosition"], homePosition);
|
||||||
if (in.contains("walkTarget")) walkTarget = readVec3(in["walkTarget"], walkTarget);
|
if (in.contains("walkTarget")) walkTarget = readVec3fp(in["walkTarget"], walkTarget);
|
||||||
if (in.contains("requestedWalkTarget")) requestedWalkTarget = readVec3(in["requestedWalkTarget"], requestedWalkTarget);
|
if (in.contains("requestedWalkTarget")) requestedWalkTarget = readVec3fp(in["requestedWalkTarget"], requestedWalkTarget);
|
||||||
pathWaypoints.clear();
|
pathWaypoints.clear();
|
||||||
if (in.contains("pathWaypoints") && in["pathWaypoints"].is_array()) {
|
if (in.contains("pathWaypoints") && in["pathWaypoints"].is_array()) {
|
||||||
for (const auto& wp : in["pathWaypoints"]) {
|
for (const auto& wp : in["pathWaypoints"]) {
|
||||||
pathWaypoints.push_back(readVec3(wp, Eigen::Vector3f::Zero()));
|
pathWaypoints.push_back(readVec3fp(wp, Vec3fp()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentWaypointIndex = in.value("currentWaypointIndex", currentWaypointIndex);
|
currentWaypointIndex = in.value("currentWaypointIndex", currentWaypointIndex);
|
||||||
homeDriftCheckTimer = in.value("homeDriftCheckTimer", homeDriftCheckTimer);
|
homeDriftCheckTimer = static_cast<fp>(in.value("homeDriftCheckTimer", static_cast<double>(homeDriftCheckTimer)));
|
||||||
movementBlockedTicks = in.value("movementBlockedTicks", movementBlockedTicks);
|
movementBlockedTicks = in.value("movementBlockedTicks", movementBlockedTicks);
|
||||||
navigationRetryTicks = in.value("navigationRetryTicks", navigationRetryTicks);
|
navigationRetryTicks = in.value("navigationRetryTicks", navigationRetryTicks);
|
||||||
movedLastTick = in.value("movedLastTick", movedLastTick);
|
movedLastTick = in.value("movedLastTick", movedLastTick);
|
||||||
|
|||||||
+25
-30
@@ -61,6 +61,9 @@ public:
|
|||||||
// Distance below which the character is considered to have reached its target.
|
// Distance below which the character is considered to have reached its target.
|
||||||
static constexpr float kWalkThreshold = 0.05f;
|
static constexpr float kWalkThreshold = 0.05f;
|
||||||
|
|
||||||
|
static fp kWalkThreshold_SQUARED() { return static_cast<fp>(kWalkThreshold * kWalkThreshold); };
|
||||||
|
|
||||||
|
|
||||||
// --- Identity ---
|
// --- Identity ---
|
||||||
std::string npcName;
|
std::string npcName;
|
||||||
bool isPlayer = false;
|
bool isPlayer = false;
|
||||||
@@ -68,15 +71,15 @@ public:
|
|||||||
bool canAttack = false;
|
bool canAttack = false;
|
||||||
|
|
||||||
// --- Position / orientation ---
|
// --- Position / orientation ---
|
||||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
Vec3fp position;
|
||||||
float facingAngle = 0.0f;
|
fp facingAngle;
|
||||||
float targetFacingAngle = 0.0f;
|
fp targetFacingAngle;
|
||||||
|
|
||||||
// --- Health ---
|
// --- Health ---
|
||||||
float hp = 200.f;
|
float hp = 200.f;
|
||||||
float initialHp = 200.f;
|
float initialHp = 200.f;
|
||||||
|
|
||||||
float visibleRange = VISIBLE_RANGE;
|
fp visibleRange = static_cast<fp>(VISIBLE_RANGE);
|
||||||
|
|
||||||
// --- Combat flags ---
|
// --- Combat flags ---
|
||||||
int battle_state = 0;
|
int battle_state = 0;
|
||||||
@@ -95,8 +98,8 @@ public:
|
|||||||
std::string faceTargetId;
|
std::string faceTargetId;
|
||||||
|
|
||||||
// --- Home position for drift recovery ---
|
// --- Home position for drift recovery ---
|
||||||
Eigen::Vector3f homePosition = Eigen::Vector3f::Zero();
|
Vec3fp homePosition;
|
||||||
float maxHomeDrift = 3.0f;
|
fp maxHomeDriftSquared = static_cast<fp>(3.f*3.f);
|
||||||
|
|
||||||
// --- Authoritative logical animation clock ---
|
// --- Authoritative logical animation clock ---
|
||||||
// Frame counts are immutable metadata copied from loaded clips during
|
// Frame counts are immutable metadata copied from loaded clips during
|
||||||
@@ -106,26 +109,27 @@ public:
|
|||||||
std::array<int, 9> animationFrameCounts = {{1, 1, 1, 1, 1, 1, 1, 1, 1}};
|
std::array<int, 9> animationFrameCounts = {{1, 1, 1, 1, 1, 1, 1, 1, 1}};
|
||||||
|
|
||||||
// --- Movement / visual parameters ---
|
// --- Movement / visual parameters ---
|
||||||
float walkSpeed = 3.0f;
|
fp walkSpeed = static_cast<fp>(3.0f);
|
||||||
float rotationSpeed = 4.0f;
|
fp rotationSpeed = static_cast<fp>(4.0f);
|
||||||
|
|
||||||
float modelScale = 0.12f;
|
float modelScale = 0.12f;
|
||||||
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
||||||
bool useGpuSkinning = true;
|
bool useGpuSkinning = true;
|
||||||
// Full clearance used while planning around stationary characters.
|
// Full clearance used while planning around stationary characters.
|
||||||
float collisionRadius = 0.45f;
|
//fp collisionRadius = static_cast<fp>(0.45f);
|
||||||
// Smaller physical contact threshold used by the movement guard.
|
// Smaller physical contact threshold used by the movement guard.
|
||||||
float movementContactRadius = 0.32f;
|
//fp movementContactRadius = static_cast<fp>(0.32f);
|
||||||
float interactionRadius = 0.0f;
|
fp interactionRadius = static_cast<fp>(0.0f);
|
||||||
bool movedLastTick = false;
|
bool movedLastTick = false;
|
||||||
|
|
||||||
// --- Pathfinding data (serializable parts) ---
|
// --- Pathfinding data (serializable parts) ---
|
||||||
Eigen::Vector3f walkTarget = Eigen::Vector3f::Zero();
|
Vec3fp walkTarget;
|
||||||
Eigen::Vector3f requestedWalkTarget = Eigen::Vector3f::Zero();
|
Vec3fp requestedWalkTarget;
|
||||||
std::vector<Eigen::Vector3f> pathWaypoints;
|
std::vector<Vec3fp> pathWaypoints;
|
||||||
size_t currentWaypointIndex = 0;
|
size_t currentWaypointIndex = 0;
|
||||||
|
|
||||||
// Internal drift-check timer (serializable so it survives save/load).
|
// Internal drift-check timer (serializable so it survives save/load).
|
||||||
float homeDriftCheckTimer = 5.0f;
|
fp homeDriftCheckTimer = static_cast<fp>(5.0f);
|
||||||
int movementBlockedTicks = 0;
|
int movementBlockedTicks = 0;
|
||||||
int navigationRetryTicks = 0;
|
int navigationRetryTicks = 0;
|
||||||
std::string blockingCharacterId;
|
std::string blockingCharacterId;
|
||||||
@@ -145,10 +149,10 @@ public:
|
|||||||
const std::string& entityId,
|
const std::string& entityId,
|
||||||
std::vector<int> cooldownTicks = {});
|
std::vector<int> cooldownTicks = {});
|
||||||
bool isMoving() const;
|
bool isMoving() const;
|
||||||
const Eigen::Vector3f& getRequestedWalkTarget() const { return requestedWalkTarget; }
|
const Vec3fp& getRequestedWalkTarget() const { return requestedWalkTarget; }
|
||||||
Eigen::Vector3f getCurrentNavigationTarget() const;
|
Vec3fp getCurrentNavigationTarget() const;
|
||||||
void stopInPlace();
|
void stopInPlace();
|
||||||
void setTarget(const Eigen::Vector3f& target,
|
void setTarget(const Vec3fp& target,
|
||||||
std::string callbackName,
|
std::string callbackName,
|
||||||
const LocationNetworkState& location,
|
const LocationNetworkState& location,
|
||||||
const std::string& entityId);
|
const std::string& entityId);
|
||||||
@@ -173,23 +177,14 @@ using PlayerStateMap =
|
|||||||
std::unordered_map<std::string, std::shared_ptr<CharacterState>>;
|
std::unordered_map<std::string, std::shared_ptr<CharacterState>>;
|
||||||
|
|
||||||
struct CharacterObservation {
|
struct CharacterObservation {
|
||||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
Vec3fp position;
|
||||||
float hp = 0.f;
|
float hp = 0.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterMovementObstacle {
|
|
||||||
std::string id;
|
|
||||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
|
||||||
float radius = 0.f;
|
|
||||||
bool moving = false;
|
|
||||||
Eigen::Vector3f movementDirection = Eigen::Vector3f::Zero();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CharacterUpdateContext {
|
struct CharacterUpdateContext {
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
const CharacterObservation* attackTarget = nullptr;
|
const CharacterObservation* attackTarget = nullptr;
|
||||||
const CharacterObservation* faceTarget = nullptr;
|
const CharacterObservation* faceTarget = nullptr;
|
||||||
const std::vector<CharacterMovementObstacle>* movementObstacles = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterUpdateEvents {
|
struct CharacterUpdateEvents {
|
||||||
@@ -197,11 +192,11 @@ struct CharacterUpdateEvents {
|
|||||||
bool deathAnimationComplete = false;
|
bool deathAnimationComplete = false;
|
||||||
bool navigationRequested = false;
|
bool navigationRequested = false;
|
||||||
bool forceNavigationReplan = false;
|
bool forceNavigationReplan = false;
|
||||||
Eigen::Vector3f navigationTarget = Eigen::Vector3f::Zero();
|
Vec3fp navigationTarget;
|
||||||
std::string blockingCharacterId;
|
std::string blockingCharacterId;
|
||||||
std::string arrivedCallbackName;
|
std::string arrivedCallbackName;
|
||||||
std::string attackTargetId;
|
std::string attackTargetId;
|
||||||
Eigen::Vector3f attackDirection = Eigen::Vector3f::Zero();
|
Vec3fp attackDirection;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FRG
|
} // namespace FRG
|
||||||
+2
-2
@@ -664,9 +664,9 @@ namespace FRG
|
|||||||
if (!loc) return;
|
if (!loc) return;
|
||||||
if (loc->player) {
|
if (loc->player) {
|
||||||
server().submit(SetCharacterPositionCommand{
|
server().submit(SetCharacterPositionCommand{
|
||||||
destName, loc->player->entityId(), destPos});
|
destName, loc->player->entityId(), Vec3fp::fromEigen(destPos)});
|
||||||
server().submit(SetCharacterFacingAngleCommand{
|
server().submit(SetCharacterFacingAngleCommand{
|
||||||
destName, loc->player->entityId(), destRotY, true});
|
destName, loc->player->entityId(), static_cast<fp>(destRotY), true});
|
||||||
loc->stopCharacter(*loc->player);
|
loc->stopCharacter(*loc->player);
|
||||||
}
|
}
|
||||||
loc->localState().cameraAzimuth = destRotY;
|
loc->localState().cameraAzimuth = destRotY;
|
||||||
|
|||||||
+16
-47
@@ -126,7 +126,7 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Location::setCharacterTarget(
|
void Location::setCharacterTarget(
|
||||||
Character& character, const Eigen::Vector3f& target, std::string callbackName)
|
Character& character, const Vec3fp& target, std::string callbackName)
|
||||||
{
|
{
|
||||||
gameServer_.submit(SetCharacterDestinationCommand{
|
gameServer_.submit(SetCharacterDestinationCommand{
|
||||||
locationId_, character.entityId(), target, std::move(callbackName)});
|
locationId_, character.entityId(), target, std::move(callbackName)});
|
||||||
@@ -1597,7 +1597,7 @@ namespace FRG
|
|||||||
if (playerState && !pointLights.empty()) {
|
if (playerState && !pointLights.empty()) {
|
||||||
float minDist = FLT_MAX;
|
float minDist = FLT_MAX;
|
||||||
for (int i = 0; i < static_cast<int>(pointLights.size()); ++i) {
|
for (int i = 0; i < static_cast<int>(pointLights.size()); ++i) {
|
||||||
const float d = (pointLights[i].position - playerState->position).norm();
|
const float d = (pointLights[i].position - playerState->position.toEigen()).norm();
|
||||||
if (d < minDist) { minDist = d; nearestLightIdx = i; }
|
if (d < minDist) { minDist = d; nearestLightIdx = i; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1611,12 +1611,12 @@ namespace FRG
|
|||||||
if (!active && playerState) {
|
if (!active && playerState) {
|
||||||
if (pl.autoLightLimitX > 0.0f && pl.autoLightLimitY > 0.0f) {
|
if (pl.autoLightLimitX > 0.0f && pl.autoLightLimitY > 0.0f) {
|
||||||
// Rectangular activation zone — independent of nearest-light heuristic
|
// Rectangular activation zone — independent of nearest-light heuristic
|
||||||
const float dx = std::abs(playerState->position.x() - pl.position.x());
|
const float dx = std::abs(static_cast<float>(playerState->position.x) - pl.position.x());
|
||||||
const float dz = std::abs(playerState->position.z() - pl.position.z());
|
const float dz = std::abs(static_cast<float>(playerState->position.z) - pl.position.z());
|
||||||
active = (dx <= pl.autoLightLimitX && dz <= pl.autoLightLimitY);
|
active = (dx <= pl.autoLightLimitX && dz <= pl.autoLightLimitY);
|
||||||
} else {
|
} else {
|
||||||
// only the nearest autoLight activates, with optional distance cap
|
// only the nearest autoLight activates, with optional distance cap
|
||||||
const float dist = (pl.position - playerState->position).norm();
|
const float dist = (pl.position - playerState->position.toEigen()).norm();
|
||||||
active = (i == nearestLightIdx) &&
|
active = (i == nearestLightIdx) &&
|
||||||
(pl.autoLightDistance <= 0.0f || dist <= pl.autoLightDistance);
|
(pl.autoLightDistance <= 0.0f || dist <= pl.autoLightDistance);
|
||||||
}
|
}
|
||||||
@@ -1632,7 +1632,7 @@ namespace FRG
|
|||||||
if (playerState && !activeLightIndices.empty()) {
|
if (playerState && !activeLightIndices.empty()) {
|
||||||
float minDist = FLT_MAX;
|
float minDist = FLT_MAX;
|
||||||
for (int idx : activeLightIndices) {
|
for (int idx : activeLightIndices) {
|
||||||
const float d = (pointLights[idx].position - playerState->position).norm();
|
const float d = (pointLights[idx].position - playerState->position.toEigen()).norm();
|
||||||
if (d < minDist) { minDist = d; shadowLight = &pointLights[idx]; }
|
if (d < minDist) { minDist = d; shadowLight = &pointLights[idx]; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1850,37 +1850,6 @@ namespace FRG
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Location::nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom)
|
|
||||||
{
|
|
||||||
auto standingState = standing ? standing->stateSnapshot() : nullptr;
|
|
||||||
if (!standingState || standingState->isPlayer) return;
|
|
||||||
auto locationState = stateSnapshot();
|
|
||||||
if (!locationState || locationState->activeNavigationIndex < 0 ||
|
|
||||||
locationState->activeNavigationIndex >=
|
|
||||||
static_cast<int>(locationState->navigationMaps.size())) return;
|
|
||||||
const PathFinder& navigation =
|
|
||||||
locationState->navigationMaps[locationState->activeNavigationIndex];
|
|
||||||
if (navigation.isReady()) return;
|
|
||||||
|
|
||||||
static constexpr float kNudgeDist = 1.2f;
|
|
||||||
|
|
||||||
Eigen::Vector3f dir = standingState->position - awayFrom;
|
|
||||||
dir.y() = 0.f;
|
|
||||||
if (dir.norm() < 1e-3f) dir = Eigen::Vector3f(1.f, 0.f, 0.f);
|
|
||||||
else dir.normalize();
|
|
||||||
|
|
||||||
const float angles[] = { 0.f, static_cast<float>(M_PI * 0.5), static_cast<float>(-M_PI * 0.5), static_cast<float>(M_PI) };
|
|
||||||
for (float angle : angles) {
|
|
||||||
Eigen::Vector3f rotated = Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitY()) * dir;
|
|
||||||
Eigen::Vector3f candidate = standingState->position + rotated * kNudgeDist;
|
|
||||||
candidate.y() = 0.f;
|
|
||||||
if (navigation.isWalkable(candidate)) {
|
|
||||||
setCharacterTarget(*standing, candidate);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Location::update(int64_t delta, const LocationSimulationEvents& events)
|
void Location::update(int64_t delta, const LocationSimulationEvents& events)
|
||||||
{
|
{
|
||||||
synchronizeRemotePlayers();
|
synchronizeRemotePlayers();
|
||||||
@@ -1951,7 +1920,7 @@ namespace FRG
|
|||||||
player->update(delta, eventFor(gameServer_.localPlayerId()));
|
player->update(delta, eventFor(gameServer_.localPlayerId()));
|
||||||
auto playerState = player->stateSnapshot();
|
auto playerState = player->stateSnapshot();
|
||||||
if (playerState && playerState->enabled)
|
if (playerState && playerState->enabled)
|
||||||
updateTriggerZones(playerState->position);
|
updateTriggerZones(playerState->position.toEigen());
|
||||||
}
|
}
|
||||||
for (auto& [id, remote] : remotePlayers_)
|
for (auto& [id, remote] : remotePlayers_)
|
||||||
if (remote) remote->update(delta, eventFor(id));
|
if (remote) remote->update(delta, eventFor(id));
|
||||||
@@ -1988,7 +1957,7 @@ namespace FRG
|
|||||||
auto playerState = player ? player->stateSnapshot() : nullptr;
|
auto playerState = player ? player->stateSnapshot() : nullptr;
|
||||||
if (auto* tz = getTargetTeleportZone(*locationState);
|
if (auto* tz = getTargetTeleportZone(*locationState);
|
||||||
tz && playerState && playerState->enabled) {
|
tz && playerState && playerState->enabled) {
|
||||||
float dist = (playerState->position - tz->position).norm();
|
float dist = (playerState->position.toEigen() - tz->position).norm();
|
||||||
if (dist <= tz->radius) {
|
if (dist <= tz->radius) {
|
||||||
logger() << "[TELEPORT] Player reached teleport zone '" << tz->id << "'" << std::endl;
|
logger() << "[TELEPORT] Player reached teleport zone '" << tz->id << "'" << std::endl;
|
||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
@@ -2005,7 +1974,7 @@ namespace FRG
|
|||||||
for (auto& tz : teleportZones) {
|
for (auto& tz : teleportZones) {
|
||||||
if (!tz.active || !tz.automatic) continue;
|
if (!tz.active || !tz.automatic) continue;
|
||||||
const float autoR = tz.automaticRadius > 0.0f ? tz.automaticRadius : tz.radius;
|
const float autoR = tz.automaticRadius > 0.0f ? tz.automaticRadius : tz.radius;
|
||||||
const float dist = (playerState->position - tz.position).norm();
|
const float dist = (playerState->position.toEigen() - tz.position).norm();
|
||||||
|
|
||||||
if (!tz.automaticInitialized) {
|
if (!tz.automaticInitialized) {
|
||||||
// If the player starts inside the zone (e.g. just arrived here via teleport),
|
// If the player starts inside the zone (e.g. just arrived here via teleport),
|
||||||
@@ -2101,7 +2070,7 @@ namespace FRG
|
|||||||
const Eigen::Vector3f interactionTarget = clickedObject->state.hasInteractionPosition
|
const Eigen::Vector3f interactionTarget = clickedObject->state.hasInteractionPosition
|
||||||
? clickedObject->state.interactionPosition
|
? clickedObject->state.interactionPosition
|
||||||
: clickedObject->state.position;
|
: clickedObject->state.position;
|
||||||
setCharacterTarget(*player, interactionTarget);
|
setCharacterTarget(*player, Vec3fp::fromEigen(interactionTarget));
|
||||||
pathVisualization.start(*player, interactionTarget);
|
pathVisualization.start(*player, interactionTarget);
|
||||||
clearCharacterAttackTarget(*player);
|
clearCharacterAttackTarget(*player);
|
||||||
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
||||||
@@ -2114,7 +2083,7 @@ namespace FRG
|
|||||||
if (clickedNpc && player) {
|
if (clickedNpc && player) {
|
||||||
auto clickedNpcState = clickedNpc->stateSnapshot();
|
auto clickedNpcState = clickedNpc->stateSnapshot();
|
||||||
if (!clickedNpcState) return;
|
if (!clickedNpcState) return;
|
||||||
float distance = (playerState->position - clickedNpcState->position).norm();
|
float distance = (playerState->position - clickedNpcState->position).toEigen().norm();
|
||||||
int npcIndex = -1;
|
int npcIndex = -1;
|
||||||
for (size_t i = 0; i < npcs.size(); ++i) {
|
for (size_t i = 0; i < npcs.size(); ++i) {
|
||||||
if (npcs[i].get() == clickedNpc) {
|
if (npcs[i].get() == clickedNpc) {
|
||||||
@@ -2144,11 +2113,11 @@ namespace FRG
|
|||||||
setCharacterTarget(*player, clickedNpcState->position);
|
setCharacterTarget(*player, clickedNpcState->position);
|
||||||
pathVisualization.start(
|
pathVisualization.start(
|
||||||
*player,
|
*player,
|
||||||
clickedNpcState->position,
|
clickedNpcState->position.toEigen(),
|
||||||
PathVisualization::Style::Hostile);
|
PathVisualization::Style::Hostile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distance <= clickedNpcState->interactionRadius) {
|
if (distance <= static_cast<float>(clickedNpcState->interactionRadius)) {
|
||||||
//logger() << "[CLICK] Hostile NPC " << npcIndex << " in range; firing on_npc_interact" << std::endl;
|
//logger() << "[CLICK] Hostile NPC " << npcIndex << " in range; firing on_npc_interact" << std::endl;
|
||||||
scriptEngine.callNpcInteractCallback(clickedNpc->entityId());
|
scriptEngine.callNpcInteractCallback(clickedNpc->entityId());
|
||||||
}
|
}
|
||||||
@@ -2172,7 +2141,7 @@ namespace FRG
|
|||||||
//logger() << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
|
//logger() << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
|
||||||
// << " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
|
// << " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
|
||||||
setCharacterTarget(*player, clickedNpcState->position);
|
setCharacterTarget(*player, clickedNpcState->position);
|
||||||
pathVisualization.start(*player, clickedNpcState->position);
|
pathVisualization.start(*player, clickedNpcState->position.toEigen());
|
||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
locationId_, std::string(), clickedNpc->entityId(),
|
locationId_, std::string(), clickedNpc->entityId(),
|
||||||
std::string()});
|
std::string()});
|
||||||
@@ -2199,7 +2168,7 @@ namespace FRG
|
|||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
locationId_, std::string(), std::string(),
|
locationId_, std::string(), std::string(),
|
||||||
clickedTeleport->id});
|
clickedTeleport->id});
|
||||||
setCharacterTarget(*player, clickedTeleport->position);
|
setCharacterTarget(*player, Vec3fp::fromEigen(clickedTeleport->position));
|
||||||
pathVisualization.start(
|
pathVisualization.start(
|
||||||
*player,
|
*player,
|
||||||
clickedTeleport->position,
|
clickedTeleport->position,
|
||||||
@@ -2207,7 +2176,7 @@ namespace FRG
|
|||||||
clearCharacterAttackTarget(*player);
|
clearCharacterAttackTarget(*player);
|
||||||
} else {
|
} else {
|
||||||
const Eigen::Vector3f clickedDestination(hit.x(), 0.f, hit.z());
|
const Eigen::Vector3f clickedDestination(hit.x(), 0.f, hit.z());
|
||||||
setCharacterTarget(*player, clickedDestination);
|
setCharacterTarget(*player, Vec3fp::fromEigen(clickedDestination));
|
||||||
pathVisualization.start(*player, clickedDestination);
|
pathVisualization.start(*player, clickedDestination);
|
||||||
clearCharacterAttackTarget(*player);
|
clearCharacterAttackTarget(*player);
|
||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
|
|||||||
+2
-3
@@ -64,7 +64,7 @@ namespace FRG
|
|||||||
std::string teleportsJsonPath;
|
std::string teleportsJsonPath;
|
||||||
std::string triggerZonesJsonPath;
|
std::string triggerZonesJsonPath;
|
||||||
std::string lightsJsonPath;
|
std::string lightsJsonPath;
|
||||||
Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero();
|
Vec3fp playerPosition;
|
||||||
// Player shared by all locations of a campaign (see Location::createPlayer);
|
// Player shared by all locations of a campaign (see Location::createPlayer);
|
||||||
// setup() creates a private one when this is null.
|
// setup() creates a private one when this is null.
|
||||||
std::shared_ptr<Character> player;
|
std::shared_ptr<Character> player;
|
||||||
@@ -174,7 +174,7 @@ namespace FRG
|
|||||||
void setup(const LocationSetup& params, AudioPlayerAsync* audioPlayer);
|
void setup(const LocationSetup& params, AudioPlayerAsync* audioPlayer);
|
||||||
//void setupNavigation(const std::vector<std::string>& paths);
|
//void setupNavigation(const std::vector<std::string>& paths);
|
||||||
bool switchNavigation(int index);
|
bool switchNavigation(int index);
|
||||||
void setCharacterTarget(Character& character, const Eigen::Vector3f& target,
|
void setCharacterTarget(Character& character, const Vec3fp& target,
|
||||||
std::string callbackName = "");
|
std::string callbackName = "");
|
||||||
void clearCharacterAttackTarget(Character& character);
|
void clearCharacterAttackTarget(Character& character);
|
||||||
void stopCharacter(Character& character);
|
void stopCharacter(Character& character);
|
||||||
@@ -259,7 +259,6 @@ namespace FRG
|
|||||||
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
|
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
|
||||||
void loadPointLights(const std::string& jsonPath, const char* zipFile);
|
void loadPointLights(const std::string& jsonPath, const char* zipFile);
|
||||||
void updateTriggerZones(const Eigen::Vector3f& playerPos);
|
void updateTriggerZones(const Eigen::Vector3f& playerPos);
|
||||||
void nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom);
|
|
||||||
void captureAuxiliaryState(LocationGameState& out) const;
|
void captureAuxiliaryState(LocationGameState& out) const;
|
||||||
void applyAuxiliaryState(const LocationGameState& in);
|
void applyAuxiliaryState(const LocationGameState& in);
|
||||||
|
|
||||||
|
|||||||
@@ -484,9 +484,9 @@ namespace FRG
|
|||||||
data.rotationZ = 0.0f;
|
data.rotationZ = 0.0f;
|
||||||
auto playerState = loc.player ? loc.player->stateSnapshot() : nullptr;
|
auto playerState = loc.player ? loc.player->stateSnapshot() : nullptr;
|
||||||
if (!playerState) return;
|
if (!playerState) return;
|
||||||
data.positionX = playerState->position.x();
|
data.positionX = static_cast<float>(playerState->position.x);
|
||||||
data.positionY = playerState->position.y();//-0.1;
|
data.positionY = static_cast<float>(playerState->position.y);
|
||||||
data.positionZ = playerState->position.z();
|
data.positionZ = static_cast<float>(playerState->position.z);
|
||||||
data.scale = scaleDist(rng);
|
data.scale = scaleDist(rng);
|
||||||
|
|
||||||
GameObjectData data2;
|
GameObjectData data2;
|
||||||
|
|||||||
+16
-124
@@ -159,7 +159,7 @@ std::vector<PathFinder::DynamicObstacle> LocationGameState::collectPathObstacles
|
|||||||
|
|
||||||
bool LocationGameState::setCharacterDestination(
|
bool LocationGameState::setCharacterDestination(
|
||||||
const std::string& id,
|
const std::string& id,
|
||||||
const Eigen::Vector3f& target,
|
const Vec3fp& target,
|
||||||
std::string callbackName,
|
std::string callbackName,
|
||||||
PlayerStateMap& players,
|
PlayerStateMap& players,
|
||||||
const std::string& forcedObstacleId)
|
const std::string& forcedObstacleId)
|
||||||
@@ -201,10 +201,10 @@ bool LocationGameState::replanCharacter(
|
|||||||
character->attackTargetId.clear();
|
character->attackTargetId.clear();
|
||||||
character->battle_state = 0;
|
character->battle_state = 0;
|
||||||
character->attack = 0;
|
character->attack = 0;
|
||||||
const Eigen::Vector3f homeTarget(
|
const Vec3fp homeTarget(
|
||||||
character->homePosition.x(), 0.f, character->homePosition.z());
|
character->homePosition.x, static_cast<fp>(0.f), character->homePosition.z);
|
||||||
if ((homeTarget - character->position).norm() >
|
if ((homeTarget - character->position).normSquared() >
|
||||||
CharacterState::kWalkThreshold) {
|
CharacterState::kWalkThreshold_SQUARED()) {
|
||||||
character->setTarget(
|
character->setTarget(
|
||||||
homeTarget, {}, *location, id);
|
homeTarget, {}, *location, id);
|
||||||
}
|
}
|
||||||
@@ -234,14 +234,14 @@ std::string LocationGameState::selectNpcTarget(
|
|||||||
playerIds.push_back(id);
|
playerIds.push_back(id);
|
||||||
std::sort(playerIds.begin(), playerIds.end());
|
std::sort(playerIds.begin(), playerIds.end());
|
||||||
|
|
||||||
const float visibleRangeSquared = npc.visibleRange * npc.visibleRange;
|
const fp visibleRangeSquared = npc.visibleRange * npc.visibleRange;
|
||||||
std::string selectedTarget;
|
std::string selectedTarget;
|
||||||
float nearestDistanceSquared = visibleRangeSquared;
|
fp nearestDistanceSquared = visibleRangeSquared;
|
||||||
for (const std::string& playerId : playerIds) {
|
for (const std::string& playerId : playerIds) {
|
||||||
const Eigen::Vector3f delta =
|
const Vec3fp delta =
|
||||||
players.at(playerId)->position - npc.position;
|
players.at(playerId)->position - npc.position;
|
||||||
const float distanceSquared =
|
const fp distanceSquared =
|
||||||
delta.x() * delta.x() + delta.z() * delta.z();
|
delta.x * delta.x + delta.z * delta.z;
|
||||||
if (distanceSquared <= nearestDistanceSquared &&
|
if (distanceSquared <= nearestDistanceSquared &&
|
||||||
(selectedTarget.empty() ||
|
(selectedTarget.empty() ||
|
||||||
distanceSquared < nearestDistanceSquared ||
|
distanceSquared < nearestDistanceSquared ||
|
||||||
@@ -423,89 +423,16 @@ LocationSimulationEvents LocationGameState::update(
|
|||||||
if (selectedTarget.empty()) {
|
if (selectedTarget.empty()) {
|
||||||
npc.battle_state = 0;
|
npc.battle_state = 0;
|
||||||
npc.attack = 0;
|
npc.attack = 0;
|
||||||
const Eigen::Vector3f homeTarget(
|
const Vec3fp homeTarget(
|
||||||
npc.homePosition.x(), 0.f, npc.homePosition.z());
|
npc.homePosition.x, static_cast<fp>(0.f), npc.homePosition.z);
|
||||||
const Eigen::Vector3f toHome = homeTarget - npc.position;
|
const Vec3fp toHome = homeTarget - npc.position;
|
||||||
if (toHome.norm() > CharacterState::kWalkThreshold &&
|
if (toHome.normSquared() > CharacterState::kWalkThreshold_SQUARED() &&
|
||||||
(npc.requestedWalkTarget - homeTarget).norm() >
|
(npc.requestedWalkTarget - homeTarget).normSquared() >
|
||||||
CharacterState::kWalkThreshold) {
|
CharacterState::kWalkThreshold_SQUARED()) {
|
||||||
setCharacterDestination(id, homeTarget, {}, players);
|
setCharacterDestination(id, homeTarget, {}, players);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
std::vector<CharacterMovementObstacle> movementObstacles;
|
|
||||||
movementObstacles.reserve(playerIds.size() + npcIds.size());
|
|
||||||
auto addMovementObstacle =
|
|
||||||
[&movementObstacles](const std::string& id,
|
|
||||||
const CharacterState& character) {
|
|
||||||
Eigen::Vector3f direction =
|
|
||||||
character.getCurrentNavigationTarget() - character.position;
|
|
||||||
direction.y() = 0.f;
|
|
||||||
const float length = direction.norm();
|
|
||||||
if (length > CharacterState::kWalkThreshold) direction /= length;
|
|
||||||
else direction = Eigen::Vector3f::Zero();
|
|
||||||
movementObstacles.push_back({
|
|
||||||
id,
|
|
||||||
character.position,
|
|
||||||
character.movementContactRadius,
|
|
||||||
character.movedLastTick,
|
|
||||||
direction});
|
|
||||||
};
|
|
||||||
for (const std::string& id : playerIds) {
|
|
||||||
const CharacterState& character = *players.at(id);
|
|
||||||
if (character.enabled && character.hp > 0.f)
|
|
||||||
addMovementObstacle(id, character);
|
|
||||||
}
|
|
||||||
for (const std::string& id : npcIds) {
|
|
||||||
const CharacterState& character = *npcs.at(id);
|
|
||||||
if (character.enabled && character.hp > 0.f)
|
|
||||||
addMovementObstacle(id, character);
|
|
||||||
}
|
|
||||||
// One global ID order also makes blocker selection deterministic.
|
|
||||||
std::sort(
|
|
||||||
movementObstacles.begin(), movementObstacles.end(),
|
|
||||||
[](const CharacterMovementObstacle& lhs,
|
|
||||||
const CharacterMovementObstacle& rhs) { return lhs.id < rhs.id; });*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
auto advance = [&](const std::string& id, CharacterState& state) {
|
|
||||||
CharacterUpdateContext context;
|
|
||||||
context.characterId = id;
|
|
||||||
context.movementObstacles = &movementObstacles;
|
|
||||||
auto attack = observations.find(state.attackTargetId);
|
|
||||||
if (attack != observations.end()) context.attackTarget = &attack->second;
|
|
||||||
auto face = observations.find(state.faceTargetId);
|
|
||||||
if (face != observations.end()) context.faceTarget = &face->second;
|
|
||||||
CharacterUpdateEvents events = state.update(deltaMs, context);
|
|
||||||
auto reservation = std::lower_bound(
|
|
||||||
movementObstacles.begin(), movementObstacles.end(), id,
|
|
||||||
[](const CharacterMovementObstacle& obstacle,
|
|
||||||
const std::string& value) { return obstacle.id < value; });
|
|
||||||
if (reservation != movementObstacles.end() && reservation->id == id) {
|
|
||||||
reservation->position = state.position;
|
|
||||||
reservation->moving = state.movedLastTick;
|
|
||||||
Eigen::Vector3f direction =
|
|
||||||
state.getCurrentNavigationTarget() - state.position;
|
|
||||||
direction.y() = 0.f;
|
|
||||||
const float length = direction.norm();
|
|
||||||
if (length > CharacterState::kWalkThreshold)
|
|
||||||
reservation->movementDirection = direction / length;
|
|
||||||
else
|
|
||||||
reservation->movementDirection = Eigen::Vector3f::Zero();
|
|
||||||
}
|
|
||||||
if (!events.arrivedCallbackName.empty() || events.attackImpact ||
|
|
||||||
events.deathAnimationComplete || events.navigationRequested) {
|
|
||||||
result.characters.push_back({id, std::move(events)});
|
|
||||||
}
|
|
||||||
};*/
|
|
||||||
/*
|
|
||||||
for (const std::string& id : playerIds)
|
|
||||||
if (players.at(id)->enabled)
|
|
||||||
advance(id, *players.at(id));
|
|
||||||
for (const std::string& id : npcIds)
|
|
||||||
if (npcs.at(id)->enablesd)
|
|
||||||
advance(id, *npcs.at(id));*/
|
|
||||||
|
|
||||||
for (const std::string& id : playerIds)
|
for (const std::string& id : playerIds)
|
||||||
{
|
{
|
||||||
@@ -544,41 +471,6 @@ LocationSimulationEvents LocationGameState::update(
|
|||||||
source.events.attackDirection});
|
source.events.attackDirection});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
auto player = players.find("player1");
|
|
||||||
if (!interactiveObjectsLocked &&
|
|
||||||
!location->targetInteractiveObjectId.empty() &&
|
|
||||||
player != players.end() && player->second && player->second->enabled) {
|
|
||||||
auto target = interactiveObjects.find(location->targetInteractiveObjectId);
|
|
||||||
const auto object =
|
|
||||||
target == interactiveObjects.end() ? nullptr : target->second;
|
|
||||||
if (object && !object->isAnimating) {
|
|
||||||
const Eigen::Vector3f& approachTarget = object->hasInteractionPosition
|
|
||||||
? object->interactionPosition : object->position;
|
|
||||||
if ((player->second->position - approachTarget).norm() <= object->approachRadius) {
|
|
||||||
result.interactiveObjects.push_back(
|
|
||||||
{location->targetInteractiveObjectId, {}, true,
|
|
||||||
object->activateFunctionName});
|
|
||||||
location->targetInteractiveObjectId.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!location->targetInteractNpcId.empty() &&
|
|
||||||
player != players.end() && player->second && player->second->enabled &&
|
|
||||||
npcs.count(location->targetInteractNpcId) != 0 &&
|
|
||||||
npcs.at(location->targetInteractNpcId)) {
|
|
||||||
const auto& npc = npcs.at(location->targetInteractNpcId);
|
|
||||||
const float distance =
|
|
||||||
(player->second->position - npc->position).norm();
|
|
||||||
if (distance <= NPC_TALK_DISTANCE) {
|
|
||||||
player->second->stopInPlace();
|
|
||||||
npc->faceTargetId = "player1";
|
|
||||||
result.npcInteractionId = location->targetInteractNpcId;
|
|
||||||
location->targetInteractNpcId.clear();
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ struct CharacterDamageEvent {
|
|||||||
float amount = 0.f;
|
float amount = 0.f;
|
||||||
float hp = 0.f;
|
float hp = 0.f;
|
||||||
float maxHp = 0.f;
|
float maxHp = 0.f;
|
||||||
Eigen::Vector3f direction = Eigen::Vector3f::Zero();
|
Vec3fp direction;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InteractiveObjectSimulationEvent {
|
struct InteractiveObjectSimulationEvent {
|
||||||
@@ -70,7 +70,7 @@ struct LocationGameState {
|
|||||||
const std::string& forcedObstacleId = {}) const;*/
|
const std::string& forcedObstacleId = {}) const;*/
|
||||||
bool setCharacterDestination(
|
bool setCharacterDestination(
|
||||||
const std::string& id,
|
const std::string& id,
|
||||||
const Eigen::Vector3f& target,
|
const Vec3fp& target,
|
||||||
std::string callbackName,
|
std::string callbackName,
|
||||||
PlayerStateMap& players,
|
PlayerStateMap& players,
|
||||||
const std::string& forcedObstacleId = {});
|
const std::string& forcedObstacleId = {});
|
||||||
|
|||||||
+19
-14
@@ -13,6 +13,9 @@
|
|||||||
|
|
||||||
namespace FRG {
|
namespace FRG {
|
||||||
|
|
||||||
|
constexpr float kPi = 3.14159265358979323846f;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Reverse-looks-up the global name a Lua function is registered under, by
|
// Reverse-looks-up the global name a Lua function is registered under, by
|
||||||
@@ -126,9 +129,9 @@ namespace FRG {
|
|||||||
std::cerr << "[script] npc_walk_to: on_arrived must be a global Lua function\n";
|
std::cerr << "[script] npc_walk_to: on_arrived must be a global Lua function\n";
|
||||||
}
|
}
|
||||||
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
||||||
loc->locationId(), npc->entityId(), Eigen::Vector3f(x, 0.f, z)});
|
loc->locationId(), npc->entityId(), Vec3fp(static_cast<fp>(x), static_cast<fp>(0.f), static_cast<fp>(z))});
|
||||||
loc->setCharacterTarget(
|
loc->setCharacterTarget(
|
||||||
*npc, Eigen::Vector3f(x, y, z), std::move(cbName));
|
*npc, Vec3fp(static_cast<fp>(x), static_cast<fp>(y), static_cast<fp>(z)), std::move(cbName));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -140,16 +143,16 @@ namespace FRG {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
const fp rad = static_cast<fp>(angle) * static_cast<fp>(kPi) / static_cast < fp>(180.f);
|
||||||
logger() << "[script] npc_rotate_to: " << npcId << " angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
logger() << "[script] npc_rotate_to: " << npcId << " angle " << angle << " degrees (" << static_cast<float>(rad) << " radians)" << std::endl;
|
||||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
loc->locationId(), npc->entityId(), rad, false});
|
loc->locationId(), npc->entityId(), rad, false});
|
||||||
});
|
});
|
||||||
|
|
||||||
api.set_function("player_rotate_to",
|
api.set_function("player_rotate_to",
|
||||||
[loc](float angle) {
|
[loc](float angle) {
|
||||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
const fp rad = static_cast<fp>(angle) * static_cast<fp>(kPi) / static_cast <fp>(180.f);
|
||||||
logger() << "[script] player_rotate_to: angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
logger() << "[script] player_rotate_to: angle " << angle << " degrees (" << static_cast<float>(rad) << " radians)" << std::endl;
|
||||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
loc->locationId(), loc->player->entityId(), rad, false});
|
loc->locationId(), loc->player->entityId(), rad, false});
|
||||||
});
|
});
|
||||||
@@ -188,9 +191,9 @@ namespace FRG {
|
|||||||
}
|
}
|
||||||
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
||||||
loc->locationId(), loc->player->entityId(),
|
loc->locationId(), loc->player->entityId(),
|
||||||
Eigen::Vector3f(x, 0.f, z)});
|
Vec3fp(static_cast<fp>(x), static_cast<fp>(0.f), static_cast<fp>(z))});
|
||||||
loc->setCharacterTarget(
|
loc->setCharacterTarget(
|
||||||
*loc->player, Eigen::Vector3f(x, y, z), std::move(cbName));
|
*loc->player, Vec3fp(static_cast<fp>(x), static_cast<fp>(y), static_cast<fp>(z)), std::move(cbName));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -424,12 +427,12 @@ namespace FRG {
|
|||||||
api.set_function("get_player_x",
|
api.set_function("get_player_x",
|
||||||
[loc]() -> float {
|
[loc]() -> float {
|
||||||
auto state = loc->player ? loc->player->stateSnapshot() : nullptr;
|
auto state = loc->player ? loc->player->stateSnapshot() : nullptr;
|
||||||
return state ? state->position.x() : 0.0f;
|
return state ? static_cast<float>(state->position.x) : 0.0f;
|
||||||
});
|
});
|
||||||
api.set_function("get_player_z",
|
api.set_function("get_player_z",
|
||||||
[loc]() -> float {
|
[loc]() -> float {
|
||||||
auto state = loc->player ? loc->player->stateSnapshot() : nullptr;
|
auto state = loc->player ? loc->player->stateSnapshot() : nullptr;
|
||||||
return state ? state->position.z() : 0.0f;
|
return state ? static_cast<float>(state->position.z) : 0.0f;
|
||||||
});
|
});
|
||||||
|
|
||||||
api.set_function("refill_current_player_hp",
|
api.set_function("refill_current_player_hp",
|
||||||
@@ -593,7 +596,7 @@ namespace FRG {
|
|||||||
std::cerr << "[script] npc_set_position: unknown NPC id '" << npcId << "'\n";
|
std::cerr << "[script] npc_set_position: unknown NPC id '" << npcId << "'\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Eigen::Vector3f pos(x, y, z);
|
Vec3fp pos(static_cast<fp>(x), static_cast<fp>(y), static_cast<fp>(z));
|
||||||
loc->gameServer().submit(SetCharacterPositionCommand{
|
loc->gameServer().submit(SetCharacterPositionCommand{
|
||||||
loc->locationId(), npc->entityId(), pos, true});
|
loc->locationId(), npc->entityId(), pos, true});
|
||||||
loc->setCharacterTarget(*npc, pos);
|
loc->setCharacterTarget(*npc, pos);
|
||||||
@@ -607,7 +610,8 @@ namespace FRG {
|
|||||||
std::cerr << "[script] npc_set_rotation: unknown NPC id '" << npcId << "'\n";
|
std::cerr << "[script] npc_set_rotation: unknown NPC id '" << npcId << "'\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
const fp rad = static_cast<fp>(angle) * static_cast<fp>(kPi) / static_cast <fp>(180.f);
|
||||||
|
|
||||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
loc->locationId(), npc->entityId(), rad, true});
|
loc->locationId(), npc->entityId(), rad, true});
|
||||||
});
|
});
|
||||||
@@ -626,7 +630,7 @@ namespace FRG {
|
|||||||
|
|
||||||
api.set_function("player_set_position",
|
api.set_function("player_set_position",
|
||||||
[loc](float x, float y, float z) {
|
[loc](float x, float y, float z) {
|
||||||
Eigen::Vector3f pos(x, y, z);
|
Vec3fp pos(static_cast<fp>(x), static_cast<fp>(y), static_cast<fp>(z));
|
||||||
loc->gameServer().submit(SetCharacterPositionCommand{
|
loc->gameServer().submit(SetCharacterPositionCommand{
|
||||||
loc->locationId(), loc->player->entityId(), pos, true});
|
loc->locationId(), loc->player->entityId(), pos, true});
|
||||||
loc->setCharacterTarget(*loc->player, pos);
|
loc->setCharacterTarget(*loc->player, pos);
|
||||||
@@ -634,7 +638,8 @@ namespace FRG {
|
|||||||
|
|
||||||
api.set_function("player_set_rotation",
|
api.set_function("player_set_rotation",
|
||||||
[loc](float angle) {
|
[loc](float angle) {
|
||||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
const fp rad = static_cast<fp>(angle) * static_cast<fp>(kPi) / static_cast <fp>(180.f);
|
||||||
|
|
||||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
loc->locationId(), loc->player->entityId(), rad, true});
|
loc->locationId(), loc->player->entityId(), rad, true});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
namespace FRG {
|
namespace FRG {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -172,7 +173,7 @@ CharacterState NpcData::toCharacterState() const
|
|||||||
CharacterState state;
|
CharacterState state;
|
||||||
state.npcName = name;
|
state.npcName = name;
|
||||||
state.position = {positionX, positionY, positionZ};
|
state.position = {positionX, positionY, positionZ};
|
||||||
state.homePosition = {positionX, 0.f, positionZ};
|
state.homePosition = {positionX, static_cast<fp>(0.f), positionZ};
|
||||||
state.stopInPlace();
|
state.stopInPlace();
|
||||||
state.facingAngle = facingAngle;
|
state.facingAngle = facingAngle;
|
||||||
state.walkSpeed = walkSpeed;
|
state.walkSpeed = walkSpeed;
|
||||||
@@ -183,9 +184,9 @@ CharacterState NpcData::toCharacterState() const
|
|||||||
state.visibleRange = visibleRange;
|
state.visibleRange = visibleRange;
|
||||||
state.canAttack = canAttack;
|
state.canAttack = canAttack;
|
||||||
state.enabled = enabled;
|
state.enabled = enabled;
|
||||||
state.collisionRadius = navigationAvoidanceRadius;
|
//state.collisionRadius = navigationAvoidanceRadius;
|
||||||
state.movementContactRadius =
|
//state.movementContactRadius =
|
||||||
(std::min)(movementContactRadius, navigationAvoidanceRadius);
|
// (std::min)(movementContactRadius, navigationAvoidanceRadius);
|
||||||
state.interactionRadius = interactionRadius;
|
state.interactionRadius = interactionRadius;
|
||||||
state.configureNavigationRetryCooldowns(
|
state.configureNavigationRetryCooldowns(
|
||||||
id, navigationRetryCooldownTicks);
|
id, navigationRetryCooldownTicks);
|
||||||
@@ -309,8 +310,8 @@ LocationDefinition LocationContentLoader::load(
|
|||||||
data.meshTextures[name] = path.get<std::string>();
|
data.meshTextures[name] = path.get<std::string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.interactionRadius =
|
data.interactionRadius = static_cast<fp>(
|
||||||
item.value("interactionRadius", 0.f);
|
item.value<double>("interactionRadius", 0.0));
|
||||||
data.animationIdlePath =
|
data.animationIdlePath =
|
||||||
item.value("animationIdlePath", "");
|
item.value("animationIdlePath", "");
|
||||||
data.animationWalkPath =
|
data.animationWalkPath =
|
||||||
@@ -327,11 +328,11 @@ LocationDefinition LocationContentLoader::load(
|
|||||||
item.value("animationActionToDeathPath", "");
|
item.value("animationActionToDeathPath", "");
|
||||||
data.animationDeathIdlePath =
|
data.animationDeathIdlePath =
|
||||||
item.value("animationDeathIdlePath", "");
|
item.value("animationDeathIdlePath", "");
|
||||||
data.positionX = item.value("positionX", 0.f);
|
data.positionX = static_cast<fp>(item.value<double>("positionX", 0.0));
|
||||||
data.positionY = item.value("positionY", 0.f);
|
data.positionY = static_cast<fp>(item.value<double>("positionY", 0.0));
|
||||||
data.positionZ = item.value("positionZ", 0.f);
|
data.positionZ = static_cast<fp>(item.value<double>("positionZ", 0.0));
|
||||||
data.walkSpeed = item.value("walkSpeed", 1.5f);
|
data.walkSpeed = static_cast<fp>(item.value<double>("walkSpeed", 1.5));
|
||||||
data.rotationSpeed = item.value("rotationSpeed", 8.f);
|
data.rotationSpeed = static_cast<fp>(item.value<double>("rotationSpeed", 8.0));
|
||||||
data.modelScale = item.value("modelScale", 0.01f);
|
data.modelScale = item.value("modelScale", 0.01f);
|
||||||
const float degrees = kPi / 180.f;
|
const float degrees = kPi / 180.f;
|
||||||
data.modelCorrectionRotX =
|
data.modelCorrectionRotX =
|
||||||
@@ -341,12 +342,13 @@ LocationDefinition LocationContentLoader::load(
|
|||||||
data.modelCorrectionRotZ =
|
data.modelCorrectionRotZ =
|
||||||
item.value("modelCorrectionRotZ", 0.f) * degrees;
|
item.value("modelCorrectionRotZ", 0.f) * degrees;
|
||||||
data.facingAngle =
|
data.facingAngle =
|
||||||
item.value("facingAngle", 0.f) * degrees;
|
static_cast<fp>(item.value<double>("facingAngle", 0.0)) * static_cast<fp>(kPi) / static_cast<fp>(180.0);
|
||||||
data.hp = item.value("hp", 100.f);
|
data.hp = item.value("hp", 100.f);
|
||||||
data.visibleRange =
|
data.visibleRange =
|
||||||
item.value("visibleRange", VISIBLE_RANGE);
|
static_cast<fp>(item.value<double>("visibleRange", static_cast<double>(VISIBLE_RANGE)));
|
||||||
data.canAttack = item.value("canAttack", false);
|
data.canAttack = item.value("canAttack", false);
|
||||||
data.enabled = item.value("enabled", true);
|
data.enabled = item.value("enabled", true);
|
||||||
|
/*
|
||||||
data.navigationAvoidanceRadius = item.value(
|
data.navigationAvoidanceRadius = item.value(
|
||||||
"navigationAvoidanceRadius",
|
"navigationAvoidanceRadius",
|
||||||
item.value("collisionRadius", 0.45f));
|
item.value("collisionRadius", 0.45f));
|
||||||
@@ -359,6 +361,7 @@ LocationDefinition LocationContentLoader::load(
|
|||||||
0.05f, (std::min)(
|
0.05f, (std::min)(
|
||||||
data.movementContactRadius,
|
data.movementContactRadius,
|
||||||
data.navigationAvoidanceRadius));
|
data.navigationAvoidanceRadius));
|
||||||
|
*/
|
||||||
const char* cooldownKey =
|
const char* cooldownKey =
|
||||||
item.contains("navigationRetryCooldownTicks")
|
item.contains("navigationRetryCooldownTicks")
|
||||||
? "navigationRetryCooldownTicks"
|
? "navigationRetryCooldownTicks"
|
||||||
|
|||||||
@@ -58,23 +58,23 @@ struct NpcData {
|
|||||||
std::string animationActionToStandPath;
|
std::string animationActionToStandPath;
|
||||||
std::string animationActionToDeathPath;
|
std::string animationActionToDeathPath;
|
||||||
std::string animationDeathIdlePath;
|
std::string animationDeathIdlePath;
|
||||||
float positionX = 0.f;
|
fp positionX = static_cast<fp>(0.f);
|
||||||
float positionY = 0.f;
|
fp positionY = static_cast<fp>(0.f);
|
||||||
float positionZ = 0.f;
|
fp positionZ = static_cast<fp>(0.f);
|
||||||
float walkSpeed = 1.5f;
|
fp walkSpeed = static_cast<fp>(1.5f);
|
||||||
float rotationSpeed = 8.f;
|
fp rotationSpeed = static_cast<fp>(8.f);
|
||||||
float modelScale = 0.01f;
|
float modelScale = 0.01f;
|
||||||
float modelCorrectionRotX = 0.f;
|
float modelCorrectionRotX = 0.f;
|
||||||
float modelCorrectionRotY = 0.f;
|
float modelCorrectionRotY = 0.f;
|
||||||
float modelCorrectionRotZ = 0.f;
|
float modelCorrectionRotZ = 0.f;
|
||||||
float facingAngle = 0.f;
|
fp facingAngle = static_cast<fp>(0.f);
|
||||||
float hp = 100.f;
|
float hp = 100.f;
|
||||||
float visibleRange = VISIBLE_RANGE;
|
fp visibleRange = static_cast<fp>(VISIBLE_RANGE);
|
||||||
bool canAttack = false;
|
bool canAttack = false;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
float navigationAvoidanceRadius = 0.45f;
|
//fp navigationAvoidanceRadius = static_cast<fp>(0.45f);
|
||||||
float movementContactRadius = 0.32f;
|
//fp movementContactRadius = static_cast<fp>(0.32f);
|
||||||
float interactionRadius = 0.f;
|
fp interactionRadius = static_cast<fp>(0.f);
|
||||||
std::vector<int> navigationRetryCooldownTicks;
|
std::vector<int> navigationRetryCooldownTicks;
|
||||||
|
|
||||||
CharacterState toCharacterState() const;
|
CharacterState toCharacterState() const;
|
||||||
|
|||||||
@@ -137,19 +137,19 @@ struct SetGlobalFloatCommand { std::string key; float value; };
|
|||||||
struct SetCharacterPositionCommand {
|
struct SetCharacterPositionCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
Eigen::Vector3f position;
|
Vec3fp position;
|
||||||
bool updateHomePosition = false;
|
bool updateHomePosition = false;
|
||||||
};
|
};
|
||||||
struct SetCharacterFacingAngleCommand {
|
struct SetCharacterFacingAngleCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
float angle;
|
fp angle;
|
||||||
bool setCurrentAngle = false;
|
bool setCurrentAngle = false;
|
||||||
};
|
};
|
||||||
struct SetCharacterHomePositionCommand {
|
struct SetCharacterHomePositionCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
Eigen::Vector3f position;
|
Vec3fp position;
|
||||||
};
|
};
|
||||||
struct SetCharacterTargetCommand {
|
struct SetCharacterTargetCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
@@ -173,7 +173,7 @@ struct ClearCharacterAttackTargetCommand {
|
|||||||
struct SetCharacterDestinationCommand {
|
struct SetCharacterDestinationCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
Eigen::Vector3f target;
|
Vec3fp target;
|
||||||
std::string callbackName;
|
std::string callbackName;
|
||||||
};
|
};
|
||||||
struct StopCharacterCommand {
|
struct StopCharacterCommand {
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ void InternalGameServer::updateBattleZoneTestBot(std::int64_t deltaMs)
|
|||||||
battleZoneTestBotElapsedMs_ %= kBattleZoneTestBotIntervalMs;
|
battleZoneTestBotElapsedMs_ %= kBattleZoneTestBotIntervalMs;
|
||||||
|
|
||||||
static std::mt19937 randomEngine(std::random_device{}());
|
static std::mt19937 randomEngine(std::random_device{}());
|
||||||
static std::uniform_real_distribution<float> coordinate(-10.0f, 10.0f);
|
static std::uniform_int_distribution<int> coordinate(-100, 100); //Divide by 10 then.
|
||||||
submit(SetCharacterDestinationCommand{
|
submit(SetCharacterDestinationCommand{
|
||||||
state_.currentLocationName,
|
state_.currentLocationName,
|
||||||
kBattleZonePlayer2Id,
|
kBattleZonePlayer2Id,
|
||||||
Eigen::Vector3f(coordinate(randomEngine), 0.f, coordinate(randomEngine)),
|
Vec3fp(static_cast<fp>(coordinate(randomEngine)) / static_cast<fp>(10), static_cast<fp>(0.f), coordinate(randomEngine) / static_cast<fp>(10)),
|
||||||
std::string()});
|
std::string()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,44 @@ bool readTarget(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool readTargetFp(
|
||||||
|
const nlohmann::json& value, FRG::Vec3fp& target, std::string& error)
|
||||||
|
{
|
||||||
|
if (!value.is_array() || value.size() != 3) {
|
||||||
|
error = "target must contain three coordinates";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FRG::fp coords[3];
|
||||||
|
for (std::size_t index = 0; index < 3; ++index) {
|
||||||
|
if (!value[index].is_number()) {
|
||||||
|
error = "target coordinates must be numbers";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const double coordinate = value[index].get<double>();
|
||||||
|
if (!std::isfinite(coordinate) || std::abs(coordinate) > 100000.0) {
|
||||||
|
error = "target coordinate is not finite or is out of range";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Округляем до сотых в double и конвертируем в Q20.12 (fp)
|
||||||
|
coords[index] = FRG::fp(coordinate);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.x = coords[0];
|
||||||
|
target.y = coords[1];
|
||||||
|
target.z = coords[2];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json targetJsonFp(const FRG::Vec3fp& target)
|
||||||
|
{
|
||||||
|
return nlohmann::json::array({
|
||||||
|
static_cast<double>(target.x),
|
||||||
|
static_cast<double>(target.y),
|
||||||
|
static_cast<double>(target.z)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json targetJson(const Eigen::Vector3f& target)
|
nlohmann::json targetJson(const Eigen::Vector3f& target)
|
||||||
{
|
{
|
||||||
return nlohmann::json::array({target.x(), target.y(), target.z()});
|
return nlohmann::json::array({target.x(), target.y(), target.z()});
|
||||||
@@ -161,7 +199,7 @@ bool parseInitialStateMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t LockstepTimeline::schedule(
|
std::uint64_t LockstepTimeline::schedule(
|
||||||
const std::string& playerId, const Eigen::Vector3f& target)
|
const std::string& playerId, const Vec3fp& target)
|
||||||
{
|
{
|
||||||
const std::uint64_t sequence = nextCommandSequence_++;
|
const std::uint64_t sequence = nextCommandSequence_++;
|
||||||
pending_[currentTick_ + inputDelayTicks_].push_back(
|
pending_[currentTick_ + inputDelayTicks_].push_back(
|
||||||
@@ -206,7 +244,7 @@ std::string makeDestinationInputMessage(const DestinationInputMessage& message)
|
|||||||
{
|
{
|
||||||
return envelope("destination_input", {
|
return envelope("destination_input", {
|
||||||
{"clientSequence", message.clientSequence},
|
{"clientSequence", message.clientSequence},
|
||||||
{"target", targetJson(message.target)}
|
{"target", targetJsonFp(message.target)}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +257,7 @@ bool parseDestinationInputMessage(
|
|||||||
return false;
|
return false;
|
||||||
if (!payload.contains("target"))
|
if (!payload.contains("target"))
|
||||||
return error = "target is required", false;
|
return error = "target is required", false;
|
||||||
return readTarget(payload["target"], message.target, error);
|
return readTargetFp(payload["target"], message.target, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string makeCharacterActionInputMessage(
|
std::string makeCharacterActionInputMessage(
|
||||||
@@ -265,7 +303,7 @@ std::string makeTickMessage(const TickMessage& message)
|
|||||||
{"type", "destination"},
|
{"type", "destination"},
|
||||||
{"commandSequence", command.commandSequence},
|
{"commandSequence", command.commandSequence},
|
||||||
{"playerId", command.playerId},
|
{"playerId", command.playerId},
|
||||||
{"target", targetJson(command.target)}
|
{"target", targetJsonFp(command.target)}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if constexpr (std::is_same_v<T, ScheduledCharacterEnabledCommand>) {
|
if constexpr (std::is_same_v<T, ScheduledCharacterEnabledCommand>) {
|
||||||
@@ -320,7 +358,7 @@ bool parseTickMessage(
|
|||||||
destination.commandSequence = previousSequence;
|
destination.commandSequence = previousSequence;
|
||||||
if (!value.contains("playerId") || !value["playerId"].is_string() ||
|
if (!value.contains("playerId") || !value["playerId"].is_string() ||
|
||||||
!value.contains("target") ||
|
!value.contains("target") ||
|
||||||
!readTarget(value["target"], destination.target, error)) {
|
!readTargetFp(value["target"], destination.target, error)) {
|
||||||
if (error.empty()) error = "invalid destination command";
|
if (error.empty()) error = "invalid destination command";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "../navigation/PathFinder.h"
|
||||||
|
|
||||||
namespace FRG {
|
namespace FRG {
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ struct InitialStateMessage {
|
|||||||
|
|
||||||
struct DestinationInputMessage {
|
struct DestinationInputMessage {
|
||||||
std::uint64_t clientSequence = 0;
|
std::uint64_t clientSequence = 0;
|
||||||
Eigen::Vector3f target = Eigen::Vector3f::Zero();
|
Vec3fp target;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterActionInputMessage {
|
struct CharacterActionInputMessage {
|
||||||
@@ -37,7 +38,7 @@ struct CharacterActionInputMessage {
|
|||||||
struct ScheduledDestinationCommand {
|
struct ScheduledDestinationCommand {
|
||||||
std::uint64_t commandSequence = 0;
|
std::uint64_t commandSequence = 0;
|
||||||
std::string playerId;
|
std::string playerId;
|
||||||
Eigen::Vector3f target = Eigen::Vector3f::Zero();
|
Vec3fp target;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScheduledCharacterEnabledCommand {
|
struct ScheduledCharacterEnabledCommand {
|
||||||
@@ -73,7 +74,7 @@ public:
|
|||||||
|
|
||||||
std::uint64_t currentTick() const { return currentTick_; }
|
std::uint64_t currentTick() const { return currentTick_; }
|
||||||
std::uint64_t schedule(
|
std::uint64_t schedule(
|
||||||
const std::string& playerId, const Eigen::Vector3f& target);
|
const std::string& playerId, const Vec3fp& target);
|
||||||
std::uint64_t scheduleCharacterEnabled(
|
std::uint64_t scheduleCharacterEnabled(
|
||||||
const std::string& locationId, const std::string& characterId,
|
const std::string& locationId, const std::string& characterId,
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
|
|
||||||
namespace FRG {
|
namespace FRG {
|
||||||
|
|
||||||
|
constexpr float kPi = 3.14159265358979323846f;
|
||||||
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@@ -341,7 +344,7 @@ namespace FRG {
|
|||||||
data.meshTextures[it.key()] = it.value().get<std::string>();
|
data.meshTextures[it.key()] = it.value().get<std::string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.interactionRadius = item.value("interactionRadius", 0.0f);
|
data.interactionRadius = static_cast<fp>(item.value<double>("interactionRadius", 0.0));
|
||||||
data.animationIdlePath = item.value("animationIdlePath", "");
|
data.animationIdlePath = item.value("animationIdlePath", "");
|
||||||
data.animationWalkPath = item.value("animationWalkPath", "");
|
data.animationWalkPath = item.value("animationWalkPath", "");
|
||||||
data.animationActionIdlePath = item.value("animationActionIdlePath", "");
|
data.animationActionIdlePath = item.value("animationActionIdlePath", "");
|
||||||
@@ -350,18 +353,18 @@ namespace FRG {
|
|||||||
data.animationActionToStandPath = item.value("animationActionToStandPath", "");
|
data.animationActionToStandPath = item.value("animationActionToStandPath", "");
|
||||||
data.animationActionToDeathPath = item.value("animationActionToDeathPath", "");
|
data.animationActionToDeathPath = item.value("animationActionToDeathPath", "");
|
||||||
data.animationDeathIdlePath = item.value("animationDeathIdlePath", "");
|
data.animationDeathIdlePath = item.value("animationDeathIdlePath", "");
|
||||||
data.positionX = item.value("positionX", 0.0f);
|
data.positionX = static_cast<fp>(item.value<double>("positionX", 0.0));
|
||||||
data.positionY = item.value("positionY", 0.0f);
|
data.positionY = static_cast<fp>(item.value<double>("positionY", 0.0));
|
||||||
data.positionZ = item.value("positionZ", 0.0f);
|
data.positionZ = static_cast<fp>(item.value<double>("positionZ", 0.0));
|
||||||
data.walkSpeed = item.value("walkSpeed", 1.5f);
|
data.walkSpeed = static_cast<fp>(item.value<double>("walkSpeed", 1.5));
|
||||||
data.rotationSpeed = item.value("rotationSpeed", 8.0f);
|
data.rotationSpeed = static_cast<fp>(item.value<double>("rotationSpeed", 8.0));
|
||||||
data.modelScale = item.value("modelScale", 0.01f);
|
data.modelScale = item.value("modelScale", 0.01f);
|
||||||
data.modelCorrectionRotX = item.value("modelCorrectionRotX", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.modelCorrectionRotX = item.value("modelCorrectionRotX", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||||
data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||||
data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||||
data.facingAngle = item.value("facingAngle", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
data.facingAngle = static_cast<fp>(item.value("facingAngle", 0.0f)) * static_cast<fp>(kPi) / static_cast<fp>(180.0f);
|
||||||
data.hp = item.value("hp", 100.0f);
|
data.hp = item.value("hp", 100.0f);
|
||||||
data.visibleRange = item.value("visibleRange", VISIBLE_RANGE);
|
data.visibleRange = static_cast<fp>(item.value<double>("visibleRange", static_cast<double>(VISIBLE_RANGE)));
|
||||||
data.canAttack = item.value("canAttack", false);
|
data.canAttack = item.value("canAttack", false);
|
||||||
data.enabled = item.value("enabled", true);
|
data.enabled = item.value("enabled", true);
|
||||||
|
|
||||||
@@ -401,7 +404,7 @@ namespace FRG {
|
|||||||
renderer, zipPath.c_str());
|
renderer, zipPath.c_str());
|
||||||
if (npc) {
|
if (npc) {
|
||||||
logger() << "Successfully loaded NPC: " << npcData.name << " at ("
|
logger() << "Successfully loaded NPC: " << npcData.name << " at ("
|
||||||
<< npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl;
|
<< static_cast<float>(npcData.positionX) << ", " << static_cast<float>(npcData.positionY) << ", " << static_cast<float>(npcData.positionZ) << ")" << std::endl;
|
||||||
npcs.push_back(std::move(npc));
|
npcs.push_back(std::move(npc));
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
|||||||
@@ -12,8 +12,27 @@
|
|||||||
namespace FRG {
|
namespace FRG {
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
Vec3fp operator-(Vec3fp a, Vec3fp b)
|
||||||
|
{
|
||||||
|
return Vec3fp{
|
||||||
|
a.x - b.x,
|
||||||
|
a.y - b.y,
|
||||||
|
a.z - b.z
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Vec3fp operator+(Vec3fp a, Vec3fp b)
|
||||||
|
{
|
||||||
|
return Vec3fp{
|
||||||
|
a.x + b.x,
|
||||||
|
a.y + b.y,
|
||||||
|
a.z + b.z
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<Eigen::Vector2f> readPolygon(const json& item)
|
std::vector<Eigen::Vector2f> readPolygon(const json& item)
|
||||||
{
|
{
|
||||||
std::vector<Eigen::Vector2f> polygon;
|
std::vector<Eigen::Vector2f> polygon;
|
||||||
|
|||||||
@@ -80,8 +80,37 @@ namespace FRG {
|
|||||||
Eigen::Vector3f toEigen() const {
|
Eigen::Vector3f toEigen() const {
|
||||||
return Eigen::Vector3f(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
|
return Eigen::Vector3f(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fp normSquared() const
|
||||||
|
{
|
||||||
|
return x * x + y * y + z * z;
|
||||||
|
}
|
||||||
|
Vec3fp operator/(fp value) const
|
||||||
|
{
|
||||||
|
if (value == static_cast<fp>(0.0))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Vec3fp error: division by zero");
|
||||||
|
return Vec3fp();
|
||||||
|
}
|
||||||
|
Vec3fp r;
|
||||||
|
r.x = x / value;
|
||||||
|
r.y = y / value;
|
||||||
|
r.z = z / value;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
Vec3fp operator*(const fp value) const
|
||||||
|
{
|
||||||
|
Vec3fp r;
|
||||||
|
r.x = x * value;
|
||||||
|
r.y = y * value;
|
||||||
|
r.z = z * value;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Vec3fp operator-(Vec3fp a, Vec3fp b);
|
||||||
|
Vec3fp operator+(Vec3fp a, Vec3fp b);
|
||||||
|
|
||||||
inline std::vector<Eigen::Vector2f> toEigenVector(const std::vector<FRG::Vec2fp>& src) {
|
inline std::vector<Eigen::Vector2f> toEigenVector(const std::vector<FRG::Vec2fp>& src) {
|
||||||
std::vector<Eigen::Vector2f> dst;
|
std::vector<Eigen::Vector2f> dst;
|
||||||
dst.reserve(src.size());
|
dst.reserve(src.size());
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ namespace FRG {
|
|||||||
const size_t firstWaypoint = (std::min)(
|
const size_t firstWaypoint = (std::min)(
|
||||||
state->currentWaypointIndex, state->pathWaypoints.size());
|
state->currentWaypointIndex, state->pathWaypoints.size());
|
||||||
for (size_t i = firstWaypoint; i < state->pathWaypoints.size(); ++i) {
|
for (size_t i = firstWaypoint; i < state->pathWaypoints.size(); ++i) {
|
||||||
const Eigen::Vector3f& waypoint = state->pathWaypoints[i];
|
const Eigen::Vector3f& waypoint = state->pathWaypoints[i].toEigen();
|
||||||
route.push_back(Eigen::Vector3f(waypoint.x(), 0.0f, waypoint.z()));
|
route.push_back(Eigen::Vector3f(waypoint.x(), 0.0f, waypoint.z()));
|
||||||
}
|
}
|
||||||
const Eigen::Vector3f clickedPosition(
|
const Eigen::Vector3f clickedPosition(
|
||||||
@@ -140,7 +140,7 @@ namespace FRG {
|
|||||||
constexpr float kPlayerClearanceMs = 20.0f;
|
constexpr float kPlayerClearanceMs = 20.0f;
|
||||||
constexpr float kPlayerMaximumSpeedMultiplier = 1.5f;
|
constexpr float kPlayerMaximumSpeedMultiplier = 1.5f;
|
||||||
const float playerSpeed = (std::max)(
|
const float playerSpeed = (std::max)(
|
||||||
state->walkSpeed * kPlayerMaximumSpeedMultiplier, 0.01f);
|
static_cast<float>(state->walkSpeed) * kPlayerMaximumSpeedMultiplier, 0.01f);
|
||||||
const float playerRouteTimeMs = totalLength / playerSpeed * 1000.0f;
|
const float playerRouteTimeMs = totalLength / playerSpeed * 1000.0f;
|
||||||
const float revealDurationMs = (std::min)(kMaximumRevealMs, playerRouteTimeMs / 3.0f);
|
const float revealDurationMs = (std::min)(kMaximumRevealMs, playerRouteTimeMs / 3.0f);
|
||||||
const float waveSpeed = totalLength / (revealDurationMs / 1000.0f);
|
const float waveSpeed = totalLength / (revealDurationMs / 1000.0f);
|
||||||
|
|||||||
Reference in new issue
Block a user