Core/AreaTriggers: Implement yaw/pitch/roll rotation
This commit is contained in:
@@ -44,23 +44,37 @@
|
||||
#include "UpdateData.h"
|
||||
#include "ZoneScript.h"
|
||||
#include "advstd.h"
|
||||
#include <G3D/AABox.h>
|
||||
#include <G3D/Box.h>
|
||||
#include <G3D/CollisionDetection.h>
|
||||
#include <G3D/CoordinateFrame.h>
|
||||
#include <G3D/Cylinder.h>
|
||||
#include <G3D/Quat.h>
|
||||
#include <bit>
|
||||
|
||||
struct AreaTrigger::MovementUpdateResult
|
||||
enum class MovementUpdateResult
|
||||
{
|
||||
enum
|
||||
{
|
||||
None,
|
||||
Moved,
|
||||
Arrived
|
||||
} Status;
|
||||
None,
|
||||
Moved,
|
||||
Arrived
|
||||
};
|
||||
|
||||
struct AreaTrigger::MovementUpdateWorldResult
|
||||
{
|
||||
MovementUpdateResult Status;
|
||||
::Position Position;
|
||||
G3D::Quat Rotation;
|
||||
};
|
||||
|
||||
struct AreaTrigger::MovementUpdateLocalResult
|
||||
{
|
||||
MovementUpdateResult Status;
|
||||
G3D::Vector3 Position;
|
||||
float Rotation = 0.0f;
|
||||
G3D::Vector3 Rotation;
|
||||
};
|
||||
|
||||
AreaTrigger::AreaTrigger() : WorldObject(false), MapObject(), _spawnId(0), _aurEff(nullptr),
|
||||
_duration(0), _totalDuration(0), _verticesUpdatePreviousOrientation(std::numeric_limits<float>::infinity()),
|
||||
_duration(0), _totalDuration(0), _verticesUpdatePreviousRotation(),
|
||||
_isRemoved(false), _reachedDestination(true),
|
||||
_areaTriggerCreateProperties(nullptr), _areaTriggerTemplate(nullptr)
|
||||
{
|
||||
@@ -291,12 +305,19 @@ bool AreaTrigger::Create(AreaTriggerCreatePropertiesId areaTriggerCreateProperti
|
||||
|
||||
SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::Facing), _stationaryPosition.GetOrientation());
|
||||
|
||||
UpdateShape();
|
||||
|
||||
AI_Initialize();
|
||||
|
||||
UpdateDynamicShapeFlag();
|
||||
|
||||
// Calculate initial position and rotation
|
||||
{
|
||||
auto [_, movedPos, movedRot] = CalculateWorldPositionAndRotation();
|
||||
Relocate(movedPos);
|
||||
_rotation = { movedRot.x, movedRot.y, movedRot.z, movedRot.w };
|
||||
}
|
||||
|
||||
UpdateShape();
|
||||
|
||||
if (!IsStaticSpawn())
|
||||
{
|
||||
if (!GetMap()->AddToMap(this))
|
||||
@@ -474,6 +495,24 @@ void AreaTrigger::SetSpellVisual(SpellCastVisual const& visual)
|
||||
SetUpdateFieldValue(spellVisualMutator.ModifyValue(&UF::SpellCastVisual::ScriptVisualID), visual.ScriptVisualID);
|
||||
}
|
||||
|
||||
void AreaTrigger::SetRollPitchYaw(float roll, float pitch, float yaw,
|
||||
Optional<float> targetRoll, Optional<float> targetPitch, Optional<float> targetYaw)
|
||||
{
|
||||
auto areaTriggerData = m_values.ModifyValue(&AreaTrigger::m_areaTriggerData);
|
||||
|
||||
SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::RollPitchYaw), { roll, pitch, yaw });
|
||||
if (targetRoll && targetPitch && targetYaw)
|
||||
{
|
||||
SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::TargetRollPitchYaw, 0), { *targetRoll, *targetPitch, *targetYaw });
|
||||
SetAreaTriggerFlag(AreaTriggerFieldFlags::DynamicShape);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveOptionalUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::TargetRollPitchYaw));
|
||||
UpdateDynamicShapeFlag();
|
||||
}
|
||||
}
|
||||
|
||||
void AreaTrigger::SetDuration(int32 newDuration)
|
||||
{
|
||||
_duration = newDuration;
|
||||
@@ -735,6 +774,11 @@ void AreaTrigger::SearchUnitInSphere(UF::AreaTriggerSphere const& sphere, std::v
|
||||
float radius = G3D::lerp(sphere.Radius, sphere.RadiusTarget, progress) * scale;
|
||||
|
||||
SearchUnits(targetList, radius, true);
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [this, radius](Unit const* unit) -> bool
|
||||
{
|
||||
return !unit->IsWithinDist(this, radius);
|
||||
});
|
||||
}
|
||||
|
||||
void AreaTrigger::SearchUnitInBox(UF::AreaTriggerBox const& box, std::vector<Unit*>& targetList)
|
||||
@@ -747,15 +791,27 @@ void AreaTrigger::SearchUnitInBox(UF::AreaTriggerBox const& box, std::vector<Uni
|
||||
float extentsX = G3D::lerp(box.Extents->Pos.GetPositionX(), box.ExtentsTarget->Pos.GetPositionX(), progress) * scale;
|
||||
float extentsY = G3D::lerp(box.Extents->Pos.GetPositionY(), box.ExtentsTarget->Pos.GetPositionY(), progress) * scale;
|
||||
float extentsZ = G3D::lerp(box.Extents->Pos.GetPositionZ(), box.ExtentsTarget->Pos.GetPositionZ(), progress) * scale;
|
||||
float radius = std::sqrt(extentsX * extentsX + extentsY * extentsY);
|
||||
float radius = std::sqrt(extentsX * extentsX + extentsY * extentsY + extentsZ * extentsZ);
|
||||
|
||||
SearchUnits(targetList, radius, false);
|
||||
|
||||
Position const& boxCenter = GetPosition();
|
||||
Trinity::Containers::EraseIf(targetList, [boxCenter, extentsX, extentsY, extentsZ](Unit const* unit) -> bool
|
||||
if (targetList.empty())
|
||||
return;
|
||||
|
||||
G3D::Box rotatedBox = G3D::CoordinateFrame(G3D::Quat(_rotation.x, _rotation.y, _rotation.z, _rotation.w), PositionToVector3(GetPosition()))
|
||||
.toWorldSpace(G3D::AABox({ -extentsX, -extentsY, -extentsZ / 2 }, { extentsX, extentsY, extentsZ / 2 }));
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [&rotatedBox](Unit const* unit) -> bool
|
||||
{
|
||||
return !unit->IsWithinBox(boxCenter, extentsX, extentsY, extentsZ / 2);
|
||||
return !rotatedBox.contains(PositionToVector3(*unit));
|
||||
});
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
// DebugVisualizeShape
|
||||
if (Player* caster = Object::ToPlayer(GetCaster()); caster && caster->isDebugAreaTriggers)
|
||||
for (int32 corner = 0; corner < 8; ++corner)
|
||||
caster->SummonCreature(1, Vector3ToPosition(rotatedBox.corner(corner)), TEMPSUMMON_TIMED_DESPAWN, 50ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AreaTrigger::SearchUnitInPolygon(UF::AreaTriggerPolygon const& polygon, std::vector<Unit*>& targetList)
|
||||
@@ -770,12 +826,28 @@ void AreaTrigger::SearchUnitInPolygon(UF::AreaTriggerPolygon const& polygon, std
|
||||
|
||||
SearchUnits(targetList, GetMaxSearchRadius(), false);
|
||||
|
||||
if (targetList.empty())
|
||||
return;
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [this, minZ, maxZ](Unit const* unit) -> bool
|
||||
{
|
||||
return unit->GetPositionZ() < minZ
|
||||
|| unit->GetPositionZ() > maxZ
|
||||
|| !unit->IsInPolygon2D(*this, _polygonVertices);
|
||||
});
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
// DebugVisualizeShape
|
||||
if (Player* caster = Object::ToPlayer(GetCaster()); caster && caster->isDebugAreaTriggers)
|
||||
{
|
||||
for (Position const& vertex : _polygonVertices)
|
||||
{
|
||||
G3D::Vector3 pos = PositionToVector3(GetPosition()) + PositionToVector3(vertex);
|
||||
caster->SummonCreature(1, Vector3ToPosition(pos + G3D::Vector3(0, 0, -height)), TEMPSUMMON_TIMED_DESPAWN, 50ms);
|
||||
caster->SummonCreature(1, Vector3ToPosition(pos + G3D::Vector3(0, 0, +height)), TEMPSUMMON_TIMED_DESPAWN, 50ms);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AreaTrigger::SearchUnitInCylinder(UF::AreaTriggerCylinder const& cylinder, std::vector<Unit*>& targetList)
|
||||
@@ -790,16 +862,30 @@ void AreaTrigger::SearchUnitInCylinder(UF::AreaTriggerCylinder const& cylinder,
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::HeightIgnoresScale))
|
||||
height *= scale;
|
||||
|
||||
float minZ = GetPositionZ() - height;
|
||||
float maxZ = GetPositionZ() + height;
|
||||
float zOffset = G3D::lerp(cylinder.LocationZOffset, cylinder.LocationZOffsetTarget, progress) * scale;
|
||||
|
||||
SearchUnits(targetList, radius, false);
|
||||
SearchUnits(targetList, std::sqrt(radius * radius + height * height), false);
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [minZ, maxZ](Unit const* unit) -> bool
|
||||
if (targetList.empty())
|
||||
return;
|
||||
|
||||
G3D::Cylinder rotatedCylinder = G3D::CoordinateFrame(G3D::Quat(_rotation.x, _rotation.y, _rotation.z, _rotation.w), PositionToVector3(GetPosition()))
|
||||
.toWorldSpace(G3D::Cylinder(
|
||||
{ 0.0f, 0.0f, 0.0f + zOffset },
|
||||
{ 0.0f, 0.0f, 0.0f + zOffset + height },
|
||||
radius));
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [&rotatedCylinder](Unit const* unit) -> bool
|
||||
{
|
||||
return unit->GetPositionZ() < minZ
|
||||
|| unit->GetPositionZ() > maxZ;
|
||||
return !rotatedCylinder.contains(PositionToVector3(*unit));
|
||||
});
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
// DebugVisualizeShape
|
||||
if (Player* caster = Object::ToPlayer(GetCaster()); caster && caster->isDebugAreaTriggers)
|
||||
for (int32 end = 0; end < 2; ++end)
|
||||
caster->SummonCreature(1, Vector3ToPosition(rotatedCylinder.point(end)), TEMPSUMMON_TIMED_DESPAWN, 50ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AreaTrigger::SearchUnitInDisk(UF::AreaTriggerDisk const& disk, std::vector<Unit*>& targetList)
|
||||
@@ -815,15 +901,33 @@ void AreaTrigger::SearchUnitInDisk(UF::AreaTriggerDisk const& disk, std::vector<
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::HeightIgnoresScale))
|
||||
height *= scale;
|
||||
|
||||
float minZ = GetPositionZ() - height;
|
||||
float maxZ = GetPositionZ() + height;
|
||||
float zOffset = G3D::lerp(disk.LocationZOffset, disk.LocationZOffsetTarget, progress) * scale;
|
||||
|
||||
SearchUnits(targetList, outerRadius, false);
|
||||
SearchUnits(targetList, std::sqrt(outerRadius * outerRadius + height * height), false);
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [this, innerRadius, minZ, maxZ](Unit const* unit) -> bool
|
||||
if (targetList.empty())
|
||||
return;
|
||||
|
||||
G3D::Cylinder innerCylinder = G3D::CoordinateFrame(G3D::Quat(_rotation.x, _rotation.y, _rotation.z, _rotation.w), PositionToVector3(GetPosition()))
|
||||
.toWorldSpace(G3D::Cylinder(
|
||||
{ 0.0f, 0.0f, 0.0f + zOffset },
|
||||
{ 0.0f, 0.0f, 0.0f + zOffset + height },
|
||||
innerRadius));
|
||||
|
||||
G3D::Cylinder outerCylinder(innerCylinder.point(0), innerCylinder.point(1), outerRadius);
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [&innerCylinder, &outerCylinder](Unit const* unit) -> bool
|
||||
{
|
||||
return unit->IsInDist2d(this, innerRadius) || unit->GetPositionZ() < minZ || unit->GetPositionZ() > maxZ;
|
||||
G3D::Vector3 position = PositionToVector3(*unit);
|
||||
return innerCylinder.contains(position) || !outerCylinder.contains(position);
|
||||
});
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
// DebugVisualizeShape
|
||||
if (Player* caster = Object::ToPlayer(GetCaster()); caster && caster->isDebugAreaTriggers)
|
||||
for (int32 end = 0; end < 2; ++end)
|
||||
caster->SummonCreature(1, Vector3ToPosition(innerCylinder.point(end)), TEMPSUMMON_TIMED_DESPAWN, 50ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AreaTrigger::SearchUnitInBoundedPlane(UF::AreaTriggerBoundedPlane const& boundedPlane, std::vector<Unit*>& targetList)
|
||||
@@ -833,17 +937,37 @@ void AreaTrigger::SearchUnitInBoundedPlane(UF::AreaTriggerBoundedPlane const& bo
|
||||
progress = sDB2Manager.GetCurveValueAt(m_areaTriggerData->MorphCurveId, progress);
|
||||
|
||||
float scale = CalcCurrentScale();
|
||||
float extentsX = G3D::lerp(boundedPlane.ExtentsX, boundedPlane.ExtentsTargetX, progress) * scale;
|
||||
float extentsY = G3D::lerp(boundedPlane.ExtentsY, boundedPlane.ExtentsTargetY, progress) * scale;
|
||||
float radius = std::sqrt(extentsX * extentsX + extentsY * extentsY);
|
||||
float extentsY = G3D::lerp(boundedPlane.ExtentsX, boundedPlane.ExtentsTargetX, progress) * scale;
|
||||
float extentsZ = G3D::lerp(boundedPlane.ExtentsY, boundedPlane.ExtentsTargetY, progress) * scale;
|
||||
float radius = std::sqrt(extentsY * extentsY + extentsZ * extentsZ);
|
||||
|
||||
SearchUnits(targetList, radius, false);
|
||||
|
||||
Position const& boxCenter = GetPosition();
|
||||
Trinity::Containers::EraseIf(targetList, [boxCenter, extentsX, extentsY](Unit const* unit) -> bool
|
||||
if (targetList.empty())
|
||||
return;
|
||||
|
||||
G3D::CoordinateFrame coordinateFrame = G3D::CoordinateFrame(G3D::Quat(_rotation.x, _rotation.y, _rotation.z, _rotation.w), PositionToVector3(GetPosition()));
|
||||
|
||||
G3D::Vector3 corners[4] =
|
||||
{
|
||||
coordinateFrame.pointToWorldSpace(G3D::Vector3(0.0f, -extentsY, -extentsZ)),
|
||||
coordinateFrame.pointToWorldSpace(G3D::Vector3(0.0f, extentsY, -extentsZ)),
|
||||
coordinateFrame.pointToWorldSpace(G3D::Vector3(0.0f, -extentsY, extentsZ)),
|
||||
coordinateFrame.pointToWorldSpace(G3D::Vector3(0.0f, extentsY, extentsZ))
|
||||
};
|
||||
|
||||
Trinity::Containers::EraseIf(targetList, [&corners](Unit const* unit) -> bool
|
||||
{
|
||||
return !unit->IsWithinBox(boxCenter, extentsX, extentsY, MAP_SIZE);
|
||||
G3D::Vector3 closestPoint = G3D::CollisionDetection::closestPointToRectangle(corners[0], corners[1], corners[2], corners[3], PositionToVector3(*unit));
|
||||
return !unit->IsInDist(closestPoint.x, closestPoint.y, closestPoint.z, unit->GetCombatReach());
|
||||
});
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
// DebugVisualizeShape
|
||||
if (Player* caster = Object::ToPlayer(GetCaster()); caster && caster->isDebugAreaTriggers)
|
||||
for (G3D::Vector3 const& corner : corners)
|
||||
caster->SummonCreature(1, Vector3ToPosition(corner), TEMPSUMMON_TIMED_DESPAWN, 50ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AreaTrigger::HandleUnitEnterExit(std::vector<Unit*> const& newTargetList, AreaTriggerExitReason exitMode)
|
||||
@@ -1056,10 +1180,9 @@ float AreaTrigger::GetMaxSearchRadius() const
|
||||
void AreaTrigger::UpdatePolygonVertices()
|
||||
{
|
||||
UF::AreaTriggerPolygon const* shape = m_areaTriggerData->ShapeData.Get<UF::AreaTriggerPolygon>();
|
||||
float newOrientation = GetOrientation();
|
||||
|
||||
// No need to recalculate, orientation didn't change
|
||||
if (G3D::fuzzyEq(_verticesUpdatePreviousOrientation, newOrientation) && shape->VerticesTarget.empty())
|
||||
if (_verticesUpdatePreviousRotation == _rotation && shape->VerticesTarget.empty())
|
||||
return;
|
||||
|
||||
_polygonVertices.assign(shape->Vertices.begin(), shape->Vertices.end());
|
||||
@@ -1079,18 +1202,16 @@ void AreaTrigger::UpdatePolygonVertices()
|
||||
}
|
||||
}
|
||||
|
||||
float angleSin = std::sin(newOrientation);
|
||||
float angleCos = std::cos(newOrientation);
|
||||
G3D::Matrix3 rotationMatrix(G3D::Quat(_rotation.x, _rotation.y, _rotation.z, _rotation.w));
|
||||
|
||||
// This is needed to rotate the vertices, following orientation
|
||||
for (Position& vertice : _polygonVertices)
|
||||
{
|
||||
float x = vertice.GetPositionX() * angleCos - vertice.GetPositionY() * angleSin;
|
||||
float y = vertice.GetPositionY() * angleCos + vertice.GetPositionX() * angleSin;
|
||||
vertice.Relocate(x, y);
|
||||
G3D::Vector3 transformed = rotationMatrix * PositionToVector3(vertice);
|
||||
vertice.Relocate(transformed.x, transformed.y);
|
||||
}
|
||||
|
||||
_verticesUpdatePreviousOrientation = newOrientation;
|
||||
_verticesUpdatePreviousRotation = _rotation;
|
||||
}
|
||||
|
||||
bool AreaTrigger::HasOverridePosition() const
|
||||
@@ -1214,44 +1335,24 @@ void AreaTrigger::UndoActions(Unit* unit)
|
||||
|
||||
void AreaTrigger::UpdatePositionAndRotation()
|
||||
{
|
||||
Position oldPosition = GetPosition();
|
||||
Position newPosition;
|
||||
float oldRotation = GetOrientation();
|
||||
auto [movementUpdateResult, position, rotation] = CalculateLocalPositionAndRotation();
|
||||
auto [movementUpdateResult, newPosition, newRotation] = CalculateWorldPositionAndRotation();
|
||||
|
||||
if (HasAreaTriggerFlag(AreaTriggerFieldFlags::Attached))
|
||||
{
|
||||
if (Unit* target = GetTarget())
|
||||
{
|
||||
RelocateStationaryPosition(target->GetPosition());
|
||||
|
||||
newPosition = target->GetPositionWithOffset(Vector3ToPosition(position));
|
||||
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::AbsoluteOrientation))
|
||||
rotation += target->GetOrientation();
|
||||
|
||||
if (movementUpdateResult == MovementUpdateResult::None)
|
||||
movementUpdateResult = MovementUpdateResult::Moved;
|
||||
}
|
||||
}
|
||||
else if (TransportBase const* transport = GetTransport())
|
||||
{
|
||||
newPosition = transport->GetPositionWithOffset(Vector3ToPosition(position));
|
||||
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::AbsoluteOrientation))
|
||||
rotation += transport->GetTransportOrientation();
|
||||
}
|
||||
else
|
||||
newPosition = GetMovementOrigin().GetPositionWithOffset(Vector3ToPosition(position));
|
||||
Position oldPosition = GetPosition();
|
||||
QuaternionData oldRotation = std::exchange(_rotation, { newRotation.x, newRotation.y, newRotation.z, newRotation.w });
|
||||
|
||||
if (newPosition != oldPosition)
|
||||
{
|
||||
GetMap()->AreaTriggerRelocation(this, newPosition.GetPositionX(), newPosition.GetPositionY(), newPosition.GetPositionZ(), rotation);
|
||||
GetMap()->AreaTriggerRelocation(this, newPosition.GetPositionX(), newPosition.GetPositionY(), newPosition.GetPositionZ(), newPosition.GetOrientation());
|
||||
}
|
||||
else if (!G3D::fuzzyEq(rotation, oldRotation))
|
||||
else
|
||||
{
|
||||
SetOrientation(rotation);
|
||||
UpdateShape();
|
||||
SetOrientation(newPosition.GetOrientation());
|
||||
if (oldRotation != _rotation)
|
||||
UpdateShape();
|
||||
}
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
@@ -1268,19 +1369,69 @@ void AreaTrigger::UpdatePositionAndRotation()
|
||||
}
|
||||
}
|
||||
|
||||
AreaTrigger::MovementUpdateWorldResult AreaTrigger::CalculateWorldPositionAndRotation() const
|
||||
{
|
||||
auto [status, localPosition, localRotation] = CalculateLocalPositionAndRotation();
|
||||
MovementUpdateWorldResult worldResult {
|
||||
.Status = status,
|
||||
.Position = Vector3ToPosition(localPosition),
|
||||
.Rotation = G3D::Quat::fromYPRAngles(localRotation.z, localRotation.y, localRotation.x)
|
||||
};
|
||||
|
||||
if (HasAreaTriggerFlag(AreaTriggerFieldFlags::Attached))
|
||||
{
|
||||
if (Unit* target = GetTarget())
|
||||
{
|
||||
worldResult.Position = target->GetPositionWithOffset(worldResult.Position);
|
||||
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::AbsoluteOrientation))
|
||||
worldResult.Rotation = G3D::Quat::fromAxisAngleRotation(G3D::Vector3::unitZ(), target->GetOrientation()) * worldResult.Rotation;
|
||||
|
||||
if (worldResult.Status == MovementUpdateResult::None)
|
||||
worldResult.Status = MovementUpdateResult::Moved;
|
||||
}
|
||||
}
|
||||
else if (TransportBase const* transport = GetTransport())
|
||||
{
|
||||
worldResult.Position = transport->GetPositionWithOffset(worldResult.Position);
|
||||
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::AbsoluteOrientation))
|
||||
worldResult.Rotation = G3D::Quat::fromAxisAngleRotation(G3D::Vector3::unitZ(), transport->GetTransportOrientation()) * worldResult.Rotation;
|
||||
}
|
||||
else
|
||||
worldResult.Position = GetMovementOrigin().GetPositionWithOffset(worldResult.Position);
|
||||
|
||||
worldResult.Rotation.toYPRAngles(localRotation.z, localRotation.y, localRotation.x);
|
||||
worldResult.Position.SetOrientation(localRotation.z);
|
||||
|
||||
return worldResult;
|
||||
}
|
||||
|
||||
struct AreaTriggerPositionAndRotationCalcVisitor
|
||||
{
|
||||
AreaTrigger::MovementUpdateResult operator()(UF::AreaTriggerSplineCalculator const&) const { return at->CalculateLocalSplinePositionAndRotation(); }
|
||||
AreaTrigger::MovementUpdateResult operator()(UF::AreaTriggerOrbit const& orbit) const { return at->CalculateLocalOrbitPositionAndRotation(orbit); }
|
||||
AreaTrigger::MovementUpdateResult operator()(std::monostate) const { return at->CalculateLocalStationaryPositionAndRotation(); }
|
||||
AreaTrigger::MovementUpdateResult operator()(UF::AreaTriggerMovementScript const& /*script*/) const { ASSERT(false, "AreaTriggerMovementScript is not implemented"); }
|
||||
AreaTrigger::MovementUpdateLocalResult operator()(UF::AreaTriggerSplineCalculator const&) const { return at->CalculateLocalSplinePositionAndRotation(); }
|
||||
AreaTrigger::MovementUpdateLocalResult operator()(UF::AreaTriggerOrbit const& orbit) const { return at->CalculateLocalOrbitPositionAndRotation(orbit); }
|
||||
AreaTrigger::MovementUpdateLocalResult operator()(std::monostate) const { return at->CalculateLocalStationaryPositionAndRotation(); }
|
||||
AreaTrigger::MovementUpdateLocalResult operator()(UF::AreaTriggerMovementScript const& /*script*/) const { ASSERT(false, "AreaTriggerMovementScript is not implemented"); }
|
||||
|
||||
AreaTrigger const* at;
|
||||
};
|
||||
|
||||
AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalPositionAndRotation() const
|
||||
AreaTrigger::MovementUpdateLocalResult AreaTrigger::CalculateLocalPositionAndRotation() const
|
||||
{
|
||||
MovementUpdateResult result = m_areaTriggerData->PathData.Visit(AreaTriggerPositionAndRotationCalcVisitor{ this });
|
||||
MovementUpdateLocalResult result = m_areaTriggerData->PathData.Visit(AreaTriggerPositionAndRotationCalcVisitor{ this });
|
||||
|
||||
if (m_areaTriggerData->TargetRollPitchYaw.has_value())
|
||||
{
|
||||
float progress = GetOverrideCurveProgress(m_areaTriggerData->OverrideFacingCurve, m_areaTriggerData->TimeToTargetFacing);
|
||||
if (m_areaTriggerData->MorphCurveId)
|
||||
progress = sDB2Manager.GetCurveValueAt(m_areaTriggerData->MorphCurveId, progress);
|
||||
|
||||
result.Rotation += PositionToVector3(m_areaTriggerData->RollPitchYaw->Pos)
|
||||
.lerp(PositionToVector3(m_areaTriggerData->TargetRollPitchYaw->Pos), progress);
|
||||
}
|
||||
else
|
||||
result.Rotation += PositionToVector3(m_areaTriggerData->RollPitchYaw->Pos);
|
||||
|
||||
if (HasOverridePosition())
|
||||
{
|
||||
@@ -1411,7 +1562,7 @@ void AreaTrigger::InitOrbit(AreaTriggerOrbitInfo const& orbit, Optional<float> o
|
||||
SetUpdateFieldValue(pathData.ModifyValue(&UF::AreaTriggerOrbit::Center), center);
|
||||
}
|
||||
|
||||
AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalSplinePositionAndRotation() const
|
||||
AreaTrigger::MovementUpdateLocalResult AreaTrigger::CalculateLocalSplinePositionAndRotation() const
|
||||
{
|
||||
float currentTimePercent = std::clamp(float(GetElapsedTimeForMovement()) / float(GetTimeToTarget()), 0.0f, 1.0f);
|
||||
bool reachedDestination = currentTimePercent >= 1.0f;
|
||||
@@ -1449,13 +1600,30 @@ AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalSplinePositionAndRo
|
||||
orientation += std::atan2(derivative.y, derivative.x);
|
||||
}
|
||||
|
||||
return { .Status = reachedDestination ? MovementUpdateResult::Arrived : MovementUpdateResult::Moved, .Position = currentPosition, .Rotation = orientation };
|
||||
return {
|
||||
.Status = reachedDestination ? MovementUpdateResult::Arrived : MovementUpdateResult::Moved,
|
||||
.Position = currentPosition,
|
||||
.Rotation = G3D::Vector3(0.0f, 0.0f, orientation)
|
||||
};
|
||||
}
|
||||
|
||||
AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalOrbitPositionAndRotation(UF::AreaTriggerOrbit const& orbit) const
|
||||
AreaTrigger::MovementUpdateLocalResult AreaTrigger::CalculateLocalOrbitPositionAndRotation(UF::AreaTriggerOrbit const& orbit) const
|
||||
{
|
||||
// AreaTrigger make exactly "Duration / TimeToTarget" loops during his life time
|
||||
float pathProgress = float(GetElapsedTimeForMovement() + *orbit.ExtraTimeForBlending) / float(GetTimeToTarget());
|
||||
uint32 movementTime = GetElapsedTimeForMovement();
|
||||
uint32 timeToTarget = GetTimeToTarget() + orbit.ExtraTimeForBlending;
|
||||
bool firstLoop = true;
|
||||
if (HasAreaTriggerFlag(AreaTriggerFieldFlags::CanLoop) && timeToTarget)
|
||||
{
|
||||
// remove ExtraTimeForBlending if not on first loop
|
||||
if (movementTime > timeToTarget)
|
||||
{
|
||||
timeToTarget -= orbit.ExtraTimeForBlending;
|
||||
movementTime = (movementTime - orbit.ExtraTimeForBlending) % timeToTarget;
|
||||
firstLoop = false;
|
||||
}
|
||||
}
|
||||
|
||||
float pathProgress = float(movementTime) / float(timeToTarget);
|
||||
if (m_areaTriggerData->MoveCurveId)
|
||||
pathProgress = sDB2Manager.GetCurveValueAt(m_areaTriggerData->MoveCurveId, pathProgress);
|
||||
|
||||
@@ -1464,24 +1632,26 @@ AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalOrbitPositionAndRot
|
||||
pathProgress = std::min(1.f, pathProgress);
|
||||
|
||||
float radius = orbit.Radius;
|
||||
if (pathProgress <= 1.0f && G3D::fuzzyNe(orbit.BlendFromRadius, radius))
|
||||
if (firstLoop && G3D::fuzzyNe(orbit.BlendFromRadius, radius))
|
||||
{
|
||||
float blendCurve = (orbit.BlendFromRadius - radius) / radius;
|
||||
RoundToInterval(blendCurve, 1.f, 4.f);
|
||||
float blendProgress = std::min(1.f, pathProgress / blendCurve * 0.63661975f);
|
||||
float blendCurve = std::clamp((orbit.BlendFromRadius - radius) / radius, 1.0f, 4.0f);
|
||||
float blendProgress = std::clamp(std::abs(4.0f * pathProgress / blendCurve), 0.0f, 1.0f);
|
||||
radius = G3D::lerp(orbit.BlendFromRadius, radius, blendProgress);
|
||||
}
|
||||
|
||||
// Adapt Path progress depending of circle direction
|
||||
if (!orbit.CounterClockwise)
|
||||
pathProgress *= -1;
|
||||
float angle = 2.0f * float(M_PI) * pathProgress;
|
||||
|
||||
// Adapt angle depending of circle direction
|
||||
if (!orbit.CounterClockwise)
|
||||
angle *= -1;
|
||||
|
||||
angle += orbit.InitialAngle;
|
||||
|
||||
float angle = orbit.InitialAngle + 2.f * float(M_PI) * pathProgress;
|
||||
G3D::Vector3 position =
|
||||
{
|
||||
radius * std::cos(angle),
|
||||
radius * std::sin(angle),
|
||||
*m_areaTriggerData->ZOffset
|
||||
m_areaTriggerData->ZOffset
|
||||
};
|
||||
|
||||
float orientation = 0.0f;
|
||||
@@ -1495,10 +1665,14 @@ AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalOrbitPositionAndRot
|
||||
orientation += orbit.CounterClockwise ? float(M_PI_4) : -float(M_PI_4);
|
||||
}
|
||||
|
||||
return { .Status = MovementUpdateResult::Moved, .Position = position, .Rotation = orientation };
|
||||
return {
|
||||
.Status = MovementUpdateResult::Moved,
|
||||
.Position = position,
|
||||
.Rotation = G3D::Vector3(0.0f, 0.0f, orientation)
|
||||
};
|
||||
}
|
||||
|
||||
AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalStationaryPositionAndRotation() const
|
||||
AreaTrigger::MovementUpdateLocalResult AreaTrigger::CalculateLocalStationaryPositionAndRotation() const
|
||||
{
|
||||
float orientation = 0.0f;
|
||||
if (m_areaTriggerData->FacingCurveId)
|
||||
@@ -1508,7 +1682,11 @@ AreaTrigger::MovementUpdateResult AreaTrigger::CalculateLocalStationaryPositionA
|
||||
if (!HasAreaTriggerFlag(AreaTriggerFieldFlags::AbsoluteOrientation))
|
||||
orientation += *m_areaTriggerData->Facing;
|
||||
|
||||
return { .Status = MovementUpdateResult::None, .Position = G3D::Vector3::zero(), .Rotation = orientation };
|
||||
return {
|
||||
.Status = MovementUpdateResult::None,
|
||||
.Position = G3D::Vector3::zero(),
|
||||
.Rotation = G3D::Vector3(0.0f, 0.0f, orientation)
|
||||
};
|
||||
}
|
||||
|
||||
struct AreaTriggerMovementOriginVisitor
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "Object.h"
|
||||
#include "GridObject.h"
|
||||
#include "MapObject.h"
|
||||
#include "QuaternionData.h"
|
||||
#include "AreaTriggerTemplate.h"
|
||||
|
||||
class AuraEffect;
|
||||
@@ -174,6 +175,9 @@ class TC_GAME_API AreaTrigger final : public WorldObject, public GridObject<Area
|
||||
|
||||
void SetSpellVisual(SpellCastVisual const& visual);
|
||||
|
||||
void SetRollPitchYaw(float roll, float pitch, float yaw,
|
||||
Optional<float> targetRoll = {}, Optional<float> targetPitch = {}, Optional<float> targetYaw = {});
|
||||
|
||||
int32 GetDuration() const { return _duration; }
|
||||
int32 GetTotalDuration() const { return _totalDuration; }
|
||||
void SetDuration(int32 newDuration);
|
||||
@@ -254,14 +258,16 @@ class TC_GAME_API AreaTrigger final : public WorldObject, public GridObject<Area
|
||||
|
||||
void UpdatePositionAndRotation();
|
||||
|
||||
struct MovementUpdateResult;
|
||||
struct MovementUpdateWorldResult;
|
||||
MovementUpdateWorldResult CalculateWorldPositionAndRotation() const;
|
||||
|
||||
MovementUpdateResult CalculateLocalPositionAndRotation() const;
|
||||
struct MovementUpdateLocalResult;
|
||||
MovementUpdateLocalResult CalculateLocalPositionAndRotation() const;
|
||||
|
||||
friend struct AreaTriggerPositionAndRotationCalcVisitor;
|
||||
MovementUpdateResult CalculateLocalSplinePositionAndRotation() const;
|
||||
MovementUpdateResult CalculateLocalOrbitPositionAndRotation(UF::AreaTriggerOrbit const& orbit) const;
|
||||
MovementUpdateResult CalculateLocalStationaryPositionAndRotation() const;
|
||||
MovementUpdateLocalResult CalculateLocalSplinePositionAndRotation() const;
|
||||
MovementUpdateLocalResult CalculateLocalOrbitPositionAndRotation(UF::AreaTriggerOrbit const& orbit) const;
|
||||
MovementUpdateLocalResult CalculateLocalStationaryPositionAndRotation() const;
|
||||
|
||||
Position const& GetMovementOrigin() const;
|
||||
|
||||
@@ -279,9 +285,10 @@ class TC_GAME_API AreaTrigger final : public WorldObject, public GridObject<Area
|
||||
AuraEffect const* _aurEff;
|
||||
|
||||
Position _stationaryPosition;
|
||||
QuaternionData _rotation;
|
||||
int32 _duration;
|
||||
int32 _totalDuration;
|
||||
float _verticesUpdatePreviousOrientation;
|
||||
QuaternionData _verticesUpdatePreviousRotation;
|
||||
bool _isRemoved;
|
||||
|
||||
std::vector<Position> _polygonVertices;
|
||||
|
||||
@@ -548,7 +548,7 @@ struct at_ymiron_the_fallen_king_bane : AreaTriggerAI
|
||||
return;
|
||||
|
||||
float radius = at->GetExactDist(caster);
|
||||
float angle = at->GetOrientation();
|
||||
float angle = caster->GetAbsoluteAngle(at);
|
||||
|
||||
AreaTriggerOrbitInfo orbitInfo;
|
||||
orbitInfo.CounterClockwise = false;
|
||||
|
||||
Reference in New Issue
Block a user