aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities/AreaTrigger
diff options
context:
space:
mode:
authorTraesh <Traesh@users.noreply.github.com>2017-01-29 00:00:43 +0100
committerjoschiwald <joschiwald.trinity@gmail.com>2017-01-29 00:00:43 +0100
commit6ab3877c91c9440b81cf1c7c66a1275ee04ea26a (patch)
tree9930d19512eef247d1c0a23284caa446ceef21c8 /src/server/game/Entities/AreaTrigger
parent5818dd364ca6a8b0cb068710291adf26b571aa23 (diff)
Core/Entities: Basic AreaTrigger System (#18035)
* Implemented AreaTrigger Templates * Implemented AreaTrigger Splines * Implemented SPELL_AURA_AREA_TRIGGER * and many more
Diffstat (limited to 'src/server/game/Entities/AreaTrigger')
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.cpp610
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.h77
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.cpp58
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h187
4 files changed, 913 insertions, 19 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
index 8de274cb4bd..4393d15a401 100644
--- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
+++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
@@ -16,18 +16,33 @@
*/
#include "AreaTrigger.h"
+#include "AreaTriggerDataStore.h"
+#include "AreaTriggerPackets.h"
+#include "AreaTriggerTemplate.h"
+#include "CellImpl.h"
+#include "Chat.h"
#include "DB2Stores.h"
+#include "GridNotifiersImpl.h"
+#include "Language.h"
#include "Log.h"
+#include "Object.h"
+#include "ObjectAccessor.h"
+#include "Player.h"
+#include "ScriptMgr.h"
#include "SpellInfo.h"
+#include "Transport.h"
#include "Unit.h"
#include "UpdateData.h"
-AreaTrigger::AreaTrigger() : WorldObject(false), _duration(0), _spellXSpellVisualId(0)
+AreaTrigger::AreaTrigger() : WorldObject(false), MapObject(),
+ _duration(0), _totalDuration(0), _timeSinceCreated(0), _previousCheckOrientation(std::numeric_limits<float>::infinity()),
+ _isRemoved(false), _reachedDestination(true), _lastSplineIndex(0), _movementTime(0),
+ _areaTriggerMiscTemplate(nullptr)
{
m_objectType |= TYPEMASK_AREATRIGGER;
m_objectTypeId = TYPEID_AREATRIGGER;
- m_updateFlag = UPDATEFLAG_STATIONARY_POSITION;
+ m_updateFlag = UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_AREATRIGGER;
m_valuesCount = AREATRIGGER_END;
_dynamicValuesCount = AREATRIGGER_DYNAMIC_END;
@@ -57,52 +72,619 @@ void AreaTrigger::RemoveFromWorld()
}
}
-bool AreaTrigger::CreateAreaTrigger(ObjectGuid::LowType guidlow, uint32 triggerEntry, Unit* caster, SpellInfo const* spell, Position const& pos, uint32 spellXSpellVisualId)
+bool AreaTrigger::CreateAreaTrigger(uint32 spellMiscId, Unit* caster, Unit* target, SpellInfo const* spell, Position const& pos, int32 duration, uint32 spellXSpellVisualId, ObjectGuid const& castId /*= ObjectGuid::Empty*/)
{
- _spellXSpellVisualId = spellXSpellVisualId;
+ _targetGuid = target ? target->GetGUID() : ObjectGuid::Empty;
+
SetMap(caster->GetMap());
Relocate(pos);
if (!IsPositionValid())
{
- TC_LOG_ERROR("misc", "AreaTrigger (spell %u) not created. Invalid coordinates (X: %f Y: %f)", spell->Id, GetPositionX(), GetPositionY());
+ TC_LOG_ERROR("entities.areatrigger", "AreaTrigger (spellMiscId %u) not created. Invalid coordinates (X: %f Y: %f)", spellMiscId, GetPositionX(), GetPositionY());
return false;
}
- Object::_Create(ObjectGuid::Create<HighGuid::AreaTrigger>(GetMapId(), triggerEntry, guidlow));
+ _areaTriggerMiscTemplate = sAreaTriggerDataStore->GetAreaTriggerMiscTemplate(spellMiscId);
+ if (!_areaTriggerMiscTemplate)
+ {
+ TC_LOG_ERROR("entities.areatrigger", "AreaTrigger (spellMiscId %u) not created. Invalid areatrigger miscid (%u)", spellMiscId, spellMiscId);
+ return false;
+ }
+
+ Object::_Create(ObjectGuid::Create<HighGuid::AreaTrigger>(GetMapId(), GetTemplate()->Id, caster->GetMap()->GenerateLowGuid<HighGuid::AreaTrigger>()));
SetPhaseMask(caster->GetPhaseMask(), false);
- SetEntry(triggerEntry);
- SetDuration(spell->GetDuration());
- SetObjectScale(1);
+ SetEntry(GetTemplate()->Id);
+ SetDuration(duration);
+
+ SetObjectScale(1.0f);
SetGuidValue(AREATRIGGER_CASTER, caster->GetGUID());
+ SetGuidValue(AREATRIGGER_CREATING_EFFECT_GUID, castId);
+
SetUInt32Value(AREATRIGGER_SPELLID, spell->Id);
SetUInt32Value(AREATRIGGER_SPELL_X_SPELL_VISUAL_ID, spellXSpellVisualId);
- SetUInt32Value(AREATRIGGER_DURATION, spell->GetDuration());
+ uint32 timeToTarget = GetMiscTemplate()->TimeToTarget != 0 ? GetMiscTemplate()->TimeToTarget : GetUInt32Value(AREATRIGGER_DURATION);
+ SetUInt32Value(AREATRIGGER_TIME_TO_TARGET_SCALE, GetMiscTemplate()->TimeToTargetScale != 0 ? GetMiscTemplate()->TimeToTargetScale : GetUInt32Value(AREATRIGGER_DURATION));
+ SetFloatValue(AREATRIGGER_BOUNDS_RADIUS_2D, GetTemplate()->MaxSearchRadius);
+ SetUInt32Value(AREATRIGGER_DECAL_PROPERTIES_ID, GetMiscTemplate()->DecalPropertiesId);
CopyPhaseFrom(caster);
+ if (target && GetTemplate()->HasFlag(AREATRIGGER_FLAG_HAS_ATTACHED))
+ {
+ m_movementInfo.transport.guid = target->GetGUID();
+ }
+
+ UpdateShape();
+
+ if (GetMiscTemplate()->HasSplines())
+ InitSplineOffsets(GetMiscTemplate()->SplinePoints, timeToTarget);
+
+ sScriptMgr->OnAreaTriggerEntityInitialize(this);
+
+ // movement on transport of areatriggers on unit is handled by themself
+ Transport* transport = m_movementInfo.transport.guid.IsEmpty() ? caster->GetTransport() : nullptr;
+ if (transport)
+ {
+ float x, y, z, o;
+ pos.GetPosition(x, y, z, o);
+ transport->CalculatePassengerOffset(x, y, z, &o);
+ m_movementInfo.transport.pos.Relocate(x, y, z, o);
+
+ // This object must be added to transport before adding to map for the client to properly display it
+ transport->AddPassenger(this);
+ }
+
if (!GetMap()->AddToMap(this))
+ {
+ // Returning false will cause the object to be deleted - remove from transport
+ if (transport)
+ transport->RemovePassenger(this);
return false;
+ }
+
+ caster->_RegisterAreaTrigger(this);
+
+ sScriptMgr->OnAreaTriggerEntityCreate(this);
return true;
}
void AreaTrigger::Update(uint32 p_time)
{
- if (GetDuration() > int32(p_time))
- _duration -= p_time;
+ WorldObject::Update(p_time);
+ _timeSinceCreated += p_time;
+
+ if (GetTemplate()->HasFlag(AREATRIGGER_FLAG_HAS_ATTACHED))
+ {
+ if (Unit* target = GetTarget())
+ GetMap()->AreaTriggerRelocation(this, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation());
+ }
else
- Remove(); // expired
+ UpdateSplinePosition(p_time);
- WorldObject::Update(p_time);
+ sScriptMgr->OnAreaTriggerEntityUpdate(this, p_time);
+
+ if (GetDuration() != -1)
+ {
+ if (GetDuration() > int32(p_time))
+ _UpdateDuration(_duration - p_time);
+ else
+ {
+ Remove(); // expired
+ return;
+ }
+ }
+
+ UpdateTargetList();
}
void AreaTrigger::Remove()
{
if (IsInWorld())
{
+ _isRemoved = true;
+
+ if (Unit* caster = GetCaster())
+ caster->_UnregisterAreaTrigger(this);
+
+ // Handle removal of all units, calling OnUnitExit & deleting auras if needed
+ HandleUnitEnterExit({});
+
+ sScriptMgr->OnAreaTriggerEntityRemove(this);
+
RemoveFromWorld();
AddObjectToRemoveList();
}
}
+
+void AreaTrigger::SetDuration(int32 newDuration)
+{
+ _duration = newDuration;
+ _totalDuration = newDuration;
+
+ // negative duration (permanent areatrigger) sent as 0
+ SetUInt32Value(AREATRIGGER_DURATION, std::max(newDuration, 0));
+}
+
+void AreaTrigger::_UpdateDuration(int32 newDuration)
+{
+ _duration = newDuration;
+
+ // should be sent in object create packets only
+ m_uint32Values[AREATRIGGER_DURATION] = _duration;
+}
+
+float AreaTrigger::GetProgress() const
+{
+ return GetTimeSinceCreated() < GetTimeToTargetScale() ? float(GetTimeSinceCreated()) / float(GetTimeToTargetScale()) : 1.0f;
+}
+
+void AreaTrigger::UpdateTargetList()
+{
+ std::list<Unit*> targetList;
+
+ switch (GetTemplate()->Type)
+ {
+ case AREATRIGGER_TYPE_SPHERE:
+ SearchUnitInSphere(targetList);
+ break;
+ case AREATRIGGER_TYPE_BOX:
+ SearchUnitInBox(targetList);
+ break;
+ case AREATRIGGER_TYPE_POLYGON:
+ SearchUnitInPolygon(targetList);
+ break;
+ case AREATRIGGER_TYPE_CYLINDER:
+ SearchUnitInCylinder(targetList);
+ break;
+ default:
+ break;
+ }
+
+ HandleUnitEnterExit(targetList);
+}
+
+void AreaTrigger::SearchUnitInSphere(std::list<Unit*>& targetList)
+{
+ float radius = GetTemplate()->SphereDatas.Radius;
+ if (GetTemplate()->HasFlag(AREATRIGGER_FLAG_HAS_DYNAMIC_SHAPE))
+ {
+ if (GetMiscTemplate()->MorphCurveId)
+ {
+ radius = G3D::lerp(GetTemplate()->SphereDatas.Radius,
+ GetTemplate()->SphereDatas.RadiusTarget,
+ sDB2Manager.GetCurveValueAt(GetMiscTemplate()->MorphCurveId, GetProgress()));
+ }
+ }
+
+ Trinity::AnyUnitInObjectRangeCheck check(this, radius);
+ Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targetList, check);
+ VisitNearbyObject(GetTemplate()->MaxSearchRadius, searcher);
+}
+
+void AreaTrigger::SearchUnitInBox(std::list<Unit*>& targetList)
+{
+ float extentsX = GetTemplate()->BoxDatas.Extents[0];
+ float extentsY = GetTemplate()->BoxDatas.Extents[1];
+ float extentsZ = GetTemplate()->BoxDatas.Extents[2];
+
+ Trinity::AnyUnitInObjectRangeCheck check(this, GetTemplate()->MaxSearchRadius, false);
+ Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targetList, check);
+ VisitNearbyObject(GetTemplate()->MaxSearchRadius, searcher);
+
+ float halfExtentsX = extentsX / 2.0f;
+ float halfExtentsY = extentsY / 2.0f;
+ float halfExtentsZ = extentsZ / 2.0f;
+
+ float minX = GetPositionX() - halfExtentsX;
+ float maxX = GetPositionX() + halfExtentsX;
+
+ float minY = GetPositionY() - halfExtentsY;
+ float maxY = GetPositionY() + halfExtentsY;
+
+ float minZ = GetPositionZ() - halfExtentsZ;
+ float maxZ = GetPositionZ() + halfExtentsZ;
+
+ G3D::AABox const box({ minX, minY, minZ }, { maxX, maxY, maxZ });
+
+ targetList.remove_if([&box](Unit* unit) -> bool
+ {
+ return !box.contains({ unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ() });
+ });
+}
+
+void AreaTrigger::SearchUnitInPolygon(std::list<Unit*>& targetList)
+{
+ Trinity::AnyUnitInObjectRangeCheck check(this, GetTemplate()->MaxSearchRadius, false);
+ Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targetList, check);
+ VisitNearbyObject(GetTemplate()->MaxSearchRadius, searcher);
+
+ float height = GetTemplate()->PolygonDatas.Height;
+ float minZ = GetPositionZ() - height;
+ float maxZ = GetPositionZ() + height;
+
+ targetList.remove_if([this, minZ, maxZ](Unit* unit) -> bool
+ {
+ return !CheckIsInPolygon2D(unit)
+ || unit->GetPositionZ() < minZ
+ || unit->GetPositionZ() > maxZ;
+ });
+}
+
+void AreaTrigger::SearchUnitInCylinder(std::list<Unit*>& targetList)
+{
+ Trinity::AnyUnitInObjectRangeCheck check(this, GetTemplate()->MaxSearchRadius, false);
+ Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targetList, check);
+ VisitNearbyObject(GetTemplate()->MaxSearchRadius, searcher);
+
+ float height = GetTemplate()->CylinderDatas.Height;
+ float minZ = GetPositionZ() - height;
+ float maxZ = GetPositionZ() + height;
+
+ targetList.remove_if([minZ, maxZ](Unit* unit) -> bool
+ {
+ return unit->GetPositionZ() < minZ
+ || unit->GetPositionZ() > maxZ;
+ });
+}
+
+void AreaTrigger::HandleUnitEnterExit(std::list<Unit*> const& newTargetList)
+{
+ GuidUnorderedSet exitUnits = _insideUnits;
+ _insideUnits.clear();
+
+ std::vector<Unit*> enteringUnits;
+
+ for (Unit* unit : newTargetList)
+ {
+ if (exitUnits.erase(unit->GetGUID()) == 0) // erase(key_type) returns number of elements erased
+ enteringUnits.push_back(unit);
+
+ _insideUnits.insert(unit->GetGUID());
+ }
+
+ // Handle after _insideUnits have been reinserted so we can use GetInsideUnits() in hooks
+ for (Unit* unit : enteringUnits)
+ {
+ if (Player* player = unit->ToPlayer())
+ if (player->isDebugAreaTriggers)
+ ChatHandler(player->GetSession()).PSendSysMessage(LANG_DEBUG_AREATRIGGER_ENTERED, GetTemplate()->Id);
+
+ DoActions(unit);
+ sScriptMgr->OnAreaTriggerEntityUnitEnter(this, unit);
+ }
+
+ for (ObjectGuid const& exitUnitGuid : exitUnits)
+ {
+ if (Unit* leavingUnit = ObjectAccessor::GetUnit(*this, exitUnitGuid))
+ {
+ if (Player* player = leavingUnit->ToPlayer())
+ if (player->isDebugAreaTriggers)
+ ChatHandler(player->GetSession()).PSendSysMessage(LANG_DEBUG_AREATRIGGER_LEFT, GetTemplate()->Id);
+
+ UndoActions(leavingUnit);
+ sScriptMgr->OnAreaTriggerEntityUnitExit(this, leavingUnit);
+ }
+ }
+}
+
+AreaTriggerTemplate const* AreaTrigger::GetTemplate() const
+{
+ return _areaTriggerMiscTemplate->Template;
+}
+
+uint32 AreaTrigger::GetScriptId() const
+{
+ return GetTemplate()->ScriptId;
+}
+
+Unit* AreaTrigger::GetCaster() const
+{
+ return ObjectAccessor::GetUnit(*this, GetCasterGuid());
+}
+
+Unit* AreaTrigger::GetTarget() const
+{
+ return ObjectAccessor::GetUnit(*this, _targetGuid);
+}
+
+void AreaTrigger::UpdatePolygonOrientation()
+{
+ float newOrientation = GetOrientation();
+
+ // No need to recalculate, orientation didn't change
+ if (G3D::fuzzyEq(_previousCheckOrientation, newOrientation))
+ return;
+
+ _polygonVertices = GetTemplate()->PolygonVertices;
+
+ float angleSin = std::sin(newOrientation);
+ float angleCos = std::cos(newOrientation);
+
+ // This is needed to rotate the vertices, following orientation
+ for (G3D::Vector2& vertice : _polygonVertices)
+ {
+ float tempX = vertice.x;
+ float tempY = vertice.y;
+
+ vertice.x = tempX * angleCos - tempY * angleSin;
+ vertice.y = tempX * angleSin + tempY * angleCos;
+ }
+
+ _previousCheckOrientation = newOrientation;
+}
+
+bool AreaTrigger::CheckIsInPolygon2D(Position const* pos) const
+{
+ float testX = pos->GetPositionX();
+ float testY = pos->GetPositionY();
+
+ //this method uses the ray tracing algorithm to determine if the point is in the polygon
+ int nPoints = _polygonVertices.size();
+ bool locatedInPolygon = false;
+
+ for (int i = 0; i < nPoints; ++i)
+ {
+ int j = -999;
+
+ //repeat loop for all sets of points
+ if (i == (nPoints - 1))
+ {
+ //if i is the last vertex, let j be the first vertex
+ j = 0;
+ }
+ else
+ {
+ //for all-else, let j=(i+1)th vertex
+ j = i + 1;
+ }
+
+ float vertX_i = GetPositionX() + _polygonVertices[i].x;
+ float vertY_i = GetPositionY() + _polygonVertices[i].y;
+ float vertX_j = GetPositionX() + _polygonVertices[j].x;
+ float vertY_j = GetPositionY() + _polygonVertices[j].y;
+
+ // following statement checks if testPoint.Y is below Y-coord of i-th vertex
+ bool belowLowY = vertY_i > testY;
+ // following statement checks if testPoint.Y is below Y-coord of i+1-th vertex
+ bool belowHighY = vertY_j > testY;
+
+ /* following statement is true if testPoint.Y satisfies either (only one is possible)
+ -->(i).Y < testPoint.Y < (i+1).Y OR
+ -->(i).Y > testPoint.Y > (i+1).Y
+
+ (Note)
+ Both of the conditions indicate that a point is located within the edges of the Y-th coordinate
+ of the (i)-th and the (i+1)- th vertices of the polygon. If neither of the above
+ conditions is satisfied, then it is assured that a semi-infinite horizontal line draw
+ to the right from the testpoint will NOT cross the line that connects vertices i and i+1
+ of the polygon
+ */
+ bool withinYsEdges = belowLowY != belowHighY;
+
+ if (withinYsEdges)
+ {
+ // this is the slope of the line that connects vertices i and i+1 of the polygon
+ float slopeOfLine = (vertX_j - vertX_i) / (vertY_j - vertY_i);
+
+ // this looks up the x-coord of a point lying on the above line, given its y-coord
+ float pointOnLine = (slopeOfLine* (testY - vertY_i)) + vertX_i;
+
+ //checks to see if x-coord of testPoint is smaller than the point on the line with the same y-coord
+ bool isLeftToLine = testX < pointOnLine;
+
+ if (isLeftToLine)
+ {
+ //this statement changes true to false (and vice-versa)
+ locatedInPolygon = !locatedInPolygon;
+ }//end if (isLeftToLine)
+ }//end if (withinYsEdges
+ }
+
+ return locatedInPolygon;
+}
+
+void AreaTrigger::UpdateShape()
+{
+ if (GetTemplate()->IsPolygon())
+ UpdatePolygonOrientation();
+}
+
+bool UnitFitToActionRequirement(Unit* unit, Unit* caster, AreaTriggerActionUserTypes targetType)
+{
+ switch (targetType)
+ {
+ case AREATRIGGER_ACTION_USER_FRIEND:
+ {
+ return caster->IsFriendlyTo(unit);
+ }
+ case AREATRIGGER_ACTION_USER_ENEMY:
+ {
+ return !caster->IsFriendlyTo(unit);
+ }
+ case AREATRIGGER_ACTION_USER_RAID:
+ {
+ return caster->IsInRaidWith(unit);
+ }
+ case AREATRIGGER_ACTION_USER_PARTY:
+ {
+ return caster->IsInPartyWith(unit);
+ }
+ case AREATRIGGER_ACTION_USER_CASTER:
+ {
+ return unit->GetGUID() == caster->GetGUID();
+ }
+ case AREATRIGGER_ACTION_USER_ANY:
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void AreaTrigger::DoActions(Unit* unit)
+{
+ if (Unit* caster = GetCaster())
+ {
+ for (AreaTriggerAction const& action : GetTemplate()->Actions)
+ {
+ if (UnitFitToActionRequirement(unit, caster, action.TargetType))
+ {
+ switch (action.ActionType)
+ {
+ case AREATRIGGER_ACTION_CAST:
+ caster->CastSpell(unit, action.Param, true);
+ break;
+ case AREATRIGGER_ACTION_ADDAURA:
+ caster->AddAura(action.Param, unit);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+void AreaTrigger::UndoActions(Unit* unit)
+{
+ for (AreaTriggerAction const& action : GetTemplate()->Actions)
+ {
+ if (action.ActionType == AREATRIGGER_ACTION_CAST || action.ActionType == AREATRIGGER_ACTION_ADDAURA)
+ unit->RemoveAurasDueToSpell(action.Param, GetCasterGuid());
+ }
+}
+
+void AreaTrigger::InitSplineOffsets(std::vector<G3D::Vector3> splinePoints, uint32 timeToTarget)
+{
+ float angleSin = std::sin(GetOrientation());
+ float angleCos = std::cos(GetOrientation());
+
+ // This is needed to rotate the spline, following caster orientation
+ for (G3D::Vector3& spline : splinePoints)
+ {
+ float tempX = spline.x;
+ float tempY = spline.y;
+ float tempZ = GetPositionZ();
+
+ spline.x = (tempX * angleCos - tempY * angleSin) + GetPositionX();
+ spline.y = (tempX * angleSin + tempY * angleCos) + GetPositionY();
+ UpdateAllowedPositionZ(spline.x, spline.y, tempZ);
+ spline.z += tempZ;
+ }
+
+ InitSplines(splinePoints, timeToTarget);
+}
+
+void AreaTrigger::InitSplines(std::vector<G3D::Vector3> const& splinePoints, uint32 timeToTarget)
+{
+ if (splinePoints.size() < 2)
+ return;
+
+ _movementTime = 0;
+
+ _spline.init_spline(&splinePoints[0], splinePoints.size(), ::Movement::SplineBase::ModeLinear);
+ _spline.initLengths();
+
+ // should be sent in object create packets only
+ m_uint32Values[AREATRIGGER_TIME_TO_TARGET] = timeToTarget;
+
+ if (IsInWorld())
+ {
+ if (_reachedDestination)
+ {
+ WorldPackets::AreaTrigger::AreaTriggerReShape reshape;
+ reshape.TriggerGUID = GetGUID();
+ SendMessageToSet(reshape.Write(), true);
+ }
+
+ WorldPackets::AreaTrigger::AreaTriggerReShape reshape;
+ reshape.TriggerGUID = GetGUID();
+ reshape.AreaTriggerSpline = boost::in_place();
+ reshape.AreaTriggerSpline->ElapsedTimeForMovement = GetElapsedTimeForMovement();
+ reshape.AreaTriggerSpline->TimeToTarget = timeToTarget;
+ reshape.AreaTriggerSpline->Points = splinePoints;
+ SendMessageToSet(reshape.Write(), true);
+ }
+
+ _reachedDestination = false;
+}
+
+void AreaTrigger::UpdateSplinePosition(uint32 diff)
+{
+ if (_reachedDestination)
+ return;
+
+ if (!HasSplines())
+ return;
+
+ _movementTime += diff;
+
+ if (_movementTime >= GetTimeToTarget())
+ {
+ _reachedDestination = true;
+ _lastSplineIndex = int32(_spline.last());
+
+ G3D::Vector3 lastSplinePosition = _spline.getPoint(_lastSplineIndex);
+ GetMap()->AreaTriggerRelocation(this, lastSplinePosition.x, lastSplinePosition.y, lastSplinePosition.z, GetOrientation());
+#ifdef TRINITY_DEBUG
+ DebugVisualizePosition();
+#endif
+
+ sScriptMgr->OnAreaTriggerEntitySplineIndexReached(this, _lastSplineIndex);
+ sScriptMgr->OnAreaTriggerEntityDestinationReached(this);
+ return;
+ }
+
+ float currentTimePercent = float(_movementTime) / float(GetTimeToTarget());
+
+ if (currentTimePercent <= 0.f)
+ return;
+
+ if (GetMiscTemplate()->MoveCurveId)
+ {
+ float progress = sDB2Manager.GetCurveValueAt(GetMiscTemplate()->MoveCurveId, currentTimePercent);
+ if (progress < 0.f || progress > 1.f)
+ {
+ TC_LOG_ERROR("entities.areatrigger", "AreaTrigger (Id: %u, SpellMiscId: %u) has wrong progress (%f) caused by curve calculation (MoveCurveId: %u)",
+ GetTemplate()->Id, GetMiscTemplate()->MiscId, progress, GetMiscTemplate()->MorphCurveId);
+ }
+ else
+ currentTimePercent = progress;
+ }
+
+ int lastPositionIndex = 0;
+ float percentFromLastPoint = 0;
+ _spline.computeIndex(currentTimePercent, lastPositionIndex, percentFromLastPoint);
+
+ G3D::Vector3 currentPosition;
+ _spline.evaluate_percent(lastPositionIndex, percentFromLastPoint, currentPosition);
+
+ float orientation = GetOrientation();
+ if (GetTemplate()->HasFlag(AREATRIGGER_FLAG_HAS_FACE_MOVEMENT_DIR))
+ {
+ G3D::Vector3 const& nextPoint = _spline.getPoint(lastPositionIndex + 1);
+ orientation = GetAngle(nextPoint.x, nextPoint.y);
+ }
+
+ GetMap()->AreaTriggerRelocation(this, currentPosition.x, currentPosition.y, currentPosition.z, orientation);
+#ifdef TRINITY_DEBUG
+ DebugVisualizePosition();
+#endif
+
+ if (_lastSplineIndex != lastPositionIndex)
+ {
+ _lastSplineIndex = lastPositionIndex;
+ sScriptMgr->OnAreaTriggerEntitySplineIndexReached(this, _lastSplineIndex);
+ }
+}
+
+void AreaTrigger::DebugVisualizePosition()
+{
+ if (Unit* caster = GetCaster())
+ if (Player* player = caster->ToPlayer())
+ if (player->isDebugAreaTriggers)
+ player->SummonCreature(1, *this, TEMPSUMMON_TIMED_DESPAWN, GetTimeToTarget());
+}
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h
index e6396544cd7..57ea7b4e704 100644
--- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h
+++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h
@@ -19,11 +19,16 @@
#define TRINITYCORE_AREATRIGGER_H
#include "Object.h"
+#include "Position.h"
+#include "Spline.h"
-class Unit;
+class AreaTriggerTemplate;
+class AreaTriggerMiscTemplate;
class SpellInfo;
+class Unit;
+struct AreaTriggerPolygonVertice;
-class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigger>
+class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigger>, public MapObject
{
public:
AreaTrigger();
@@ -32,16 +37,78 @@ class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigge
void AddToWorld() override;
void RemoveFromWorld() override;
- bool CreateAreaTrigger(ObjectGuid::LowType guidlow, uint32 triggerEntry, Unit* caster, SpellInfo const* spell, Position const& pos, uint32 spellXSpellVisualId);
+ bool CreateAreaTrigger(uint32 triggerEntry, Unit* caster, Unit* target, SpellInfo const* spell, Position const& pos, int32 duration, uint32 spellXSpellVisualId, ObjectGuid const& castId = ObjectGuid::Empty);
void Update(uint32 p_time) override;
void Remove();
+ bool IsRemoved() const { return _isRemoved; }
uint32 GetSpellId() const { return GetUInt32Value(AREATRIGGER_SPELLID); }
+ uint32 GetTimeSinceCreated() const { return _timeSinceCreated; }
+ uint32 GetTimeToTarget() const { return GetUInt32Value(AREATRIGGER_TIME_TO_TARGET); }
+ uint32 GetTimeToTargetScale() const { return GetUInt32Value(AREATRIGGER_TIME_TO_TARGET_SCALE); }
int32 GetDuration() const { return _duration; }
- void SetDuration(int32 newDuration) { _duration = newDuration; }
+ int32 GetTotalDuration() const { return _totalDuration; }
+ void SetDuration(int32 newDuration);
void Delay(int32 delaytime) { SetDuration(GetDuration() - delaytime); }
+ GuidUnorderedSet const& GetInsideUnits() const { return _insideUnits; }
+
+ AreaTriggerMiscTemplate const* GetMiscTemplate() const { return _areaTriggerMiscTemplate; }
+ AreaTriggerTemplate const* GetTemplate() const;
+ uint32 GetScriptId() const;
+
+ ObjectGuid const& GetCasterGuid() const { return GetGuidValue(AREATRIGGER_CASTER); }
+ Unit* GetCaster() const;
+ Unit* GetTarget() const;
+
+ G3D::Vector3 const& GetRollPitchYaw() const { return _rollPitchYaw; }
+ G3D::Vector3 const& GetTargetRollPitchYaw() const { return _targetRollPitchYaw; }
+ void InitSplineOffsets(std::vector<G3D::Vector3> splinePoints, uint32 timeToTarget);
+ void InitSplines(std::vector<G3D::Vector3> const& splinePoints, uint32 timeToTarget);
+ bool HasSplines() const { return !_spline.empty(); }
+ ::Movement::Spline<int32> const& GetSpline() const { return _spline; }
+ uint32 GetElapsedTimeForMovement() const { return GetTimeSinceCreated(); } /// @todo: research the right value, in sniffs both timers are nearly identical
+
+ void UpdateShape();
+
protected:
+ void _UpdateDuration(int32 newDuration);
+ float GetProgress() const;
+
+ void UpdateTargetList();
+ void SearchUnitInSphere(std::list<Unit*>& targetList);
+ void SearchUnitInBox(std::list<Unit*>& targetList);
+ void SearchUnitInPolygon(std::list<Unit*>& targetList);
+ void SearchUnitInCylinder(std::list<Unit*>& targetList);
+ bool CheckIsInPolygon2D(Position const* pos) const;
+ void HandleUnitEnterExit(std::list<Unit*> const& targetList);
+
+ void DoActions(Unit* unit);
+ void UndoActions(Unit* unit);
+
+ void UpdatePolygonOrientation();
+ void UpdateSplinePosition(uint32 diff);
+
+ void DebugVisualizePosition(); // Debug purpose only
+
+ ObjectGuid _targetGuid;
+
int32 _duration;
- uint32 _spellXSpellVisualId;
+ int32 _totalDuration;
+ uint32 _timeSinceCreated;
+ float _previousCheckOrientation;
+ bool _isRemoved;
+
+ G3D::Vector3 _rollPitchYaw;
+ G3D::Vector3 _targetRollPitchYaw;
+ std::vector<G3D::Vector2> _polygonVertices;
+ ::Movement::Spline<int32> _spline;
+
+ bool _reachedDestination;
+ int32 _lastSplineIndex;
+ uint32 _movementTime;
+
+ AreaTriggerMiscTemplate const* _areaTriggerMiscTemplate;
+ GuidUnorderedSet _insideUnits;
};
+
#endif
diff --git a/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.cpp b/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.cpp
new file mode 100644
index 00000000000..81b1b8c7900
--- /dev/null
+++ b/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AreaTriggerTemplate.h"
+
+// Init the MaxSearchRadius that will be used in TrinitySearcher, avoiding calculate it at each update
+void AreaTriggerTemplate::InitMaxSearchRadius()
+{
+ switch (Type)
+ {
+ case AREATRIGGER_TYPE_SPHERE:
+ {
+ MaxSearchRadius = std::max(SphereDatas.Radius, SphereDatas.RadiusTarget);
+ break;
+ }
+ case AREATRIGGER_TYPE_BOX:
+ {
+ MaxSearchRadius = std::sqrt(BoxDatas.Extents[0] * BoxDatas.Extents[0] / 4 + BoxDatas.Extents[1] * BoxDatas.Extents[1] / 4);
+ break;
+ }
+ case AREATRIGGER_TYPE_POLYGON:
+ {
+ if (PolygonDatas.Height <= 0.0f)
+ PolygonDatas.Height = 1.0f;
+
+ for (G3D::Vector2 const& vertice : PolygonVertices)
+ {
+ float pointDist = vertice.length();
+
+ if (pointDist > MaxSearchRadius)
+ MaxSearchRadius = pointDist;
+ }
+
+ break;
+ }
+ case AREATRIGGER_TYPE_CYLINDER:
+ {
+ MaxSearchRadius = CylinderDatas.Radius;
+ break;
+ }
+ default:
+ break;
+ }
+}
diff --git a/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h b/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h
new file mode 100644
index 00000000000..c4701567abe
--- /dev/null
+++ b/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITYCORE_AREATRIGGER_TEMPLATE_H
+#define TRINITYCORE_AREATRIGGER_TEMPLATE_H
+
+#include <G3D/Vector3.h>
+
+#define MAX_AREATRIGGER_ENTITY_DATA 6
+
+enum AreaTriggerFlags
+{
+ AREATRIGGER_FLAG_HAS_ABSOLUTE_ORIENTATION = 0x00001, // NYI
+ AREATRIGGER_FLAG_HAS_DYNAMIC_SHAPE = 0x00002, // Implemented for Spheres
+ AREATRIGGER_FLAG_HAS_ATTACHED = 0x00004,
+ AREATRIGGER_FLAG_HAS_FACE_MOVEMENT_DIR = 0x00008,
+ AREATRIGGER_FLAG_HAS_FOLLOWS_TERRAIN = 0x00010, // NYI
+ AREATRIGGER_FLAG_UNK1 = 0x00020,
+ AREATRIGGER_FLAG_HAS_TARGET_ROLL_PITCH_YAW = 0x00040, // NYI
+ AREATRIGGER_FLAG_UNK2 = 0x00080,
+ AREATRIGGER_FLAG_UNK3 = 0x00100,
+ AREATRIGGER_FLAG_UNK4 = 0x00200,
+ AREATRIGGER_FLAG_UNK5 = 0x00400
+};
+
+enum AreaTriggerTypes
+{
+ AREATRIGGER_TYPE_SPHERE = 0,
+ AREATRIGGER_TYPE_BOX = 1,
+ AREATRIGGER_TYPE_UNK = 2,
+ AREATRIGGER_TYPE_POLYGON = 3,
+ AREATRIGGER_TYPE_CYLINDER = 4,
+ AREATRIGGER_TYPE_MAX = 5
+};
+
+enum AreaTriggerActionTypes
+{
+ AREATRIGGER_ACTION_CAST = 0,
+ AREATRIGGER_ACTION_ADDAURA = 1,
+ AREATRIGGER_ACTION_MAX = 2
+};
+
+enum AreaTriggerActionUserTypes
+{
+ AREATRIGGER_ACTION_USER_ANY = 0,
+ AREATRIGGER_ACTION_USER_FRIEND = 1,
+ AREATRIGGER_ACTION_USER_ENEMY = 2,
+ AREATRIGGER_ACTION_USER_RAID = 3,
+ AREATRIGGER_ACTION_USER_PARTY = 4,
+ AREATRIGGER_ACTION_USER_CASTER = 5,
+ AREATRIGGER_ACTION_USER_MAX = 6
+};
+
+struct AreaTriggerAction
+{
+ uint32 Param;
+ AreaTriggerActionTypes ActionType;
+ AreaTriggerActionUserTypes TargetType;
+};
+
+class AreaTriggerTemplate
+{
+public:
+ AreaTriggerTemplate()
+ {
+ Id = 0;
+ Flags = 0;
+ ScriptId = 0;
+ MaxSearchRadius = 0.0f;
+
+ memset(DefaultDatas.Data, 0, sizeof(DefaultDatas.Data));
+ }
+
+ bool HasFlag(uint32 flag) const { return (Flags & flag) != 0; }
+
+ bool IsSphere() const { return Type == AREATRIGGER_TYPE_SPHERE; }
+ bool IsBox() const { return Type == AREATRIGGER_TYPE_BOX; }
+ bool IsPolygon() const { return Type == AREATRIGGER_TYPE_POLYGON; }
+ bool IsCylinder() const { return Type == AREATRIGGER_TYPE_CYLINDER; }
+
+ void InitMaxSearchRadius();
+
+ uint32 Id;
+ AreaTriggerTypes Type;
+ uint32 Flags;
+ uint32 ScriptId;
+ float MaxSearchRadius;
+ std::vector<G3D::Vector2> PolygonVertices;
+ std::vector<G3D::Vector2> PolygonVerticesTarget;
+ std::vector<AreaTriggerAction> Actions;
+
+ union
+ {
+ struct
+ {
+ float Data[MAX_AREATRIGGER_ENTITY_DATA];
+ } DefaultDatas;
+
+ // AREATRIGGER_TYPE_SPHERE
+ struct
+ {
+ float Radius;
+ float RadiusTarget;
+ } SphereDatas;
+
+ // AREATRIGGER_TYPE_BOX
+ struct
+ {
+ float Extents[3];
+ float ExtentsTarget[3];
+ } BoxDatas;
+
+ // AREATRIGGER_TYPE_POLYGON
+ struct
+ {
+ float Height;
+ float HeightTarget;
+ } PolygonDatas;
+
+ // AREATRIGGER_TYPE_CYLINDER
+ struct
+ {
+ float Radius;
+ float RadiusTarget;
+ float Height;
+ float HeightTarget;
+ float LocationZOffset;
+ float LocationZOffsetTarget;
+ } CylinderDatas;
+ };
+};
+
+class AreaTriggerMiscTemplate
+{
+public:
+ AreaTriggerMiscTemplate()
+ {
+ MiscId = 0;
+ AreaTriggerEntry = 0;
+
+ MoveCurveId = 0;
+ ScaleCurveId = 0;
+ MorphCurveId = 0;
+ FacingCurveId = 0;
+
+ DecalPropertiesId = 0;
+
+ TimeToTarget = 0;
+ TimeToTargetScale = 0;
+
+ Template = nullptr;
+ }
+
+ bool HasSplines() const { return SplinePoints.size() >= 2; }
+
+ uint32 MiscId;
+ uint32 AreaTriggerEntry;
+
+ uint32 MoveCurveId;
+ uint32 ScaleCurveId;
+ uint32 MorphCurveId;
+ uint32 FacingCurveId;
+
+ uint32 DecalPropertiesId;
+
+ uint32 TimeToTarget;
+ uint32 TimeToTargetScale;
+
+ AreaTriggerTemplate const* Template;
+ std::vector<G3D::Vector3> SplinePoints;
+};
+
+#endif