diff options
Diffstat (limited to 'src')
114 files changed, 4028 insertions, 633 deletions
diff --git a/src/server/authserver/Authentication/TOTP.h b/src/server/authserver/Authentication/TOTP.h index 04b4a8c2110..7ced260758c 100644 --- a/src/server/authserver/Authentication/TOTP.h +++ b/src/server/authserver/Authentication/TOTP.h @@ -16,7 +16,7 @@ */ #ifndef _TOTP_H -#define _TOPT_H +#define _TOTP_H #include "openssl/hmac.h" #include "openssl/evp.h" diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h index 8b0d42b83cd..7840921e4e6 100644 --- a/src/server/collision/Management/MMapManager.h +++ b/src/server/collision/Management/MMapManager.h @@ -23,6 +23,7 @@ #include "DetourAlloc.h" #include "DetourNavMesh.h" #include "DetourNavMeshQuery.h" +#include <string> // move map related classes namespace MMAP diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp index d254a640279..42584693a13 100644 --- a/src/server/collision/Models/GameObjectModel.cpp +++ b/src/server/collision/Models/GameObjectModel.cpp @@ -130,17 +130,13 @@ bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayIn for (int i = 0; i < 8; ++i) rotated_bounds.merge(iRotation * mdl_box.corner(i)); - this->iBound = rotated_bounds + iPos; + iBound = rotated_bounds + iPos; #ifdef SPAWN_CORNERS // test: for (int i = 0; i < 8; ++i) { Vector3 pos(iBound.corner(i)); - if (Creature* c = const_cast<GameObject&>(go).SummonCreature(24440, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN)) - { - c->setFaction(35); - c->SetObjectScale(0.1f); - } + go.SummonCreature(1, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN); } #endif @@ -184,3 +180,43 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto } return hit; } + +bool GameObjectModel::Relocate(const GameObject& go) +{ + if (!iModel) + return false; + + ModelList::const_iterator it = model_list.find(go.GetDisplayId()); + if (it == model_list.end()) + return false; + + G3D::AABox mdl_box(it->second.bound); + // ignore models with no bounds + if (mdl_box == G3D::AABox::zero()) + { + VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str()); + return false; + } + + iPos = Vector3(go.GetPositionX(), go.GetPositionY(), go.GetPositionZ()); + + G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(go.GetOrientation(), 0, 0); + iInvRot = iRotation.inverse(); + // transform bounding box: + mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale); + AABox rotated_bounds; + for (int i = 0; i < 8; ++i) + rotated_bounds.merge(iRotation * mdl_box.corner(i)); + + iBound = rotated_bounds + iPos; +#ifdef SPAWN_CORNERS + // test: + for (int i = 0; i < 8; ++i) + { + Vector3 pos(iBound.corner(i)); + go.SummonCreature(1, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN); + } +#endif + + return true; +} diff --git a/src/server/collision/Models/GameObjectModel.h b/src/server/collision/Models/GameObjectModel.h index 06a74cc6eb0..6088b924343 100644 --- a/src/server/collision/Models/GameObjectModel.h +++ b/src/server/collision/Models/GameObjectModel.h @@ -45,7 +45,7 @@ class GameObjectModel /*, public Intersectable*/ float iScale; VMAP::WorldModel* iModel; - GameObjectModel() : phasemask(0), iModel(NULL) { } + GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL) { } bool initialize(const GameObject& go, const GameObjectDisplayInfoEntry& info); public: @@ -66,6 +66,8 @@ public: bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const; static GameObjectModel* Create(const GameObject& go); + + bool Relocate(GameObject const& go); }; -#endif // _GAMEOBJECT_MODEL_H
\ No newline at end of file +#endif // _GAMEOBJECT_MODEL_H diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 49beb0ae86f..231f3808aa9 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -512,7 +512,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u // unless target is outside spell range, out of mana, or LOS. bool _allowMove = false; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell); int32 mana = me->GetPower(POWER_MANA); if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) || @@ -3118,6 +3118,68 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui ProcessTimedAction(e, e.event.friendlyHealthPct.repeatMin, e.event.friendlyHealthPct.repeatMax, target); break; } + case SMART_EVENT_DISTANCE_CREATURE: + { + if (!me) + return; + + WorldObject* creature = NULL; + + if (e.event.distance.guid != 0) + { + creature = FindCreatureNear(me, e.event.distance.guid); + + if (!creature) + return; + + if (!me->IsInRange(creature, 0, (float)e.event.distance.dist)) + return; + } + else if (e.event.distance.entry != 0) + { + std::list<Creature*> list; + me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist); + + if (list.size() > 0) + creature = list.front(); + } + + if (creature) + ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat); + + break; + } + case SMART_EVENT_DISTANCE_GAMEOBJECT: + { + if (!me) + return; + + WorldObject* gameobject = NULL; + + if (e.event.distance.guid != 0) + { + gameobject = FindGameObjectNear(me, e.event.distance.guid); + + if (!gameobject) + return; + + if (!me->IsInRange(gameobject, 0, (float)e.event.distance.dist)) + return; + } + else if (e.event.distance.entry != 0) + { + std::list<GameObject*> list; + me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist); + + if (list.size() > 0) + gameobject = list.front(); + } + + if (gameobject) + ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat); + + break; + } default: TC_LOG_ERROR("sql.sql", "SmartScript::ProcessEvent: Unhandled Event type %u", e.GetEventType()); break; @@ -3138,6 +3200,10 @@ void SmartScript::InitTimer(SmartScriptHolder& e) case SMART_EVENT_OOC_LOS: RecalcTimer(e, e.event.los.cooldownMin, e.event.los.cooldownMax); break; + case SMART_EVENT_DISTANCE_CREATURE: + case SMART_EVENT_DISTANCE_GAMEOBJECT: + RecalcTimer(e, e.event.distance.repeat, e.event.distance.repeat); + break; default: e.active = true; break; @@ -3198,6 +3264,8 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff) case SMART_EVENT_TARGET_BUFFED: case SMART_EVENT_IS_BEHIND_TARGET: case SMART_EVENT_FRIENDLY_HEALTH_PCT: + case SMART_EVENT_DISTANCE_CREATURE: + case SMART_EVENT_DISTANCE_GAMEOBJECT: { ProcessEvent(e); if (e.GetScriptType() == SMART_SCRIPT_TYPE_TIMED_ACTIONLIST) diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index c6b73a468d2..93a9c6419b0 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -583,6 +583,56 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; } break; + case SMART_EVENT_DISTANCE_CREATURE: + if (e.event.distance.guid == 0 && e.event.distance.entry == 0) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE did not provide creature guid or entry, skipped."); + return false; + } + + if (e.event.distance.guid != 0 && e.event.distance.entry != 0) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE provided both an entry and guid, skipped."); + return false; + } + + if (e.event.distance.guid != 0 && !sObjectMgr->GetCreatureData(e.event.distance.guid)) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE using invalid creature guid %u, skipped.", e.event.distance.guid); + return false; + } + + if (e.event.distance.entry != 0 && !sObjectMgr->GetCreatureTemplate(e.event.distance.entry)) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE using invalid creature entry %u, skipped.", e.event.distance.entry); + return false; + } + break; + case SMART_EVENT_DISTANCE_GAMEOBJECT: + if (e.event.distance.guid == 0 && e.event.distance.entry == 0) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT did not provide gameobject guid or entry, skipped."); + return false; + } + + if (e.event.distance.guid != 0 && e.event.distance.entry != 0) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT provided both an entry and guid, skipped."); + return false; + } + + if (e.event.distance.guid != 0 && !sObjectMgr->GetGOData(e.event.distance.guid)) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid %u, skipped.", e.event.distance.guid); + return false; + } + + if (e.event.distance.entry != 0 && !sObjectMgr->GetGameObjectTemplate(e.event.distance.entry)) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject entry %u, skipped.", e.event.distance.entry); + return false; + } + break; case SMART_EVENT_GO_STATE_CHANGED: case SMART_EVENT_GO_EVENT_INFORM: case SMART_EVENT_TIMED_EVENT_TRIGGERED: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 265df8e2ac4..ac67fac0bfd 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -165,8 +165,10 @@ enum SMART_EVENT SMART_EVENT_ACTION_DONE = 72, // eventId (SharedDefines.EventId) SMART_EVENT_ON_SPELLCLICK = 73, // clicker (unit) SMART_EVENT_FRIENDLY_HEALTH_PCT = 74, // minHpPct, maxHpPct, repeatMin, repeatMax + SMART_EVENT_DISTANCE_CREATURE = 75, // guid, entry, distance, repeat + SMART_EVENT_DISTANCE_GAMEOBJECT = 76, // guid, entry, distance, repeat - SMART_EVENT_END = 75 + SMART_EVENT_END = 77 }; struct SmartEvent @@ -388,6 +390,14 @@ struct SmartEvent struct { + uint32 guid; + uint32 entry; + uint32 dist; + uint32 repeat; + } distance; + + struct + { uint32 param1; uint32 param2; uint32 param3; @@ -1220,7 +1230,7 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] = {SMART_EVENT_REACHED_HOME, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_RECEIVE_EMOTE, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_HAS_AURA, SMART_SCRIPT_TYPE_MASK_CREATURE }, - {SMART_EVENT_TARGET_BUFFED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, + {SMART_EVENT_TARGET_BUFFED, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_RESET, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_IC_LOS, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_PASSENGER_BOARDED, SMART_SCRIPT_TYPE_MASK_CREATURE }, @@ -1271,6 +1281,8 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] = {SMART_EVENT_ACTION_DONE, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_ON_SPELLCLICK, SMART_SCRIPT_TYPE_MASK_CREATURE }, {SMART_EVENT_FRIENDLY_HEALTH_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE }, + {SMART_EVENT_DISTANCE_CREATURE, SMART_SCRIPT_TYPE_MASK_CREATURE }, + {SMART_EVENT_DISTANCE_GAMEOBJECT, SMART_SCRIPT_TYPE_MASK_CREATURE }, }; enum SmartEventFlags diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 3fcfab81c73..c4aa3ef4481 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1522,21 +1522,33 @@ void Battleground::DoorOpen(uint32 type) type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID); } -GameObject* Battleground::GetBGObject(uint32 type) +GameObject* Battleground::GetBGObject(uint32 type, bool logError) { GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]); if (!obj) - TC_LOG_ERROR("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!", - type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID); + { + if (logError) + TC_LOG_ERROR("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!", + type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID); + else + TC_LOG_INFO("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!", + type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID); + } return obj; } -Creature* Battleground::GetBGCreature(uint32 type) +Creature* Battleground::GetBGCreature(uint32 type, bool logError) { Creature* creature = GetBgMap()->GetCreature(BgCreatures[type]); if (!creature) - TC_LOG_ERROR("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!", - type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID); + { + if (logError) + TC_LOG_ERROR("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!", + type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID); + else + TC_LOG_INFO("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!", + type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID); + } return creature; } diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index a866f09639a..5e2aa4b2316 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -382,8 +382,8 @@ class Battleground void StartBattleground(); - GameObject* GetBGObject(uint32 type); - Creature* GetBGCreature(uint32 type); + GameObject* GetBGObject(uint32 type, bool logError = true); + Creature* GetBGCreature(uint32 type, bool logError = true); // Location void SetMapId(uint32 MapID) { m_MapId = MapID; } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp index 6256f53f07b..610e0bcecc7 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp @@ -790,7 +790,7 @@ void BattlegroundAV::PopulateNode(BG_AV_Nodes node) if (node >= BG_AV_NODES_MAX)//fail safe return; - Creature* trigger = GetBGCreature(node + 302);//0-302 other creatures + Creature* trigger = GetBGCreature(node + 302, false);//0-302 other creatures if (!trigger) { trigger = AddCreature(WORLD_TRIGGER, diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp index be2684631df..bc684aed176 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp @@ -780,7 +780,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point) if (Point >= EY_POINTS_MAX) return; - Creature* trigger = GetBGCreature(Point + 6);//0-5 spirit guides + Creature* trigger = GetBGCreature(Point + 6, false);//0-5 spirit guides if (!trigger) trigger = AddCreature(WORLD_TRIGGER, Point+6, Team, BG_EY_TriggerPositions[Point][0], BG_EY_TriggerPositions[Point][1], BG_EY_TriggerPositions[Point][2], BG_EY_TriggerPositions[Point][3]); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index 590a07541fe..fc3f839ae76 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -658,7 +658,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) // we must del opposing faction vehicles when the node is captured (unused ones) for (uint8 i = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_1_H : BG_IC_NPC_GLAIVE_THROWER_1_A); i < (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_2_H : BG_IC_NPC_GLAIVE_THROWER_2_A); ++i) { - if (Creature* glaiveThrower = GetBGCreature(i)) + if (Creature* glaiveThrower = GetBGCreature(i, false)) { if (Vehicle* vehicleGlaive = glaiveThrower->GetVehicleKit()) { @@ -670,7 +670,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) for (uint8 i = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_1_H : BG_IC_NPC_CATAPULT_1_A); i < (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_4_H : BG_IC_NPC_CATAPULT_4_A); ++i) { - if (Creature* catapult = GetBGCreature(i)) + if (Creature* catapult = GetBGCreature(i, false)) { if (Vehicle* vehicleGlaive = catapult->GetVehicleKit()) { @@ -685,7 +685,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) { uint8 type = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_1_A : BG_IC_NPC_GLAIVE_THROWER_1_H)+i; - if (GetBGCreature(type) && GetBGCreature(type)->IsAlive()) + if (GetBGCreature(type, false) && GetBGCreature(type)->IsAlive()) continue; if (AddCreature(nodePoint->faction == TEAM_ALLIANCE ? NPC_GLAIVE_THROWER_A : NPC_GLAIVE_THROWER_H, type, nodePoint->faction, @@ -700,7 +700,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) { uint8 type = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_1_A : BG_IC_NPC_CATAPULT_1_H)+i; - if (GetBGCreature(type) && GetBGCreature(type)->IsAlive()) + if (GetBGCreature(type, false) && GetBGCreature(type)->IsAlive()) continue; if (AddCreature(NPC_CATAPULT, type, nodePoint->faction, @@ -720,7 +720,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) // we must del opposing faction vehicles when the node is captured (unused ones) for (uint8 i = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_DEMOLISHER_1_H : BG_IC_NPC_DEMOLISHER_1_A); i < (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_DEMOLISHER_4_H : BG_IC_NPC_DEMOLISHER_4_A); ++i) { - if (Creature* demolisher = GetBGCreature(i)) + if (Creature* demolisher = GetBGCreature(i, false)) { if (Vehicle* vehicleDemolisher = demolisher->GetVehicleKit()) { @@ -735,7 +735,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) { uint8 type = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_DEMOLISHER_1_A : BG_IC_NPC_DEMOLISHER_1_H)+i; - if (GetBGCreature(type) && GetBGCreature(type)->IsAlive()) + if (GetBGCreature(type, false) && GetBGCreature(type)->IsAlive()) continue; if (AddCreature(NPC_DEMOLISHER, type, nodePoint->faction, @@ -748,7 +748,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) // we check if the opossing siege engine is in use int8 enemySiege = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_SIEGE_ENGINE_H : BG_IC_NPC_SIEGE_ENGINE_A); - if (Creature* siegeEngine = GetBGCreature(enemySiege)) + if (Creature* siegeEngine = GetBGCreature(enemySiege, false)) { if (Vehicle* vehicleSiege = siegeEngine->GetVehicleKit()) { @@ -759,7 +759,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) } uint8 siegeType = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_SIEGE_ENGINE_A : BG_IC_NPC_SIEGE_ENGINE_H); - if (!GetBGCreature(siegeType) || !GetBGCreature(siegeType)->IsAlive()) + if (!GetBGCreature(siegeType, false) || !GetBGCreature(siegeType)->IsAlive()) { AddCreature((nodePoint->faction == TEAM_ALLIANCE ? NPC_SIEGE_ENGINE_A : NPC_SIEGE_ENGINE_H), siegeType, nodePoint->faction, BG_IC_WorkshopVehicles[4].GetPositionX(), BG_IC_WorkshopVehicles[4].GetPositionY(), diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index e76ba18f1e2..bb1a722ec42 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1078,8 +1078,7 @@ bool ConditionMgr::addToGossipMenuItems(Condition* cond) bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond) { uint32 conditionEffMask = cond->SourceGroup; - SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->GetSpellInfo(cond->SourceEntry)); - ASSERT(spellInfo); + SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->EnsureSpellInfo(cond->SourceEntry)); std::list<uint32> sharedMasks; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { @@ -1994,7 +1993,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) } case CONDITION_UNIT_STATE: { - if (cond->ConditionValue1 > uint32(UNIT_STATE_ALL_STATE)) + if (!(cond->ConditionValue1 & UNIT_STATE_ALL_STATE_SUPPORTED)) { TC_LOG_ERROR("sql.sql", "UnitState condition has non existing UnitState in value1 (%u), skipped", cond->ConditionValue1); return false; diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index a4b3ded80e5..37f4f4c1930 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -123,6 +123,7 @@ DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt); DBCStorage <ItemSetEntry> sItemSetStore(ItemSetEntryfmt); DBCStorage <LFGDungeonEntry> sLFGDungeonStore(LFGDungeonEntryfmt); +DBCStorage <LightEntry> sLightStore(LightEntryfmt); DBCStorage <LiquidTypeEntry> sLiquidTypeStore(LiquidTypefmt); DBCStorage <LockEntry> sLockStore(LockEntryfmt); @@ -371,6 +372,7 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sItemSetStore, dbcPath, "ItemSet.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sLFGDungeonStore, dbcPath, "LFGDungeons.dbc"); + LoadDBC(availableDbcLocales, bad_dbc_files, sLightStore, dbcPath, "Light.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sLiquidTypeStore, dbcPath, "LiquidType.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sLockStore, dbcPath, "Lock.dbc"); @@ -944,3 +946,18 @@ LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty) return NULL; } + +uint32 GetDefaultMapLight(uint32 mapId) +{ + for (int32 i = sLightStore.GetNumRows(); i >= 0; --i) + { + LightEntry const* light = sLightStore.LookupEntry(uint32(i)); + if (!light) + continue; + + if (light->MapId == mapId && light->X == 0.0f && light->Y == 0.0f && light->Z == 0.0f) + return light->Id; + } + + return 0; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index fe8fb35220b..39747141322 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -72,6 +72,8 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); +uint32 GetDefaultMapLight(uint32 mapId); + extern DBCStorage <AchievementEntry> sAchievementStore; extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index d1db4fb634d..8efc32cc9b2 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1230,6 +1230,24 @@ struct LFGDungeonEntry uint32 Entry() const { return ID + (type << 24); } }; +struct LightEntry +{ + uint32 Id; + uint32 MapId; + float X; + float Y; + float Z; + //float FalloffStart; + //float FalloffEnd; + //uint32 SkyAndFog; + //uint32 WaterSettings; + //uint32 SunsetParams; + //uint32 OtherParams; + //uint32 DeathParams; + //uint32 Unknown; + //uint32 Unknown; + //uint32 Unknown; +}; struct LiquidTypeEntry { diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index be4369399d4..a1587dd6087 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -79,6 +79,7 @@ char const ItemRandomPropertiesfmt[] = "nxiiixxssssssssssssssssx"; char const ItemRandomSuffixfmt[] = "nssssssssssssssssxxiiixxiiixx"; char const ItemSetEntryfmt[] = "dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; char const LFGDungeonEntryfmt[] = "nssssssssssssssssxiiiiiiiiixxixixxxxxxxxxxxxxxxxx"; +char const LightEntryfmt[] = "nifffxxxxxxxxxx"; char const LiquidTypefmt[] = "nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char const LockEntryfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; char const MailTemplateEntryfmt[] = "nxxxxxxxxxxxxxxxxxssssssssssssssssx"; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index d9df2057b54..98ca9222e67 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -968,6 +968,7 @@ void LFGMgr::MakeNewGroup(LfgProposal const& proposal) player->CastSpell(player, LFG_SPELL_DUNGEON_COOLDOWN, false); } + ASSERT(grp); grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); uint64 gguid = grp->GetGUID(); SetDungeon(gguid, dungeon->Entry()); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 737e3072091..69c3d169170 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -635,13 +635,13 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject void SetHomePosition(float x, float y, float z, float o) { m_homePosition.Relocate(x, y, z, o); } void SetHomePosition(const Position &pos) { m_homePosition.Relocate(pos); } - void GetHomePosition(float &x, float &y, float &z, float &ori) const { m_homePosition.GetPosition(x, y, z, ori); } - Position GetHomePosition() const { return m_homePosition; } + void GetHomePosition(float& x, float& y, float& z, float& ori) const { m_homePosition.GetPosition(x, y, z, ori); } + Position const& GetHomePosition() const { return m_homePosition; } void SetTransportHomePosition(float x, float y, float z, float o) { m_transportHomePosition.Relocate(x, y, z, o); } void SetTransportHomePosition(const Position &pos) { m_transportHomePosition.Relocate(pos); } - void GetTransportHomePosition(float &x, float &y, float &z, float &ori) { m_transportHomePosition.GetPosition(x, y, z, ori); } - Position GetTransportHomePosition() { return m_transportHomePosition; } + void GetTransportHomePosition(float& x, float& y, float& z, float& ori) const { m_transportHomePosition.GetPosition(x, y, z, ori); } + Position const& GetTransportHomePosition() const { return m_transportHomePosition; } uint32 GetWaypointPath() const { return m_path_id; } void LoadPath(uint32 pathid) { m_path_id = pathid; } diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp index ffb28849925..8671caf864d 100644 --- a/src/server/game/Entities/Creature/GossipDef.cpp +++ b/src/server/game/Entities/Creature/GossipDef.cpp @@ -28,6 +28,7 @@ GossipMenu::GossipMenu() { _menuId = 0; _locale = DEFAULT_LOCALE; + _senderGUID = 0; } GossipMenu::~GossipMenu() @@ -166,8 +167,10 @@ void PlayerMenu::ClearMenus() _questMenu.ClearMenu(); } -void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const +void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) { + _gossipMenu.SetSenderGUID(objectGUID); + WorldPacket data(SMSG_GOSSIP_MESSAGE, 100); // guess size data << uint64(objectGUID); data << uint32(_gossipMenu.GetMenuId()); // new 2.4.0 @@ -222,8 +225,10 @@ void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const _session->SendPacket(&data); } -void PlayerMenu::SendCloseGossip() const +void PlayerMenu::SendCloseGossip() { + _gossipMenu.SetSenderGUID(0); + WorldPacket data(SMSG_GOSSIP_COMPLETE, 0); _session->SendPacket(&data); } diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h index 9a30fdeee2b..e8fac878409 100644 --- a/src/server/game/Entities/Creature/GossipDef.h +++ b/src/server/game/Entities/Creature/GossipDef.h @@ -25,7 +25,7 @@ class WorldSession; -#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough +#define GOSSIP_MAX_MENU_ITEMS 32 #define DEFAULT_GOSSIP_MESSAGE 0xffffff enum Gossip_Option @@ -167,6 +167,8 @@ class GossipMenu void SetMenuId(uint32 menu_id) { _menuId = menu_id; } uint32 GetMenuId() const { return _menuId; } + void SetSenderGUID(uint64 guid) { _senderGUID = guid; } + uint64 GetSenderGUID() const { return _senderGUID; } void SetLocale(LocaleConstant locale) { _locale = locale; } LocaleConstant GetLocale() const { return _locale; } @@ -215,6 +217,7 @@ class GossipMenu GossipMenuItemContainer _menuItems; GossipMenuItemDataContainer _menuItemData; uint32 _menuId; + uint64 _senderGUID; LocaleConstant _locale; }; @@ -264,8 +267,8 @@ class PlayerMenu uint32 GetGossipOptionAction(uint32 selection) const { return _gossipMenu.GetMenuItemAction(selection); } bool IsGossipOptionCoded(uint32 selection) const { return _gossipMenu.IsMenuItemCoded(selection); } - void SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const; - void SendCloseGossip() const; + void SendGossipMenu(uint32 titleTextId, uint64 objectGUID); + void SendCloseGossip(); void SendPointOfInterest(uint32 poiId) const; /*********************************************************/ diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index f4e956e8839..7cc94d992a1 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -26,6 +26,7 @@ #include "CellImpl.h" #include "GridNotifiersImpl.h" #include "ScriptMgr.h" +#include "Transport.h" DynamicObject::DynamicObject(bool isWorldObject) : WorldObject(isWorldObject), _aura(NULL), _removedAura(NULL), _caster(NULL), _duration(0), _isViewpoint(false) @@ -47,6 +48,18 @@ DynamicObject::~DynamicObject() delete _removedAura; } +void DynamicObject::CleanupsBeforeDelete(bool finalCleanup /* = true */) +{ + WorldObject::CleanupsBeforeDelete(finalCleanup); + + if (Transport* transport = GetTransport()) + { + transport->RemovePassenger(this); + SetTransport(NULL); + m_movementInfo.transport.Reset(); + } +} + void DynamicObject::AddToWorld() { ///- Register the dynamicObject for guid lookup and for caster @@ -108,8 +121,28 @@ bool DynamicObject::CreateDynamicObject(uint32 guidlow, Unit* caster, uint32 spe if (IsWorldObject()) setActive(true); //must before add to map to be put in world container + Transport* transport = caster->GetTransport(); + if (transport) + { + m_movementInfo.transport.guid = GetGUID(); + + float x, y, z, o; + pos.GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + m_movementInfo.transport.pos.Relocate(x, y, z, o); + + SetTransport(transport); + // This object must be added to transport before adding to map for the client to properly display it + transport->AddPassenger(this); + } + if (!GetMap()->AddToMap(this)) + { + // Returning false will cause the object to be deleted - remove from transport + if (transport) + transport->RemovePassenger(this); return false; + } return true; } diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h index 5b68bf377b9..cdba5982b3f 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -32,7 +32,7 @@ enum DynamicObjectType DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2 }; -class DynamicObject : public WorldObject, public GridObject<DynamicObject> +class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MapObject { public: DynamicObject(bool isWorldObject); @@ -41,6 +41,8 @@ class DynamicObject : public WorldObject, public GridObject<DynamicObject> void AddToWorld(); void RemoveFromWorld(); + void CleanupsBeforeDelete(bool finalCleanup = true) OVERRIDE; + bool CreateDynamicObject(uint32 guidlow, Unit* caster, uint32 spellId, Position const& pos, float radius, DynamicObjectType type); void Update(uint32 p_time); void Remove(); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index e3c8c9c8136..67f7b1a46b2 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -148,7 +148,7 @@ void GameObject::AddToWorld() sObjectAccessor->AddObject(this); // The state can be changed after GameObject::Create but before GameObject::AddToWorld - bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : GetGoState() == GO_STATE_READY; + bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport()); if (m_model) GetMap()->InsertGameObjectModel(*m_model); @@ -317,7 +317,6 @@ void GameObject::Update(uint32 diff) m_lootState = GO_READY; break; } - /* TODO: Fix movement in unloaded grid - currently GO will just disappear case GAMEOBJECT_TYPE_TRANSPORT: { if (!m_goValue.Transport.AnimationInfo) @@ -326,6 +325,7 @@ void GameObject::Update(uint32 diff) if (GetGoState() == GO_STATE_READY) { m_goValue.Transport.PathProgress += diff; + /* TODO: Fix movement in unloaded grid - currently GO will just disappear uint32 timer = m_goValue.Transport.PathProgress % m_goValue.Transport.AnimationInfo->TotalTime; TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer); if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg) @@ -341,14 +341,14 @@ void GameObject::Update(uint32 diff) G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ()); - TC_LOG_INFO("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str()); + TC_LOG_DEBUG("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str()); GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation()); } + */ } break; } - */ case GAMEOBJECT_TYPE_FISHINGNODE: { // fishing code (bobber ready) @@ -2019,7 +2019,7 @@ void GameObject::SetGoState(GOState state) { SetByteValue(GAMEOBJECT_BYTES_1, 0, state); sScriptMgr->OnGameObjectStateChanged(this, state); - if (m_model) + if (m_model && !IsTransport()) { if (!IsInWorld()) return; @@ -2238,3 +2238,16 @@ float GameObject::GetInteractionDistance() return INTERACTION_DISTANCE; } } + +void GameObject::UpdateModelPosition() +{ + if (!m_model) + return; + + if (GetMap()->ContainsGameObjectModel(*m_model)) + { + GetMap()->RemoveGameObjectModel(*m_model); + m_model->Relocate(*this); + GetMap()->InsertGameObjectModel(*m_model); + } +} diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 84abc391bc6..48db64a3687 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -832,6 +832,8 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map float GetInteractionDistance(); + void UpdateModelPosition(); + protected: bool AIM_Initialize(); void UpdateModel(); // updates model in case displayId were changed diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 8af48f277bd..12c8d0ba2ac 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -370,6 +370,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const { if (flags & UPDATEFLAG_POSITION) { + ASSERT(object); Transport* transport = object->GetTransport(); if (transport) @@ -412,6 +413,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x40 if (flags & UPDATEFLAG_STATIONARY_POSITION) { + ASSERT(object); *data << object->GetStationaryX(); *data << object->GetStationaryY(); *data << object->GetStationaryZ(); @@ -459,6 +461,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x4 if (flags & UPDATEFLAG_HAS_TARGET) { + ASSERT(unit); if (Unit* victim = unit->GetVictim()) data->append(victim->GetPackGUID()); else @@ -469,7 +472,12 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const if (flags & UPDATEFLAG_TRANSPORT) { GameObject const* go = ToGameObject(); - if (go && go->IsTransport()) + /** @TODO Use IsTransport() to also handle type 11 (TRANSPORT) + Currently grid objects are not updated if there are no nearby players, + this causes clients to receive different PathProgress + resulting in players seeing the object in a different position + */ + if (go && go->ToTransport()) *data << uint32(go->GetGOValue()->Transport.PathProgress); else *data << uint32(getMSTime()); @@ -479,6 +487,9 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const if (flags & UPDATEFLAG_VEHICLE) { /// @todo Allow players to aquire this updateflag. + ASSERT(unit); + ASSERT(unit->GetVehicleKit()); + ASSERT(unit->GetVehicleKit()->GetVehicleInfo()); *data << uint32(unit->GetVehicleKit()->GetVehicleInfo()->m_ID); if (unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) *data << float(unit->GetTransOffsetO()); @@ -739,7 +750,7 @@ void Object::SetByteValue(uint16 index, uint8 offset, uint8 value) { ASSERT(index < m_valuesCount || PrintIndexError(index, true)); - if (offset > 4) + if (offset > 3) { TC_LOG_ERROR("misc", "Object::SetByteValue: wrong offset %u", offset); return; @@ -763,7 +774,7 @@ void Object::SetUInt16Value(uint16 index, uint8 offset, uint16 value) { ASSERT(index < m_valuesCount || PrintIndexError(index, true)); - if (offset > 2) + if (offset > 1) { TC_LOG_ERROR("misc", "Object::SetUInt16Value: wrong offset %u", offset); return; @@ -903,7 +914,7 @@ void Object::SetByteFlag(uint16 index, uint8 offset, uint8 newFlag) { ASSERT(index < m_valuesCount || PrintIndexError(index, true)); - if (offset > 4) + if (offset > 3) { TC_LOG_ERROR("misc", "Object::SetByteFlag: wrong offset %u", offset); return; @@ -926,7 +937,7 @@ void Object::RemoveByteFlag(uint16 index, uint8 offset, uint8 oldFlag) { ASSERT(index < m_valuesCount || PrintIndexError(index, true)); - if (offset > 4) + if (offset > 3) { TC_LOG_ERROR("misc", "Object::RemoveByteFlag: wrong offset %u", offset); return; @@ -1552,9 +1563,9 @@ void WorldObject::GetRandomPoint(const Position &srcPos, float distance, Positio void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const { - float new_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true); + float new_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z + 2.0f, true); if (new_z > INVALID_HEIGHT) - z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface + z = new_z + 0.05f; // just to be sure that we are not a few pixel under the surface } void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const @@ -1574,8 +1585,8 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const bool canSwim = ToCreature()->CanSwim(); float ground_z = z; float max_z = canSwim - ? GetBaseMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)) - : ((ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true))); + ? GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)) + : ((ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true))); if (max_z > INVALID_HEIGHT) { if (z > max_z) @@ -1586,7 +1597,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const } else { - float ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true); + float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true); if (z < ground_z) z = ground_z; } @@ -1598,7 +1609,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const if (!ToPlayer()->CanFly()) { float ground_z = z; - float max_z = GetBaseMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)); + float max_z = GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)); if (max_z > INVALID_HEIGHT) { if (z > max_z) @@ -1609,7 +1620,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const } else { - float ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true); + float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true); if (z < ground_z) z = ground_z; } @@ -1617,7 +1628,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const } default: { - float ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true); + float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true); if (ground_z > INVALID_HEIGHT) z = ground_z; break; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 1d7db005a0b..19ac8dd57b5 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -2060,8 +2060,7 @@ void Pet::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) continue; uint32 unSpellId = itr->first; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(unSpellId); - ASSERT(spellInfo); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(unSpellId); // Not send cooldown for this spells if (spellInfo->IsCooldownStartedOnEvent()) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 2f7b37a2254..05c89a99ed2 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4204,7 +4204,7 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId) if (_spell_idx->second->skillId != SKILL_MOUNTS) break; // We can break because mount spells belong only to one skillline (at least 310 flyers do) - spellInfo = sSpellMgr->GetSpellInfo(itr->first); + spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310) @@ -15743,6 +15743,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) // not allow have daily quest if daily quest from exclusive group already recently completed Quest const* Nquest = sObjectMgr->GetQuestTemplate(exclude_Id); + ASSERT(Nquest); if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false) || !SatisfyQuestSeasonal(Nquest, false)) { if (msg) @@ -15944,6 +15945,7 @@ bool Player::TakeQuestSourceItem(uint32 questId, bool msg) return false; } + ASSERT(item); bool destroyItem = true; for (uint8 n = 0; n < QUEST_ITEM_OBJECTIVES_COUNT; ++n) if (item->StartQuest == questId && srcItemId == quest->RequiredItemId[n]) @@ -21334,6 +21336,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc if (sWorld->getBoolConfig(CONFIG_INSTANT_TAXI)) { TaxiNodesEntry const* lastPathNode = sTaxiNodesStore.LookupEntry(nodes[nodes.size()-1]); + ASSERT(lastPathNode); m_taxi.ClearTaxiDestinations(); TeleportTo(lastPathNode->map_id, lastPathNode->x, lastPathNode->y, lastPathNode->z, GetOrientation()); return false; @@ -21554,6 +21557,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c if (crItem->ExtendedCost) // case for new honor system { ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); + ASSERT(iece); if (iece->reqhonorpoints) ModifyHonorPoints(- int32(iece->reqhonorpoints * count)); @@ -24745,6 +24749,12 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) return; } + if (!item->AllowedForPlayer(this)) + { + SendLootRelease(GetLootGUID()); + return; + } + // questitems use the blocked field for other purposes if (!qitem && item->is_blocked) { diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index f8c38d32390..cf7cadadbfd 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -31,6 +31,7 @@ #include "Player.h" #include "Cell.h" #include "CellImpl.h" +#include "Totem.h" Transport::Transport() : GameObject(), _transportInfo(NULL), _isMoving(true), _pendingStop(false), @@ -95,9 +96,26 @@ bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, floa SetGoAnimProgress(animprogress); SetName(goinfo->name); UpdateRotationFields(0.0f, 1.0f); + + m_model = GameObjectModel::Create(*this); return true; } +void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) +{ + UnloadStaticPassengers(); + while (!_passengers.empty()) + { + WorldObject* obj = *_passengers.begin(); + obj->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + obj->m_movementInfo.transport.Reset(); + obj->SetTransport(NULL); + RemovePassenger(obj); + } + + GameObject::CleanupsBeforeDelete(finalCleanup); +} + void Transport::Update(uint32 diff) { uint32 const positionUpdateDelay = 200; @@ -202,6 +220,9 @@ void Transport::Update(uint32 diff) void Transport::AddPassenger(WorldObject* passenger) { + if (!IsInWorld()) + return; + if (_passengers.insert(passenger).second) { TC_LOG_DEBUG("entities.transport", "Object %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); @@ -308,11 +329,135 @@ GameObject* Transport::CreateGOPassenger(uint32 guid, GameObjectData const* data return go; } +TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties /*= NULL*/, uint32 duration /*= 0*/, Unit* summoner /*= NULL*/, uint32 spellId /*= 0*/, uint32 vehId /*= 0*/) +{ + Map* map = FindMap(); + if (!map) + return NULL; + + uint32 mask = UNIT_MASK_SUMMON; + if (properties) + { + switch (properties->Category) + { + case SUMMON_CATEGORY_PET: + mask = UNIT_MASK_GUARDIAN; + break; + case SUMMON_CATEGORY_PUPPET: + mask = UNIT_MASK_PUPPET; + break; + case SUMMON_CATEGORY_VEHICLE: + mask = UNIT_MASK_MINION; + break; + case SUMMON_CATEGORY_WILD: + case SUMMON_CATEGORY_ALLY: + case SUMMON_CATEGORY_UNK: + { + switch (properties->Type) + { + case SUMMON_TYPE_MINION: + case SUMMON_TYPE_GUARDIAN: + case SUMMON_TYPE_GUARDIAN2: + mask = UNIT_MASK_GUARDIAN; + break; + case SUMMON_TYPE_TOTEM: + case SUMMON_TYPE_LIGHTWELL: + mask = UNIT_MASK_TOTEM; + break; + case SUMMON_TYPE_VEHICLE: + case SUMMON_TYPE_VEHICLE2: + mask = UNIT_MASK_SUMMON; + break; + case SUMMON_TYPE_MINIPET: + mask = UNIT_MASK_MINION; + break; + default: + if (properties->Flags & 512) // Mirror Image, Summon Gargoyle + mask = UNIT_MASK_GUARDIAN; + break; + } + break; + } + default: + return NULL; + } + } + + uint32 phase = PHASEMASK_NORMAL; + uint32 team = 0; + if (summoner) + { + phase = summoner->GetPhaseMask(); + if (summoner->GetTypeId() == TYPEID_PLAYER) + team = summoner->ToPlayer()->GetTeam(); + } + + TempSummon* summon = NULL; + switch (mask) + { + case UNIT_MASK_SUMMON: + summon = new TempSummon(properties, summoner, false); + break; + case UNIT_MASK_GUARDIAN: + summon = new Guardian(properties, summoner, false); + break; + case UNIT_MASK_PUPPET: + summon = new Puppet(properties, summoner); + break; + case UNIT_MASK_TOTEM: + summon = new Totem(properties, summoner); + break; + case UNIT_MASK_MINION: + summon = new Minion(properties, summoner, false); + break; + } + + float x, y, z, o; + pos.GetPosition(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + + if (!summon->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, phase, entry, vehId, team, x, y, z, o)) + { + delete summon; + return NULL; + } + + summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, spellId); + + summon->SetTransport(this); + summon->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + summon->m_movementInfo.transport.guid = GetGUID(); + summon->m_movementInfo.transport.pos.Relocate(pos); + summon->Relocate(x, y, z, o); + summon->SetHomePosition(x, y, z, o); + summon->SetTransportHomePosition(pos); + + /// @HACK - transport models are not added to map's dynamic LoS calculations + /// because the current GameObjectModel cannot be moved without recreating + summon->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + + summon->InitStats(duration); + + if (!map->AddToMap<Creature>(summon)) + { + delete summon; + return NULL; + } + + _staticPassengers.insert(summon); + + summon->InitSummon(); + summon->SetTempSummonType(summonType); + + return summon; +} + void Transport::UpdatePosition(float x, float y, float z, float o) { bool newActive = GetMap()->IsGridLoaded(x, y); Relocate(x, y, z, o); + UpdateModelPosition(); UpdatePassengerPositions(_passengers); @@ -450,12 +595,16 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl if (!obj->ToPlayer()->TeleportTo(newMapid, destX, destY, destZ, destO, TELE_TO_NOT_LEAVE_TRANSPORT)) _passengers.erase(obj); break; + case TYPEID_DYNAMICOBJECT: + obj->AddObjectToRemoveList(); + break; default: break; } } Relocate(x, y, z, o); + UpdateModelPosition(); GetMap()->AddToMap<Transport>(this); return true; } @@ -518,6 +667,9 @@ void Transport::UpdatePassengerPositions(std::set<WorldObject*>& passengers) case TYPEID_GAMEOBJECT: GetMap()->GameObjectRelocation(passenger->ToGameObject(), x, y, z, o, false); break; + case TYPEID_DYNAMICOBJECT: + GetMap()->DynamicObjectRelocation(passenger->ToDynObject(), x, y, z, o); + break; default: break; } diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 55f4daddecc..398356c4980 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -34,9 +34,11 @@ class Transport : public GameObject, public TransportBase ~Transport(); bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); - void Update(uint32 diff); + void CleanupsBeforeDelete(bool finalCleanup = true) OVERRIDE; - void BuildUpdate(UpdateDataMapType& data_map); + void Update(uint32 diff) OVERRIDE; + + void BuildUpdate(UpdateDataMapType& data_map) OVERRIDE; void AddPassenger(WorldObject* passenger); void RemovePassenger(WorldObject* passenger); @@ -45,14 +47,32 @@ class Transport : public GameObject, public TransportBase Creature* CreateNPCPassenger(uint32 guid, CreatureData const* data); GameObject* CreateGOPassenger(uint32 guid, GameObjectData const* data); + /** + * @fn bool Transport::SummonPassenger(uint64, Position const&, TempSummonType, SummonPropertiesEntry const*, uint32, Unit*, uint32, uint32) + * + * @brief Temporarily summons a creature as passenger on this transport. + * + * @param entry Id of the creature from creature_template table + * @param pos Initial position of the creature (transport offsets) + * @param summonType + * @param properties + * @param duration Determines how long the creauture will exist in world depending on @summonType (in milliseconds) + * @param summoner Summoner of the creature (for AI purposes) + * @param spellId + * @param vehId If set, this value overrides vehicle id from creature_template that the creature will use + * + * @return Summoned creature. + */ + TempSummon* SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties = NULL, uint32 duration = 0, Unit* summoner = NULL, uint32 spellId = 0, uint32 vehId = 0); + /// This method transforms supplied transport offsets into global coordinates - void CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= NULL*/) const + void CalculatePassengerPosition(float& x, float& y, float& z, float* o = NULL) const OVERRIDE { TransportBase::CalculatePassengerPosition(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); } /// This method transforms supplied global coordinates into local offsets - void CalculatePassengerOffset(float& x, float& y, float& z, float* o /*= NULL*/) const + void CalculatePassengerOffset(float& x, float& y, float& z, float* o = NULL) const OVERRIDE { TransportBase::CalculatePassengerOffset(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); } diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 3afa6b016d2..04136221d0d 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -1149,7 +1149,7 @@ bool Guardian::UpdateStats(Stats stat) if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value AddPct(mod, spellInfo->Effects[EFFECT_0].CalcValue()); } } @@ -1318,7 +1318,7 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged) if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt { - SpellInfo const* sProto = sSpellMgr->GetSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value + SpellInfo const* sProto = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value mod += CalculatePct(1.0f, sProto->Effects[1].CalcValue()); } } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 52565a2eaa5..379cecc3db7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1286,10 +1286,8 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo |= HITINFO_AFFECTS_VICTIM; int32 resilienceReduction = damageInfo->damage; - if (attackType != RANGED_ATTACK) - ApplyResilience(victim, NULL, &resilienceReduction, (damageInfo->hitOutCome == MELEE_HIT_CRIT), CR_CRIT_TAKEN_MELEE); - else - ApplyResilience(victim, NULL, &resilienceReduction, (damageInfo->hitOutCome == MELEE_HIT_CRIT), CR_CRIT_TAKEN_RANGED); + // attackType is checked already for BASE_ATTACK or OFF_ATTACK so it can't be RANGED_ATTACK here + ApplyResilience(victim, NULL, &resilienceReduction, (damageInfo->hitOutCome == MELEE_HIT_CRIT), CR_CRIT_TAKEN_MELEE); resilienceReduction = damageInfo->damage - resilienceReduction; damageInfo->damage -= resilienceReduction; damageInfo->cleanDamage += resilienceReduction; @@ -12990,6 +12988,8 @@ float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spell return 0; if (spellInfo->RangeEntry->maxRangeFriend == spellInfo->RangeEntry->maxRangeHostile) return spellInfo->GetMaxRange(); + if (target == NULL) + return spellInfo->GetMaxRange(true); return spellInfo->GetMaxRange(!IsHostileTo(target)); } @@ -17288,7 +17288,12 @@ bool CharmInfo::IsCommandFollow() void CharmInfo::SaveStayPosition() { //! At this point a new spline destination is enabled because of Unit::StopMoving() - G3D::Vector3 const stayPos = _unit->movespline->FinalDestination(); + G3D::Vector3 stayPos = _unit->movespline->FinalDestination(); + + if (_unit->movespline->onTransport) + if (TransportBase* transport = _unit->GetDirectTransport()) + transport->CalculatePassengerPosition(stayPos.x, stayPos.y, stayPos.z); + _stayX = stayPos.x; _stayY = stayPos.y; _stayZ = stayPos.z; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 751bfcb8126..6b6d844472a 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -513,6 +513,12 @@ enum UnitState UNIT_STATE_CHASE_MOVE = 0x04000000, UNIT_STATE_FOLLOW_MOVE = 0x08000000, UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator + UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE + | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED + | UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING + | UNIT_STATE_POSSESSED | UNIT_STATE_CHARGING | UNIT_STATE_JUMPING | UNIT_STATE_MOVE | UNIT_STATE_ROTATING + | UNIT_STATE_EVADE | UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE + | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE | UNIT_STATE_IGNORE_PATHFINDING, UNIT_STATE_UNATTACKABLE = UNIT_STATE_IN_FLIGHT, // for real move using movegen check and stop (except unstoppable flight) UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE, diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 48e4411e035..8784e2ca4e7 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1087,6 +1087,11 @@ void ObjectMgr::LoadEquipmentTemplates() } uint8 id = fields[1].GetUInt8(); + if (!id) + { + TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature %u, skipped.", entry); + continue; + } EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id]; @@ -1437,6 +1442,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guidLow, uint32 linkedGuidLow) return false; const CreatureData* master = GetCreatureData(guidLow); + ASSERT(master); uint64 guid = MAKE_NEW_GUID(guidLow, master->id, HIGHGUID_UNIT); if (!linkedGuidLow) // we're removing the linking diff --git a/src/server/game/Handlers/AddonHandler.cpp b/src/server/game/Handlers/AddonHandler.cpp index 3c9a66bedb5..806cbd1c7fc 100644 --- a/src/server/game/Handlers/AddonHandler.cpp +++ b/src/server/game/Handlers/AddonHandler.cpp @@ -49,7 +49,7 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* source, WorldPacket* target) AddOnPacked.resize(AddonRealSize); // resize target for zlib action - if (!uncompress(AddOnPacked.contents(), &AddonRealSize, source->contents() + CurrentPosition, source->size() - CurrentPosition)!= Z_OK) + if (uncompress(AddOnPacked.contents(), &AddonRealSize, source->contents() + CurrentPosition, source->size() - CurrentPosition) == Z_OK) { target->Initialize(SMSG_ADDON_INFO); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 60f4fb09c28..4fd1d516d31 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -97,9 +97,19 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) recvData >> guid >> menuId >> gossipListId; + if (!_player->PlayerTalkClass->GetGossipMenu().GetItem(gossipListId)) + { + recvData.rfinish(); + return; + } + if (_player->PlayerTalkClass->IsGossipOptionCoded(gossipListId)) recvData >> code; + // Prevent cheating on C++ scripted menus + if (_player->PlayerTalkClass->GetGossipMenu().GetSenderGUID() != guid) + return; + Creature* unit = NULL; GameObject* go = NULL; if (IS_CRE_OR_VEH_GUID(guid)) @@ -151,7 +161,8 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) else { go->AI()->GossipSelectCode(_player, menuId, gossipListId, code.c_str()); - sScriptMgr->OnGossipSelectCode(_player, go, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId), code.c_str()); + if (!sScriptMgr->OnGossipSelectCode(_player, go, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId), code.c_str())) + _player->OnGossipSelect(go, gossipListId, menuId); } } else diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 21e282cfcf4..8dc393c5f85 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -217,13 +217,13 @@ void Map::DeleteStateMachine() } Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent): -_creatureToMoveLock(false), _gameObjectsToMoveLock(false), +_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD), m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()), i_gridExpiry(expiry), -i_scriptLock(false) +i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) { m_parentMap = (_parent ? _parent : this); for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx) @@ -281,6 +281,15 @@ void Map::AddToGrid(GameObject* obj, Cell const& cell) obj->SetCurrentCell(cell); } +template<> +void Map::AddToGrid(DynamicObject* obj, Cell const& cell) +{ + NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); + grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj); + + obj->SetCurrentCell(cell); +} + template<class T> void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/) { } @@ -468,6 +477,7 @@ bool Map::AddPlayerToMap(Player* player) SendInitSelf(player); SendInitTransports(player); + SendZoneDynamicInfo(player); player->m_clientGUIDs.clear(); player->UpdateObjectVisibility(false); @@ -944,6 +954,7 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float else { go->Relocate(x, y, z, orientation); + go->UpdateModelPosition(); go->UpdateObjectVisibility(false); RemoveGameObjectFromMoveList(go); } @@ -953,6 +964,38 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float ASSERT(integrity_check == old_cell); } +void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float z, float orientation) +{ + Cell integrity_check(dynObj->GetPositionX(), dynObj->GetPositionY()); + Cell old_cell = dynObj->GetCurrentCell(); + + ASSERT(integrity_check == old_cell); + Cell new_cell(x, y); + + if (!getNGrid(new_cell.GridX(), new_cell.GridY())) + return; + + // delay creature move for grid/cell to grid/cell moves + if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell)) + { +#ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "GameObject (GUID: %u) added to moving list from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", dynObj->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); +#endif + AddDynamicObjectToMoveList(dynObj, x, y, z, orientation); + // in diffcell/diffgrid case notifiers called at finishing move dynObj in Map::MoveAllGameObjectsInMoveList + } + else + { + dynObj->Relocate(x, y, z, orientation); + dynObj->UpdateObjectVisibility(false); + RemoveDynamicObjectFromMoveList(dynObj); + } + + old_cell = dynObj->GetCurrentCell(); + integrity_check = Cell(dynObj->GetPositionX(), dynObj->GetPositionY()); + ASSERT(integrity_check == old_cell); +} + void Map::AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang) { if (_creatureToMoveLock) //can this happen? @@ -991,6 +1034,25 @@ void Map::RemoveGameObjectFromMoveList(GameObject* go) go->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } +void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj, float x, float y, float z, float ang) +{ + if (_dynamicObjectsToMoveLock) //can this happen? + return; + + if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_NONE) + _dynamicObjectsToMove.push_back(dynObj); + dynObj->SetNewCellPosition(x, y, z, ang); +} + +void Map::RemoveDynamicObjectFromMoveList(DynamicObject* dynObj) +{ + if (_dynamicObjectsToMoveLock) //can this happen? + return; + + if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) + dynObj->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; +} + void Map::MoveAllCreaturesInMoveList() { _creatureToMoveLock = true; @@ -1071,6 +1133,7 @@ void Map::MoveAllGameObjectsInMoveList() { // update pos go->Relocate(go->_newPosition); + go->UpdateModelPosition(); go->UpdateObjectVisibility(false); } else @@ -1091,6 +1154,44 @@ void Map::MoveAllGameObjectsInMoveList() _gameObjectsToMoveLock = false; } +void Map::MoveAllDynamicObjectsInMoveList() +{ + _dynamicObjectsToMoveLock = true; + for (std::vector<DynamicObject*>::iterator itr = _dynamicObjectsToMove.begin(); itr != _dynamicObjectsToMove.end(); ++itr) + { + DynamicObject* dynObj = *itr; + if (dynObj->FindMap() != this) //transport is teleported to another map + continue; + + if (dynObj->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) + { + dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; + continue; + } + + dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; + if (!dynObj->IsInWorld()) + continue; + + // do move or do move to respawn or remove creature if previous all fail + if (DynamicObjectCellRelocation(dynObj, Cell(dynObj->_newPosition.m_positionX, dynObj->_newPosition.m_positionY))) + { + // update pos + dynObj->Relocate(dynObj->_newPosition); + dynObj->UpdateObjectVisibility(false); + } + else + { +#ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) cannot be moved to unloaded grid.", dynObj->GetGUIDLow()); +#endif + } + } + + _dynamicObjectsToMove.clear(); + _dynamicObjectsToMoveLock = false; +} + bool Map::CreatureCellRelocation(Creature* c, Cell new_cell) { Cell const& old_cell = c->GetCurrentCell(); @@ -1213,6 +1314,67 @@ bool Map::GameObjectCellRelocation(GameObject* go, Cell new_cell) return false; } +bool Map::DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell) +{ + Cell const& old_cell = go->GetCurrentCell(); + if (!old_cell.DiffGrid(new_cell)) // in same grid + { + // if in same cell then none do + if (old_cell.DiffCell(new_cell)) + { + #ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) moved in grid[%u, %u] from cell[%u, %u] to cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); + #endif + + go->RemoveFromGrid(); + AddToGrid(go, new_cell); + } + else + { + #ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) moved in same grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY()); + #endif + } + + return true; + } + + // in diff. grids but active GameObject + if (go->isActiveObject()) + { + EnsureGridLoadedForActiveObject(new_cell, go); + + #ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "Active DynamicObject (GUID: %u) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + + go->RemoveFromGrid(); + AddToGrid(go, new_cell); + + return true; + } + + // in diff. loaded grid normal GameObject + if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY()))) + { + #ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + + go->RemoveFromGrid(); + EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY())); + AddToGrid(go, new_cell); + + return true; + } + + // fail to move: normal GameObject attempt move to unloaded grid + #ifdef TRINITY_DEBUG + TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) attempted to move from grid[%u, %u]cell[%u, %u] to unloaded grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + return false; +} + bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly) { float resp_x, resp_y, resp_z, resp_o; @@ -2490,7 +2652,10 @@ void Map::RemoveAllObjectsInRemoveList() RemoveFromMap((DynamicObject*)obj, true); break; case TYPEID_GAMEOBJECT: - RemoveFromMap((GameObject*)obj, true); + if (Transport* transport = obj->ToGameObject()->ToTransport()) + RemoveFromMap(transport, true); + else + RemoveFromMap(obj->ToGameObject(), true); break; case TYPEID_UNIT: // in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call @@ -3274,3 +3439,103 @@ time_t Map::GetLinkedRespawnTime(uint64 guid) const return time_t(0); } +void Map::SendZoneDynamicInfo(Player* player) +{ + uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); + ZoneDynamicInfoMap::const_iterator itr = _zoneDynamicInfo.find(zoneId); + if (itr == _zoneDynamicInfo.end()) + return; + + if (uint32 music = itr->second.MusicId) + { + WorldPacket data(SMSG_PLAY_MUSIC, 4); + data << uint32(music); + player->SendDirectMessage(&data); + } + + if (uint32 weather = itr->second.WeatherId) + { + WorldPacket data(SMSG_WEATHER, 4 + 4 + 1); + data << uint32(weather); + data << float(itr->second.WeatherGrade); + data << uint8(0); + player->SendDirectMessage(&data); + } + + if (uint32 overrideLight = itr->second.OverrideLightId) + { + WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 1); + data << uint32(_defaultLight); + data << uint32(overrideLight); + data << uint32(itr->second.LightFadeInTime); + player->SendDirectMessage(&data); + } +} + +void Map::SetZoneMusic(uint32 zoneId, uint32 musicId) +{ + if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) + _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); + + _zoneDynamicInfo[zoneId].MusicId = musicId; + + Map::PlayerList const& players = GetPlayers(); + if (!players.isEmpty()) + { + WorldPacket data(SMSG_PLAY_MUSIC, 4); + data << uint32(musicId); + + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()) + if (player->GetZoneId() == zoneId) + player->SendDirectMessage(&data); + } +} + +void Map::SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade) +{ + if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) + _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); + + ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId]; + info.WeatherId = weatherId; + info.WeatherGrade = weatherGrade; + Map::PlayerList const& players = GetPlayers(); + + if (!players.isEmpty()) + { + WorldPacket data(SMSG_WEATHER, 4 + 4 + 1); + data << uint32(weatherId); + data << float(weatherGrade); + data << uint8(0); + + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()) + if (player->GetZoneId() == zoneId) + player->SendDirectMessage(&data); + } +} + +void Map::SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime) +{ + if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) + _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); + + ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId]; + info.OverrideLightId = lightId; + info.LightFadeInTime = fadeInTime; + Map::PlayerList const& players = GetPlayers(); + + if (!players.isEmpty()) + { + WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 1); + data << uint32(_defaultLight); + data << uint32(lightId); + data << uint32(fadeInTime); + + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()) + if (player->GetZoneId() == zoneId) + player->SendDirectMessage(&data); + } +} diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 8e7886dc5d2..4daeebe43d1 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -229,6 +229,18 @@ enum LevelRequirementVsMode LEVELREQUIREMENT_HEROIC = 70 }; +struct ZoneDynamicInfo +{ + ZoneDynamicInfo() : MusicId(0), WeatherId(0), WeatherGrade(0.0f), + OverrideLightId(0), LightFadeInTime(0) { } + + uint32 MusicId; + uint32 WeatherId; + float WeatherGrade; + uint32 OverrideLightId; + uint32 LightFadeInTime; +}; + #if defined(__GNUC__) #pragma pack() #else @@ -243,6 +255,8 @@ enum LevelRequirementVsMode typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType; +typedef UNORDERED_MAP<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap; + class Map : public GridRefManager<NGridType> { friend class MapReference; @@ -280,6 +294,7 @@ class Map : public GridRefManager<NGridType> void PlayerRelocation(Player*, float x, float y, float z, float orientation); void CreatureRelocation(Creature* creature, float x, float y, float z, float ang, bool respawnRelocationOnFail = true); void GameObjectRelocation(GameObject* go, float x, float y, float z, float orientation, bool respawnRelocationOnFail = true); + void DynamicObjectRelocation(DynamicObject* go, float x, float y, float z, float orientation); template<class T, class CONTAINER> void Visit(const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor); @@ -353,6 +368,7 @@ class Map : public GridRefManager<NGridType> void MoveAllCreaturesInMoveList(); void MoveAllGameObjectsInMoveList(); + void MoveAllDynamicObjectsInMoveList(); void RemoveAllObjectsInRemoveList(); virtual void RemoveAllPlayers(); @@ -488,6 +504,11 @@ class Map : public GridRefManager<NGridType> void SendInitTransports(Player* player); void SendRemoveTransports(Player* player); + void SendZoneDynamicInfo(Player* player); + + void SetZoneMusic(uint32 zoneId, uint32 musicId); + void SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade); + void SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime); private: void LoadMapAndVMap(int gx, int gy); @@ -502,12 +523,15 @@ class Map : public GridRefManager<NGridType> bool CreatureCellRelocation(Creature* creature, Cell new_cell); bool GameObjectCellRelocation(GameObject* go, Cell new_cell); + bool DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell); template<class T> void InitializeObject(T* obj); void AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang); void RemoveCreatureFromMoveList(Creature* c); void AddGameObjectToMoveList(GameObject* go, float x, float y, float z, float ang); void RemoveGameObjectFromMoveList(GameObject* go); + void AddDynamicObjectToMoveList(DynamicObject* go, float x, float y, float z, float ang); + void RemoveDynamicObjectFromMoveList(DynamicObject* go); bool _creatureToMoveLock; std::vector<Creature*> _creaturesToMove; @@ -515,6 +539,9 @@ class Map : public GridRefManager<NGridType> bool _gameObjectsToMoveLock; std::vector<GameObject*> _gameObjectsToMove; + bool _dynamicObjectsToMoveLock; + std::vector<DynamicObject*> _dynamicObjectsToMove; + bool IsGridLoaded(const GridCoord &) const; void EnsureGridCreated(const GridCoord &); void EnsureGridCreated_i(const GridCoord &); @@ -626,6 +653,9 @@ class Map : public GridRefManager<NGridType> UNORDERED_MAP<uint32 /*dbGUID*/, time_t> _creatureRespawnTimes; UNORDERED_MAP<uint32 /*dbGUID*/, time_t> _goRespawnTimes; + + ZoneDynamicInfoMap _zoneDynamicInfo; + uint32 _defaultLight; }; enum InstanceResetMethod diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index f1656592000..28e58c87323 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -25,6 +25,7 @@ #include "MoveSplineInit.h" #include "MoveSpline.h" #include "Player.h" +#include "VehicleDefines.h" template<class T, typename D> void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool updateDestination) @@ -154,6 +155,9 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff) //More distance let have better performance, less distance let have more sensitive reaction at target move. float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); G3D::Vector3 dest = owner->movespline->FinalDestination(); + if (owner->movespline->onTransport) + if (TransportBase* transport = owner->GetDirectTransport()) + transport->CalculatePassengerPosition(dest.x, dest.y, dest.z); if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly()) targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist); diff --git a/src/server/game/Movement/Spline/MovementTypedefs.h b/src/server/game/Movement/Spline/MovementTypedefs.h index ffc19f10454..22850036b61 100644 --- a/src/server/game/Movement/Spline/MovementTypedefs.h +++ b/src/server/game/Movement/Spline/MovementTypedefs.h @@ -47,12 +47,6 @@ namespace Movement float computeFallTime(float path_length, bool isSafeFall); float computeFallElevation(float t_passed, bool isSafeFall, float start_velocity = 0.0f); -#ifndef static_assert - #define CONCAT(x, y) CONCAT1 (x, y) - #define CONCAT1(x, y) x##y - #define static_assert(expr, msg) typedef char CONCAT(static_assert_failed_at_line_, __LINE__) [(expr) ? 1 : -1] -#endif - template<class T, T limit> class counter { diff --git a/src/server/game/Movement/Spline/Spline.h b/src/server/game/Movement/Spline/Spline.h index 1444b2872d1..6e8a5a0281d 100644 --- a/src/server/game/Movement/Spline/Spline.h +++ b/src/server/game/Movement/Spline/Spline.h @@ -56,7 +56,7 @@ protected: // client's value is 20, blizzs use 2-3 steps to compute length STEPS_PER_SEGMENT = 3 }; - static_assert(STEPS_PER_SEGMENT > 0, "shouldn't be lesser than 1"); + static_assert(STEPS_PER_SEGMENT > 0, "STEPS_PER_SEGMENT shouldn't be lesser than 1"); protected: void EvaluateLinear(index_type, float, Vector3&) const; diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 8da94ef2852..7365d592a62 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -522,6 +522,7 @@ void AddSC_boss_falric(); void AddSC_boss_marwyn(); void AddSC_boss_lord_marrowgar(); // Icecrown Citadel void AddSC_boss_lady_deathwhisper(); +void AddSC_boss_icecrown_gunship_battle(); void AddSC_boss_deathbringer_saurfang(); void AddSC_boss_festergut(); void AddSC_boss_rotface(); @@ -1361,6 +1362,7 @@ void AddNorthrendScripts() AddSC_boss_marwyn(); AddSC_boss_lord_marrowgar(); // Icecrown Citadel AddSC_boss_lady_deathwhisper(); + AddSC_boss_icecrown_gunship_battle(); AddSC_boss_deathbringer_saurfang(); AddSC_boss_festergut(); AddSC_boss_rotface(); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index b7aabcc7589..4ed742167a5 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -4772,7 +4772,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool uint32 spellId = 24659; if (apply && caster) { - SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId); for (uint32 i = 0; i < spell->StackAmount; ++i) caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID()); @@ -4787,7 +4787,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool uint32 spellId = 24662; if (apply && caster) { - SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId); for (uint32 i = 0; i < spell->StackAmount; ++i) caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID()); break; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8b88ec9af92..124c1c21332 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -953,7 +953,7 @@ void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTa case TARGET_UNIT_CHANNEL_TARGET: { WorldObject* target = ObjectAccessor::GetUnit(*m_caster, m_originalCaster->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT)); - CallScriptObjectTargetSelectHandlers(target, effIndex); + CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); // unit target may be no longer avalible - teleported out of map for example if (target && target->ToUnit()) AddUnitTarget(target->ToUnit(), 1 << effIndex); @@ -966,7 +966,7 @@ void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTa m_targets.SetDst(channeledSpell->m_targets); else if (WorldObject* target = ObjectAccessor::GetWorldObject(*m_caster, m_originalCaster->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT))) { - CallScriptObjectTargetSelectHandlers(target, effIndex); + CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); if (target) m_targets.SetDst(*target); } @@ -1047,17 +1047,34 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar return; } - CallScriptObjectTargetSelectHandlers(target, effIndex); + CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); + if (!target) + { + TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id %u set NULL target, effect %u", m_spellInfo->Id, effIndex); + return; + } switch (targetType.GetObjectType()) { case TARGET_OBJECT_TYPE_UNIT: + { if (Unit* unitTarget = target->ToUnit()) AddUnitTarget(unitTarget, effMask, true, false); + else + { + TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id %u set object of wrong type, expected unit, got %s, effect %u", m_spellInfo->Id, GetLogNameForGuid(target->GetGUID()), effMask); + return; + } break; + } case TARGET_OBJECT_TYPE_GOBJ: if (GameObject* gobjTarget = target->ToGameObject()) AddGOTarget(gobjTarget, effMask); + else + { + TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id %u set object of wrong type, expected gameobject, got %s, effect %u", m_spellInfo->Id, GetLogNameForGuid(target->GetGUID()), effMask); + return; + } break; case TARGET_OBJECT_TYPE_DEST: m_targets.SetDst(*target); @@ -1090,7 +1107,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> searcher(m_caster, targets, check, containerTypeMask); SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> >(searcher, containerTypeMask, m_caster, m_caster, radius); - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex); + CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); if (!targets.empty()) { @@ -1171,7 +1188,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod; SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions); - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex); + CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); if (!targets.empty()) { @@ -1280,7 +1297,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici } } - CallScriptDestinationTargetSelectHandlers(dest, effIndex); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); m_targets.SetDst(dest); } @@ -1313,7 +1330,7 @@ void Spell::SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplici } } - CallScriptDestinationTargetSelectHandlers(dest, effIndex); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); m_targets.SetDst(dest); } @@ -1352,7 +1369,7 @@ void Spell::SelectImplicitDestDestTargets(SpellEffIndex effIndex, SpellImplicitT } } - CallScriptDestinationTargetSelectHandlers(dest, effIndex); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); m_targets.ModDst(dest); } @@ -1395,7 +1412,7 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli break; } - CallScriptObjectTargetSelectHandlers(target, effIndex); + CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); if (target && target->ToUnit()) AddUnitTarget(target->ToUnit(), 1 << effIndex, checkIfValid); @@ -1407,7 +1424,7 @@ void Spell::SelectImplicitTargetObjectTargets(SpellEffIndex effIndex, SpellImpli WorldObject* target = m_targets.GetObjectTarget(); - CallScriptObjectTargetSelectHandlers(target, effIndex); + CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); if (target) { @@ -1442,7 +1459,7 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg , m_spellInfo->Effects[effIndex].ImplicitTargetConditions, targetType.GetTarget() == TARGET_UNIT_TARGET_CHAINHEAL_ALLY); // Chain primary target is added earlier - CallScriptObjectAreaTargetSelectHandlers(targets, effIndex); + CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr) if (Unit* unitTarget = (*itr)->ToUnit()) @@ -1596,7 +1613,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex) SpellDestination dest(*m_targets.GetDst()); dest.Relocate(trajDst); - CallScriptDestinationTargetSelectHandlers(dest, effIndex); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, SpellImplicitTargetInfo(TARGET_DEST_TRAJ)); m_targets.ModDst(dest); } } @@ -1613,7 +1630,7 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) { WorldObject* target = ObjectAccessor::FindPlayer(m_caster->GetTarget()); - CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex)); + CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo()); if (target && target->ToPlayer()) AddUnitTarget(target->ToUnit(), 1 << effIndex, false); @@ -1673,7 +1690,7 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) break; } - CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex)); + CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo()); if (target) { @@ -5120,6 +5137,7 @@ SpellCastResult Spell::CheckCast(bool strict) case SUMMON_CATEGORY_PET: if (m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; + // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: if (m_caster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; @@ -6967,42 +6985,42 @@ void Spell::CallScriptAfterHitHandlers() } } -void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex) +void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_OBJECT_AREA_TARGET_SELECT); std::list<SpellScript::ObjectAreaTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnObjectAreaTargetSelect.end(), hookItr = (*scritr)->OnObjectAreaTargetSelect.begin(); for (; hookItr != hookItrEnd; ++hookItr) - if ((*hookItr).IsEffectAffected(m_spellInfo, effIndex)) - (*hookItr).Call(*scritr, targets); + if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget()) + hookItr->Call(*scritr, targets); (*scritr)->_FinishScriptCall(); } } -void Spell::CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex) +void Spell::CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_OBJECT_TARGET_SELECT); std::list<SpellScript::ObjectTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnObjectTargetSelect.end(), hookItr = (*scritr)->OnObjectTargetSelect.begin(); for (; hookItr != hookItrEnd; ++hookItr) - if ((*hookItr).IsEffectAffected(m_spellInfo, effIndex)) - (*hookItr).Call(*scritr, target); + if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget()) + hookItr->Call(*scritr, target); (*scritr)->_FinishScriptCall(); } } -void Spell::CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex) +void Spell::CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_DESTINATION_TARGET_SELECT); std::list<SpellScript::DestinationTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnDestinationTargetSelect.end(), hookItr = (*scritr)->OnDestinationTargetSelect.begin(); for (; hookItr != hookItrEnd; ++hookItr) - if (hookItr->IsEffectAffected(m_spellInfo, effIndex)) + if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget()) hookItr->Call(*scritr, target); (*scritr)->_FinishScriptCall(); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 031311f2749..e87e2c2085a 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -639,9 +639,9 @@ class Spell void CallScriptBeforeHitHandlers(); void CallScriptOnHitHandlers(); void CallScriptAfterHitHandlers(); - void CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex); - void CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex); - void CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex); + void CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); + void CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); + void CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); bool CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToCheck); std::list<SpellScript*> m_loadedScripts; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 73d3c39148a..609c9ba05be 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1829,7 +1829,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) sSpellMgr->GetSetOfSpellsInSpellGroup(SPELL_GROUP_ELIXIR_BATTLE, avalibleElixirs); for (std::set<uint32>::iterator itr = avalibleElixirs.begin(); itr != avalibleElixirs.end();) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(*itr); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(*itr); if (spellInfo->SpellLevel < m_spellInfo->SpellLevel || spellInfo->SpellLevel > unitTarget->getLevel()) avalibleElixirs.erase(itr++); else if (sSpellMgr->IsSpellMemberOfSpellGroup(*itr, SPELL_GROUP_ELIXIR_SHATTRATH)) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 93f264d2d61..009b9861eb6 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3384,6 +3384,15 @@ void SpellMgr::LoadSpellInfoCorrections() case 71169: // Shadow's Fate spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS; break; + case 72347: // Lock Players and Tap Chest + spellInfo->AttributesEx3 &= ~SPELL_ATTR3_NO_INITIAL_AGGRO; + break; + case 73843: // Award Reputation - Boss Kill + case 73844: // Award Reputation - Boss Kill + case 73845: // Award Reputation - Boss Kill + case 73846: // Award Reputation - Boss Kill + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_50000_YARDS); // 50000yd + break; case 72378: // Blood Nova (Deathbringer Saurfang) case 73058: // Blood Nova (Deathbringer Saurfang) spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_200_YARDS); diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 01fe7708db2..76d59bd1e85 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -690,6 +690,14 @@ class SpellMgr // SpellInfo object management SpellInfo const* GetSpellInfo(uint32 spellId) const { return spellId < GetSpellInfoStoreSize() ? mSpellInfoMap[spellId] : NULL; } + // Use this only with 100% valid spellIds + SpellInfo const* EnsureSpellInfo(uint32 spellId) const + { + ASSERT(spellId < GetSpellInfoStoreSize()); + SpellInfo const* spellInfo = mSpellInfoMap[spellId]; + ASSERT(spellInfo); + return spellInfo; + } uint32 GetSpellInfoStoreSize() const { return mSpellInfoMap.size(); } private: diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 6378a8bed9b..75a191a9801 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -208,6 +208,7 @@ class SpellScript : public _SpellScript TargetHook(uint8 _effectIndex, uint16 _targetType, bool _area, bool _dest); bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex); std::string ToString(); + uint16 GetTarget() const { return targetType; } protected: uint16 targetType; bool area; diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index 43c9ba80e24..b9ecfffb8c3 100644 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -36,7 +36,9 @@ GmTicket::GmTicket() : _id(0), _playerGuid(0), _posX(0), _posY(0), _posZ(0), _ma _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), _needResponse(false), _needMoreHelp(false) { } -GmTicket::GmTicket(Player* player) : _createTime(time(NULL)), _lastModifiedTime(time(NULL)), _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), _needResponse(false), _needMoreHelp(false) +GmTicket::GmTicket(Player* player) : _posX(0), _posY(0), _posZ(0), _mapId(0), _createTime(time(NULL)), _lastModifiedTime(time(NULL)), + _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), + _needResponse(false), _needMoreHelp(false) { _id = sTicketMgr->GenerateTicketId(); _playerName = player->GetName(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index d0c21168c80..cae2f5e511d 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -1018,7 +1018,6 @@ public: int32 area = GetAreaFlagByAreaID(atoi((char*)args)); int32 offset = area / 32; - uint32 val = uint32((1 << (area % 32))); if (area<0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) { @@ -1027,6 +1026,7 @@ public: return false; } + uint32 val = uint32((1 << (area % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields | val))); @@ -1049,7 +1049,6 @@ public: int32 area = GetAreaFlagByAreaID(atoi((char*)args)); int32 offset = area / 32; - uint32 val = uint32((1 << (area % 32))); if (area < 0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) { @@ -1058,6 +1057,7 @@ public: return false; } + uint32 val = uint32((1 << (area % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields ^ val))); diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp index ab135a2fd78..0b031f54ea0 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp @@ -50,7 +50,12 @@ public: struct npc_water_elementalAI : public ScriptedAI { - npc_water_elementalAI(Creature* creature) : ScriptedAI(creature) { } + npc_water_elementalAI(Creature* creature) : ScriptedAI(creature) + { + waterBoltTimer = 3 * IN_MILLISECONDS; + resetTimer = 5 * IN_MILLISECONDS; + balindaGUID = 0; + } uint32 waterBoltTimer; uint64 balindaGUID; diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp index 8989a8065dc..0cb96a519e7 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp @@ -164,7 +164,12 @@ public: struct boss_rend_blackhandAI : public BossAI { - boss_rend_blackhandAI(Creature* creature) : BossAI(creature, DATA_WARCHIEF_REND_BLACKHAND) { } + boss_rend_blackhandAI(Creature* creature) : BossAI(creature, DATA_WARCHIEF_REND_BLACKHAND) + { + gythEvent = false; + victorGUID = 0; + portcullisGUID = 0; + } void Reset() OVERRIDE { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp index 39d2a6d87d5..560f2e2e995 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp @@ -56,6 +56,28 @@ public: { instance_blackwing_lair_InstanceMapScript(Map* map) : InstanceScript(map) { + // Razorgore + EggCount = 0; + EggEvent = 0; + RazorgoreTheUntamedGUID = 0; + RazorgoreDoorGUID = 0; + // Vaelastrasz the Corrupt + VaelastraszTheCorruptGUID = 0; + VaelastraszDoorGUID = 0; + // Broodlord Lashlayer + BroodlordLashlayerGUID = 0; + BroodlordDoorGUID = 0; + // 3 Dragons + FiremawGUID = 0; + EbonrocGUID = 0; + FlamegorGUID = 0; + ChrommagusDoorGUID = 0; + // Chormaggus + ChromaggusGUID = 0; + NefarianDoorGUID = 0; + // Nefarian + LordVictorNefariusGUID = 0; + NefarianGUID = 0; SetBossNumber(EncounterCount); } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp index 241ac85faa2..b5c3bdd86d4 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp @@ -584,10 +584,13 @@ public: arca->MonsterYell(SAY_DIALOG_ARCANAGOS_8, LANG_UNIVERSAL, NULL); return 5000; case 12: - arca->GetMotionMaster()->MovePoint(0, -11010.82f, -1761.18f, 156.47f); - arca->setActive(true); - arca->InterruptNonMeleeSpells(true); - arca->SetSpeed(MOVE_FLIGHT, 2.0f); + if (arca) + { + arca->GetMotionMaster()->MovePoint(0, -11010.82f, -1761.18f, 156.47f); + arca->setActive(true); + arca->InterruptNonMeleeSpells(true); + arca->SetSpeed(MOVE_FLIGHT, 2.0f); + } return 10000; case 13: me->MonsterYell(SAY_DIALOG_MEDIVH_9, LANG_UNIVERSAL, NULL); diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index 31219e18121..ad87b2d8d3d 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -787,7 +787,11 @@ public: struct npc_pulsing_pumpkinAI : public ScriptedAI { - npc_pulsing_pumpkinAI(Creature* creature) : ScriptedAI(creature) { } + npc_pulsing_pumpkinAI(Creature* creature) : ScriptedAI(creature) + { + sprouted = false; + debuffGUID = 0; + } bool sprouted; uint64 debuffGUID; diff --git a/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp b/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp index d8518085b26..9ceaf8cb85b 100644 --- a/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp +++ b/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp @@ -310,6 +310,15 @@ class instance_stratholme : public InstanceMapScript { HandleGameObject(ziggurat4GUID, false); HandleGameObject(ziggurat5GUID, false); + } + if (data == DONE || data == NOT_STARTED) + { + HandleGameObject(ziggurat4GUID, true); + HandleGameObject(ziggurat5GUID, true); + } + if (data == DONE) + { + HandleGameObject(portGauntletGUID, true); if (GetData(TYPE_BARON_RUN) == IN_PROGRESS) { DoRemoveAurasDueToSpellOnPlayers(SPELL_BARON_ULTIMATUM); @@ -318,18 +327,13 @@ class instance_stratholme : public InstanceMapScript for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (Player* player = itr->GetSource()) if (player->GetQuestStatus(QUEST_DEAD_MAN_PLEA) == QUEST_STATUS_INCOMPLETE) + { player->AreaExploredOrEventHappens(QUEST_DEAD_MAN_PLEA); - + player->KilledMonsterCredit(NPC_YSIDA); + } SetData(TYPE_BARON_RUN, DONE); } } - if (data == DONE || data == NOT_STARTED) - { - HandleGameObject(ziggurat4GUID, true); - HandleGameObject(ziggurat5GUID, true); - } - if (data == DONE) - HandleGameObject(portGauntletGUID, true); EncounterState[5] = data; break; case TYPE_SH_AELMAR: diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp index 4e354b47be5..0c8eb663c9a 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp @@ -16,35 +16,11 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: boss_Halazzi -SD%Complete: 80 -SDComment: -SDCategory: Zul'Aman -EndScriptData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "zulaman.h" #include "SpellInfo.h" -#define YELL_AGGRO "Get on your knees and bow to da fang and claw!" -#define SOUND_AGGRO 12020 -#define YELL_SABER_ONE "You gonna leave in pieces!" -#define YELL_SABER_TWO "Me gonna carve ya now!" -#define YELL_SPLIT "Me gonna carve ya now!" -#define SOUND_SPLIT 12021 -#define YELL_MERGE "Spirit, come back to me!" -#define SOUND_MERGE 12022 -#define YELL_KILL_ONE "You cant fight the power!" -#define SOUND_KILL_ONE 12026 -#define YELL_KILL_TWO "You gonna fail!" -#define SOUND_KILL_TWO 12027 -#define YELL_DEATH "Chaga... choka'jinn." -#define SOUND_DEATH 12028 -#define YELL_BERSERK "Whatch you be doing? Pissin' yourselves..." -#define SOUND_BERSERK 12025 - enum Spells { SPELL_DUAL_WIELD = 29651, @@ -78,23 +54,32 @@ enum PhaseHalazzi PHASE_ENRAGE = 5 }; +enum Yells +{ + SAY_AGGRO = 0, + SAY_SABER = 1, + SAY_SPLIT = 2, + SAY_MERGE = 3, + SAY_KILL = 4, + SAY_DEATH = 5, + SAY_BERSERK = 6 +}; + class boss_halazzi : public CreatureScript { public: - - boss_halazzi() - : CreatureScript("boss_halazzi") - { - } + boss_halazzi() : CreatureScript("boss_halazzi") { } struct boss_halazziAI : public ScriptedAI { - boss_halazziAI(Creature* creature) : ScriptedAI(creature) + boss_halazziAI(Creature* creature) : ScriptedAI(creature), summons(me) { instance = creature->GetInstanceScript(); } InstanceScript* instance; + SummonList summons; + PhaseHalazzi Phase; uint32 FrenzyTimer; uint32 SaberlashTimer; @@ -102,16 +87,14 @@ class boss_halazzi : public CreatureScript uint32 TotemTimer; uint32 CheckTimer; uint32 BerserkTimer; - uint32 TransformCount; - PhaseHalazzi Phase; - uint64 LynxGUID; void Reset() OVERRIDE { instance->SetData(DATA_HALAZZIEVENT, NOT_STARTED); + summons.DespawnAll(); LynxGUID = 0; TransformCount = 0; @@ -127,10 +110,7 @@ class boss_halazzi : public CreatureScript void EnterCombat(Unit* /*who*/) OVERRIDE { instance->SetData(DATA_HALAZZIEVENT, IN_PROGRESS); - - me->MonsterYell(YELL_AGGRO, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_AGGRO); - + Talk(SAY_AGGRO); EnterPhase(PHASE_LYNX); } @@ -139,6 +119,7 @@ class boss_halazzi : public CreatureScript summon->AI()->AttackStart(me->GetVictim()); if (summon->GetEntry() == NPC_SPIRIT_LYNX) LynxGUID = summon->GetGUID(); + summons.Summon(summon); } void DamageTaken(Unit* /*done_by*/, uint32 &damage) OVERRIDE @@ -155,7 +136,8 @@ class boss_halazzi : public CreatureScript void AttackStart(Unit* who) OVERRIDE { - if (Phase != PHASE_MERGE) ScriptedAI::AttackStart(who); + if (Phase != PHASE_MERGE) + ScriptedAI::AttackStart(who); } void EnterPhase(PhaseHalazzi NextPhase) @@ -180,8 +162,7 @@ class boss_halazzi : public CreatureScript TotemTimer = 12000; break; case PHASE_SPLIT: - me->MonsterYell(YELL_SPLIT, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_SPLIT); + Talk(SAY_SPLIT); DoCast(me, SPELL_TRANSFORM_SPLIT, true); break; case PHASE_HUMAN: @@ -195,8 +176,7 @@ class boss_halazzi : public CreatureScript case PHASE_MERGE: if (Unit* pLynx = Unit::GetUnit(*me, LynxGUID)) { - me->MonsterYell(YELL_MERGE, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_MERGE); + Talk(SAY_MERGE); pLynx->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); pLynx->GetMotionMaster()->Clear(); pLynx->GetMotionMaster()->MoveFollow(me, 0, 0); @@ -211,15 +191,14 @@ class boss_halazzi : public CreatureScript Phase = NextPhase; } - void UpdateAI(uint32 diff) OVERRIDE + void UpdateAI(uint32 diff) OVERRIDE { if (!UpdateVictim()) return; if (BerserkTimer <= diff) { - me->MonsterYell(YELL_BERSERK, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_BERSERK); + Talk(SAY_BERSERK); DoCast(me, SPELL_BERSERK, true); BerserkTimer = 60000; } else BerserkTimer -= diff; @@ -313,28 +292,18 @@ class boss_halazzi : public CreatureScript DoMeleeAttackIfReady(); } - void KilledUnit(Unit* /*victim*/) OVERRIDE + void KilledUnit(Unit* victim) OVERRIDE { - switch (urand(0, 1)) - { - case 0: - me->MonsterYell(YELL_KILL_ONE, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_KILL_ONE); - break; - - case 1: - me->MonsterYell(YELL_KILL_TWO, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_KILL_TWO); - break; - } + if (victim->GetTypeId() != TYPEID_PLAYER) + return; + + Talk(SAY_KILL); } void JustDied(Unit* /*killer*/) OVERRIDE { instance->SetData(DATA_HALAZZIEVENT, DONE); - - me->MonsterYell(YELL_DEATH, LANG_UNIVERSAL, NULL); - DoPlaySoundToSet(me, SOUND_DEATH); + Talk(SAY_DEATH); } }; @@ -348,11 +317,7 @@ class boss_halazzi : public CreatureScript class npc_halazzi_lynx : public CreatureScript { public: - - npc_halazzi_lynx() - : CreatureScript("npc_halazzi_lynx") - { - } + npc_halazzi_lynx() : CreatureScript("npc_halazzi_lynx") { } struct npc_halazzi_lynxAI : public ScriptedAI { @@ -414,5 +379,3 @@ void AddSC_boss_halazzi() new boss_halazzi(); new npc_halazzi_lynx(); } - - diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp index 336599f2cc5..5d4bafb5f94 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp @@ -152,9 +152,11 @@ class boss_jeklik : public CreatureScript case EVENT_SONIC_BURST: DoCastVictim(SPELL_SONICBURST); events.ScheduleEvent(EVENT_SONIC_BURST, urand(8000, 13000), 0, PHASE_ONE); + break; case EVENT_SCREECH: DoCastVictim(SPELL_SCREECH); events.ScheduleEvent(EVENT_SCREECH, urand(18000, 26000), 0, PHASE_ONE); + break; case EVENT_SPAWN_BATS: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) for (uint8 i = 0; i < 6; ++i) diff --git a/src/server/scripts/EasternKingdoms/zone_blasted_lands.cpp b/src/server/scripts/EasternKingdoms/zone_blasted_lands.cpp index 3fcd3c093b7..bc09983fee1 100644 --- a/src/server/scripts/EasternKingdoms/zone_blasted_lands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_blasted_lands.cpp @@ -17,11 +17,7 @@ /* Blasted_Lands -Quest support: 3628. Teleporter to Rise of the Defiler. -*/ - -/* -npc_deathly_usher +Quest support: 3628. */ #include "ScriptMgr.h" @@ -31,10 +27,6 @@ npc_deathly_usher #include "Player.h" #include "Group.h" -/*###### -## npc_deathly_usher -######*/ - enum DeathlyUsher { SPELL_TELEPORT_SINGLE = 12885, @@ -42,28 +34,6 @@ enum DeathlyUsher SPELL_TELEPORT_GROUP = 27686 }; -class npc_deathly_usher : public CreatureScript -{ -public: - npc_deathly_usher() : CreatureScript("npc_deathly_usher") { } - - struct npc_deathly_usherAI : public ScriptedAI - { - npc_deathly_usherAI(Creature* creature) : ScriptedAI(creature) { } - - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) OVERRIDE - { - player->CLOSE_GOSSIP_MENU(); - me->CastSpell(player, SPELL_TELEPORT_GROUP, true); - } - }; - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_deathly_usherAI(creature); - } -}; - /*##### # spell_razelikh_teleport_group #####*/ @@ -113,6 +83,5 @@ class spell_razelikh_teleport_group : public SpellScriptLoader void AddSC_blasted_lands() { - new npc_deathly_usher(); new spell_razelikh_teleport_group(); } diff --git a/src/server/scripts/EasternKingdoms/zone_westfall.cpp b/src/server/scripts/EasternKingdoms/zone_westfall.cpp index 350ceead706..d01806028c6 100644 --- a/src/server/scripts/EasternKingdoms/zone_westfall.cpp +++ b/src/server/scripts/EasternKingdoms/zone_westfall.cpp @@ -19,13 +19,12 @@ /* ScriptData SDName: Westfall SD%Complete: 90 -SDComment: Quest support: 155, 1651 +SDComment: Quest support: 1651 SDCategory: Westfall EndScriptData */ /* ContentData npc_daphne_stilwell -npc_defias_traitor EndContentData */ #include "ScriptMgr.h" @@ -202,79 +201,7 @@ public: }; }; -/*###### -## npc_defias_traitor -######*/ -enum DefiasSays -{ - SAY_START = 0, - SAY_PROGRESS = 1, - SAY_END = 2, - SAY_AGGRO = 3 -}; - - -#define QUEST_DEFIAS_BROTHERHOOD 155 - -class npc_defias_traitor : public CreatureScript -{ -public: - npc_defias_traitor() : CreatureScript("npc_defias_traitor") { } - - bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) OVERRIDE - { - if (quest->GetQuestId() == QUEST_DEFIAS_BROTHERHOOD) - { - if (npc_escortAI* pEscortAI = CAST_AI(npc_defias_traitor::npc_defias_traitorAI, creature->AI())) - pEscortAI->Start(true, true, player->GetGUID()); - - creature->AI()->Talk(SAY_START, player); - } - - return true; - } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_defias_traitorAI(creature); - } - - struct npc_defias_traitorAI : public npc_escortAI - { - npc_defias_traitorAI(Creature* creature) : npc_escortAI(creature) { Reset(); } - - void WaypointReached(uint32 waypointId) OVERRIDE - { - Player* player = GetPlayerForEscort(); - if (!player) - return; - - switch (waypointId) - { - case 35: - SetRun(false); - break; - case 36: - Talk(SAY_PROGRESS, player); - break; - case 44: - Talk(SAY_END, player); - player->GroupEventHappens(QUEST_DEFIAS_BROTHERHOOD, me); - break; - } - } - - void EnterCombat(Unit* who) OVERRIDE - { - Talk(SAY_AGGRO, who); - } - - void Reset() OVERRIDE { } - }; -}; - void AddSC_westfall() { new npc_daphne_stilwell(); - new npc_defias_traitor(); } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp index c7803f23e1b..e5ddcd1c2ef 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp @@ -185,6 +185,8 @@ public: { npc_towering_infernalAI(Creature* creature) : ScriptedAI(creature) { + ImmolationTimer = 5000; + CheckTimer = 5000; instance = creature->GetInstanceScript(); AnetheronGUID = instance->GetData64(DATA_ANETHERON); } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp index 4decce7482f..16002b59f1a 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp @@ -191,6 +191,9 @@ public: { npc_lesser_doomguardAI(Creature* creature) : hyjal_trashAI(creature) { + CrippleTimer = 50000; + WarstompTimer = 10000; + CheckTimer = 5000; instance = creature->GetInstanceScript(); AzgalorGUID = instance->GetData64(DATA_AZGALOR); } diff --git a/src/server/scripts/Kalimdor/RazorfenDowns/boss_glutton.cpp b/src/server/scripts/Kalimdor/RazorfenDowns/boss_glutton.cpp index 830c3fed382..1e0d481249d 100644 --- a/src/server/scripts/Kalimdor/RazorfenDowns/boss_glutton.cpp +++ b/src/server/scripts/Kalimdor/RazorfenDowns/boss_glutton.cpp @@ -42,6 +42,7 @@ public: { boss_gluttonAI(Creature* creature) : BossAI(creature, DATA_GLUTTON) { + hp50 = false; hp15 = false; } diff --git a/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp b/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp index b9605794074..f1d8e1b67e8 100644 --- a/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp +++ b/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp @@ -158,6 +158,8 @@ public: { instance = creature->GetInstanceScript(); eventInProgress = false; + channeling = false; + eventProgress = 0; spawnerCount = 0; } diff --git a/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp b/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp index f04b71d1da9..1dc37e063ba 100644 --- a/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp +++ b/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp @@ -171,7 +171,11 @@ public: struct npc_snufflenose_gopherAI : public PetAI { - npc_snufflenose_gopherAI(Creature* creature) : PetAI(creature) { } + npc_snufflenose_gopherAI(Creature* creature) : PetAI(creature) + { + IsMovementActive = false; + TargetTubberGUID = 0; + } void Reset() OVERRIDE { diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp index 9e3244cccdb..988436066b6 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp @@ -898,6 +898,9 @@ public: { eye_tentacleAI(Creature* creature) : ScriptedAI(creature) { + MindflayTimer = 500; + KillSelfTimer = 35000; + Portal = 0; if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) { @@ -974,6 +977,10 @@ public: { claw_tentacleAI(Creature* creature) : ScriptedAI(creature) { + GroundRuptureTimer = 500; + HamstringTimer = 2000; + EvadeTimer = 5000; + SetCombatMovement(false); Portal = 0; @@ -1085,6 +1092,11 @@ public: { giant_claw_tentacleAI(Creature* creature) : ScriptedAI(creature) { + GroundRuptureTimer = 500; + HamstringTimer = 2000; + ThrashTimer = 5000; + EvadeTimer = 5000; + SetCombatMovement(false); Portal = 0; @@ -1205,6 +1217,8 @@ public: { giant_eye_tentacleAI(Creature* creature) : ScriptedAI(creature) { + BeamTimer = 500; + SetCombatMovement(false); Portal = 0; diff --git a/src/server/scripts/Kalimdor/zone_mulgore.cpp b/src/server/scripts/Kalimdor/zone_mulgore.cpp index f70460ec7b6..f8cc21da209 100644 --- a/src/server/scripts/Kalimdor/zone_mulgore.cpp +++ b/src/server/scripts/Kalimdor/zone_mulgore.cpp @@ -24,7 +24,6 @@ SDCategory: Mulgore EndScriptData */ /* ContentData -npc_skorn_whitecloud npc_kyle_frenzied npc_plains_vision EndContentData */ @@ -35,41 +34,6 @@ EndContentData */ #include "Player.h" #include "SpellInfo.h" -/*###### -# npc_skorn_whitecloud -######*/ - -#define GOSSIP_SW "Tell me a story, Skorn." - -class npc_skorn_whitecloud : public CreatureScript -{ -public: - npc_skorn_whitecloud() : CreatureScript("npc_skorn_whitecloud") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) OVERRIDE - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF) - player->SEND_GOSSIP_MENU(523, creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) OVERRIDE - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (!player->GetQuestRewardStatus(770)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SW, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - - player->SEND_GOSSIP_MENU(522, creature->GetGUID()); - - return true; - } - -}; - /*##### # npc_kyle_frenzied ######*/ @@ -321,7 +285,6 @@ public: void AddSC_mulgore() { - new npc_skorn_whitecloud(); new npc_kyle_frenzied(); new npc_plains_vision(); } diff --git a/src/server/scripts/Kalimdor/zone_silithus.cpp b/src/server/scripts/Kalimdor/zone_silithus.cpp index b002bbe8a48..24d557222eb 100644 --- a/src/server/scripts/Kalimdor/zone_silithus.cpp +++ b/src/server/scripts/Kalimdor/zone_silithus.cpp @@ -649,11 +649,11 @@ public: Unit* mob = NULL; for (uint8 i = 0; i < 4; ++i) { - mob = player->FindNearestCreature(entries[i], 50, me); + mob = player->FindNearestCreature(entries[i], 50); while (mob) { mob->RemoveFromWorld(); - mob = player->FindNearestCreature(15423, 50, me); + mob = player->FindNearestCreature(15423, 50); } } break; @@ -1021,7 +1021,7 @@ public: { if (quest->GetQuestId() == QUEST_A_PAWN_ON_THE_ETERNAL_BOARD) { - if (Creature* trigger = go->FindNearestCreature(15454, 100, player)) + if (Creature* trigger = go->FindNearestCreature(15454, 100)) { Unit* Merithra = trigger->SummonCreature(15378, -8034.535f, 1535.14f, 2.61f, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); Unit* Caelestrasz = trigger->SummonCreature(15379, -8032.767f, 1533.148f, 2.61f, 1.5f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); diff --git a/src/server/scripts/Kalimdor/zone_tanaris.cpp b/src/server/scripts/Kalimdor/zone_tanaris.cpp index 204a2960611..3ced3c7115d 100644 --- a/src/server/scripts/Kalimdor/zone_tanaris.cpp +++ b/src/server/scripts/Kalimdor/zone_tanaris.cpp @@ -19,14 +19,13 @@ /* ScriptData SDName: Tanaris SD%Complete: 80 -SDComment: Quest support: 648, 1560, 2954, 4005, 10277, 10279(Special flight path). Noggenfogger vendor +SDComment: Quest support: 648, 1560, 2954, 4005, 10277, 10279(Special flight path). SDCategory: Tanaris EndScriptData */ /* ContentData npc_aquementas npc_custodian_of_time -npc_marin_noggenfogger npc_steward_of_time npc_stone_watcher_of_norgannon npc_OOX17 @@ -282,39 +281,6 @@ public: }; /*###### -## npc_marin_noggenfogger -######*/ - -class npc_marin_noggenfogger : public CreatureScript -{ -public: - npc_marin_noggenfogger() : CreatureScript("npc_marin_noggenfogger") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) OVERRIDE - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) OVERRIDE - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (creature->IsVendor() && player->GetQuestRewardStatus(2662)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } - -}; - -/*###### ## npc_steward_of_time ######*/ @@ -681,7 +647,6 @@ void AddSC_tanaris() { new npc_aquementas(); new npc_custodian_of_time(); - new npc_marin_noggenfogger(); new npc_steward_of_time(); new npc_stone_watcher_of_norgannon(); new npc_OOX17(); diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp index efe439de440..31c565e5be2 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp @@ -88,6 +88,9 @@ class boss_prince_taldaram : public CreatureScript boss_prince_taldaramAI(Creature* creature) : BossAI(creature, DATA_PRINCE_TALDARAM) { me->SetDisableGravity(true); + _flameSphereTargetGUID = 0; + _embraceTargetGUID = 0; + _embraceTakenDamage = 0; } void Reset() OVERRIDE @@ -283,7 +286,10 @@ class npc_prince_taldaram_flame_sphere : public CreatureScript struct npc_prince_taldaram_flame_sphereAI : public ScriptedAI { - npc_prince_taldaram_flame_sphereAI(Creature* creature) : ScriptedAI(creature) { } + npc_prince_taldaram_flame_sphereAI(Creature* creature) : ScriptedAI(creature) + { + _flameSphereTargetGUID = 0; + } void Reset() OVERRIDE { diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index 19e6b55b5c5..1281d40fe5c 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -203,7 +203,7 @@ class npc_skittering_infector : public CreatureScript enum TrashEvents { // Anubar Skrimisher - EVENT_ANUBAR_CHARGE, + EVENT_ANUBAR_CHARGE = 1, EVENT_BACKSTAB, // Anubar Shadowcaster diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt index 42a7ee15bb6..aff3c0a9528 100644 --- a/src/server/scripts/Northrend/CMakeLists.txt +++ b/src/server/scripts/Northrend/CMakeLists.txt @@ -168,6 +168,7 @@ set(scripts_STAT_SRCS Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp + Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp Northrend/IcecrownCitadel/boss_festergut.cpp Northrend/IcecrownCitadel/boss_rotface.cpp diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp index 9ae6f811e19..a24cd4db7c5 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp @@ -73,7 +73,7 @@ enum Enums //Whelps NPC_TWILIGHT_WHELP = 30890, - NPC_SHARTHARION_TWILIGHT_WHELP = 31214, + NPC_SARTHARION_TWILIGHT_WHELP = 31214, SPELL_FADE_ARMOR = 60708, // Reduces the armor of an enemy by 1500 for 15s //flame tsunami @@ -253,7 +253,7 @@ struct dummy_dragonAI : public ScriptedAI { case NPC_TENEBRON: { - if (!instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) { for (uint32 i = 0; i < 6; ++i) me->SummonCreature(NPC_TWILIGHT_EGG, TwilightEggs[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); @@ -267,7 +267,7 @@ struct dummy_dragonAI : public ScriptedAI } case NPC_SHADRON: { - if (!instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) me->SummonCreature(NPC_ACOLYTE_OF_SHADRON, AcolyteofShadron, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 28000); else me->SummonCreature(NPC_ACOLYTE_OF_SHADRON, AcolyteofShadron2, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 28000); @@ -276,7 +276,7 @@ struct dummy_dragonAI : public ScriptedAI } case NPC_VESPERON: { - if (!instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) { if (Creature* acolyte = me->SummonCreature(NPC_ACOLYTE_OF_VESPERON, AcolyteofVesperon, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) { @@ -816,10 +816,10 @@ public: { me->RemoveAllAuras(); - if (!instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) me->SummonCreature(NPC_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); else - me->SummonCreature(NPC_SHARTHARION_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + me->SummonCreature(NPC_SARTHARION_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); me->DealDamage(me, me->GetHealth()); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp index ea166585bb3..4210ebfcad5 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -294,6 +294,8 @@ class npc_snobold_vassal : public CreatureScript { npc_snobold_vassalAI(Creature* creature) : ScriptedAI(creature) { + _targetGUID = 0; + _targetDied = false; _instance = creature->GetInstanceScript(); _instance->SetData(DATA_SNOBOLD_COUNT, INCREASE); } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index 1f4713415ac..de14b930220 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -1180,7 +1180,10 @@ enum TrashEvents struct npc_gauntlet_trash : public ScriptedAI { - npc_gauntlet_trash(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + npc_gauntlet_trash(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + { + InternalWaveId = 0; + } void Reset() OVERRIDE { diff --git a/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp b/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp index dd1d35e639e..0feec20d55f 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp @@ -219,7 +219,10 @@ public: struct npc_slad_ran_constrictorAI : public ScriptedAI { - npc_slad_ran_constrictorAI(Creature* creature) : ScriptedAI(creature) { } + npc_slad_ran_constrictorAI(Creature* creature) : ScriptedAI(creature) + { + uiGripOfSladRanTimer = 1 * IN_MILLISECONDS; + } uint32 uiGripOfSladRanTimer; @@ -247,8 +250,9 @@ public: target->CastSpell(target, SPELL_SNAKE_WRAP, true); if (TempSummon* _me = me->ToTempSummon()) - if (Creature* sladran = _me->GetSummoner()->ToCreature()) - sladran->AI()->SetGUID(target->GetGUID(), DATA_SNAKES_WHYD_IT_HAVE_TO_BE_SNAKES); + if (Unit* summoner = _me->GetSummoner()) + if (Creature* sladran = summoner->ToCreature()) + sladran->AI()->SetGUID(target->GetGUID(), DATA_SNAKES_WHYD_IT_HAVE_TO_BE_SNAKES); me->DespawnOrUnsummon(); } @@ -270,7 +274,10 @@ public: struct npc_slad_ran_viperAI : public ScriptedAI { - npc_slad_ran_viperAI(Creature* creature) : ScriptedAI(creature) { } + npc_slad_ran_viperAI(Creature* creature) : ScriptedAI(creature) + { + uiVenomousBiteTimer = 2 * IN_MILLISECONDS; + } uint32 uiVenomousBiteTimer; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp index 3d4ee279685..bbf3e8afc0f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp @@ -800,7 +800,7 @@ class spell_blood_queen_pact_of_the_darkfallen_dmg : public SpellScriptLoader // this is an additional effect to be executed void PeriodicTick(AuraEffect const* aurEff) { - SpellInfo const* damageSpell = sSpellMgr->GetSpellInfo(SPELL_PACT_OF_THE_DARKFALLEN_DAMAGE); + SpellInfo const* damageSpell = sSpellMgr->EnsureSpellInfo(SPELL_PACT_OF_THE_DARKFALLEN_DAMAGE); int32 damage = damageSpell->Effects[EFFECT_0].CalcValue(); float multiplier = 0.3375f + 0.1f * uint32(aurEff->GetTickNumber()/10); // do not convert to 0.01f - we need tick number/10 as INT (damage increases every 10 ticks) damage = int32(damage * multiplier); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 7be766c0a0e..e8048404027 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -333,6 +333,7 @@ class boss_deathbringer_saurfang : public CreatureScript void JustReachedHome() OVERRIDE { _JustReachedHome(); + Reset(); instance->SetBossState(DATA_DEATHBRINGER_SAURFANG, FAIL); instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MARK_OF_THE_FALLEN_CHAMPION); } @@ -376,7 +377,16 @@ class boss_deathbringer_saurfang : public CreatureScript void JustSummoned(Creature* summon) OVERRIDE { if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true)) + { + if (target->GetTransport()) + { + summon->DespawnOrUnsummon(1); + EnterEvadeMode(); + return; + } + summon->AI()->AttackStart(target); + } summon->CastSpell(summon, SPELL_BLOOD_LINK_BEAST, true); summon->CastSpell(summon, SPELL_RESISTANT_SKIN, true); @@ -399,6 +409,12 @@ class boss_deathbringer_saurfang : public CreatureScript void SpellHitTarget(Unit* target, SpellInfo const* spell) OVERRIDE { + if (target->GetTransport()) + { + EnterEvadeMode(); + return; + } + switch (spell->Id) { case SPELL_MARK_OF_THE_FALLEN_CHAMPION: @@ -571,6 +587,14 @@ class boss_deathbringer_saurfang : public CreatureScript } } + bool CanAIAttack(Unit const* target) const OVERRIDE + { + if (target->GetTransport()) + return false; + + return true; + } + static uint32 const FightWonValue; private: diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp new file mode 100644 index 00000000000..0a9e207db36 --- /dev/null +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -0,0 +1,2484 @@ +/* + * Copyright (C) 2008-2014 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 "CellImpl.h" +#include "CreatureTextMgr.h" +#include "GridNotifiersImpl.h" +#include "GossipDef.h" +#include "MoveSpline.h" +#include "MoveSplineInit.h" +#include "PassiveAI.h" +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "SpellAuraEffects.h" +#include "SpellScript.h" +#include "Transport.h" +#include "TransportMgr.h" +#include "Vehicle.h" +#include "icecrown_citadel.h" + +enum Texts +{ + // High Overlord Saurfang + SAY_SAURFANG_INTRO_1 = 0, + SAY_SAURFANG_INTRO_2 = 1, + SAY_SAURFANG_INTRO_3 = 2, + SAY_SAURFANG_INTRO_4 = 3, + SAY_SAURFANG_INTRO_5 = 4, + SAY_SAURFANG_INTRO_6 = 5, + SAY_SAURFANG_INTRO_A = 6, + SAY_SAURFANG_BOARD = 7, + SAY_SAURFANG_ENTER_SKYBREAKER = 8, + SAY_SAURFANG_AXETHROWERS = 9, + SAY_SAURFANG_ROCKETEERS = 10, + SAY_SAURFANG_MAGES = 11, + SAY_SAURFANG_VICTORY = 12, + SAY_SAURFANG_WIPE = 13, + + // Muradin Bronzebeard + SAY_MURADIN_INTRO_1 = 0, + SAY_MURADIN_INTRO_2 = 1, + SAY_MURADIN_INTRO_3 = 2, + SAY_MURADIN_INTRO_4 = 3, + SAY_MURADIN_INTRO_5 = 4, + SAY_MURADIN_INTRO_6 = 5, + SAY_MURADIN_INTRO_7 = 6, + SAY_MURADIN_INTRO_H = 7, + SAY_MURADIN_BOARD = 8, + SAY_MURADIN_ENTER_ORGRIMMS_HAMMER = 9, + SAY_MURADIN_RIFLEMAN = 10, + SAY_MURADIN_MORTAR = 11, + SAY_MURADIN_SORCERERS = 12, + SAY_MURADIN_VICTORY = 13, + SAY_MURADIN_WIPE = 14, + + SAY_ZAFOD_ROCKET_PACK_ACTIVE = 0, + SAY_ZAFOD_ROCKET_PACK_DISABLED = 1, + + SAY_OVERHEAT = 0 +}; + +enum Events +{ + // High Overlord Saurfang + EVENT_INTRO_H_1 = 1, + EVENT_INTRO_H_2 = 2, + EVENT_INTRO_SUMMON_SKYBREAKER = 3, + EVENT_INTRO_H_3 = 4, + EVENT_INTRO_H_4 = 5, + EVENT_INTRO_H_5 = 6, + EVENT_INTRO_H_6 = 7, + + // Muradin Bronzebeard + EVENT_INTRO_A_1 = 1, + EVENT_INTRO_A_2 = 2, + EVENT_INTRO_SUMMON_ORGRIMS_HAMMER = 3, + EVENT_INTRO_A_3 = 4, + EVENT_INTRO_A_4 = 5, + EVENT_INTRO_A_5 = 6, + EVENT_INTRO_A_6 = 7, + EVENT_INTRO_A_7 = 8, + + EVENT_KEEP_PLAYER_IN_COMBAT = 9, + EVENT_SUMMON_MAGE = 10, + EVENT_ADDS = 11, + EVENT_ADDS_BOARD_YELL = 12, + EVENT_CHECK_RIFLEMAN = 13, + EVENT_CHECK_MORTAR = 14, + EVENT_CLEAVE = 15, + + EVENT_BLADESTORM = 16, + EVENT_WOUNDING_STRIKE = 17 +}; + +enum Spells +{ + // Applied on friendly transport NPCs + SPELL_FRIENDLY_BOSS_DAMAGE_MOD = 70339, + SPELL_CHECK_FOR_PLAYERS = 70332, + SPELL_GUNSHIP_FALL_TELEPORT = 67335, + SPELL_TELEPORT_PLAYERS_ON_RESET_A = 70446, + SPELL_TELEPORT_PLAYERS_ON_RESET_H = 71284, + SPELL_TELEPORT_PLAYERS_ON_VICTORY = 72340, + SPELL_ACHIEVEMENT = 72959, + SPELL_AWARD_REPUTATION_BOSS_KILL = 73843, + + // Murading Bronzebeard + // High Overlord Saurfang + SPELL_BATTLE_FURY = 69637, + SPELL_RENDING_THROW = 70309, + SPELL_CLEAVE = 15284, + SPELL_TASTE_OF_BLOOD = 69634, + + // Applied on enemy NPCs + SPELL_MELEE_TARGETING_ON_SKYBREAKER = 70219, + SPELL_MELEE_TARGETING_ON_ORGRIMS_HAMMER = 70294, + + // Gunship Hull + SPELL_EXPLOSION_WIPE = 72134, + SPELL_EXPLOSION_VICTORY = 72137, + + // Hostile NPCs + SPELL_TELEPORT_TO_ENEMY_SHIP = 70104, + SPELL_BATTLE_EXPERIENCE = 71201, + SPELL_EXPERIENCED = 71188, + SPELL_VETERAN = 71193, + SPELL_ELITE = 71195, + SPELL_ADDS_BERSERK = 72525, + + // Skybreaker Sorcerer + // Kor'kron Battle-Mage + SPELL_SHADOW_CHANNELING = 43897, + SPELL_BELOW_ZERO = 69705, + + // Skybreaker Rifleman + // Kor'kron Axethrower + SPELL_SHOOT = 70162, + SPELL_HURL_AXE = 70161, + SPELL_BURNING_PITCH_A = 70403, + SPELL_BURNING_PITCH_H = 70397, + SPELL_BURNING_PITCH = 69660, + + // Skybreaker Mortar Soldier + // Kor'kron Rocketeer + SPELL_ROCKET_ARTILLERY_A = 70609, + SPELL_ROCKET_ARTILLERY_H = 69678, + SPELL_BURNING_PITCH_DAMAGE_A = 70383, + SPELL_BURNING_PITCH_DAMAGE_H = 70374, + + // Skybreaker Marine + // Kor'kron Reaver + SPELL_DESPERATE_RESOLVE = 69647, + + // Skybreaker Sergeant + // Kor'kron Sergeant + SPELL_BLADESTORM = 69652, + SPELL_WOUNDING_STRIKE = 69651, + + // + SPELL_LOCK_PLAYERS_AND_TAP_CHEST = 72347, + SPELL_ON_SKYBREAKER_DECK = 70120, + SPELL_ON_ORGRIMS_HAMMER_DECK = 70121, + + // Rocket Pack + SPELL_ROCKET_PACK_DAMAGE = 69193, + SPELL_ROCKET_BURST = 69192, + SPELL_ROCKET_PACK_USEABLE = 70348, + + // Alliance Gunship Cannon + // Horde Gunship Cannon + SPELL_OVERHEAT = 69487, + SPELL_EJECT_ALL_PASSENGERS_BELOW_ZERO = 68576, + SPELL_EJECT_ALL_PASSENGERS_WIPE = 50630 +}; + +enum MiscData +{ + ITEM_GOBLIN_ROCKET_PACK = 49278, + + PHASE_COMBAT = 0, + PHASE_INTRO = 1, + + MUSIC_ENCOUNTER = 17289 +}; + +enum EncounterActions +{ + ACTION_SPAWN_MAGE = 1, + ACTION_SPAWN_ALL_ADDS = 2, + ACTION_CLEAR_SLOT = 3, + ACTION_SET_SLOT = 4, + ACTION_SHIP_VISITS = 5 +}; + +Position const SkybreakerAddsSpawnPos = { 15.91131f, 0.0f, 20.4628f, M_PI }; +Position const OrgrimsHammerAddsSpawnPos = { 60.728395f, 0.0f, 38.93467f, M_PI }; + +// Horde encounter +Position const SkybreakerTeleportPortal = { 6.666975f, 0.013001f, 20.87888f, 0.0f }; +Position const OrgrimsHammerTeleportExit = { 7.461699f, 0.158853f, 35.72989f, 0.0f }; + +// Alliance encounter +Position const OrgrimsHammerTeleportPortal = { 47.550990f, -0.101778f, 37.61111f, 0.0f }; +Position const SkybreakerTeleportExit = { -17.55738f, -0.090421f, 21.18366f, 0.0f }; + +uint32 const MuradinExitPathSize = 10; +G3D::Vector3 const MuradinExitPath[MuradinExitPathSize] = +{ + { 8.130936f, -0.2699585f, 20.31728f }, + { 6.380936f, -0.2699585f, 20.31728f }, + { 3.507703f, 0.02986573f, 20.78463f }, + { -2.767633f, 3.743143f, 20.37663f }, + { -4.017633f, 4.493143f, 20.12663f }, + { -7.242224f, 6.856013f, 20.03468f }, + { -7.742224f, 8.606013f, 20.78468f }, + { -7.992224f, 9.856013f, 21.28468f }, + { -12.24222f, 23.10601f, 21.28468f }, + { -14.88477f, 25.20844f, 21.59985f }, +}; + +uint32 const SaurfangExitPathSize = 13; +G3D::Vector3 const SaurfangExitPath[SaurfangExitPathSize] = +{ + { 30.43987f, 0.1475817f, 36.10674f }, + { 21.36141f, -3.056458f, 35.42970f }, + { 19.11141f, -3.806458f, 35.42970f }, + { 19.01736f, -3.299440f, 35.39428f }, + { 18.6747f, -5.862823f, 35.66611f }, + { 18.6747f, -7.862823f, 35.66611f }, + { 18.1747f, -17.36282f, 35.66611f }, + { 18.1747f, -22.61282f, 35.66611f }, + { 17.9247f, -24.36282f, 35.41611f }, + { 17.9247f, -26.61282f, 35.66611f }, + { 17.9247f, -27.86282f, 35.66611f }, + { 17.9247f, -29.36282f, 35.66611f }, + { 15.33203f, -30.42621f, 35.93796f } +}; + +enum PassengerSlots +{ + // Freezing the cannons + SLOT_FREEZE_MAGE = 0, + + // Channeling the portal, refilled with adds that board player's ship + SLOT_MAGE_1 = 1, + SLOT_MAGE_2 = 2, + + // Rifleman + SLOT_RIFLEMAN_1 = 3, + SLOT_RIFLEMAN_2 = 4, + SLOT_RIFLEMAN_3 = 5, + SLOT_RIFLEMAN_4 = 6, + + // Additional Rifleman on 25 man + SLOT_RIFLEMAN_5 = 7, + SLOT_RIFLEMAN_6 = 8, + SLOT_RIFLEMAN_7 = 9, + SLOT_RIFLEMAN_8 = 10, + + // Mortar + SLOT_MORTAR_1 = 11, + SLOT_MORTAR_2 = 12, + + // Additional spawns on 25 man + SLOT_MORTAR_3 = 13, + SLOT_MORTAR_4 = 14, + + // Marines + SLOT_MARINE_1 = 15, + SLOT_MARINE_2 = 16, + + // Additional spawns on 25 man + SLOT_MARINE_3 = 17, + SLOT_MARINE_4 = 18, + + // Sergeants + SLOT_SERGEANT_1 = 19, + + // Additional spawns on 25 man + SLOT_SERGEANT_2 = 20, + + MAX_SLOTS +}; + +struct SlotInfo +{ + uint32 Entry; + Position TargetPosition; + uint32 Cooldown; +}; + +SlotInfo const SkybreakerSlotInfo[MAX_SLOTS] = +{ + { NPC_SKYBREAKER_SORCERER, { -9.479858f, 0.05663967f, 20.77026f, 4.729842f }, 0 }, + + { NPC_SKYBREAKER_SORCERER, { 6.385986f, 4.978760f, 20.55417f, 4.694936f }, 0 }, + { NPC_SKYBREAKER_SORCERER, { 6.579102f, -4.674561f, 20.55060f, 1.553343f }, 0 }, + + { NPC_SKYBREAKER_RIFLEMAN, { -29.563900f, -17.95801f, 20.73837f, 4.747295f }, 30 }, + { NPC_SKYBREAKER_RIFLEMAN, { -18.017210f, -18.82056f, 20.79150f, 4.747295f }, 30 }, + { NPC_SKYBREAKER_RIFLEMAN, { -9.1193850f, -18.79102f, 20.58887f, 4.712389f }, 30 }, + { NPC_SKYBREAKER_RIFLEMAN, { -0.3364258f, -18.87183f, 20.56824f, 4.712389f }, 30 }, + + { NPC_SKYBREAKER_RIFLEMAN, { -34.705810f, -17.67261f, 20.51523f, 4.729842f }, 30 }, + { NPC_SKYBREAKER_RIFLEMAN, { -23.562010f, -18.28564f, 20.67859f, 4.729842f }, 30 }, + { NPC_SKYBREAKER_RIFLEMAN, { -13.602780f, -18.74268f, 20.59622f, 4.712389f }, 30 }, + { NPC_SKYBREAKER_RIFLEMAN, { -4.3350220f, -18.84619f, 20.58234f, 4.712389f }, 30 }, + + { NPC_SKYBREAKER_MORTAR_SOLDIER, { -31.70142f, 18.02783f, 20.77197f, 4.712389f }, 30 }, + { NPC_SKYBREAKER_MORTAR_SOLDIER, { -9.368652f, 18.75806f, 20.65335f, 4.712389f }, 30 }, + + { NPC_SKYBREAKER_MORTAR_SOLDIER, { -20.40851f, 18.40381f, 20.50647f, 4.694936f }, 30 }, + { NPC_SKYBREAKER_MORTAR_SOLDIER, { 0.1585693f, 18.11523f, 20.41949f, 4.729842f }, 30 }, + + { NPC_SKYBREAKER_MARINE, SkybreakerTeleportPortal, 0 }, + { NPC_SKYBREAKER_MARINE, SkybreakerTeleportPortal, 0 }, + + { NPC_SKYBREAKER_MARINE, SkybreakerTeleportPortal, 0 }, + { NPC_SKYBREAKER_MARINE, SkybreakerTeleportPortal, 0 }, + + { NPC_SKYBREAKER_SERGEANT, SkybreakerTeleportPortal, 0 }, + + { NPC_SKYBREAKER_SERGEANT, SkybreakerTeleportPortal, 0 } +}; + +SlotInfo const OrgrimsHammerSlotInfo[MAX_SLOTS] = +{ + { NPC_KOR_KRON_BATTLE_MAGE, { 13.58548f, 0.3867192f, 34.99243f, 1.53589f }, 0 }, + + { NPC_KOR_KRON_BATTLE_MAGE, { 47.29290f, -4.308941f, 37.55550f, 1.570796f }, 0 }, + { NPC_KOR_KRON_BATTLE_MAGE, { 47.34621f, 4.032004f, 37.70952f, 4.817109f }, 0 }, + + { NPC_KOR_KRON_AXETHROWER, { -12.09280f, 27.65942f, 33.58557f, 1.53589f }, 30 }, + { NPC_KOR_KRON_AXETHROWER, { -3.170555f, 28.30652f, 34.21082f, 1.53589f }, 30 }, + { NPC_KOR_KRON_AXETHROWER, { 14.928040f, 26.18018f, 35.47803f, 1.53589f }, 30 }, + { NPC_KOR_KRON_AXETHROWER, { 24.703310f, 25.36584f, 35.97845f, 1.53589f }, 30 }, + + { NPC_KOR_KRON_AXETHROWER, { -16.65302f, 27.59668f, 33.18726f, 1.53589f }, 30 }, + { NPC_KOR_KRON_AXETHROWER, { -8.084572f, 28.21448f, 33.93805f, 1.53589f }, 30 }, + { NPC_KOR_KRON_AXETHROWER, { 7.594765f, 27.41968f, 35.00775f, 1.53589f }, 30 }, + { NPC_KOR_KRON_AXETHROWER, { 20.763390f, 25.58215f, 35.75287f, 1.53589f }, 30 }, + + { NPC_KOR_KRON_ROCKETEER, { -11.44849f, -25.71838f, 33.64343f, 1.518436f }, 30 }, + { NPC_KOR_KRON_ROCKETEER, { 12.30336f, -25.69653f, 35.32373f, 1.518436f }, 30 }, + + { NPC_KOR_KRON_ROCKETEER, { -0.05931854f, -25.46399f, 34.50592f, 1.518436f }, 30 }, + { NPC_KOR_KRON_ROCKETEER, { 27.62149000f, -23.48108f, 36.12708f, 1.518436f }, 30 }, + + { NPC_KOR_KRON_REAVER, OrgrimsHammerTeleportPortal, 0 }, + { NPC_KOR_KRON_REAVER, OrgrimsHammerTeleportPortal, 0 }, + + { NPC_KOR_KRON_REAVER, OrgrimsHammerTeleportPortal, 0 }, + { NPC_KOR_KRON_REAVER, OrgrimsHammerTeleportPortal, 0 }, + + { NPC_KOR_KRON_SERGEANT, OrgrimsHammerTeleportPortal, 0 }, + + { NPC_KOR_KRON_SERGEANT, OrgrimsHammerTeleportPortal, 0 } +}; + +class PassengerController +{ +public: + PassengerController() + { + ResetSlots(HORDE); + } + + void SetTransport(Transport* transport) { _transport = transport; } + + void ResetSlots(uint32 team) + { + _transport = NULL; + memset(_controlledSlots, 0, sizeof(uint64)* MAX_SLOTS); + memset(_respawnCooldowns, 0, sizeof(time_t)* MAX_SLOTS); + _spawnPoint = team == HORDE ? &OrgrimsHammerAddsSpawnPos : &SkybreakerAddsSpawnPos; + _slotInfo = team == HORDE ? OrgrimsHammerSlotInfo : SkybreakerSlotInfo; + } + + bool SummonCreatures(PassengerSlots first, PassengerSlots last) + { + if (!_transport) + return false; + + bool summoned = false; + time_t now = time(NULL); + for (int32 i = first; i <= last; ++i) + { + if (_respawnCooldowns[i] > now) + continue; + + if (_controlledSlots[i]) + { + Creature* current = ObjectAccessor::GetCreature(*_transport, _controlledSlots[i]); + if (current && current->IsAlive()) + continue; + } + + if (Creature* passenger = _transport->SummonPassenger(_slotInfo[i].Entry, SelectSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, NULL, 15000)) + { + _controlledSlots[i] = passenger->GetGUID(); + _respawnCooldowns[i] = time_t(0); + passenger->AI()->SetData(ACTION_SET_SLOT, i); + summoned = true; + } + } + + return summoned; + } + + void ClearSlot(PassengerSlots slot) + { + _controlledSlots[slot] = 0; + _respawnCooldowns[slot] = time(NULL) + _slotInfo[slot].Cooldown; + } + + bool SlotsNeedRefill(PassengerSlots first, PassengerSlots last) const + { + for (int32 i = first; i <= last; ++i) + if (!_controlledSlots[i]) + return true; + + return false; + } + +private: + Position SelectSpawnPoint() const + { + Position newPos; + float angle = frand(-M_PI * 0.5f, M_PI * 0.5f); + newPos.m_positionX = _spawnPoint->GetPositionX() + 2.0f * std::cos(angle); + newPos.m_positionY = _spawnPoint->GetPositionY() + 2.0f * std::sin(angle); + newPos.m_positionZ = _spawnPoint->GetPositionZ(); + newPos.SetOrientation(_spawnPoint->GetOrientation()); + return newPos; + } + + Transport* _transport; + uint64 _controlledSlots[MAX_SLOTS]; + time_t _respawnCooldowns[MAX_SLOTS]; + Position const* _spawnPoint; + SlotInfo const* _slotInfo; +}; + +class DelayedMovementEvent : public BasicEvent +{ +public: + DelayedMovementEvent(Creature* owner, Position const& dest) : _owner(owner), _dest(dest) { } + + bool Execute(uint64, uint32) OVERRIDE + { + if (!_owner->IsAlive()) + return true; + + _owner->GetMotionMaster()->MovePoint(EVENT_CHARGE_PREPATH, *_owner, false); + + Movement::MoveSplineInit init(_owner); + init.DisableTransportPathTransformations(); + init.MoveTo(_dest.GetPositionX(), _dest.GetPositionY(), _dest.GetPositionZ(), false); + init.Launch(); + + return true; + } + +private: + Creature* _owner; + Position const& _dest; +}; + +class ResetEncounterEvent : public BasicEvent +{ +public: + ResetEncounterEvent(Unit* caster, uint32 spellId, uint64 otherTransport) : _caster(caster), _spellId(spellId), _otherTransport(otherTransport) { } + + bool Execute(uint64, uint32) OVERRIDE + { + _caster->CastSpell(_caster, _spellId, true); + _caster->GetTransport()->AddObjectToRemoveList(); + + if (GameObject* go = HashMapHolder<GameObject>::Find(_otherTransport)) + go->AddObjectToRemoveList(); + + return true; + } + +private: + Unit* _caster; + uint32 _spellId; + uint64 _otherTransport; +}; + +class BattleExperienceEvent : public BasicEvent +{ +public: + static uint32 const ExperiencedSpells[5]; + static uint32 const ExperiencedTimes[5]; + + BattleExperienceEvent(Creature* creature) : _creature(creature), _level(0) { } + + bool Execute(uint64 timer, uint32 /*diff*/) OVERRIDE + { + if (!_creature->IsAlive()) + return true; + + _creature->RemoveAurasDueToSpell(ExperiencedSpells[_level]); + ++_level; + + _creature->CastSpell(_creature, ExperiencedSpells[_level], TRIGGERED_FULL_MASK); + if (_level < (_creature->GetMap()->IsHeroic() ? 4 : 3)) + { + _creature->m_Events.AddEvent(this, timer + ExperiencedTimes[_level]); + return false; + } + + return true; + } + +private: + Creature* _creature; + int32 _level; +}; + +uint32 const BattleExperienceEvent::ExperiencedSpells[5] = { 0, SPELL_EXPERIENCED, SPELL_VETERAN, SPELL_ELITE, SPELL_ADDS_BERSERK }; +uint32 const BattleExperienceEvent::ExperiencedTimes[5] = { 100000, 70000, 60000, 90000, 0 }; + +struct gunship_npc_AI : public ScriptedAI +{ + gunship_npc_AI(Creature* creature) : ScriptedAI(creature), + Instance(creature->GetInstanceScript()), Slot(NULL), Index(uint32(-1)) + { + BurningPitchId = Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_BURNING_PITCH_A : SPELL_BURNING_PITCH_H; + me->setRegeneratingHealth(false); + } + + void SetData(uint32 type, uint32 data) OVERRIDE + { + if (type == ACTION_SET_SLOT && data < MAX_SLOTS) + { + SetSlotInfo(data); + + me->SetReactState(REACT_PASSIVE); + + float x, y, z, o; + Slot->TargetPosition.GetPosition(x, y, z, o); + + me->SetTransportHomePosition(Slot->TargetPosition); + float hx = x, hy = y, hz = z, ho = o; + me->GetTransport()->CalculatePassengerPosition(hx, hy, hz, &ho); + me->SetHomePosition(hx, hy, hz, ho); + + me->GetMotionMaster()->MovePoint(EVENT_CHARGE_PREPATH, Slot->TargetPosition, false); + + Movement::MoveSplineInit init(me); + init.DisableTransportPathTransformations(); + init.MoveTo(x, y, z, false); + init.Launch(); + } + } + + void EnterEvadeMode() OVERRIDE + { + if (!me->IsAlive() || !me->IsInCombat()) + return; + + me->DeleteThreatList(); + me->CombatStop(true); + me->GetMotionMaster()->MoveTargetedHome(); + } + + void JustDied(Unit* /*killer*/) OVERRIDE + { + if (Slot) + if (Creature* captain = me->FindNearestCreature(Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? NPC_IGB_MURADIN_BRONZEBEARD : NPC_IGB_HIGH_OVERLORD_SAURFANG, 200.0f)) + captain->AI()->SetData(ACTION_CLEAR_SLOT, Index); + } + + void MovementInform(uint32 type, uint32 pointId) OVERRIDE + { + if (type != POINT_MOTION_TYPE) + return; + + if (pointId == EVENT_CHARGE_PREPATH && Slot) + { + me->SetFacingTo(Slot->TargetPosition.GetOrientation()); + me->m_Events.AddEvent(new BattleExperienceEvent(me), me->m_Events.CalculateTime(BattleExperienceEvent::ExperiencedTimes[0])); + DoCast(me, SPELL_BATTLE_EXPERIENCE, true); + me->SetReactState(REACT_AGGRESSIVE); + } + } + + bool CanAIAttack(Unit const* target) const OVERRIDE + { + if (Instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) + return false; + return target->HasAura(Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_ON_ORGRIMS_HAMMER_DECK : SPELL_ON_SKYBREAKER_DECK); + } + +protected: + void SetSlotInfo(uint32 index) + { + Index = index; + Slot = &((Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SkybreakerSlotInfo : OrgrimsHammerSlotInfo)[Index]); + } + + bool SelectVictim() + { + if (Instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) + { + EnterEvadeMode(); + return false; + } + + if (!me->HasReactState(REACT_PASSIVE)) + { + if (Unit* victim = me->SelectVictim()) + AttackStart(victim); + return me->GetVictim(); + } + else if (me->getThreatManager().isThreatListEmpty()) + { + EnterEvadeMode(); + return false; + } + + return true; + } + + void TriggerBurningPitch() + { + if (Instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS && + !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE) && + !me->HasSpellCooldown(BurningPitchId)) + { + DoCastAOE(BurningPitchId, true); + me->_AddCreatureSpellCooldown(BurningPitchId, time(NULL) + urand(3000, 4000) / IN_MILLISECONDS); + } + } + + InstanceScript* Instance; + SlotInfo const* Slot; + uint32 Index; + uint32 BurningPitchId; +}; + +class npc_gunship : public CreatureScript +{ + public: + npc_gunship() : CreatureScript("npc_gunship") { } + + struct npc_gunshipAI : public NullCreatureAI + { + npc_gunshipAI(Creature* creature) : NullCreatureAI(creature), + _teamInInstance(creature->GetInstanceScript()->GetData(DATA_TEAM_IN_INSTANCE)), + _summonedFirstMage(false), _died(false) + { + me->setRegeneratingHealth(false); + } + + void DamageTaken(Unit* /*source*/, uint32& damage) OVERRIDE + { + if (damage >= me->GetHealth()) + { + JustDied(NULL); + damage = me->GetHealth() - 1; + return; + } + + if (_summonedFirstMage) + return; + + if (me->GetTransport()->GetEntry() != uint32(_teamInInstance == HORDE ? GO_THE_SKYBREAKER_H : GO_ORGRIMS_HAMMER_A)) + return; + + if (!me->HealthBelowPctDamaged(90, damage)) + return; + + _summonedFirstMage = true; + if (Creature* captain = me->FindNearestCreature(_teamInInstance == HORDE ? NPC_IGB_MURADIN_BRONZEBEARD : NPC_IGB_HIGH_OVERLORD_SAURFANG, 100.0f)) + captain->AI()->DoAction(ACTION_SPAWN_MAGE); + } + + void JustDied(Unit* /*killer*/) OVERRIDE + { + if (_died) + return; + + _died = true; + + bool isVictory = me->GetTransport()->GetEntry() == GO_THE_SKYBREAKER_H || me->GetTransport()->GetEntry() == GO_ORGRIMS_HAMMER_A; + InstanceScript* instance = me->GetInstanceScript(); + instance->SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, isVictory ? DONE : FAIL); + if (Creature* creature = me->FindNearestCreature(me->GetEntry() == NPC_ORGRIMS_HAMMER ? NPC_THE_SKYBREAKER : NPC_ORGRIMS_HAMMER, 200.0f)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, creature); + creature->RemoveAurasDueToSpell(SPELL_CHECK_FOR_PLAYERS); + } + + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + me->RemoveAurasDueToSpell(SPELL_CHECK_FOR_PLAYERS); + + me->GetMap()->SetZoneMusic(AREA_ICECROWN_CITADEL, 0); + std::list<Creature*> creatures; + GetCreatureListWithEntryInGrid(creatures, me, NPC_MARTYR_STALKER_IGB_SAURFANG, SIZE_OF_GRIDS); + for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + { + Creature* stalker = *itr; + stalker->RemoveAllAuras(); + stalker->DeleteThreatList(); + stalker->CombatStop(true); + } + + uint32 explosionSpell = isVictory ? SPELL_EXPLOSION_VICTORY : SPELL_EXPLOSION_WIPE; + creatures.clear(); + GetCreatureListWithEntryInGrid(creatures, me, NPC_GUNSHIP_HULL, 200.0f); + for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + { + Creature* hull = *itr; + if (hull->GetTransport() != me->GetTransport()) + continue; + + hull->CastSpell(hull, explosionSpell, TRIGGERED_FULL_MASK); + } + + creatures.clear(); + GetCreatureListWithEntryInGrid(creatures, me, _teamInInstance == HORDE ? NPC_HORDE_GUNSHIP_CANNON : NPC_ALLIANCE_GUNSHIP_CANNON, 200.0f); + for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + { + Creature* cannon = *itr; + if (isVictory) + { + cannon->CastSpell(cannon, SPELL_EJECT_ALL_PASSENGERS_BELOW_ZERO, TRIGGERED_FULL_MASK); + + WorldPacket data(SMSG_PLAYER_VEHICLE_DATA, cannon->GetPackGUID().size() + 4); + data.append(cannon->GetPackGUID()); + data << uint32(0); + cannon->SendMessageToSet(&data, true); + + cannon->RemoveVehicleKit(); + } + else + cannon->CastSpell(cannon, SPELL_EJECT_ALL_PASSENGERS_WIPE, TRIGGERED_FULL_MASK); + } + + uint32 creatureEntry = NPC_IGB_MURADIN_BRONZEBEARD; + uint8 textId = isVictory ? SAY_MURADIN_VICTORY : SAY_MURADIN_WIPE; + if (_teamInInstance == HORDE) + { + creatureEntry = NPC_IGB_HIGH_OVERLORD_SAURFANG; + textId = isVictory ? SAY_SAURFANG_VICTORY : SAY_SAURFANG_WIPE; + } + + if (Creature* creature = me->FindNearestCreature(creatureEntry, 100.0f)) + creature->AI()->Talk(textId); + + if (isVictory) + { + if (GameObject* go = HashMapHolder<GameObject>::Find(instance->GetData64(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (Transport* otherTransport = go->ToTransport()) + otherTransport->EnableMovement(true); + + me->GetTransport()->EnableMovement(true); + + if (Creature* ship = me->FindNearestCreature(_teamInInstance == HORDE ? NPC_ORGRIMS_HAMMER : NPC_THE_SKYBREAKER, 200.0f)) + { + ship->CastSpell(ship, SPELL_TELEPORT_PLAYERS_ON_VICTORY, TRIGGERED_FULL_MASK); + ship->CastSpell(ship, SPELL_ACHIEVEMENT, TRIGGERED_FULL_MASK); + ship->CastSpell(ship, SPELL_AWARD_REPUTATION_BOSS_KILL, TRIGGERED_FULL_MASK); + } + + creatures.clear(); + GetCreatureListWithEntryInGrid(creatures, me, NPC_SKYBREAKER_MARINE, 200.0f); + GetCreatureListWithEntryInGrid(creatures, me, NPC_SKYBREAKER_SERGEANT, 200.0f); + GetCreatureListWithEntryInGrid(creatures, me, NPC_KOR_KRON_REAVER, 200.0f); + GetCreatureListWithEntryInGrid(creatures, me, NPC_KOR_KRON_SERGEANT, 200.0f); + for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + (*itr)->DespawnOrUnsummon(1); + } + else + { + uint32 teleportSpellId = _teamInInstance == HORDE ? SPELL_TELEPORT_PLAYERS_ON_RESET_H : SPELL_TELEPORT_PLAYERS_ON_RESET_A; + me->m_Events.AddEvent(new ResetEncounterEvent(me, teleportSpellId, me->GetInstanceScript()->GetData64(DATA_ENEMY_GUNSHIP)), + me->m_Events.CalculateTime(8000)); + } + } + + void SetGUID(uint64 guid, int32 id/* = 0*/) OVERRIDE + { + if (id != ACTION_SHIP_VISITS) + return; + + std::map<uint64, uint32>::iterator itr = _shipVisits.find(guid); + if (itr == _shipVisits.end()) + _shipVisits[guid] = 1; + else + ++itr->second; + } + + uint32 GetData(uint32 id) const OVERRIDE + { + if (id != ACTION_SHIP_VISITS) + return 0; + + uint32 max = 0; + for (std::map<uint64, uint32>::const_iterator itr = _shipVisits.begin(); itr != _shipVisits.end(); ++itr) + max = std::max(max, itr->second); + + return max; + } + + private: + uint32 _teamInInstance; + std::map<uint64, uint32> _shipVisits; + bool _summonedFirstMage; + bool _died; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + if (!creature->GetTransport()) + return NULL; + + return GetIcecrownCitadelAI<npc_gunshipAI>(creature); + } +}; + +class npc_high_overlord_saurfang_igb : public CreatureScript +{ + public: + npc_high_overlord_saurfang_igb() : CreatureScript("npc_high_overlord_saurfang_igb") { } + + struct npc_high_overlord_saurfang_igbAI : public ScriptedAI + { + npc_high_overlord_saurfang_igbAI(Creature* creature) : ScriptedAI(creature), + _instance(creature->GetInstanceScript()) + { + _controller.ResetSlots(HORDE); + _controller.SetTransport(creature->GetTransport()); + me->setRegeneratingHealth(false); + me->m_CombatDistance = 70.0f; + _firstMageCooldown = time(NULL) + 60; + _axethrowersYellCooldown = time_t(0); + _rocketeersYellCooldown = time_t(0); + } + + void InitializeAI() OVERRIDE + { + ScriptedAI::InitializeAI(); + + _events.Reset(); + _firstMageCooldown = time(NULL) + 60; + _axethrowersYellCooldown = time_t(0); + _rocketeersYellCooldown = time_t(0); + } + + void EnterCombat(Unit* /*target*/) OVERRIDE + { + _events.SetPhase(PHASE_COMBAT); + DoCast(me, _instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_FRIENDLY_BOSS_DAMAGE_MOD : SPELL_MELEE_TARGETING_ON_ORGRIMS_HAMMER, true); + DoCast(me, SPELL_BATTLE_FURY, true); + _events.ScheduleEvent(EVENT_CLEAVE, urand(2000, 10000)); + } + + void EnterEvadeMode() OVERRIDE + { + if (!me->IsAlive()) + return; + + me->DeleteThreatList(); + me->CombatStop(true); + me->GetMotionMaster()->MoveTargetedHome(); + + Reset(); + } + + void DoAction(int32 action) OVERRIDE + { + if (action == ACTION_ENEMY_GUNSHIP_TALK) + { + if (Creature* muradin = me->FindNearestCreature(NPC_IGB_MURADIN_BRONZEBEARD, 100.0f)) + muradin->AI()->DoAction(ACTION_SPAWN_ALL_ADDS); + + Talk(SAY_SAURFANG_INTRO_5); + _events.ScheduleEvent(EVENT_INTRO_H_5, 4000); + _events.ScheduleEvent(EVENT_INTRO_H_6, 11000); + _events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, 1); + + _instance->SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, IN_PROGRESS); + // Combat starts now + if (Creature* skybreaker = me->FindNearestCreature(NPC_THE_SKYBREAKER, 100.0f)) + _instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, skybreaker, 1); + + if (Creature* orgrimsHammer = me->FindNearestCreature(NPC_ORGRIMS_HAMMER, 100.0f)) + { + _instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, orgrimsHammer, 2); + orgrimsHammer->CastSpell(orgrimsHammer, SPELL_CHECK_FOR_PLAYERS, TRIGGERED_FULL_MASK); + } + + me->GetMap()->SetZoneMusic(AREA_ICECROWN_CITADEL, MUSIC_ENCOUNTER); + } + else if (action == ACTION_SPAWN_MAGE) + { + time_t now = time(NULL); + if (_firstMageCooldown > now) + _events.ScheduleEvent(EVENT_SUMMON_MAGE, (_firstMageCooldown - now) * IN_MILLISECONDS); + else + _events.ScheduleEvent(EVENT_SUMMON_MAGE, 1); + } + else if (action == ACTION_SPAWN_ALL_ADDS) + { + _events.ScheduleEvent(EVENT_ADDS, 12000); + _events.ScheduleEvent(EVENT_CHECK_RIFLEMAN, 13000); + _events.ScheduleEvent(EVENT_CHECK_MORTAR, 13000); + if (Is25ManRaid()) + _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MORTAR_4); + else + { + _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); + _controller.SummonCreatures(SLOT_MORTAR_1, SLOT_MORTAR_2); + _controller.SummonCreatures(SLOT_RIFLEMAN_1, SLOT_RIFLEMAN_4); + } + } + else if (action == ACTION_EXIT_SHIP) + { + Position pos; + pos.Relocate(SaurfangExitPath[SaurfangExitPathSize - 1].x, SaurfangExitPath[SaurfangExitPathSize - 1].y, SaurfangExitPath[SaurfangExitPathSize - 1].z); + me->GetMotionMaster()->MovePoint(EVENT_CHARGE_PREPATH, pos, false); + + Movement::PointsArray path(SaurfangExitPath, SaurfangExitPath + SaurfangExitPathSize); + + Movement::MoveSplineInit init(me); + init.DisableTransportPathTransformations(); + init.MovebyPath(path, 0); + init.Launch(); + + me->DespawnOrUnsummon(18000); + } + } + + void SetData(uint32 type, uint32 data) OVERRIDE + { + if (type == ACTION_CLEAR_SLOT) + { + _controller.ClearSlot(PassengerSlots(data)); + if (data == SLOT_FREEZE_MAGE) + _events.ScheduleEvent(EVENT_SUMMON_MAGE, urand(30000, 33500)); + } + } + + void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) OVERRIDE + { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->GetTransport()->EnableMovement(true); + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_H_1, 5000, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_H_2, 16000, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_SUMMON_SKYBREAKER, 24600, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_H_3, 29600, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_H_4, 39200, 0, PHASE_INTRO); + } + + void DamageTaken(Unit* , uint32& damage) OVERRIDE + { + if (me->HealthBelowPctDamaged(65, damage) && !me->HasAura(SPELL_TASTE_OF_BLOOD)) + DoCast(me, SPELL_TASTE_OF_BLOOD, true); + + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } + + void UpdateAI(uint32 diff) OVERRIDE + { + if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO) && _instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_INTRO_H_1: + Talk(SAY_SAURFANG_INTRO_1); + break; + case EVENT_INTRO_H_2: + Talk(SAY_SAURFANG_INTRO_2); + break; + case EVENT_INTRO_SUMMON_SKYBREAKER: + sTransportMgr->CreateTransport(GO_THE_SKYBREAKER_H, 0, me->GetMap()); + break; + case EVENT_INTRO_H_3: + Talk(SAY_SAURFANG_INTRO_3); + break; + case EVENT_INTRO_H_4: + Talk(SAY_SAURFANG_INTRO_4); + break; + case EVENT_INTRO_H_5: + if (Creature* muradin = me->FindNearestCreature(NPC_IGB_MURADIN_BRONZEBEARD, 100.0f)) + muradin->AI()->Talk(SAY_MURADIN_INTRO_H); + break; + case EVENT_INTRO_H_6: + Talk(SAY_SAURFANG_INTRO_6); + break; + case EVENT_KEEP_PLAYER_IN_COMBAT: + if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS) + { + _instance->DoCastSpellOnPlayers(SPELL_LOCK_PLAYERS_AND_TAP_CHEST); + _events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, urand(5000, 8000)); + } + break; + case EVENT_SUMMON_MAGE: + Talk(SAY_SAURFANG_MAGES); + _controller.SummonCreatures(SLOT_FREEZE_MAGE, SLOT_FREEZE_MAGE); + break; + case EVENT_ADDS: + Talk(SAY_SAURFANG_ENTER_SKYBREAKER); + _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); + _controller.SummonCreatures(SLOT_MARINE_1, Is25ManRaid() ? SLOT_MARINE_4 : SLOT_MARINE_2); + _controller.SummonCreatures(SLOT_SERGEANT_1, Is25ManRaid() ? SLOT_SERGEANT_2 : SLOT_SERGEANT_1); + if (Transport* orgrimsHammer = me->GetTransport()) + orgrimsHammer->SummonPassenger(NPC_TELEPORT_PORTAL, OrgrimsHammerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, NULL, 21000); + + if (GameObject* go = HashMapHolder<GameObject>::Find(_instance->GetData64(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (Transport* skybreaker = go->ToTransport()) + skybreaker->SummonPassenger(NPC_TELEPORT_EXIT, SkybreakerTeleportExit, TEMPSUMMON_TIMED_DESPAWN, NULL, 23000); + + _events.ScheduleEvent(EVENT_ADDS_BOARD_YELL, 6000); + _events.ScheduleEvent(EVENT_ADDS, 60000); + break; + case EVENT_ADDS_BOARD_YELL: + if (Creature* muradin = me->FindNearestCreature(NPC_IGB_MURADIN_BRONZEBEARD, 200.0f)) + muradin->AI()->Talk(SAY_MURADIN_BOARD); + break; + case EVENT_CHECK_RIFLEMAN: + if (_controller.SummonCreatures(SLOT_RIFLEMAN_1, Is25ManRaid() ? SLOT_RIFLEMAN_8 : SLOT_RIFLEMAN_4)) + { + if (_axethrowersYellCooldown < time(NULL)) + { + Talk(SAY_SAURFANG_AXETHROWERS); + _axethrowersYellCooldown = time(NULL) + 5; + } + } + _events.ScheduleEvent(EVENT_CHECK_RIFLEMAN, 1000); + break; + case EVENT_CHECK_MORTAR: + if (_controller.SummonCreatures(SLOT_MORTAR_1, Is25ManRaid() ? SLOT_MORTAR_4 : SLOT_MORTAR_2)) + { + if (_rocketeersYellCooldown < time(NULL)) + { + Talk(SAY_SAURFANG_ROCKETEERS); + _rocketeersYellCooldown = time(NULL) + 5; + } + } + _events.ScheduleEvent(EVENT_CHECK_MORTAR, 1000); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + _events.ScheduleEvent(EVENT_CLEAVE, urand(2000, 10000)); + break; + default: + break; + } + } + + if (me->IsWithinMeleeRange(me->GetVictim())) + DoMeleeAttackIfReady(); + else if (me->isAttackReady()) + { + DoCastVictim(SPELL_RENDING_THROW); + me->resetAttackTimer(); + } + } + + bool CanAIAttack(Unit const* target) const OVERRIDE + { + if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) + return false; + return target->HasAura(SPELL_ON_ORGRIMS_HAMMER_DECK) || target->GetEntry() == NPC_SKYBREAKER_MARINE || target->GetEntry() == NPC_SKYBREAKER_SERGEANT; + } + + private: + EventMap _events; + PassengerController _controller; + InstanceScript* _instance; + time_t _firstMageCooldown; + time_t _axethrowersYellCooldown; + time_t _rocketeersYellCooldown; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_high_overlord_saurfang_igbAI>(creature); + } +}; + +class npc_muradin_bronzebeard_igb : public CreatureScript +{ + public: + npc_muradin_bronzebeard_igb() : CreatureScript("npc_muradin_bronzebeard_igb") { } + + struct npc_muradin_bronzebeard_igbAI : public ScriptedAI + { + npc_muradin_bronzebeard_igbAI(Creature* creature) : ScriptedAI(creature), + _instance(creature->GetInstanceScript()) + { + _controller.ResetSlots(ALLIANCE); + _controller.SetTransport(creature->GetTransport()); + me->setRegeneratingHealth(false); + me->m_CombatDistance = 70.0f; + _firstMageCooldown = time(NULL) + 60; + _riflemanYellCooldown = time_t(0); + _mortarYellCooldown = time_t(0); + } + + void InitializeAI() OVERRIDE + { + ScriptedAI::InitializeAI(); + + _events.Reset(); + _firstMageCooldown = time(NULL) + 60; + _riflemanYellCooldown = time_t(0); + _mortarYellCooldown = time_t(0); + } + + void EnterCombat(Unit* /*target*/) OVERRIDE + { + _events.SetPhase(PHASE_COMBAT); + DoCast(me, _instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE ? SPELL_FRIENDLY_BOSS_DAMAGE_MOD : SPELL_MELEE_TARGETING_ON_SKYBREAKER, true); + DoCast(me, SPELL_BATTLE_FURY, true); + _events.ScheduleEvent(EVENT_CLEAVE, urand(2000, 10000)); + } + + void EnterEvadeMode() OVERRIDE + { + if (!me->IsAlive()) + return; + + me->DeleteThreatList(); + me->CombatStop(true); + me->GetMotionMaster()->MoveTargetedHome(); + + Reset(); + } + + void DoAction(int32 action) OVERRIDE + { + if (action == ACTION_ENEMY_GUNSHIP_TALK) + { + if (Creature* muradin = me->FindNearestCreature(NPC_IGB_HIGH_OVERLORD_SAURFANG, 100.0f)) + muradin->AI()->DoAction(ACTION_SPAWN_ALL_ADDS); + + Talk(SAY_MURADIN_INTRO_6); + _events.ScheduleEvent(EVENT_INTRO_A_6, 5000); + _events.ScheduleEvent(EVENT_INTRO_A_7, 11000); + _events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, 1); + + _instance->SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, IN_PROGRESS); + // Combat starts now + if (Creature* orgrimsHammer = me->FindNearestCreature(NPC_ORGRIMS_HAMMER, 100.0f)) + _instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, orgrimsHammer, 1); + + if (Creature* skybreaker = me->FindNearestCreature(NPC_THE_SKYBREAKER, 100.0f)) + { + _instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, skybreaker, 2); + skybreaker->CastSpell(skybreaker, SPELL_CHECK_FOR_PLAYERS, TRIGGERED_FULL_MASK); + } + + me->GetMap()->SetZoneMusic(AREA_ICECROWN_CITADEL, MUSIC_ENCOUNTER); + } + else if (action == ACTION_SPAWN_MAGE) + { + time_t now = time(NULL); + if (_firstMageCooldown > now) + _events.ScheduleEvent(EVENT_SUMMON_MAGE, (_firstMageCooldown - now) * IN_MILLISECONDS); + else + _events.ScheduleEvent(EVENT_SUMMON_MAGE, 1); + } + else if (action == ACTION_SPAWN_ALL_ADDS) + { + _events.ScheduleEvent(EVENT_ADDS, 12000); + _events.ScheduleEvent(EVENT_CHECK_RIFLEMAN, 13000); + _events.ScheduleEvent(EVENT_CHECK_MORTAR, 13000); + if (Is25ManRaid()) + _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MORTAR_4); + else + { + _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); + _controller.SummonCreatures(SLOT_MORTAR_1, SLOT_MORTAR_2); + _controller.SummonCreatures(SLOT_RIFLEMAN_1, SLOT_RIFLEMAN_4); + } + } + else if (action == ACTION_EXIT_SHIP) + { + Position pos; + pos.Relocate(MuradinExitPath[MuradinExitPathSize - 1].x, MuradinExitPath[MuradinExitPathSize - 1].y, MuradinExitPath[MuradinExitPathSize - 1].z); + me->GetMotionMaster()->MovePoint(EVENT_CHARGE_PREPATH, pos, false); + + Movement::PointsArray path(MuradinExitPath, MuradinExitPath + MuradinExitPathSize); + + Movement::MoveSplineInit init(me); + init.DisableTransportPathTransformations(); + init.MovebyPath(path, 0); + init.Launch(); + + me->DespawnOrUnsummon(18000); + } + } + + void SetData(uint32 type, uint32 data) OVERRIDE + { + if (type == ACTION_CLEAR_SLOT) + { + _controller.ClearSlot(PassengerSlots(data)); + if (data == SLOT_FREEZE_MAGE) + _events.ScheduleEvent(EVENT_SUMMON_MAGE, urand(30000, 33500)); + } + } + + void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) OVERRIDE + { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->GetTransport()->EnableMovement(true); + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_A_1, 5000); + _events.ScheduleEvent(EVENT_INTRO_A_2, 10000, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_SUMMON_ORGRIMS_HAMMER, 28000, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_A_3, 33000, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_A_4, 39000, 0, PHASE_INTRO); + _events.ScheduleEvent(EVENT_INTRO_A_5, 45000, 0, PHASE_INTRO); + } + + void DamageTaken(Unit* , uint32& damage) OVERRIDE + { + if (me->HealthBelowPctDamaged(65, damage) && me->HasAura(SPELL_TASTE_OF_BLOOD)) + DoCast(me, SPELL_TASTE_OF_BLOOD, true); + + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } + + void UpdateAI(uint32 diff) OVERRIDE + { + if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO) && _instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_INTRO_A_1: + Talk(SAY_MURADIN_INTRO_1); + break; + case EVENT_INTRO_A_2: + Talk(SAY_MURADIN_INTRO_2); + break; + case EVENT_INTRO_SUMMON_ORGRIMS_HAMMER: + sTransportMgr->CreateTransport(GO_ORGRIMS_HAMMER_A, 0, me->GetMap()); + break; + case EVENT_INTRO_A_3: + Talk(SAY_MURADIN_INTRO_3); + break; + case EVENT_INTRO_A_4: + Talk(SAY_MURADIN_INTRO_4); + break; + case EVENT_INTRO_A_5: + Talk(SAY_MURADIN_INTRO_5); + break; + case EVENT_INTRO_A_6: + if (Creature* saurfang = me->FindNearestCreature(NPC_IGB_HIGH_OVERLORD_SAURFANG, 100.0f)) + saurfang->AI()->Talk(SAY_SAURFANG_INTRO_A); + break; + case EVENT_INTRO_A_7: + Talk(SAY_MURADIN_INTRO_7); + break; + case EVENT_KEEP_PLAYER_IN_COMBAT: + if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS) + { + _instance->DoCastSpellOnPlayers(SPELL_LOCK_PLAYERS_AND_TAP_CHEST); + _events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, urand(5000, 8000)); + } + break; + case EVENT_SUMMON_MAGE: + Talk(SAY_MURADIN_SORCERERS); + _controller.SummonCreatures(SLOT_FREEZE_MAGE, SLOT_FREEZE_MAGE); + break; + case EVENT_ADDS: + Talk(SAY_MURADIN_ENTER_ORGRIMMS_HAMMER); + _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); + _controller.SummonCreatures(SLOT_MARINE_1, Is25ManRaid() ? SLOT_MARINE_4 : SLOT_MARINE_2); + _controller.SummonCreatures(SLOT_SERGEANT_1, Is25ManRaid() ? SLOT_SERGEANT_2 : SLOT_SERGEANT_1); + if (Transport* skybreaker = me->GetTransport()) + skybreaker->SummonPassenger(NPC_TELEPORT_PORTAL, SkybreakerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, NULL, 21000); + + if (GameObject* go = HashMapHolder<GameObject>::Find(_instance->GetData64(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (Transport* orgrimsHammer = go->ToTransport()) + orgrimsHammer->SummonPassenger(NPC_TELEPORT_EXIT, OrgrimsHammerTeleportExit, TEMPSUMMON_TIMED_DESPAWN, NULL, 23000); + + _events.ScheduleEvent(EVENT_ADDS_BOARD_YELL, 6000); + _events.ScheduleEvent(EVENT_ADDS, 60000); + break; + case EVENT_ADDS_BOARD_YELL: + if (Creature* saurfang = me->FindNearestCreature(NPC_IGB_HIGH_OVERLORD_SAURFANG, 200.0f)) + saurfang->AI()->Talk(SAY_SAURFANG_BOARD); + break; + case EVENT_CHECK_RIFLEMAN: + if (_controller.SummonCreatures(SLOT_RIFLEMAN_1, Is25ManRaid() ? SLOT_RIFLEMAN_8 : SLOT_RIFLEMAN_4)) + { + if (_riflemanYellCooldown < time(NULL)) + { + Talk(SAY_MURADIN_RIFLEMAN); + _riflemanYellCooldown = time(NULL) + 5; + } + } + _events.ScheduleEvent(EVENT_CHECK_RIFLEMAN, 1000); + break; + case EVENT_CHECK_MORTAR: + if (_controller.SummonCreatures(SLOT_MORTAR_1, Is25ManRaid() ? SLOT_MORTAR_4 : SLOT_MORTAR_2)) + { + if (_mortarYellCooldown < time(NULL)) + { + Talk(SAY_MURADIN_MORTAR); + _mortarYellCooldown = time(NULL) + 5; + } + } + _events.ScheduleEvent(EVENT_CHECK_MORTAR, 1000); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + _events.ScheduleEvent(EVENT_CLEAVE, urand(2000, 10000)); + break; + default: + break; + } + } + + if (me->IsWithinMeleeRange(me->GetVictim())) + DoMeleeAttackIfReady(); + else if (me->isAttackReady()) + { + DoCastVictim(SPELL_RENDING_THROW); + me->resetAttackTimer(); + } + } + + bool CanAIAttack(Unit const* target) const OVERRIDE + { + if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) + return false; + return target->HasAura(SPELL_ON_SKYBREAKER_DECK) || target->GetEntry() == NPC_KOR_KRON_REAVER || target->GetEntry() == NPC_KOR_KRON_SERGEANT; + } + + private: + EventMap _events; + PassengerController _controller; + InstanceScript* _instance; + time_t _firstMageCooldown; + time_t _riflemanYellCooldown; + time_t _mortarYellCooldown; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_muradin_bronzebeard_igbAI>(creature); + } +}; + +class npc_zafod_boombox : public CreatureScript +{ + public: + npc_zafod_boombox() : CreatureScript("npc_zafod_boombox") { } + + struct npc_zafod_boomboxAI : public gunship_npc_AI + { + npc_zafod_boomboxAI(Creature* creature) : gunship_npc_AI(creature) + { + } + + void Reset() OVERRIDE + { + me->SetReactState(REACT_PASSIVE); + } + + void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) OVERRIDE + { + player->AddItem(ITEM_GOBLIN_ROCKET_PACK, 1); + player->PlayerTalkClass->SendCloseGossip(); + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + UpdateVictim(); + } + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_zafod_boomboxAI>(creature); + } +}; + +struct npc_gunship_boarding_addAI : public gunship_npc_AI +{ + npc_gunship_boarding_addAI(Creature* creature) : gunship_npc_AI(creature) + { + me->m_CombatDistance = 80.0f; + _usedDesperateResolve = false; + } + + void SetData(uint32 type, uint32 data) OVERRIDE + { + // detach from captain + if (type == ACTION_SET_SLOT) + { + SetSlotInfo(data); + + me->SetReactState(REACT_PASSIVE); + + me->m_Events.AddEvent(new DelayedMovementEvent(me, Slot->TargetPosition), me->m_Events.CalculateTime(3000 * (Index - SLOT_MARINE_1))); + + if (Creature* captain = me->FindNearestCreature(Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? NPC_IGB_MURADIN_BRONZEBEARD : NPC_IGB_HIGH_OVERLORD_SAURFANG, 200.0f)) + captain->AI()->SetData(ACTION_CLEAR_SLOT, Index); + } + } + + void MovementInform(uint32 type, uint32 pointId) OVERRIDE + { + if (type != POINT_MOTION_TYPE) + return; + + if (pointId == EVENT_CHARGE_PREPATH && Slot) + { + Position const& otherTransportPos = Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? OrgrimsHammerTeleportExit : SkybreakerTeleportExit; + float x, y, z, o; + otherTransportPos.GetPosition(x, y, z, o); + + Transport* myTransport = me->GetTransport(); + if (!myTransport) + return; + + if (GameObject* go = HashMapHolder<GameObject>::Find(Instance->GetData64(DATA_ICECROWN_GUNSHIP_BATTLE))) + if (Transport* destTransport = go->ToTransport()) + destTransport->CalculatePassengerPosition(x, y, z, &o); + + float angle = frand(0, M_PI * 2.0f); + x += 2.0f * std::cos(angle); + y += 2.0f * std::sin(angle); + + me->SetHomePosition(x, y, z, o); + myTransport->CalculatePassengerOffset(x, y, z, &o); + me->SetTransportHomePosition(x, y, z, o); + + me->m_Events.AddEvent(new BattleExperienceEvent(me), me->m_Events.CalculateTime(BattleExperienceEvent::ExperiencedTimes[0])); + DoCast(me, SPELL_BATTLE_EXPERIENCE, true); + DoCast(me, SPELL_TELEPORT_TO_ENEMY_SHIP, true); + DoCast(me, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_MELEE_TARGETING_ON_ORGRIMS_HAMMER : SPELL_MELEE_TARGETING_ON_SKYBREAKER, true); + me->_AddCreatureSpellCooldown(BurningPitchId, time(NULL) + 3); + + std::list<Player*> players; + Trinity::UnitAuraCheck check(true, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_ON_ORGRIMS_HAMMER_DECK : SPELL_ON_SKYBREAKER_DECK); + Trinity::PlayerListSearcher<Trinity::UnitAuraCheck> searcher(me, players, check); + me->VisitNearbyWorldObject(200.0f, searcher); + + players.remove_if([this](Player* player) + { + return !me->_IsTargetAcceptable(player) || !me->CanStartAttack(player, true); + }); + + if (!players.empty()) + { + players.sort(Trinity::ObjectDistanceOrderPred(me)); + for (std::list<Player*>::iterator itr = players.begin(); itr != players.end(); ++itr) + me->AddThreat(*itr, 1.0f); + + AttackStart(players.front()); + } + + me->SetReactState(REACT_AGGRESSIVE); + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) OVERRIDE + { + if (_usedDesperateResolve) + return; + + if (!me->HealthBelowPctDamaged(25, damage)) + return; + + _usedDesperateResolve = true; + DoCast(me, SPELL_DESPERATE_RESOLVE, true); + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + if (!SelectVictim()) + { + TriggerBurningPitch(); + return; + } + + if (!HasAttackablePlayerNearby()) + TriggerBurningPitch(); + + DoMeleeAttackIfReady(); + } + + bool CanAIAttack(Unit const* target) const OVERRIDE + { + uint32 spellId = SPELL_ON_SKYBREAKER_DECK; + uint32 creatureEntry = NPC_IGB_MURADIN_BRONZEBEARD; + if (Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE) + { + spellId = SPELL_ON_ORGRIMS_HAMMER_DECK; + creatureEntry = NPC_IGB_HIGH_OVERLORD_SAURFANG; + } + + return target->HasAura(spellId) || target->GetEntry() == creatureEntry; + } + + bool HasAttackablePlayerNearby() + { + std::list<Player*> players; + Trinity::UnitAuraCheck check(true, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_ON_ORGRIMS_HAMMER_DECK : SPELL_ON_SKYBREAKER_DECK); + Trinity::PlayerListSearcher<Trinity::UnitAuraCheck> searcher(me, players, check); + me->VisitNearbyWorldObject(200.0f, searcher); + + players.remove_if([this](Player* player) + { + return !me->_IsTargetAcceptable(player) || !me->CanStartAttack(player, true); + }); + + return !players.empty(); + } + +private: + bool _usedDesperateResolve; +}; + +class npc_gunship_boarding_leader : public CreatureScript +{ + public: + npc_gunship_boarding_leader() : CreatureScript("npc_gunship_boarding_leader") { } + + struct npc_gunship_boarding_leaderAI : public npc_gunship_boarding_addAI + { + npc_gunship_boarding_leaderAI(Creature* creature) : npc_gunship_boarding_addAI(creature) + { + } + + void EnterCombat(Unit* target) OVERRIDE + { + npc_gunship_boarding_addAI::EnterCombat(target); + _events.ScheduleEvent(EVENT_BLADESTORM, urand(13000, 18000)); + _events.ScheduleEvent(EVENT_WOUNDING_STRIKE, urand(8000, 10000)); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + if (!SelectVictim()) + { + TriggerBurningPitch(); + return; + } + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasAura(SPELL_BLADESTORM)) + return; + + if (!HasAttackablePlayerNearby()) + TriggerBurningPitch(); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BLADESTORM: + DoCastAOE(SPELL_BLADESTORM); + _events.ScheduleEvent(EVENT_BLADESTORM, urand(25000, 30000)); + break; + case EVENT_WOUNDING_STRIKE: + DoCastVictim(SPELL_WOUNDING_STRIKE); + _events.ScheduleEvent(EVENT_WOUNDING_STRIKE, urand(9000, 13000)); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } + + private: + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_gunship_boarding_leaderAI>(creature); + } +}; + +class npc_gunship_boarding_add : public CreatureScript +{ + public: + npc_gunship_boarding_add() : CreatureScript("npc_gunship_boarding_add") { } + + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_gunship_boarding_addAI>(creature); + } +}; + +class npc_gunship_gunner : public CreatureScript +{ + public: + npc_gunship_gunner() : CreatureScript("npc_gunship_gunner") { } + + struct npc_gunship_gunnerAI : public gunship_npc_AI + { + npc_gunship_gunnerAI(Creature* creature) : gunship_npc_AI(creature) + { + creature->m_CombatDistance = 200.0f; + } + + void AttackStart(Unit* target) OVERRIDE + { + me->Attack(target, false); + } + + void MovementInform(uint32 type, uint32 pointId) OVERRIDE + { + gunship_npc_AI::MovementInform(type, pointId); + if (type == POINT_MOTION_TYPE && pointId == EVENT_CHARGE_PREPATH) + me->SetControlled(true, UNIT_STATE_ROOT); + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + if (!SelectVictim()) + { + TriggerBurningPitch(); + return; + } + + DoSpellAttackIfReady(me->GetEntry() == NPC_SKYBREAKER_RIFLEMAN ? SPELL_SHOOT : SPELL_HURL_AXE); + } + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_gunship_gunnerAI>(creature); + } +}; + +class npc_gunship_rocketeer : public CreatureScript +{ + public: + npc_gunship_rocketeer() : CreatureScript("npc_gunship_rocketeer") { } + + struct npc_gunship_rocketeerAI : public gunship_npc_AI + { + npc_gunship_rocketeerAI(Creature* creature) : gunship_npc_AI(creature) + { + creature->m_CombatDistance = 200.0f; + } + + void MovementInform(uint32 type, uint32 pointId) OVERRIDE + { + gunship_npc_AI::MovementInform(type, pointId); + if (type == POINT_MOTION_TYPE && pointId == EVENT_CHARGE_PREPATH) + me->SetControlled(true, UNIT_STATE_ROOT); + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + if (!SelectVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + uint32 spellId = me->GetEntry() == NPC_SKYBREAKER_MORTAR_SOLDIER ? SPELL_ROCKET_ARTILLERY_A : SPELL_ROCKET_ARTILLERY_H; + if (me->HasSpellCooldown(spellId)) + return; + + DoCastAOE(spellId, true); + me->_AddCreatureSpellCooldown(spellId, time(NULL) + 9); + } + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_gunship_rocketeerAI>(creature); + } +}; + +class npc_gunship_mage : public CreatureScript +{ + public: + npc_gunship_mage() : CreatureScript("npc_gunship_mage") { } + + struct npc_gunship_mageAI : public gunship_npc_AI + { + npc_gunship_mageAI(Creature* creature) : gunship_npc_AI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void EnterEvadeMode() OVERRIDE + { + ScriptedAI::EnterEvadeMode(); + } + + void MovementInform(uint32 type, uint32 pointId) OVERRIDE + { + if (type != POINT_MOTION_TYPE) + return; + + if (pointId == EVENT_CHARGE_PREPATH && Slot) + { + SlotInfo const* slots = Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SkybreakerSlotInfo : OrgrimsHammerSlotInfo; + me->SetFacingTo(slots[Index].TargetPosition.GetOrientation()); + switch (Index) + { + case SLOT_FREEZE_MAGE: + DoCastAOE(SPELL_BELOW_ZERO); + break; + case SLOT_MAGE_1: + case SLOT_MAGE_2: + DoCastAOE(SPELL_SHADOW_CHANNELING); + break; + default: + break; + } + + me->SetControlled(true, UNIT_STATE_ROOT); + } + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + UpdateVictim(); + } + + bool CanAIAttack(Unit const* /*target*/) const OVERRIDE + { + return true; + } + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return GetIcecrownCitadelAI<npc_gunship_mageAI>(creature); + } +}; + +/** @HACK This AI only resets MOVEMENTFLAG_ROOT on the vehicle. + Currently the core always removes MOVEMENTFLAG_ROOT sent from client packets to prevent cheaters from freezing clients of other players + but it actually is a valid flag - needs more research to fix both freezes and keep the flag as is (see WorldSession::ReadMovementInfo) + +Example packet: +ClientToServer: CMSG_FORCE_MOVE_ROOT_ACK (0x00E9) Length: 67 ConnectionIndex: 0 Time: 03/04/2010 03:57:55.000 Number: 471326 +Guid: +Movement Counter: 80 +Movement Flags: OnTransport, Root (2560) +Extra Movement Flags: None (0) +Time: 52291611 +Position: X: -396.0302 Y: 2482.906 Z: 249.86 +Orientation: 1.468665 +Transport GUID: Full: 0x1FC0000000000460 Type: MOTransport Low: 1120 +Transport Position: X: -6.152398 Y: -23.49037 Z: 21.64464 O: 4.827727 +Transport Time: 9926 +Transport Seat: 255 +Fall Time: 824 +*/ +class npc_gunship_cannon : public CreatureScript +{ + public: + npc_gunship_cannon() : CreatureScript("npc_gunship_cannon") { } + + struct npc_gunship_cannonAI : public PassiveAI + { + npc_gunship_cannonAI(Creature* creature) : PassiveAI(creature) + { + } + + void OnCharmed(bool /*apply*/) OVERRIDE { } + + void PassengerBoarded(Unit* /*passenger*/, int8 /*seat*/, bool apply) OVERRIDE + { + if (!apply) + { + me->SetControlled(false, UNIT_STATE_ROOT); + me->SetControlled(true, UNIT_STATE_ROOT); + } + } + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_gunship_cannonAI(creature); + } +}; + +class spell_igb_rocket_pack : public SpellScriptLoader +{ + public: + spell_igb_rocket_pack() : SpellScriptLoader("spell_igb_rocket_pack") { } + + class spell_igb_rocket_pack_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_rocket_pack_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) OVERRIDE + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROCKET_PACK_DAMAGE) || + !sSpellMgr->GetSpellInfo(SPELL_ROCKET_BURST)) + return false; + + return true; + } + + void HandlePeriodic(AuraEffect const* /*aurEff*/) + { + if (GetTarget()->movespline->Finalized()) + Remove(AURA_REMOVE_BY_EXPIRE); + } + + void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + SpellInfo const* damageInfo = sSpellMgr->EnsureSpellInfo(SPELL_ROCKET_PACK_DAMAGE); + GetTarget()->CastCustomSpell(SPELL_ROCKET_PACK_DAMAGE, SPELLVALUE_BASE_POINT0, 2 * (damageInfo->Effects[EFFECT_0].CalcValue() + aurEff->GetTickNumber() * aurEff->GetAmplitude()), NULL, TRIGGERED_FULL_MASK); + GetTarget()->CastSpell(NULL, SPELL_ROCKET_BURST, TRIGGERED_FULL_MASK); + } + + void Register() OVERRIDE + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_igb_rocket_pack_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + OnEffectRemove += AuraEffectRemoveFn(spell_igb_rocket_pack_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_igb_rocket_pack_AuraScript(); + } +}; + +class spell_igb_rocket_pack_useable : public SpellScriptLoader +{ + public: + spell_igb_rocket_pack_useable() : SpellScriptLoader("spell_igb_rocket_pack_useable") { } + + class spell_igb_rocket_pack_useable_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_rocket_pack_useable_AuraScript); + + bool Load() + { + return GetOwner()->GetInstanceScript(); + } + + bool CheckAreaTarget(Unit* target) + { + return target->GetTypeId() == TYPEID_PLAYER && GetOwner()->GetInstanceScript()->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE; + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* owner = GetOwner()->ToCreature()) + if (Player* target = GetTarget()->ToPlayer()) + if (target->HasItemCount(ITEM_GOBLIN_ROCKET_PACK, 1)) + sCreatureTextMgr->SendChat(owner, SAY_ZAFOD_ROCKET_PACK_ACTIVE, target, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, target); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* owner = GetOwner()->ToCreature()) + if (Player* target = GetTarget()->ToPlayer()) + if (target->HasItemCount(ITEM_GOBLIN_ROCKET_PACK, 1)) + sCreatureTextMgr->SendChat(owner, SAY_ZAFOD_ROCKET_PACK_DISABLED, target, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, target); + } + + void Register() OVERRIDE + { + DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_igb_rocket_pack_useable_AuraScript::CheckAreaTarget); + AfterEffectApply += AuraEffectApplyFn(spell_igb_rocket_pack_useable_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_igb_rocket_pack_useable_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_igb_rocket_pack_useable_AuraScript(); + } +}; + +class spell_igb_on_gunship_deck : public SpellScriptLoader +{ + public: + spell_igb_on_gunship_deck() : SpellScriptLoader("spell_igb_on_gunship_deck") { } + + class spell_igb_on_gunship_deck_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_on_gunship_deck_AuraScript); + + bool Load() OVERRIDE + { + if (InstanceScript* instance = GetOwner()->GetInstanceScript()) + _teamInInstance = instance->GetData(DATA_TEAM_IN_INSTANCE); + else + _teamInInstance = 0; + return true; + } + + bool CheckAreaTarget(Unit* unit) + { + return unit->GetTypeId() == TYPEID_PLAYER; + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetSpellInfo()->Id == uint32(_teamInInstance == HORDE ? SPELL_ON_SKYBREAKER_DECK : SPELL_ON_ORGRIMS_HAMMER_DECK)) + if (Creature* gunship = GetOwner()->FindNearestCreature(_teamInInstance == HORDE ? NPC_ORGRIMS_HAMMER : NPC_THE_SKYBREAKER, 200.0f)) + gunship->AI()->SetGUID(GetTarget()->GetGUID(), ACTION_SHIP_VISITS); + } + + void Register() OVERRIDE + { + DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_igb_on_gunship_deck_AuraScript::CheckAreaTarget); + AfterEffectApply += AuraEffectApplyFn(spell_igb_on_gunship_deck_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + + uint32 _teamInInstance; + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_igb_on_gunship_deck_AuraScript(); + } +}; + +class spell_igb_periodic_trigger_with_power_cost : public SpellScriptLoader +{ + public: + spell_igb_periodic_trigger_with_power_cost() : SpellScriptLoader("spell_igb_periodic_trigger_with_power_cost") { } + + class spell_igb_periodic_trigger_with_power_cost_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_periodic_trigger_with_power_cost_AuraScript); + + void HandlePeriodicTick(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)); + } + + void Register() OVERRIDE + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_igb_periodic_trigger_with_power_cost_AuraScript::HandlePeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_igb_periodic_trigger_with_power_cost_AuraScript(); + } +}; + +class spell_igb_cannon_blast : public SpellScriptLoader +{ + public: + spell_igb_cannon_blast() : SpellScriptLoader("spell_igb_cannon_blast") { } + + class spell_igb_cannon_blast_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_cannon_blast_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void CheckEnergy() + { + if (GetCaster()->GetPower(POWER_ENERGY) >= 100) + { + GetCaster()->CastSpell(GetCaster(), SPELL_OVERHEAT, TRIGGERED_FULL_MASK); + if (Vehicle* vehicle = GetCaster()->GetVehicleKit()) + if (Unit* passenger = vehicle->GetPassenger(0)) + sCreatureTextMgr->SendChat(GetCaster()->ToCreature(), SAY_OVERHEAT, passenger); + } + } + + void Register() OVERRIDE + { + AfterHit += SpellHitFn(spell_igb_cannon_blast_SpellScript::CheckEnergy); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_cannon_blast_SpellScript(); + } +}; + +class spell_igb_incinerating_blast : public SpellScriptLoader +{ + public: + spell_igb_incinerating_blast() : SpellScriptLoader("spell_igb_incinerating_blast") { } + + class spell_igb_incinerating_blast_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_incinerating_blast_SpellScript); + + void StoreEnergy() + { + _energyLeft = GetCaster()->GetPower(POWER_ENERGY) - 10; + } + + void RemoveEnergy() + { + GetCaster()->SetPower(POWER_ENERGY, 0); + } + + void CalculateDamage(SpellEffIndex /*effIndex*/) + { + SetEffectValue(GetEffectValue() + _energyLeft * _energyLeft * 8); + } + + void Register() OVERRIDE + { + OnCast += SpellCastFn(spell_igb_incinerating_blast_SpellScript::StoreEnergy); + AfterCast += SpellCastFn(spell_igb_incinerating_blast_SpellScript::RemoveEnergy); + OnEffectLaunchTarget += SpellEffectFn(spell_igb_incinerating_blast_SpellScript::CalculateDamage, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); + } + + uint32 _energyLeft; + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_incinerating_blast_SpellScript(); + } +}; + +class spell_igb_overheat : public SpellScriptLoader +{ + public: + spell_igb_overheat() : SpellScriptLoader("spell_igb_overheat") { } + + class spell_igb_overheat_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_overheat_AuraScript); + + bool Load() OVERRIDE + { + if (GetAura()->GetType() != UNIT_AURA_TYPE) + return false; + return GetUnitOwner()->IsVehicle(); + } + + void SendClientControl(uint8 value) + { + if (Vehicle* vehicle = GetUnitOwner()->GetVehicleKit()) + { + if (Unit* passenger = vehicle->GetPassenger(0)) + { + if (Player* player = passenger->ToPlayer()) + { + WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, GetUnitOwner()->GetPackGUID().size() + 1); + data.append(GetUnitOwner()->GetPackGUID()); + data << uint8(value); + player->GetSession()->SendPacket(&data); + } + } + } + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + SendClientControl(0); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + SendClientControl(1); + } + + void Register() OVERRIDE + { + AfterEffectApply += AuraEffectApplyFn(spell_igb_overheat_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_igb_overheat_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_igb_overheat_AuraScript(); + } +}; + +class spell_igb_below_zero : public SpellScriptLoader +{ + public: + spell_igb_below_zero() : SpellScriptLoader("spell_igb_below_zero") { } + + class spell_igb_below_zero_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_below_zero_SpellScript); + + void RemovePassengers() + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_EJECT_ALL_PASSENGERS_BELOW_ZERO, TRIGGERED_FULL_MASK); + } + + void Register() OVERRIDE + { + BeforeHit += SpellHitFn(spell_igb_below_zero_SpellScript::RemovePassengers); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_below_zero_SpellScript(); + } +}; + +class spell_igb_teleport_to_enemy_ship : public SpellScriptLoader +{ + public: + spell_igb_teleport_to_enemy_ship() : SpellScriptLoader("spell_igb_teleport_to_enemy_ship") { } + + class spell_igb_teleport_to_enemy_ship_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_teleport_to_enemy_ship_SpellScript); + + void RelocateTransportOffset(SpellEffIndex /*effIndex*/) + { + WorldLocation const* dest = GetHitDest(); + Unit* target = GetHitUnit(); + if (!dest || !target || !target->GetTransport()) + return; + + float x, y, z, o; + dest->GetPosition(x, y, z, o); + target->GetTransport()->CalculatePassengerOffset(x, y, z, &o); + target->m_movementInfo.transport.pos.Relocate(x, y, z, o); + } + + void Register() OVERRIDE + { + OnEffectHitTarget += SpellEffectFn(spell_igb_teleport_to_enemy_ship_SpellScript::RelocateTransportOffset, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_teleport_to_enemy_ship_SpellScript(); + } +}; + +class spell_igb_burning_pitch_selector : public SpellScriptLoader +{ + public: + spell_igb_burning_pitch_selector() : SpellScriptLoader("spell_igb_burning_pitch_selector") { } + + class spell_igb_burning_pitch_selector_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_burning_pitch_selector_SpellScript); + + void FilterTargets(std::list<WorldObject*>& targets) + { + uint32 team = HORDE; + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + team = instance->GetData(DATA_TEAM_IN_INSTANCE); + + targets.remove_if([team](WorldObject* target) -> bool + { + if (Transport* transport = target->GetTransport()) + return transport->GetEntry() != uint32(team == HORDE ? GO_ORGRIMS_HAMMER_H : GO_THE_SKYBREAKER_A); + return true; + }); + + if (!targets.empty()) + { + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + } + + void HandleDummy(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), TRIGGERED_NONE); + } + + void Register() OVERRIDE + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_igb_burning_pitch_selector_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_igb_burning_pitch_selector_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_burning_pitch_selector_SpellScript(); + } +}; + +class spell_igb_burning_pitch : public SpellScriptLoader +{ + public: + spell_igb_burning_pitch() : SpellScriptLoader("spell_igb_burning_pitch") { } + + class spell_igb_burning_pitch_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_burning_pitch_SpellScript); + + void HandleDummy(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetCaster()->CastCustomSpell(uint32(GetEffectValue()), SPELLVALUE_BASE_POINT0, 8000, NULL, TRIGGERED_FULL_MASK); + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_BURNING_PITCH, TRIGGERED_FULL_MASK); + } + + void Register() OVERRIDE + { + OnEffectHitTarget += SpellEffectFn(spell_igb_burning_pitch_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_burning_pitch_SpellScript(); + } +}; + +class spell_igb_rocket_artillery : public SpellScriptLoader +{ + public: + spell_igb_rocket_artillery() : SpellScriptLoader("spell_igb_rocket_artillery") { } + + class spell_igb_rocket_artillery_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_rocket_artillery_SpellScript); + + void SelectRandomTarget(std::list<WorldObject*>& targets) + { + if (!targets.empty()) + { + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), TRIGGERED_NONE); + } + + void Register() OVERRIDE + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_igb_rocket_artillery_SpellScript::SelectRandomTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_igb_rocket_artillery_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_rocket_artillery_SpellScript(); + } +}; + +class spell_igb_rocket_artillery_explosion : public SpellScriptLoader +{ + public: + spell_igb_rocket_artillery_explosion() : SpellScriptLoader("spell_igb_rocket_artillery_explosion") { } + + class spell_igb_rocket_artillery_explosion_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_rocket_artillery_explosion_SpellScript); + + void DamageGunship(SpellEffIndex /*effIndex*/) + { + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + GetCaster()->CastCustomSpell(instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_BURNING_PITCH_DAMAGE_A : SPELL_BURNING_PITCH_DAMAGE_H, SPELLVALUE_BASE_POINT0, 5000, NULL, TRIGGERED_FULL_MASK); + } + + void Register() OVERRIDE + { + OnEffectHit += SpellEffectFn(spell_igb_rocket_artillery_explosion_SpellScript::DamageGunship, EFFECT_0, SPELL_EFFECT_TRIGGER_MISSILE); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_rocket_artillery_explosion_SpellScript(); + } +}; + +class spell_igb_gunship_fall_teleport : public SpellScriptLoader +{ + public: + spell_igb_gunship_fall_teleport() : SpellScriptLoader("spell_igb_gunship_fall_teleport") { } + + class spell_igb_gunship_fall_teleport_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_gunship_fall_teleport_SpellScript); + + bool Load() + { + return GetCaster()->GetInstanceScript(); + } + + void SelectTransport(WorldObject*& target) + { + if (InstanceScript* instance = target->GetInstanceScript()) + target = HashMapHolder<GameObject>::Find(instance->GetData64(DATA_ICECROWN_GUNSHIP_BATTLE)); + } + + void RelocateDest(SpellEffIndex /*effIndex*/) + { + if (GetCaster()->GetInstanceScript()->GetData(DATA_TEAM_IN_INSTANCE) == HORDE) + GetHitDest()->RelocateOffset({ 0.0f, 0.0f, 36.0f, 0.0f }); + else + GetHitDest()->RelocateOffset({ 0.0f, 0.0f, 21.0f, 0.0f }); + } + + void Register() OVERRIDE + { + OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_igb_gunship_fall_teleport_SpellScript::SelectTransport, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); + OnEffectLaunch += SpellEffectFn(spell_igb_gunship_fall_teleport_SpellScript::RelocateDest, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_gunship_fall_teleport_SpellScript(); + } +}; + +class spell_igb_check_for_players : public SpellScriptLoader +{ + public: + spell_igb_check_for_players() : SpellScriptLoader("spell_igb_check_for_players") { } + + class spell_igb_check_for_players_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_check_for_players_SpellScript); + + bool Load() OVERRIDE + { + _playerCount = 0; + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void CountTargets(std::list<WorldObject*>& targets) + { + _playerCount = targets.size(); + } + + void TriggerWipe() + { + if (!_playerCount) + GetCaster()->ToCreature()->AI()->JustDied(NULL); + } + + void TeleportPlayer(SpellEffIndex /*effIndex*/) + { + if (GetHitUnit()->GetPositionZ() < GetCaster()->GetPositionZ() - 10.0f) + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_GUNSHIP_FALL_TELEPORT, TRIGGERED_FULL_MASK); + } + + void Register() OVERRIDE + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_igb_check_for_players_SpellScript::CountTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + AfterCast += SpellCastFn(spell_igb_check_for_players_SpellScript::TriggerWipe); + OnEffectHitTarget += SpellEffectFn(spell_igb_check_for_players_SpellScript::TeleportPlayer, EFFECT_0, SPELL_EFFECT_DUMMY); + } + + uint32 _playerCount; + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_check_for_players_SpellScript(); + } +}; + +class spell_igb_teleport_players_on_victory : public SpellScriptLoader +{ + public: + spell_igb_teleport_players_on_victory() : SpellScriptLoader("spell_igb_teleport_players_on_victory") { } + + class spell_igb_teleport_players_on_victory_SpellScript : public SpellScript + { + PrepareSpellScript(spell_igb_teleport_players_on_victory_SpellScript); + + bool Load() OVERRIDE + { + return GetCaster()->GetInstanceScript(); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + InstanceScript* instance = GetCaster()->GetInstanceScript(); + targets.remove_if([instance](WorldObject* target) -> bool + { + return target->GetTransGUID() != instance->GetData64(DATA_ENEMY_GUNSHIP); + }); + } + + void Register() OVERRIDE + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_igb_teleport_players_on_victory_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_DEST_AREA_ENTRY); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_igb_teleport_players_on_victory_SpellScript(); + } +}; + +class achievement_im_on_a_boat : public AchievementCriteriaScript +{ + public: + achievement_im_on_a_boat() : AchievementCriteriaScript("achievement_im_on_a_boat") { } + + bool OnCheck(Player* /*source*/, Unit* target) OVERRIDE + { + return target->GetAI() && target->GetAI()->GetData(ACTION_SHIP_VISITS) <= 2; + } +}; + +void AddSC_boss_icecrown_gunship_battle() +{ + new npc_gunship(); + new npc_high_overlord_saurfang_igb(); + new npc_muradin_bronzebeard_igb(); + new npc_zafod_boombox(); + new npc_gunship_boarding_leader(); + new npc_gunship_boarding_add(); + new npc_gunship_gunner(); + new npc_gunship_rocketeer(); + new npc_gunship_mage(); + new npc_gunship_cannon(); + new spell_igb_rocket_pack(); + new spell_igb_rocket_pack_useable(); + new spell_igb_on_gunship_deck(); + new spell_igb_periodic_trigger_with_power_cost(); + new spell_igb_cannon_blast(); + new spell_igb_incinerating_blast(); + new spell_igb_overheat(); + new spell_igb_below_zero(); + new spell_igb_teleport_to_enemy_ship(); + new spell_igb_burning_pitch_selector(); + new spell_igb_burning_pitch(); + new spell_igb_rocket_artillery(); + new spell_igb_rocket_artillery_explosion(); + new spell_igb_gunship_fall_teleport(); + new spell_igb_check_for_players(); + new spell_igb_teleport_players_on_victory(); + new achievement_im_on_a_boat(); +} diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 30d520ce7bf..0d262110b5f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -560,7 +560,7 @@ class boss_the_lich_king : public CreatureScript me->VisitNearbyGridObject(333.0f, worker); // Reset any light override - SendLightOverride(0, 5000); + me->GetMap()->SetZoneOverrideLight(AREA_THE_FROZEN_THRONE, 0, 5000); } bool CanAIAttack(Unit const* target) const OVERRIDE @@ -593,7 +593,7 @@ class boss_the_lich_king : public CreatureScript case ACTION_START_ENCOUNTER: instance->SetBossState(DATA_THE_LICH_KING, IN_PROGRESS); Talk(SAY_LK_INTRO_1); - SendMusicToPlayers(MUSIC_FROZEN_THRONE); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_FROZEN_THRONE); // schedule talks me->SetStandState(UNIT_STAND_STATE_STAND); events.ScheduleEvent(EVENT_INTRO_MOVE_1, 4000); @@ -602,10 +602,10 @@ class boss_the_lich_king : public CreatureScript events.ScheduleEvent(EVENT_START_ATTACK, 5000); break; case ACTION_PLAY_MUSIC: - SendMusicToPlayers(MUSIC_FINAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_FINAL); break; case ACTION_RESTORE_LIGHT: - SendLightOverride(0, 5000); + me->GetMap()->SetZoneOverrideLight(AREA_THE_FROZEN_THRONE, 0, 5000); break; case ACTION_BREAK_FROSTMOURNE: me->CastSpell((Unit*)NULL, SPELL_SUMMON_BROKEN_FROSTMOURNE, TRIGGERED_IGNORE_CAST_IN_PROGRESS); @@ -687,7 +687,7 @@ class boss_the_lich_king : public CreatureScript events.Reset(); events.SetPhase(PHASE_OUTRO); summons.DespawnAll(); - SendMusicToPlayers(MUSIC_FURY_OF_FROSTMOURNE); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_FURY_OF_FROSTMOURNE); me->InterruptNonMeleeSpells(true); me->CastSpell((Unit*)NULL, SPELL_FURY_OF_FROSTMOURNE, TRIGGERED_NONE); me->SetWalk(true); @@ -739,8 +739,8 @@ class boss_the_lich_king : public CreatureScript { summon->CastSpell((Unit*)NULL, SPELL_BROKEN_FROSTMOURNE, true); - SendLightOverride(LIGHT_SOULSTORM, 10000); - SendWeather(WEATHER_STATE_BLACKSNOW); + me->GetMap()->SetZoneOverrideLight(AREA_THE_FROZEN_THRONE, LIGHT_SOULSTORM, 10000); + me->GetMap()->SetZoneWeather(AREA_THE_FROZEN_THRONE, WEATHER_STATE_BLACKSNOW, 0.5f); events.ScheduleEvent(EVENT_OUTRO_SOUL_BARRAGE, 5000, 0, PHASE_OUTRO); return; @@ -792,8 +792,8 @@ class boss_the_lich_king : public CreatureScript { if (spell->Id == REMORSELESS_WINTER_1 || spell->Id == REMORSELESS_WINTER_2) { - SendLightOverride(LIGHT_SNOWSTORM, 5000); - SendWeather(WEATHER_STATE_LIGHT_SNOW); + me->GetMap()->SetZoneOverrideLight(AREA_THE_FROZEN_THRONE, LIGHT_SNOWSTORM, 5000); + me->GetMap()->SetZoneWeather(AREA_THE_FROZEN_THRONE, WEATHER_STATE_LIGHT_SNOW, 0.5f); } } @@ -819,7 +819,7 @@ class boss_the_lich_king : public CreatureScript case POINT_CENTER_1: me->SetFacingTo(0.0f); Talk(SAY_LK_REMORSELESS_WINTER); - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); DoCast(me, SPELL_REMORSELESS_WINTER_1); events.DelayEvents(62500, EVENT_GROUP_BERSERK); // delay berserk timer, its not ticking during phase transitions events.ScheduleEvent(EVENT_QUAKE, 62500, 0, PHASE_TRANSITION); @@ -834,7 +834,7 @@ class boss_the_lich_king : public CreatureScript case POINT_CENTER_2: me->SetFacingTo(0.0f); Talk(SAY_LK_REMORSELESS_WINTER); - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); DoCast(me, SPELL_REMORSELESS_WINTER_2); summons.DespawnEntry(NPC_VALKYR_SHADOWGUARD); events.DelayEvents(62500, EVENT_GROUP_BERSERK); // delay berserk timer, its not ticking during phase transitions @@ -920,7 +920,7 @@ class boss_the_lich_king : public CreatureScript break; case EVENT_SUMMON_SHAMBLING_HORROR: DoCast(me, SPELL_SUMMON_SHAMBLING_HORROR); - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); events.ScheduleEvent(EVENT_SUMMON_SHAMBLING_HORROR, 60000, 0, PHASE_ONE); break; case EVENT_SUMMON_DRUDGE_GHOUL: @@ -980,18 +980,18 @@ class boss_the_lich_king : public CreatureScript events.SetPhase(PHASE_TWO); me->ClearUnitState(UNIT_STATE_CASTING); // clear state to ensure check in DoCastAOE passes DoCastAOE(SPELL_QUAKE); - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); Talk(SAY_LK_QUAKE); break; case EVENT_QUAKE_2: events.SetPhase(PHASE_THREE); me->ClearUnitState(UNIT_STATE_CASTING); // clear state to ensure check in DoCastAOE passes DoCastAOE(SPELL_QUAKE); - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); Talk(SAY_LK_QUAKE); break; case EVENT_SUMMON_VALKYR: - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); Talk(SAY_LK_SUMMON_VALKYR); DoCastAOE(SUMMON_VALKYR); events.ScheduleEvent(EVENT_SUMMON_VALKYR, urand(45000, 50000), 0, PHASE_TWO); @@ -1002,7 +1002,7 @@ class boss_the_lich_king : public CreatureScript events.SetPhase(PHASE_THREE); break; case EVENT_VILE_SPIRITS: - SendMusicToPlayers(MUSIC_SPECIAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); DoCastAOE(SPELL_VILE_SPIRITS); events.ScheduleEvent(EVENT_VILE_SPIRITS, urand(35000, 40000), EVENT_GROUP_VILE_SPIRITS, PHASE_THREE); break; @@ -1073,7 +1073,7 @@ class boss_the_lich_king : public CreatureScript case EVENT_OUTRO_RAISE_DEAD: DoCastAOE(SPELL_RAISE_DEAD); me->ClearUnitState(UNIT_STATE_CASTING); - SendMusicToPlayers(MUSIC_FINAL); + me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_FINAL); break; case EVENT_OUTRO_TALK_5: Talk(SAY_LK_OUTRO_5); @@ -1114,42 +1114,6 @@ class boss_the_lich_king : public CreatureScript } private: - void SendMusicToPlayers(uint32 musicId) const - { - WorldPacket data(SMSG_PLAY_MUSIC, 4); - data << uint32(musicId); - SendPacketToPlayers(&data); - } - - void SendLightOverride(uint32 overrideId, uint32 fadeInTime) const - { - WorldPacket data(SMSG_OVERRIDE_LIGHT, 12); - data << uint32(2488); // Light.dbc entry (map default) - data << uint32(overrideId); // Light.dbc entry (override) - data << uint32(fadeInTime); - SendPacketToPlayers(&data); - } - - void SendWeather(WeatherState weather) const - { - WorldPacket data(SMSG_WEATHER, 9); - data << uint32(weather); - data << float(0.5f); - data << uint8(0); - SendPacketToPlayers(&data); - } - - // Send packet to all players in The Frozen Throne - void SendPacketToPlayers(WorldPacket const* data) const - { - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - if (!players.isEmpty()) - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - if (Player* player = itr->GetSource()) - if (player->GetAreaId() == AREA_THE_FROZEN_THRONE) - player->GetSession()->SendPacket(data); - } - uint32 _necroticPlagueStack; uint32 _vileSpiritExplosions; }; @@ -2606,38 +2570,6 @@ class spell_the_lich_king_valkyr_target_search : public SpellScriptLoader } }; -class spell_the_lich_king_eject_all_passengers : public SpellScriptLoader -{ - public: - spell_the_lich_king_eject_all_passengers() : SpellScriptLoader("spell_the_lich_king_eject_all_passengers") { } - - class spell_the_lich_king_eject_all_passengers_SpellScript : public SpellScript - { - PrepareSpellScript(spell_the_lich_king_eject_all_passengers_SpellScript); - - bool Load() OVERRIDE - { - return GetCaster()->IsVehicle(); - } - - void HandleDummy(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - GetCaster()->GetVehicleKit()->RemoveAllPassengers(); - } - - void Register() OVERRIDE - { - OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_eject_all_passengers_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const OVERRIDE - { - return new spell_the_lich_king_eject_all_passengers_SpellScript(); - } -}; - class spell_the_lich_king_cast_back_to_caster : public SpellScriptLoader { public: @@ -3270,7 +3202,6 @@ void AddSC_boss_the_lich_king() new spell_the_lich_king_summon_into_air(); new spell_the_lich_king_soul_reaper(); new spell_the_lich_king_valkyr_target_search(); - new spell_the_lich_king_eject_all_passengers(); new spell_the_lich_king_cast_back_to_caster(); new spell_the_lich_king_life_siphon(); new spell_the_lich_king_vile_spirits(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 8e529ba0ee9..6aeab015b78 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -68,7 +68,7 @@ enum TeleporterSpells DEATHBRINGER_S_RISE_TELEPORT = 70858, UPPER_SPIRE_TELEPORT = 70859, FROZEN_THRONE_TELEPORT = 70860, - SINDRAGOSA_S_LAIR_TELEPORT = 70861, + SINDRAGOSA_S_LAIR_TELEPORT = 70861 }; enum DataTypes @@ -76,7 +76,7 @@ enum DataTypes // Encounter States/Boss GUIDs DATA_LORD_MARROWGAR = 0, DATA_LADY_DEATHWHISPER = 1, - DATA_GUNSHIP_EVENT = 2, + DATA_ICECROWN_GUNSHIP_BATTLE = 2, DATA_DEATHBRINGER_SAURFANG = 3, DATA_FESTERGUT = 4, DATA_ROTFACE = 5, @@ -116,6 +116,7 @@ enum DataTypes DATA_HIGHLORD_TIRION_FORDRING = 37, DATA_ARTHAS_PLATFORM = 38, DATA_TERENAS_MENETHIL = 39, + DATA_ENEMY_GUNSHIP = 40 }; enum CreaturesIds @@ -169,6 +170,33 @@ enum CreaturesIds NPC_REANIMATED_ADHERENT = 38010, NPC_VENGEFUL_SHADE = 38222, + // Icecrown Gunship Battle + NPC_MARTYR_STALKER_IGB_SAURFANG = 38569, + NPC_ALLIANCE_GUNSHIP_CANNON = 36838, + NPC_HORDE_GUNSHIP_CANNON = 36839, + NPC_SKYBREAKER_DECKHAND = 36970, + NPC_ORGRIMS_HAMMER_CREW = 36971, + NPC_IGB_HIGH_OVERLORD_SAURFANG = 36939, + NPC_IGB_MURADIN_BRONZEBEARD = 36948, + NPC_THE_SKYBREAKER = 37540, + NPC_ORGRIMS_HAMMER = 37215, + NPC_GUNSHIP_HULL = 37547, + NPC_TELEPORT_PORTAL = 37227, + NPC_TELEPORT_EXIT = 37488, + NPC_SKYBREAKER_SORCERER = 37116, + NPC_SKYBREAKER_RIFLEMAN = 36969, + NPC_SKYBREAKER_MORTAR_SOLDIER = 36978, + NPC_SKYBREAKER_MARINE = 36950, + NPC_SKYBREAKER_SERGEANT = 36961, + NPC_KOR_KRON_BATTLE_MAGE = 37117, + NPC_KOR_KRON_AXETHROWER = 36968, + NPC_KOR_KRON_ROCKETEER = 36982, + NPC_KOR_KRON_REAVER = 36957, + NPC_KOR_KRON_SERGEANT = 36960, + NPC_ZAFOD_BOOMBOX = 37184, + NPC_HIGH_CAPTAIN_JUSTIN_BARTLETT = 37182, + NPC_SKY_REAVER_KORM_BLACKSCAR = 37833, + // Deathbringer Saurfang NPC_DEATHBRINGER_SAURFANG = 37813, NPC_BLOOD_BEAST = 38508, @@ -285,7 +313,7 @@ enum CreaturesIds NPC_FROSTMOURNE_TRIGGER = 38584, // Generic - NPC_INVISIBLE_STALKER = 30298, + NPC_INVISIBLE_STALKER = 30298 }; enum GameObjectsIds @@ -305,6 +333,22 @@ enum GameObjectsIds GO_ORATORY_OF_THE_DAMNED_ENTRANCE = 201563, GO_LADY_DEATHWHISPER_ELEVATOR = 202220, + // Icecrown Gunship Battle - Horde raid + GO_ORGRIMS_HAMMER_H = 201812, + GO_THE_SKYBREAKER_H = 201811, + GO_GUNSHIP_ARMORY_H_10N = 202178, + GO_GUNSHIP_ARMORY_H_25N = 202180, + GO_GUNSHIP_ARMORY_H_10H = 202177, + GO_GUNSHIP_ARMORY_H_25H = 202179, + + // Icecrown Gunship Battle - Alliance raid + GO_ORGRIMS_HAMMER_A = 201581, + GO_THE_SKYBREAKER_A = 201580, + GO_GUNSHIP_ARMORY_A_10N = 201873, + GO_GUNSHIP_ARMORY_A_25N = 201874, + GO_GUNSHIP_ARMORY_A_10H = 201872, + GO_GUNSHIP_ARMORY_A_25H = 201875, + // Deathbringer Saurfang GO_SAURFANG_S_DOOR = 201825, GO_DEATHBRINGER_S_CACHE_10N = 202239, @@ -368,7 +412,7 @@ enum GameObjectsIds GO_DOODAD_ICECROWN_SNOWEDGEWARNING01 = 202190, GO_FROZEN_LAVAMAN = 202436, GO_LAVAMAN_PILLARS_CHAINED = 202437, - GO_LAVAMAN_PILLARS_UNCHAINED = 202438, + GO_LAVAMAN_PILLARS_UNCHAINED = 202438 }; enum AchievementCriteriaIds @@ -403,11 +447,15 @@ enum AchievementCriteriaIds CRITERIA_ONCE_BITTEN_TWICE_SHY_10N = 12780, CRITERIA_ONCE_BITTEN_TWICE_SHY_25N = 13012, CRITERIA_ONCE_BITTEN_TWICE_SHY_10V = 13011, - CRITERIA_ONCE_BITTEN_TWICE_SHY_25V = 13013, + CRITERIA_ONCE_BITTEN_TWICE_SHY_25V = 13013 }; enum SharedActions { + // Icecrown Gunship Battle + ACTION_ENEMY_GUNSHIP_TALK = -369390, + ACTION_EXIT_SHIP = -369391, + // Festergut ACTION_FESTERGUT_COMBAT = -366260, ACTION_FESTERGUT_GAS = -366261, @@ -431,7 +479,7 @@ enum SharedActions // The Lich King ACTION_RESTORE_LIGHT = -72262, - ACTION_FROSTMOURNE_INTRO = -36823, + ACTION_FROSTMOURNE_INTRO = -36823 }; enum WeekliesICC @@ -445,7 +493,7 @@ enum WeekliesICC QUEST_BLOOD_QUICKENING_10 = 24874, QUEST_BLOOD_QUICKENING_25 = 24879, QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_10 = 24872, - QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_25 = 24880, + QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_25 = 24880 }; enum WorldStatesICC @@ -454,12 +502,13 @@ enum WorldStatesICC WORLDSTATE_EXECUTION_TIME = 4904, WORLDSTATE_SHOW_ATTEMPTS = 4940, WORLDSTATE_ATTEMPTS_REMAINING = 4941, - WORLDSTATE_ATTEMPTS_MAX = 4942, + WORLDSTATE_ATTEMPTS_MAX = 4942 }; enum AreaIds { - AREA_THE_FROZEN_THRONE = 4859, + AREA_ICECROWN_CITADEL = 4812, + AREA_THE_FROZEN_THRONE = 4859 }; class spell_trigger_spell_from_caster : public SpellScriptLoader diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp index ad06bc1c485..b56e0dd6360 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp @@ -38,7 +38,7 @@ class icecrown_citadel_teleport : public GameObjectScript player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Oratory of the Damned.", GOSSIP_SENDER_ICC_PORT, ORATORY_OF_THE_DAMNED_TELEPORT); if (instance->GetBossState(DATA_LADY_DEATHWHISPER) == DONE) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Rampart of Skulls.", GOSSIP_SENDER_ICC_PORT, RAMPART_OF_SKULLS_TELEPORT); - if (instance->GetBossState(DATA_GUNSHIP_EVENT) == DONE) + if (instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == DONE) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Deathbringer's Rise.", GOSSIP_SENDER_ICC_PORT, DEATHBRINGER_S_RISE_TELEPORT); if (instance->GetData(DATA_COLDFLAME_JETS) == DONE) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Upper Spire.", GOSSIP_SENDER_ICC_PORT, UPPER_SPIRE_TELEPORT); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index edd80377d68..31daa92cbe8 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -15,23 +15,30 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ObjectMgr.h" -#include "ScriptMgr.h" +#include "AccountMgr.h" #include "InstanceScript.h" -#include "ScriptedCreature.h" #include "Map.h" -#include "PoolMgr.h" -#include "AccountMgr.h" -#include "icecrown_citadel.h" +#include "ObjectMgr.h" #include "Player.h" +#include "PoolMgr.h" +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "Transport.h" +#include "TransportMgr.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "icecrown_citadel.h" enum EventIds { + EVENT_PLAYERS_GUNSHIP_SPAWN = 22663, + EVENT_PLAYERS_GUNSHIP_COMBAT = 22664, + EVENT_PLAYERS_GUNSHIP_SAURFANG = 22665, + EVENT_ENEMY_GUNSHIP_COMBAT = 22860, + EVENT_ENEMY_GUNSHIP_DESPAWN = 22861, EVENT_QUAKE = 23437, EVENT_SECOND_REMORSELESS_WINTER = 23507, - EVENT_TELEPORT_TO_FROSMOURNE = 23617, + EVENT_TELEPORT_TO_FROSTMOURNE = 23617 }; enum TimedEvents @@ -39,6 +46,7 @@ enum TimedEvents EVENT_UPDATE_EXECUTION_TIME = 1, EVENT_QUAKE_SHATTER = 2, EVENT_REBUILD_PLATFORM = 3, + EVENT_RESPAWN_GUNSHIP = 4 }; DoorData const doorData[] = @@ -68,7 +76,7 @@ DoorData const doorData[] = {GO_SINDRAGOSA_SHORTCUT_EXIT_DOOR, DATA_SINDRAGOSA, DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, {GO_ICE_WALL, DATA_SINDRAGOSA, DOOR_TYPE_ROOM, BOUNDARY_SE }, {GO_ICE_WALL, DATA_SINDRAGOSA, DOOR_TYPE_ROOM, BOUNDARY_SW }, - {0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE}, // END + {0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE} // END }; // this doesnt have to only store questgivers, also can be used for related quest spawns @@ -89,7 +97,7 @@ WeeklyQuest const WeeklyQuestData[WeeklyNPCs] = {NPC_ALRIN_THE_AGILE, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening {NPC_INFILTRATOR_MINCHAR_BQ, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening {NPC_MINCHAR_BEAM_STALKER, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening - {NPC_VALITHRIA_DREAMWALKER_QUEST, {QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_10, QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_25}}, // Respite for a Tormented Soul + {NPC_VALITHRIA_DREAMWALKER_QUEST, {QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_10, QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_25}} // Respite for a Tormented Soul }; class instance_icecrown_citadel : public InstanceMapScript @@ -106,6 +114,9 @@ class instance_icecrown_citadel : public InstanceMapScript TeamInInstance = 0; HeroicAttempts = MaxHeroicAttempts; LadyDeathwisperElevatorGUID = 0; + GunshipGUID = 0; + EnemyGunshipGUID = 0; + GunshipArmoryGUID = 0; DeathbringerSaurfangGUID = 0; DeathbringerSaurfangDoorGUID = 0; DeathbringerSaurfangEventGUID = 0; @@ -164,6 +175,9 @@ class instance_icecrown_citadel : public InstanceMapScript { if (!TeamInInstance) TeamInInstance = player->GetTeam(); + + if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE && GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE) + SpawnGunship(); } void OnCreatureCreate(Creature* creature) OVERRIDE @@ -217,6 +231,10 @@ class instance_icecrown_citadel : public InstanceMapScript case NPC_DEATHBRINGER_SAURFANG: DeathbringerSaurfangGUID = creature->GetGUID(); break; + case NPC_ALLIANCE_GUNSHIP_CANNON: + case NPC_HORDE_GUNSHIP_CANNON: + creature->SetControlled(true, UNIT_STATE_ROOT); + break; case NPC_SE_HIGH_OVERLORD_SAURFANG: if (TeamInInstance == ALLIANCE) creature->UpdateEntry(NPC_SE_MURADIN_BRONZEBEARD, ALLIANCE, creature->GetCreatureData()); @@ -335,12 +353,60 @@ class instance_icecrown_citadel : public InstanceMapScript { uint8 diffIndex = uint8(instance->GetSpawnMode() & 1); if (!sPoolMgr->IsSpawnedObject<Quest>(WeeklyQuestData[questIndex].questId[diffIndex])) - entry = 0; + return 0; break; } } break; } + case NPC_HORDE_GUNSHIP_CANNON: + case NPC_ORGRIMS_HAMMER_CREW: + case NPC_SKY_REAVER_KORM_BLACKSCAR: + if (TeamInInstance == ALLIANCE) + return 0; + break; + case NPC_ALLIANCE_GUNSHIP_CANNON: + case NPC_SKYBREAKER_DECKHAND: + case NPC_HIGH_CAPTAIN_JUSTIN_BARTLETT: + if (TeamInInstance == HORDE) + return 0; + break; + case NPC_ZAFOD_BOOMBOX: + if (GameObjectTemplate const* go = sObjectMgr->GetGameObjectTemplate(GO_THE_SKYBREAKER_A)) + if ((TeamInInstance == ALLIANCE && data->mapid == go->moTransport.mapID) || + (TeamInInstance == HORDE && data->mapid != go->moTransport.mapID)) + return entry; + return 0; + case NPC_IGB_MURADIN_BRONZEBEARD: + if ((TeamInInstance == ALLIANCE && data->posX > 10.0f) || + (TeamInInstance == HORDE && data->posX < 10.0f)) + return entry; + return 0; + default: + break; + } + + return entry; + } + + uint32 GetGameObjectEntry(uint32 /*guidLow*/, uint32 entry) OVERRIDE + { + switch (entry) + { + case GO_GUNSHIP_ARMORY_H_10N: + case GO_GUNSHIP_ARMORY_H_25N: + case GO_GUNSHIP_ARMORY_H_10H: + case GO_GUNSHIP_ARMORY_H_25H: + if (TeamInInstance == ALLIANCE) + return 0; + break; + case GO_GUNSHIP_ARMORY_A_10N: + case GO_GUNSHIP_ARMORY_A_25N: + case GO_GUNSHIP_ARMORY_A_10H: + case GO_GUNSHIP_ARMORY_A_25H: + if (TeamInInstance == HORDE) + return 0; + break; default: break; } @@ -446,6 +512,20 @@ class instance_icecrown_citadel : public InstanceMapScript go->SetGoState(GO_STATE_READY); } break; + case GO_THE_SKYBREAKER_H: + case GO_ORGRIMS_HAMMER_A: + EnemyGunshipGUID = go->GetGUID(); + break; + case GO_GUNSHIP_ARMORY_H_10N: + case GO_GUNSHIP_ARMORY_H_25N: + case GO_GUNSHIP_ARMORY_H_10H: + case GO_GUNSHIP_ARMORY_H_25H: + case GO_GUNSHIP_ARMORY_A_10N: + case GO_GUNSHIP_ARMORY_A_25N: + case GO_GUNSHIP_ARMORY_A_10H: + case GO_GUNSHIP_ARMORY_A_25H: + GunshipArmoryGUID = go->GetGUID(); + break; case GO_SAURFANG_S_DOOR: DeathbringerSaurfangDoorGUID = go->GetGUID(); AddDoor(go, true); @@ -587,6 +667,10 @@ class instance_icecrown_citadel : public InstanceMapScript case GO_ICE_WALL: AddDoor(go, false); break; + case GO_THE_SKYBREAKER_A: + case GO_ORGRIMS_HAMMER_H: + GunshipGUID = 0; + break; default: break; } @@ -621,6 +705,10 @@ class instance_icecrown_citadel : public InstanceMapScript { switch (type) { + case DATA_ICECROWN_GUNSHIP_BATTLE: + return GunshipGUID; + case DATA_ENEMY_GUNSHIP: + return EnemyGunshipGUID; case DATA_DEATHBRINGER_SAURFANG: return DeathbringerSaurfangGUID; case DATA_SAURFANG_EVENT_NPC: @@ -691,7 +779,7 @@ class instance_icecrown_citadel : public InstanceMapScript switch (type) { case DATA_LADY_DEATHWHISPER: - SetBossState(DATA_GUNSHIP_EVENT, state); // TEMP HACK UNTIL GUNSHIP SCRIPTED + { if (state == DONE) { if (GameObject* elevator = instance->GetGameObject(LadyDeathwisperElevatorGUID)) @@ -699,7 +787,19 @@ class instance_icecrown_citadel : public InstanceMapScript elevator->SetUInt32Value(GAMEOBJECT_LEVEL, 0); elevator->SetGoState(GO_STATE_READY); } + + SpawnGunship(); + } + break; + } + case DATA_ICECROWN_GUNSHIP_BATTLE: + if (state == DONE) + { + if (GameObject* loot = instance->GetGameObject(GunshipArmoryGUID)) + loot->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN); } + else if (state == FAIL) + Events.ScheduleEvent(EVENT_RESPAWN_GUNSHIP, 30000); break; case DATA_DEATHBRINGER_SAURFANG: switch (state) @@ -846,6 +946,17 @@ class instance_icecrown_citadel : public InstanceMapScript return true; } + void SpawnGunship() + { + if (!GunshipGUID) + { + SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, NOT_STARTED); + uint32 gunshipEntry = TeamInInstance == HORDE ? GO_ORGRIMS_HAMMER_H : GO_THE_SKYBREAKER_A; + if (Transport* gunship = sTransportMgr->CreateTransport(gunshipEntry, 0, instance)) + GunshipGUID = gunship->GetGUID(); + } + } + void SetData(uint32 type, uint32 data) OVERRIDE { switch (type) @@ -1066,10 +1177,10 @@ class instance_icecrown_citadel : public InstanceMapScript return false; // no break case DATA_DEATHBRINGER_SAURFANG: - if (GetBossState(DATA_GUNSHIP_EVENT) != DONE) + if (GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE) return false; // no break - case DATA_GUNSHIP_EVENT: + case DATA_ICECROWN_GUNSHIP_BATTLE: if (GetBossState(DATA_LADY_DEATHWHISPER) != DONE) return false; // no break @@ -1163,7 +1274,7 @@ class instance_icecrown_citadel : public InstanceMapScript void Update(uint32 diff) OVERRIDE { - if (BloodQuickeningState != IN_PROGRESS && GetBossState(DATA_THE_LICH_KING) != IN_PROGRESS) + if (BloodQuickeningState != IN_PROGRESS && GetBossState(DATA_THE_LICH_KING) != IN_PROGRESS && GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != FAIL) return; Events.Update(diff); @@ -1213,16 +1324,42 @@ class instance_icecrown_citadel : public InstanceMapScript if (GameObject* wind = instance->GetGameObject(FrozenThroneWindGUID)) wind->SetGoState(GO_STATE_ACTIVE); break; + case EVENT_RESPAWN_GUNSHIP: + SpawnGunship(); + break; default: break; } } } - void ProcessEvent(WorldObject* /*source*/, uint32 eventId) OVERRIDE + void ProcessEvent(WorldObject* source, uint32 eventId) OVERRIDE { switch (eventId) { + case EVENT_ENEMY_GUNSHIP_DESPAWN: + if (GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == DONE) + source->AddObjectToRemoveList(); + break; + case EVENT_ENEMY_GUNSHIP_COMBAT: + if (Creature* captain = source->FindNearestCreature(TeamInInstance == HORDE ? NPC_IGB_HIGH_OVERLORD_SAURFANG : NPC_IGB_MURADIN_BRONZEBEARD, 100.0f)) + captain->AI()->DoAction(ACTION_ENEMY_GUNSHIP_TALK); + // no break; + case EVENT_PLAYERS_GUNSHIP_SPAWN: + case EVENT_PLAYERS_GUNSHIP_COMBAT: + if (GameObject* go = source->ToGameObject()) + if (Transport* transport = go->ToTransport()) + transport->EnableMovement(false); + break; + case EVENT_PLAYERS_GUNSHIP_SAURFANG: + { + if (Creature* captain = source->FindNearestCreature(TeamInInstance == HORDE ? NPC_IGB_HIGH_OVERLORD_SAURFANG : NPC_IGB_MURADIN_BRONZEBEARD, 100.0f)) + captain->AI()->DoAction(ACTION_EXIT_SHIP); + if (GameObject* go = source->ToGameObject()) + if (Transport* transport = go->ToTransport()) + transport->EnableMovement(false); + break; + } case EVENT_QUAKE: if (GameObject* warning = instance->GetGameObject(FrozenThroneWarningGUID)) warning->SetGoState(GO_STATE_ACTIVE); @@ -1235,7 +1372,7 @@ class instance_icecrown_citadel : public InstanceMapScript Events.ScheduleEvent(EVENT_REBUILD_PLATFORM, 1500); } break; - case EVENT_TELEPORT_TO_FROSMOURNE: // Harvest Soul (normal mode) + case EVENT_TELEPORT_TO_FROSTMOURNE: // Harvest Soul (normal mode) if (Creature* terenas = instance->SummonCreature(NPC_TERENAS_MENETHIL_FROSTMOURNE, TerenasSpawn, NULL, 63000)) { terenas->AI()->DoAction(ACTION_FROSTMOURNE_INTRO); @@ -1261,6 +1398,9 @@ class instance_icecrown_citadel : public InstanceMapScript protected: EventMap Events; uint64 LadyDeathwisperElevatorGUID; + uint64 GunshipGUID; + uint64 EnemyGunshipGUID; + uint64 GunshipArmoryGUID; uint64 DeathbringerSaurfangGUID; uint64 DeathbringerSaurfangDoorGUID; uint64 DeathbringerSaurfangEventGUID; // Muradin Bronzebeard or High Overlord Saurfang diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 7ae9ce3faa6..1df92618a3a 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -584,7 +584,7 @@ public: { instance->SetBossState(DATA_MALYGOS_EVENT, FAIL); - SendLightOverride(LIGHT_GET_DEFAULT_FOR_MAP, 1*IN_MILLISECONDS); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_GET_DEFAULT_FOR_MAP, 1*IN_MILLISECONDS); if (_phase == PHASE_THREE) me->SetControlled(false, UNIT_STATE_ROOT); @@ -725,8 +725,8 @@ public: me->SetDisableGravity(true); if (Creature* alexstraszaBunny = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ALEXSTRASZA_BUNNY_GUID))) me->SetFacingToObject(alexstraszaBunny); - SendLightOverride(LIGHT_ARCANE_RUNES, 5*IN_MILLISECONDS); - events.ScheduleEvent(EVENT_FLY_OUT_OF_PLATFORM, 18*IN_MILLISECONDS, 0, PHASE_TWO); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_ARCANE_RUNES, 5 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FLY_OUT_OF_PLATFORM, 18 * IN_MILLISECONDS, 0, PHASE_TWO); break; case POINT_SURGE_OF_POWER_P_TWO: if (!_performingDestroyPlatform) @@ -737,7 +737,7 @@ public: } break; case POINT_DESTROY_PLATFORM_P_TWO: - SendLightOverride(LIGHT_OBSCURE_SPACE, 1*IN_MILLISECONDS); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_OBSCURE_SPACE, 1 * IN_MILLISECONDS); DoCast(me, SPELL_DESTROY_PLATFORM_CHANNEL); events.ScheduleEvent(EVENT_MOVE_TO_P_THREE_POINT, 11*IN_MILLISECONDS, 0, PHASE_TWO); break; @@ -929,7 +929,7 @@ public: } break; case EVENT_LIGHT_DIMENSION_CHANGE: - SendLightOverride(LIGHT_CHANGE_DIMENSIONS, 2*IN_MILLISECONDS); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_CHANGE_DIMENSIONS, 2 * IN_MILLISECONDS); break; case EVENT_DELAY_MOVE_TO_DESTROY_P: me->GetMotionMaster()->MovePoint(POINT_DESTROY_PLATFORM_P_TWO, MalygosPositions[0]); @@ -939,7 +939,7 @@ public: me->GetMotionMaster()->MovePoint(POINT_IDLE_P_THREE, MalygosPositions[4]); break; case EVENT_START_P_THREE: - SendLightOverride(LIGHT_OBSCURE_ARCANE_RUNES, 1*IN_MILLISECONDS); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_OBSCURE_ARCANE_RUNES, 1 * IN_MILLISECONDS); DoCast(me, SPELL_CLEAR_ALL_DEBUFFS); DoCast(me, SPELL_IMMUNE_CURSES); _canAttack = true; @@ -1026,27 +1026,6 @@ public: } } - // Function that will change lights of map for all players on map. - void SendLightOverride(uint32 overrideId, uint32 fadeInTime) const - { - WorldPacket data(SMSG_OVERRIDE_LIGHT, 12); - data << uint32(1773); // Light.dbc entry (map default) - data << uint32(overrideId); // Light.dbc entry (override) - data << uint32(fadeInTime); - SendPacketToPlayers(&data); - } - - // Send packet to all players in Eye of Eternity - void SendPacketToPlayers(WorldPacket const* data) const - { - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - if (!players.isEmpty()) - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - if (Player* player = itr->GetSource()) - if (player->GetAreaId() == AREA_EYE_OF_ETERNITY) - player->GetSession()->SendPacket(data); - } - uint8 _phase; // Counter for phases used with a getter. uint8 _summonDeaths; // Keeps count of arcane trash. uint8 _preparingPulsesChecker; // In retail they use 2 preparing pulses with 7 sec CD, after they pass 2 seconds. diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index 2c809b17367..39dec9999cc 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -208,9 +208,11 @@ class instance_oculus : public InstanceMapScript break; case DATA_VAROS: if (state == DONE) + { DoUpdateWorldState(WORLD_STATE_CENTRIFUGE_CONSTRUCT_SHOW, 0); if (Creature* urom = instance->GetCreature(UromGUID)) urom->SetPhaseMask(1, true); + } break; case DATA_UROM: if (state == DONE) diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index f840c0562b3..f7f473297ef 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -44,6 +44,7 @@ EndContentData */ #include "Player.h" #include "SpellInfo.h" #include "WorldSession.h" +#include "SpellScript.h" /*###### ## npc_sinkhole_kill_credit @@ -2537,6 +2538,39 @@ public: }; +enum WindsoulTotemAura +{ + SPELL_WINDSOUL_CREDT = 46378 +}; + +class spell_windsoul_totem_aura : public SpellScriptLoader +{ +public: + spell_windsoul_totem_aura() : SpellScriptLoader("spell_windsoul_totem_aura") { } + + class spell_windsoul_totem_aura_AuraScript : public AuraScript + { + PrepareAuraScript(spell_windsoul_totem_aura_AuraScript); + + void OnRemove(AuraEffect const*, AuraEffectHandleModes) + { + if (GetTarget()->isDead()) + if (Unit* caster = GetCaster()) + caster->CastSpell(NULL, SPELL_WINDSOUL_CREDT); + } + + void Register() OVERRIDE + { + OnEffectRemove += AuraEffectRemoveFn(spell_windsoul_totem_aura_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const OVERRIDE + { + return new spell_windsoul_totem_aura_AuraScript(); + } +}; + void AddSC_borean_tundra() { new npc_sinkhole_kill_credit(); @@ -2565,4 +2599,5 @@ void AddSC_borean_tundra() new npc_valiance_keep_cannoneer(); new npc_warmage_coldarra(); new npc_hidden_cultist(); + new spell_windsoul_totem_aura(); } diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index 7487c3c8828..a9c46d19138 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -654,6 +654,7 @@ class npc_torturer_lecraft : public CreatureScript { npc_torturer_lecraftAI(Creature* creature) : ScriptedAI(creature) { + _textCounter = 1; _playerGUID = 0; } diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 1c6ec703f0c..bd80de627a8 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -454,6 +454,9 @@ public: { npc_brann_bronzebeard_keystoneAI(Creature* creature) : ScriptedAI(creature) { + memset(&objectGUID, 0, sizeof(objectGUID)); + playerGUID = 0; + voiceGUID = 0; objectCounter = 0; } @@ -640,6 +643,7 @@ public: { npc_king_jokkum_vehicleAI(Creature* creature) : VehicleAI(creature) { + playerGUID = 0; pathEnd = false; } @@ -788,37 +792,6 @@ class spell_veranus_summon : public SpellScriptLoader } }; -/*##### -# spell_jokkum_eject_all -#####*/ - -class spell_jokkum_eject_all : public SpellScriptLoader -{ - public: spell_jokkum_eject_all() : SpellScriptLoader("spell_jokkum_eject_all") { } - - class spell_jokkum_eject_all_SpellScript : public SpellScript - { - PrepareSpellScript(spell_jokkum_eject_all_SpellScript); - - void HandleScriptEffect(SpellEffIndex /* effIndex */) - { - if (Unit* caster = GetCaster()) - if (caster->IsVehicle()) - caster->GetVehicleKit()->RemoveAllPassengers(); - } - - void Register() OVERRIDE - { - OnEffectHitTarget += SpellEffectFn(spell_jokkum_eject_all_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const OVERRIDE - { - return new spell_jokkum_eject_all_SpellScript(); - } -}; - enum CloseRift { SPELL_DESPAWN_RIFT = 61665 @@ -878,6 +851,5 @@ void AddSC_storm_peaks() new npc_king_jokkum_vehicle(); new spell_jokkum_scriptcast(); new spell_veranus_summon(); - new spell_jokkum_eject_all(); new spell_close_rift(); } diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index 8f5ae0f65ff..0f542b3aca7 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -453,7 +453,11 @@ public: struct npc_alchemist_finklesteinAI : public ScriptedAI { - npc_alchemist_finklesteinAI(Creature* creature) : ScriptedAI(creature) { } + npc_alchemist_finklesteinAI(Creature* creature) : ScriptedAI(creature) + { + _playerGUID = 0; + _getingredienttry = 0; + } void Reset() OVERRIDE { diff --git a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp index 5266a7bd137..c9d452bfada 100644 --- a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp +++ b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp @@ -61,7 +61,11 @@ public: struct npc_stolen_soulAI : public ScriptedAI { - npc_stolen_soulAI(Creature* creature) : ScriptedAI(creature) { } + npc_stolen_soulAI(Creature* creature) : ScriptedAI(creature) + { + myClass = CLASS_NONE; + Class_Timer = 1000; + } uint8 myClass; uint32 Class_Timer; diff --git a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp index ca0ea947295..d92d8aaf5db 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp @@ -295,13 +295,13 @@ public: Timer = 1000; if (Phase == 3) { - if (!Essence->IsAlive()) + if (Essence && !Essence->IsAlive()) DoCast(me, 7, true); else return; } else { - if (Essence->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + if (Essence && Essence->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) { MergeThreatList(Essence); Essence->RemoveAllAuras(); @@ -312,31 +312,37 @@ public: break; case 4: Timer = 1500; - if (Essence->IsWithinDistInMap(me, 10)) + if (Essence) { - Essence->SetUInt32Value(UNIT_NPC_EMOTESTATE, 374); //rotate and disappear - Timer = 2000; - me->RemoveAurasDueToSpell(SPELL_SUBMERGE); - } - else - { - MergeThreatList(Essence); - Essence->RemoveAllAuras(); - Essence->DeleteThreatList(); - Essence->GetMotionMaster()->MoveFollow(me, 0, 0); - return; + if (Essence->IsWithinDistInMap(me, 10)) + { + Essence->SetUInt32Value(UNIT_NPC_EMOTESTATE, 374); //rotate and disappear + Timer = 2000; + me->RemoveAurasDueToSpell(SPELL_SUBMERGE); + } + else + { + MergeThreatList(Essence); + Essence->RemoveAllAuras(); + Essence->DeleteThreatList(); + Essence->GetMotionMaster()->MoveFollow(me, 0, 0); + return; + } } break; case 5: - if (Phase == 1) + if (Essence) { - Essence->AI()->Talk(SUFF_SAY_AFTER); - } - else - { - Essence->AI()->Talk(DESI_SAY_AFTER); + if (Phase == 1) + { + Essence->AI()->Talk(SUFF_SAY_AFTER); + } + else + { + Essence->AI()->Talk(DESI_SAY_AFTER); + } + Essence->DespawnOrUnsummon(); } - Essence->DespawnOrUnsummon(); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); EssenceGUID = 0; SoulCount = 0; diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp index e34e4ebdb23..09eb261282f 100644 --- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp +++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp @@ -48,9 +48,9 @@ enum Spells enum Events { - EVENT_STREAM_OF_MACHINE_FLUID = 0, - EVENT_JACKHAMMER = 1, - EVENT_SHADOW_POWER = 2 + EVENT_STREAM_OF_MACHINE_FLUID = 1, + EVENT_JACKHAMMER = 2, + EVENT_SHADOW_POWER = 3 }; class boss_gatewatcher_iron_hand : public CreatureScript diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp index bfc304e6a99..18388341a36 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_dalliah_the_doomsayer.cpp @@ -59,11 +59,16 @@ class boss_dalliah_the_doomsayer : public CreatureScript struct boss_dalliah_the_doomsayerAI : public BossAI { - boss_dalliah_the_doomsayerAI(Creature* creature) : BossAI(creature, DATA_DALLIAH) { } + boss_dalliah_the_doomsayerAI(Creature* creature) : BossAI(creature, DATA_DALLIAH) + { + soccothratesTaunt = false; + soccothratesDeath = false; + } void Reset() OVERRIDE { _Reset(); + soccothratesTaunt = false; soccothratesDeath = false; } diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp index 044be4c1534..6c96708c69d 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp @@ -82,7 +82,12 @@ class boss_wrath_scryer_soccothrates : public CreatureScript struct boss_wrath_scryer_soccothratesAI : public BossAI { - boss_wrath_scryer_soccothratesAI(Creature* creature) : BossAI(creature, DATA_SOCCOTHRATES) { } + boss_wrath_scryer_soccothratesAI(Creature* creature) : BossAI(creature, DATA_SOCCOTHRATES) + { + preFight = false; + dalliahTaunt = false; + dalliahDeath = false; + } void Reset() OVERRIDE { diff --git a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp index 3f1579c42b1..fdeaa1c2520 100644 --- a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp +++ b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp @@ -115,6 +115,7 @@ public: { npc_bloodmaul_bruteAI(Creature* creature) : ScriptedAI(creature) { + PlayerGUID = 0; hp30 = false; } diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index 72e7332381b..8542b32d631 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -728,7 +728,15 @@ public: struct npc_phase_hunterAI : public ScriptedAI { - npc_phase_hunterAI(Creature* creature) : ScriptedAI(creature) { } + npc_phase_hunterAI(Creature* creature) : ScriptedAI(creature) + { + Weak = false; + Materialize = false; + Drained = false; + WeakPercent = 25; + PlayerGUID = 0; + ManaBurnTimer = 5000; + } bool Weak; bool Materialize; diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index e8108f03e7d..c100630a452 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -197,7 +197,7 @@ class spell_dk_anti_magic_zone : public SpellScriptLoader void CalculateAmount(AuraEffect const* /*aurEff*/, int32 & amount, bool & /*canBeRecalculated*/) { - SpellInfo const* talentSpell = sSpellMgr->GetSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT); + SpellInfo const* talentSpell = sSpellMgr->EnsureSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT); amount = talentSpell->Effects[EFFECT_0].CalcValue(GetCaster()); if (Player* player = GetCaster()->ToPlayer()) amount += int32(2 * player->GetTotalAttackPowerValue(BASE_ATTACK)); @@ -1424,7 +1424,7 @@ class spell_dk_will_of_the_necropolis : public SpellScriptLoader { // min pct of hp is stored in effect 0 of talent spell uint8 rank = GetSpellInfo()->GetRank(); - SpellInfo const* talentProto = sSpellMgr->GetSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1, rank)); + SpellInfo const* talentProto = sSpellMgr->EnsureSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1, rank)); int32 remainingHp = int32(GetTarget()->GetHealth() - dmgInfo.GetDamage()); int32 minHp = int32(GetTarget()->CountPctFromMaxHealth(talentProto->Effects[EFFECT_0].CalcValue(GetCaster()))); diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 1e9a8713019..68c16a1b2d6 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -36,6 +36,7 @@ #include "SkillDiscovery.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "Vehicle.h" class spell_gen_absorb0_hitlimit1 : public SpellScriptLoader { @@ -1191,7 +1192,7 @@ class spell_gen_defend : public SpellScriptLoader void Register() OVERRIDE { - SpellInfo const* spell = sSpellMgr->GetSpellInfo(m_scriptSpellId); + SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(m_scriptSpellId); // Defend spells cast by NPCs (add visuals) if (spell->Effects[EFFECT_0].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN) @@ -2177,7 +2178,7 @@ class spell_gen_mounted_charge: public SpellScriptLoader void Register() OVERRIDE { - SpellInfo const* spell = sSpellMgr->GetSpellInfo(m_scriptSpellId); + SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(m_scriptSpellId); if (spell->HasEffect(SPELL_EFFECT_SCRIPT_EFFECT)) OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge_SpellScript::HandleScriptEffect, EFFECT_FIRST_FOUND, SPELL_EFFECT_SCRIPT_EFFECT); @@ -3673,6 +3674,33 @@ class spell_gen_whisper_gulch_yogg_saron_whisper : public SpellScriptLoader } }; +class spell_gen_eject_all_passengers : public SpellScriptLoader +{ + public: + spell_gen_eject_all_passengers() : SpellScriptLoader("spell_gen_eject_all_passengers") { } + + class spell_gen_eject_all_passengers_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gen_eject_all_passengers_SpellScript); + + void RemoveVehicleAuras() + { + if (Vehicle* vehicle = GetHitUnit()->GetVehicleKit()) + vehicle->RemoveAllPassengers(); + } + + void Register() OVERRIDE + { + AfterHit += SpellHitFn(spell_gen_eject_all_passengers_SpellScript::RemoveVehicleAuras); + } + }; + + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_gen_eject_all_passengers_SpellScript(); + } +}; + void AddSC_generic_spell_scripts() { new spell_gen_absorb0_hitlimit1(); @@ -3754,4 +3782,5 @@ void AddSC_generic_spell_scripts() new spell_gen_vendor_bark_trigger(); new spell_gen_wg_water(); new spell_gen_whisper_gulch_yogg_saron_whisper(); + new spell_gen_eject_all_passengers(); } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index ba59f701445..5f03c64eaf1 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -176,7 +176,7 @@ class spell_mage_cold_snap : public SpellScriptLoader const SpellCooldowns& cm = caster->GetSpellCooldownMap(); for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); if (spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) && @@ -388,7 +388,7 @@ class spell_mage_ignite : public SpellScriptLoader { PreventDefaultAction(); - SpellInfo const* igniteDot = sSpellMgr->GetSpellInfo(SPELL_MAGE_IGNITE); + SpellInfo const* igniteDot = sSpellMgr->EnsureSpellInfo(SPELL_MAGE_IGNITE); int32 pct = 8 * GetSpellInfo()->GetRank(); int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks()); diff --git a/src/server/scripts/Spells/spell_pet.cpp b/src/server/scripts/Spells/spell_pet.cpp index a7742630ccd..491bb7100b2 100644 --- a/src/server/scripts/Spells/spell_pet.cpp +++ b/src/server/scripts/Spells/spell_pet.cpp @@ -897,7 +897,7 @@ public: if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value AddPct(mod, spellInfo->Effects[EFFECT_0].CalcValue()); } @@ -940,7 +940,7 @@ public: if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value mod += CalculatePct(1.0f, spellInfo->Effects[EFFECT_1].CalcValue()); } @@ -970,7 +970,7 @@ public: if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value mod += CalculatePct(1.0f, spellInfo->Effects[EFFECT_1].CalcValue()); } diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 71e5dac28ec..f12a57aa2ec 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -236,7 +236,7 @@ class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader { PreventDefaultAction(); - SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL); + SpellInfo const* triggeredSpellInfo = sSpellMgr->EnsureSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL); int32 heal = int32(CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks()); GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, NULL, aurEff); } diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index cdf32bd94d5..d1b32570795 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -903,7 +903,7 @@ class spell_q12659_ahunaes_knife : public SpellScriptLoader enum StoppingTheSpread { NPC_VILLAGER_KILL_CREDIT = 18240, - SPELL_FLAMES = 39199, + SPELL_FLAMES = 39199 }; class spell_q9874_liquid_fire : public SpellScriptLoader @@ -926,7 +926,7 @@ class spell_q9874_liquid_fire : public SpellScriptLoader { Player* caster = GetCaster()->ToPlayer(); if (Creature* target = GetHitCreature()) - if (target && target->HasAura(SPELL_FLAMES)) + if (target && !target->HasAura(SPELL_FLAMES)) { caster->KilledMonsterCredit(NPC_VILLAGER_KILL_CREDIT, 0); target->CastSpell(target, SPELL_FLAMES, true); diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index d1d43684f3e..4c1a4d096b9 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -428,7 +428,7 @@ class spell_rog_preparation : public SpellScriptLoader const SpellCooldowns& cm = caster->GetSpellCooldownMap(); for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE) { diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 36e63b5a94e..0e3ada5d119 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -270,7 +270,7 @@ class spell_warl_demonic_circle_summon : public SpellScriptLoader // WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST; allowing him to cast the WARLOCK_DEMONIC_CIRCLE_TELEPORT. // If not in range remove the WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST. - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT); if (GetTarget()->IsWithinDist(circle, spellInfo->GetMaxRange(true))) { @@ -353,6 +353,7 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader if (targetCreature->IsPet()) { CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(targetCreature->GetEntry()); + ASSERT(ci); switch (ci->family) { case CREATURE_FAMILY_SUCCUBUS: @@ -360,7 +361,7 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader break; case CREATURE_FAMILY_VOIDWALKER: { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER); int32 hp = int32(targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(targetCreature, spellInfo, 0))); targetCreature->CastCustomSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER, &hp, NULL, NULL, true); //unitTarget->CastSpell(unitTarget, 54441, true); diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 5a655a92996..d0ba39e8b09 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -273,7 +273,7 @@ class spell_warr_deep_wounds : public SpellScriptLoader damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, SPELL_DIRECT_DAMAGE); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_PERIODIC); + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_RANK_PERIODIC); uint32 ticks = spellInfo->GetDuration() / spellInfo->Effects[EFFECT_0].Amplitude; // Add remaining ticks to damage done diff --git a/src/server/shared/Common.h b/src/server/shared/Common.h index 5dea589da25..fb153f2ba3e 100644 --- a/src/server/shared/Common.h +++ b/src/server/shared/Common.h @@ -71,12 +71,6 @@ #include <signal.h> #include <assert.h> -#if PLATFORM == PLATFORM_WINDOWS -#define STRCASECMP stricmp -#else -#define STRCASECMP strcasecmp -#endif - #include <set> #include <list> #include <string> @@ -114,8 +108,6 @@ #include <float.h> -#define I32FMT "%08I32X" -#define I64FMT "%016I64X" #define snprintf _snprintf #define atoll _atoi64 #define vsnprintf _vsnprintf @@ -126,8 +118,6 @@ #define stricmp strcasecmp #define strnicmp strncasecmp -#define I32FMT "%08X" -#define I64FMT "%016llX" #endif diff --git a/src/server/shared/Database/AdhocStatement.cpp b/src/server/shared/Database/AdhocStatement.cpp index 15732f20849..896fefde5b7 100644 --- a/src/server/shared/Database/AdhocStatement.cpp +++ b/src/server/shared/Database/AdhocStatement.cpp @@ -42,13 +42,13 @@ bool BasicStatementTask::Execute() if (m_has_result) { ResultSet* result = m_conn->Query(m_sql); - if (!result || !result->GetRowCount()) + if (!result || !result->GetRowCount() || !result->NextRow()) { delete result; m_result.set(QueryResult(NULL)); return false; } - result->NextRow(); + m_result.set(QueryResult(result)); return true; } diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h index 6c2e961d2ad..e2ebda9e8ae 100644 --- a/src/server/shared/Database/DatabaseWorkerPool.h +++ b/src/server/shared/Database/DatabaseWorkerPool.h @@ -232,13 +232,12 @@ class DatabaseWorkerPool ResultSet* result = conn->Query(sql); conn->Unlock(); - if (!result || !result->GetRowCount()) + if (!result || !result->GetRowCount() || !result->NextRow()) { delete result; return QueryResult(NULL); } - result->NextRow(); return QueryResult(result); } diff --git a/src/server/shared/Database/QueryHolder.cpp b/src/server/shared/Database/QueryHolder.cpp index 0c80f70acd2..7b4105ee076 100644 --- a/src/server/shared/Database/QueryHolder.cpp +++ b/src/server/shared/Database/QueryHolder.cpp @@ -89,10 +89,9 @@ QueryResult SQLQueryHolder::GetResult(size_t index) if (index < m_queries.size()) { ResultSet* result = m_queries[index].second.qresult; - if (!result || !result->GetRowCount()) + if (!result || !result->GetRowCount() || !result->NextRow()) return QueryResult(NULL); - result->NextRow(); return QueryResult(result); } else diff --git a/src/server/shared/Dynamic/LinkedList.h b/src/server/shared/Dynamic/LinkedList.h index 72d035c2cb6..402aaf3d40c 100644 --- a/src/server/shared/Dynamic/LinkedList.h +++ b/src/server/shared/Dynamic/LinkedList.h @@ -33,8 +33,8 @@ class LinkedListElement LinkedListElement* iNext; LinkedListElement* iPrev; public: - LinkedListElement(): iNext(NULL), iPrev(NULL) { } - ~LinkedListElement() { delink(); } + LinkedListElement() : iNext(NULL), iPrev(NULL) { } + virtual ~LinkedListElement() { delink(); } bool hasNext() const { return(iNext && iNext->iNext != NULL); } bool hasPrev() const { return(iPrev && iPrev->iPrev != NULL); } @@ -73,6 +73,10 @@ class LinkedListElement iNext->iPrev = pElem; iNext = pElem; } + + private: + LinkedListElement(LinkedListElement const&); + LinkedListElement& operator=(LinkedListElement const&); }; //============================================ @@ -83,6 +87,7 @@ class LinkedListHead LinkedListElement iFirst; LinkedListElement iLast; uint32 iSize; + public: LinkedListHead(): iSize(0) { @@ -92,6 +97,8 @@ class LinkedListHead iLast.iPrev = &iFirst; } + virtual ~LinkedListHead() { } + bool isEmpty() const { return(!iFirst.iNext->isInList()); } LinkedListElement * getFirst() { return(isEmpty() ? NULL : iFirst.iNext); } @@ -239,6 +246,10 @@ class LinkedListHead }; typedef Iterator<LinkedListElement> iterator; + + private: + LinkedListHead(LinkedListHead const&); + LinkedListHead& operator=(LinkedListHead const&); }; //============================================ diff --git a/src/server/shared/Dynamic/LinkedReference/Reference.h b/src/server/shared/Dynamic/LinkedReference/Reference.h index 1ba41f1f454..83a1dd155b1 100644 --- a/src/server/shared/Dynamic/LinkedReference/Reference.h +++ b/src/server/shared/Dynamic/LinkedReference/Reference.h @@ -94,6 +94,10 @@ template <class TO, class FROM> class Reference : public LinkedListElement TO* getTarget() const { return iRefTo; } FROM* GetSource() const { return iRefFrom; } + + private: + Reference(Reference const&); + Reference& operator=(Reference const&); }; //===================================================== diff --git a/src/server/shared/Logging/AppenderFile.cpp b/src/server/shared/Logging/AppenderFile.cpp index a88188266e2..f2532ad8bb8 100644 --- a/src/server/shared/Logging/AppenderFile.cpp +++ b/src/server/shared/Logging/AppenderFile.cpp @@ -16,6 +16,7 @@ */ #include "AppenderFile.h" +#include "Common.h" #if PLATFORM == PLATFORM_WINDOWS # include <Windows.h> @@ -80,6 +81,7 @@ FILE* AppenderFile::OpenFile(std::string const &filename, std::string const &mod std::string newName(fullName); newName.push_back('.'); newName.append(LogMessage::getTimeStr(time(NULL))); + std::replace(newName.begin(), newName.end(), ':', '-'); rename(fullName.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore } diff --git a/src/tools/vmap4_extractor/mpq_libmpq04.h b/src/tools/vmap4_extractor/mpq_libmpq04.h index d045abe307a..46cb50fb003 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq04.h +++ b/src/tools/vmap4_extractor/mpq_libmpq04.h @@ -36,7 +36,7 @@ public: mpq_archive_s *mpq_a; MPQArchive(const char* filename); - void close(); + ~MPQArchive() { close(); } void GetFileListTo(vector<string>& filelist) { uint32_t filenum; @@ -65,6 +65,9 @@ public: delete[] buffer; } + +private: + void close(); }; typedef std::deque<MPQArchive*> ArchiveSet; |