Finally restored attacks
This commit is contained in:
1 parent
889c6d465b
commit
0fcf8c4fa5
7 files changed
+689
-232
No files matched your search
+121
-116
@@ -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());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user