Restore back dialogs and interaction with objects

This commit is contained in:
Vladislav Khorev committed 2026-11-14 18:11:41 +06:00
1 parent 0fcf8c4fa5
commit f90324e2b7
7 files changed
+86 -57

No files matched your search

+4 -4
View File
@@ -2067,11 +2067,11 @@ namespace FRG
gameServer_.submit(SetLocationTargetsCommand{
locationId_, clickedObject->entityId(), std::string(), std::string()});
const Eigen::Vector3f interactionTarget = clickedObject->state.hasInteractionPosition
const Vec3fp interactionTarget = clickedObject->state.hasInteractionPosition
? clickedObject->state.interactionPosition
: clickedObject->state.position;
setCharacterTarget(*player, Vec3fp::fromEigen(interactionTarget));
pathVisualization.start(*player, interactionTarget);
: Vec3fp::fromEigen(clickedObject->state.position);
setCharacterTarget(*player, interactionTarget);
pathVisualization.start(*player, interactionTarget.toEigen());
clearCharacterAttackTarget(*player);
//logger() << "[CLICK] Player moving to object..." << std::endl;
}
+6 -9
View File
@@ -278,7 +278,7 @@ namespace FRG
// --- interaction position pole ---
if (obj.state.hasInteractionPosition) {
const Eigen::Vector3f& p = obj.state.interactionPosition;
Eigen::Vector3f p = obj.state.interactionPosition.toEigen();
static constexpr float hw = 0.05f; // half width/depth
static constexpr float hh = 1.5f; // half height (pole = 3.0 tall)
@@ -340,7 +340,7 @@ namespace FRG
if (ctrlHeld) {
// Set the interaction position for the selected object (world XZ, Y=0).
auto& target = loc.interactiveObjects[selectedInteractiveObjectIndex];
target.state.interactionPosition = Eigen::Vector3f(worldHit.x(), 0.f, worldHit.z());
target.state.interactionPosition = Vec3fp(static_cast<fp>(worldHit.x()), static_cast<fp>(0.f), static_cast<fp>(worldHit.z()));
target.state.hasInteractionPosition = true;
boundsClickCount = 0; // cancel any in-progress bounds placement
buildInteractiveObjectBoundsMeshes();
@@ -417,12 +417,9 @@ namespace FRG
item["rotationX"] = obj.loadedObject.creationInfo.meshRotationX;
item["rotationY"] = obj.loadedObject.creationInfo.meshRotationY;
item["rotationZ"] = obj.loadedObject.creationInfo.meshRotationZ;
item["positionX"] = obj.state.jsonPositionX;
item["positionY"] = obj.state.jsonPositionY;
item["positionZ"] = obj.state.jsonPositionZ;
item["scale"] = obj.loadedObject.creationInfo.meshScale;
item["interactionRadius"] = obj.state.interactionRadius;
item["approachRadius"] = obj.state.approachRadius;
item["approachRadius"] = static_cast<double>(obj.state.approachRadius);
if (!obj.state.activateFunctionName.empty())
item["activateFunction"] = obj.state.activateFunctionName;
item["pivotX"] = obj.state.pivot.x();
@@ -435,9 +432,9 @@ namespace FRG
item["boundsMaxY"] = obj.state.boundsMax.y();
item["boundsMaxZ"] = obj.state.boundsMax.z();
if (obj.state.hasInteractionPosition) {
item["interactionPositionX"] = obj.state.interactionPosition.x();
item["interactionPositionY"] = obj.state.interactionPosition.y();
item["interactionPositionZ"] = obj.state.interactionPosition.z();
item["interactionPositionX"] = static_cast<double>(obj.state.interactionPosition.x);
item["interactionPositionY"] = static_cast<double>(obj.state.interactionPosition.y);
item["interactionPositionZ"] = static_cast<double>(obj.state.interactionPosition.z);
}
j["objects"].push_back(item);
}
+39
View File
@@ -9,6 +9,7 @@ namespace FRG {
namespace {
constexpr float PI = 3.14159265358979323846f;
constexpr float NPC_TALK_DISTANCE = 1.35f;
constexpr fp NPC_TALK_DISTANCE_FP_SQUARED() { return fp(NPC_TALK_DISTANCE * NPC_TALK_DISTANCE); };
}
bool LocationGameState::setup(
@@ -496,6 +497,44 @@ LocationSimulationEvents LocationGameState::update(
source.events.attackDirection });
}
//Main player can interact with interactive objects
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) {
Vec3fp approachTarget = object->hasInteractionPosition
? object->interactionPosition : Vec3fp::fromEigen(object->position);
if ((player->second->position - approachTarget).normSquared() <= object->approachRadius* object->approachRadius) {
result.interactiveObjects.push_back(
{ location->targetInteractiveObjectId, {}, true,
object->activateFunctionName });
location->targetInteractiveObjectId.clear();
}
}
}
//Main player can also talk to other NPCs
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 fp distanceSquared =
(player->second->position - npc->position).normSquared();
if (distanceSquared <= NPC_TALK_DISTANCE_FP_SQUARED()) {
player->second->stopInPlace();
npc->faceTargetId = "player1";
result.npcInteractionId = location->targetInteractNpcId;
location->targetInteractNpcId.clear();
}
}
return result;
}
+26 -30
View File
@@ -51,7 +51,7 @@ GameObjectData parseGameObject(const json& item)
return data;
}
Eigen::Vector3f vector(
Eigen::Vector3f toVectorJson(
const json& item, const char* x, const char* y, const char* z,
const Eigen::Vector3f& fallback = Eigen::Vector3f::Zero())
{
@@ -59,6 +59,17 @@ Eigen::Vector3f vector(
item.value(z, fallback.z())};
}
Vec3fp toVec3fp(
const json& item, const char* x, const char* y, const char* z)
{
return {
static_cast<fp>(item.value<double>(x, 0.0)),
static_cast<fp>(item.value<double>(y, 0.0)),
static_cast<fp>(item.value<double>(z, 0.0))
};
}
std::pair<int, int> readBinaryAnimationMetadata(
const std::string& path, const std::string& zipFile)
{
@@ -160,9 +171,7 @@ InteractiveObjectState InteractiveObjectData::toInteractiveObjectState() const
state.boundsMax = boundsMax;
state.hasInteractionPosition = hasInteractionPosition;
state.interactionPosition = interactionPosition;
state.jsonPositionX = base.positionX;
state.jsonPositionY = base.positionY;
state.jsonPositionZ = base.positionZ;
state.position =
Eigen::Vector3f(base.positionX, base.positionY, base.positionZ);
return state;
@@ -264,20 +273,20 @@ LocationDefinition LocationContentLoader::load(
"Missing or duplicate interactive-object id in " +
config.interactiveObjectsJsonPath);
data.base = parseGameObject(item);
data.interactionRadius = item.value("interactionRadius", 2.f);
data.interactionRadius = item.value<double>("interactionRadius", 2.0);
data.approachRadius =
item.value("approachRadius", data.interactionRadius);
static_cast<fp>(item.value<double>("approachRadius", data.interactionRadius));
data.activateFunctionName =
item.value("activateFunction", "");
data.pivot = vector(item, "pivotX", "pivotY", "pivotZ");
data.pivot = toVectorJson(item, "pivotX", "pivotY", "pivotZ");
data.boundsMin =
vector(item, "boundsMinX", "boundsMinY", "boundsMinZ");
toVectorJson(item, "boundsMinX", "boundsMinY", "boundsMinZ");
data.boundsMax =
vector(item, "boundsMaxX", "boundsMaxY", "boundsMaxZ");
toVectorJson(item, "boundsMaxX", "boundsMaxY", "boundsMaxZ");
data.hasInteractionPosition =
item.contains("interactionPositionX");
if (data.hasInteractionPosition) {
data.interactionPosition = vector(
data.interactionPosition = toVec3fp(
item, "interactionPositionX", "interactionPositionY",
"interactionPositionZ");
}
@@ -348,20 +357,7 @@ LocationDefinition LocationContentLoader::load(
static_cast<fp>(item.value<double>("visibleRange", static_cast<double>(VISIBLE_RANGE)));
data.canAttack = item.value("canAttack", false);
data.enabled = item.value("enabled", true);
/*
data.navigationAvoidanceRadius = item.value(
"navigationAvoidanceRadius",
item.value("collisionRadius", 0.45f));
data.navigationAvoidanceRadius =
(std::max)(0.05f, data.navigationAvoidanceRadius);
data.movementContactRadius = item.value(
"movementContactRadius",
data.navigationAvoidanceRadius * 0.72f);
data.movementContactRadius = (std::max)(
0.05f, (std::min)(
data.movementContactRadius,
data.navigationAvoidanceRadius));
*/
const char* cooldownKey =
item.contains("navigationRetryCooldownTicks")
? "navigationRetryCooldownTicks"
@@ -394,14 +390,14 @@ LocationDefinition LocationContentLoader::load(
config.teleportsJsonPath);
data.active = item.value("active", false);
data.position =
vector(item, "positionX", "positionY", "positionZ");
toVectorJson(item, "positionX", "positionY", "positionZ");
data.radius = item.value("radius", 0.f);
data.automatic = item.value("automatic", false);
data.automaticRadius =
item.value("automaticRadius", data.radius);
data.destinationLocation =
item.value("destinationLocation", "");
data.destinationPosition = vector(
data.destinationPosition = toVectorJson(
item, "destinationPositionX", "destinationPositionY",
"destinationPositionZ");
data.destinationRotationY =
@@ -425,7 +421,7 @@ LocationDefinition LocationContentLoader::load(
data.radius = item.value("radius", 1.5f);
data.hysteresis = item.value("hysteresis", 0.3f);
data.position =
vector(item, "positionX", "positionY", "positionZ");
toVectorJson(item, "positionX", "positionY", "positionZ");
result.triggerZones.push_back(std::move(data));
}
}
@@ -436,11 +432,11 @@ LocationDefinition LocationContentLoader::load(
PointLightDefinition data;
data.id = item.value("id", "");
data.position =
vector(item, "positionX", "positionY", "positionZ");
data.direction = vector(
toVectorJson(item, "positionX", "positionY", "positionZ");
data.direction = toVectorJson(
item, "directionX", "directionY", "directionZ",
Eigen::Vector3f(0.f, -1.f, 0.f));
data.color = vector(
data.color = toVectorJson(
item, "colorR", "colorG", "colorB",
Eigen::Vector3f::Ones());
data.autoLight = item.value("autoLight", false);
+3 -3
View File
@@ -30,14 +30,14 @@ struct GameObjectData {
struct InteractiveObjectData {
std::string id;
GameObjectData base;
float interactionRadius = 2.f;
float approachRadius = 2.f;
float interactionRadius = 2.f; //This is only for detecting mouse/touch event
fp approachRadius = static_cast<fp>(2.f);
std::string activateFunctionName;
Eigen::Vector3f pivot = Eigen::Vector3f::Zero();
Eigen::Vector3f boundsMin = Eigen::Vector3f::Zero();
Eigen::Vector3f boundsMax = Eigen::Vector3f::Zero();
bool hasInteractionPosition = false;
Eigen::Vector3f interactionPosition = Eigen::Vector3f::Zero();
Vec3fp interactionPosition;
bool castShadow = true;
bool castShadowNight = true;
bool isActive = true;
+4 -4
View File
@@ -100,7 +100,7 @@ namespace FRG {
throw std::runtime_error("Interactive object id must not be empty in " + jsonPath);
data.base = parseGameObjectData(item);
data.interactionRadius = item.value("interactionRadius", 2.0f);
data.approachRadius = item.value("approachRadius", data.interactionRadius);
data.approachRadius = static_cast<fp>(item.value<double>("approachRadius", data.interactionRadius));
data.castShadow = item.value("castShadow", true);
data.castShadowNight = item.value("castShadowNight", true);
data.isActive = item.value("isActive", true);
@@ -117,9 +117,9 @@ namespace FRG {
if (item.contains("interactionPositionX")) {
data.hasInteractionPosition = true;
data.interactionPosition = {
item.value("interactionPositionX", 0.0f),
item.value("interactionPositionY", 0.0f),
item.value("interactionPositionZ", 0.0f)};
static_cast<fp>(item.value<double>("interactionPositionX", 0.0)),
static_cast<fp>(item.value<double>("interactionPositionY", 0.0)),
static_cast<fp>(item.value<double>("interactionPositionZ", 0.0))};
}
if (!data.base.meshPath.empty())
objects.push_back(std::move(data));
+4 -7
View File
@@ -6,6 +6,7 @@
#include <Eigen/Geometry>
#include "external/nlohmann/json.hpp"
#include "ISaveable.h"
#include "../navigation/PathFinder.h"
namespace FRG {
@@ -47,20 +48,16 @@ class InteractiveObjectState : public ISaveable {
public:
// --- Creation info (fixed at load time) ---
LoadedGameObjectState objectInfo;
float interactionRadius = 2.f;
float approachRadius = 2.f;
float interactionRadius = 2.f; //This is only for detecting mouse/touch event
fp approachRadius = static_cast<fp>(2.f);
Eigen::Vector3f pivot = Eigen::Vector3f::Zero();
Eigen::Vector3f boundsMin = Eigen::Vector3f::Zero();
Eigen::Vector3f boundsMax = Eigen::Vector3f::Zero();
bool hasInteractionPosition = false;
Eigen::Vector3f interactionPosition = Eigen::Vector3f::Zero();
Vec3fp interactionPosition;
bool castShadow = true;
bool castShadowNight = true;
std::string activateFunctionName;
// Original JSON position before mesh-centering offset — needed for re-serialization.
float jsonPositionX = 0.f;
float jsonPositionY = 0.f;
float jsonPositionZ = 0.f;
// --- Runtime mutable state ---
Eigen::Vector3f position = Eigen::Vector3f::Zero();