diff options
Diffstat (limited to 'src')
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 |