Remove broad command
This commit is contained in:
1 parent
1ab9f300b6
commit
3f68a8ba07
11 files changed
+120
-160
No files matched your search
@@ -140,7 +140,9 @@ Snapshots have a monotonically increasing revision. `InternalGameServer` caches
|
|||||||
- Script globals.
|
- Script globals.
|
||||||
- Character transforms, attack targets, navigation destinations, stopping,
|
- Character transforms, attack targets, navigation destinations, stopping,
|
||||||
facing, replanning, health, damage, death reset, attack combos, and animation
|
facing, replanning, health, damage, death reset, attack combos, and animation
|
||||||
metadata.
|
metadata. Character position, facing angle, home position, face target, and
|
||||||
|
enabled state use separate semantic commands; no whole-character commit
|
||||||
|
command is exposed.
|
||||||
- Interactive-object activation.
|
- Interactive-object activation.
|
||||||
- Trigger-zone state.
|
- Trigger-zone state.
|
||||||
- Quest-definition reloads.
|
- Quest-definition reloads.
|
||||||
|
|||||||
@@ -144,24 +144,6 @@ CharacterUpdateEvents CharacterState::update(
|
|||||||
CharacterUpdateEvents events;
|
CharacterUpdateEvents events;
|
||||||
if (!enabled) return events;
|
if (!enabled) return events;
|
||||||
|
|
||||||
if (slowMoTimeRemaining > 0.0f) {
|
|
||||||
slowMoTimeRemaining = (std::max)(
|
|
||||||
0.0f, slowMoTimeRemaining - static_cast<float>(deltaMs) / 1000.0f);
|
|
||||||
|
|
||||||
float transitionProgress = 1.0f;
|
|
||||||
if (slowMoTimeRemaining > slowMoActiveTime - slowMoTransitionTime) {
|
|
||||||
const float timeInTransition = slowMoActiveTime - slowMoTimeRemaining;
|
|
||||||
transitionProgress = (std::min)(1.0f, timeInTransition / slowMoTransitionTime);
|
|
||||||
} else if (slowMoTimeRemaining < slowMoTransitionTime) {
|
|
||||||
transitionProgress = (std::max)(0.0f, slowMoTimeRemaining / slowMoTransitionTime);
|
|
||||||
}
|
|
||||||
const float speedMultiplier = isPlayer
|
|
||||||
? 1.0f + 0.5f * transitionProgress
|
|
||||||
: 1.0f - 0.9f * transitionProgress;
|
|
||||||
deltaMs = static_cast<std::int64_t>(
|
|
||||||
static_cast<float>(deltaMs) * speedMultiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
const CharacterObservation* attackTarget = context.attackTarget;
|
const CharacterObservation* attackTarget = context.attackTarget;
|
||||||
const CharacterObservation* faceTarget = context.faceTarget;
|
const CharacterObservation* faceTarget = context.faceTarget;
|
||||||
if (!attackTarget) {
|
if (!attackTarget) {
|
||||||
|
|||||||
@@ -97,11 +97,6 @@ public:
|
|||||||
Eigen::Vector3f homePosition = Eigen::Vector3f::Zero();
|
Eigen::Vector3f homePosition = Eigen::Vector3f::Zero();
|
||||||
float maxHomeDrift = 3.0f;
|
float maxHomeDrift = 3.0f;
|
||||||
|
|
||||||
// --- Slow-motion effect ---
|
|
||||||
float slowMoTimeRemaining = 0.0f;
|
|
||||||
float slowMoActiveTime = 6.0f;
|
|
||||||
float slowMoTransitionTime = 0.5f;
|
|
||||||
|
|
||||||
// --- 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
|
||||||
// character construction. Simulation advances animationFrame itself.
|
// character construction. Simulation advances animationFrame itself.
|
||||||
|
|||||||
+4
-25
@@ -649,11 +649,10 @@ namespace FRG
|
|||||||
Location* loc = currentLocation();
|
Location* loc = currentLocation();
|
||||||
if (!loc) return;
|
if (!loc) return;
|
||||||
if (loc->player) {
|
if (loc->player) {
|
||||||
loc->updateCharacterState(*loc->player, [&](CharacterState& workingState) {
|
server().submit(SetCharacterPositionCommand{
|
||||||
workingState.position = destPos;
|
destName, loc->player->entityId(), destPos});
|
||||||
workingState.facingAngle = destRotY;
|
server().submit(SetCharacterFacingAngleCommand{
|
||||||
workingState.targetFacingAngle = destRotY;
|
destName, loc->player->entityId(), destRotY, true});
|
||||||
});
|
|
||||||
loc->stopCharacter(*loc->player);
|
loc->stopCharacter(*loc->player);
|
||||||
}
|
}
|
||||||
loc->localState().cameraAzimuth = destRotY;
|
loc->localState().cameraAzimuth = destRotY;
|
||||||
@@ -1536,26 +1535,6 @@ namespace FRG
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::activateSlowMoEffect() {
|
|
||||||
if (!currentLocation()) return;
|
|
||||||
|
|
||||||
if (currentLocation()->player) {
|
|
||||||
currentLocation()->updateCharacterState(
|
|
||||||
*currentLocation()->player, [](CharacterState& workingState) {
|
|
||||||
workingState.slowMoTimeRemaining = workingState.slowMoActiveTime;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& npc : currentLocation()->npcs) {
|
|
||||||
if (npc) {
|
|
||||||
currentLocation()->updateCharacterState(
|
|
||||||
*npc, [](CharacterState& workingState) {
|
|
||||||
workingState.slowMoTimeRemaining = workingState.slowMoActiveTime;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::enterCameraDragMode(int eventX, int eventY)
|
void Game::enterCameraDragMode(int eventX, int eventY)
|
||||||
{
|
{
|
||||||
cameraDragging = true;
|
cameraDragging = true;
|
||||||
|
|||||||
@@ -105,8 +105,6 @@ namespace FRG {
|
|||||||
const IGameServer& server() const { return gameServer; }
|
const IGameServer& server() const { return gameServer; }
|
||||||
|
|
||||||
MenuManager menuManager;
|
MenuManager menuManager;
|
||||||
|
|
||||||
void activateSlowMoEffect();
|
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
static Game* s_instance;
|
static Game* s_instance;
|
||||||
|
|||||||
+17
-42
@@ -132,18 +132,6 @@ namespace FRG
|
|||||||
locationId_, character.entityId(), target, std::move(callbackName)});
|
locationId_, character.entityId(), target, std::move(callbackName)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Location::updateCharacterState(
|
|
||||||
Character& character,
|
|
||||||
const std::function<void(CharacterState&)>& mutation)
|
|
||||||
{
|
|
||||||
auto snapshot = character.stateSnapshot();
|
|
||||||
if (!snapshot) return;
|
|
||||||
CharacterState stateCopy = *snapshot;
|
|
||||||
mutation(stateCopy);
|
|
||||||
gameServer_.submit(CommitCharacterStateCommand{
|
|
||||||
locationId_, character.entityId(), std::move(stateCopy)});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Location::clearCharacterAttackTarget(Character& character)
|
void Location::clearCharacterAttackTarget(Character& character)
|
||||||
{
|
{
|
||||||
gameServer_.submit(ClearCharacterAttackTargetCommand{
|
gameServer_.submit(ClearCharacterAttackTargetCommand{
|
||||||
@@ -1847,7 +1835,6 @@ namespace FRG
|
|||||||
|
|
||||||
void Location::update(int64_t delta, const LocationSimulationEvents& events)
|
void Location::update(int64_t delta, const LocationSimulationEvents& events)
|
||||||
{
|
{
|
||||||
pathVisualization.update(delta);
|
|
||||||
synchronizeRemotePlayers();
|
synchronizeRemotePlayers();
|
||||||
auto locationState = stateSnapshot();
|
auto locationState = stateSnapshot();
|
||||||
if (!locationState) return;
|
if (!locationState) return;
|
||||||
@@ -2022,7 +2009,7 @@ namespace FRG
|
|||||||
Eigen::Vector3f camRight(cosAzim, 0.f, sinAzim);
|
Eigen::Vector3f camRight(cosAzim, 0.f, sinAzim);
|
||||||
Eigen::Vector3f camForward(sinAzim * cosIncl, -sinIncl, -cosAzim * cosIncl);
|
Eigen::Vector3f camForward(sinAzim * cosIncl, -sinIncl, -cosAzim * cosIncl);
|
||||||
Eigen::Vector3f camUp(sinAzim * sinIncl, cosIncl, -cosAzim * sinIncl);
|
Eigen::Vector3f camUp(sinAzim * sinIncl, cosIncl, -cosAzim * sinIncl);
|
||||||
const Eigen::Vector3f playerPos = playerState->position;
|
const Eigen::Vector3f playerPos = player->renderPosition();
|
||||||
Eigen::Vector3f camPos = playerPos + Eigen::Vector3f(-sinAzim * cosIncl, sinIncl, cosAzim * cosIncl) * Environment::zoom + Eigen::Vector3f(0,1,0);
|
Eigen::Vector3f camPos = playerPos + Eigen::Vector3f(-sinAzim * cosIncl, sinIncl, cosAzim * cosIncl) * Environment::zoom + Eigen::Vector3f(0,1,0);
|
||||||
|
|
||||||
Eigen::Vector3f rayDir = (camForward + camRight * (ndcX * aspect * tanHalfFov) + camUp * (ndcY * tanHalfFov)).normalized();
|
Eigen::Vector3f rayDir = (camForward + camRight * (ndcX * aspect * tanHalfFov) + camUp * (ndcY * tanHalfFov)).normalized();
|
||||||
@@ -2065,10 +2052,7 @@ namespace FRG
|
|||||||
? clickedObject->state.interactionPosition
|
? clickedObject->state.interactionPosition
|
||||||
: clickedObject->state.position;
|
: clickedObject->state.position;
|
||||||
setCharacterTarget(*player, interactionTarget);
|
setCharacterTarget(*player, interactionTarget);
|
||||||
auto updatedPlayerState = player->stateSnapshot();
|
pathVisualization.start(*player, interactionTarget);
|
||||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
|
||||||
pathVisualization.start(*player, interactionTarget);
|
|
||||||
}
|
|
||||||
clearCharacterAttackTarget(*player);
|
clearCharacterAttackTarget(*player);
|
||||||
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
||||||
}
|
}
|
||||||
@@ -2101,21 +2085,17 @@ namespace FRG
|
|||||||
tryCharacterAttackCombo(*player);
|
tryCharacterAttackCombo(*player);
|
||||||
}
|
}
|
||||||
const std::string targetId = clickedNpc->entityId();
|
const std::string targetId = clickedNpc->entityId();
|
||||||
updateCharacterState(*player, [targetId](CharacterState& workingState) {
|
gameServer_.submit(SetCharacterTargetCommand{
|
||||||
workingState.attackTargetId = targetId;
|
locationId_, player->entityId(), targetId});
|
||||||
});
|
|
||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
locationId_, std::string(), std::string(), std::string()});
|
locationId_, std::string(), std::string(), std::string()});
|
||||||
|
|
||||||
if (distance >= Character::kAttackRange) {
|
if (distance >= Character::kAttackRange) {
|
||||||
setCharacterTarget(*player, clickedNpcState->position);
|
setCharacterTarget(*player, clickedNpcState->position);
|
||||||
auto updatedPlayerState = player->stateSnapshot();
|
pathVisualization.start(
|
||||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
*player,
|
||||||
pathVisualization.start(
|
clickedNpcState->position,
|
||||||
*player,
|
PathVisualization::Style::Hostile);
|
||||||
clickedNpcState->position,
|
|
||||||
PathVisualization::Style::Hostile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distance <= clickedNpcState->interactionRadius) {
|
if (distance <= clickedNpcState->interactionRadius) {
|
||||||
@@ -2131,9 +2111,9 @@ namespace FRG
|
|||||||
// Already in talk range — fire immediately, stop, and face the player.
|
// Already in talk range — fire immediately, stop, and face the player.
|
||||||
//logger() << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
|
//logger() << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
|
||||||
setCharacterTarget(*player, playerState->position);
|
setCharacterTarget(*player, playerState->position);
|
||||||
updateCharacterState(*npcs[npcIndex], [this](CharacterState& workingState) {
|
gameServer_.submit(SetCharacterFaceTargetCommand{
|
||||||
workingState.faceTargetId = gameServer_.localPlayerId();
|
locationId_, npcs[npcIndex]->entityId(),
|
||||||
});
|
gameServer_.localPlayerId()});
|
||||||
scriptEngine.callNpcInteractCallback(clickedNpc->entityId());
|
scriptEngine.callNpcInteractCallback(clickedNpc->entityId());
|
||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
locationId_, std::string(), std::string(), std::string()});
|
locationId_, std::string(), std::string(), std::string()});
|
||||||
@@ -2142,10 +2122,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);
|
||||||
auto updatedPlayerState = player->stateSnapshot();
|
pathVisualization.start(*player, clickedNpcState->position);
|
||||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
|
||||||
pathVisualization.start(*player, clickedNpcState->position);
|
|
||||||
}
|
|
||||||
gameServer_.submit(SetLocationTargetsCommand{
|
gameServer_.submit(SetLocationTargetsCommand{
|
||||||
locationId_, std::string(), clickedNpc->entityId(),
|
locationId_, std::string(), clickedNpc->entityId(),
|
||||||
std::string()});
|
std::string()});
|
||||||
@@ -2173,13 +2150,10 @@ namespace FRG
|
|||||||
locationId_, std::string(), std::string(),
|
locationId_, std::string(), std::string(),
|
||||||
clickedTeleport->id});
|
clickedTeleport->id});
|
||||||
setCharacterTarget(*player, clickedTeleport->position);
|
setCharacterTarget(*player, clickedTeleport->position);
|
||||||
auto updatedPlayerState = player->stateSnapshot();
|
pathVisualization.start(
|
||||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
*player,
|
||||||
pathVisualization.start(
|
clickedTeleport->position,
|
||||||
*player,
|
PathVisualization::Style::Teleport);
|
||||||
clickedTeleport->position,
|
|
||||||
PathVisualization::Style::Teleport);
|
|
||||||
}
|
|
||||||
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());
|
||||||
@@ -2235,6 +2209,7 @@ namespace FRG
|
|||||||
void Location::updatePresentation(int64_t delta)
|
void Location::updatePresentation(int64_t delta)
|
||||||
{
|
{
|
||||||
dialogueSystem.update(static_cast<int>(delta));
|
dialogueSystem.update(static_cast<int>(delta));
|
||||||
|
pathVisualization.update(delta);
|
||||||
for (auto& tz : teleportZones)
|
for (auto& tz : teleportZones)
|
||||||
tz.update(static_cast<float>(delta));
|
tz.update(static_cast<float>(delta));
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -80,6 +80,7 @@ namespace FRG
|
|||||||
// Detaches the (shared, longer-lived) player from this location.
|
// Detaches the (shared, longer-lived) player from this location.
|
||||||
~Location();
|
~Location();
|
||||||
const std::string& locationId() const { return locationId_; }
|
const std::string& locationId() const { return locationId_; }
|
||||||
|
IGameServer& gameServer() { return gameServer_; }
|
||||||
std::shared_ptr<const LocationNetworkState> stateSnapshot() const;
|
std::shared_ptr<const LocationNetworkState> stateSnapshot() const;
|
||||||
LocationLocalState& localState() { return *localState_; }
|
LocationLocalState& localState() { return *localState_; }
|
||||||
const LocationLocalState& localState() const { return *localState_; }
|
const LocationLocalState& localState() const { return *localState_; }
|
||||||
@@ -175,9 +176,6 @@ namespace FRG
|
|||||||
bool switchNavigation(int index);
|
bool switchNavigation(int index);
|
||||||
void setCharacterTarget(Character& character, const Eigen::Vector3f& target,
|
void setCharacterTarget(Character& character, const Eigen::Vector3f& target,
|
||||||
std::string callbackName = "");
|
std::string callbackName = "");
|
||||||
void updateCharacterState(
|
|
||||||
Character& character,
|
|
||||||
const std::function<void(CharacterState&)>& mutation);
|
|
||||||
void clearCharacterAttackTarget(Character& character);
|
void clearCharacterAttackTarget(Character& character);
|
||||||
void stopCharacter(Character& character);
|
void stopCharacter(Character& character);
|
||||||
void faceCharacter(Character& character, std::string targetId);
|
void faceCharacter(Character& character, std::string targetId);
|
||||||
|
|||||||
+20
-44
@@ -124,9 +124,8 @@ namespace FRG {
|
|||||||
if (cbName.empty())
|
if (cbName.empty())
|
||||||
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->updateCharacterState(*npc, [x, z](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
||||||
workingState.homePosition = Eigen::Vector3f(x, 0.f, z);
|
loc->locationId(), npc->entityId(), Eigen::Vector3f(x, 0.f, z)});
|
||||||
});
|
|
||||||
loc->setCharacterTarget(
|
loc->setCharacterTarget(
|
||||||
*npc, Eigen::Vector3f(x, y, z), std::move(cbName));
|
*npc, Eigen::Vector3f(x, y, z), std::move(cbName));
|
||||||
});
|
});
|
||||||
@@ -142,18 +141,16 @@ namespace FRG {
|
|||||||
|
|
||||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||||
logger() << "[script] npc_rotate_to: " << npcId << " angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
logger() << "[script] npc_rotate_to: " << npcId << " angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||||
loc->updateCharacterState(*npc, [rad](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
workingState.targetFacingAngle = rad;
|
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 float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||||
logger() << "[script] player_rotate_to: angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
logger() << "[script] player_rotate_to: angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||||
loc->updateCharacterState(*loc->player, [rad](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
workingState.targetFacingAngle = rad;
|
loc->locationId(), loc->player->entityId(), rad, false});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
api.set_function("player_stop_and_rotate_to_npc",
|
api.set_function("player_stop_and_rotate_to_npc",
|
||||||
@@ -188,9 +185,9 @@ namespace FRG {
|
|||||||
if (cbName.empty())
|
if (cbName.empty())
|
||||||
std::cerr << "[script] player_walk_to: on_arrived must be a global Lua function\n";
|
std::cerr << "[script] player_walk_to: on_arrived must be a global Lua function\n";
|
||||||
}
|
}
|
||||||
loc->updateCharacterState(*loc->player, [x, z](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
||||||
workingState.homePosition = Eigen::Vector3f(x, 0.f, z);
|
loc->locationId(), loc->player->entityId(),
|
||||||
});
|
Eigen::Vector3f(x, 0.f, z)});
|
||||||
loc->setCharacterTarget(
|
loc->setCharacterTarget(
|
||||||
*loc->player, Eigen::Vector3f(x, y, z), std::move(cbName));
|
*loc->player, Eigen::Vector3f(x, y, z), std::move(cbName));
|
||||||
});
|
});
|
||||||
@@ -440,13 +437,7 @@ namespace FRG {
|
|||||||
if (loc->player)
|
if (loc->player)
|
||||||
{
|
{
|
||||||
loc->setCharacterHp(*loc->player, value);
|
loc->setCharacterHp(*loc->player, value);
|
||||||
loc->updateCharacterState(*loc->player, [](CharacterState& workingState) {
|
loc->resetCharacterAfterDeath(*loc->player);
|
||||||
if (workingState.currentState == AnimationState::ACTION_TO_DEATH ||
|
|
||||||
workingState.currentState == AnimationState::DEATH_IDLE) {
|
|
||||||
workingState.currentState = AnimationState::STAND;
|
|
||||||
workingState.resetAnim = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// get_player_hp() → float (0 if no player)
|
// get_player_hp() → float (0 if no player)
|
||||||
@@ -602,10 +593,8 @@ namespace FRG {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Eigen::Vector3f pos(x, y, z);
|
Eigen::Vector3f pos(x, y, z);
|
||||||
loc->updateCharacterState(*npc, [&pos](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterPositionCommand{
|
||||||
workingState.position = pos;
|
loc->locationId(), npc->entityId(), pos, true});
|
||||||
workingState.homePosition = pos;
|
|
||||||
});
|
|
||||||
loc->setCharacterTarget(*npc, pos);
|
loc->setCharacterTarget(*npc, pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -618,10 +607,8 @@ namespace FRG {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||||
loc->updateCharacterState(*npc, [rad](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
workingState.facingAngle = rad;
|
loc->locationId(), npc->entityId(), rad, true});
|
||||||
workingState.targetFacingAngle = rad;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// set_npc_enabled(npc_id, enabled)
|
// set_npc_enabled(npc_id, enabled)
|
||||||
@@ -632,34 +619,23 @@ namespace FRG {
|
|||||||
std::cerr << "[script] set_npc_enabled: unknown NPC id '" << npcId << "'\n";
|
std::cerr << "[script] set_npc_enabled: unknown NPC id '" << npcId << "'\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loc->updateCharacterState(*npc, [value](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterEnabledCommand{
|
||||||
workingState.enabled = value;
|
loc->locationId(), npc->entityId(), value});
|
||||||
workingState.attack = 0;
|
|
||||||
workingState.attack_cooldown = 0;
|
|
||||||
workingState.currentState = AnimationState::STAND;
|
|
||||||
if (workingState.canAttack)
|
|
||||||
workingState.attackTargetId = "player";
|
|
||||||
workingState.battle_state = 0;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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);
|
Eigen::Vector3f pos(x, y, z);
|
||||||
loc->updateCharacterState(*loc->player, [&pos](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterPositionCommand{
|
||||||
workingState.position = pos;
|
loc->locationId(), loc->player->entityId(), pos, true});
|
||||||
workingState.homePosition = pos;
|
|
||||||
});
|
|
||||||
loc->setCharacterTarget(*loc->player, pos);
|
loc->setCharacterTarget(*loc->player, pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
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 float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||||
loc->updateCharacterState(*loc->player, [rad](CharacterState& workingState) {
|
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||||
workingState.facingAngle = rad;
|
loc->locationId(), loc->player->entityId(), rad, true});
|
||||||
workingState.targetFacingAngle = rad;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// move_object(name, x, y, z, duration_sec [, on_complete])
|
// move_object(name, x, y, z, duration_sec [, on_complete])
|
||||||
|
|||||||
@@ -136,17 +136,38 @@ struct SetQuestActiveObjectiveCommand {
|
|||||||
};
|
};
|
||||||
struct SetGlobalIntCommand { std::string key; int value; };
|
struct SetGlobalIntCommand { std::string key; int value; };
|
||||||
struct SetGlobalFloatCommand { std::string key; float value; };
|
struct SetGlobalFloatCommand { std::string key; float value; };
|
||||||
struct SetCharacterTransformCommand {
|
struct SetCharacterPositionCommand {
|
||||||
|
std::string locationId;
|
||||||
|
std::string characterId;
|
||||||
|
Eigen::Vector3f position;
|
||||||
|
bool updateHomePosition = false;
|
||||||
|
};
|
||||||
|
struct SetCharacterFacingAngleCommand {
|
||||||
|
std::string locationId;
|
||||||
|
std::string characterId;
|
||||||
|
float angle;
|
||||||
|
bool setCurrentAngle = false;
|
||||||
|
};
|
||||||
|
struct SetCharacterHomePositionCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
Eigen::Vector3f position;
|
Eigen::Vector3f position;
|
||||||
float facingAngle;
|
|
||||||
};
|
};
|
||||||
struct SetCharacterTargetCommand {
|
struct SetCharacterTargetCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
std::string targetId;
|
std::string targetId;
|
||||||
};
|
};
|
||||||
|
struct SetCharacterFaceTargetCommand {
|
||||||
|
std::string locationId;
|
||||||
|
std::string characterId;
|
||||||
|
std::string targetId;
|
||||||
|
};
|
||||||
|
struct SetCharacterEnabledCommand {
|
||||||
|
std::string locationId;
|
||||||
|
std::string characterId;
|
||||||
|
bool enabled;
|
||||||
|
};
|
||||||
struct ClearCharacterAttackTargetCommand {
|
struct ClearCharacterAttackTargetCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string characterId;
|
std::string characterId;
|
||||||
@@ -196,11 +217,6 @@ struct DamageCharacterCommand {
|
|||||||
float amount;
|
float amount;
|
||||||
std::string attackerId;
|
std::string attackerId;
|
||||||
};
|
};
|
||||||
struct CommitCharacterStateCommand {
|
|
||||||
std::string locationId;
|
|
||||||
std::string characterId;
|
|
||||||
CharacterState state;
|
|
||||||
};
|
|
||||||
struct SetInteractiveObjectActiveCommand {
|
struct SetInteractiveObjectActiveCommand {
|
||||||
std::string locationId;
|
std::string locationId;
|
||||||
std::string objectId;
|
std::string objectId;
|
||||||
@@ -241,8 +257,12 @@ using GameCommand = std::variant<
|
|||||||
SetQuestActiveObjectiveCommand,
|
SetQuestActiveObjectiveCommand,
|
||||||
SetGlobalIntCommand,
|
SetGlobalIntCommand,
|
||||||
SetGlobalFloatCommand,
|
SetGlobalFloatCommand,
|
||||||
SetCharacterTransformCommand,
|
SetCharacterPositionCommand,
|
||||||
|
SetCharacterFacingAngleCommand,
|
||||||
|
SetCharacterHomePositionCommand,
|
||||||
SetCharacterTargetCommand,
|
SetCharacterTargetCommand,
|
||||||
|
SetCharacterFaceTargetCommand,
|
||||||
|
SetCharacterEnabledCommand,
|
||||||
ClearCharacterAttackTargetCommand,
|
ClearCharacterAttackTargetCommand,
|
||||||
SetCharacterDestinationCommand,
|
SetCharacterDestinationCommand,
|
||||||
StopCharacterCommand,
|
StopCharacterCommand,
|
||||||
@@ -253,7 +273,6 @@ using GameCommand = std::variant<
|
|||||||
SetCharacterHealthCommand,
|
SetCharacterHealthCommand,
|
||||||
SetCharacterAnimationMetadataCommand,
|
SetCharacterAnimationMetadataCommand,
|
||||||
DamageCharacterCommand,
|
DamageCharacterCommand,
|
||||||
CommitCharacterStateCommand,
|
|
||||||
SetInteractiveObjectActiveCommand,
|
SetInteractiveObjectActiveCommand,
|
||||||
SetTriggerZoneEnabledCommand,
|
SetTriggerZoneEnabledCommand,
|
||||||
ReloadQuestDefinitionsCommand>;
|
ReloadQuestDefinitionsCommand>;
|
||||||
|
|||||||
@@ -378,18 +378,47 @@ void InternalGameServer::submit(const GameCommand& command)
|
|||||||
state_.globalInts[value.key] = value.value;
|
state_.globalInts[value.key] = value.value;
|
||||||
} else if constexpr (std::is_same_v<T, SetGlobalFloatCommand>) {
|
} else if constexpr (std::is_same_v<T, SetGlobalFloatCommand>) {
|
||||||
state_.globalFloats[value.key] = value.value;
|
state_.globalFloats[value.key] = value.value;
|
||||||
} else if constexpr (std::is_same_v<T, SetCharacterTransformCommand>) {
|
} else if constexpr (std::is_same_v<T, SetCharacterPositionCommand>) {
|
||||||
CharacterState* character =
|
CharacterState* character =
|
||||||
state_.findCharacter(value.locationId, value.characterId);
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
if (character) {
|
if (character) {
|
||||||
character->position = value.position;
|
character->position = value.position;
|
||||||
character->facingAngle = value.facingAngle;
|
if (value.updateHomePosition)
|
||||||
character->targetFacingAngle = value.facingAngle;
|
character->homePosition = value.position;
|
||||||
}
|
}
|
||||||
|
} else if constexpr (std::is_same_v<T, SetCharacterFacingAngleCommand>) {
|
||||||
|
CharacterState* character =
|
||||||
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
|
if (character) {
|
||||||
|
character->targetFacingAngle = value.angle;
|
||||||
|
if (value.setCurrentAngle)
|
||||||
|
character->facingAngle = value.angle;
|
||||||
|
}
|
||||||
|
} else if constexpr (std::is_same_v<T, SetCharacterHomePositionCommand>) {
|
||||||
|
CharacterState* character =
|
||||||
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
|
if (character)
|
||||||
|
character->homePosition = value.position;
|
||||||
} else if constexpr (std::is_same_v<T, SetCharacterTargetCommand>) {
|
} else if constexpr (std::is_same_v<T, SetCharacterTargetCommand>) {
|
||||||
CharacterState* character =
|
CharacterState* character =
|
||||||
state_.findCharacter(value.locationId, value.characterId);
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
if (character) character->attackTargetId = value.targetId;
|
if (character) character->attackTargetId = value.targetId;
|
||||||
|
} else if constexpr (std::is_same_v<T, SetCharacterFaceTargetCommand>) {
|
||||||
|
CharacterState* character =
|
||||||
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
|
if (character) character->faceTargetId = value.targetId;
|
||||||
|
} else if constexpr (std::is_same_v<T, SetCharacterEnabledCommand>) {
|
||||||
|
CharacterState* character =
|
||||||
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
|
if (character) {
|
||||||
|
character->enabled = value.enabled;
|
||||||
|
character->attack = 0;
|
||||||
|
character->attack_cooldown = 0;
|
||||||
|
character->currentState = AnimationState::STAND;
|
||||||
|
if (character->canAttack)
|
||||||
|
character->attackTargetId = "player";
|
||||||
|
character->battle_state = 0;
|
||||||
|
}
|
||||||
} else if constexpr (std::is_same_v<T, ClearCharacterAttackTargetCommand>) {
|
} else if constexpr (std::is_same_v<T, ClearCharacterAttackTargetCommand>) {
|
||||||
CharacterState* character =
|
CharacterState* character =
|
||||||
state_.findCharacter(value.locationId, value.characterId);
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
@@ -445,10 +474,6 @@ void InternalGameServer::submit(const GameCommand& command)
|
|||||||
CharacterState* character =
|
CharacterState* character =
|
||||||
state_.findCharacter(value.locationId, value.characterId);
|
state_.findCharacter(value.locationId, value.characterId);
|
||||||
if (character) character->hp = (std::max)(0.f, character->hp - (std::max)(0.f, value.amount));
|
if (character) character->hp = (std::max)(0.f, character->hp - (std::max)(0.f, value.amount));
|
||||||
} else if constexpr (std::is_same_v<T, CommitCharacterStateCommand>) {
|
|
||||||
CharacterState* character =
|
|
||||||
state_.findCharacter(value.locationId, value.characterId);
|
|
||||||
if (character) *character = value.state;
|
|
||||||
} else if constexpr (std::is_same_v<T, SetInteractiveObjectActiveCommand>) {
|
} else if constexpr (std::is_same_v<T, SetInteractiveObjectActiveCommand>) {
|
||||||
auto location = state_.locations.find(value.locationId);
|
auto location = state_.locations.find(value.locationId);
|
||||||
if (location == state_.locations.end()) return;
|
if (location == state_.locations.end()) return;
|
||||||
|
|||||||
@@ -104,17 +104,28 @@ namespace FRG {
|
|||||||
if (!state) return;
|
if (!state) return;
|
||||||
std::vector<Eigen::Vector3f> route;
|
std::vector<Eigen::Vector3f> route;
|
||||||
route.reserve(state->pathWaypoints.size() + 1);
|
route.reserve(state->pathWaypoints.size() + 1);
|
||||||
route.push_back(Eigen::Vector3f(state->position.x(), 0.0f, state->position.z()));
|
const Eigen::Vector3f renderPosition = player.renderPosition();
|
||||||
|
route.push_back(Eigen::Vector3f(renderPosition.x(), 0.0f, renderPosition.z()));
|
||||||
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];
|
||||||
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(
|
||||||
|
clickedDestination.x(), 0.0f, clickedDestination.z());
|
||||||
|
if (route.size() == 1 ||
|
||||||
|
(route.back() - clickedPosition).norm() > 0.5f) {
|
||||||
|
// A remote destination is not present in the replica yet, or the
|
||||||
|
// replica still contains the route for a previous click. Preview
|
||||||
|
// the new intent directly until authoritative waypoints catch up.
|
||||||
|
route.clear();
|
||||||
|
route.push_back(Eigen::Vector3f(
|
||||||
|
renderPosition.x(), 0.0f, renderPosition.z()));
|
||||||
|
route.push_back(clickedPosition);
|
||||||
|
}
|
||||||
|
|
||||||
destination = route.size() > 1
|
destination = clickedPosition;
|
||||||
? route.back()
|
|
||||||
: Eigen::Vector3f(clickedDestination.x(), 0.0f, clickedDestination.z());
|
|
||||||
|
|
||||||
float totalLength = 0.0f;
|
float totalLength = 0.0f;
|
||||||
for (size_t i = 1; i < route.size(); ++i) {
|
for (size_t i = 1; i < route.size(); ++i) {
|
||||||
|
|||||||
Reference in new issue
Block a user