Restore back dialogs and interaction with objects
This commit is contained in:
1 parent
0fcf8c4fa5
commit
f90324e2b7
7 files changed
+86
-57
No files matched your search
+4
-4
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user