aboutsummaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/server/game/AI/CreatureAI.h4
-rw-r--r--src/server/game/Accounts/RBAC.h1
-rw-r--r--src/server/game/DataStores/DB2Stores.h1
-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
-rw-r--r--src/server/game/Entities/Object/Object.cpp236
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateData.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp3
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp4
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp54
-rw-r--r--src/server/game/Entities/Unit/Unit.h11
-rw-r--r--src/server/game/Globals/AreaTriggerDataStore.cpp230
-rw-r--r--src/server/game/Globals/AreaTriggerDataStore.h39
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp2
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h5
-rw-r--r--src/server/game/Grids/ObjectGridLoader.cpp4
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp5
-rw-r--r--src/server/game/Maps/Map.cpp165
-rw-r--r--src/server/game/Maps/Map.h8
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp107
-rw-r--r--src/server/game/Scripting/ScriptMgr.h44
-rw-r--r--src/server/game/Server/Packets/AllPackets.h1
-rw-r--r--src/server/game/Server/Packets/AreaTriggerPackets.cpp97
-rw-r--r--src/server/game/Server/Packets/AreaTriggerPackets.h107
-rw-r--r--src/server/game/Server/Packets/MiscPackets.cpp7
-rw-r--r--src/server/game/Server/Packets/MiscPackets.h22
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp6
-rw-r--r--src/server/game/Server/WorldSession.h8
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp22
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp32
-rw-r--r--src/server/game/Spells/SpellInfo.cpp11
-rw-r--r--src/server/game/Spells/SpellInfo.h1
-rw-r--r--src/server/game/World/World.cpp4
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp9
-rw-r--r--src/server/worldserver/worldserver.conf.dist1
38 files changed, 2012 insertions, 174 deletions
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index c9c43ef7d9d..007f6ceabc8 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -136,6 +136,10 @@ class TC_GAME_API CreatureAI : public UnitAI
virtual void JustRegisteredDynObject(DynamicObject* /*dynObject*/) { }
virtual void JustUnregisteredDynObject(DynamicObject* /*dynObject*/) { }
+ // Called when the creature successfully registers an areatrigger
+ virtual void JustRegisteredAreaTrigger(AreaTrigger* /*areaTrigger*/) { }
+ virtual void JustUnregisteredAreaTrigger(AreaTrigger* /*areaTrigger*/) { }
+
// Called when hit by a spell
virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) { }
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index a053a9e86bf..9fd7d4d3a70 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -755,6 +755,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_SCENE_CANCEL = 848,
RBAC_PERM_COMMAND_LIST_SCENES = 849,
RBAC_PERM_COMMAND_RELOAD_SCENE_TEMPLATE = 850,
+ RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TEMPLATE = 851,
// custom permissions 1000+
RBAC_PERM_MAX
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index f20ba89a6c0..94999b10ba6 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -58,6 +58,7 @@ TC_GAME_API extern DB2Storage<CreatureTypeEntry> sCreatureTyp
TC_GAME_API extern DB2Storage<CriteriaEntry> sCriteriaStore;
TC_GAME_API extern DB2Storage<CriteriaTreeEntry> sCriteriaTreeStore;
TC_GAME_API extern DB2Storage<CurrencyTypesEntry> sCurrencyTypesStore;
+TC_GAME_API extern DB2Storage<CurveEntry> sCurveStore;
TC_GAME_API extern DB2Storage<DestructibleModelDataEntry> sDestructibleModelDataStore;
TC_GAME_API extern DB2Storage<DifficultyEntry> sDifficultyStore;
TC_GAME_API extern DB2Storage<DungeonEncounterEntry> sDungeonEncounterStore;
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
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index ef42f5a3f68..fcbb82708b0 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -45,6 +45,7 @@
#include "BattlefieldMgr.h"
#include "MiscPackets.h"
#include "InstanceScenario.h"
+#include "AreaTriggerTemplate.h"
Object::Object()
{
@@ -346,7 +347,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const
bool VehicleCreate = (flags & UPDATEFLAG_VEHICLE) != 0;
bool AnimKitCreate = (flags & UPDATEFLAG_ANIMKITS) != 0;
bool Rotation = (flags & UPDATEFLAG_ROTATION) != 0;
- bool HasAreaTrigger = false;
+ bool HasAreaTrigger = (flags & UPDATEFLAG_AREATRIGGER) != 0;
bool HasGameObject = false;
bool ThisIsYou = (flags & UPDATEFLAG_SELF) != 0;
bool SmoothPhasing = false;
@@ -509,111 +510,160 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const
*data << self->m_movementInfo.transport;
}
- //if (AreaTrigger)
- //{
- // *data << uint32(ElapsedMs);
- // *data << Vector3(RollPitchYaw);
- // data->WriteBit(HasAbsoluteOrientation);
- // data->WriteBit(HasDynamicShape);
- // data->WriteBit(HasAttached);
- // data->WriteBit(HasFaceMovementDir);
- // data->WriteBit(HasFollowsTerrain);
- // data->WriteBit(Unknown_1);
- // data->WriteBit(HasTargetRollPitchYaw);
- // data->WriteBit(HasScaleCurveID);
- // data->WriteBit(HasMorphCurveID);
- // data->WriteBit(HasFacingCurveID);
- // data->WriteBit(HasMoveCurveID);
- // data->WriteBit(HasAreaTriggerSphere);
- // data->WriteBit(HasAreaTriggerBox);
- // data->WriteBit(HasAreaTriggerPolygon);
- // data->WriteBit(HasAreaTriggerCylinder);
- // data->WriteBit(HasAreaTriggerSpline);
- // data->WriteBit(HasAreaTriggerUnkType);
-
- // if (HasAreaTriggerUnkType)
- // {
- // data->WriteBit(Unk_1);
- // data->WriteBit(HasCenter);
- // data->WriteBit(Unk_3);
- // data->WriteBit(Unk_4);
-
- // *data << uint32();
- // *data << int32();
- // *data << uint32();
- // *data << float(Radius);
- // *data << float(BlendFromRadius);
- // *data << float(InitialAngel);
- // *data << float(ZOffset);
-
- // if (Unk_1)
- // *data << ObjectGuid();
-
- // if (HasCenter)
- // *data << Vector3(Center);
- // }
+ if (HasAreaTrigger)
+ {
+ AreaTrigger const* areaTrigger = ToAreaTrigger();
+ AreaTriggerMiscTemplate const* areaTriggerMiscTemplate = areaTrigger->GetMiscTemplate();
+ AreaTriggerTemplate const* areaTriggerTemplate = areaTrigger->GetTemplate();
+
+ *data << uint32(areaTrigger->GetTimeSinceCreated());
+
+ *data << areaTrigger->GetRollPitchYaw();
+
+ bool hasAbsoluteOrientation = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_HAS_ABSOLUTE_ORIENTATION);
+ bool hasDynamicShape = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_HAS_DYNAMIC_SHAPE);
+ bool hasAttached = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_HAS_ATTACHED);
+ bool hasFaceMovementDir = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_HAS_FACE_MOVEMENT_DIR);
+ bool hasFollowsTerrain = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_HAS_FOLLOWS_TERRAIN);
+ bool hasUnk1 = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_UNK1);
+ bool hasTargetRollPitchYaw = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_HAS_TARGET_ROLL_PITCH_YAW);
+ bool hasScaleCurveID = areaTriggerMiscTemplate->ScaleCurveId != 0;
+ bool hasMorphCurveID = areaTriggerMiscTemplate->MorphCurveId != 0;
+ bool hasFacingCurveID = areaTriggerMiscTemplate->FacingCurveId != 0;
+ bool hasMoveCurveID = areaTriggerMiscTemplate->MoveCurveId != 0;
+ bool hasUnk2 = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_UNK2);
+ bool hasUnk3 = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_UNK3);
+ bool hasUnk4 = areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_UNK4);
+ bool hasAreaTriggerSphere = areaTriggerTemplate->IsSphere();
+ bool hasAreaTriggerBox = areaTriggerTemplate->IsBox();
+ bool hasAreaTriggerPolygon = areaTriggerTemplate->IsPolygon();
+ bool hasAreaTriggerCylinder = areaTriggerTemplate->IsCylinder();
+ bool hasAreaTriggerSpline = areaTrigger->HasSplines();
+ bool hasAreaTriggerUnkType = false; // areaTriggerTemplate->HasFlag(AREATRIGGER_FLAG_UNK5);
+
+ data->WriteBit(hasAbsoluteOrientation);
+ data->WriteBit(hasDynamicShape);
+ data->WriteBit(hasAttached);
+ data->WriteBit(hasFaceMovementDir);
+ data->WriteBit(hasFollowsTerrain);
+ data->WriteBit(hasUnk1);
+ data->WriteBit(hasTargetRollPitchYaw);
+ data->WriteBit(hasScaleCurveID);
+ data->WriteBit(hasMorphCurveID);
+ data->WriteBit(hasFacingCurveID);
+ data->WriteBit(hasMoveCurveID);
+ data->WriteBit(hasUnk2);
+ data->WriteBit(hasUnk3);
+ data->WriteBit(hasUnk4);
+ data->WriteBit(hasAreaTriggerSphere);
+ data->WriteBit(hasAreaTriggerBox);
+ data->WriteBit(hasAreaTriggerPolygon);
+ data->WriteBit(hasAreaTriggerCylinder);
+ data->WriteBit(hasAreaTriggerSpline);
+ data->WriteBit(hasAreaTriggerUnkType);
+
+ if (hasUnk3)
+ data->WriteBit(0);
- // if (HasTargetRollPitchYaw)
- // *data << Vector3(TargetRollPitchYaw);
+ data->FlushBits();
- // if (HasScaleCurveID)
- // *data << uint32(ScaleCurveID);
+ if (hasAreaTriggerSpline)
+ {
+ std::vector<G3D::Vector3> const& splinePoints = areaTrigger->GetSpline().getPoints();
- // if (HasMorphCurveID)
- // *data << uint32(MorphCurveID);
+ *data << uint32(areaTrigger->GetTimeToTarget());
+ *data << uint32(areaTrigger->GetElapsedTimeForMovement());
- // if (HasFacingCurveID)
- // *data << uint32(FacingCurveID);
+ data->WriteBits(splinePoints.size(), 16);
- // if (HasMoveCurveID)
- // *data << uint32(MoveCurveID);
+ for (G3D::Vector3 const& spline : splinePoints)
+ *data << spline;
+ }
- // if (HasAreaTriggerSphere)
- // {
- // *data << float(Radius);
- // *data << float(RadiusTarget);
- // }
+ if (hasTargetRollPitchYaw)
+ *data << areaTrigger->GetTargetRollPitchYaw();
- // if (HasAreaTriggerBox)
- // {
- // *data << Vector3(Extents);
- // *data << Vector3(ExtentsTarget);
- // }
+ if (hasScaleCurveID)
+ *data << uint32(areaTriggerMiscTemplate->ScaleCurveId);
- // if (HasAreaTriggerPolygon)
- // {
- // *data << uint32(Vertices.size());
- // *data << uint32(VerticesTarget.size());
- // *data << float(Height);
- // *data << float(HeightTarget);
+ if (hasMorphCurveID)
+ *data << uint32(areaTriggerMiscTemplate->MorphCurveId);
- // for (std::size_t i = 0; i < Vertices.size(); ++i)
- // *data << Vector2(Vertices[i]);
+ if (hasFacingCurveID)
+ *data << uint32(areaTriggerMiscTemplate->FacingCurveId);
- // for (std::size_t i = 0; i < VerticesTarget.size(); ++i)
- // *data << Vector2(VerticesTarget[i]);
- // }
+ if (hasMoveCurveID)
+ *data << uint32(areaTriggerMiscTemplate->MoveCurveId);
- // if (HasAreaTriggerCylinder)
- // {
- // *data << float(Radius);
- // *data << float(RadiusTarget);
- // *data << float(Height);
- // *data << float(HeightTarget);
- // *data << float(LocationZOffset);
- // *data << float(LocationZOffsetTarget);
- // }
+ if (hasUnk2)
+ *data << int32(0);
- // if (HasAreaTriggerSpline)
- // {
- // *data << uint32(TimeToTarget);
- // *data << uint32(ElapsedTimeForMovement);
- // *data << uint32(Points.size());
+ if (hasUnk4)
+ *data << uint32(0);
- // for (std::size_t i = 0; i < Points.size(); ++i)
- // *data << Vector3(Points[i]);
- // }
- //}
+ if (hasAreaTriggerSphere)
+ {
+ *data << float(areaTriggerTemplate->SphereDatas.Radius);
+ *data << float(areaTriggerTemplate->SphereDatas.RadiusTarget);
+ }
+
+ if (hasAreaTriggerBox)
+ {
+ *data << float(areaTriggerTemplate->BoxDatas.Extents[0]);
+ *data << float(areaTriggerTemplate->BoxDatas.Extents[1]);
+ *data << float(areaTriggerTemplate->BoxDatas.Extents[2]);
+ *data << float(areaTriggerTemplate->BoxDatas.ExtentsTarget[0]);
+ *data << float(areaTriggerTemplate->BoxDatas.ExtentsTarget[1]);
+ *data << float(areaTriggerTemplate->BoxDatas.ExtentsTarget[2]);
+ }
+
+ if (hasAreaTriggerPolygon)
+ {
+ *data << int32(areaTriggerTemplate->PolygonVertices.size());
+ *data << int32(areaTriggerTemplate->PolygonVerticesTarget.size());
+ *data << float(areaTriggerTemplate->PolygonDatas.Height);
+ *data << float(areaTriggerTemplate->PolygonDatas.HeightTarget);
+
+ for (G3D::Vector2 const& vertice : areaTriggerTemplate->PolygonVertices)
+ *data << vertice;
+
+ for (G3D::Vector2 const& vertice : areaTriggerTemplate->PolygonVerticesTarget)
+ *data << vertice;
+ }
+
+ if (hasAreaTriggerCylinder)
+ {
+ *data << float(areaTriggerTemplate->CylinderDatas.Radius);
+ *data << float(areaTriggerTemplate->CylinderDatas.RadiusTarget);
+ *data << float(areaTriggerTemplate->CylinderDatas.Height);
+ *data << float(areaTriggerTemplate->CylinderDatas.HeightTarget);
+ *data << float(areaTriggerTemplate->CylinderDatas.LocationZOffset);
+ *data << float(areaTriggerTemplate->CylinderDatas.LocationZOffsetTarget);
+ }
+
+ if (hasAreaTriggerUnkType)
+ {
+ /*packet.ResetBitReader();
+ var unk1 = packet.ReadBit("AreaTriggerUnk1");
+ var hasCenter = packet.ReadBit("HasCenter", index);
+ packet.ReadBit("Unk bit 703 1", index);
+ packet.ReadBit("Unk bit 703 2", index);
+
+ packet.ReadUInt32();
+ packet.ReadInt32();
+ packet.ReadUInt32();
+ packet.ReadSingle("Radius", index);
+ packet.ReadSingle("BlendFromRadius", index);
+ packet.ReadSingle("InitialAngel", index);
+ packet.ReadSingle("ZOffset", index);
+
+ if (unk1)
+ packet.ReadPackedGuid128("AreaTriggerUnkGUID", index);
+
+ if (hasCenter)
+ packet.ReadVector3("Center", index);*/
+ }
+ }
//if (GameObject)
//{
diff --git a/src/server/game/Entities/Object/Updates/UpdateData.h b/src/server/game/Entities/Object/Updates/UpdateData.h
index 27042f3c6a1..7e7d86590d9 100644
--- a/src/server/game/Entities/Object/Updates/UpdateData.h
+++ b/src/server/game/Entities/Object/Updates/UpdateData.h
@@ -45,7 +45,7 @@ enum OBJECT_UPDATE_FLAGS
UPDATEFLAG_TRANSPORT_POSITION = 0x0040,
UPDATEFLAG_ROTATION = 0x0080,
UPDATEFLAG_ANIMKITS = 0x0100,
- //UPDATEFLAG_AREATRIGGER = 0x0200,
+ UPDATEFLAG_AREATRIGGER = 0x0200,
//UPDATEFLAG_GAMEOBJECT = 0x0400,
//UPDATEFLAG_REPLACE_ACTIVE = 0x0800,
//UPDATEFLAG_NO_BIRTH_ANIM = 0x1000,
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 23f845feee1..bfb0bc377f2 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1606,6 +1606,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
// remove all dyn objects
RemoveAllDynObjects();
+ // remove all areatriggers entities
+ RemoveAllAreaTriggers();
+
// stop spellcasting
// not attempt interrupt teleportation spell at caster teleport
if (!(options & TELE_TO_SPELL))
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index c24f1e375e3..e9b850530e3 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -670,6 +670,7 @@ void Transport::DelayedTeleportTransport()
RemovePassenger(obj);
break;
case TYPEID_DYNAMICOBJECT:
+ case TYPEID_AREATRIGGER:
obj->AddObjectToRemoveList();
break;
default:
@@ -725,6 +726,9 @@ void Transport::UpdatePassengerPositions(PassengerSet& passengers)
case TYPEID_DYNAMICOBJECT:
GetMap()->DynamicObjectRelocation(passenger->ToDynObject(), x, y, z, o);
break;
+ case TYPEID_AREATRIGGER:
+ GetMap()->AreaTriggerRelocation(passenger->ToAreaTrigger(), x, y, z, o);
+ break;
default:
break;
}
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 2ff096d2d27..4fc38747f96 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -4911,6 +4911,59 @@ void Unit::RemoveAllGameObjects()
}
}
+void Unit::_RegisterAreaTrigger(AreaTrigger* areaTrigger)
+{
+ m_areaTrigger.push_back(areaTrigger);
+ if (GetTypeId() == TYPEID_UNIT && IsAIEnabled)
+ ToCreature()->AI()->JustRegisteredAreaTrigger(areaTrigger);
+}
+
+void Unit::_UnregisterAreaTrigger(AreaTrigger* areaTrigger)
+{
+ m_areaTrigger.erase(std::remove(m_areaTrigger.begin(), m_areaTrigger.end(), areaTrigger));
+ if (GetTypeId() == TYPEID_UNIT && IsAIEnabled)
+ ToCreature()->AI()->JustUnregisteredAreaTrigger(areaTrigger);
+}
+
+AreaTrigger* Unit::GetAreaTrigger(uint32 spellId) const
+{
+ std::vector<AreaTrigger*> areaTriggers = GetAreaTriggers(spellId);
+ return areaTriggers.empty() ? nullptr : areaTriggers.front();
+}
+
+std::vector<AreaTrigger*> Unit::GetAreaTriggers(uint32 spellId) const
+{
+ std::vector<AreaTrigger*> areaTriggers;
+ for (AreaTriggerList::const_iterator i = m_areaTrigger.begin(); i != m_areaTrigger.end(); ++i)
+ if ((*i)->GetSpellId() == spellId)
+ areaTriggers.push_back(*i);
+
+ return areaTriggers;
+}
+
+void Unit::RemoveAreaTrigger(uint32 spellId)
+{
+ if (m_areaTrigger.empty())
+ return;
+ for (AreaTriggerList::iterator i = m_areaTrigger.begin(); i != m_areaTrigger.end();)
+ {
+ AreaTrigger* areaTrigger = *i;
+ if (areaTrigger->GetSpellId() == spellId)
+ {
+ areaTrigger->Remove();
+ i = m_areaTrigger.begin();
+ }
+ else
+ ++i;
+ }
+}
+
+void Unit::RemoveAllAreaTriggers()
+{
+ while (!m_areaTrigger.empty())
+ m_areaTrigger.front()->Remove();
+}
+
void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage const* log)
{
WorldPackets::CombatLog::SpellNonMeleeDamageLog packet;
@@ -10953,6 +11006,7 @@ void Unit::RemoveFromWorld()
RemoveAllGameObjects();
RemoveAllDynObjects();
+ RemoveAllAreaTriggers();
ExitVehicle(); // Remove applied auras with SPELL_AURA_CONTROL_VEHICLE
UnsummonAllTotems();
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 793bb07c52e..2efca610c1e 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -2073,6 +2073,14 @@ class TC_GAME_API Unit : public WorldObject
void RemoveGameObject(uint32 spellid, bool del);
void RemoveAllGameObjects();
+ // AreaTrigger management
+ void _RegisterAreaTrigger(AreaTrigger* areaTrigger);
+ void _UnregisterAreaTrigger(AreaTrigger* areaTrigger);
+ AreaTrigger* GetAreaTrigger(uint32 spellId) const;
+ std::vector<AreaTrigger*> GetAreaTriggers(uint32 spellId) const;
+ void RemoveAreaTrigger(uint32 spellId);
+ void RemoveAllAreaTriggers();
+
void ModifyAuraState(AuraStateType flag, bool apply);
uint32 BuildAuraStateUpdateForTarget(Unit* target) const;
bool HasAuraState(AuraStateType flag, SpellInfo const* spellProto = NULL, Unit const* Caster = NULL) const;
@@ -2309,6 +2317,9 @@ class TC_GAME_API Unit : public WorldObject
typedef std::list<GameObject*> GameObjectList;
GameObjectList m_gameObj;
+ typedef std::vector<AreaTrigger*> AreaTriggerList;
+ AreaTriggerList m_areaTrigger;
+
uint32 m_transform;
Spell* m_currentSpells[CURRENT_MAX_SPELL];
diff --git a/src/server/game/Globals/AreaTriggerDataStore.cpp b/src/server/game/Globals/AreaTriggerDataStore.cpp
new file mode 100644
index 00000000000..d93cc54bf76
--- /dev/null
+++ b/src/server/game/Globals/AreaTriggerDataStore.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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 "AreaTriggerDataStore.h"
+#include "AreaTriggerTemplate.h"
+#include "DatabaseEnv.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "Timer.h"
+
+namespace
+{
+ std::unordered_map<uint32, AreaTriggerTemplate> _areaTriggerTemplateStore;
+ std::unordered_map<uint32, AreaTriggerMiscTemplate> _areaTriggerTemplateSpellMisc;
+}
+
+void AreaTriggerDataStore::LoadAreaTriggerTemplates()
+{
+ uint32 oldMSTime = getMSTime();
+ std::unordered_map<uint32, std::vector<G3D::Vector2>> verticesByAreaTrigger;
+ std::unordered_map<uint32, std::vector<G3D::Vector2>> verticesTargetByAreaTrigger;
+ std::unordered_map<uint32, std::vector<G3D::Vector3>> splinesBySpellMisc;
+ std::unordered_map<uint32, std::vector<AreaTriggerAction>> actionsByAreaTrigger;
+
+ // 0 1 2 3
+ if (QueryResult templateActions = WorldDatabase.Query("SELECT AreaTriggerId, ActionType, ActionParam, TargetType FROM `areatrigger_template_actions`"))
+ {
+ do
+ {
+ Field* templateActionFields = templateActions->Fetch();
+ uint32 areaTriggerId = templateActionFields[0].GetUInt32();
+
+ AreaTriggerAction action;
+ action.Param = templateActionFields[2].GetUInt32();
+ uint32 actionType = templateActionFields[1].GetUInt32();
+ uint32 targetType = templateActionFields[3].GetUInt32();
+
+ if (actionType >= AREATRIGGER_ACTION_MAX)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `areatrigger_template_actions` has invalid ActionType (%u) for AreaTriggerId %u and Param %u", actionType, areaTriggerId, action.Param);
+ continue;
+ }
+
+ if (targetType >= AREATRIGGER_ACTION_USER_MAX)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `areatrigger_template_actions` has invalid TargetType (%u) for AreaTriggerId %u and Param %u", targetType, areaTriggerId, action.Param);
+ continue;
+ }
+
+ action.TargetType = AreaTriggerActionUserTypes(targetType);
+ action.ActionType = AreaTriggerActionTypes(actionType);
+
+ actionsByAreaTrigger[areaTriggerId].push_back(action);
+ }
+ while (templateActions->NextRow());
+ }
+ else
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 AreaTrigger templates actions. DB table `areatrigger_template_actions` is empty.");
+ }
+
+ // 0 1 2 3 4 5
+ if (QueryResult vertices = WorldDatabase.Query("SELECT AreaTriggerId, Idx, VerticeX, VerticeY, VerticeTargetX, VerticeTargetY FROM `areatrigger_template_polygon_vertices` ORDER BY `AreaTriggerId`, `Idx`"))
+ {
+ do
+ {
+ Field* verticeFields = vertices->Fetch();
+ uint32 areaTriggerId = verticeFields[0].GetUInt32();
+
+ verticesByAreaTrigger[areaTriggerId].emplace_back(verticeFields[2].GetFloat(), verticeFields[3].GetFloat());
+
+ if (!verticeFields[4].IsNull() && !verticeFields[5].IsNull())
+ verticesTargetByAreaTrigger[areaTriggerId].emplace_back(verticeFields[4].GetFloat(), verticeFields[5].GetFloat());
+ else if (verticeFields[4].IsNull() != !verticeFields[5].IsNull())
+ TC_LOG_ERROR("sql.sql", "Table `areatrigger_template_polygon_vertices` has listed invalid target vertices (AreaTrigger: %u, Index: %u).", areaTriggerId, verticeFields[1].GetUInt32());
+ }
+ while (vertices->NextRow());
+ }
+ else
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 AreaTrigger templates polygon vertices. DB table `areatrigger_template_polygon_vertices` is empty.");
+ }
+
+ // 0 1 2 3
+ if (QueryResult splines = WorldDatabase.Query("SELECT SpellMiscId, X, Y, Z FROM `spell_areatrigger_splines` ORDER BY `SpellMiscId`, `Idx`"))
+ {
+ do
+ {
+ Field* splineFields = splines->Fetch();
+ uint32 spellMiscId = splineFields[0].GetUInt32();
+
+ G3D::Vector3 spline;
+ spline.x = splineFields[1].GetFloat();
+ spline.y = splineFields[2].GetFloat();
+ spline.z = splineFields[3].GetFloat();
+
+ splinesBySpellMisc[spellMiscId].push_back(spline);
+ }
+ while (splines->NextRow());
+ }
+ else
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 AreaTrigger templates splines. DB table `spell_areatrigger_splines` is empty.");
+ }
+
+ // 0 1 2 3 4 5 6 7 8 9
+ if (QueryResult templates = WorldDatabase.Query("SELECT Id, Type, Flags, Data0, Data1, Data2, Data3, Data4, Data5, ScriptName FROM `areatrigger_template`"))
+ {
+ do
+ {
+ Field* fields = templates->Fetch();
+
+ AreaTriggerTemplate areaTriggerTemplate;
+ areaTriggerTemplate.Id = fields[0].GetUInt32();
+ uint8 type = fields[1].GetUInt8();
+
+ if (type >= AREATRIGGER_TYPE_MAX)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `areatrigger_template` has listed areatrigger (Id: %u) with invalid type %u.", areaTriggerTemplate.Id, type);
+ continue;
+ }
+
+ areaTriggerTemplate.Type = static_cast<AreaTriggerTypes>(type);
+ areaTriggerTemplate.Flags = fields[2].GetUInt32();
+
+ for (uint8 i = 0; i < MAX_AREATRIGGER_ENTITY_DATA; ++i)
+ areaTriggerTemplate.DefaultDatas.Data[i] = fields[3 + i].GetFloat();
+
+ areaTriggerTemplate.ScriptId = sObjectMgr->GetScriptId(fields[9].GetString());
+ areaTriggerTemplate.PolygonVertices = std::move(verticesByAreaTrigger[areaTriggerTemplate.Id]);
+ areaTriggerTemplate.PolygonVerticesTarget = std::move(verticesTargetByAreaTrigger[areaTriggerTemplate.Id]);
+ areaTriggerTemplate.Actions = std::move(actionsByAreaTrigger[areaTriggerTemplate.Id]);
+
+ areaTriggerTemplate.InitMaxSearchRadius();
+ _areaTriggerTemplateStore[areaTriggerTemplate.Id] = areaTriggerTemplate;
+ }
+ while (templates->NextRow());
+ }
+
+ // 0 1 2 3 4 5 6 7 8
+ if (QueryResult areatriggerSpellMiscs = WorldDatabase.Query("SELECT SpellMiscId, AreaTriggerId, MoveCurveId, ScaleCurveId, MorphCurveId, FacingCurveId, DecalPropertiesId, TimeToTarget, TimeToTargetScale FROM `spell_areatrigger`"))
+ {
+ do
+ {
+ AreaTriggerMiscTemplate miscTemplate;
+
+ Field* areatriggerSpellMiscFields = areatriggerSpellMiscs->Fetch();
+ miscTemplate.MiscId = areatriggerSpellMiscFields[0].GetUInt32();
+
+ uint32 areatriggerId = areatriggerSpellMiscFields[1].GetUInt32();
+ miscTemplate.Template = GetAreaTriggerTemplate(areatriggerId);
+
+ if (!miscTemplate.Template)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `spell_areatrigger` reference invalid AreaTriggerId %u for miscId %u", areatriggerId, miscTemplate.MiscId);
+ continue;
+ }
+
+#define VALIDATE_AND_SET_CURVE(Curve, Value) \
+ miscTemplate.Curve = Value; \
+ if (miscTemplate.Curve && !sCurveStore.LookupEntry(miscTemplate.Curve)) \
+ { \
+ TC_LOG_ERROR("sql.sql", "Table `spell_areatrigger` has listed areatrigger (MiscId: %u, Id: %u) with invalid " #Curve " (%u), set to 0!", \
+ miscTemplate.MiscId, areatriggerId, miscTemplate.Curve); \
+ miscTemplate.Curve = 0; \
+ }
+
+ VALIDATE_AND_SET_CURVE(MoveCurveId, areatriggerSpellMiscFields[2].GetUInt32());
+ VALIDATE_AND_SET_CURVE(ScaleCurveId, areatriggerSpellMiscFields[3].GetUInt32());
+ VALIDATE_AND_SET_CURVE(MorphCurveId, areatriggerSpellMiscFields[4].GetUInt32());
+ VALIDATE_AND_SET_CURVE(FacingCurveId, areatriggerSpellMiscFields[5].GetUInt32());
+
+#undef VALIDATE_AND_SET_CURVE
+
+ miscTemplate.DecalPropertiesId = areatriggerSpellMiscFields[6].GetUInt32();
+
+ miscTemplate.TimeToTarget = areatriggerSpellMiscFields[7].GetUInt32();
+ miscTemplate.TimeToTargetScale = areatriggerSpellMiscFields[8].GetUInt32();
+
+ miscTemplate.SplinePoints = splinesBySpellMisc[miscTemplate.MiscId];
+
+ _areaTriggerTemplateSpellMisc[miscTemplate.MiscId] = miscTemplate;
+ }
+ while (areatriggerSpellMiscs->NextRow());
+ }
+ else
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 Spell AreaTrigger templates. DB table `spell_areatrigger` is empty.");
+ }
+
+ TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " spell areatrigger templates in %u ms.", _areaTriggerTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
+}
+
+AreaTriggerTemplate const* AreaTriggerDataStore::GetAreaTriggerTemplate(uint32 areaTriggerId) const
+{
+ auto itr = _areaTriggerTemplateStore.find(areaTriggerId);
+ if (itr != _areaTriggerTemplateStore.end())
+ return &itr->second;
+
+ return nullptr;
+}
+
+AreaTriggerMiscTemplate const* AreaTriggerDataStore::GetAreaTriggerMiscTemplate(uint32 spellMiscValue) const
+{
+ auto itr = _areaTriggerTemplateSpellMisc.find(spellMiscValue);
+ if (itr != _areaTriggerTemplateSpellMisc.end())
+ return &itr->second;
+
+ return nullptr;
+}
+
+AreaTriggerDataStore* AreaTriggerDataStore::Instance()
+{
+ static AreaTriggerDataStore instance;
+ return &instance;
+}
diff --git a/src/server/game/Globals/AreaTriggerDataStore.h b/src/server/game/Globals/AreaTriggerDataStore.h
new file mode 100644
index 00000000000..7b4707327f3
--- /dev/null
+++ b/src/server/game/Globals/AreaTriggerDataStore.h
@@ -0,0 +1,39 @@
+/*
+ * 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 AreaTriggerDataStore_h__
+#define AreaTriggerDataStore_h__
+
+#include "Define.h"
+
+class AreaTriggerTemplate;
+class AreaTriggerMiscTemplate;
+
+class TC_GAME_API AreaTriggerDataStore
+{
+public:
+ void LoadAreaTriggerTemplates();
+
+ AreaTriggerTemplate const* GetAreaTriggerTemplate(uint32 areaTriggerId) const;
+ AreaTriggerMiscTemplate const* GetAreaTriggerMiscTemplate(uint32 spellMiscValue) const;
+
+ static AreaTriggerDataStore* Instance();
+};
+
+#define sAreaTriggerDataStore AreaTriggerDataStore::Instance()
+
+#endif // AreaTriggerDataStore_h__
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index fc19f9ebad1..1ee182f4273 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -8763,6 +8763,8 @@ void ObjectMgr::LoadScriptNames()
"UNION "
"SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
"UNION "
+ "SELECT DISTINCT(ScriptName) FROM areatrigger_template WHERE ScriptName <> '' "
+ "UNION "
"SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
"UNION "
"SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 311bb9dee48..f97f5ac298a 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -902,11 +902,11 @@ namespace Trinity
class AnyUnitInObjectRangeCheck
{
public:
- AnyUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) { }
+ AnyUnitInObjectRangeCheck(WorldObject const* obj, float range, bool check3D = true) : i_obj(obj), i_range(range), i_check3D(check3D) { }
bool operator()(Unit* u) const
{
- if (u->IsAlive() && i_obj->IsWithinDistInMap(u, i_range))
+ if (u->IsAlive() && i_obj->IsWithinDistInMap(u, i_range, i_check3D))
return true;
return false;
@@ -915,6 +915,7 @@ namespace Trinity
private:
WorldObject const* i_obj;
float i_range;
+ bool i_check3D;
};
// Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit)
diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp
index 13de080ca2b..eab7573f552 100644
--- a/src/server/game/Grids/ObjectGridLoader.cpp
+++ b/src/server/game/Grids/ObjectGridLoader.cpp
@@ -213,10 +213,12 @@ void ObjectGridUnloader::Visit(GridRefManager<T> &m)
void ObjectGridStoper::Visit(CreatureMapType &m)
{
- // stop any fights at grid de-activation and remove dynobjects created at cast by creatures
+ // stop any fights at grid de-activation and remove dynobjects/areatriggers created at cast by creatures
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
iter->GetSource()->RemoveAllDynObjects();
+ iter->GetSource()->RemoveAllAreaTriggers();
+
if (iter->GetSource()->IsInCombat())
{
iter->GetSource()->CombatStop();
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 813cd1fe84c..036c4a01fe6 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -48,6 +48,7 @@
#include "WhoPackets.h"
#include "InstancePackets.h"
#include "InstanceScript.h"
+#include "AreaTriggerPackets.h"
void WorldSession::HandleRepopRequest(WorldPackets::Misc::RepopRequest& /*packet*/)
{
@@ -464,7 +465,7 @@ void WorldSession::HandleResurrectResponse(WorldPackets::Misc::ResurrectResponse
GetPlayer()->ResurrectUsingRequestData();
}
-void WorldSession::HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& packet)
+void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigger& packet)
{
Player* player = GetPlayer();
if (player->IsInFlight())
@@ -567,7 +568,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& pack
reviveAtTrigger = true;
break;
case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE:
- player->GetSession()->SendPacket(WorldPackets::Misc::AreaTriggerNoCorpse().Write());
+ player->GetSession()->SendPacket(WorldPackets::AreaTrigger::AreaTriggerNoCorpse().Write());
TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance map %d and cannot enter", player->GetName().c_str(), at->target_mapId);
break;
case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH:
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 6c759662c83..807232b88a1 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -234,7 +234,7 @@ void Map::DeleteStateMachine()
}
Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent):
-_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false),
+_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), _areaTriggersToMoveLock(false),
i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD),
@@ -310,6 +310,15 @@ void Map::AddToGrid(DynamicObject* obj, Cell const& cell)
}
template<>
+void Map::AddToGrid(AreaTrigger* obj, Cell const& cell)
+{
+ NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
+ grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
+
+ obj->SetCurrentCell(cell);
+}
+
+template<>
void Map::AddToGrid(Corpse* obj, Cell const& cell)
{
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
@@ -784,6 +793,7 @@ void Map::Update(const uint32 t_diff)
MoveAllCreaturesInMoveList();
MoveAllGameObjectsInMoveList();
+ MoveAllAreaTriggersInMoveList();
if (!m_mapRefManager.isEmpty() || !m_activeNonPlayers.empty())
ProcessRelocationNotifies(t_diff);
@@ -1092,6 +1102,39 @@ void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float
ASSERT(integrity_check == old_cell);
}
+void Map::AreaTriggerRelocation(AreaTrigger* at, float x, float y, float z, float orientation)
+{
+ Cell integrity_check(at->GetPositionX(), at->GetPositionY());
+ Cell old_cell = at->GetCurrentCell();
+
+ ASSERT(integrity_check == old_cell);
+ Cell new_cell(x, y);
+
+ if (!getNGrid(new_cell.GridX(), new_cell.GridY()))
+ return;
+
+ // delay areatrigger move for grid/cell to grid/cell moves
+ if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "AreaTrigger (%s) added to moving list from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", at->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+#endif
+ AddAreaTriggerToMoveList(at, x, y, z, orientation);
+ // in diffcell/diffgrid case notifiers called at finishing move at in Map::MoveAllAreaTriggersInMoveList
+ }
+ else
+ {
+ at->Relocate(x, y, z, orientation);
+ at->UpdateShape();
+ at->UpdateObjectVisibility(false);
+ RemoveAreaTriggerFromMoveList(at);
+ }
+
+ old_cell = at->GetCurrentCell();
+ integrity_check = Cell(at->GetPositionX(), at->GetPositionY());
+ ASSERT(integrity_check == old_cell);
+}
+
void Map::AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang)
{
if (_creatureToMoveLock) //can this happen?
@@ -1149,6 +1192,25 @@ void Map::RemoveDynamicObjectFromMoveList(DynamicObject* dynObj)
dynObj->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
}
+void Map::AddAreaTriggerToMoveList(AreaTrigger* at, float x, float y, float z, float ang)
+{
+ if (_areaTriggersToMoveLock) //can this happen?
+ return;
+
+ if (at->_moveState == MAP_OBJECT_CELL_MOVE_NONE)
+ _areaTriggersToMove.push_back(at);
+ at->SetNewCellPosition(x, y, z, ang);
+}
+
+void Map::RemoveAreaTriggerFromMoveList(AreaTrigger* at)
+{
+ if (_areaTriggersToMoveLock) //can this happen?
+ return;
+
+ if (at->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE)
+ at->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
+}
+
void Map::MoveAllCreaturesInMoveList()
{
_creatureToMoveLock = true;
@@ -1288,6 +1350,44 @@ void Map::MoveAllDynamicObjectsInMoveList()
_dynamicObjectsToMoveLock = false;
}
+void Map::MoveAllAreaTriggersInMoveList()
+{
+ _areaTriggersToMoveLock = true;
+ for (std::vector<AreaTrigger*>::iterator itr = _areaTriggersToMove.begin(); itr != _areaTriggersToMove.end(); ++itr)
+ {
+ AreaTrigger* at = *itr;
+ if (at->FindMap() != this) //transport is teleported to another map
+ continue;
+
+ if (at->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE)
+ {
+ at->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
+ continue;
+ }
+
+ at->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
+ if (!at->IsInWorld())
+ continue;
+
+ // do move or do move to respawn or remove creature if previous all fail
+ if (AreaTriggerCellRelocation(at, Cell(at->_newPosition.m_positionX, at->_newPosition.m_positionY)))
+ {
+ // update pos
+ at->Relocate(at->_newPosition);
+ at->UpdateObjectVisibility(false);
+ }
+ else
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "AreaTrigger (%s) cannot be moved to unloaded grid.", at->GetGUID().ToString().c_str());
+#endif
+ }
+ }
+
+ _areaTriggersToMove.clear();
+ _areaTriggersToMoveLock = false;
+}
+
bool Map::CreatureCellRelocation(Creature* c, Cell new_cell)
{
Cell const& old_cell = c->GetCurrentCell();
@@ -1471,6 +1571,67 @@ bool Map::DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell)
return false;
}
+bool Map::AreaTriggerCellRelocation(AreaTrigger* at, Cell new_cell)
+{
+ Cell const& old_cell = at->GetCurrentCell();
+ if (!old_cell.DiffGrid(new_cell)) // in same grid
+ {
+ // if in same cell then none do
+ if (old_cell.DiffCell(new_cell))
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "AreaTrigger (%s) moved in grid[%u, %u] from cell[%u, %u] to cell[%u, %u].", at->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY());
+#endif
+
+ at->RemoveFromGrid();
+ AddToGrid(at, new_cell);
+ }
+ else
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "AreaTrigger (%s) moved in same grid[%u, %u]cell[%u, %u].", at->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY());
+#endif
+ }
+
+ return true;
+ }
+
+ // in diff. grids but active AreaTrigger
+ if (at->isActiveObject())
+ {
+ EnsureGridLoadedForActiveObject(new_cell, at);
+
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "Active AreaTrigger (%s) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", at->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+#endif
+
+ at->RemoveFromGrid();
+ AddToGrid(at, new_cell);
+
+ return true;
+ }
+
+ // in diff. loaded grid normal AreaTrigger
+ if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY())))
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "AreaTrigger (%s) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", at->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+#endif
+
+ at->RemoveFromGrid();
+ EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY()));
+ AddToGrid(at, new_cell);
+
+ return true;
+ }
+
+ // fail to move: normal AreaTrigger attempt move to unloaded grid
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "AreaTrigger (%s) attempted to move from grid[%u, %u]cell[%u, %u] to unloaded grid[%u, %u]cell[%u, %u].", at->GetGUID().ToString().c_str(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+#endif
+ return false;
+}
+
bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly)
{
float resp_x, resp_y, resp_z, resp_o;
@@ -1550,6 +1711,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
// Must know real mob position before move
MoveAllCreaturesInMoveList();
MoveAllGameObjectsInMoveList();
+ MoveAllAreaTriggersInMoveList();
// move creatures to respawn grids if this is diff.grid or to remove list
ObjectGridEvacuator worker;
@@ -1559,6 +1721,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
// Finish creature moves, remove and delete all creatures with delayed remove before unload
MoveAllCreaturesInMoveList();
MoveAllGameObjectsInMoveList();
+ MoveAllAreaTriggersInMoveList();
}
{
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index b209f83189a..913128f818d 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -299,6 +299,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
void CreatureRelocation(Creature* creature, float x, float y, float z, float ang, bool respawnRelocationOnFail = true);
void GameObjectRelocation(GameObject* go, float x, float y, float z, float orientation, bool respawnRelocationOnFail = true);
void DynamicObjectRelocation(DynamicObject* go, float x, float y, float z, float orientation);
+ void AreaTriggerRelocation(AreaTrigger* at, float x, float y, float z, float orientation);
template<class T, class CONTAINER>
void Visit(const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor);
@@ -359,6 +360,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
void MoveAllCreaturesInMoveList();
void MoveAllGameObjectsInMoveList();
void MoveAllDynamicObjectsInMoveList();
+ void MoveAllAreaTriggersInMoveList();
void RemoveAllObjectsInRemoveList();
virtual void RemoveAllPlayers();
@@ -588,6 +590,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
bool CreatureCellRelocation(Creature* creature, Cell new_cell);
bool GameObjectCellRelocation(GameObject* go, Cell new_cell);
bool DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell);
+ bool AreaTriggerCellRelocation(AreaTrigger* at, Cell new_cell);
template<class T> void InitializeObject(T* obj);
void AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang);
@@ -596,6 +599,8 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
void RemoveGameObjectFromMoveList(GameObject* go);
void AddDynamicObjectToMoveList(DynamicObject* go, float x, float y, float z, float ang);
void RemoveDynamicObjectFromMoveList(DynamicObject* go);
+ void AddAreaTriggerToMoveList(AreaTrigger* at, float x, float y, float z, float ang);
+ void RemoveAreaTriggerFromMoveList(AreaTrigger* at);
bool _creatureToMoveLock;
std::vector<Creature*> _creaturesToMove;
@@ -606,6 +611,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
bool _dynamicObjectsToMoveLock;
std::vector<DynamicObject*> _dynamicObjectsToMove;
+ bool _areaTriggersToMoveLock;
+ std::vector<AreaTrigger*> _areaTriggersToMove;
+
bool IsGridLoaded(const GridCoord &) const;
void EnsureGridCreated(const GridCoord &);
void EnsureGridCreated_i(const GridCoord &);
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 2e48d791525..7c94aa3ed55 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -97,6 +97,10 @@ struct is_script_database_bound<AchievementCriteriaScript>
: std::true_type { };
template<>
+struct is_script_database_bound<AreaTriggerEntityScript>
+ : std::true_type { };
+
+template<>
struct is_script_database_bound<SceneScript>
: std::true_type { };
@@ -662,6 +666,35 @@ private:
bool swapped;
};
+/// This hook is responsible for swapping AreaTriggerEntityScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<AreaTriggerEntityScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ swapped = false;
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
/// This hook is responsible for swapping SceneScript's
template<typename Base>
class ScriptRegistrySwapHooks<SceneScript, Base>
@@ -2337,6 +2370,73 @@ void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& dama
FOREACH_SCRIPT(PlayerScript)->ModifySpellDamageTaken(target, attacker, damage);
}
+// AreaTriggerEntityScript
+void ScriptMgr::OnAreaTriggerEntityInitialize(AreaTrigger* areaTrigger)
+{
+ ASSERT(areaTrigger);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnInitialize(areaTrigger);
+}
+
+void ScriptMgr::OnAreaTriggerEntityCreate(AreaTrigger* areaTrigger)
+{
+ ASSERT(areaTrigger);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnCreate(areaTrigger);
+}
+
+void ScriptMgr::OnAreaTriggerEntityUpdate(AreaTrigger* areaTrigger, uint32 diff)
+{
+ ASSERT(areaTrigger);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnUpdate(areaTrigger, diff);
+}
+
+void ScriptMgr::OnAreaTriggerEntitySplineIndexReached(AreaTrigger* areaTrigger, int splineIndex)
+{
+ ASSERT(areaTrigger);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnSplineIndexReached(areaTrigger, splineIndex);
+}
+
+void ScriptMgr::OnAreaTriggerEntityDestinationReached(AreaTrigger* areaTrigger)
+{
+ ASSERT(areaTrigger);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnDestinationReached(areaTrigger);
+}
+
+void ScriptMgr::OnAreaTriggerEntityUnitEnter(AreaTrigger* areaTrigger, Unit* unit)
+{
+ ASSERT(areaTrigger);
+ ASSERT(unit);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnUnitEnter(areaTrigger, unit);
+}
+
+void ScriptMgr::OnAreaTriggerEntityUnitExit(AreaTrigger* areaTrigger, Unit* unit)
+{
+ ASSERT(areaTrigger);
+ ASSERT(unit);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnUnitExit(areaTrigger, unit);
+}
+
+void ScriptMgr::OnAreaTriggerEntityRemove(AreaTrigger* areaTrigger)
+{
+ ASSERT(areaTrigger);
+
+ GET_SCRIPT(AreaTriggerEntityScript, areaTrigger->GetScriptId(), tmpscript);
+ tmpscript->OnRemove(areaTrigger);
+}
+
// Scene
void ScriptMgr::OnSceneStart(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate)
{
@@ -2546,6 +2646,12 @@ GroupScript::GroupScript(const char* name)
ScriptRegistry<GroupScript>::Instance()->AddScript(this);
}
+AreaTriggerEntityScript::AreaTriggerEntityScript(const char* name)
+ : ScriptObject(name)
+{
+ ScriptRegistry<AreaTriggerEntityScript>::Instance()->AddScript(this);
+}
+
// Specialize for each script type class like so:
template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
template class TC_GAME_API ScriptRegistry<ServerScript>;
@@ -2573,4 +2679,5 @@ template class TC_GAME_API ScriptRegistry<GuildScript>;
template class TC_GAME_API ScriptRegistry<GroupScript>;
template class TC_GAME_API ScriptRegistry<UnitScript>;
template class TC_GAME_API ScriptRegistry<AccountScript>;
+template class TC_GAME_API ScriptRegistry<AreaTriggerEntityScript>;
template class TC_GAME_API ScriptRegistry<SceneScript>;
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 6c0b660fb0b..76a5f663f82 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -28,6 +28,7 @@
#include "Weather.h"
class AccountMgr;
+class AreaTrigger;
class AuctionHouseObject;
class Aura;
class AuraScript;
@@ -836,6 +837,38 @@ class TC_GAME_API GroupScript : public ScriptObject
virtual void OnDisband(Group* /*group*/) { }
};
+class TC_GAME_API AreaTriggerEntityScript : public ScriptObject
+{
+ protected:
+
+ AreaTriggerEntityScript(const char* name);
+
+ public:
+ // Called when the AreaTrigger has just been initialized, just before added to map
+ virtual void OnInitialize(AreaTrigger* /*areaTrigger*/) { }
+
+ // Called when the AreaTrigger has just been created
+ virtual void OnCreate(AreaTrigger* /*areaTrigger*/) { }
+
+ // Called on each AreaTrigger update
+ virtual void OnUpdate(AreaTrigger* /*areaTrigger*/, uint32 /*diff*/) { }
+
+ // Called when the AreaTrigger reach splineIndex
+ virtual void OnSplineIndexReached(AreaTrigger* /*areaTrigger*/, int /*splineIndex*/) { }
+
+ // Called when the AreaTrigger reach its destination
+ virtual void OnDestinationReached(AreaTrigger* /*areaTrigger*/) { }
+
+ // Called when an unit enter the AreaTrigger
+ virtual void OnUnitEnter(AreaTrigger* /*areaTrigger*/, Unit* /*unit*/) { }
+
+ // Called when an unit exit the AreaTrigger, or when the AreaTrigger is removed
+ virtual void OnUnitExit(AreaTrigger* /*areaTrigger*/, Unit* /*unit*/) { }
+
+ // Called when the AreaTrigger is removed
+ virtual void OnRemove(AreaTrigger* /*areaTrigger*/) { }
+};
+
class TC_GAME_API SceneScript : public ScriptObject
{
protected:
@@ -1133,6 +1166,17 @@ class TC_GAME_API ScriptMgr
void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage);
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage);
+ public: /* AreaTriggerEntityScript */
+
+ void OnAreaTriggerEntityInitialize(AreaTrigger* areaTrigger);
+ void OnAreaTriggerEntityCreate(AreaTrigger* areaTrigger);
+ void OnAreaTriggerEntityUpdate(AreaTrigger* areaTrigger, uint32 diff);
+ void OnAreaTriggerEntitySplineIndexReached(AreaTrigger* areaTrigger, int splineIndex);
+ void OnAreaTriggerEntityDestinationReached(AreaTrigger* areaTrigger);
+ void OnAreaTriggerEntityUnitEnter(AreaTrigger* areaTrigger, Unit* unit);
+ void OnAreaTriggerEntityUnitExit(AreaTrigger* areaTrigger, Unit* unit);
+ void OnAreaTriggerEntityRemove(AreaTrigger* areaTrigger);
+
public: /* SceneScript */
void OnSceneStart(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate);
void OnSceneTrigger(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate, std::string const& triggerName);
diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h
index 034496d027d..732a8c1d081 100644
--- a/src/server/game/Server/Packets/AllPackets.h
+++ b/src/server/game/Server/Packets/AllPackets.h
@@ -19,6 +19,7 @@
#define AllPackets_h__
#include "AchievementPackets.h"
+#include "AreaTriggerPackets.h"
#include "ArtifactPackets.h"
#include "AuctionHousePackets.h"
#include "AuthenticationPackets.h"
diff --git a/src/server/game/Server/Packets/AreaTriggerPackets.cpp b/src/server/game/Server/Packets/AreaTriggerPackets.cpp
new file mode 100644
index 00000000000..446a37534a9
--- /dev/null
+++ b/src/server/game/Server/Packets/AreaTriggerPackets.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 "AreaTriggerPackets.h"
+
+ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::AreaTrigger::AreaTriggerSplineInfo const& areaTriggerSpline)
+{
+ data << uint32(areaTriggerSpline.TimeToTarget);
+ data << uint32(areaTriggerSpline.ElapsedTimeForMovement);
+
+ data.WriteBits(areaTriggerSpline.Points.size(), 16);
+ data.FlushBits();
+
+ for (G3D::Vector3 const& point : areaTriggerSpline.Points)
+ data << point;
+
+ return data;
+}
+
+ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::AreaTrigger::AreaTriggerUnkTypeInfo const& areaTriggerUnkType)
+{
+ data.WriteBit(areaTriggerUnkType.AreaTriggerUnkGUID.is_initialized());
+ data.WriteBit(areaTriggerUnkType.Center.is_initialized());
+ data.WriteBit(areaTriggerUnkType.UnkBit1);
+ data.WriteBit(areaTriggerUnkType.UnkBit2);
+
+ data << uint32(areaTriggerUnkType.UnkUInt1);
+ data << int32(areaTriggerUnkType.UnkInt1);
+ data << uint32(areaTriggerUnkType.UnkUInt2);
+ data << float(areaTriggerUnkType.Radius);
+ data << float(areaTriggerUnkType.BlendFromRadius);
+ data << float(areaTriggerUnkType.InitialAngel);
+ data << float(areaTriggerUnkType.ZOffset);
+
+ if (areaTriggerUnkType.AreaTriggerUnkGUID)
+ data << *areaTriggerUnkType.AreaTriggerUnkGUID;
+
+ if (areaTriggerUnkType.Center)
+ data << *areaTriggerUnkType.Center;
+
+ return data;
+}
+
+void WorldPackets::AreaTrigger::AreaTrigger::Read()
+{
+ _worldPacket >> AreaTriggerID;
+ Entered = _worldPacket.ReadBit();
+ FromClient = _worldPacket.ReadBit();
+}
+
+WorldPacket const* WorldPackets::AreaTrigger::AreaTriggerDenied::Write()
+{
+ _worldPacket << int32(AreaTriggerID);
+ _worldPacket.WriteBit(Entered);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::AreaTrigger::AreaTriggerRePath::Write()
+{
+ _worldPacket << TriggerGUID;
+ _worldPacket << AreaTriggerSpline;
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::AreaTrigger::AreaTriggerReShape::Write()
+{
+ _worldPacket << TriggerGUID;
+
+ _worldPacket.WriteBit(AreaTriggerSpline.is_initialized());
+ _worldPacket.WriteBit(AreaTriggerUnkType.is_initialized());
+ _worldPacket.FlushBits();
+
+ if (AreaTriggerSpline)
+ _worldPacket << *AreaTriggerSpline;
+
+ if (AreaTriggerUnkType)
+ _worldPacket << *AreaTriggerUnkType;
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/AreaTriggerPackets.h b/src/server/game/Server/Packets/AreaTriggerPackets.h
new file mode 100644
index 00000000000..967a8ba0f5f
--- /dev/null
+++ b/src/server/game/Server/Packets/AreaTriggerPackets.h
@@ -0,0 +1,107 @@
+/*
+ * 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 AreaTriggerPackets_h__
+#define AreaTriggerPackets_h__
+
+#include "Packet.h"
+#include "PacketUtilities.h"
+#include "ObjectGuid.h"
+
+namespace WorldPackets
+{
+ namespace AreaTrigger
+ {
+ struct AreaTriggerSplineInfo
+ {
+ uint32 TimeToTarget = 0;
+ uint32 ElapsedTimeForMovement = 0;
+ std::vector<G3D::Vector3> Points;
+ };
+
+ struct AreaTriggerUnkTypeInfo
+ {
+ Optional<ObjectGuid> AreaTriggerUnkGUID;
+ Optional<G3D::Vector3> Center;
+ bool UnkBit1 = false;
+ bool UnkBit2 = false;
+ uint32 UnkUInt1 = 0;
+ int32 UnkInt1 = 0;
+ uint32 UnkUInt2 = 0;
+ float Radius = 0.0;
+ float BlendFromRadius = 0.0f;
+ float InitialAngel = 0.0f;
+ float ZOffset = 0.0f;
+ };
+
+ class AreaTrigger final : public ClientPacket
+ {
+ public:
+ AreaTrigger(WorldPacket&& packet) : ClientPacket(CMSG_AREA_TRIGGER, std::move(packet)) { }
+
+ void Read() override;
+
+ int32 AreaTriggerID = 0;
+ bool Entered = false;
+ bool FromClient = false;
+ };
+
+ class AreaTriggerDenied final : public ServerPacket
+ {
+ public:
+ AreaTriggerDenied() : ServerPacket(SMSG_AREA_TRIGGER_DENIED, 5) { }
+
+ int32 AreaTriggerID = 0;
+ bool Entered = false;
+
+ WorldPacket const* Write() override;
+ };
+
+ class AreaTriggerNoCorpse final : public ServerPacket
+ {
+ public:
+ AreaTriggerNoCorpse() : ServerPacket(SMSG_AREA_TRIGGER_NO_CORPSE, 0) { }
+
+ WorldPacket const* Write() override { return &_worldPacket; }
+ };
+
+ class AreaTriggerRePath final : public ServerPacket
+ {
+ public:
+ AreaTriggerRePath() : ServerPacket(SMSG_AREA_TRIGGER_RE_PATH, 50) { }
+
+ WorldPacket const* Write() override;
+
+ AreaTriggerSplineInfo AreaTriggerSpline;
+ ObjectGuid TriggerGUID;
+ };
+
+ class AreaTriggerReShape final : public ServerPacket
+ {
+ public:
+ AreaTriggerReShape() : ServerPacket(SMSG_AREA_TRIGGER_RE_SHAPE, 17) { }
+
+ WorldPacket const* Write() override;
+
+ Optional<AreaTriggerSplineInfo> AreaTriggerSpline;
+ Optional<AreaTriggerUnkTypeInfo> AreaTriggerUnkType;
+ ObjectGuid TriggerGUID;
+ };
+ }
+}
+
+#endif // AreaTriggerPackets_h__
diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp
index 18e46e81356..4880b750319 100644
--- a/src/server/game/Server/Packets/MiscPackets.cpp
+++ b/src/server/game/Server/Packets/MiscPackets.cpp
@@ -178,13 +178,6 @@ WorldPacket const* WorldPackets::Misc::WorldServerInfo::Write()
return &_worldPacket;
}
-void WorldPackets::Misc::AreaTrigger::Read()
-{
- _worldPacket >> AreaTriggerID;
- Entered = _worldPacket.ReadBit();
- FromClient = _worldPacket.ReadBit();
-}
-
void WorldPackets::Misc::SetDungeonDifficulty::Read()
{
_worldPacket >> DifficultyID;
diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h
index becb5c81661..b9ee1dd0480 100644
--- a/src/server/game/Server/Packets/MiscPackets.h
+++ b/src/server/game/Server/Packets/MiscPackets.h
@@ -247,18 +247,6 @@ namespace WorldPackets
Optional<uint32> InstanceGroupSize;
};
- class AreaTrigger final : public ClientPacket
- {
- public:
- AreaTrigger(WorldPacket&& packet) : ClientPacket(CMSG_AREA_TRIGGER, std::move(packet)) { }
-
- void Read() override;
-
- int32 AreaTriggerID = 0;
- bool Entered = false;
- bool FromClient = false;
- };
-
class SetDungeonDifficulty final : public ClientPacket
{
public:
@@ -390,15 +378,7 @@ namespace WorldPackets
uint32 Response = 0;
};
- class AreaTriggerNoCorpse final : public ServerPacket
- {
- public:
- AreaTriggerNoCorpse() : ServerPacket(SMSG_AREA_TRIGGER_NO_CORPSE, 0) { }
-
- WorldPacket const* Write() override { return &_worldPacket; }
- };
-
- class TC_GAME_API Weather final : public ServerPacket
+ class TC_GAME_API Weather final : public ServerPacket
{
public:
Weather();
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 26f151a5d2b..7bc9b2c59d0 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -857,10 +857,10 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_GUILD_ACHIEVEMENTS, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ARCHAEOLOGY_SURVERY_CAST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_SPIRIT_HEALER_TIME, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_DENIED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_DENIED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_NO_CORPSE, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_RE_PATH, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_RE_SHAPE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_RE_PATH, STATUS_NEVER, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_RE_SHAPE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ARENA_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ARENA_PREP_OPPONENT_SPECIALIZATIONS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ARTIFACT_APPEARANCE_CHANGED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index dd5c32133c3..1469a1f1d4f 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -81,6 +81,11 @@ namespace WorldPackets
class GuildGetAchievementMembers;
}
+ namespace AreaTrigger
+ {
+ class AreaTrigger;
+ }
+
namespace Artifact
{
class ArtifactAddPower;
@@ -405,7 +410,6 @@ namespace WorldPackets
namespace Misc
{
- class AreaTrigger;
class SetSelection;
class ViolenceLevel;
class TimeSyncResponse;
@@ -1228,7 +1232,7 @@ class TC_GAME_API WorldSession
void HandleDelIgnoreOpcode(WorldPackets::Social::DelIgnore& packet);
void HandleSetContactNotesOpcode(WorldPackets::Social::SetContactNotes& packet);
- void HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& packet);
+ void HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigger& packet);
void HandleSetFactionAtWar(WorldPackets::Character::SetFactionAtWar& packet);
void HandleSetFactionNotAtWar(WorldPackets::Character::SetFactionNotAtWar& packet);
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 14b2e5eb607..856d8e586d0 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -454,7 +454,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //392
&AuraEffect::HandleNULL, //393
&AuraEffect::HandleShowConfirmationPrompt, //394 SPELL_AURA_SHOW_CONFIRMATION_PROMPT
- &AuraEffect::HandleNULL, //395 SPELL_AURA_AREA_TRIGGER
+ &AuraEffect::HandleCreateAreaTrigger, //395 SPELL_AURA_AREA_TRIGGER
&AuraEffect::HandleNoImmediateEffect, //396 SPELL_AURA_PROC_ON_POWER_AMOUNT_2 implemented in Unit::HandleAuraProcOnPowerAmount
&AuraEffect::HandleNULL, //397
&AuraEffect::HandleNULL, //398
@@ -6573,3 +6573,23 @@ void AuraEffect::HandlePlayScene(AuraApplication const* aurApp, uint8 mode, bool
player->GetSceneMgr().CancelSceneByPackageId(sceneTemplate->ScenePackageId);
}
}
+
+void AuraEffect::HandleCreateAreaTrigger(AuraApplication const* aurApp, uint8 mode, bool apply) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_REAL))
+ return;
+
+ Unit* target = aurApp->GetTarget();
+
+ if (apply)
+ {
+ AreaTrigger* areaTrigger = new AreaTrigger();
+ if (!areaTrigger->CreateAreaTrigger(GetMiscValue(), GetCaster(), target, GetSpellInfo(), *target, GetBase()->GetDuration(), GetBase()->GetSpellXSpellVisualId()))
+ delete areaTrigger;
+ }
+ else
+ {
+ if (Unit* caster = GetCaster())
+ caster->RemoveAreaTrigger(GetSpellInfo()->Id);
+ }
+}
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h
index eb7dc1f3448..494884f355e 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.h
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.h
@@ -310,6 +310,7 @@ class TC_GAME_API AuraEffect
void HandleOverridePetSpecs(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAllowUsingGameobjectsWhileMounted(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandlePlayScene(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleCreateAreaTrigger(AuraApplication const* aurApp, uint8 mode, bool apply) const;
// aura effect periodic tick handlers
void HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 12b56dc1ccf..2bd23ee79dc 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2021,9 +2021,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
if (!m_originalCaster)
return;
- int32 duration = m_spellInfo->GetDuration();
- if (Player* modOwner = m_originalCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
+ int32 duration = m_spellInfo->CalcDuration(m_originalCaster);
TempSummon* summon = NULL;
@@ -2377,7 +2375,7 @@ void Spell::EffectAddFarsight(SpellEffIndex /*effIndex*/)
return;
float radius = effectInfo->CalcRadius();
- int32 duration = m_spellInfo->GetDuration();
+ int32 duration = m_spellInfo->CalcDuration(m_caster);
// Caster not in world, might be spell triggered from aura removal
if (!m_caster->IsInWorld())
return;
@@ -3168,7 +3166,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex)
pGameObj->CopyPhaseFrom(m_caster);
- int32 duration = m_spellInfo->GetDuration();
+ int32 duration = m_spellInfo->CalcDuration(m_caster);
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
pGameObj->SetSpellId(m_spellInfo->Id);
@@ -3794,7 +3792,7 @@ void Spell::EffectDuel(SpellEffIndex effIndex)
pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction());
pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1);
- int32 duration = m_spellInfo->GetDuration();
+ int32 duration = m_spellInfo->CalcDuration(m_caster);
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
pGameObj->SetSpellId(m_spellInfo->Id);
@@ -4128,7 +4126,7 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex)
go->CopyPhaseFrom(m_caster);
//pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel());
- int32 duration = m_spellInfo->GetDuration();
+ int32 duration = m_spellInfo->CalcDuration(m_caster);
go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
go->SetSpellId(m_spellInfo->Id);
m_caster->AddGameObject(go);
@@ -4785,7 +4783,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex)
pGameObj->CopyPhaseFrom(m_caster);
- int32 duration = m_spellInfo->GetDuration();
+ int32 duration = m_spellInfo->CalcDuration(m_caster);
switch (goinfo->type)
{
@@ -5296,10 +5294,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const*
level = skill202 / 5;
float radius = 5.0f;
- int32 duration = m_spellInfo->GetDuration();
-
- if (Player* modOwner = m_originalCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
+ int32 duration = m_spellInfo->CalcDuration(m_originalCaster);
//TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Map* map = caster->GetMap();
@@ -5612,17 +5607,12 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex /*effIndex*/)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
- Position pos;
if (!m_targets.HasDst())
- pos = GetCaster()->GetPosition();
- else
- pos = destTarget->GetPosition();
-
- // trigger entry/miscvalue relation is currently unknown, for now use MiscValue as trigger entry
- uint32 triggerEntry = effectInfo->MiscValue;
+ return;
- AreaTrigger * areaTrigger = new AreaTrigger;
- if (!areaTrigger->CreateAreaTrigger(GetCaster()->GetMap()->GenerateLowGuid<HighGuid::AreaTrigger>(), triggerEntry, GetCaster(), GetSpellInfo(), pos, m_SpellVisual))
+ int32 duration = GetSpellInfo()->CalcDuration(GetCaster());
+ AreaTrigger* areaTrigger = new AreaTrigger();
+ if (!areaTrigger->CreateAreaTrigger(effectInfo->MiscValue, GetCaster(), nullptr, GetSpellInfo(), destTarget->GetPosition(), duration, m_SpellVisual, m_castId))
delete areaTrigger;
}
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index ccc5d81d5c8..c08a0c43374 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -2445,6 +2445,17 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const
return range;
}
+int32 SpellInfo::CalcDuration(Unit* caster /*= nullptr*/) const
+{
+ int32 duration = GetDuration();
+
+ if (caster)
+ if (Player* modOwner = caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(Id, SPELLMOD_DURATION, duration);
+
+ return duration;
+}
+
int32 SpellInfo::GetDuration() const
{
if (!DurationEntry)
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 3f8a4dc38dd..2c5049dd4ae 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -503,6 +503,7 @@ public:
float GetMinRange(bool positive = false) const;
float GetMaxRange(bool positive = false, Unit* caster = NULL, Spell* spell = NULL) const;
+ int32 CalcDuration(Unit* caster = nullptr) const;
int32 GetDuration() const;
int32 GetMaxDuration() const;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index c2da938cfa8..382d1cd3b65 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -22,6 +22,7 @@
#include "World.h"
#include "AchievementMgr.h"
+#include "AreaTriggerDataStore.h"
#include "ArenaTeamMgr.h"
#include "AuctionHouseBot.h"
#include "AuctionHouseMgr.h"
@@ -1794,6 +1795,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Pet Name Parts...");
sObjectMgr->LoadPetNames();
+ TC_LOG_INFO("server.loading", "Loading AreaTrigger Templates...");
+ sAreaTriggerDataStore->LoadAreaTriggerTemplates();
+
TC_LOG_INFO("server.loading", "Loading Scenes Templates...");
sObjectMgr->LoadSceneTemplates();
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index 0f1325be444..4bc97623254 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -24,6 +24,7 @@ EndScriptData */
#include "AccountMgr.h"
#include "AchievementMgr.h"
+#include "AreaTriggerDataStore.h"
#include "AuctionHouseMgr.h"
#include "BattlegroundMgr.h"
#include "Chat.h"
@@ -72,6 +73,7 @@ public:
{ "areatrigger_involvedrelation", rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_INVOLVEDRELATION, true, &HandleReloadQuestAreaTriggersCommand, "" },
{ "areatrigger_tavern", rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TAVERN, true, &HandleReloadAreaTriggerTavernCommand, "" },
{ "areatrigger_teleport", rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TELEPORT, true, &HandleReloadAreaTriggerTeleportCommand, "" },
+ { "areatrigger_template", rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TEMPLATE, true, &HandleReloadAreaTriggerTemplateCommand, "" },
{ "autobroadcast", rbac::RBAC_PERM_COMMAND_RELOAD_AUTOBROADCAST, true, &HandleReloadAutobroadcastCommand, "" },
{ "battleground_template", rbac::RBAC_PERM_COMMAND_RELOAD_BATTLEGROUND_TEMPLATE, true, &HandleReloadBattlegroundTemplate, "" },
{ "character_template", rbac::RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE, true, &HandleReloadCharacterTemplate, "" },
@@ -1127,6 +1129,13 @@ public:
return true;
}
+ static bool HandleReloadAreaTriggerTemplateCommand(ChatHandler* handler, const char* /*args*/)
+ {
+ TC_LOG_INFO("misc", "Reloading areatrigger_template table...");
+ sAreaTriggerDataStore->LoadAreaTriggerTemplates();
+ handler->SendGlobalGMSysMessage("AreaTrigger templates reloaded. Already spawned AT won't be affected. New scriptname need a reboot.");
+ return true;
+ }
static bool HandleReloadSceneTemplateCommand(ChatHandler* handler, const char* /*args*/)
{
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 0a26196ccc3..96b8aace805 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -3778,6 +3778,7 @@ Logger.mmaps=3,Server
#Logger.condition=3,Console Server
#Logger.criteria=3,Console Server
#Logger.criteria.achievement=3,Console Server
+#Logger.entities.areatrigger=3,Console Server
#Logger.entities.pet=3,Console Server
#Logger.entities.player.character=3,Console Server
#Logger.entities.player.dump=3,Console Server