Finally restored attacks

This commit is contained in:
Vladislav Khorev committed 2026-11-14 02:09:03 +06:00
1 parent 889c6d465b
commit 0fcf8c4fa5
7 files changed
+689 -232

No files matched your search

+121 -116
View File
@@ -92,7 +92,8 @@ void CharacterState::stopInPlace() {
onArrivedCallbackName.clear();
movementBlockedTicks = 0;
navigationRetryTicks = 0;
blockingCharacterId.clear();
velocity = Vec2fp(fp(0), fp(0));
preferredVelocity = Vec2fp(fp(0), fp(0));
}
void CharacterState::setTarget(
@@ -101,6 +102,8 @@ void CharacterState::setTarget(
const LocationNetworkState& location,
const std::string& entityId)
{
const Vec3fp normalizedTarget(target.x, static_cast<fp>(0.f), target.z);
const bool sameRequestedTarget =
(normalizedTarget - requestedWalkTarget).normSquared() <= TARGET_REPLAN_THRESHOLD_SQUARED;
@@ -119,11 +122,20 @@ void CharacterState::setTarget(
onArrivedCallbackName = std::move(callbackName);
movementBlockedTicks = 0;
navigationRetryTicks = 0;
blockingCharacterId.clear();
pathWaypoints = location.navigationMaps[location.activeNavigationIndex].findPathToNearest(this->position, target);
// В setTarget и forceReplan:
if (location.activeNavigationIndex < 0 ||
location.activeNavigationIndex >= static_cast<int>(location.navigationMaps.size())) {
walkTarget = Vec3fp(position.x, static_cast<fp>(0.f), position.z);
return;
}
pathWaypoints = location.navigationMaps[location.activeNavigationIndex]
.findPathToNearest(this->position, normalizedTarget);
// Сразу целимся во второй элемент (индекс 1), так как индекс 0 — это точка старта
currentWaypointIndex = (pathWaypoints.size() > 1) ? 1 : 0;
currentWaypointIndex = 0;
if (!pathWaypoints.empty()) {
for (Vec3fp& waypoint : pathWaypoints)
{
@@ -143,19 +155,25 @@ void CharacterState::forceReplan(
{
const Vec3fp normalizedTarget(
requestedWalkTarget.x, static_cast<fp>(0.f), requestedWalkTarget.z);
pathWaypoints = location.navigationMaps[location.activeNavigationIndex].findPathToNearest(this->position, normalizedTarget);
currentWaypointIndex = 0;
if (location.activeNavigationIndex < 0 ||
location.activeNavigationIndex >= static_cast<int>(location.navigationMaps.size())) {
walkTarget = Vec3fp(position.x, static_cast<fp>(0.f), position.z);
return;
}
pathWaypoints = location.navigationMaps[location.activeNavigationIndex]
.findPathToNearest(this->position, normalizedTarget);
currentWaypointIndex = (pathWaypoints.size() > 1) ? 1 : 0;
movementBlockedTicks = 0;
navigationRetryTicks = 0;
if (!pathWaypoints.empty()) {
for (Vec3fp& waypoint : pathWaypoints)
{
for (Vec3fp& waypoint : pathWaypoints) {
waypoint.y = static_cast<fp>(0.f);
}
walkTarget = pathWaypoints.back();
blockingCharacterId.clear();
return;
}
@@ -237,121 +255,110 @@ CharacterUpdateEvents CharacterState::update(
battle_state = 0;
} else {
battle_state = 1;
velocity = Vec2fp(fp(0), fp(0));
}
}
const Vec3fp activeTarget = getCurrentNavigationTarget();
const Vec3fp lookTarget =
battle_state == 1 && attackTarget ? attackTarget->position : activeTarget;
if (currentState == AnimationState::WALK || currentState == AnimationState::STAND) {
Vec3fp toTarget = activeTarget - position;
toTarget.y = static_cast<fp>(0.f);
const fp distanceSquared = toTarget.normSquared();
if (distanceSquared > kWalkThreshold_SQUARED()) {
fp distance = sqrt(distanceSquared); //FP SQRT
const Vec3fp direction = toTarget / distance;
const fp moveAmount = walkSpeed * static_cast<fp>(deltaMs) / static_cast < fp>(1000.f);
Vec3fp candidate =
moveAmount >= distance ? activeTarget : position + direction * moveAmount;
candidate.y = static_cast<fp>(0.f);
/*const CharacterMovementObstacle* blocker = nullptr;
if (context.movementObstacles) {
for (const CharacterMovementObstacle& obstacle :
*context.movementObstacles) {
if (obstacle.id == context.characterId) continue;
Eigen::Vector3f startDelta = position - obstacle.position;
Eigen::Vector3f endDelta = candidate - obstacle.position;
startDelta.y() = 0.f;
endDelta.y() = 0.f;
const float minimumDistance =
movementContactRadius + obstacle.radius;
const float minimumDistanceSq =
minimumDistance * minimumDistance;
const Eigen::Vector3f segment = candidate - position;
const float segmentLengthSq =
segment.x() * segment.x() + segment.z() * segment.z();
float t = 0.f;
if (segmentLengthSq > 1e-8f) {
t = -(startDelta.x() * segment.x() +
startDelta.z() * segment.z()) / segmentLengthSq;
t = (std::max)(0.f, (std::min)(1.f, t));
}
Eigen::Vector3f closest = startDelta + segment * t;
closest.y() = 0.f;
const bool movingCloser =
endDelta.squaredNorm() + 1e-6f < startDelta.squaredNorm();
if (movingCloser &&
closest.squaredNorm() < minimumDistanceSq) {
blocker = &obstacle;
break;
}
}
}
if (blocker) {
blockingCharacterId = blocker->id;
currentState = AnimationState::STAND;
const bool sameDirection =
blocker->moving &&
blocker->movementDirection.dot(direction) > 0.5f;
if (sameDirection) {
movementBlockedTicks = 0;
} else if (++movementBlockedTicks >= BLOCKED_REPLAN_TICKS) {
if (isPlayer) {
stopInPlace();
} else {
pathWaypoints.clear();
currentWaypointIndex = 0;
walkTarget =
Eigen::Vector3f(position.x(), 0.f, position.z());
movementBlockedTicks = 0;
scheduleNextNavigationRetry();
navigationPausedThisTick = true;
}
}
} else*/{
movedLastTick =
(candidate - position).normSquared() > static_cast<fp>(1e-8f);
position = candidate;
movementBlockedTicks = 0;
blockingCharacterId.clear();
}
targetFacingAngle = fpm::atan2(direction.x, -direction.z);
currentState = AnimationState::WALK;
} else {
currentState = AnimationState::STAND;
const bool completedPath = !pathWaypoints.empty();
const bool hasNextWaypoint =
!pathWaypoints.empty() && currentWaypointIndex + 1 < pathWaypoints.size();
if (hasNextWaypoint) {
preferredVelocity = Vec2fp(fp(0), fp(0));
if (hp > 0.f && battle_state == 0 && !pathWaypoints.empty() &&
(currentState == AnimationState::WALK || currentState == AnimationState::STAND))
{
// Если мы уже достигли промежуточного waypoint'а, сразу переключаемся на следующий в этом же тике
while (!pathWaypoints.empty() && currentWaypointIndex + 1 < pathWaypoints.size()) {
Vec3fp diff = pathWaypoints[currentWaypointIndex] - position;
diff.y = fp(0);
if (diff.normSquared() <= kWalkThreshold_SQUARED()) {
++currentWaypointIndex;
} else {
pathWaypoints.clear();
currentWaypointIndex = 0;
if (completedPath &&
(requestedWalkTarget - position).normSquared() >
TARGET_REPLAN_THRESHOLD_SQUARED &&
!isPlayer)
scheduleNextNavigationRetry();
if (completedPath && !onArrivedCallbackName.empty()) {
events.arrivedCallbackName = onArrivedCallbackName;
onArrivedCallbackName.clear();
}
}
else {
break;
}
}
const Vec3fp currentTarget = getCurrentNavigationTarget();
Vec3fp toTarget = currentTarget - position;
toTarget.y = fp(0);
const fp distanceSquared = toTarget.normSquared();
if (distanceSquared > kWalkThreshold_SQUARED()) {
const fp distance = fpm::sqrt(distanceSquared);
const fp dtSec = static_cast<fp>(deltaMs) / fp(1000);
fp desiredSpeed = distance / dtSec;
if (desiredSpeed > walkSpeed) {
desiredSpeed = walkSpeed;
}
preferredVelocity = Vec2fp(
(toTarget.x / distance) * desiredSpeed,
(toTarget.z / distance) * desiredSpeed
);
// Если стартуем с места — сразу целим поворот на точку,
// а в движении угол скорректирует Фаза 3 по реальному вектору ORCA (newVel)
if (velocity.lengthSq() <= static_cast<fp>(0.05f)) {
targetFacingAngle = fpm::atan2(toTarget.x, -toTarget.z);
}
}
else {
// Дошли до ФИНАЛЬНОЙ точки маршрута
velocity = Vec2fp(fp(0), fp(0));
if (currentState == AnimationState::WALK) {
currentState = AnimationState::STAND;
resetAnim = true;
}
const bool completedPath = !pathWaypoints.empty();
pathWaypoints.clear();
currentWaypointIndex = 0;
if (completedPath &&
(requestedWalkTarget - position).normSquared() > TARGET_REPLAN_THRESHOLD_SQUARED &&
!isPlayer) {
scheduleNextNavigationRetry();
}
if (completedPath && !onArrivedCallbackName.empty()) {
events.arrivedCallbackName = onArrivedCallbackName;
onArrivedCallbackName.clear();
}
if (faceTarget) {
Vec3fp toFace = faceTarget->position - position;
toFace.y = static_cast<fp>(0.f);
const fp distanceToFace = sqrt(toFace.normSquared()); //FP SQRT
toFace.y = fp(0);
const fp distanceToFace = fpm::sqrt(toFace.normSquared());
targetFacingAngle = distanceToFace > static_cast<fp>(1e-3f)
? fpm::atan2(toFace.x / distanceToFace, -toFace.z / distanceToFace)
: facingAngle;
} else if (!faceTargetId.empty()) {
}
else if (!faceTargetId.empty()) {
faceTargetId.clear();
}
}
}
else if (hp > 0.f && battle_state == 0 && pathWaypoints.empty() &&
(currentState == AnimationState::WALK || currentState == AnimationState::STAND))
{
// Маршрута нет — стоим на месте и при необходимости поворачиваемся к faceTarget
velocity = Vec2fp(fp(0), fp(0));
if (currentState == AnimationState::WALK) {
currentState = AnimationState::STAND;
resetAnim = true;
}
if (faceTarget) {
Vec3fp toFace = faceTarget->position - position;
toFace.y = fp(0);
const fp distanceToFace = fpm::sqrt(toFace.normSquared());
targetFacingAngle = distanceToFace > static_cast<fp>(1e-3f)
? fpm::atan2(toFace.x / distanceToFace, -toFace.z / distanceToFace)
: facingAngle;
}
else if (!faceTargetId.empty()) {
faceTargetId.clear();
}
}
if (!isPlayer && !navigationPausedThisTick && pathWaypoints.empty() &&
(requestedWalkTarget - position).normSquared() > TARGET_REPLAN_THRESHOLD_SQUARED &&
!events.navigationRequested) {
@@ -360,7 +367,6 @@ CharacterUpdateEvents CharacterState::update(
events.navigationRequested = true;
events.forceNavigationReplan = true;
events.navigationTarget = requestedWalkTarget;
events.blockingCharacterId = blockingCharacterId;
}
}
@@ -371,9 +377,11 @@ CharacterUpdateEvents CharacterState::update(
resetAnim = true;
attackTargetId.clear();
}
} else if (battle_state == 1 && attackTarget) {
}
else if (battle_state == 1 && attackTarget) {
targetFacingAngle = fpm::atan2(
lookTarget.x - position.x, -(lookTarget.z - position.z));
attackTarget->position.x - position.x, -(attackTarget->position.z - position.z));
if (currentState == AnimationState::STAND || currentState == AnimationState::WALK) {
currentState = AnimationState::STAND_TO_ACTION;
resetAnim = true;
@@ -439,8 +447,8 @@ CharacterUpdateEvents CharacterState::update(
events.attackTargetId = attackTargetId;
events.attackDirection = attackTarget->position - position;
events.attackDirection.y = static_cast<fp>(0.f);
const fp lengthSquared = fpm::sqrt(events.attackDirection.normSquared());
if (lengthSquared > static_cast<fp>(1e-3f * 1e-3f))
const fp lengthSquared = events.attackDirection.normSquared();
if (lengthSquared > static_cast<fp>(0.001f))
{
const fp length = fpm::sqrt(lengthSquared); //FP SQRT
@@ -511,8 +519,7 @@ void CharacterState::save(nlohmann::json& out) const
out["movedLastTick"] = movedLastTick;
out["navigationRetryCooldownTicks"] = navigationRetryCooldownTicks;
out["navigationRetryCooldownIndex"] = navigationRetryCooldownIndex;
if (!blockingCharacterId.empty())
out["blockingCharacterId"] = blockingCharacterId;
out["animationFrame"] = animationFrame;
out["visibleRange"] = static_cast<double>(visibleRange);
if (!onArrivedCallbackName.empty())
@@ -567,8 +574,6 @@ void CharacterState::load(const nlohmann::json& in)
"navigationRetryCooldownIndex", navigationRetryCooldownIndex);
if (!navigationRetryCooldownTicks.empty())
navigationRetryCooldownIndex %= navigationRetryCooldownTicks.size();
blockingCharacterId =
in.value("blockingCharacterId", std::string());
animationFrame = in.value("animationFrame", animationFrame);
onArrivedCallbackName = in.value("onArrivedCallbackName", std::string());
}