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.
|
||||
- Character transforms, attack targets, navigation destinations, stopping,
|
||||
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.
|
||||
- Trigger-zone state.
|
||||
- Quest-definition reloads.
|
||||
|
||||
@@ -144,24 +144,6 @@ CharacterUpdateEvents CharacterState::update(
|
||||
CharacterUpdateEvents 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* faceTarget = context.faceTarget;
|
||||
if (!attackTarget) {
|
||||
|
||||
@@ -97,11 +97,6 @@ public:
|
||||
Eigen::Vector3f homePosition = Eigen::Vector3f::Zero();
|
||||
float maxHomeDrift = 3.0f;
|
||||
|
||||
// --- Slow-motion effect ---
|
||||
float slowMoTimeRemaining = 0.0f;
|
||||
float slowMoActiveTime = 6.0f;
|
||||
float slowMoTransitionTime = 0.5f;
|
||||
|
||||
// --- Authoritative logical animation clock ---
|
||||
// Frame counts are immutable metadata copied from loaded clips during
|
||||
// character construction. Simulation advances animationFrame itself.
|
||||
|
||||
+4
-25
@@ -649,11 +649,10 @@ namespace FRG
|
||||
Location* loc = currentLocation();
|
||||
if (!loc) return;
|
||||
if (loc->player) {
|
||||
loc->updateCharacterState(*loc->player, [&](CharacterState& workingState) {
|
||||
workingState.position = destPos;
|
||||
workingState.facingAngle = destRotY;
|
||||
workingState.targetFacingAngle = destRotY;
|
||||
});
|
||||
server().submit(SetCharacterPositionCommand{
|
||||
destName, loc->player->entityId(), destPos});
|
||||
server().submit(SetCharacterFacingAngleCommand{
|
||||
destName, loc->player->entityId(), destRotY, true});
|
||||
loc->stopCharacter(*loc->player);
|
||||
}
|
||||
loc->localState().cameraAzimuth = destRotY;
|
||||
@@ -1536,26 +1535,6 @@ namespace FRG
|
||||
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)
|
||||
{
|
||||
cameraDragging = true;
|
||||
|
||||
@@ -105,8 +105,6 @@ namespace FRG {
|
||||
const IGameServer& server() const { return gameServer; }
|
||||
|
||||
MenuManager menuManager;
|
||||
|
||||
void activateSlowMoEffect();
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
static Game* s_instance;
|
||||
|
||||
+17
-42
@@ -132,18 +132,6 @@ namespace FRG
|
||||
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)
|
||||
{
|
||||
gameServer_.submit(ClearCharacterAttackTargetCommand{
|
||||
@@ -1847,7 +1835,6 @@ namespace FRG
|
||||
|
||||
void Location::update(int64_t delta, const LocationSimulationEvents& events)
|
||||
{
|
||||
pathVisualization.update(delta);
|
||||
synchronizeRemotePlayers();
|
||||
auto locationState = stateSnapshot();
|
||||
if (!locationState) return;
|
||||
@@ -2022,7 +2009,7 @@ namespace FRG
|
||||
Eigen::Vector3f camRight(cosAzim, 0.f, sinAzim);
|
||||
Eigen::Vector3f camForward(sinAzim * cosIncl, -sinIncl, -cosAzim * cosIncl);
|
||||
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 rayDir = (camForward + camRight * (ndcX * aspect * tanHalfFov) + camUp * (ndcY * tanHalfFov)).normalized();
|
||||
@@ -2065,10 +2052,7 @@ namespace FRG
|
||||
? clickedObject->state.interactionPosition
|
||||
: clickedObject->state.position;
|
||||
setCharacterTarget(*player, interactionTarget);
|
||||
auto updatedPlayerState = player->stateSnapshot();
|
||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
||||
pathVisualization.start(*player, interactionTarget);
|
||||
}
|
||||
pathVisualization.start(*player, interactionTarget);
|
||||
clearCharacterAttackTarget(*player);
|
||||
//logger() << "[CLICK] Player moving to object..." << std::endl;
|
||||
}
|
||||
@@ -2101,21 +2085,17 @@ namespace FRG
|
||||
tryCharacterAttackCombo(*player);
|
||||
}
|
||||
const std::string targetId = clickedNpc->entityId();
|
||||
updateCharacterState(*player, [targetId](CharacterState& workingState) {
|
||||
workingState.attackTargetId = targetId;
|
||||
});
|
||||
gameServer_.submit(SetCharacterTargetCommand{
|
||||
locationId_, player->entityId(), targetId});
|
||||
gameServer_.submit(SetLocationTargetsCommand{
|
||||
locationId_, std::string(), std::string(), std::string()});
|
||||
|
||||
if (distance >= Character::kAttackRange) {
|
||||
setCharacterTarget(*player, clickedNpcState->position);
|
||||
auto updatedPlayerState = player->stateSnapshot();
|
||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
||||
pathVisualization.start(
|
||||
*player,
|
||||
clickedNpcState->position,
|
||||
PathVisualization::Style::Hostile);
|
||||
}
|
||||
pathVisualization.start(
|
||||
*player,
|
||||
clickedNpcState->position,
|
||||
PathVisualization::Style::Hostile);
|
||||
}
|
||||
|
||||
if (distance <= clickedNpcState->interactionRadius) {
|
||||
@@ -2131,9 +2111,9 @@ namespace FRG
|
||||
// Already in talk range — fire immediately, stop, and face the player.
|
||||
//logger() << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
|
||||
setCharacterTarget(*player, playerState->position);
|
||||
updateCharacterState(*npcs[npcIndex], [this](CharacterState& workingState) {
|
||||
workingState.faceTargetId = gameServer_.localPlayerId();
|
||||
});
|
||||
gameServer_.submit(SetCharacterFaceTargetCommand{
|
||||
locationId_, npcs[npcIndex]->entityId(),
|
||||
gameServer_.localPlayerId()});
|
||||
scriptEngine.callNpcInteractCallback(clickedNpc->entityId());
|
||||
gameServer_.submit(SetLocationTargetsCommand{
|
||||
locationId_, std::string(), std::string(), std::string()});
|
||||
@@ -2142,10 +2122,7 @@ namespace FRG
|
||||
//logger() << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
|
||||
// << " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
|
||||
setCharacterTarget(*player, clickedNpcState->position);
|
||||
auto updatedPlayerState = player->stateSnapshot();
|
||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
||||
pathVisualization.start(*player, clickedNpcState->position);
|
||||
}
|
||||
pathVisualization.start(*player, clickedNpcState->position);
|
||||
gameServer_.submit(SetLocationTargetsCommand{
|
||||
locationId_, std::string(), clickedNpc->entityId(),
|
||||
std::string()});
|
||||
@@ -2173,13 +2150,10 @@ namespace FRG
|
||||
locationId_, std::string(), std::string(),
|
||||
clickedTeleport->id});
|
||||
setCharacterTarget(*player, clickedTeleport->position);
|
||||
auto updatedPlayerState = player->stateSnapshot();
|
||||
if (updatedPlayerState && !updatedPlayerState->pathWaypoints.empty()) {
|
||||
pathVisualization.start(
|
||||
*player,
|
||||
clickedTeleport->position,
|
||||
PathVisualization::Style::Teleport);
|
||||
}
|
||||
pathVisualization.start(
|
||||
*player,
|
||||
clickedTeleport->position,
|
||||
PathVisualization::Style::Teleport);
|
||||
clearCharacterAttackTarget(*player);
|
||||
} else {
|
||||
const Eigen::Vector3f clickedDestination(hit.x(), 0.f, hit.z());
|
||||
@@ -2235,6 +2209,7 @@ namespace FRG
|
||||
void Location::updatePresentation(int64_t delta)
|
||||
{
|
||||
dialogueSystem.update(static_cast<int>(delta));
|
||||
pathVisualization.update(delta);
|
||||
for (auto& tz : teleportZones)
|
||||
tz.update(static_cast<float>(delta));
|
||||
}
|
||||
|
||||
+1
-3
@@ -80,6 +80,7 @@ namespace FRG
|
||||
// Detaches the (shared, longer-lived) player from this location.
|
||||
~Location();
|
||||
const std::string& locationId() const { return locationId_; }
|
||||
IGameServer& gameServer() { return gameServer_; }
|
||||
std::shared_ptr<const LocationNetworkState> stateSnapshot() const;
|
||||
LocationLocalState& localState() { return *localState_; }
|
||||
const LocationLocalState& localState() const { return *localState_; }
|
||||
@@ -175,9 +176,6 @@ namespace FRG
|
||||
bool switchNavigation(int index);
|
||||
void setCharacterTarget(Character& character, const Eigen::Vector3f& target,
|
||||
std::string callbackName = "");
|
||||
void updateCharacterState(
|
||||
Character& character,
|
||||
const std::function<void(CharacterState&)>& mutation);
|
||||
void clearCharacterAttackTarget(Character& character);
|
||||
void stopCharacter(Character& character);
|
||||
void faceCharacter(Character& character, std::string targetId);
|
||||
|
||||
+20
-44
@@ -124,9 +124,8 @@ namespace FRG {
|
||||
if (cbName.empty())
|
||||
std::cerr << "[script] npc_walk_to: on_arrived must be a global Lua function\n";
|
||||
}
|
||||
loc->updateCharacterState(*npc, [x, z](CharacterState& workingState) {
|
||||
workingState.homePosition = Eigen::Vector3f(x, 0.f, z);
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
||||
loc->locationId(), npc->entityId(), Eigen::Vector3f(x, 0.f, z)});
|
||||
loc->setCharacterTarget(
|
||||
*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;
|
||||
logger() << "[script] npc_rotate_to: " << npcId << " angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||
loc->updateCharacterState(*npc, [rad](CharacterState& workingState) {
|
||||
workingState.targetFacingAngle = rad;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||
loc->locationId(), npc->entityId(), rad, false});
|
||||
});
|
||||
|
||||
api.set_function("player_rotate_to",
|
||||
[loc](float angle) {
|
||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||
logger() << "[script] player_rotate_to: angle " << angle << " degrees (" << rad << " radians)" << std::endl;
|
||||
loc->updateCharacterState(*loc->player, [rad](CharacterState& workingState) {
|
||||
workingState.targetFacingAngle = rad;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||
loc->locationId(), loc->player->entityId(), rad, false});
|
||||
});
|
||||
|
||||
api.set_function("player_stop_and_rotate_to_npc",
|
||||
@@ -188,9 +185,9 @@ namespace FRG {
|
||||
if (cbName.empty())
|
||||
std::cerr << "[script] player_walk_to: on_arrived must be a global Lua function\n";
|
||||
}
|
||||
loc->updateCharacterState(*loc->player, [x, z](CharacterState& workingState) {
|
||||
workingState.homePosition = Eigen::Vector3f(x, 0.f, z);
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterHomePositionCommand{
|
||||
loc->locationId(), loc->player->entityId(),
|
||||
Eigen::Vector3f(x, 0.f, z)});
|
||||
loc->setCharacterTarget(
|
||||
*loc->player, Eigen::Vector3f(x, y, z), std::move(cbName));
|
||||
});
|
||||
@@ -440,13 +437,7 @@ namespace FRG {
|
||||
if (loc->player)
|
||||
{
|
||||
loc->setCharacterHp(*loc->player, value);
|
||||
loc->updateCharacterState(*loc->player, [](CharacterState& workingState) {
|
||||
if (workingState.currentState == AnimationState::ACTION_TO_DEATH ||
|
||||
workingState.currentState == AnimationState::DEATH_IDLE) {
|
||||
workingState.currentState = AnimationState::STAND;
|
||||
workingState.resetAnim = true;
|
||||
}
|
||||
});
|
||||
loc->resetCharacterAfterDeath(*loc->player);
|
||||
}
|
||||
});
|
||||
// get_player_hp() → float (0 if no player)
|
||||
@@ -602,10 +593,8 @@ namespace FRG {
|
||||
return;
|
||||
}
|
||||
Eigen::Vector3f pos(x, y, z);
|
||||
loc->updateCharacterState(*npc, [&pos](CharacterState& workingState) {
|
||||
workingState.position = pos;
|
||||
workingState.homePosition = pos;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterPositionCommand{
|
||||
loc->locationId(), npc->entityId(), pos, true});
|
||||
loc->setCharacterTarget(*npc, pos);
|
||||
});
|
||||
|
||||
@@ -618,10 +607,8 @@ namespace FRG {
|
||||
return;
|
||||
}
|
||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||
loc->updateCharacterState(*npc, [rad](CharacterState& workingState) {
|
||||
workingState.facingAngle = rad;
|
||||
workingState.targetFacingAngle = rad;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||
loc->locationId(), npc->entityId(), rad, true});
|
||||
});
|
||||
|
||||
// set_npc_enabled(npc_id, enabled)
|
||||
@@ -632,34 +619,23 @@ namespace FRG {
|
||||
std::cerr << "[script] set_npc_enabled: unknown NPC id '" << npcId << "'\n";
|
||||
return;
|
||||
}
|
||||
loc->updateCharacterState(*npc, [value](CharacterState& workingState) {
|
||||
workingState.enabled = value;
|
||||
workingState.attack = 0;
|
||||
workingState.attack_cooldown = 0;
|
||||
workingState.currentState = AnimationState::STAND;
|
||||
if (workingState.canAttack)
|
||||
workingState.attackTargetId = "player";
|
||||
workingState.battle_state = 0;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterEnabledCommand{
|
||||
loc->locationId(), npc->entityId(), value});
|
||||
});
|
||||
|
||||
api.set_function("player_set_position",
|
||||
[loc](float x, float y, float z) {
|
||||
Eigen::Vector3f pos(x, y, z);
|
||||
loc->updateCharacterState(*loc->player, [&pos](CharacterState& workingState) {
|
||||
workingState.position = pos;
|
||||
workingState.homePosition = pos;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterPositionCommand{
|
||||
loc->locationId(), loc->player->entityId(), pos, true});
|
||||
loc->setCharacterTarget(*loc->player, pos);
|
||||
});
|
||||
|
||||
api.set_function("player_set_rotation",
|
||||
[loc](float angle) {
|
||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||
loc->updateCharacterState(*loc->player, [rad](CharacterState& workingState) {
|
||||
workingState.facingAngle = rad;
|
||||
workingState.targetFacingAngle = rad;
|
||||
});
|
||||
loc->gameServer().submit(SetCharacterFacingAngleCommand{
|
||||
loc->locationId(), loc->player->entityId(), rad, true});
|
||||
});
|
||||
|
||||
// move_object(name, x, y, z, duration_sec [, on_complete])
|
||||
|
||||
@@ -136,17 +136,38 @@ struct SetQuestActiveObjectiveCommand {
|
||||
};
|
||||
struct SetGlobalIntCommand { std::string key; int 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 characterId;
|
||||
Eigen::Vector3f position;
|
||||
float facingAngle;
|
||||
};
|
||||
struct SetCharacterTargetCommand {
|
||||
std::string locationId;
|
||||
std::string characterId;
|
||||
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 {
|
||||
std::string locationId;
|
||||
std::string characterId;
|
||||
@@ -196,11 +217,6 @@ struct DamageCharacterCommand {
|
||||
float amount;
|
||||
std::string attackerId;
|
||||
};
|
||||
struct CommitCharacterStateCommand {
|
||||
std::string locationId;
|
||||
std::string characterId;
|
||||
CharacterState state;
|
||||
};
|
||||
struct SetInteractiveObjectActiveCommand {
|
||||
std::string locationId;
|
||||
std::string objectId;
|
||||
@@ -241,8 +257,12 @@ using GameCommand = std::variant<
|
||||
SetQuestActiveObjectiveCommand,
|
||||
SetGlobalIntCommand,
|
||||
SetGlobalFloatCommand,
|
||||
SetCharacterTransformCommand,
|
||||
SetCharacterPositionCommand,
|
||||
SetCharacterFacingAngleCommand,
|
||||
SetCharacterHomePositionCommand,
|
||||
SetCharacterTargetCommand,
|
||||
SetCharacterFaceTargetCommand,
|
||||
SetCharacterEnabledCommand,
|
||||
ClearCharacterAttackTargetCommand,
|
||||
SetCharacterDestinationCommand,
|
||||
StopCharacterCommand,
|
||||
@@ -253,7 +273,6 @@ using GameCommand = std::variant<
|
||||
SetCharacterHealthCommand,
|
||||
SetCharacterAnimationMetadataCommand,
|
||||
DamageCharacterCommand,
|
||||
CommitCharacterStateCommand,
|
||||
SetInteractiveObjectActiveCommand,
|
||||
SetTriggerZoneEnabledCommand,
|
||||
ReloadQuestDefinitionsCommand>;
|
||||
|
||||
@@ -378,18 +378,47 @@ void InternalGameServer::submit(const GameCommand& command)
|
||||
state_.globalInts[value.key] = value.value;
|
||||
} else if constexpr (std::is_same_v<T, SetGlobalFloatCommand>) {
|
||||
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 =
|
||||
state_.findCharacter(value.locationId, value.characterId);
|
||||
if (character) {
|
||||
character->position = value.position;
|
||||
character->facingAngle = value.facingAngle;
|
||||
character->targetFacingAngle = value.facingAngle;
|
||||
if (value.updateHomePosition)
|
||||
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>) {
|
||||
CharacterState* character =
|
||||
state_.findCharacter(value.locationId, value.characterId);
|
||||
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>) {
|
||||
CharacterState* character =
|
||||
state_.findCharacter(value.locationId, value.characterId);
|
||||
@@ -445,10 +474,6 @@ void InternalGameServer::submit(const GameCommand& command)
|
||||
CharacterState* character =
|
||||
state_.findCharacter(value.locationId, value.characterId);
|
||||
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>) {
|
||||
auto location = state_.locations.find(value.locationId);
|
||||
if (location == state_.locations.end()) return;
|
||||
|
||||
@@ -104,17 +104,28 @@ namespace FRG {
|
||||
if (!state) return;
|
||||
std::vector<Eigen::Vector3f> route;
|
||||
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)(
|
||||
state->currentWaypointIndex, state->pathWaypoints.size());
|
||||
for (size_t i = firstWaypoint; i < state->pathWaypoints.size(); ++i) {
|
||||
const Eigen::Vector3f& waypoint = state->pathWaypoints[i];
|
||||
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
|
||||
? route.back()
|
||||
: Eigen::Vector3f(clickedDestination.x(), 0.0f, clickedDestination.z());
|
||||
destination = clickedPosition;
|
||||
|
||||
float totalLength = 0.0f;
|
||||
for (size_t i = 1; i < route.size(); ++i) {
|
||||
|
||||
Reference in new issue
Block a user