diff options
Diffstat (limited to 'src')
47 files changed, 1464 insertions, 502 deletions
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 9eafcc30c81..efe55830b1b 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -128,7 +128,7 @@ class UnitAI virtual void InitializeAI() { if (!me->isDead()) Reset(); } - virtual void Reset() { }; + virtual void Reset() { } // Called when unit is charmed virtual void OnCharmed(bool apply) = 0; diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 8f9db8d5a27..339788dee01 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -802,6 +802,7 @@ class RBACData * @return Success or failure (with reason) to grant the permission * * Example Usage: + * @code * // previously defined "RBACData* rbac" with proper initialization * uint32 permissionId = 2; * if (rbac->GrantRole(permissionId) == RBAC_IN_DENIED_LIST) @@ -825,6 +826,7 @@ class RBACData * @return Success or failure (with reason) to deny the permission * * Example Usage: + * @code * // previously defined "RBACData* rbac" with proper initialization * uint32 permissionId = 2; * if (rbac->DenyRole(permissionId) == RBAC_ID_DOES_NOT_EXISTS) @@ -849,6 +851,7 @@ class RBACData * @return Success or failure (with reason) to remove the permission * * Example Usage: + * @code * // previously defined "RBACData* rbac" with proper initialization * uint32 permissionId = 2; * if (rbac->RevokeRole(permissionId) == RBAC_OK) @@ -939,9 +942,6 @@ class RBACData * * Given a list of permissions, gets all the inherited permissions * @param permissions The list of permissions to expand - * - * @return new list of permissions containing original permissions and - * all other pemissions that are linked to the original ones */ void ExpandPermissions(RBACPermissionContainer& permissions); diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h index b0adaa4078d..7e8debfd24d 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.h +++ b/src/server/game/Battlegrounds/BattlegroundQueue.h @@ -106,7 +106,7 @@ class BattlegroundQueue class SelectionPool { public: - SelectionPool(): PlayerCount(0) { }; + SelectionPool(): PlayerCount(0) { } void Init(); bool AddGroup(GroupQueueInfo* ginfo, uint32 desiredCount); bool KickGroup(uint32 size); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index e14e9fadafc..f267bf7c6c6 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -99,7 +99,7 @@ bool BattlegroundSA::ResetObjs() for (uint8 i = BG_SA_MAXNPC; i < BG_SA_MAXNPC + BG_SA_MAX_GY; i++) DelCreature(i); - for (uint8 i = 0; i < 6; i++) + for (uint8 i = 0; i < MAX_GATES; ++i) GateStatus[i] = BG_SA_GATE_OK; if (!AddCreature(BG_SA_NpcEntries[BG_SA_NPC_KANRETHAD], BG_SA_NPC_KANRETHAD, BG_SA_NpcSpawnlocs[BG_SA_NPC_KANRETHAD])) @@ -179,9 +179,6 @@ bool BattlegroundSA::ResetObjs() GetBGObject(BG_SA_TITAN_RELIC)->SetFaction(atF); GetBGObject(BG_SA_TITAN_RELIC)->Refresh(); - for (uint8 i = 0; i <= 5; i++) - GateStatus[i] = BG_SA_GATE_OK; - TotalTime = 0; ShipsStarted = false; @@ -221,6 +218,8 @@ bool BattlegroundSA::ResetObjs() GetBGObject(i)->SetFaction(atF); } + UpdateObjectInteractionFlags(); + for (uint8 i = BG_SA_BOMB; i < BG_SA_MAXOBJ; i++) { if (!AddObject(i, BG_SA_ObjEntries[BG_SA_BOMB], BG_SA_ObjSpawnlocs[i], 0, 0, 0, 0, RESPAWN_ONE_DAY)) @@ -481,28 +480,7 @@ void BattlegroundSA::AddPlayer(Player* player) SendTransportInit(player); - if (!ShipsStarted) - { - if (player->GetTeamId() == Attackers) - { - player->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); - - if (urand(0, 1)) - player->TeleportTo(607, 2682.936f, -830.368f, 15.0f, 2.895f, 0); - else - player->TeleportTo(607, 2577.003f, 980.261f, 15.0f, 0.807f, 0); - - } - else - player->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); - } - else - { - if (player->GetTeamId() == Attackers) - player->TeleportTo(607, 1600.381f, -106.263f, 8.8745f, 3.78f, 0); - else - player->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); - } + TeleportToEntrancePosition(player); } void BattlegroundSA::RemovePlayer(Player* /*player*/, ObjectGuid /*guid*/, uint32 /*team*/) { } @@ -533,23 +511,31 @@ void BattlegroundSA::TeleportPlayers() player->ResetAllPowers(); player->CombatStopWithPets(true); - for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if (Player* p = ObjectAccessor::FindPlayer(itr->first)) - p->CastSpell(p, SPELL_PREPARATION, true); + player->CastSpell(player, SPELL_PREPARATION, true); - if (player->GetTeamId() == Attackers) - { - player->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + TeleportToEntrancePosition(player); + } + } +} - if (urand(0, 1)) - player->TeleportTo(607, 2682.936f, -830.368f, 15.0f, 2.895f, 0); - else - player->TeleportTo(607, 2577.003f, 980.261f, 15.0f, 0.807f, 0); - } +void BattlegroundSA::TeleportToEntrancePosition(Player* player) +{ + if (player->GetTeamId() == Attackers) + { + if (!ShipsStarted) + { + player->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + + if (urand(0, 1)) + player->TeleportTo(607, 2682.936f, -830.368f, 15.0f, 2.895f, 0); else - player->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); + player->TeleportTo(607, 2577.003f, 980.261f, 15.0f, 0.807f, 0); } + else + player->TeleportTo(607, 1600.381f, -106.263f, 8.8745f, 3.78f, 0); } + else + player->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); } void BattlegroundSA::ProcessEvent(WorldObject* obj, uint32 eventId, WorldObject* invoker /*= NULL*/) @@ -628,6 +614,8 @@ void BattlegroundSA::ProcessEvent(WorldObject* obj, uint32 eventId, WorldObject* } } } + + UpdateObjectInteractionFlags(); } else break; @@ -711,7 +699,7 @@ WorldSafeLocsEntry const* BattlegroundSA::GetClosestGraveYard(Player* player) safeloc = BG_SA_GYEntries[BG_SA_DEFENDER_LAST_GY]; closest = sWorldSafeLocsStore.LookupEntry(safeloc); - nearest = std::sqrt((closest->x - x)*(closest->x - x) + (closest->y - y)*(closest->y - y) + (closest->z - z)*(closest->z - z)); + nearest = player->GetExactDistSq(closest->x, closest->y, closest->z); for (uint8 i = BG_SA_RIGHT_CAPTURABLE_GY; i < BG_SA_MAX_GY; i++) { @@ -719,7 +707,7 @@ WorldSafeLocsEntry const* BattlegroundSA::GetClosestGraveYard(Player* player) continue; ret = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); - dist = std::sqrt((ret->x - x)*(ret->x - x) + (ret->y - y)*(ret->y - y) + (ret->z - z)*(ret->z - z)); + dist = player->GetExactDistSq(ret->x, ret->y, ret->z); if (dist < nearest) { closest = ret; @@ -738,23 +726,66 @@ void BattlegroundSA::SendTime() UpdateWorldState(BG_SA_TIMER_SEC_DECS, ((end_of_round%60000)%10000)/1000); } +bool BattlegroundSA::CanInteractWithObject(uint32 objectId) +{ + switch (objectId) + { + case BG_SA_TITAN_RELIC: + if (GateStatus[BG_SA_ANCIENT_GATE] != BG_SA_GATE_DESTROYED || GateStatus[BG_SA_YELLOW_GATE] != BG_SA_GATE_DESTROYED) + return false; + // no break + case BG_SA_CENTRAL_FLAG: + if (GateStatus[BG_SA_RED_GATE] != BG_SA_GATE_DESTROYED && GateStatus[BG_SA_PURPLE_GATE] != BG_SA_GATE_DESTROYED) + return false; + // no break + case BG_SA_LEFT_FLAG: + case BG_SA_RIGHT_FLAG: + if (GateStatus[BG_SA_GREEN_GATE] != BG_SA_GATE_DESTROYED && GateStatus[BG_SA_BLUE_GATE] != BG_SA_GATE_DESTROYED) + return false; + break; + default: + ASSERT(false); + break; + } + + return true; +} + +void BattlegroundSA::UpdateObjectInteractionFlags(uint32 objectId) +{ + if (GameObject* go = GetBGObject(objectId)) + { + if (CanInteractWithObject(objectId)) + go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + else + go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + } +} + +void BattlegroundSA::UpdateObjectInteractionFlags() +{ + for (uint8 i = BG_SA_CENTRAL_FLAG; i <= BG_SA_LEFT_FLAG; ++i) + UpdateObjectInteractionFlags(i); + UpdateObjectInteractionFlags(BG_SA_TITAN_RELIC); +} + void BattlegroundSA::EventPlayerClickedOnFlag(Player* source, GameObject* go) { switch (go->GetEntry()) { case 191307: case 191308: - if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) + if (CanInteractWithObject(BG_SA_LEFT_FLAG)) CaptureGraveyard(BG_SA_LEFT_CAPTURABLE_GY, source); break; case 191305: case 191306: - if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) + if (CanInteractWithObject(BG_SA_RIGHT_FLAG)) CaptureGraveyard(BG_SA_RIGHT_CAPTURABLE_GY, source); break; case 191310: case 191309: - if ((GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) && (GateStatus[BG_SA_RED_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_PURPLE_GATE] == BG_SA_GATE_DESTROYED)) + if (CanInteractWithObject(BG_SA_CENTRAL_FLAG)) CaptureGraveyard(BG_SA_CENTRAL_CAPTURABLE_GY, source); break; default: @@ -856,10 +887,7 @@ void BattlegroundSA::TitanRelicActivated(Player* clicker) if (!clicker) return; - if (GateStatus[BG_SA_ANCIENT_GATE] == BG_SA_GATE_DESTROYED && - GateStatus[BG_SA_YELLOW_GATE] == BG_SA_GATE_DESTROYED && - (GateStatus[BG_SA_PURPLE_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_RED_GATE] == BG_SA_GATE_DESTROYED) && - (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED)) + if (CanInteractWithObject(BG_SA_TITAN_RELIC)) { if (clicker->GetTeamId() == Attackers) { diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.h b/src/server/game/Battlegrounds/Zones/BattlegroundSA.h index 7f9a656c979..118cea41a7b 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.h @@ -620,6 +620,7 @@ class BattlegroundSA : public Battleground * -Teleport all players to good location */ void TeleportPlayers(); + void TeleportToEntrancePosition(Player* player); /** * \brief Called on start and between the two round * -Update faction of all vehicle @@ -627,6 +628,11 @@ class BattlegroundSA : public Battleground void OverrideGunFaction(); /// Set selectable or not demolisher, called on battle start, when boats arrive to dock void DemolisherStartState(bool start); + /// Checks if a player can interact with the given object + bool CanInteractWithObject(uint32 objectId); + /// Updates interaction flags of specific objects + void UpdateObjectInteractionFlags(uint32 objectId); + void UpdateObjectInteractionFlags(); /** * \brief Called when a gate is destroy * -Give honor to player witch destroy it diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h index 9b9c2f1e514..0f2a940e3b4 100644 --- a/src/server/game/Chat/Channels/Channel.h +++ b/src/server/game/Chat/Channels/Channel.h @@ -184,7 +184,7 @@ class Channel void DeVoice(ObjectGuid guid1, ObjectGuid guid2); void JoinNotify(ObjectGuid guid); // invisible notify void LeaveNotify(ObjectGuid guid); // invisible notify - void SetOwnership(bool ownership) { _ownership = ownership; }; + void SetOwnership(bool ownership) { _ownership = ownership; } static void CleanOldChannelsInDB(); private: diff --git a/src/server/game/Entities/Object/Position.cpp b/src/server/game/Entities/Object/Position.cpp index e31b913a25a..530e51cd8f5 100644 --- a/src/server/game/Entities/Object/Position.cpp +++ b/src/server/game/Entities/Object/Position.cpp @@ -17,6 +17,8 @@ #include "Position.h" #include "ByteBuffer.h" +#include "G3D/g3dmath.h" +#include "GridDefines.h" bool Position::operator==(Position const &a) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index af640e4c73c..256cd920880 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -346,9 +346,9 @@ struct Runes struct EnchantDuration { - EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) { }; + EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) { } EnchantDuration(Item* _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot), - leftduration(_leftduration){ ASSERT(item); }; + leftduration(_leftduration){ ASSERT(item); } Item* item; EnchantmentSlot slot; @@ -1839,7 +1839,7 @@ class Player : public Unit, public GridObject<Player> bool UpdatePosition(const Position &pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } void UpdateUnderwaterState(Map* m, float x, float y, float z) override; - void SendMessageToSet(WorldPacket* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); };// overwrite Object::SendMessageToSet + void SendMessageToSet(WorldPacket* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); }// overwrite Object::SendMessageToSet void SendMessageToSetInRange(WorldPacket* data, float fist, bool self) override;// overwrite Object::SendMessageToSetInRange void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only); void SendMessageToSet(WorldPacket* data, Player const* skipped_rcvr) override; diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 85dccddf186..6d98a4c78b2 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -226,7 +226,7 @@ void Transport::Update(uint32 diff) sScriptMgr->OnTransportUpdate(this, diff); } -void Transport::DelayedUpdate(uint32 diff) +void Transport::DelayedUpdate(uint32 /*diff*/) { if (GetKeyFrames().size() <= 1) return; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 219ee8b1002..66571393124 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -9949,11 +9949,15 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin DoneTotal += int32(DoneAdvertisedBenefit * coeff * factorMod); } - // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - float tmpDamage = (int32(pdamage) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype)); - // apply spellmod to Done damage (flat and pct) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); + float tmpDamage = float(int32(pdamage) + DoneTotal); + // SPELLMOD_DOT will be applied in AuraEffect::HandlePeriodicDamageAurasTick. + if (damagetype != DOT) + { + tmpDamage *= SpellDamagePctDone(victim, spellProto, damagetype); + // apply spellmod to Done damage (flat and pct) + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage); + } return uint32(std::max(tmpDamage, 0.0f)); } @@ -10793,11 +10797,15 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui DoneTotal = 0; } - // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - float heal = float(int32(healamount) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellHealingPctDone(victim, spellProto)); - // apply spellmod to Done amount - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); + float heal = float(int32(healamount) + DoneTotal); + // SPELLMOD_DOT will be applied in AuraEffect::HandlePeriodicHealAurasTick. + if (damagetype != DOT) + { + heal *= SpellHealingPctDone(victim, spellProto); + // apply spellmod to Done amount + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, heal); + } return uint32(std::max(heal, 0.0f)); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 088a0bbdf73..e6d2718dc53 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -273,13 +273,6 @@ enum UnitRename #define MAX_AGGRO_RESET_TIME 10 // in seconds #define MAX_AGGRO_RADIUS 45.0f // yards -enum Swing -{ - NOSWING = 0, - SINGLEHANDEDSWING = 1, - TWOHANDEDSWING = 2 -}; - enum VictimState { VICTIMSTATE_INTACT = 0, // set when attacker misses @@ -356,7 +349,7 @@ class TransportBase; class SpellCastTargets; typedef std::list<Unit*> UnitList; -typedef std::list< std::pair<Aura*, uint8> > DispelChargesList; +typedef std::list<std::pair<Aura*, uint8>> DispelChargesList; struct SpellImmune { @@ -381,13 +374,6 @@ enum WeaponDamageRange MAXDAMAGE }; -enum DamageTypeToSchool -{ - RESISTANCE, - DAMAGE_DEALT, - DAMAGE_TAKEN -}; - enum AuraRemoveMode { AURA_REMOVE_NONE = 0, @@ -1896,7 +1882,7 @@ class Unit : public WorldObject void TauntApply(Unit* victim); void TauntFadeOut(Unit* taunter); ThreatManager& getThreatManager() { return m_ThreatManager; } - void addHatedBy(HostileReference* pHostileReference) { m_HostileRefManager.insertFirst(pHostileReference); }; + void addHatedBy(HostileReference* pHostileReference) { m_HostileRefManager.insertFirst(pHostileReference); } void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ } HostileRefManager& getHostileRefManager() { return m_HostileRefManager; } diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h index a47b6f29e80..eb29e463a4d 100644 --- a/src/server/game/Events/GameEventMgr.h +++ b/src/server/game/Events/GameEventMgr.h @@ -96,7 +96,7 @@ class GameEventMgr { private: GameEventMgr(); - ~GameEventMgr() { }; + ~GameEventMgr() { } public: static GameEventMgr* instance() diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a3414c72613..e540824e71c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1057,12 +1057,12 @@ void ObjectMgr::LoadGameObjectAddons() { Field* fields = result->Fetch(); - ObjectGuid::LowType guid = fields[0].GetUInt64(); + ObjectGuid::LowType guid = fields[0].GetUInt32(); const GameObjectData* goData = GetGOData(guid); if (!goData) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") does not exist but has a record in `gameobject_addon`", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid); continue; } @@ -1072,14 +1072,14 @@ void ObjectMgr::LoadGameObjectAddons() if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") has invalid InvisibilityType in `gameobject_addon`", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`", guid); gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL; gameObjectAddon.InvisibilityValue = 0; } if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: " UI64FMTD ") has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid); gameObjectAddon.InvisibilityValue = 1; } diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 28e933bd08c..732afce9517 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -218,7 +218,7 @@ class Group ObjectGuid GetMasterLooterGuid() const; ItemQualities GetLootThreshold() const; - uint32 GetDbStoreId() const { return m_dbStoreId; }; + uint32 GetDbStoreId() const { return m_dbStoreId; } // member manipulation methods bool IsMember(ObjectGuid guid) const; diff --git a/src/server/game/Maps/ZoneScript.h b/src/server/game/Maps/ZoneScript.h index d8dba57d438..fd52bdd4c14 100644 --- a/src/server/game/Maps/ZoneScript.h +++ b/src/server/game/Maps/ZoneScript.h @@ -29,8 +29,8 @@ class ZoneScript ZoneScript() { } virtual ~ZoneScript() { } - virtual uint32 GetCreatureEntry(uint32 /*guidlow*/, CreatureData const* data) { return data->id; } - virtual uint32 GetGameObjectEntry(uint32 /*guidlow*/, uint32 entry) { return entry; } + virtual uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) { return data->id; } + virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) { return entry; } virtual void OnCreatureCreate(Creature* ) { } virtual void OnCreatureRemove(Creature* ) { } diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 0b547d96e7f..c8da50364ba 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -182,7 +182,7 @@ class MotionMaster //: private std::stack<MovementGenerator *> void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); void MoveJumpTo(float angle, float speedXY, float speedZ); void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP) - { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); }; + { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); } void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = EVENT_JUMP); void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount); void MoveFall(uint32 id = 0); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 97e3880161a..f91fc1985d5 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -367,7 +367,7 @@ void FlightPathMovementGenerator::InitEndGridInfo() _endMapId = (*i_path)[nodeCount - 1].MapID; //! MapId of last node _preloadTargetNode = nodeCount - 3; _endGridX = (*i_path)[nodeCount - 1].LocX; - _endGridY = (*i_path)[nodeCount - 1].LocZ; + _endGridY = (*i_path)[nodeCount - 1].LocY; } void FlightPathMovementGenerator::PreloadEndGrid() diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 8be5da91c32..f0ee013bcc2 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -622,6 +622,7 @@ void AddSC_instance_magtheridons_lair(); void AddSC_boss_grand_warlock_nethekurse(); //HC Shattered Halls void AddSC_boss_warbringer_omrogg(); void AddSC_boss_warchief_kargath_bladefist(); +void AddSC_shattered_halls(); void AddSC_instance_shattered_halls(); void AddSC_boss_watchkeeper_gargolmar(); //HC Ramparts void AddSC_boss_omor_the_unscarred(); @@ -1152,6 +1153,7 @@ void AddOutlandScripts() AddSC_boss_grand_warlock_nethekurse(); //HC Shattered Halls AddSC_boss_warbringer_omrogg(); AddSC_boss_warchief_kargath_bladefist(); + AddSC_shattered_halls(); AddSC_instance_shattered_halls(); AddSC_boss_watchkeeper_gargolmar(); //HC Ramparts AddSC_boss_omor_the_unscarred(); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 378f5f0d622..918b000a557 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5845,6 +5845,9 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const else damage = std::max(int32(damage * GetDonePct()), 0); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); // Calculate armor mitigation @@ -6148,6 +6151,9 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const else damage = std::max(int32(damage * GetDonePct()), 0); + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); + damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); } diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index b16993f28a3..09e3be690b1 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -22,6 +22,7 @@ #include "SpellInfo.h" #include "Spell.h" #include "World.h" +#include "Opcodes.h" SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelay = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH)); SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelayCheck = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH / 2)); @@ -627,6 +628,7 @@ void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 sp void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) const { data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size()); + data << uint64(_owner->GetGUID()); data << uint8(flags); for (auto const& cooldown : cooldowns) { diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 191850516d4..13290320084 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2955,7 +2955,7 @@ void SpellMgr::LoadSpellInfoCorrections() switch (spellInfo->Id) { case 63026: // Force Cast (HACK: Target shouldn't be changed) - case 63171: // Force Cast (HACK: Target shouldn't be changed; summon position should be untied from spell destination) + case 63137: // Force Cast (HACK: Target shouldn't be changed; summon position should be untied from spell destination) spellInfo->Effects[0].TargetA = SpellImplicitTargetInfo(TARGET_DEST_DB); break; case 53096: // Quetz'lun's Judgment @@ -3134,6 +3134,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 64823: // Item - Druid T8 Balance 4P Bonus case 34477: // Misdirection case 44401: // Missile Barrage + case 18820: // Insight spellInfo->ProcCharges = 1; break; case 44544: // Fingers of Frost diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 390be999d9d..09a41fd82b5 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -370,28 +370,13 @@ public: uint32 lowGuid = atoi((char*)guidStr); - Creature* creature = NULL; - - /* FIXME: impossible without entry - if (lowguid) - creature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), MAKE_GUID(lowguid, HIGHGUID_UNIT)); - */ - // attempt check creature existence by DB data - if (!creature) - { - CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid); - if (!data) - { - handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid); - handler->SetSentErrorMessage(true); - return false; - } - } - else + CreatureData const* data = sObjectMgr->GetCreatureData(lowGuid); + if (!data) { - // obtain real GUID for DB operations - lowGuid = creature->GetDBTableGUIDLow(); + handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowGuid); + handler->SetSentErrorMessage(true); + return false; } int wait = waitStr ? atoi(waitStr) : 0; @@ -407,18 +392,6 @@ public: WorldDatabase.Execute(stmt); - if (creature && creature->GetWaypointPath()) - { - creature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE); - creature->GetMotionMaster()->Initialize(); - if (creature->IsAlive()) // dead creature will reset movement generator at respawn - { - creature->setDeathState(JUST_DIED); - creature->Respawn(true); - } - creature->SaveToDB(); - } - handler->SendSysMessage(LANG_WAYPOINT_ADDED); return true; @@ -830,34 +803,22 @@ public: lowguid = atoi(cId); - /* FIXME: impossible without entry - if (lowguid) - creature = ObjectAccessor::GetCreature(*handler->GetSession()->GetPlayer(), MAKE_GUID(lowguid, HIGHGUID_UNIT)); - */ - // Attempting creature load from DB data - if (!creature) + CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); + if (!data) { - CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); - if (!data) - { - handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); - handler->SetSentErrorMessage(true); - return false; - } + handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + handler->SetSentErrorMessage(true); + return false; + } - uint32 map_id = data->mapid; + uint32 map_id = data->mapid; - if (handler->GetSession()->GetPlayer()->GetMapId() != map_id) - { - handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); - handler->SetSentErrorMessage(true); - return false; - } - } - else + if (handler->GetSession()->GetPlayer()->GetMapId() != map_id) { - lowguid = creature->GetDBTableGUIDLow(); + handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); + handler->SetSentErrorMessage(true); + return false; } } else diff --git a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp index 1d7e1594d05..ca944f7037b 100644 --- a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp +++ b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp @@ -102,7 +102,7 @@ public: } } - virtual void Update(uint32 /*diff*/) // correct order goes form 1-6 + virtual void Update(uint32 /*diff*/) override // correct order goes form 1-6 { switch (State) { diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp index cae26735568..29002460b2a 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp @@ -202,7 +202,7 @@ class boss_archaedas : public CreatureScript DoMeleeAttackIfReady(); } - void JustDied (Unit* /*killer*/) + void JustDied (Unit* /*killer*/) override { instance->SetData(DATA_ANCIENT_DOOR, DONE); // open the vault door instance->SetData(DATA_MINIONS, SPECIAL); // deactivate his minions diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index 704fefe2678..163620230ef 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -384,6 +384,8 @@ public: } PlayerGUID = player->GetGUID(); Start(true, false, PlayerGUID); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid1); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } return; } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index 660a639487f..fabe54303b8 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -196,7 +196,7 @@ class instance_halls_of_reflection : public InstanceMapScript } } - uint32 GetGameObjectEntry(uint32 /*guidLow*/, uint32 entry) override + uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) override { if (!_teamInInstance) { diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index 63082808e03..d7a580800bc 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -1060,8 +1060,8 @@ class spell_putricide_ooze_eruption_searcher : public SpellScriptLoader uint32 adhesiveId = sSpellMgr->GetSpellIdForDifficulty(SPELL_VOLATILE_OOZE_ADHESIVE, GetCaster()); if (GetHitUnit()->HasAura(adhesiveId)) { - GetCaster()->CastSpell(GetHitUnit(), SPELL_OOZE_ERUPTION, true); GetHitUnit()->RemoveAurasDueToSpell(adhesiveId, GetCaster()->GetGUID(), 0, AURA_REMOVE_BY_ENEMY_SPELL); + GetCaster()->CastSpell(GetHitUnit(), SPELL_OOZE_ERUPTION, true); } } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index 60268fc8d87..e908d088554 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -348,7 +348,7 @@ class boss_sindragosa : public CreatureScript events.ScheduleEvent(EVENT_AIR_MOVEMENT, 1); break; case POINT_AIR_PHASE: - me->CastCustomSpell(SPELL_ICE_TOMB_TARGET, SPELLVALUE_MAX_TARGETS, RAID_MODE<int32>(2, 5, 2, 6), NULL); + me->CastCustomSpell(SPELL_ICE_TOMB_TARGET, SPELLVALUE_MAX_TARGETS, RAID_MODE<int32>(2, 5, 2, 6), NULL, TRIGGERED_FULL_MASK); me->SetFacingTo(float(M_PI)); events.ScheduleEvent(EVENT_AIR_MOVEMENT_FAR, 1); events.ScheduleEvent(EVENT_FROST_BOMB, 9000); @@ -491,7 +491,7 @@ class boss_sindragosa : public CreatureScript if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, FrostBeaconSelector(me))) { Talk(EMOTE_WARN_FROZEN_ORB, target); - DoCast(target, SPELL_ICE_TOMB_DUMMY, true); + me->CastCustomSpell(SPELL_ICE_TOMB_TARGET, SPELLVALUE_MAX_TARGETS, 1, nullptr, TRIGGERED_FULL_MASK); } events.ScheduleEvent(EVENT_ICE_TOMB, urand(16000, 23000)); break; @@ -1631,7 +1631,7 @@ void AddSC_boss_sindragosa() new spell_rimefang_icy_blast(); new spell_frostwarden_handler_order_whelp(); new spell_frostwarden_handler_focus_fire(); - new spell_trigger_spell_from_caster("spell_sindragosa_ice_tomb", SPELL_ICE_TOMB_DUMMY); + new spell_trigger_spell_from_caster("spell_sindragosa_ice_tomb", SPELL_ICE_TOMB_DUMMY, TRIGGERED_IGNORE_SET_FACING); new spell_trigger_spell_from_caster("spell_sindragosa_ice_tomb_dummy", SPELL_FROST_BEACON); new at_sindragosa_lair(); new achievement_all_you_can_eat(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 8bf8e5ee6fb..091190b6b4e 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -524,14 +524,16 @@ enum AreaIds class spell_trigger_spell_from_caster : public SpellScriptLoader { public: - spell_trigger_spell_from_caster(char const* scriptName, uint32 triggerId) : SpellScriptLoader(scriptName), _triggerId(triggerId) { } + spell_trigger_spell_from_caster(char const* scriptName, uint32 triggerId, TriggerCastFlags triggerFlags = TRIGGERED_FULL_MASK) + : SpellScriptLoader(scriptName), _triggerId(triggerId), _triggerFlags(triggerFlags) { } class spell_trigger_spell_from_caster_SpellScript : public SpellScript { PrepareSpellScript(spell_trigger_spell_from_caster_SpellScript); public: - spell_trigger_spell_from_caster_SpellScript(uint32 triggerId) : SpellScript(), _triggerId(triggerId) { } + spell_trigger_spell_from_caster_SpellScript(uint32 triggerId, TriggerCastFlags triggerFlags) + : SpellScript(), _triggerId(triggerId), _triggerFlags(triggerFlags) { } bool Validate(SpellInfo const* /*spell*/) override { @@ -542,7 +544,7 @@ class spell_trigger_spell_from_caster : public SpellScriptLoader void HandleTrigger() { - GetCaster()->CastSpell(GetHitUnit(), _triggerId, true); + GetCaster()->CastSpell(GetHitUnit(), _triggerId, _triggerFlags); } void Register() override @@ -551,15 +553,17 @@ class spell_trigger_spell_from_caster : public SpellScriptLoader } uint32 _triggerId; + TriggerCastFlags _triggerFlags; }; SpellScript* GetSpellScript() const override { - return new spell_trigger_spell_from_caster_SpellScript(_triggerId); + return new spell_trigger_spell_from_caster_SpellScript(_triggerId, _triggerFlags); } private: uint32 _triggerId; + TriggerCastFlags _triggerFlags; }; template<class AI> diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index 61848070170..1b823b4a452 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -317,7 +317,7 @@ class instance_icecrown_citadel : public InstanceMapScript } // Weekly quest spawn prevention - uint32 GetCreatureEntry(uint32 /*guidLow*/, CreatureData const* data) override + uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override { uint32 entry = data->id; switch (entry) @@ -372,7 +372,7 @@ class instance_icecrown_citadel : public InstanceMapScript return entry; } - uint32 GetGameObjectEntry(uint32 /*guidLow*/, uint32 entry) override + uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) override { switch (entry) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index 181fb49ba2e..d0e4b7be63b 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -903,17 +903,15 @@ class boss_yogg_saron : public CreatureScript DoCast(me, SPELL_KNOCK_AWAY); me->ResetLootMode(); - switch (_instance->GetData(DATA_KEEPERS_COUNT)) - { - case 0: - me->AddLootMode(LOOT_MODE_HARD_MODE_4); - case 1: - me->AddLootMode(LOOT_MODE_HARD_MODE_3); - case 2: - me->AddLootMode(LOOT_MODE_HARD_MODE_2); - case 3: - me->AddLootMode(LOOT_MODE_HARD_MODE_1); - } + uint32 keepersCount = _instance->GetData(DATA_KEEPERS_COUNT); + if (keepersCount == 0) + me->AddLootMode(LOOT_MODE_HARD_MODE_4); + if (keepersCount <= 1) + me->AddLootMode(LOOT_MODE_HARD_MODE_3); + if (keepersCount <= 2) + me->AddLootMode(LOOT_MODE_HARD_MODE_2); + if (keepersCount <= 3) + me->AddLootMode(LOOT_MODE_HARD_MODE_1); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h b/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h index 80c8041bc6d..fac9d8fa357 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPNA.h @@ -261,13 +261,13 @@ class OPvPCapturePointNA : public OPvPCapturePoint public: OPvPCapturePointNA(OutdoorPvP* pvp); - bool Update(uint32 diff); + bool Update(uint32 diff) override; - void ChangeState(); + void ChangeState() override; - void FillInitialWorldStates(WorldPacket & data); + void FillInitialWorldStates(WorldPacket & data) override; - bool HandleCustomSpell(Player* player, uint32 spellId, GameObject* go); + bool HandleCustomSpell(Player* player, uint32 spellId, GameObject* go) override; int32 HandleOpenGo(Player* player, ObjectGuid guid) override; diff --git a/src/server/scripts/Outland/CMakeLists.txt b/src/server/scripts/Outland/CMakeLists.txt index 24658dbc21f..42621b76941 100644 --- a/src/server/scripts/Outland/CMakeLists.txt +++ b/src/server/scripts/Outland/CMakeLists.txt @@ -27,6 +27,7 @@ set(scripts_STAT_SRCS Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp + Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp Outland/CoilfangReservoir/SteamVault/boss_mekgineer_steamrigger.cpp diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp index 21ed710f4d8..7f2e08b39ca 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp @@ -87,11 +87,11 @@ class boss_warchief_kargath_bladefist : public CreatureScript resetcheck_timer = 5000; } - void SetData(uint32 type, uint32 data) override + void DoAction(int32 action) override { - if (type == 1) + if (action == ACTION_EXECUTIONER_TAUNT) { - switch (data) + switch (instance->GetData(DATA_TEAM_IN_INSTANCE)) { case ALLIANCE: Talk(SAY_CALL_EXECUTIONER_A); diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp index 6939b15585a..5cfae286f9b 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp @@ -32,20 +32,11 @@ EndScriptData */ #include "SpellAuras.h" #include "shattered_halls.h" -enum Spells -{ - SPELL_CLEAVE = 15284, - - SPELL_EXECUTE_1 = 39288, - SPELL_EXECUTE_2, - SPELL_EXECUTE_3 -}; - DoorData const doorData[] = { - {GO_GRAND_WARLOCK_CHAMBER_DOOR_1, DATA_NETHEKURSE, DOOR_TYPE_PASSAGE, BOUNDARY_S }, - {GO_GRAND_WARLOCK_CHAMBER_DOOR_2, DATA_NETHEKURSE, DOOR_TYPE_PASSAGE, BOUNDARY_N }, - {0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE} + { GO_GRAND_WARLOCK_CHAMBER_DOOR_1, DATA_NETHEKURSE, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_GRAND_WARLOCK_CHAMBER_DOOR_2, DATA_NETHEKURSE, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE } }; class instance_shattered_halls : public InstanceMapScript @@ -77,9 +68,7 @@ class instance_shattered_halls : public InstanceMapScript if (!_team) _team = player->GetTeam(); - player->RemoveAurasDueToSpell(SPELL_EXECUTE_1); - player->RemoveAurasDueToSpell(SPELL_EXECUTE_2); - player->RemoveAurasDueToSpell(SPELL_EXECUTE_3); + player->CastSpell(player, SPELL_REMOVE_KARGATH_EXECUTIONER, true); if (!executionTimer || executionerGUID.IsEmpty()) return; @@ -87,13 +76,13 @@ class instance_shattered_halls : public InstanceMapScript switch (executed) { case 0: - ex = player->AddAura(SPELL_EXECUTE_1, player); + ex = player->AddAura(SPELL_KARGATH_EXECUTIONER_1, player); break; case 1: - ex = player->AddAura(SPELL_EXECUTE_2, player); + ex = player->AddAura(SPELL_KARGATH_EXECUTIONER_2, player); break; case 2: - ex = player->AddAura(SPELL_EXECUTE_3, player); + ex = player->AddAura(SPELL_KARGATH_EXECUTIONER_3, player); break; default: break; @@ -151,10 +140,22 @@ class instance_shattered_halls : public InstanceMapScript break; case NPC_SHATTERED_EXECUTIONER: executionTimer = 55 * MINUTE * IN_MILLISECONDS; - DoCastSpellOnPlayers(SPELL_EXECUTE_1); + DoCastSpellOnPlayers(SPELL_KARGATH_EXECUTIONER_1); executionerGUID = creature->GetGUID(); SaveToDB(); break; + case NPC_CAPTAIN_ALINA: + case NPC_CAPTAIN_BONESHATTER: + victimsGUID[0] = creature->GetGUID(); + break; + case NPC_ALLIANCE_VICTIM_1: + case NPC_HORDE_VICTIM_1: + victimsGUID[1] = creature->GetGUID(); + break; + case NPC_ALLIANCE_VICTIM_2: + case NPC_HORDE_VICTIM_2: + victimsGUID[2] = creature->GetGUID(); + break; } } @@ -168,16 +169,14 @@ class instance_shattered_halls : public InstanceMapScript case DATA_SHATTERED_EXECUTIONER: if (state == DONE) { - DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_1); - DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_2); - DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_3); + DoCastSpellOnPlayers(SPELL_REMOVE_KARGATH_EXECUTIONER); executionTimer = 0; SaveToDB(); } break; case DATA_KARGATH: if (Creature* executioner = instance->GetCreature(executionerGUID)) - executioner->AI()->Reset(); + executioner->AI()->Reset(); // trigger removal of IMMUNE_TO_PC flag break; case DATA_OMROGG: break; @@ -191,16 +190,16 @@ class instance_shattered_halls : public InstanceMapScript { case NPC_GRAND_WARLOCK_NETHEKURSE: return nethekurseGUID; - break; case NPC_KARGATH_BLADEFIST: return kargathGUID; - break; case NPC_SHATTERED_EXECUTIONER: return executionerGUID; - break; + case DATA_FIRST_PRISONER: + case DATA_SECOND_PRISONER: + case DATA_THIRD_PRISONER: + return victimsGUID[data - DATA_FIRST_PRISONER]; default: return ObjectGuid::Empty; - break; } } @@ -253,13 +252,10 @@ class instance_shattered_halls : public InstanceMapScript { case DATA_PRISONERS_EXECUTED: return executed; - break; case DATA_TEAM_IN_INSTANCE: return _team; - break; default: return 0; - break; } } @@ -269,27 +265,25 @@ class instance_shattered_halls : public InstanceMapScript return; if (executionTimer <= diff) - { + { + DoCastSpellOnPlayers(SPELL_REMOVE_KARGATH_EXECUTIONER); switch (++executed) { case 1: - DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_1); - DoCastSpellOnPlayers(SPELL_EXECUTE_2); + DoCastSpellOnPlayers(SPELL_KARGATH_EXECUTIONER_2); executionTimer = 10 * MINUTE * IN_MILLISECONDS; break; case 2: - DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_2); - DoCastSpellOnPlayers(SPELL_EXECUTE_3); + DoCastSpellOnPlayers(SPELL_KARGATH_EXECUTIONER_3); executionTimer = 15 * MINUTE * IN_MILLISECONDS; break; default: - DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_3); executionTimer = 0; break; } if (Creature* executioner = instance->GetCreature(executionerGUID)) - executioner->AI()->SetData(1, executed); + executioner->AI()->SetData(DATA_PRISONERS_EXECUTED, executed); SaveToDB(); } @@ -297,10 +291,11 @@ class instance_shattered_halls : public InstanceMapScript executionTimer -= diff; } - protected: + private: ObjectGuid nethekurseGUID; ObjectGuid kargathGUID; ObjectGuid executionerGUID; + ObjectGuid victimsGUID[3]; uint8 executed; uint32 executionTimer; @@ -308,189 +303,7 @@ class instance_shattered_halls : public InstanceMapScript }; }; -class at_nethekurse_exit : public AreaTriggerScript -{ - public: - at_nethekurse_exit() : AreaTriggerScript("at_nethekurse_exit") { }; - - bool OnTrigger(Player* player, AreaTriggerEntry const*) override - { - if (InstanceScript* is = player->GetInstanceScript()) - { - if (is->instance->IsHeroic()) - { - Creature* executioner = nullptr; - - is->instance->LoadGrid(Executioner.GetPositionX(), Executioner.GetPositionY()); - if (Creature* kargath = ObjectAccessor::GetCreature(*player, is->GetGuidData(NPC_KARGATH_BLADEFIST))) - { - if (is->GetGuidData(NPC_SHATTERED_EXECUTIONER).IsEmpty()) - { - executioner = kargath->SummonCreature(NPC_SHATTERED_EXECUTIONER, Executioner); - kargath->AI()->SetData(1, is->GetData(DATA_TEAM_IN_INSTANCE)); - } - } - - if (executioner) - for (uint8 i = 0; i < VictimCount; ++i) - executioner->SummonCreature(executionerVictims[i](is->GetData(DATA_TEAM_IN_INSTANCE)), executionerVictims[i].GetPos()); - } - } - - return false; - } -}; - -class boss_shattered_executioner : public CreatureScript -{ - public: - boss_shattered_executioner() : CreatureScript("boss_shattered_executioner") { } - - struct boss_shattered_executionerAI : public BossAI - { - boss_shattered_executionerAI(Creature* creature) : BossAI(creature, DATA_SHATTERED_EXECUTIONER) - { - Initialize(); - }; - - void Initialize() - { - cleaveTimer = 500; - me->ResetLootMode(); - switch (instance->GetData(DATA_PRISONERS_EXECUTED)) - { - case 0: - me->AddLootMode(LOOT_MODE_HARD_MODE_3); - case 1: - me->AddLootMode(LOOT_MODE_HARD_MODE_2); - case 2: - me->AddLootMode(LOOT_MODE_HARD_MODE_1); - } - } - - void Reset() override - { - _Reset(); - if (instance->GetBossState(DATA_KARGATH) == DONE) - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - else - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - - Initialize(); - } - - void JustSummoned(Creature*) override { } - - void JustDied(Unit*) override - { - _JustDied(); - Map::PlayerList const &players = instance->instance->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - { - Player* pl = itr->GetSource(); - uint32 qId = pl->GetTeam() == ALLIANCE ? QUEST_IMPRISONED_A : QUEST_IMPRISONED_H; - if (pl->GetQuestStatus(qId) != QUEST_STATUS_NONE && pl->GetQuestStatus(qId) != QUEST_STATUS_FAILED) - pl->CompleteQuest(qId); - } - } - - void SetData(uint32 type, uint32 data) override - { - if (type == 1) - { - uint32 entry = executionerVictims[data - 1](instance->GetData(DATA_TEAM_IN_INSTANCE)); - if (Creature* victim = me->FindNearestCreature(entry, 30.0f, true)) - me->Kill(victim); - - if (data == 1) - { - Map::PlayerList const &players = instance->instance->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - { - Player* pl = itr->GetSource(); - uint32 qId = pl->GetTeam() == ALLIANCE ? QUEST_IMPRISONED_A : QUEST_IMPRISONED_H; - if (pl->GetQuestStatus(qId) == QUEST_STATUS_INCOMPLETE) - pl->FailQuest(qId); - } - } - - switch (data) - { - case 3: - me->RemoveLootMode(LOOT_MODE_HARD_MODE_1); - case 2: - me->RemoveLootMode(LOOT_MODE_HARD_MODE_2); - case 1: - me->RemoveLootMode(LOOT_MODE_HARD_MODE_3); - default: - break; - } - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (cleaveTimer <= diff) - { - DoCast(SPELL_CLEAVE); - cleaveTimer = urand(5000, 7000); - } - else - cleaveTimer -= diff; - - DoMeleeAttackIfReady(); - } - private: - uint32 cleaveTimer; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_shattered_executionerAI>(creature); - } -}; - -class spell_kargath_executioner : public SpellScriptLoader -{ - public: - spell_kargath_executioner() : SpellScriptLoader("spell_kargath_executioner") { } - - class spell_kargath_executioner_AuraScript : public AuraScript - { - PrepareAuraScript(spell_kargath_executioner_AuraScript); - - bool AreaCheck(Unit* target) - { - if (target->GetMap()->GetId() != 540) - return false; - - return true; - } - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - void Register() override - { - DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_kargath_executioner_AuraScript::AreaCheck); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_kargath_executioner_AuraScript(); - } -}; - void AddSC_instance_shattered_halls() { new instance_shattered_halls(); - new at_nethekurse_exit(); - new boss_shattered_executioner(); - new spell_kargath_executioner(); } diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp new file mode 100644 index 00000000000..500b0ae4bcb --- /dev/null +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008-2015 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 "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" +#include "InstanceScript.h" +#include "Player.h" +#include "SpellAuras.h" +#include "shattered_halls.h" + +class at_nethekurse_exit : public AreaTriggerScript +{ + public: + at_nethekurse_exit() : AreaTriggerScript("at_nethekurse_exit") { }; + + bool OnTrigger(Player* player, AreaTriggerEntry const*) override + { + if (InstanceScript* is = player->GetInstanceScript()) + { + if (is->instance->IsHeroic()) + { + Creature* executioner = nullptr; + + is->instance->LoadGrid(Executioner.GetPositionX(), Executioner.GetPositionY()); + if (Creature* kargath = ObjectAccessor::GetCreature(*player, is->GetGuidData(NPC_KARGATH_BLADEFIST))) + { + if (is->GetGuidData(NPC_SHATTERED_EXECUTIONER).IsEmpty()) + { + executioner = kargath->SummonCreature(NPC_SHATTERED_EXECUTIONER, Executioner); + kargath->AI()->DoAction(ACTION_EXECUTIONER_TAUNT); + } + } + + if (executioner) + for (uint8 i = 0; i < VictimCount; ++i) + executioner->SummonCreature(executionerVictims[i](is->GetData(DATA_TEAM_IN_INSTANCE)), executionerVictims[i].GetPos()); + } + } + + return false; + } +}; + +enum Spells +{ + SPELL_CLEAVE = 15284 +}; + +class boss_shattered_executioner : public CreatureScript +{ + public: + boss_shattered_executioner() : CreatureScript("boss_shattered_executioner") { } + + struct boss_shattered_executionerAI : public BossAI + { + boss_shattered_executionerAI(Creature* creature) : BossAI(creature, DATA_SHATTERED_EXECUTIONER) + { + Initialize(); + }; + + void Initialize() + { + cleaveTimer = 500; + } + + void Reset() override + { + _Reset(); + + // _Reset() resets the loot mode, so we add them again, if any + uint32 prisonersExecuted = instance->GetData(DATA_PRISONERS_EXECUTED); + if (prisonersExecuted == 0) + me->AddLootMode(LOOT_MODE_HARD_MODE_3); + if (prisonersExecuted <= 1) + me->AddLootMode(LOOT_MODE_HARD_MODE_2); + if (prisonersExecuted <= 2) + me->AddLootMode(LOOT_MODE_HARD_MODE_1); + + if (instance->GetBossState(DATA_KARGATH) == DONE) + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + else + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + + Initialize(); + } + + void JustSummoned(Creature*) override { } // avoid despawn of prisoners on death/reset + + void JustDied(Unit*) override + { + _JustDied(); + + if (instance->GetData(DATA_PRISONERS_EXECUTED) > 0) + return; + + Map::PlayerList const &players = instance->instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player* pl = itr->GetSource(); + uint32 qId = pl->GetTeam() == ALLIANCE ? QUEST_IMPRISONED_A : QUEST_IMPRISONED_H; + if (pl->GetQuestStatus(qId) == QUEST_STATUS_INCOMPLETE) + pl->CompleteQuest(qId); + } + } + + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_PRISONERS_EXECUTED && data <= 3) + { + if (Creature* victim = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FIRST_PRISONER + data - 1))) + me->Kill(victim); + + if (data == 1) + { + Map::PlayerList const &players = instance->instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + { + Player* pl = itr->GetSource(); + uint32 qId = pl->GetTeam() == ALLIANCE ? QUEST_IMPRISONED_A : QUEST_IMPRISONED_H; + if (pl->GetQuestStatus(qId) == QUEST_STATUS_INCOMPLETE) + pl->FailQuest(qId); + } + } + + switch (data) + { + case 3: + me->RemoveLootMode(LOOT_MODE_HARD_MODE_1); + case 2: + me->RemoveLootMode(LOOT_MODE_HARD_MODE_2); + case 1: + me->RemoveLootMode(LOOT_MODE_HARD_MODE_3); + default: + break; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (cleaveTimer <= diff) + { + DoCast(SPELL_CLEAVE); + cleaveTimer = urand(5000, 7000); + } + else + cleaveTimer -= diff; + + DoMeleeAttackIfReady(); + } + private: + uint32 cleaveTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_shattered_executionerAI>(creature); + } +}; + +class spell_kargath_executioner : public SpellScriptLoader +{ + public: + spell_kargath_executioner() : SpellScriptLoader("spell_kargath_executioner") { } + + class spell_kargath_executioner_AuraScript : public AuraScript + { + PrepareAuraScript(spell_kargath_executioner_AuraScript); + + bool AreaCheck(Unit* target) + { + if (target->GetMap()->GetId() != 540) + return false; + + return true; + } + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void Register() override + { + DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_kargath_executioner_AuraScript::AreaCheck); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_kargath_executioner_AuraScript(); + } +}; + +class spell_remove_kargath_executioner : public SpellScriptLoader +{ + public: + spell_remove_kargath_executioner() : SpellScriptLoader("spell_remove_kargath_executioner") { } + + class spell_remove_kargath_executioner_SpellScript : public SpellScript + { + PrepareSpellScript(spell_remove_kargath_executioner_SpellScript); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetCaster(); + + target->RemoveAurasDueToSpell(SPELL_KARGATH_EXECUTIONER_1); + target->RemoveAurasDueToSpell(SPELL_KARGATH_EXECUTIONER_2); + target->RemoveAurasDueToSpell(SPELL_KARGATH_EXECUTIONER_3); + } + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_remove_kargath_executioner_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_remove_kargath_executioner_SpellScript(); + } +}; + +void AddSC_shattered_halls() +{ + new at_nethekurse_exit(); + new boss_shattered_executioner(); + new spell_kargath_executioner(); + new spell_remove_kargath_executioner(); +} diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h index adc07bec2ff..894cc9c40a6 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h @@ -33,7 +33,11 @@ enum DataTypes DATA_SHATTERED_EXECUTIONER = 3, DATA_PRISONERS_EXECUTED = 4, - DATA_TEAM_IN_INSTANCE = 5 + DATA_TEAM_IN_INSTANCE = 5, + + DATA_FIRST_PRISONER, + DATA_SECOND_PRISONER, + DATA_THIRD_PRISONER }; enum CreatureIds @@ -70,6 +74,20 @@ enum QuestIds QUEST_IMPRISONED_H = 9525 }; +enum InstanceSpells +{ + SPELL_KARGATH_EXECUTIONER_1 = 39288, + SPELL_KARGATH_EXECUTIONER_2 = 39289, + SPELL_KARGATH_EXECUTIONER_3 = 39290, + + SPELL_REMOVE_KARGATH_EXECUTIONER = 39291 +}; + +enum Actions +{ + ACTION_EXECUTIONER_TAUNT = 1 +}; + const Position Executioner = { 152.8524f, -83.63912f, 2.021005f, 0.06981317f }; struct FactionSpawnerHelper @@ -77,7 +95,7 @@ struct FactionSpawnerHelper FactionSpawnerHelper(uint32 allianceEntry, uint32 hordeEntry, const Position& pos) : _allianceNPC(allianceEntry), _hordeNPC(hordeEntry), _spawnPos(pos) { } inline uint32 operator()(uint32 teamID) const { return teamID == ALLIANCE ? _allianceNPC : _hordeNPC; } - inline const Position GetPos() const { return _spawnPos; } + inline Position const& GetPos() const { return _spawnPos; } private: const uint32 _allianceNPC; diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 471c776d54b..d1a31906d58 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -770,7 +770,8 @@ public: void Initialize() { circleRounds = 0; - point = 0; + point = 3; + wpreached = false; } void Reset() override @@ -778,9 +779,7 @@ public: events.Reset(); summons.DespawnAll(); - circleRounds = 0; - point = 3; - wpreached = false; + Initialize(); } void DoAction(int32 action) override diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index d98ef2ec799..60ff67d74c0 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -208,22 +208,23 @@ class spell_q6124_6129_apply_salve : public SpellScriptLoader if (GetCastItem()) if (Creature* creatureTarget = GetHitCreature()) { - uint32 uiNewEntry = 0; + uint32 newEntry = 0; switch (caster->GetTeam()) { case HORDE: if (creatureTarget->GetEntry() == NPC_SICKLY_GAZELLE) - uiNewEntry = NPC_CURED_GAZELLE; + newEntry = NPC_CURED_GAZELLE; break; case ALLIANCE: if (creatureTarget->GetEntry() == NPC_SICKLY_DEER) - uiNewEntry = NPC_CURED_DEER; + newEntry = NPC_CURED_DEER; break; } - if (uiNewEntry) + if (newEntry) { - creatureTarget->UpdateEntry(uiNewEntry); + creatureTarget->UpdateEntry(newEntry); creatureTarget->DespawnOrUnsummon(DESPAWN_TIME); + caster->KilledMonsterCredit(newEntry); } } } @@ -338,7 +339,7 @@ class spell_q11396_11399_scourging_crystal_controller : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q11396_11399_scourging_crystal_controller_SpellScript(); - }; + } }; // 43882 Scourging Crystal Controller Dummy @@ -374,7 +375,7 @@ class spell_q11396_11399_scourging_crystal_controller_dummy : public SpellScript SpellScript* GetSpellScript() const override { return new spell_q11396_11399_scourging_crystal_controller_dummy_SpellScript(); - }; + } }; // http://www.wowhead.com/quest=11515 Blood for Blood @@ -855,7 +856,7 @@ class spell_symbol_of_life_dummy : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_symbol_of_life_dummy_SpellScript(); - }; + } }; // http://www.wowhead.com/quest=12659 Scalps! @@ -898,7 +899,7 @@ class spell_q12659_ahunaes_knife : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q12659_ahunaes_knife_SpellScript(); - }; + } }; enum StoppingTheSpread @@ -944,7 +945,7 @@ class spell_q9874_liquid_fire : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q9874_liquid_fire_SpellScript(); - }; + } }; enum SalvagingLifesStength @@ -988,7 +989,7 @@ class spell_q12805_lifeblood_dummy : public SpellScriptLoader SpellScript* GetSpellScript() const override { return new spell_q12805_lifeblood_dummy_SpellScript(); - }; + } }; /* diff --git a/src/server/shared/Cryptography/SHA1.h b/src/server/shared/Cryptography/SHA1.h index ebd9f721d4a..f59bdc25556 100644 --- a/src/server/shared/Cryptography/SHA1.h +++ b/src/server/shared/Cryptography/SHA1.h @@ -39,8 +39,8 @@ class SHA1Hash void Initialize(); void Finalize(); - uint8 *GetDigest(void) { return mDigest; }; - int GetLength(void) const { return SHA_DIGEST_LENGTH; }; + uint8 *GetDigest(void) { return mDigest; } + int GetLength(void) const { return SHA_DIGEST_LENGTH; } private: SHA_CTX mC; diff --git a/src/server/shared/Debugging/WheatyExceptionReport.cpp b/src/server/shared/Debugging/WheatyExceptionReport.cpp index f8f641a9ea7..e9f888f280d 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.cpp +++ b/src/server/shared/Debugging/WheatyExceptionReport.cpp @@ -61,6 +61,9 @@ HANDLE WheatyExceptionReport::m_hDumpFile; HANDLE WheatyExceptionReport::m_hProcess; SymbolPairs WheatyExceptionReport::symbols; std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails; +bool WheatyExceptionReport::stackOverflowException; +bool WheatyExceptionReport::alreadyCrashed; +std::mutex WheatyExceptionReport::alreadyCrashedLock; // Declare global instance of class WheatyExceptionReport g_WheatyExceptionReport; @@ -72,6 +75,8 @@ WheatyExceptionReport::WheatyExceptionReport() // Constructor // Install the unhandled exception filter function m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); m_hProcess = GetCurrentProcess(); + stackOverflowException = false; + alreadyCrashed = false; if (!IsDebuggerPresent()) { _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); @@ -97,6 +102,16 @@ WheatyExceptionReport::~WheatyExceptionReport() LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo) { + std::unique_lock<std::mutex> guard(alreadyCrashedLock); + // Handle only 1 exception in the whole process lifetime + if (alreadyCrashed) + return EXCEPTION_EXECUTE_HANDLER; + + alreadyCrashed = true; + + if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) + stackOverflowException = true; + TCHAR module_folder_name[MAX_PATH]; GetModuleFileName(0, module_folder_name, MAX_PATH); TCHAR* pos = _tcsrchr(module_folder_name, '\\'); @@ -419,107 +434,114 @@ void WheatyExceptionReport::printTracesForAllThreads(bool bWriteVariables) void WheatyExceptionReport::GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo) { - SYSTEMTIME systime; - GetLocalTime(&systime); - - // Start out with a banner - _tprintf(_T("Revision: %s\r\n"), _FULLVERSION); - _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); - PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; - - PrintSystemInfo(); - // First print information about the type of fault - _tprintf(_T("\r\n//=====================================================\r\n")); - _tprintf(_T("Exception code: %08X %s\r\n"), - pExceptionRecord->ExceptionCode, - GetExceptionString(pExceptionRecord->ExceptionCode)); - - // Now print information about where the fault occured - TCHAR szFaultingModule[MAX_PATH]; - DWORD section; - DWORD_PTR offset; - GetLogicalAddress(pExceptionRecord->ExceptionAddress, - szFaultingModule, - sizeof(szFaultingModule), - section, offset); + __try + { + SYSTEMTIME systime; + GetLocalTime(&systime); + + // Start out with a banner + _tprintf(_T("Revision: %s\r\n"), _FULLVERSION); + _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + + PrintSystemInfo(); + // First print information about the type of fault + _tprintf(_T("\r\n//=====================================================\r\n")); + _tprintf(_T("Exception code: %08X %s\r\n"), + pExceptionRecord->ExceptionCode, + GetExceptionString(pExceptionRecord->ExceptionCode)); + + // Now print information about where the fault occured + TCHAR szFaultingModule[MAX_PATH]; + DWORD section; + DWORD_PTR offset; + GetLogicalAddress(pExceptionRecord->ExceptionAddress, + szFaultingModule, + sizeof(szFaultingModule), + section, offset); #ifdef _M_IX86 - _tprintf(_T("Fault address: %08X %02X:%08X %s\r\n"), - pExceptionRecord->ExceptionAddress, - section, offset, szFaultingModule); + _tprintf(_T("Fault address: %08X %02X:%08X %s\r\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule); #endif #ifdef _M_X64 - _tprintf(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"), - pExceptionRecord->ExceptionAddress, - section, offset, szFaultingModule); + _tprintf(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule); #endif - PCONTEXT pCtx = pExceptionInfo->ContextRecord; + PCONTEXT pCtx = pExceptionInfo->ContextRecord; - // Show the registers - #ifdef _M_IX86 // X86 Only! - _tprintf(_T("\r\nRegisters:\r\n")); + // Show the registers +#ifdef _M_IX86 // X86 Only! + _tprintf(_T("\r\nRegisters:\r\n")); - _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") - , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, - pCtx->Esi, pCtx->Edi); + _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") + , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, + pCtx->Esi, pCtx->Edi); - _tprintf(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip); - _tprintf(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"), - pCtx->SegSs, pCtx->Esp, pCtx->Ebp); - _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), - pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); - _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags); - #endif + _tprintf(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip); + _tprintf(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"), + pCtx->SegSs, pCtx->Esp, pCtx->Ebp); + _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); + _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags); +#endif - #ifdef _M_X64 - _tprintf(_T("\r\nRegisters:\r\n")); - _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") - _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n") - , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, - pCtx->Rsi, pCtx->Rdi, pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15); - _tprintf(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip); - _tprintf(_T("SS:RSP:%04X:%016X RBP:%08X\r\n"), - pCtx->SegSs, pCtx->Rsp, pCtx->Rbp); - _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), - pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); - _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags); - #endif +#ifdef _M_X64 + _tprintf(_T("\r\nRegisters:\r\n")); + _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") + _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n") + , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, + pCtx->Rsi, pCtx->Rdi, pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15); + _tprintf(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip); + _tprintf(_T("SS:RSP:%04X:%016X RBP:%08X\r\n"), + pCtx->SegSs, pCtx->Rsp, pCtx->Rbp); + _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); + _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags); +#endif - SymSetOptions(SYMOPT_DEFERRED_LOADS); + SymSetOptions(SYMOPT_DEFERRED_LOADS); - // Initialize DbgHelp - if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) - { - _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), - ErrorMessage(GetLastError())); - } + // Initialize DbgHelp + if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) + { + _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), + ErrorMessage(GetLastError())); + } - CONTEXT trashableContext = *pCtx; + CONTEXT trashableContext = *pCtx; - WriteStackDetails(&trashableContext, false, NULL); - printTracesForAllThreads(false); + WriteStackDetails(&trashableContext, false, NULL); + printTracesForAllThreads(false); -// #ifdef _M_IX86 // X86 Only! + // #ifdef _M_IX86 // X86 Only! - _tprintf(_T("========================\r\n")); - _tprintf(_T("Local Variables And Parameters\r\n")); + _tprintf(_T("========================\r\n")); + _tprintf(_T("Local Variables And Parameters\r\n")); - trashableContext = *pCtx; - WriteStackDetails(&trashableContext, true, NULL); - printTracesForAllThreads(true); + trashableContext = *pCtx; + WriteStackDetails(&trashableContext, true, NULL); + printTracesForAllThreads(true); - /*_tprintf(_T("========================\r\n")); - _tprintf(_T("Global Variables\r\n")); + /*_tprintf(_T("========================\r\n")); + _tprintf(_T("Global Variables\r\n")); - SymEnumSymbols(GetCurrentProcess(), + SymEnumSymbols(GetCurrentProcess(), (UINT_PTR)GetModuleHandle(szFaultingModule), 0, EnumerateSymbolsCallback, 0);*/ - // #endif // X86 Only! + // #endif // X86 Only! - SymCleanup(GetCurrentProcess()); + SymCleanup(GetCurrentProcess()); - _tprintf(_T("\r\n")); + _tprintf(_T("\r\n")); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + _tprintf(_T("Error writing the crash log\r\n")); + } } //====================================================================== @@ -1313,16 +1335,43 @@ DWORD_PTR WheatyExceptionReport::DereferenceUnsafePointer(DWORD_PTR address) //============================================================================ int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...) { - TCHAR szBuff[WER_LARGE_BUFFER_SIZE]; int retValue; - DWORD cbWritten; va_list argptr; - va_start(argptr, format); + if (stackOverflowException) + { + retValue = heapprintf(format, argptr); + va_end(argptr); + } + else + { + retValue = stackprintf(format, argptr); + va_end(argptr); + } + + return retValue; +} + +int __cdecl WheatyExceptionReport::stackprintf(const TCHAR * format, va_list argptr) +{ + int retValue; + DWORD cbWritten; + + TCHAR szBuff[WER_LARGE_BUFFER_SIZE]; retValue = vsprintf(szBuff, format, argptr); - va_end(argptr); + WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0); + return retValue; +} + +int __cdecl WheatyExceptionReport::heapprintf(const TCHAR * format, va_list argptr) +{ + int retValue; + DWORD cbWritten; + TCHAR* szBuff = (TCHAR*)malloc(sizeof(TCHAR) * WER_LARGE_BUFFER_SIZE); + retValue = vsprintf(szBuff, format, argptr); WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0); + free(szBuff); return retValue; } diff --git a/src/server/shared/Debugging/WheatyExceptionReport.h b/src/server/shared/Debugging/WheatyExceptionReport.h index b7731daaa2b..ef6334add16 100644 --- a/src/server/shared/Debugging/WheatyExceptionReport.h +++ b/src/server/shared/Debugging/WheatyExceptionReport.h @@ -178,6 +178,8 @@ class WheatyExceptionReport static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address); static int __cdecl _tprintf(const TCHAR * format, ...); + static int __cdecl stackprintf(const TCHAR * format, va_list argptr); + static int __cdecl heapprintf(const TCHAR * format, va_list argptr); static bool StoreSymbol(DWORD type , DWORD_PTR offset); static void ClearSymbols(); @@ -191,6 +193,9 @@ class WheatyExceptionReport static HANDLE m_hProcess; static SymbolPairs symbols; static std::stack<SymbolDetail> symbolDetails; + static bool stackOverflowException; + static bool alreadyCrashed; + static std::mutex alreadyCrashedLock; static char* PushSymbolDetail(char* pszCurrBuffer); static char* PopSymbolDetail(char* pszCurrBuffer); diff --git a/src/server/shared/PrecompiledHeaders/sharedPCH.h b/src/server/shared/PrecompiledHeaders/sharedPCH.h index d0c15b17f0c..87af9f44eb7 100644 --- a/src/server/shared/PrecompiledHeaders/sharedPCH.h +++ b/src/server/shared/PrecompiledHeaders/sharedPCH.h @@ -6,3 +6,4 @@ #include "SQLOperation.h" #include "Errors.h" #include "TypeList.h" +#include "TaskScheduler.h" diff --git a/src/server/shared/Updater/DBUpdater.cpp b/src/server/shared/Updater/DBUpdater.cpp index e5c571e4517..20ded669cec 100644 --- a/src/server/shared/Updater/DBUpdater.cpp +++ b/src/server/shared/Updater/DBUpdater.cpp @@ -349,8 +349,27 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos args.push_back("-h" + host); args.push_back("-u" + user); args.push_back("-p" + password); + + // Check if we want to connect through ip or socket (Unix only) +#ifdef _WIN32 + args.push_back("-P" + port_or_socket); +#else + + if (!std::isdigit(port_or_socket[0])) + { + // We can't check here if host == "." because is named localhost if socket option is enabled + args.push_back("-P0"); + args.push_back("--protocol=SOCKET"); + args.push_back("-S" + port_or_socket); + } + else + // generic case + args.push_back("-P" + port_or_socket); + +#endif + // Set the default charset to utf8 args.push_back("--default-character-set=utf8"); diff --git a/src/server/shared/Utilities/TaskScheduler.cpp b/src/server/shared/Utilities/TaskScheduler.cpp new file mode 100644 index 00000000000..c945ad4dab0 --- /dev/null +++ b/src/server/shared/Utilities/TaskScheduler.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2008-2015 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 "TaskScheduler.h" + +TaskScheduler& TaskScheduler::Update() +{ + _now = clock_t::now(); + Dispatch(); + return *this; +} + +TaskScheduler& TaskScheduler::Update(size_t const milliseconds) +{ + return Update(std::chrono::milliseconds(milliseconds)); +} + +TaskScheduler& TaskScheduler::Async(std::function<void()> const& callable) +{ + _asyncHolder.push(callable); + return *this; +} + +TaskScheduler& TaskScheduler::CancelAll() +{ + /// Clear the task holder + _task_holder.Clear(); + _asyncHolder = AsyncHolder(); + return *this; +} + +TaskScheduler& TaskScheduler::CancelGroup(group_t const group) +{ + _task_holder.RemoveIf([group](TaskContainer const& task) -> bool + { + return task->IsInGroup(group); + }); + return *this; +} + +TaskScheduler& TaskScheduler::CancelGroupsOf(std::vector<group_t> const& groups) +{ + std::for_each(groups.begin(), groups.end(), + std::bind(&TaskScheduler::CancelGroup, this, std::placeholders::_1)); + + return *this; +} + +TaskScheduler& TaskScheduler::InsertTask(TaskContainer task) +{ + _task_holder.Push(std::forward<TaskContainer>(task)); + return *this; +} + +void TaskScheduler::Dispatch() +{ + // Process all asyncs + while (!_asyncHolder.empty()) + { + _asyncHolder.front()(); + _asyncHolder.pop(); + } + + while (!_task_holder.IsEmpty()) + { + if (_task_holder.First()->_end > _now) + break; + + // Perfect forward the context to the handler + // Use weak references to catch destruction before callbacks. + TaskContext context(new TaskContextInstance(_task_holder.Pop(), + std::weak_ptr<TaskScheduler>(self_reference))); + + // Invoke the context + context->Invoke(); + } +} + +void TaskScheduler::TaskQueue::Push(TaskContainer&& task) +{ + container.insert(task); +} + +auto TaskScheduler::TaskQueue::Pop() -> TaskContainer +{ + TaskContainer result = *container.begin(); + container.erase(container.begin()); + return result; +} + +auto TaskScheduler::TaskQueue::First() const -> TaskContainer const& +{ + return *container.begin(); +} + +void TaskScheduler::TaskQueue::Clear() +{ + container.clear(); +} + +void TaskScheduler::TaskQueue::RemoveIf(std::function<bool(TaskContainer const&)> const& filter) +{ + for (auto itr = container.begin(); itr != container.end();) + if (filter(*itr)) + itr = container.erase(itr); + else + ++itr; +} + +void TaskScheduler::TaskQueue::ModifyIf(std::function<bool(TaskContainer const&)> const& filter) +{ + std::vector<TaskContainer> cache; + for (auto itr = container.begin(); itr != container.end();) + if (filter(*itr)) + { + cache.push_back(*itr); + itr = container.erase(itr); + } + else + ++itr; + + container.insert(cache.begin(), cache.end()); +} + +bool TaskScheduler::TaskQueue::IsEmpty() const +{ + return container.empty(); +} + +TaskContextInstance& TaskContextInstance::Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply) +{ + if (auto const owner = _owner.lock()) + apply(*owner); + + return *this; +} + +bool TaskContextInstance::IsInGroup(TaskScheduler::group_t const group) const +{ + return _task->IsInGroup(group); +} + +TaskContextInstance& TaskContextInstance::SetGroup(TaskScheduler::group_t const group) +{ + _task->_group = group; + return *this; +} + +TaskContextInstance& TaskContextInstance::ClearGroup() +{ + _task->_group = boost::none; + return *this; +} + +TaskScheduler::repeated_t TaskContextInstance::GetRepeatCounter() const +{ + return _task->_repeated; +} + +TaskContextInstance& TaskContextInstance::Async(std::function<void()> const& callable) +{ + return Dispatch(std::bind(&TaskScheduler::Async, std::placeholders::_1, callable)); +} + +TaskContextInstance& TaskContextInstance::CancelAll() +{ + return Dispatch(std::mem_fn(&TaskScheduler::CancelAll)); +} + +TaskContextInstance& TaskContextInstance::CancelGroup(TaskScheduler::group_t const group) +{ + return Dispatch(std::bind(&TaskScheduler::CancelGroup, std::placeholders::_1, group)); +} + +TaskContextInstance& TaskContextInstance::CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups) +{ + return Dispatch(std::bind(&TaskScheduler::CancelGroupsOf, std::placeholders::_1, groups)); +} + +void TaskContextInstance::AssertOnConsumed() +{ + // This was adapted to TC to prevent static analysis tools from complaining. + // If you encounter this assertion check if you repeat a TaskContext more then 1 time! + ASSERT(_task && "Bad task logic, task context was consumed already!"); +} + +void TaskContextInstance::Invoke() +{ + _task->_task(shared_from_this()); +} diff --git a/src/server/shared/Utilities/TaskScheduler.h b/src/server/shared/Utilities/TaskScheduler.h new file mode 100644 index 00000000000..3498a9bcf6c --- /dev/null +++ b/src/server/shared/Utilities/TaskScheduler.h @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2008-2015 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 _TASK_SCHEDULER_H_ +#define _TASK_SCHEDULER_H_ + +#include <algorithm> +#include <chrono> +#include <vector> +#include <queue> +#include <memory> +#include <utility> +#include <set> + +#include <boost/optional.hpp> + +#include "Util.h" + +class TaskContextInstance; + +typedef std::shared_ptr<TaskContextInstance> TaskContext; + +/// The TaskScheduler class provides the ability to schedule std::function's in the near future. +/// Use TaskScheduler::Update to update the scheduler. +/// Popular methods are: +/// * Schedule (Schedules a std::function which will be executed in the near future). +/// * Schedules an asynchronous function which will be executed at the next update tick. +/// * Cancel, Delay & Reschedule (Methods to manipulate already scheduled tasks). +/// Tasks are organized in groups (uint), multiple tasks can have the same group id, +/// you can provide a group or not, but keep in mind that you can only manipulate specific tasks through its group id! +/// Tasks callbacks use the function signature void(TaskContext) where TaskContext provides +/// access to the function schedule plan which makes it possible to repeat the task +/// with the same duration or a new one. +/// It also provides access to the repeat counter which is useful for task that repeat itself often +/// but behave different every time (spoken event dialogs for example). +class TaskScheduler +{ + friend class TaskContextInstance; + + // Time definitions (use steady clock) + typedef std::chrono::steady_clock clock_t; + typedef clock_t::time_point timepoint_t; + typedef clock_t::duration duration_t; + + // Task group type + typedef uint32 group_t; + // Task repeated type + typedef uint32 repeated_t; + // Task handle type + typedef std::function<void(TaskContext)> task_handler_t; + + class Task + { + friend class TaskContextInstance; + friend class TaskScheduler; + + timepoint_t _end; + duration_t _duration; + boost::optional<group_t> _group; + repeated_t _repeated; + task_handler_t _task; + + public: + // All Argument construct + Task(timepoint_t const& end, duration_t const& duration, boost::optional<group_t> const& group, + repeated_t const repeated, task_handler_t const& task) + : _end(end), _duration(duration), _group(group), _repeated(repeated), _task(task) { } + + // Minimal Argument construct + Task(timepoint_t const& end, duration_t const& duration, task_handler_t const& task) + : _end(end), _duration(duration), _group(boost::none), _repeated(0), _task(task) { } + + // Copy construct + Task(Task const&) = delete; + // Move construct + Task(Task&&) = delete; + // Copy Assign + Task& operator= (Task const&) = default; + // Move Assign + Task& operator= (Task&& right) = delete; + + // Order tasks by its end + inline bool operator< (Task const& other) const + { + return _end < other._end; + } + + inline bool operator> (Task const& other) const + { + return _end > other._end; + } + + // Compare tasks with its end + inline bool operator== (Task const& other) + { + return _end == other._end; + } + + // Returns true if the task is in the given group + inline bool IsInGroup(group_t const group) const + { + return _group == group; + } + }; + + typedef std::shared_ptr<Task> TaskContainer; + + /// Container which provides Task order, insert and reschedule operations. + struct Compare + { + bool operator() (TaskContainer const& left, TaskContainer const& right) + { + return (*left.get()) < (*right.get()); + }; + }; + + class TaskQueue + { + std::multiset<TaskContainer, Compare> container; + + public: + // Pushes the task in the container + void Push(TaskContainer&& task); + + /// Pops the task out of the container + TaskContainer Pop(); + + TaskContainer const& First() const; + + void Clear(); + + void RemoveIf(std::function<bool(TaskContainer const&)> const& filter); + + void ModifyIf(std::function<bool(TaskContainer const&)> const& filter); + + bool IsEmpty() const; + }; + + /// Contains a self reference to track if this object was deleted or not. + std::shared_ptr<TaskScheduler> self_reference; + + /// The current time point (now) + timepoint_t _now; + + /// The Task Queue which contains all task objects. + TaskQueue _task_holder; + + typedef std::queue<std::function<void()>> AsyncHolder; + + /// Contains all asynchronous tasks which will be invoked at + /// the next update tick. + AsyncHolder _asyncHolder; + +public: + TaskScheduler() : self_reference(this, [](TaskScheduler const*) { }), + _now(clock_t::now()) { } + + TaskScheduler(TaskScheduler const&) = delete; + TaskScheduler(TaskScheduler&&) = delete; + TaskScheduler& operator= (TaskScheduler const&) = delete; + TaskScheduler& operator= (TaskScheduler&&) = delete; + + /// Update the scheduler to the current time. + TaskScheduler& Update(); + + /// Update the scheduler with a difftime in ms. + TaskScheduler& Update(size_t const milliseconds); + + /// Update the scheduler with a difftime. + template<class _Rep, class _Period> + TaskScheduler& Update(std::chrono::duration<_Rep, _Period> const& difftime) + { + _now += difftime; + Dispatch(); + return *this; + } + + /// Schedule an callable function that is executed at the next update tick. + /// Its safe to modify the TaskScheduler from within the callable. + TaskScheduler& Async(std::function<void()> const& callable); + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _Rep, class _Period> + TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, + task_handler_t const& task) + { + return ScheduleAt(_now, time, task); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _Rep, class _Period> + TaskScheduler& Schedule(std::chrono::duration<_Rep, _Period> const& time, + group_t const group, task_handler_t const& task) + { + return ScheduleAt(_now, time, group, task); + } + + /// Schedule an event with a randomized rate between min and max rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, task_handler_t const& task) + { + return Schedule(RandomDurationBetween(min, max), task); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::Schedule instead! + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, group_t const group, + task_handler_t const& task) + { + return Schedule(RandomDurationBetween(min, max), group, task); + } + + /// Cancels all tasks. + /// Never call this from within a task context! Use TaskContext::CancelAll instead! + TaskScheduler& CancelAll(); + + /// Cancel all tasks of a single group. + /// Never call this from within a task context! Use TaskContext::CancelGroup instead! + TaskScheduler& CancelGroup(group_t const group); + + /// Cancels all groups in the given std::vector. + /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" + TaskScheduler& CancelGroupsOf(std::vector<group_t> const& groups); + + /// Delays all tasks with the given duration. + template<class _Rep, class _Period> + TaskScheduler& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) + { + _task_holder.ModifyIf([&duration](TaskContainer const& task) -> bool + { + task->_end += duration; + return true; + }); + return *this; + } + + /// Delays all tasks with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayAll(RandomDurationBetween(min, max)); + } + + /// Delays all tasks of a group with the given duration. + template<class _Rep, class _Period> + TaskScheduler& DelayGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + _task_holder.ModifyIf([&duration, group](TaskContainer const& task) -> bool + { + if (task->IsInGroup(group)) + { + task->_end += duration; + return true; + } + else + return false; + }); + return *this; + } + + /// Delays all tasks of a group with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& DelayGroup(group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayGroup(group, RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks with a given duration. + template<class _Rep, class _Period> + TaskScheduler& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) + { + auto const end = _now + duration; + _task_holder.ModifyIf([end](TaskContainer const& task) -> bool + { + task->_end = end; + return true; + }); + return *this; + } + + /// Reschedule all tasks with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleAll(RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks of a group with the given duration. + template<class _Rep, class _Period> + TaskScheduler& RescheduleGroup(group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + auto const end = _now + duration; + _task_holder.ModifyIf([end, group](TaskContainer const& task) -> bool + { + if (task->IsInGroup(group)) + { + task->_end = end; + return true; + } + else + return false; + }); + return *this; + } + + /// Reschedule all tasks of a group with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskScheduler& RescheduleGroup(group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleGroup(group, RandomDurationBetween(min, max)); + } + +private: + /// Insert a new task to the enqueued tasks. + TaskScheduler& InsertTask(TaskContainer task); + + template<class _Rep, class _Period> + TaskScheduler& ScheduleAt(timepoint_t const& end, + std::chrono::duration<_Rep, _Period> const& time, task_handler_t const& task) + { + return InsertTask(TaskContainer(new Task(end + time, time, task))); + } + + /// Schedule an event with a fixed rate. + /// Never call this from within a task context! Use TaskContext::schedule instead! + template<class _Rep, class _Period> + TaskScheduler& ScheduleAt(timepoint_t const& end, + std::chrono::duration<_Rep, _Period> const& time, + group_t const group, task_handler_t const& task) + { + static repeated_t const DEFAULT_REPEATED = 0; + return InsertTask(TaskContainer(new Task(end + time, time, group, DEFAULT_REPEATED, task))); + } + + // Returns a random duration between min and max + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + std::chrono::milliseconds + static RandomDurationBetween(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + auto const milli_min = std::chrono::duration_cast<std::chrono::milliseconds>(min); + auto const milli_max = std::chrono::duration_cast<std::chrono::milliseconds>(max); + + // TC specific: use SFMT URandom + return std::chrono::milliseconds(urand(milli_min.count(), milli_max.count())); + } + + /// Dispatch remaining tasks + void Dispatch(); +}; + +class TaskContextInstance + : public std::enable_shared_from_this<TaskContextInstance> +{ + friend class TaskScheduler; + + /// Associated task + TaskScheduler::TaskContainer _task; + + /// Owner + std::weak_ptr<TaskScheduler> const _owner; + + /// Dispatches an action safe on the TaskScheduler + TaskContextInstance& Dispatch(std::function<TaskScheduler&(TaskScheduler&)> const& apply); + +public: + explicit TaskContextInstance(TaskScheduler::TaskContainer task, std::weak_ptr<TaskScheduler>&& owner) + : _task(task), _owner(owner) { } + + /// Returns true if the event is in the given group + bool IsInGroup(TaskScheduler::group_t const group) const; + + /// Sets the event in the given group + TaskContextInstance& SetGroup(TaskScheduler::group_t const group); + + /// Removes the group from the event + TaskContextInstance& ClearGroup(); + + /// Returns the repeat counter which increases every time the task is repeated. + TaskScheduler::repeated_t GetRepeatCounter() const; + + /// Repeats the event and sets a new duration. + /// std::chrono::seconds(5) for example. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + template<class _Rep, class _Period> + TaskContextInstance& Repeat(std::chrono::duration<_Rep, _Period> const& duration) + { + AssertOnConsumed(); + + // Set new duration, in-context timing and increment repeat counter + _task->_duration = duration; + _task->_end += duration; + _task->_repeated += 1; + return Dispatch(std::bind(&TaskScheduler::InsertTask, std::placeholders::_1, _task)); + } + + /// Repeats the event with the same duration. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + TaskContextInstance& Repeat() + { + return Repeat(_task->_duration); + } + + /// Repeats the event and set a new duration that is randomized between min and max. + /// std::chrono::seconds(5) for example. + /// This will consume the task context, its not possible to repeat the task again + /// from the same task context! + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& Repeat(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return Repeat(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Schedule a callable function that is executed at the next update tick from within the context. + /// Its safe to modify the TaskScheduler from within the callable. + TaskContextInstance& Async(std::function<void()> const& callable); + + /// Schedule an event with a fixed rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _Rep, class _Period> + TaskContextInstance& Schedule(std::chrono::duration<_Rep, _Period> const& time, + TaskScheduler::task_handler_t const& task) + { + auto const end = _task->_end; + return Dispatch([end, time, task](TaskScheduler& scheduler) -> TaskScheduler& + { + return scheduler.ScheduleAt<_Rep, _Period>(end, time, task); + }); + } + + /// Schedule an event with a fixed rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _Rep, class _Period> + TaskContextInstance& Schedule(std::chrono::duration<_Rep, _Period> const& time, + TaskScheduler::group_t const group, TaskScheduler::task_handler_t const& task) + { + auto const end = _task->_end; + return Dispatch([end, time, group, task](TaskScheduler& scheduler) -> TaskScheduler& + { + return scheduler.ScheduleAt<_Rep, _Period>(end, time, group, task); + }); + } + + /// Schedule an event with a randomized rate between min and max rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::task_handler_t const& task) + { + return Schedule(TaskScheduler::RandomDurationBetween(min, max), task); + } + + /// Schedule an event with a randomized rate between min and max rate from within the context. + /// Its possible that the new event is executed immediately! + /// Use TaskScheduler::Async to create a task + /// which will be called at the next update tick. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& Schedule(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max, TaskScheduler::group_t const group, + TaskScheduler::task_handler_t const& task) + { + return Schedule(TaskScheduler::RandomDurationBetween(min, max), group, task); + } + + /// Cancels all tasks from within the context. + TaskContextInstance& CancelAll(); + + /// Cancel all tasks of a single group from within the context. + TaskContextInstance& CancelGroup(TaskScheduler::group_t const group); + + /// Cancels all groups in the given std::vector from within the context. + /// Hint: Use std::initializer_list for this: "{1, 2, 3, 4}" + TaskContextInstance& CancelGroupsOf(std::vector<TaskScheduler::group_t> const& groups); + + /// Delays all tasks with the given duration from within the context. + template<class _Rep, class _Period> + TaskContextInstance& DelayAll(std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::DelayAll<_Rep, _Period>, std::placeholders::_1, duration)); + } + + /// Delays all tasks with a random duration between min and max from within the context. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& DelayAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayAll(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Delays all tasks of a group with the given duration from within the context. + template<class _Rep, class _Period> + TaskContextInstance& DelayGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::DelayGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); + } + + /// Delays all tasks of a group with a random duration between min and max from within the context. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& DelayGroup(TaskScheduler::group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return DelayGroup(group, TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks with the given duration. + template<class _Rep, class _Period> + TaskContextInstance& RescheduleAll(std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::RescheduleAll, std::placeholders::_1, duration)); + } + + /// Reschedule all tasks with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& RescheduleAll(std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleAll(TaskScheduler::RandomDurationBetween(min, max)); + } + + /// Reschedule all tasks of a group with the given duration. + template<class _Rep, class _Period> + TaskContextInstance& RescheduleGroup(TaskScheduler::group_t const group, std::chrono::duration<_Rep, _Period> const& duration) + { + return Dispatch(std::bind(&TaskScheduler::RescheduleGroup<_Rep, _Period>, std::placeholders::_1, group, duration)); + } + + /// Reschedule all tasks of a group with a random duration between min and max. + template<class _RepLeft, class _PeriodLeft, class _RepRight, class _PeriodRight> + TaskContextInstance& RescheduleGroup(TaskScheduler::group_t const group, + std::chrono::duration<_RepLeft, _PeriodLeft> const& min, + std::chrono::duration<_RepRight, _PeriodRight> const& max) + { + return RescheduleGroup(group, TaskScheduler::RandomDurationBetween(min, max)); + } + +private: + void AssertOnConsumed(); + + /// Invokes the associated hook of the task. + void Invoke(); +}; + +/// Milliseconds shorthand typedef. +typedef std::chrono::milliseconds Milliseconds; + +/// Seconds shorthand typedef. +typedef std::chrono::seconds Seconds; + +/// Minutes shorthand typedef. +typedef std::chrono::minutes Minutes; + +/// Hours shorthand typedef. +typedef std::chrono::hours Hours; + +#endif /// _TASK_SCHEDULER_H_ diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 2758c8e39cd..447ef9a45a8 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1021,7 +1021,7 @@ OffhandCheckAtSpellUnlearn = 1 # ClientCacheVersion # Description: Client cache version for client cache data reset. Use any value different # from DB and not recently been used to trigger client side cache reset. -# Default: 0 - (Use DB value from world DB db_version.cache_id field) +# Default: 0 - (Use DB value from world DB version.cache_id field) ClientCacheVersion = 0 |