diff options
Diffstat (limited to 'src')
70 files changed, 2529 insertions, 1516 deletions
diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp index 1ab9ae6eb62..4a9d3ce7faf 100644 --- a/src/server/authserver/Server/AuthSocket.cpp +++ b/src/server/authserver/Server/AuthSocket.cpp @@ -222,12 +222,25 @@ void AuthSocket::OnClose(void) // Read the packet from the client void AuthSocket::OnRead() { + #define MAX_AUTH_LOGON_CHALLENGES_IN_A_ROW 3 + uint32 challengesInARow = 0; uint8 _cmd; while (1) { if (!socket().recv_soft((char *)&_cmd, 1)) return; + if (_cmd == AUTH_LOGON_CHALLENGE) + { + ++challengesInARow; + if (challengesInARow == MAX_AUTH_LOGON_CHALLENGES_IN_A_ROW) + { + TC_LOG_WARN(LOG_FILTER_AUTHSERVER, "Got %u AUTH_LOGON_CHALLENGE in a row from '%s', possible ongoing DoS", challengesInARow, socket().getRemoteAddress().c_str()); + socket().shutdown(); + return; + } + } + size_t i; // Circle through known commands and call the correct command handler diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 3823f7ba287..f01d0d1ab03 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -34,6 +34,7 @@ #include "SmartScript.h" #include "SpellMgr.h" #include "Vehicle.h" +#include "MoveSplineInit.h" #include "GameEventMgr.h" class TrinityStringTextBuilder @@ -507,18 +508,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) { - if (IsUnit(*itr)) + if (!IsUnit(*itr)) + continue; + + if (!(e.action.cast.flags & SMARTCAST_AURA_NOT_PRESENT) || !(*itr)->ToUnit()->HasAura(e.action.cast.spell)) { if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) me->InterruptNonMeleeSpells(false); - if (!(e.action.cast.flags & SMARTCAST_AURA_NOT_PRESENT) || !(*itr)->ToUnit()->HasAura(e.action.cast.spell)) - me->CastSpell((*itr)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED) ? true : false); - else - TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "Spell %u not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: " UI64FMTD " Entry: %u Type: %u) already has the aura", e.action.cast.spell, (*itr)->GetGUID(), (*itr)->GetEntry(), uint32((*itr)->GetTypeId())); + me->CastSpell((*itr)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED) ? true : false); TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "SmartScript::ProcessAction:: SMART_ACTION_CAST:: Creature %u casts spell %u on target %u with castflags %u", me->GetGUIDLow(), e.action.cast.spell, (*itr)->GetGUIDLow(), e.action.cast.flags); } + else + TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "Spell %u not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: " UI64FMTD " Entry: %u Type: %u) already has the aura", e.action.cast.spell, (*itr)->GetGUID(), (*itr)->GetEntry(), uint32((*itr)->GetTypeId())); } delete targets; @@ -536,19 +539,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) { - if (IsUnit(*itr)) - { - if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) - tempLastInvoker->InterruptNonMeleeSpells(false); + if (!IsUnit(*itr)) + continue; if (!(e.action.cast.flags & SMARTCAST_AURA_NOT_PRESENT) || !(*itr)->ToUnit()->HasAura(e.action.cast.spell)) + { + if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) + tempLastInvoker->InterruptNonMeleeSpells(false); + tempLastInvoker->CastSpell((*itr)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED) ? true : false); + TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "SmartScript::ProcessAction:: SMART_ACTION_INVOKER_CAST: Invoker %u casts spell %u on target %u with castflags %u", + tempLastInvoker->GetGUIDLow(), e.action.cast.spell, (*itr)->GetGUIDLow(), e.action.cast.flags); + } else TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "Spell %u not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: " UI64FMTD " Entry: %u Type: %u) already has the aura", e.action.cast.spell, (*itr)->GetGUID(), (*itr)->GetEntry(), uint32((*itr)->GetTypeId())); - - TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "SmartScript::ProcessAction:: SMART_ACTION_INVOKER_CAST: Invoker %u casts spell %u on target %u with castflags %u", - tempLastInvoker->GetGUIDLow(), e.action.cast.spell, (*itr)->GetGUIDLow(), e.action.cast.flags); - } } delete targets; @@ -1350,7 +1354,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; if (e.GetTargetType() == SMART_TARGET_SELF) - me->SetFacingTo(me->GetHomePosition().GetOrientation()); + me->SetFacingTo((me->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && me->GetTransGUID() ? + me->GetTransportHomePosition() : me->GetHomePosition()).GetOrientation()); else if (e.GetTargetType() == SMART_TARGET_POSITION) me->SetFacingTo(e.target.o); else if (ObjectList* targets = GetTargets(e, unit)) @@ -1403,7 +1408,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u } if (!target) - me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, e.target.x, e.target.y, e.target.z); + { + G3D::Vector3 dest(e.target.x, e.target.y, e.target.z); + if (e.action.MoveToPos.transport) + if (TransportBase* trans = me->GetDirectTransport()) + trans->CalculatePassengerPosition(dest.x, dest.y, dest.z); + + me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, dest.x, dest.y, dest.z); + } else me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); break; @@ -1659,21 +1671,28 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u for (ObjectList::const_iterator itr = casters->begin(); itr != casters->end(); ++itr) { - if (IsUnit(*itr)) + if (!IsUnit(*itr)) + continue; + + bool interruptedSpell = false; + + for (ObjectList::const_iterator it = targets->begin(); it != targets->end(); ++it) { - if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) - (*itr)->ToUnit()->InterruptNonMeleeSpells(false); + if (!IsUnit(*it)) + continue; - for (ObjectList::const_iterator it = targets->begin(); it != targets->end(); ++it) + if (!(e.action.cast.flags & SMARTCAST_AURA_NOT_PRESENT) || !(*it)->ToUnit()->HasAura(e.action.cast.spell)) { - if (IsUnit(*it)) + if (!interruptedSpell && e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) { - if (!(e.action.cast.flags & SMARTCAST_AURA_NOT_PRESENT) || !(*it)->ToUnit()->HasAura(e.action.cast.spell)) - (*itr)->ToUnit()->CastSpell((*it)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED) ? true : false); - else - TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "Spell %u not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: " UI64FMTD " Entry: %u Type: %u) already has the aura", e.action.cast.spell, (*it)->GetGUID(), (*it)->GetEntry(), uint32((*it)->GetTypeId())); + (*itr)->ToUnit()->InterruptNonMeleeSpells(false); + interruptedSpell = true; } + + (*itr)->ToUnit()->CastSpell((*it)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED) ? true : false); } + else + TC_LOG_DEBUG(LOG_FILTER_DATABASE_AI, "Spell %u not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: " UI64FMTD " Entry: %u Type: %u) already has the aura", e.action.cast.spell, (*it)->GetGUID(), (*it)->GetEntry(), uint32((*it)->GetTypeId())); } } diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 56fa7ed1275..f12f2ab69c0 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -907,6 +907,7 @@ struct SmartAction struct { uint32 pointId; + uint32 transport; } MoveToPos; struct diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 6d79c6c77a1..1cff80ba10d 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -466,7 +466,7 @@ void AccountMgr::LoadRBAC() secId = newId; permissions = &_defaultPermissions[secId]; } - + permissions->insert(field[1].GetUInt32()); ++count3; } diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp index 8cd70721976..f7c9444cdb0 100644 --- a/src/server/game/Accounts/RBAC.cpp +++ b/src/server/game/Accounts/RBAC.cpp @@ -231,13 +231,13 @@ void RBACData::ExpandPermissions(RBACPermissionContainer& permissions) {
RBACPermissionContainer toCheck = permissions;
permissions.clear();
-
+
while (!toCheck.empty())
{
// remove the permission from original list
uint32 permissionId = *toCheck.begin();
toCheck.erase(toCheck.begin());
-
+
RBACPermission const* permission = sAccountMgr->GetRBACPermission(permissionId);
if (!permission)
continue;
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index e12e6835cb8..bcc4122ea5b 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -303,7 +303,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_GOBJECT_SET_STATE = 397, RBAC_PERM_COMMAND_GOBJECT_TARGET = 398, RBAC_PERM_COMMAND_GOBJECT_TURN = 399, - // 400 - reuse + RBAC_PERM_COMMAND_DEBUG_TRANSPORT = 400, RBAC_PERM_COMMAND_GUILD = 401, RBAC_PERM_COMMAND_GUILD_CREATE = 402, RBAC_PERM_COMMAND_GUILD_DELETE = 403, diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index 29d4e4124c0..82d7de0d9bf 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -58,8 +58,17 @@ BattlegroundIC::BattlegroundIC() BattlegroundIC::~BattlegroundIC() { - delete gunshipHorde; - delete gunshipAlliance; + if (gunshipHorde) + { + gunshipHorde->RemoveFromWorld(); + delete gunshipHorde; + } + + if (gunshipAlliance) + { + gunshipAlliance->RemoveFromWorld(); + delete gunshipAlliance; + } } void BattlegroundIC::HandlePlayerResurrect(Player* player) @@ -71,22 +80,6 @@ void BattlegroundIC::HandlePlayerResurrect(Player* player) player->CastSpell(player, SPELL_OIL_REFINERY, true); } -void BattlegroundIC::SendTransportInit(Player* player) -{ - if (!gunshipAlliance || !gunshipHorde) - return; - - UpdateData transData; - - gunshipAlliance->BuildCreateUpdateBlockForPlayer(&transData, player); - gunshipHorde->BuildCreateUpdateBlockForPlayer(&transData, player); - - WorldPacket packet; - - transData.BuildPacket(&packet); - player->SendDirectMessage(&packet); -} - void BattlegroundIC::DoAction(uint32 action, uint64 var) { if (action != ACTION_TELEPORT_PLAYER_TO_TRANSPORT) @@ -300,8 +293,6 @@ void BattlegroundIC::AddPlayer(Player* player) if (nodePoint[NODE_TYPE_REFINERY].nodeState == (player->GetTeamId() == TEAM_ALLIANCE ? NODE_STATE_CONTROLLED_A : NODE_STATE_CONTROLLED_H)) player->CastSpell(player, SPELL_OIL_REFINERY, true); - - SendTransportInit(player); } void BattlegroundIC::RemovePlayer(Player* player, uint64 /*guid*/, uint32 /*team*/) @@ -409,8 +400,8 @@ bool BattlegroundIC::SetupBattleground() return false; } - gunshipHorde = CreateTransport(GO_HORDE_GUNSHIP, TRANSPORT_PERIOD_TIME); - gunshipAlliance = CreateTransport(GO_ALLIANCE_GUNSHIP, TRANSPORT_PERIOD_TIME); + gunshipHorde = sTransportMgr->CreateTransport(GO_HORDE_GUNSHIP, 0, GetBgMap()); + gunshipAlliance = sTransportMgr->CreateTransport(GO_ALLIANCE_GUNSHIP, 0, GetBgMap()); if (!gunshipAlliance || !gunshipHorde) { @@ -420,10 +411,8 @@ bool BattlegroundIC::SetupBattleground() //Send transport init packet to all player in map for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - { if (Player* player = ObjectAccessor::FindPlayer(itr->first)) - SendTransportInit(player); - } + GetBgMap()->SendInitTransports(player); // setting correct factions for Keep Cannons for (uint8 i = BG_IC_NPC_KEEP_CANNON_1; i < BG_IC_NPC_KEEP_CANNON_12; ++i) @@ -616,8 +605,8 @@ void BattlegroundIC::HandleContestedNodes(ICNodePoint* nodePoint) { if (nodePoint->nodeType == NODE_TYPE_HANGAR) { - if (gunshipAlliance && gunshipHorde) - (nodePoint->faction == TEAM_ALLIANCE ? gunshipHorde : gunshipAlliance)->BuildStopMovePacket(GetBgMap()); + if (gunshipAlliance && gunshipHorde) + (nodePoint->faction == TEAM_ALLIANCE ? gunshipHorde : gunshipAlliance)->EnableMovement(false); for (uint8 u = BG_IC_GO_HANGAR_TELEPORTER_1; u < BG_IC_GO_HANGAR_TELEPORTER_3; ++u) DelObject(u); @@ -656,8 +645,8 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture) //TC_LOG_ERROR(LOG_FILTER_BATTLEGROUND, "BG_IC_GO_HANGAR_BANNER CAPTURED Faction: %u", nodePoint->faction); - (nodePoint->faction == TEAM_ALLIANCE ? gunshipAlliance : gunshipHorde)->BuildStartMovePacket(GetBgMap()); - (nodePoint->faction == TEAM_ALLIANCE ? gunshipHorde : gunshipAlliance)->BuildStopMovePacket(GetBgMap()); + (nodePoint->faction == TEAM_ALLIANCE ? gunshipAlliance : gunshipHorde)->EnableMovement(true); + (nodePoint->faction == TEAM_ALLIANCE ? gunshipHorde : gunshipAlliance)->EnableMovement(false); // we should spawn teleporters break; case BG_IC_GO_QUARRY_BANNER: @@ -865,7 +854,6 @@ void BattlegroundIC::DestroyGate(Player* player, GameObject* go) void BattlegroundIC::EventPlayerDamagedGO(Player* /*player*/, GameObject* /*go*/, uint32 /*eventType*/) { - } WorldSafeLocsEntry const* BattlegroundIC::GetClosestGraveYard(Player* player) @@ -907,52 +895,6 @@ WorldSafeLocsEntry const* BattlegroundIC::GetClosestGraveYard(Player* player) return good_entry; } -Transport* BattlegroundIC::CreateTransport(uint32 goEntry, uint32 period) -{ - Transport* t = new Transport(period, 0); - - GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(goEntry); - - if (!goinfo) - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport ID: %u will not be loaded, gameobject_template missing", goEntry); - delete t; - return NULL; - } - - std::set<uint32> mapsUsed; - - if (!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed)) - // skip transports with empty waypoints list - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.", goinfo->moTransport.taxiPathId); - delete t; - return NULL; - } - - uint32 mapid = t->m_WayPoints[0].mapid; - - float x = t->m_WayPoints[0].x; - float y = t->m_WayPoints[0].y; - float z = t->m_WayPoints[0].z; - float o = 1; - - // creates the Gameobject - if (!t->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_MO_TRANSPORT), goEntry, mapid, x, y, z, o, 255, 0)) - { - delete t; - return NULL; - } - - //If we someday decide to use the grid to track transports, here: - t->SetMap(GetBgMap()); - - for (uint8 i = 0; i < 5; ++i) - t->AddNPCPassenger(0, (goEntry == GO_HORDE_GUNSHIP ? NPC_HORDE_GUNSHIP_CANNON : NPC_ALLIANCE_GUNSHIP_CANNON), (goEntry == GO_HORDE_GUNSHIP ? hordeGunshipPassengers[i].GetPositionX() : allianceGunshipPassengers[i].GetPositionX()), (goEntry == GO_HORDE_GUNSHIP ? hordeGunshipPassengers[i].GetPositionY() : allianceGunshipPassengers[i].GetPositionY()), (goEntry == GO_HORDE_GUNSHIP ? hordeGunshipPassengers[i].GetPositionZ() : allianceGunshipPassengers[i].GetPositionZ()), (goEntry == GO_HORDE_GUNSHIP ? hordeGunshipPassengers[i].GetOrientation() : allianceGunshipPassengers[i].GetOrientation())); - - return t; -} - bool BattlegroundIC::IsAllNodesControlledByTeam(uint32 team) const { uint32 count = 0; @@ -983,4 +925,4 @@ bool BattlegroundIC::IsSpellAllowed(uint32 spellId, Player const* player) const } return true; -}
\ No newline at end of file +} diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h index 6fdc97f25c5..acb5046444c 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h @@ -956,8 +956,6 @@ class BattlegroundIC : public Battleground void UpdateNodeWorldState(ICNodePoint* nodePoint); void HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture); void HandleContestedNodes(ICNodePoint* nodePoint); - Transport* CreateTransport(uint32 goEntry, uint32 period); - void SendTransportInit(Player* player); }; #endif diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index aaa630ce4b8..6d806336315 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -344,7 +344,7 @@ bool ChatHandler::ExecuteCommandInTable(ChatCommand* table, const char* text, st Player* player = m_session->GetPlayer(); if (!AccountMgr::IsPlayerAccount(m_session->GetSecurity())) { - uint64 guid = player->GetSelection(); + uint64 guid = player->GetTarget(); uint32 areaId = player->GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; @@ -713,12 +713,11 @@ Player* ChatHandler::getSelectedPlayer() if (!m_session) return NULL; - uint64 guid = m_session->GetPlayer()->GetSelection(); - - if (guid == 0) + uint64 selected = m_session->GetPlayer()->GetTarget(); + if (!selected) return m_session->GetPlayer(); - return ObjectAccessor::FindPlayer(guid); + return ObjectAccessor::FindPlayer(selected); } Unit* ChatHandler::getSelectedUnit() @@ -726,12 +725,10 @@ Unit* ChatHandler::getSelectedUnit() if (!m_session) return NULL; - uint64 guid = m_session->GetPlayer()->GetSelection(); - - if (guid == 0) - return m_session->GetPlayer(); + if (Unit* selected = m_session->GetPlayer()->GetSelectedUnit()) + return selected; - return ObjectAccessor::GetUnit(*m_session->GetPlayer(), guid); + return m_session->GetPlayer(); } WorldObject* ChatHandler::getSelectedObject() @@ -739,7 +736,7 @@ WorldObject* ChatHandler::getSelectedObject() if (!m_session) return NULL; - uint64 guid = m_session->GetPlayer()->GetSelection(); + uint64 guid = m_session->GetPlayer()->GetTarget(); if (guid == 0) return GetNearbyGameObject(); @@ -752,7 +749,7 @@ Creature* ChatHandler::getSelectedCreature() if (!m_session) return NULL; - return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(), m_session->GetPlayer()->GetSelection()); + return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(), m_session->GetPlayer()->GetTarget()); } char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1) diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 71fbf1c62b2..95592cd87d9 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -20,6 +20,7 @@ #include "Log.h" #include "SharedDefines.h" #include "SpellMgr.h" +#include "TransportMgr.h" #include "DBCfmt.h" #include "Timer.h" #include "ObjectDefines.h" @@ -190,6 +191,8 @@ static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt); DBCStorage <TeamContributionPointsEntry> sTeamContributionPointsStore(TeamContributionPointsfmt); DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt); +DBCStorage <TransportAnimationEntry> sTransportAnimationStore(TransportAnimationfmt); +DBCStorage <TransportRotationEntry> sTransportRotationStore(TransportRotationfmt); DBCStorage <VehicleEntry> sVehicleStore(VehicleEntryfmt); DBCStorage <VehicleSeatEntry> sVehicleSeatStore(VehicleSeatEntryfmt); DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore(WMOAreaTableEntryfmt); @@ -604,6 +607,25 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sTeamContributionPointsStore, dbcPath, "TeamContributionPoints.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sTotemCategoryStore, dbcPath, "TotemCategory.dbc"); + LoadDBC(availableDbcLocales, bad_dbc_files, sTransportAnimationStore, dbcPath, "TransportAnimation.dbc"); + for (uint32 i = 0; i < sTransportAnimationStore.GetNumRows(); ++i) + { + TransportAnimationEntry const* anim = sTransportAnimationStore.LookupEntry(i); + if (!anim) + continue; + + sTransportMgr->AddPathNodeToTransport(anim->TransportEntry, anim->TimeSeg, anim); + } + + LoadDBC(availableDbcLocales, bad_dbc_files, sTransportRotationStore, dbcPath, "TransportRotation.dbc"); + for (uint32 i = 0; i < sTransportRotationStore.GetNumRows(); ++i) + { + TransportRotationEntry const* rot = sTransportRotationStore.LookupEntry(i); + if (!rot) + continue; + + sTransportMgr->AddPathRotationToTransport(rot->TransportEntry, rot->TimeSeg, rot); + } LoadDBC(availableDbcLocales, bad_dbc_files, sVehicleStore, dbcPath, "Vehicle.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sVehicleSeatStore, dbcPath, "VehicleSeat.dbc"); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index e12f70baa41..aff54f75a40 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1875,6 +1875,28 @@ struct TotemCategoryEntry uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods) }; +struct TransportAnimationEntry +{ + //uint32 Id; + uint32 TransportEntry; + uint32 TimeSeg; + float X; + float Y; + float Z; + //uint32 MovementId; +}; + +struct TransportRotationEntry +{ + //uint32 Id; + uint32 TransportEntry; + uint32 TimeSeg; + float X; + float Y; + float Z; + float W; +}; + #define MAX_VEHICLE_SEATS 8 struct VehicleEntry diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index a81eec51338..5326ab70fa3 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -120,6 +120,8 @@ char const TaxiPathEntryfmt[] = "niii"; char const TaxiPathNodeEntryfmt[] = "diiifffiiii"; char const TeamContributionPointsfmt[] = "df"; char const TotemCategoryEntryfmt[] = "nxxxxxxxxxxxxxxxxxii"; +char const TransportAnimationfmt[] = "diifffx"; +char const TransportRotationfmt[] = "diiffff"; char const VehicleEntryfmt[] = "niffffiiiiiiiifffffffffffffffssssfifiixx"; char const VehicleSeatEntryfmt[] = "niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx"; char const WMOAreaTableEntryfmt[] = "niiixxxxxiixxxxxxxxxxxxxxxxx"; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 06f88f0f37f..f68d87de2f4 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -52,7 +52,7 @@ #include "World.h" #include "WorldPacket.h" -// apply implementation of the singletons +#include "Transport.h" TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const { @@ -141,7 +141,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) return true; } -Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapCreature(), +Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), @@ -165,6 +165,7 @@ m_creatureInfo(NULL), m_creatureData(NULL), m_path_id(0), m_formation(NULL) ResetLootMode(); // restore default loot mode TriggerJustRespawned = false; m_isTempWorldObject = false; + _focusSpell = NULL; } Creature::~Creature() @@ -933,7 +934,8 @@ void Creature::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnMask, GetPhaseMask()); + uint32 mapId = GetTransport() ? GetTransport()->GetGOInfo()->moTransport.mapID : GetMapId(); + SaveToDB(mapId, data->spawnMask, GetPhaseMask()); } void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) @@ -972,10 +974,21 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.phaseMask = phaseMask; data.displayid = displayId; data.equipmentId = GetCurrentEquipmentId(); - data.posX = GetPositionX(); - data.posY = GetPositionY(); - data.posZ = GetPositionZMinusOffset(); - data.orientation = GetOrientation(); + if (!GetTransport()) + { + data.posX = GetPositionX(); + data.posY = GetPositionY(); + data.posZ = GetPositionZMinusOffset(); + data.orientation = GetOrientation(); + } + else + { + data.posX = GetTransOffsetX(); + data.posY = GetTransOffsetY(); + data.posZ = GetTransOffsetZ(); + data.orientation = GetTransOffsetO(); + } + data.spawntimesecs = m_respawnDelay; // prevent add data integrity problems data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius; @@ -2640,3 +2653,41 @@ void Creature::SetDisplayId(uint32 modelId) SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach * GetObjectScale()); } } + +void Creature::SetTarget(uint64 guid) +{ + if (!_focusSpell) + SetUInt64Value(UNIT_FIELD_TARGET, guid); +} + +void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target) +{ + // already focused + if (_focusSpell) + return; + + _focusSpell = focusSpell; + SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID()); + if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) + AddUnitState(UNIT_STATE_ROTATING); + + // Set serverside orientation if needed (needs to be after attribute check) + SetInFront(target); +} + +void Creature::ReleaseFocus(Spell const* focusSpell) +{ + // focused to something else + if (focusSpell != _focusSpell) + return; + + _focusSpell = NULL; + if (Unit* victim = GetVictim()) + SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID()); + else + SetUInt64Value(UNIT_FIELD_TARGET, 0); + + if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) + ClearUnitState(UNIT_STATE_ROTATING); +} + diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index bb9cc40ace1..a555469da63 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -415,36 +415,7 @@ typedef std::map<uint32, time_t> CreatureSpellCooldowns; #define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY -enum CreatureCellMoveState -{ - CREATURE_CELL_MOVE_NONE, // not in move list - CREATURE_CELL_MOVE_ACTIVE, // in move list - CREATURE_CELL_MOVE_INACTIVE // in move list but should not move -}; - -class MapCreature -{ - friend class Map; // map for moving creatures - friend class ObjectGridLoader; // grid loader for loading creatures - -protected: - MapCreature() : _moveState(CREATURE_CELL_MOVE_NONE) {} - -private: - Cell _currentCell; - Cell const& GetCurrentCell() const { return _currentCell; } - void SetCurrentCell(Cell const& cell) { _currentCell = cell; } - - CreatureCellMoveState _moveState; - Position _newPosition; - void SetNewCellPosition(float x, float y, float z, float o) - { - _moveState = CREATURE_CELL_MOVE_ACTIVE; - _newPosition.Relocate(x, y, z, o); - } -}; - -class Creature : public Unit, public GridObject<Creature>, public MapCreature +class Creature : public Unit, public GridObject<Creature>, public MapObject { public: @@ -703,6 +674,11 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature bool m_isTempWorldObject; //true when possessed + // Handling caster facing during spellcast + void SetTarget(uint64 guid); + void FocusTarget(Spell const* focusSpell, WorldObject const* target); + void ReleaseFocus(Spell const* focusSpell); + protected: bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData* data = NULL); bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL); @@ -762,6 +738,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature //Formation var CreatureGroup* m_formation; bool TriggerJustRespawned; + + Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing }; class AssistDelayEvent : public BasicEvent diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 15e7eb436e1..af92f0e9df4 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -16,6 +16,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <G3D/Quat.h> #include "GameObjectAI.h" #include "Battleground.h" #include "CellImpl.h" @@ -32,8 +33,10 @@ #include "SpellMgr.h" #include "UpdateFieldFlags.h" #include "World.h" +#include "Transport.h" -GameObject::GameObject(): WorldObject(false), m_model(NULL), m_goValue(), m_AI(NULL) +GameObject::GameObject() : WorldObject(false), MapObject(), + m_model(NULL), m_goValue(), m_AI(NULL) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; @@ -100,6 +103,9 @@ void GameObject::CleanupsBeforeDelete(bool /*finalCleanup*/) if (m_uint32Values) // field array can be not exist if GameOBject not loaded RemoveFromOwner(); + + if (GetTransport() && !ToTransport()) + GetTransport()->RemovePassenger(this); } void GameObject::RemoveFromOwner() @@ -169,6 +175,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa SetMap(map); Relocate(x, y, z, ang); + m_stationaryPosition.Relocate(x, y, z, ang); if (!IsPositionValid()) { TC_LOG_ERROR(LOG_FILTER_GENERAL, "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, x, y); @@ -192,6 +199,9 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa return false; } + if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT) + m_updateFlag = (m_updateFlag | UPDATEFLAG_TRANSPORT) & ~UPDATEFLAG_POSITION; + Object::_Create(guidlow, goinfo->entry, HIGHGUID_GAMEOBJECT); m_goInfo = goinfo; @@ -238,9 +248,11 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa break; case GAMEOBJECT_TYPE_TRANSPORT: SetUInt32Value(GAMEOBJECT_LEVEL, goinfo->transport.pause); - if (goinfo->transport.startOpen) - SetGoState(GO_STATE_ACTIVE); + SetGoState(goinfo->transport.startOpen ? GO_STATE_ACTIVE : GO_STATE_READY); SetGoAnimProgress(animprogress); + m_goValue.Transport.PathProgress = 0; + m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(goinfo->entry); + m_goValue.Transport.CurrentSeg = 0; break; case GAMEOBJECT_TYPE_FISHINGNODE: SetGoAnimProgress(0); @@ -274,18 +286,10 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa void GameObject::Update(uint32 diff) { - if (!AI()) - { - if (!AIM_Initialize()) - TC_LOG_ERROR(LOG_FILTER_GENERAL, "Could not initialize GameObjectAI"); - } else + if (AI()) AI()->UpdateAI(diff); - - if (IS_MO_TRANSPORT_GUID(GetGUID())) - { - //((Transport*)this)->Update(p_time); - return; - } + else if (!AIM_Initialize()) + TC_LOG_ERROR(LOG_FILTER_GENERAL, "Could not initialize GameObjectAI"); switch (m_lootState) { @@ -308,6 +312,38 @@ 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) + break; + + if (GetGoState() == GO_STATE_READY) + { + m_goValue.Transport.PathProgress += diff; + 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) + { + m_goValue.Transport.CurrentSeg = node->TimeSeg; + + G3D::Quat rotation = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer); + G3D::Vector3 pos = rotation.toRotationMatrix() + * G3D::Matrix3::fromEulerAnglesZYX(GetOrientation(), 0.0f, 0.0f) + * G3D::Vector3(node->X, node->Y, node->Z); + + pos += G3D::Vector3(GetStationaryX(), GetStationaryY(), GetStationaryZ()); + + G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ()); + + sLog->outInfo(LOG_FILTER_GENERAL, "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) @@ -1182,10 +1218,12 @@ void GameObject::Use(Unit* user) if (itr->second) { if (Player* ChairUser = ObjectAccessor::FindPlayer(itr->second)) + { if (ChairUser->IsSitState() && ChairUser->getStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f) continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->getStandState() != UNIT_STAND_STATE_SIT check is required. else itr->second = 0; // This seat is unoccupied. + } else itr->second = 0; // The seat may of had an occupant, but they're offline. } @@ -1508,7 +1546,7 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); - Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection()); + Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetTarget()); // accept only use by player from same raid as caster, except caster itself if (!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameRaidWith(player)) @@ -2102,6 +2140,7 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (index == GAMEOBJECT_DYNAMIC) { uint16 dynFlags = 0; + int16 pathProgress = -1; switch (GetGoType()) { case GAMEOBJECT_TYPE_CHEST: @@ -2115,12 +2154,15 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (ActivateToQuest(target)) dynFlags |= GO_DYNFLAG_LO_SPARKLE; break; + case GAMEOBJECT_TYPE_MO_TRANSPORT: + pathProgress = int16(float(m_goValue.Transport.PathProgress) / float(GetUInt32Value(GAMEOBJECT_LEVEL)) * 65535.0f); + break; default: break; } fieldBuffer << uint16(dynFlags); - fieldBuffer << uint16(-1); + fieldBuffer << int16(pathProgress); } else if (index == GAMEOBJECT_FLAGS) { @@ -2140,3 +2182,25 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t updateMask.AppendToPacket(data); data->append(fieldBuffer); } + +void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = NULL*/) const +{ + if (m_DBTableGuid) + { + if (GameObjectData const* data = sObjectMgr->GetGOData(GetDBTableGUIDLow())) + { + x = data->posX; + y = data->posY; + z = data->posZ; + if (ori) + *ori = data->orientation; + return; + } + } + + x = GetPositionX(); + y = GetPositionY(); + z = GetPositionZ(); + if (ori) + *ori = GetOrientation(); +} diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 3bddac81ee9..de92257893b 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -28,6 +28,7 @@ class GameObjectAI; class Group; +class Transport; #define MAX_GAMEOBJECT_QUEST_ITEMS 6 @@ -229,6 +230,7 @@ struct GameObjectTemplate uint32 transportPhysics; //5 uint32 mapID; //6 uint32 worldState1; //7 + uint32 canBeStopped; //8 } moTransport; //16 GAMEOBJECT_TYPE_DUELFLAG - empty //17 GAMEOBJECT_TYPE_FISHINGNODE - empty @@ -539,9 +541,17 @@ struct GameObjectTemplate typedef UNORDERED_MAP<uint32, GameObjectTemplate> GameObjectTemplateContainer; class OPvPCapturePoint; +struct TransportAnimation; union GameObjectValue { + //11 GAMEOBJECT_TYPE_TRANSPORT + struct + { + uint32 PathProgress; + TransportAnimation const* AnimationInfo; + uint32 CurrentSeg; + } Transport; //25 GAMEOBJECT_TYPE_FISHINGHOLE struct { @@ -617,7 +627,7 @@ class GameObjectModel; // 5 sec for bobber catch #define FISHING_BOBBER_READY_TIME 5 -class GameObject : public WorldObject, public GridObject<GameObject> +class GameObject : public WorldObject, public GridObject<GameObject>, public MapObject { public: explicit GameObject(); @@ -811,8 +821,19 @@ class GameObject : public WorldObject, public GridObject<GameObject> uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); } GameObjectModel* m_model; + void GetRespawnPosition(float &x, float &y, float &z, float* ori = NULL) const; + + Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast<Transport*>(this); else return NULL; } + Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast<Transport const*>(this); else return NULL; } + + float GetStationaryX() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionX(); return GetPositionX(); } + float GetStationaryY() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionY(); return GetPositionY(); } + float GetStationaryZ() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionZ(); return GetPositionZ(); } + float GetStationaryO() const { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetOrientation(); return GetOrientation(); } + protected: bool AIM_Initialize(); + void UpdateModel(); // updates model in case displayId were changed uint32 m_spellId; time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()), uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer @@ -835,6 +856,7 @@ class GameObject : public WorldObject, public GridObject<GameObject> GameObjectValue m_goValue; uint64 m_rotation; + Position m_stationaryPosition; uint64 m_lootRecipient; uint32 m_lootRecipientGroup; @@ -842,7 +864,6 @@ class GameObject : public WorldObject, public GridObject<GameObject> private: void RemoveFromOwner(); void SwitchDoorOrButton(bool activate, bool alternative = false); - void UpdateModel(); // updates model in case displayId were changed //! Object distance/size - overridden from Object::_IsWithinDist. Needs to take in account proper GO size. bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool /*is3D*/) const diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 9d77e144c0b..1471e966045 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -219,9 +219,6 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c case GAMEOBJECT_TYPE_FLAGDROP: updateType = UPDATETYPE_CREATE_OBJECT2; break; - case GAMEOBJECT_TYPE_TRANSPORT: - flags |= UPDATEFLAG_TRANSPORT; - break; default: break; } @@ -414,13 +411,10 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x40 if (flags & UPDATEFLAG_STATIONARY_POSITION) { - *data << object->GetPositionX(); - *data << object->GetPositionY(); - if (isType(TYPEMASK_UNIT)) - *data << unit->GetPositionZMinusOffset(); - else - *data << object->GetPositionZ(); - *data << object->GetOrientation(); + *data << object->GetStationaryX(); + *data << object->GetStationaryY(); + *data << object->GetStationaryZ(); + *data << object->GetStationaryO(); } } } @@ -473,7 +467,11 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x2 if (flags & UPDATEFLAG_TRANSPORT) { - *data << uint32(getMSTime()); // Unknown - getMSTime is wrong. + GameObject const* go = ToGameObject(); + if (go && go->IsTransport()) + *data << uint32(go->GetGOValue()->Transport.PathProgress); + else + *data << uint32(getMSTime()); } // 0x80 @@ -2675,7 +2673,8 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle) // Prevent invalid coordinates here, position is unchanged if (!Trinity::IsValidMapCoord(destx, desty, pos.m_positionZ)) { - TC_LOG_FATAL(LOG_FILTER_GENERAL, "WorldObject::MovePosition invalid coordinates X: %f and Y: %f were passed!", destx, desty); + TC_LOG_FATAL(LOG_FILTER_GENERAL, "WorldObject::MovePosition: Object (TypeId: %u Entry: %u GUID: %u) has invalid coordinates X: %f and Y: %f were passed!", + GetTypeId(), GetEntry(), GetGUIDLow(), destx, desty); return; } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 66b3a737e0c..3e010fa98a2 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -523,6 +523,35 @@ class FlaggedValuesArray32 T_FLAGS m_flags; }; +enum MapObjectCellMoveState +{ + MAP_OBJECT_CELL_MOVE_NONE, //not in move list + MAP_OBJECT_CELL_MOVE_ACTIVE, //in move list + MAP_OBJECT_CELL_MOVE_INACTIVE, //in move list but should not move +}; + +class MapObject +{ + friend class Map; //map for moving creatures + friend class ObjectGridLoader; //grid loader for loading creatures + + protected: + MapObject() : _moveState(MAP_OBJECT_CELL_MOVE_NONE) {} + + private: + Cell _currentCell; + Cell const& GetCurrentCell() const { return _currentCell; } + void SetCurrentCell(Cell const& cell) { _currentCell = cell; } + + MapObjectCellMoveState _moveState; + Position _newPosition; + void SetNewCellPosition(float x, float y, float z, float o) + { + _moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; + _newPosition.Relocate(x, y, z, o); + } +}; + class WorldObject : public Object, public WorldLocation { protected: @@ -700,16 +729,22 @@ class WorldObject : public Object, public WorldLocation // Transports Transport* GetTransport() const { return m_transport; } - virtual float GetTransOffsetX() const { return 0; } - virtual float GetTransOffsetY() const { return 0; } - virtual float GetTransOffsetZ() const { return 0; } - virtual float GetTransOffsetO() const { return 0; } - virtual uint32 GetTransTime() const { return 0; } - virtual int8 GetTransSeat() const { return -1; } + float GetTransOffsetX() const { return m_movementInfo.transport.pos.GetPositionX(); } + float GetTransOffsetY() const { return m_movementInfo.transport.pos.GetPositionY(); } + float GetTransOffsetZ() const { return m_movementInfo.transport.pos.GetPositionZ(); } + float GetTransOffsetO() const { return m_movementInfo.transport.pos.GetOrientation(); } + uint32 GetTransTime() const { return m_movementInfo.transport.time; } + int8 GetTransSeat() const { return m_movementInfo.transport.seat; } virtual uint64 GetTransGUID() const; void SetTransport(Transport* t) { m_transport = t; } MovementInfo m_movementInfo; + + virtual float GetStationaryX() const { return GetPositionX(); } + virtual float GetStationaryY() const { return GetPositionY(); } + virtual float GetStationaryZ() const { return GetPositionZ(); } + virtual float GetStationaryO() const { return GetOrientation(); } + protected: std::string m_name; bool m_isActive; @@ -739,7 +774,6 @@ class WorldObject : public Object, public WorldLocation uint16 m_notifyflags; uint16 m_executed_notifies; - virtual bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D) const; bool CanNeverSee(WorldObject const* obj) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 09ae919618a..40dbe6c2fe7 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -673,7 +673,6 @@ Player::Player(WorldSession* session): Unit(true) if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS)) SetAcceptWhispers(true); - m_curSelection = 0; m_lootGuid = 0; m_comboTarget = 0; @@ -17183,17 +17182,15 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) } else { - for (MapManager::TransportSet::iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) + if (GameObject* go = HashMapHolder<GameObject>::Find(m_movementInfo.transport.guid)) + m_transport = go->ToTransport(); + + if (m_transport) { - if ((*iter)->GetGUIDLow() == transGUID) - { - m_transport = *iter; - m_transport->AddPassenger(this); - mapId = (m_transport->GetMapId()); - break; - } + m_transport->AddPassenger(this); + mapId = m_transport->GetMapId(); } - if (!m_transport) + else { TC_LOG_ERROR(LOG_FILTER_PLAYER, "Player (guidlow %d) have problems with transport guid (%u). Teleport to bind location.", guid, transGUID); @@ -22166,7 +22163,7 @@ inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target, std::set template<> inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target, std::set<Unit*>& /*v*/) { - // Don't update only GAMEOBJECT_TYPE_TRANSPORT (or all transports and destructible buildings?) + // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) s64.insert(target->GetGUID()); } @@ -22218,9 +22215,6 @@ void Player::UpdateVisibilityOf(WorldObject* target) { if (CanSeeOrDetect(target, false, true)) { - //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle) - // UpdateVisibilityOf(((Unit*)target)->m_Vehicle); - target->SendUpdateToPlayer(this); m_clientGUIDs.insert(target->GetGUID()); @@ -22309,9 +22303,6 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<Unit*>& vi { if (CanSeeOrDetect(target, false, true)) { - //if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle) - // UpdateVisibilityOf(((Unit*)target)->m_Vehicle, data, visibleNow); - target->BuildCreateUpdateBlockForPlayer(&data, this); UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow); @@ -22397,15 +22388,15 @@ bool Player::IsQuestRewarded(uint32 quest_id) const Unit* Player::GetSelectedUnit() const { - if (m_curSelection) - return ObjectAccessor::GetUnit(*this, m_curSelection); + if (uint64 selectionGUID = GetUInt64Value(UNIT_FIELD_TARGET)) + return ObjectAccessor::GetUnit(*this, selectionGUID); return NULL; } Player* Player::GetSelectedPlayer() const { - if (m_curSelection) - return ObjectAccessor::GetPlayer(*this, m_curSelection); + if (uint64 selectionGUID = GetUInt64Value(UNIT_FIELD_TARGET)) + return ObjectAccessor::GetPlayer(*this, selectionGUID); return NULL; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index bac794422be..fee542a752e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1506,10 +1506,11 @@ class Player : public Unit, public GridObject<Player> size_t GetRewardedQuestCount() const { return m_RewardedQuests.size(); } bool IsQuestRewarded(uint32 quest_id) const; - uint64 GetSelection() const { return m_curSelection; } Unit* GetSelectedUnit() const; Player* GetSelectedPlayer() const; - void SetSelection(uint64 guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); } + + void SetTarget(uint64 /*guid*/) OVERRIDE { } /// Used for serverside target changes, does not apply to players + void SetSelection(uint64 guid) { SetUInt64Value(UNIT_FIELD_TARGET, guid); } uint8 GetComboPoints() const { return m_comboPoints; } uint64 GetComboTarget() const { return m_comboTarget; } @@ -2424,7 +2425,6 @@ class Player : public Unit, public GridObject<Player> bool m_itemUpdateQueueBlocked; uint32 m_ExtraFlags; - uint64 m_curSelection; uint64 m_comboTarget; int8 m_comboPoints; diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 23197b40ad0..06af4244246 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -26,166 +26,26 @@ #include "DBCStores.h" #include "World.h" #include "GameObjectAI.h" +#include "Vehicle.h" +#include "MapReference.h" #include "Player.h" +#include "Cell.h" +#include "CellImpl.h" -void MapManager::LoadTransports() +Transport::Transport() : GameObject(), + _transportInfo(NULL), _isMoving(true), _pendingStop(false) { - uint32 oldMSTime = getMSTime(); - - QueryResult result = WorldDatabase.Query("SELECT guid, entry, name, period, ScriptName FROM transports"); - - if (!result) - { - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 transports. DB table `transports` is empty!"); - return; - } - - uint32 count = 0; - - do - { - - Field* fields = result->Fetch(); - uint32 lowguid = fields[0].GetUInt32(); - uint32 entry = fields[1].GetUInt32(); - std::string name = fields[2].GetString(); - uint32 period = fields[3].GetUInt32(); - uint32 scriptId = sObjectMgr->GetScriptId(fields[4].GetCString()); - - GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); - - if (!goinfo) - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str()); - continue; - } - - if (goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT) - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str()); - continue; - } - - // TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name); - - std::set<uint32> mapsUsed; - - Transport* t = new Transport(period, scriptId); - if (!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed)) - // skip transports with empty waypoints list - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.", goinfo->moTransport.taxiPathId); - delete t; - continue; - } - - float x = t->m_WayPoints[0].x; - float y = t->m_WayPoints[0].y; - float z = t->m_WayPoints[0].z; - uint32 mapid = t->m_WayPoints[0].mapid; - float o = 1.0f; - - // creates the Gameobject - if (!t->Create(lowguid, entry, mapid, x, y, z, o, 255, 0)) - { - delete t; - continue; - } - - m_Transports.insert(t); - - for (std::set<uint32>::const_iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i) - m_TransportsByMap[*i].insert(t); - - //If we someday decide to use the grid to track transports, here: - t->SetMap(sMapMgr->CreateBaseMap(mapid)); - t->AddToWorld(); - - ++count; - } - while (result->NextRow()); - - // check transport data DB integrity - result = WorldDatabase.Query("SELECT gameobject.guid, gameobject.id, transports.name FROM gameobject, transports WHERE gameobject.id = transports.entry"); - if (result) // wrong data found - { - do - { - Field* fields = result->Fetch(); - - uint32 guid = fields[0].GetUInt32(); - uint32 entry = fields[1].GetUInt32(); - std::string name = fields[2].GetString(); - TC_LOG_ERROR(LOG_FILTER_SQL, "Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports must not have any records in `gameobject` or its behavior will be unpredictable/bugged.", entry, name.c_str(), guid); - } - while (result->NextRow()); - } - - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded %u transports in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} - -void MapManager::LoadTransportNPCs() -{ - uint32 oldMSTime = getMSTime(); - - // 0 1 2 3 4 5 6 7 - QueryResult result = WorldDatabase.Query("SELECT guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO, emote FROM creature_transport"); - - if (!result) - { - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 transport NPCs. DB table `creature_transport` is empty!"); - return; - } - - uint32 count = 0; - - do - { - Field* fields = result->Fetch(); - uint32 guid = fields[0].GetInt32(); - uint32 entry = fields[1].GetInt32(); - uint32 transportEntry = fields[2].GetInt32(); - float tX = fields[3].GetFloat(); - float tY = fields[4].GetFloat(); - float tZ = fields[5].GetFloat(); - float tO = fields[6].GetFloat(); - uint32 anim = fields[7].GetInt32(); - - for (MapManager::TransportSet::iterator itr = m_Transports.begin(); itr != m_Transports.end(); ++itr) - { - if ((*itr)->GetEntry() == transportEntry) - { - (*itr)->AddNPCPassenger(guid, entry, tX, tY, tZ, tO, anim); - break; - } - } - - ++count; - } - while (result->NextRow()); - - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded %u transport npcs in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} - -Transport::Transport(uint32 period, uint32 script) : GameObject(), m_pathTime(0), m_timer(0), -currenttguid(0), m_period(period), ScriptId(script), m_nextNodeTime(0) -{ - m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION); + m_updateFlag = UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION; } Transport::~Transport() { - for (CreatureSet::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end(); ++itr) - { - (*itr)->SetTransport(NULL); - GetMap()->AddObjectToRemoveList(*itr); - } + UnloadStaticPassengers(); } -bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags) +bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress) { Relocate(x, y, z, ang); - // instance id and phaseMask isn't set to values different from std. if (!IsPositionValid()) { @@ -206,520 +66,516 @@ bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, floa m_goInfo = goinfo; - SetObjectScale(goinfo->size); + TransportTemplate const* tInfo = sTransportMgr->GetTransportTemplate(entry); + if (!tInfo) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "Transport %u (name: %s) will not be created, missing `transport_template` entry.", entry, goinfo->name.c_str()); + return false; + } + + _transportInfo = tInfo; + // initialize waypoints + _nextFrame = tInfo->keyFrames.begin(); + _currentFrame = _nextFrame++; + _triggeredArrivalEvent = false; + _triggeredDepartureEvent = false; + + m_goValue.Transport.PathProgress = 0; + SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size); SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); - SetUInt32Value(GAMEOBJECT_LEVEL, m_period); + SetPeriod(tInfo->pathTime); SetEntry(goinfo->entry); - SetDisplayId(goinfo->displayId); - SetGoState(GO_STATE_READY); - SetGoType(GameobjectTypes(goinfo->type)); - + _pendingStop = goinfo->moTransport.canBeStopped != 0; + SetGoType(GAMEOBJECT_TYPE_MO_TRANSPORT); SetGoAnimProgress(animprogress); - if (dynflags) - SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags)); - SetName(goinfo->name); - - SetZoneScript(); - + UpdateRotationFields(0.0f, 1.0f); return true; } -struct keyFrame +void Transport::Update(uint32 diff) { - explicit keyFrame(TaxiPathNodeEntry const& _node) : node(&_node), - distSinceStop(-1.0f), distUntilStop(-1.0f), distFromPrev(-1.0f), tFrom(0.0f), tTo(0.0f) - { - } + uint32 const positionUpdateDelay = 200; - TaxiPathNodeEntry const* node; + if (AI()) + AI()->UpdateAI(diff); + else if (!AIM_Initialize()) + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Could not initialize GameObjectAI for Transport"); - float distSinceStop; - float distUntilStop; - float distFromPrev; - float tFrom, tTo; -}; + if (GetKeyFrames().size() <= 1) + return; -bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids) -{ - if (pathid >= sTaxiPathNodesByPath.size()) - return false; + m_goValue.Transport.PathProgress += diff; - TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathid]; + uint32 timer = m_goValue.Transport.PathProgress % GetPeriod(); - std::vector<keyFrame> keyFrames; - int mapChange = 0; - mapids.clear(); - for (size_t i = 1; i < path.size() - 1; ++i) + // Set current waypoint + // Desired outcome: _currentFrame->DepartureTime < timer < _nextFrame->ArriveTime + // ... arrive | ... delay ... | departure + // event / event / + for (;;) { - if (mapChange == 0) + if (timer >= _currentFrame->ArriveTime) { - TaxiPathNodeEntry const& node_i = path[i]; - if (node_i.mapid == path[i+1].mapid) + if (!_triggeredArrivalEvent) { - keyFrame k(node_i); - keyFrames.push_back(k); - mapids.insert(k.node->mapid); + DoEventIfAny(*_currentFrame, false); + _triggeredArrivalEvent = true; } - else + + if (timer < _currentFrame->DepartureTime) { - mapChange = 1; + SetMoving(false); + if (_pendingStop) + SetGoState(GO_STATE_READY); + break; // its a stop frame and we are waiting } } - else - { - --mapChange; - } - } - - int lastStop = -1; - int firstStop = -1; - // first cell is arrived at by teleportation :S - keyFrames[0].distFromPrev = 0; - if (keyFrames[0].node->actionFlag == 2) - { - lastStop = 0; - } - - // find the rest of the distances between key points - for (size_t i = 1; i < keyFrames.size(); ++i) - { - if ((keyFrames[i].node->actionFlag == 1) || (keyFrames[i].node->mapid != keyFrames[i-1].node->mapid)) + if (_pendingStop && timer >= _currentFrame->DepartureTime && GetGoState() == GO_STATE_READY) { - keyFrames[i].distFromPrev = 0; + m_goValue.Transport.PathProgress = (m_goValue.Transport.PathProgress / GetPeriod()); + m_goValue.Transport.PathProgress *= GetPeriod(); + m_goValue.Transport.PathProgress += _currentFrame->ArriveTime; + break; } - else - { - keyFrames[i].distFromPrev = - sqrt(pow(keyFrames[i].node->x - keyFrames[i - 1].node->x, 2) + - pow(keyFrames[i].node->y - keyFrames[i - 1].node->y, 2) + - pow(keyFrames[i].node->z - keyFrames[i - 1].node->z, 2)); - } - if (keyFrames[i].node->actionFlag == 2) + + if (timer >= _currentFrame->DepartureTime && !_triggeredDepartureEvent) { - // remember first stop frame - if (firstStop == -1) - firstStop = i; - lastStop = i; + DoEventIfAny(*_currentFrame, true); // departure event + _triggeredDepartureEvent = true; } - } - - float tmpDist = 0; - for (size_t i = 0; i < keyFrames.size(); ++i) - { - int j = (i + lastStop) % keyFrames.size(); - if (keyFrames[j].node->actionFlag == 2) - tmpDist = 0; - else - tmpDist += keyFrames[j].distFromPrev; - keyFrames[j].distSinceStop = tmpDist; - } - for (int i = int(keyFrames.size()) - 1; i >= 0; i--) - { - int j = (i + (firstStop+1)) % keyFrames.size(); - tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev; - keyFrames[j].distUntilStop = tmpDist; - if (keyFrames[j].node->actionFlag == 2) - tmpDist = 0; - } + if (timer >= _currentFrame->DepartureTime && timer < _currentFrame->NextArriveTime) + break; // found current waypoint - for (size_t i = 0; i < keyFrames.size(); ++i) - { - if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f)) - keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop); - else - keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30; + MoveToNextWaypoint(); - if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f)) - keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop); - else - keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30; + // not waiting anymore + SetMoving(true); - keyFrames[i].tFrom *= 1000; - keyFrames[i].tTo *= 1000; - } - - // for (int i = 0; i < keyFrames.size(); ++i) { - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo); - // } + // Enable movement + if (GetGOInfo()->moTransport.canBeStopped) + SetGoState(GO_STATE_ACTIVE); - // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals - // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2 - int t = 0; - bool teleport = false; - if (keyFrames[keyFrames.size() - 1].node->mapid != keyFrames[0].node->mapid) - teleport = true; + // Departure event + if (_currentFrame->IsTeleportFrame()) + TeleportTransport(_nextFrame->Node->mapid, _nextFrame->Node->x, _nextFrame->Node->y, _nextFrame->Node->z); - m_WayPoints[0] = WayPoint(keyFrames[0].node->mapid, keyFrames[0].node->x, keyFrames[0].node->y, keyFrames[0].node->z, teleport, 0, - keyFrames[0].node->arrivalEventID, keyFrames[0].node->departureEventID); + sScriptMgr->OnRelocate(this, _currentFrame->Node->index, _currentFrame->Node->mapid, _currentFrame->Node->x, _currentFrame->Node->y, _currentFrame->Node->z); - t += keyFrames[0].node->delay * 1000; + TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "Transport %u (%s) moved to node %u %u %f %f %f", GetEntry(), GetName().c_str(), _currentFrame->Node->index, _currentFrame->Node->mapid, _currentFrame->Node->x, _currentFrame->Node->y, _currentFrame->Node->z); + } - uint32 cM = keyFrames[0].node->mapid; - for (size_t i = 0; i < keyFrames.size() - 1; ++i) + // Set position + _positionChangeTimer.Update(diff); + if (_positionChangeTimer.Passed()) { - float d = 0; - float tFrom = keyFrames[i].tFrom; - float tTo = keyFrames[i].tTo; - - // keep the generation of all these points; we use only a few now, but may need the others later - if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))) + _positionChangeTimer.Reset(positionUpdateDelay); + if (IsMoving()) { - while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)) - { - tFrom += 100; - tTo -= 100; - - if (d > 0) - { - float newX = keyFrames[i].node->x + (keyFrames[i + 1].node->x - keyFrames[i].node->x) * d / keyFrames[i + 1].distFromPrev; - float newY = keyFrames[i].node->y + (keyFrames[i + 1].node->y - keyFrames[i].node->y) * d / keyFrames[i + 1].distFromPrev; - float newZ = keyFrames[i].node->z + (keyFrames[i + 1].node->z - keyFrames[i].node->z) * d / keyFrames[i + 1].distFromPrev; - - teleport = false; - if (keyFrames[i].node->mapid != cM) - { - teleport = true; - cM = keyFrames[i].node->mapid; - } - - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ); - if (teleport) - m_WayPoints[t] = WayPoint(keyFrames[i].node->mapid, newX, newY, newZ, teleport, 0); - } - - if (tFrom < tTo) // caught in tFrom dock's "gravitational pull" - { - if (tFrom <= 30000) - { - d = 0.5f * (tFrom / 1000) * (tFrom / 1000); - } - else - { - d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000); - } - d = d - keyFrames[i].distSinceStop; - } - else - { - if (tTo <= 30000) - { - d = 0.5f * (tTo / 1000) * (tTo / 1000); - } - else - { - d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000); - } - d = keyFrames[i].distUntilStop - d; - } - t += 100; - } - t -= 100; + float t = CalculateSegmentPos(float(timer) * 0.001f); + G3D::Vector3 pos, dir; + _currentFrame->Spline->evaluate_percent(_currentFrame->Index, t, pos); + //_currentFrame->Spline->evaluate_derivative(_currentFrame->Index, t, dir); + UpdatePosition(pos.x, pos.y, pos.z, 0.0f/*atan2(dir.x, dir.y)*/); } - - if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo) - t += 100 - ((long)keyFrames[i + 1].tTo % 100); - else - t += (long)keyFrames[i + 1].tTo % 100; - - teleport = false; - if ((keyFrames[i + 1].node->actionFlag == 1) || (keyFrames[i + 1].node->mapid != keyFrames[i].node->mapid)) - { - teleport = true; - cM = keyFrames[i + 1].node->mapid; - } - - m_WayPoints[t] = WayPoint(keyFrames[i + 1].node->mapid, keyFrames[i + 1].node->x, keyFrames[i + 1].node->y, keyFrames[i + 1].node->z, teleport, - 0, keyFrames[i + 1].node->arrivalEventID, keyFrames[i + 1].node->departureEventID); - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport); - - t += keyFrames[i + 1].node->delay * 1000; } - uint32 timer = t; - - // TC_LOG_INFO(LOG_FILTER_TRANSPORTS, " Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer); - - m_curr = m_WayPoints.begin(); - m_next = GetNextWayPoint(); - m_pathTime = timer; + sScriptMgr->OnTransportUpdate(this, diff); +} - m_nextNodeTime = m_curr->first; +void Transport::AddPassenger(WorldObject* passenger) +{ + if (_passengers.insert(passenger).second) + TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "Object %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); - return true; + if (Player* plr = passenger->ToPlayer()) + sScriptMgr->OnAddPassenger(this, plr); } -Transport::WayPointMap::const_iterator Transport::GetNextWayPoint() +void Transport::RemovePassenger(WorldObject* passenger) { - WayPointMap::const_iterator iter = m_curr; - ++iter; - if (iter == m_WayPoints.end()) - iter = m_WayPoints.begin(); - return iter; + if (_passengers.erase(passenger) || _staticPassengers.erase(passenger)) // static passenger can remove itself in case of grid unload + TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "Object %s removed from transport %s.", passenger->GetName().c_str(), GetName().c_str()); + + + if (Player* plr = passenger->ToPlayer()) + sScriptMgr->OnRemovePassenger(this, plr); } -void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) +Creature* Transport::CreateNPCPassenger(uint32 guid, CreatureData const* data) { - Map const* oldMap = GetMap(); - Relocate(x, y, z); + Map* map = GetMap(); + Creature* creature = new Creature(); - for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();) + if (!creature->LoadCreatureFromDB(guid, map, false)) { - Player* player = *itr; - ++itr; - - if (player->isDead() && !player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) - player->ResurrectPlayer(1.0f); - - player->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); + delete creature; + return NULL; } - //we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference... - //player far teleport would try to create same instance, but we need it NOW for transport... + float x = data->posX; + float y = data->posY; + float z = data->posZ; + float o = data->orientation; - RemoveFromWorld(); - ResetMap(); - Map* newMap = sMapMgr->CreateBaseMap(newMapid); - SetMap(newMap); - ASSERT(GetMap()); - AddToWorld(); + creature->SetTransport(this); + creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + creature->m_movementInfo.transport.guid = GetGUID(); + creature->m_movementInfo.transport.pos.Relocate(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + creature->Relocate(x, y, z, o); + creature->SetHomePosition(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); + creature->SetTransportHomePosition(creature->m_movementInfo.transport.pos); - if (oldMap != newMap) + if (!creature->IsPositionValid()) { - UpdateForMap(oldMap); - UpdateForMap(newMap); + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Creature (guidlow %d, entry %d) not created. Suggested coordinates aren't valid (X: %f Y: %f)",creature->GetGUIDLow(),creature->GetEntry(),creature->GetPositionX(),creature->GetPositionY()); + delete creature; + return NULL; } - for (CreatureSet::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end(); ++itr) - (*itr)->FarTeleportTo(newMap, x, y, z, (*itr)->GetOrientation()); -} - -bool Transport::AddPassenger(Player* passenger) -{ - if (m_passengers.insert(passenger).second) - TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "Player %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); + map->AddToMap(creature); + _staticPassengers.insert(creature); - sScriptMgr->OnAddPassenger(this, passenger); - return true; + sScriptMgr->OnAddCreaturePassenger(this, creature); + return creature; } -bool Transport::RemovePassenger(Player* passenger) +GameObject* Transport::CreateGOPassenger(uint32 guid, GameObjectData const* data) { - if (m_passengers.erase(passenger)) - TC_LOG_INFO(LOG_FILTER_TRANSPORTS, "Player %s removed from transport %s.", passenger->GetName().c_str(), GetName().c_str()); - - sScriptMgr->OnRemovePassenger(this, passenger); - return true; -} + Map* map = GetMap(); + GameObject* go = new GameObject(); -void Transport::Update(uint32 p_diff) -{ - if (!AI()) + if (!go->LoadGameObjectFromDB(guid, map, false)) { - if (!AIM_Initialize()) - TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Could not initialize GameObjectAI for Transport"); - } else - AI()->UpdateAI(p_diff); + delete go; + return NULL; + } - if (m_WayPoints.size() <= 1) - return; + float x = data->posX; + float y = data->posY; + float z = data->posZ; + float o = data->orientation; - m_timer = getMSTime() % m_period; - while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime)) - { - DoEventIfAny(*m_curr, true); + go->SetTransport(this); + go->m_movementInfo.transport.guid = GetGUID(); + go->m_movementInfo.transport.pos.Relocate(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + go->Relocate(x, y, z, o); - m_curr = GetNextWayPoint(); - m_next = GetNextWayPoint(); + if (!go->IsPositionValid()) + { + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "GameObject (guidlow %d, entry %d) not created. Suggested coordinates aren't valid (X: %f Y: %f)", go->GetGUIDLow(), go->GetEntry(), go->GetPositionX(), go->GetPositionY()); + delete go; + return NULL; + } - DoEventIfAny(*m_curr, false); + map->AddToMap(go); + _staticPassengers.insert(go); - // first check help in case client-server transport coordinates de-synchronization - if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport) - { - TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z); - } - else - { - Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z, GetAngle(m_next->second.x, m_next->second.y) + float(M_PI)); - UpdatePassengerPositions(); // COME BACK MARKER - } - - sScriptMgr->OnRelocate(this, m_curr->first, m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z); + //sScriptMgr->OnAddCreaturePassenger(this, go); + return go; +} - m_nextNodeTime = m_curr->first; +void Transport::CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= NULL*/) const +{ + float inx = x, iny = y, inz = z; + if (o) + *o = Position::NormalizeOrientation(GetOrientation() + *o); - if (m_curr == m_WayPoints.begin()) - TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, " ************ BEGIN ************** %s", m_name.c_str()); + x = GetPositionX() + inx * std::cos(GetOrientation()) - iny * std::sin(GetOrientation()); + y = GetPositionY() + iny * std::cos(GetOrientation()) + inx * std::sin(GetOrientation()); + z = GetPositionZ() + inz; +} - TC_LOG_DEBUG(LOG_FILTER_TRANSPORTS, "%s moved to %d %f %f %f %d", m_name.c_str(), m_curr->second.id, m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid); - } +void Transport::CalculatePassengerOffset(float& x, float& y, float& z, float* o /*= NULL*/) const +{ + if (o) + *o = Position::NormalizeOrientation(*o - GetOrientation()); - sScriptMgr->OnTransportUpdate(this, p_diff); + z -= GetPositionZ(); + y -= GetPositionY(); // y = searchedY * std::cos(o) + searchedX * std::sin(o) + x -= GetPositionX(); // x = searchedX * std::cos(o) + searchedY * std::sin(o + pi) + float inx = x, iny = y; + y = (iny - inx * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); + x = (inx + iny * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); } -void Transport::UpdateForMap(Map const* targetMap) +void Transport::UpdatePosition(float x, float y, float z, float o) { - Map::PlayerList const& player = targetMap->GetPlayers(); - if (player.isEmpty()) - return; + bool newActive = GetMap()->IsGridLoaded(x, y); + + Relocate(x, y, z, o); + + UpdatePassengerPositions(_passengers); + + /* There are four possible scenarios that trigger loading/unloading passengers: + 1. transport moves from inactive to active grid + 2. the grid that transport is currently in becomes active + 3. transport moves from active to inactive grid + 4. the grid that transport is currently in unloads + */ + if (_staticPassengers.empty() && newActive) // 1. and 2. + LoadStaticPassengers(); + else if (!_staticPassengers.empty() && !newActive && Cell(x, y).DiffGrid(Cell(GetPositionX(), GetPositionY()))) // 3. + UnloadStaticPassengers(); + else + UpdatePassengerPositions(_staticPassengers); + // 4. is handed by grid unload +} - if (GetMapId() == targetMap->GetId()) +void Transport::LoadStaticPassengers() +{ + if (uint32 mapId = GetGOInfo()->moTransport.mapID) { - for (Map::PlayerList::const_iterator itr = player.begin(); itr != player.end(); ++itr) + CellObjectGuidsMap const& cells = sObjectMgr->GetMapObjectGuids(mapId, GetMap()->GetSpawnMode()); + CellGuidSet::const_iterator guidEnd; + for (CellObjectGuidsMap::const_iterator cellItr = cells.begin(); cellItr != cells.end(); ++cellItr) { - if (this != itr->GetSource()->GetTransport()) - { - UpdateData transData; - BuildCreateUpdateBlockForPlayer(&transData, itr->GetSource()); - WorldPacket packet; - transData.BuildPacket(&packet); - itr->GetSource()->SendDirectMessage(&packet); - } + // Creatures on transport + guidEnd = cellItr->second.creatures.end(); + for (CellGuidSet::const_iterator guidItr = cellItr->second.creatures.begin(); guidItr != guidEnd; ++guidItr) + CreateNPCPassenger(*guidItr, sObjectMgr->GetCreatureData(*guidItr)); + + // GameObjects on transport + guidEnd = cellItr->second.gameobjects.end(); + for (CellGuidSet::const_iterator guidItr = cellItr->second.gameobjects.begin(); guidItr != guidEnd; ++guidItr) + CreateGOPassenger(*guidItr, sObjectMgr->GetGOData(*guidItr)); } } - else - { - UpdateData transData; - BuildOutOfRangeUpdateBlock(&transData); - WorldPacket out_packet; - transData.BuildPacket(&out_packet); - - for (Map::PlayerList::const_iterator itr = player.begin(); itr != player.end(); ++itr) - if (this != itr->GetSource()->GetTransport()) - itr->GetSource()->SendDirectMessage(&out_packet); - } } -void Transport::DoEventIfAny(WayPointMap::value_type const& node, bool departure) +void Transport::UnloadStaticPassengers() { - if (uint32 eventid = departure ? node.second.departureEventID : node.second.arrivalEventID) + while (!_staticPassengers.empty()) { - TC_LOG_DEBUG(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of %s path", departure ? "departure" : "arrival", eventid, node.first, GetName().c_str()); - GetMap()->ScriptsStart(sEventScripts, eventid, this, this); - EventInform(eventid); + WorldObject* obj = *_staticPassengers.begin(); + obj->AddObjectToRemoveList(); // also removes from _staticPassengers } } -void Transport::BuildStartMovePacket(Map const* targetMap) +void Transport::EnableMovement(bool enabled) { - SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - SetGoState(GO_STATE_ACTIVE); - UpdateForMap(targetMap); + if (!GetGOInfo()->moTransport.canBeStopped) + return; + + _pendingStop = !enabled; } -void Transport::BuildStopMovePacket(Map const* targetMap) +void Transport::MoveToNextWaypoint() { - RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - SetGoState(GO_STATE_READY); - UpdateForMap(targetMap); + // Clear events flagging + _triggeredArrivalEvent = false; + _triggeredDepartureEvent = false; + + // Set frames + _currentFrame = _nextFrame++; + if (_nextFrame == GetKeyFrames().end()) + _nextFrame = GetKeyFrames().begin(); } -uint32 Transport::AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y, float z, float o, uint32 anim) +float Transport::CalculateSegmentPos(float now) { - Map* map = GetMap(); - //make it world object so it will not be unloaded with grid - Creature* creature = new Creature(true); - - if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, GetPhaseMask(), entry, 0, GetGOInfo()->faction, 0, 0, 0, 0)) + KeyFrame const& frame = *_currentFrame; + const float speed = float(m_goInfo->moTransport.moveSpeed); + const float accel = float(m_goInfo->moTransport.accelRate); + float timeSinceStop = frame.TimeFrom + (now - (1.0f/IN_MILLISECONDS) * frame.DepartureTime); + float timeUntilStop = frame.TimeTo - (now - (1.0f/IN_MILLISECONDS) * frame.DepartureTime); + float segmentPos, dist; + float accelTime = _transportInfo->accelTime; + float accelDist = _transportInfo->accelDist; + // calculate from nearest stop, less confusing calculation... + if (timeSinceStop < timeUntilStop) { - delete creature; - return 0; + if (timeSinceStop < accelTime) + dist = 0.5f * accel * timeSinceStop * timeSinceStop; + else + dist = accelDist + (timeSinceStop - accelTime) * speed; + segmentPos = dist - frame.DistSinceStop; + } + else + { + if (timeUntilStop < _transportInfo->accelTime) + dist = 0.5f * accel * timeUntilStop * timeUntilStop; + else + dist = accelDist + (timeUntilStop - accelTime) * speed; + segmentPos = frame.DistUntilStop - dist; } - creature->SetTransport(this); - creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); - creature->m_movementInfo.guid = GetGUID(); - creature->m_movementInfo.transport.pos.Relocate(x, y, z, o); + return segmentPos / frame.NextDistFromPrev; +} - if (anim) - creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, anim); +void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) +{ + Map const* oldMap = GetMap(); - creature->Relocate( - GetPositionX() + (x * std::cos(GetOrientation()) + y * std::sin(GetOrientation() + float(M_PI))), - GetPositionY() + (y * std::cos(GetOrientation()) + x * std::sin(GetOrientation())), - z + GetPositionZ(), - o + GetOrientation()); + if (oldMap->GetId() != newMapid) + { + Map* newMap = sMapMgr->CreateBaseMap(newMapid); + Map::PlayerList const& oldPlayers = GetMap()->GetPlayers(); + if (!oldPlayers.isEmpty()) + { + UpdateData data; + BuildOutOfRangeUpdateBlock(&data); + WorldPacket packet; + data.BuildPacket(&packet); + for (Map::PlayerList::const_iterator itr = oldPlayers.begin(); itr != oldPlayers.end(); ++itr) + if (itr->GetSource()->GetTransport() != this) + itr->GetSource()->SendDirectMessage(&packet); + } - creature->SetHomePosition(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); - creature->SetTransportHomePosition(creature->m_movementInfo.transport.pos); + UnloadStaticPassengers(); + GetMap()->RemoveFromMap<Transport>(this, false); + SetMap(newMap); - if (!creature->IsPositionValid()) - { - TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", creature->GetGUIDLow(), creature->GetEntry(), creature->GetPositionX(), creature->GetPositionY()); - delete creature; - return 0; - } + Map::PlayerList const& newPlayers = GetMap()->GetPlayers(); + if (!newPlayers.isEmpty()) + { + for (Map::PlayerList::const_iterator itr = newPlayers.begin(); itr != newPlayers.end(); ++itr) + { + if (itr->GetSource()->GetTransport() != this) + { + UpdateData data; + BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); + WorldPacket packet; + data.BuildPacket(&packet); + itr->GetSource()->SendDirectMessage(&packet); + } + } + } - map->AddToMap(creature); - m_NPCPassengerSet.insert(creature); + // Teleport passengers after everyone on destination map are sent create packet + // but before transport itself is registered there and begins updating + for (std::set<WorldObject*>::iterator itr = _staticPassengers.begin(); itr != _staticPassengers.end(); ++itr) + { + switch ((*itr)->GetTypeId()) + { + case TYPEID_UNIT: + (*itr)->ToCreature()->FarTeleportTo(newMap, x, y, z, (*itr)->GetOrientation()); + break; + case TYPEID_GAMEOBJECT: + { + GameObject* go = (*itr)->ToGameObject(); + go->GetMap()->RemoveFromMap(go, false); + Relocate(x, y, z, go->GetOrientation()); + SetMap(newMap); + newMap->AddToMap(go); + break; + } + default: + break; + } + } - if (tguid == 0) - { - ++currenttguid; - tguid = currenttguid; + for (std::set<WorldObject*>::iterator itr = _passengers.begin(); itr != _passengers.end(); ++itr) + { + switch ((*itr)->GetTypeId()) + { + case TYPEID_UNIT: + if (!IS_PLAYER_GUID((*itr)->ToUnit()->GetOwnerGUID())) // pets should be teleported with player + (*itr)->ToCreature()->FarTeleportTo(newMap, x, y, z, (*itr)->GetOrientation()); + break; + case TYPEID_GAMEOBJECT: + { + GameObject* go = (*itr)->ToGameObject(); + go->GetMap()->RemoveFromMap(go, false); + Relocate(x, y, z, go->GetOrientation()); + SetMap(newMap); + newMap->AddToMap(go); + break; + } + case TYPEID_PLAYER: + (*itr)->ToPlayer()->TeleportTo(newMapid, x, y, z, (*itr)->GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); + break; + default: + break; + } + } + + GetMap()->AddToMap<Transport>(this); } else - currenttguid = std::max(tguid, currenttguid); - - creature->SetGUIDTransport(tguid); - sScriptMgr->OnAddCreaturePassenger(this, creature); - return tguid; -} - -void Transport::UpdatePosition(MovementInfo* mi) -{ - float transport_o = mi->pos.GetOrientation() - mi->transport.pos.GetOrientation(); - float transport_x = mi->pos.m_positionX - (mi->transport.pos.m_positionX * std::cos(transport_o) - mi->transport.pos.m_positionY * std::sin(transport_o)); - float transport_y = mi->pos.m_positionY - (mi->transport.pos.m_positionY * std::cos(transport_o) + mi->transport.pos.m_positionX * std::sin(transport_o)); - float transport_z = mi->pos.m_positionZ - mi->transport.pos.m_positionZ; + { + // Teleport players, they need to know it + for (std::set<WorldObject*>::iterator itr = _passengers.begin(); itr != _passengers.end(); ++itr) + if ((*itr)->GetTypeId() == TYPEID_PLAYER) + (*itr)->ToUnit()->NearTeleportTo(x, y, z, GetOrientation()); + } - Relocate(transport_x, transport_y, transport_z, transport_o); - UpdatePassengerPositions(); + UpdatePosition(x, y, z, GetOrientation()); } -void Transport::UpdatePassengerPositions() +void Transport::UpdatePassengerPositions(std::set<WorldObject*>& passengers) { - for (CreatureSet::iterator itr = m_NPCPassengerSet.begin(); itr != m_NPCPassengerSet.end(); ++itr) + for (std::set<WorldObject*>::iterator itr = passengers.begin(); itr != passengers.end(); ++itr) { - Creature* npc = *itr; + WorldObject* passenger = *itr; + // transport teleported but passenger not yet (can happen for players) + if (passenger->GetMap() != GetMap()) + continue; + + // if passenger is on vehicle we have to assume the vehicle is also on transport + // and its the vehicle that will be updating its passengers + if (Unit* unit = passenger->ToUnit()) + if (unit->GetVehicle()) + continue; + // Do not use Unit::UpdatePosition here, we don't want to remove auras + // as if regular movement occurred float x, y, z, o; - npc->m_movementInfo.transport.pos.GetPosition(x, y, z, o); + passenger->m_movementInfo.transport.pos.GetPosition(x, y, z, o); CalculatePassengerPosition(x, y, z, &o); - GetMap()->CreatureRelocation(npc, x, y, z, o, false); - npc->GetTransportHomePosition(x, y, z, o); - CalculatePassengerPosition(x, y, z, &o); - npc->SetHomePosition(x, y, z, o); + switch (passenger->GetTypeId()) + { + case TYPEID_UNIT: + { + Creature* creature = passenger->ToCreature(); + GetMap()->CreatureRelocation(creature, x, y, z, o, false); + creature->GetTransportHomePosition(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + creature->SetHomePosition(x, y, z, o); + break; + } + case TYPEID_PLAYER: + GetMap()->PlayerRelocation(passenger->ToPlayer(), x, y, z, o); + break; + case TYPEID_GAMEOBJECT: + GetMap()->GameObjectRelocation(passenger->ToGameObject(), x, y, z, o, false); + break; + default: + break; + } + + if (Unit* unit = passenger->ToUnit()) + if (Vehicle* vehicle = unit->GetVehicleKit()) + vehicle->RelocatePassengers(); } } -void Transport::CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= NULL*/) const +void Transport::DoEventIfAny(KeyFrame const& node, bool departure) { - float inx = x, iny = y, inz = z; - if (o) - *o = Position::NormalizeOrientation(GetOrientation() + *o); - - x = GetPositionX() + inx * std::cos(GetOrientation()) - iny * std::sin(GetOrientation()); - y = GetPositionY() + iny * std::cos(GetOrientation()) + inx * std::sin(GetOrientation()); - z = GetPositionZ() + inz; + if (uint32 eventid = departure ? node.Node->departureEventID : node.Node->arrivalEventID) + { + TC_LOG_DEBUG(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of %s path", departure ? "departure" : "arrival", eventid, node.Node->index, GetName().c_str()); + GetMap()->ScriptsStart(sEventScripts, eventid, this, this); + EventInform(eventid); + } } -void Transport::CalculatePassengerOffset(float& x, float& y, float& z, float* o /*= NULL*/) const +void Transport::BuildUpdate(UpdateDataMapType& data_map) { - if (o) - *o = Position::NormalizeOrientation(*o - GetOrientation()); + Map::PlayerList const& players = GetMap()->GetPlayers(); + if (players.isEmpty()) + return; - z -= GetPositionZ(); - y -= GetPositionY(); // y = searchedY * std::cos(o) + searchedX * std::sin(o) - x -= GetPositionX(); // x = searchedX * std::cos(o) + searchedY * std::sin(o + pi) - float inx = x, iny = y; - y = (iny - inx * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); - x = (inx + iny * std::tan(GetOrientation())) / (std::cos(GetOrientation()) + std::sin(GetOrientation()) * std::tan(GetOrientation())); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + BuildFieldsUpdate(itr->GetSource(), data_map); + + ClearUpdateMask(true); } diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 445bec456fd..0d020fe2c34 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -20,34 +20,30 @@ #define TRANSPORTS_H #include "GameObject.h" +#include "TransportMgr.h" #include "VehicleDefines.h" -#include <map> -#include <set> -#include <string> +struct CreatureData; class Transport : public GameObject, public TransportBase { + friend Transport* TransportMgr::CreateTransport(uint32, uint32, Map*); + + Transport(); public: - Transport(uint32 period, uint32 script); ~Transport(); - bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags); - bool GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids); - void Update(uint32 p_time); - bool AddPassenger(Player* passenger); - bool RemovePassenger(Player* passenger); + bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); + void Update(uint32 diff); - void RemovePassenger(Creature* passenger) { m_NPCPassengerSet.erase(passenger); } + void BuildUpdate(UpdateDataMapType& data_map); - typedef std::set<Player*> PlayerSet; - PlayerSet const& GetPassengers() const { return m_passengers; } + void AddPassenger(WorldObject* passenger); + void RemovePassenger(WorldObject* passenger); + std::set<WorldObject*> const& GetPassengers() const { return _passengers; } - typedef std::set<Creature*> CreatureSet; - CreatureSet m_NPCPassengerSet; - uint32 AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y, float z, float o, uint32 anim=0); - void UpdatePosition(MovementInfo* mi); - void UpdatePassengerPositions(); + Creature* CreateNPCPassenger(uint32 guid, CreatureData const* data); + GameObject* CreateGOPassenger(uint32 guid, GameObjectData const* data); /// This method transforms supplied transport offsets into global coordinates void CalculatePassengerPosition(float& x, float& y, float& z, float* o = NULL) const; @@ -55,50 +51,50 @@ class Transport : public GameObject, public TransportBase /// This method transforms supplied global coordinates into local offsets void CalculatePassengerOffset(float& x, float& y, float& z, float* o = NULL) const; - void BuildStartMovePacket(Map const* targetMap); - void BuildStopMovePacket(Map const* targetMap); - uint32 GetScriptId() const { return ScriptId; } - private: - struct WayPoint - { - WayPoint() : mapid(0), x(0), y(0), z(0), teleport(false), id(0) {} - WayPoint(uint32 _mapid, float _x, float _y, float _z, bool _teleport, uint32 _id = 0, - uint32 _arrivalEventID = 0, uint32 _departureEventID = 0) - : mapid(_mapid), x(_x), y(_y), z(_z), teleport(_teleport), id(_id), - arrivalEventID(_arrivalEventID), departureEventID(_departureEventID) - { - } - uint32 mapid; - float x; - float y; - float z; - bool teleport; - uint32 id; - uint32 arrivalEventID; - uint32 departureEventID; - }; - - typedef std::map<uint32, WayPoint> WayPointMap; - - WayPointMap::const_iterator m_curr; - WayPointMap::const_iterator m_next; - uint32 m_pathTime; - uint32 m_timer; - - PlayerSet m_passengers; - - uint32 currenttguid; - uint32 m_period; - uint32 ScriptId; - public: - WayPointMap m_WayPoints; - uint32 m_nextNodeTime; + uint32 GetPeriod() const { return GetUInt32Value(GAMEOBJECT_LEVEL); } + void SetPeriod(uint32 period) { SetUInt32Value(GAMEOBJECT_LEVEL, period); } + uint32 GetTimer() const { return GetGOValue()->Transport.PathProgress; } + + KeyFrameVec const& GetKeyFrames() const { return _transportInfo->keyFrames; } + + void UpdatePosition(float x, float y, float z, float o); + + //! Needed when transport moves from inactive to active grid + void LoadStaticPassengers(); + + //! Needed when transport enters inactive grid + void UnloadStaticPassengers(); + + void EnableMovement(bool enabled); + + TransportTemplate const* GetTransportTemplate() const { return _transportInfo; } private: + void MoveToNextWaypoint(); + float CalculateSegmentPos(float perc); void TeleportTransport(uint32 newMapid, float x, float y, float z); - void UpdateForMap(Map const* map); - void DoEventIfAny(WayPointMap::value_type const& node, bool departure); - WayPointMap::const_iterator GetNextWayPoint(); + void UpdatePassengerPositions(std::set<WorldObject*>& passengers); + void DoEventIfAny(KeyFrame const& node, bool departure); + + //! Helpers to know if stop frame was reached + bool IsMoving() const { return _isMoving; } + void SetMoving(bool val) { _isMoving = val; } + + TransportTemplate const* _transportInfo; + + KeyFrameVec::const_iterator _currentFrame; + KeyFrameVec::const_iterator _nextFrame; + uint32 _moveTimer; + TimeTrackerSmall _positionChangeTimer; + bool _isMoving; + bool _pendingStop; + + //! These are needed to properly control events triggering only once for each frame + bool _triggeredArrivalEvent; + bool _triggeredDepartureEvent; + + std::set<WorldObject*> _passengers; + std::set<WorldObject*> _staticPassengers; }; -#endif +#endif diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index c34768c9851..2fd1c500305 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -259,7 +259,6 @@ Unit::Unit(bool isWorldObject) : m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); - _focusSpell = NULL; _lastLiquid = NULL; _isWalkingBeforeCharm = false; } @@ -568,6 +567,9 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (IsAIEnabled) GetAI()->DamageDealt(victim, damage, damagetype); + // Hook for OnDamage Event + sScriptMgr->OnDamage(this, victim, damage); + if (victim->GetTypeId() == TYPEID_PLAYER && this != victim) { // Signal to pets that their owner was attacked @@ -2483,7 +2485,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) { // Can`t miss on dead target (on skinning for example) - if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) + if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) || spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_HIT_RESULT) return SPELL_MISS_NONE; SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); @@ -2508,19 +2510,15 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras modHitChance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask); - // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects - if (!(spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_HIT_RESULT)) - { - // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras - modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); - // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura - if (spellInfo->IsTargetingArea()) - modHitChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE); + // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras + modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); + // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura + if (spellInfo->IsTargetingArea()) + modHitChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE); - // Decrease hit chance from victim rating bonus - if (victim->GetTypeId() == TYPEID_PLAYER) - modHitChance -= int32(victim->ToPlayer()->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)); - } + // Decrease hit chance from victim rating bonus + if (victim->GetTypeId() == TYPEID_PLAYER) + modHitChance -= int32(victim->ToPlayer()->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)); int32 HitChance = modHitChance * 100; // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings @@ -2535,11 +2533,6 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (rand < tmp) return SPELL_MISS_MISS; - // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore - // resist and deflect chances - if (spellInfo->AttributesEx3 & SPELL_ATTR3_IGNORE_HIT_RESULT) - return SPELL_MISS_NONE; - // Chance resist mechanic (select max value from every mechanic spell effect) int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; tmp += resist_chance; @@ -2564,7 +2557,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo } } - // Roll chance + // Roll chance if (rand < tmp) return SPELL_MISS_RESIST; @@ -5258,18 +5251,19 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // cast 45429 Arcane Bolt if Exalted by Scryers case 45481: { - if (GetTypeId() != TYPEID_PLAYER) + Player* player = ToPlayer(); + if (!player) return false; // Get Aldor reputation rank - if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) + if (player->GetReputationRank(932) == REP_EXALTED) { target = this; triggered_spell_id = 45479; break; } // Get Scryers reputation rank - if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) + if (player->GetReputationRank(934) == REP_EXALTED) { // triggered at positive/self casts also, current attack target used then if (target && IsFriendlyTo(target)) @@ -5277,8 +5271,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere target = GetVictim(); if (!target) { - uint64 selected_guid = ToPlayer()->GetSelection(); - target = ObjectAccessor::GetUnit(*this, selected_guid); + target = player->GetSelectedUnit(); if (!target) return false; } @@ -9588,6 +9581,9 @@ int32 Unit::DealHeal(Unit* victim, uint32 addhealth) if (addhealth) gain = victim->ModifyHealth(int32(addhealth)); + // Hook for OnHeal Event + sScriptMgr->OnHeal(this, victim, (uint32&)gain); + Unit* unit = this; if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTotem()) @@ -16904,7 +16900,7 @@ void Unit::_ExitVehicle(Position const* exitPosition) if (vehicle->GetBase()->HasUnitTypeMask(UNIT_MASK_MINION) && vehicle->GetBase()->GetTypeId() == TYPEID_UNIT) if (((Minion*)vehicle->GetBase())->GetOwner() == this) - vehicle->GetBase()->ToCreature()->DespawnOrUnsummon(); + vehicle->GetBase()->ToCreature()->DespawnOrUnsummon(1); if (HasUnitTypeMask(UNIT_MASK_ACCESSORY)) { @@ -17333,6 +17329,8 @@ void Unit::SetFacingTo(float ori) { Movement::MoveSplineInit init(this); init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset(), false); + if (HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && GetTransGUID()) + init.DisableTransportPathTransformations(); // It makes no sense to target global orientation init.SetFacing(ori); init.Launch(); } @@ -17344,7 +17342,10 @@ void Unit::SetFacingToObject(WorldObject* object) return; /// @todo figure out under what conditions creature will move towards object instead of facing it where it currently is. - SetFacingTo(GetAngle(object)); + Movement::MoveSplineInit init(this); + init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); + init.SetFacing(GetAngle(object)); // when on transport, GetAngle will still return global coordinates (and angle) that needs transforming + init.Launch(); } bool Unit::SetWalk(bool enable) @@ -17471,43 +17472,6 @@ bool Unit::SetHover(bool enable, bool /*packetOnly = false*/) return true; } -void Unit::SetTarget(uint64 guid) -{ - if (!_focusSpell) - SetUInt64Value(UNIT_FIELD_TARGET, guid); -} - -void Unit::FocusTarget(Spell const* focusSpell, WorldObject const* target) -{ - // already focused - if (_focusSpell) - return; - - _focusSpell = focusSpell; - SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID()); - if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) - AddUnitState(UNIT_STATE_ROTATING); - - // Set serverside orientation if needed (needs to be after attribute check) - SetInFront(target); -} - -void Unit::ReleaseFocus(Spell const* focusSpell) -{ - // focused to something else - if (focusSpell != _focusSpell) - return; - - _focusSpell = NULL; - if (Unit* victim = GetVictim()) - SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID()); - else - SetUInt64Value(UNIT_FIELD_TARGET, 0); - - if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) - ClearUnitState(UNIT_STATE_ROTATING); -} - void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const { if (!target) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 119a1dd1966..25352a924ef 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2078,12 +2078,6 @@ class Unit : public WorldObject bool IsOnVehicle(const Unit* vehicle) const; Unit* GetVehicleBase() const; Creature* GetVehicleCreatureBase() const; - float GetTransOffsetX() const { return m_movementInfo.transport.pos.GetPositionX(); } - float GetTransOffsetY() const { return m_movementInfo.transport.pos.GetPositionY(); } - float GetTransOffsetZ() const { return m_movementInfo.transport.pos.GetPositionZ(); } - float GetTransOffsetO() const { return m_movementInfo.transport.pos.GetOrientation(); } - uint32 GetTransTime() const { return m_movementInfo.transport.time; } - int8 GetTransSeat() const { return m_movementInfo.transport.seat; } uint64 GetTransGUID() const; /// Returns the transport this unit is on directly (if on vehicle and transport, return vehicle) TransportBase* GetDirectTransport() const; @@ -2124,11 +2118,8 @@ class Unit : public WorldObject TempSummon* ToTempSummon() { if (IsSummon()) return reinterpret_cast<TempSummon*>(this); else return NULL; } TempSummon const* ToTempSummon() const { if (IsSummon()) return reinterpret_cast<TempSummon const*>(this); else return NULL; } - void SetTarget(uint64 guid); - - // Handling caster facing during spellcast - void FocusTarget(Spell const* focusSpell, WorldObject const* target); - void ReleaseFocus(Spell const* focusSpell); + uint64 GetTarget() const { return GetUInt64Value(UNIT_FIELD_TARGET); } + virtual void SetTarget(uint64 /*guid*/) = 0; // Movement info Movement::MoveSpline * movespline; @@ -2253,7 +2244,6 @@ class Unit : public WorldObject bool m_cleanupDone; // lock made to not add stuff after cleanup before delete bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world - Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing bool _isWalkingBeforeCharm; // Are we walking before we were charmed? time_t _lastDamagedTime; // Part of Evade mechanics diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp index afc27b74ecc..67474c1dca0 100644 --- a/src/server/game/Globals/ObjectAccessor.cpp +++ b/src/server/game/Globals/ObjectAccessor.cpp @@ -147,6 +147,15 @@ GameObject* ObjectAccessor::GetGameObject(WorldObject const& u, uint64 guid) return GetObjectInMap(guid, u.GetMap(), (GameObject*)NULL); } +Transport* ObjectAccessor::GetTransport(WorldObject const& u, uint64 guid) +{ + if (GUID_HIPART(guid) != HIGHGUID_MO_TRANSPORT) + return NULL; + + GameObject* go = GetGameObject(u, guid); + return go ? go->ToTransport() : NULL; +} + DynamicObject* ObjectAccessor::GetDynamicObject(WorldObject const& u, uint64 guid) { return GetObjectInMap(guid, u.GetMap(), (DynamicObject*)NULL); diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h index 1abe3550729..a2707920c63 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -40,6 +40,7 @@ class WorldObject; class Vehicle; class Map; class WorldRunnable; +class Transport; template <class T> class HashMapHolder @@ -145,6 +146,7 @@ class ObjectAccessor static Object* GetObjectByTypeMask(WorldObject const&, uint64, uint32 typemask); static Corpse* GetCorpse(WorldObject const& u, uint64 guid); static GameObject* GetGameObject(WorldObject const& u, uint64 guid); + static Transport* GetTransport(WorldObject const& u, uint64 guid); static DynamicObject* GetDynamicObject(WorldObject const& u, uint64 guid); static Unit* GetUnit(WorldObject const&, uint64 guid); static Creature* GetCreature(WorldObject const& u, uint64 guid); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 2f309c07cce..86e6963b1de 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -804,6 +804,18 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE) TC_LOG_ERROR(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong trainer type %u.", cInfo->Entry, cInfo->trainer_type); + if (cInfo->speed_walk == 0.0f) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong value (%f) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk); + const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f; + } + + if (cInfo->speed_run == 0.0f) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong value (%f) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run); + const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f; + } + if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type)) { TC_LOG_ERROR(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid creature type (%u) in `type`.", cInfo->Entry, cInfo->type); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index e44333c4a7a..752cc3b56ab 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1019,6 +1019,11 @@ class ObjectMgr return _mapObjectGuidsStore[MAKE_PAIR32(mapid, spawnMode)][cell_id]; } + CellObjectGuidsMap const& GetMapObjectGuids(uint16 mapid, uint8 spawnMode) + { + return _mapObjectGuidsStore[MAKE_PAIR32(mapid, spawnMode)]; + } + /** * Gets temp summon data for all creatures of specified group. * diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index b8ab75f213b..00b1e82a0ea 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -35,16 +35,28 @@ void VisibleNotifier::SendToSelf() // at this moment i_clientGUIDs have guids that not iterate at grid level checks // but exist one case when this possible and object not out of range: transports if (Transport* transport = i_player.GetTransport()) - for (Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr != transport->GetPassengers().end();++itr) + for (std::set<WorldObject*>::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end();++itr) { if (vis_guids.find((*itr)->GetGUID()) != vis_guids.end()) { vis_guids.erase((*itr)->GetGUID()); - i_player.UpdateVisibilityOf((*itr), i_data, i_visibleNow); - - if (!(*itr)->isNeedNotify(NOTIFY_VISIBILITY_CHANGED)) - (*itr)->UpdateVisibilityOf(&i_player); + switch ((*itr)->GetTypeId()) + { + case TYPEID_GAMEOBJECT: + i_player.UpdateVisibilityOf((*itr)->ToGameObject(), i_data, i_visibleNow); + break; + case TYPEID_PLAYER: + i_player.UpdateVisibilityOf((*itr)->ToPlayer(), i_data, i_visibleNow); + if (!(*itr)->isNeedNotify(NOTIFY_VISIBILITY_CHANGED)) + (*itr)->ToPlayer()->UpdateVisibilityOf(&i_player); + break; + case TYPEID_UNIT: + i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); + break; + default: + break; + } } } @@ -324,10 +336,8 @@ template<class T> void ObjectUpdater::Visit(GridRefManager<T> &m) { for (typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter) - { if (iter->GetSource()->IsInWorld()) iter->GetSource()->Update(i_timeDiff); - } } bool AnyDeadUnitObjectInRangeCheck::operator()(Player* u) @@ -360,5 +370,6 @@ bool AnyDeadUnitSpellTargetInRangeCheck::operator()(Creature* u) return AnyDeadUnitObjectInRangeCheck::operator()(u) && i_check(u); } -template void ObjectUpdater::Visit<GameObject>(GameObjectMapType &); -template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType &); +template void ObjectUpdater::Visit<Creature>(CreatureMapType&); +template void ObjectUpdater::Visit<GameObject>(GameObjectMapType&); +template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType&); diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index 58a5bbbff06..05dff6823be 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -43,6 +43,20 @@ void ObjectGridEvacuator::Visit(CreatureMapType &m) } } +void ObjectGridEvacuator::Visit(GameObjectMapType &m) +{ + // gameobject in unloading grid can have respawn point in another grid + // if it will be unloaded then it will not respawn in original grid until unload/load original grid + // move to respawn point to prevent this case. For player view in respawn grid this will be normal respawn. + for (GameObjectMapType::iterator iter = m.begin(); iter != m.end();) + { + GameObject* go = iter->GetSource(); + ++iter; + + go->GetMap()->GameObjectRespawnRelocation(go, true); + } +} + // for loading world object at grid loading (Corpses) /// @todo to implement npc on transport, also need to load npcs at grid loading class ObjectWorldLoader @@ -70,12 +84,26 @@ template<class T> void ObjectGridLoader::SetObjectCell(T* /*obj*/, CellCoord con template<> void ObjectGridLoader::SetObjectCell(Creature* obj, CellCoord const& cellCoord) { Cell cell(cellCoord); + obj->SetCurrentCell(cell); +} +template<> void ObjectGridLoader::SetObjectCell(GameObject* obj, CellCoord const& cellCoord) +{ + Cell cell(cellCoord); obj->SetCurrentCell(cell); } template <class T> -void AddObjectHelper(CellCoord &cell, GridRefManager<T> &m, uint32 &count, Map* map, T *obj) +void AddObjectHelper(CellCoord &cell, GridRefManager<T> &m, uint32 &count, Map* /*map*/, T *obj) +{ + obj->AddToGrid(m); + ObjectGridLoader::SetObjectCell(obj, cell); + obj->AddToWorld(); + ++count; +} + +template <> +void AddObjectHelper(CellCoord &cell, CreatureMapType &m, uint32 &count, Map* map, Creature *obj) { obj->AddToGrid(m); ObjectGridLoader::SetObjectCell(obj, cell); diff --git a/src/server/game/Grids/ObjectGridLoader.h b/src/server/game/Grids/ObjectGridLoader.h index 11f91670a5f..b858b92da32 100644 --- a/src/server/game/Grids/ObjectGridLoader.h +++ b/src/server/game/Grids/ObjectGridLoader.h @@ -67,6 +67,7 @@ class ObjectGridEvacuator { public: void Visit(CreatureMapType &m); + void Visit(GameObjectMapType &m); template<class T> void Visit(GridRefManager<T> &) {} }; diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 031febc3806..1a3e48c8217 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -1764,7 +1764,7 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool { //clamp amount to MAX_MONEY_AMOUNT, Players can't hold more than that anyway amount = std::min(amount, uint32(MAX_MONEY_AMOUNT)); - + if (m_bankMoney < amount) // Not enough money in bank return false; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index f65641eaa76..71453581ed0 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1167,8 +1167,6 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recvData) TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_INSPECT"); - _player->SetSelection(guid); - Player* player = ObjectAccessor::FindPlayer(guid); if (!player) { diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 1faa68b6ea4..36d1e1cb1ea 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -303,30 +303,21 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) { if (!plrMover->GetTransport()) { - // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list - for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) + if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) { - if ((*iter)->GetGUID() == movementInfo.transport.guid) - { - plrMover->m_transport = *iter; - (*iter)->AddPassenger(plrMover); - break; - } + plrMover->m_transport = transport; + transport->AddPassenger(plrMover); } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); - for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) + if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) { - if ((*iter)->GetGUID() == movementInfo.transport.guid) - { - foundNewTransport = true; - plrMover->m_transport = *iter; - (*iter)->AddPassenger(plrMover); - break; - } + foundNewTransport = true; + plrMover->m_transport = transport; + transport->AddPassenger(plrMover); } if (!foundNewTransport) diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 5a94d5b391b..4638e05ee3d 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -267,7 +267,6 @@ void WorldSession::HandleNpcTextQueryOpcode(WorldPacket& recvData) TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID); recvData >> guid; - GetPlayer()->SetSelection(guid); GossipText const* pGossip = sObjectMgr->GetGossipText(textID); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 4660489004d..9abb78187a9 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -63,6 +63,17 @@ Map::~Map() obj->ResetMap(); } + for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end(); ++itr) + { + Transport* transport = *itr; + // Destroy local transports + if (transport->GetTransportTemplate()->inInstance) + { + transport->RemoveFromWorld(); + delete transport; + } + } + if (!m_scriptSchedule.empty()) sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size()); @@ -217,10 +228,12 @@ void Map::DeleteStateMachine() } Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent): -_creatureToMoveLock(false), i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), +_creatureToMoveLock(false), _gameObjectsToMoveLock(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()), i_gridExpiry(expiry), +m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()), +i_gridExpiry(expiry), i_scriptLock(false) { m_parentMap = (_parent ? _parent : this); @@ -270,6 +283,21 @@ void Map::AddToGrid(Creature* obj, Cell const& cell) obj->SetCurrentCell(cell); } +template<> +void Map::AddToGrid(GameObject* 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*/) +{ +} + +template<> void Map::SwitchGridContainers(Creature* obj, bool on) { ASSERT(!obj->IsPermanentWorldObject()); @@ -291,6 +319,7 @@ void Map::SwitchGridContainers(Creature* obj, bool on) GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY()); obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add + if (on) { grid.AddWorldObject(obj); @@ -301,9 +330,45 @@ void Map::SwitchGridContainers(Creature* obj, bool on) grid.AddGridObject(obj); RemoveWorldObject(obj); } + obj->m_isTempWorldObject = on; } +template<> +void Map::SwitchGridContainers(GameObject* obj, bool on) +{ + ASSERT(!obj->IsPermanentWorldObject()); + CellCoord p = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); + if (!p.IsCoordValid()) + { + TC_LOG_ERROR(LOG_FILTER_MAPS, "Map::SwitchGridContainers: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y))) + return; + + TC_LOG_DEBUG(LOG_FILTER_MAPS, "Switch object " UI64FMTD " from grid[%u, %u] %u", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y, on); + NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY()); + ASSERT(ngrid != NULL); + + GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY()); + + obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add + + if (on) + { + grid.AddWorldObject(obj); + AddWorldObject(obj); + } + else + { + grid.AddGridObject(obj); + RemoveWorldObject(obj); + } +} + template<class T> void Map::DeleteFromWorld(T* obj) { @@ -432,11 +497,17 @@ void Map::InitializeObject(T* /*obj*/) template<> void Map::InitializeObject(Creature* obj) { - obj->_moveState = CREATURE_CELL_MOVE_NONE; + obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; +} + +template<> +void Map::InitializeObject(GameObject* obj) +{ + obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; } template<class T> -bool Map::AddToMap(T *obj) +bool Map::AddToMap(T* obj) { /// @todo Needs clean up. An object should not be added to map twice. if (obj->IsInWorld()) @@ -480,6 +551,26 @@ bool Map::AddToMap(T *obj) return true; } +template<> +bool Map::AddToMap(Transport* obj) +{ + //TODO: Needs clean up. An object should not be added to map twice. + if (obj->IsInWorld()) + return true; + + CellCoord cellCoord = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); + if (!cellCoord.IsCoordValid()) + { + TC_LOG_ERROR(LOG_FILTER_MAPS, "Map::Add: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord); + return false; //Should delete object + } + + obj->AddToWorld(); + _transports.insert(obj); + + return true; +} + bool Map::IsGridLoaded(const GridCoord &p) const { return (getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord)); @@ -565,6 +656,17 @@ void Map::Update(const uint32 t_diff) VisitNearbyCellsOf(obj, grid_object_update, world_object_update); } + for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) + { + WorldObject* obj = *_transportsUpdateIter; + ++_transportsUpdateIter; + + if (!obj->IsInWorld()) + continue; + + obj->Update(t_diff); + } + ///- Process necessary scripts if (!m_scriptSchedule.empty()) { @@ -574,6 +676,7 @@ void Map::Update(const uint32 t_diff) } MoveAllCreaturesInMoveList(); + MoveAllGameObjectsInMoveList(); if (!m_mapRefManager.isEmpty() || !m_activeNonPlayers.empty()) ProcessRelocationNotifies(t_diff); @@ -708,6 +811,34 @@ void Map::RemoveFromMap(T *obj, bool remove) } } +template<> +void Map::RemoveFromMap(Transport* obj, bool remove) +{ + obj->RemoveFromWorld(); + + if (_transportsUpdateIter != _transports.end()) + { + TransportsContainer::iterator itr = _transports.find(obj); + if (itr == _transports.end()) + return; + if (itr == _transportsUpdateIter) + ++_transportsUpdateIter; + _transports.erase(itr); + } + else + _transports.erase(obj); + + obj->ResetMap(); + + if (remove) + { + // if option set then object already saved at this moment + if (!sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY)) + obj->SaveRespawnTime(); + DeleteFromWorld(obj); + } +} + void Map::PlayerRelocation(Player* player, float x, float y, float z, float orientation) { ASSERT(player); @@ -777,12 +908,44 @@ void Map::CreatureRelocation(Creature* creature, float x, float y, float z, floa ASSERT(CheckGridIntegrity(creature, true)); } +void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float orientation, bool respawnRelocationOnFail) +{ + Cell integrity_check(go->GetPositionX(), go->GetPositionY()); + Cell old_cell = go->GetCurrentCell(); + + ASSERT(integrity_check == old_cell); + Cell new_cell(x, y); + + if (!respawnRelocationOnFail && !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(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) added to moving list from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), 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 + AddGameObjectToMoveList(go, x, y, z, orientation); + // in diffcell/diffgrid case notifiers called at finishing move go in Map::MoveAllGameObjectsInMoveList + } + else + { + go->Relocate(x, y, z, orientation); + go->UpdateObjectVisibility(false); + RemoveGameObjectFromMoveList(go); + } + + old_cell = go->GetCurrentCell(); + integrity_check = Cell(go->GetPositionX(), go->GetPositionY()); + ASSERT(integrity_check == old_cell); +} + void Map::AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang) { if (_creatureToMoveLock) //can this happen? return; - if (c->_moveState == CREATURE_CELL_MOVE_NONE) + if (c->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _creaturesToMove.push_back(c); c->SetNewCellPosition(x, y, z, ang); } @@ -792,8 +955,27 @@ void Map::RemoveCreatureFromMoveList(Creature* c) if (_creatureToMoveLock) //can this happen? return; - if (c->_moveState == CREATURE_CELL_MOVE_ACTIVE) - c->_moveState = CREATURE_CELL_MOVE_INACTIVE; + if (c->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) + c->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; +} + +void Map::AddGameObjectToMoveList(GameObject* go, float x, float y, float z, float ang) +{ + if (_gameObjectsToMoveLock) //can this happen? + return; + + if (go->_moveState == MAP_OBJECT_CELL_MOVE_NONE) + _gameObjectsToMove.push_back(go); + go->SetNewCellPosition(x, y, z, ang); +} + +void Map::RemoveGameObjectFromMoveList(GameObject* go) +{ + if (_gameObjectsToMoveLock) //can this happen? + return; + + if (go->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) + go->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } void Map::MoveAllCreaturesInMoveList() @@ -805,13 +987,13 @@ void Map::MoveAllCreaturesInMoveList() if (c->FindMap() != this) //pet is teleported to another map continue; - if (c->_moveState != CREATURE_CELL_MOVE_ACTIVE) + if (c->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) { - c->_moveState = CREATURE_CELL_MOVE_NONE; + c->_moveState = MAP_OBJECT_CELL_MOVE_NONE; continue; } - c->_moveState = CREATURE_CELL_MOVE_NONE; + c->_moveState = MAP_OBJECT_CELL_MOVE_NONE; if (!c->IsInWorld()) continue; @@ -852,6 +1034,50 @@ void Map::MoveAllCreaturesInMoveList() _creatureToMoveLock = false; } +void Map::MoveAllGameObjectsInMoveList() +{ + _gameObjectsToMoveLock = true; + for (std::vector<GameObject*>::iterator itr = _gameObjectsToMove.begin(); itr != _gameObjectsToMove.end(); ++itr) + { + GameObject* go = *itr; + if (go->FindMap() != this) //transport is teleported to another map + continue; + + if (go->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) + { + go->_moveState = MAP_OBJECT_CELL_MOVE_NONE; + continue; + } + + go->_moveState = MAP_OBJECT_CELL_MOVE_NONE; + if (!go->IsInWorld()) + continue; + + // do move or do move to respawn or remove creature if previous all fail + if (GameObjectCellRelocation(go, Cell(go->_newPosition.m_positionX, go->_newPosition.m_positionY))) + { + // update pos + go->Relocate(go->_newPosition); + go->UpdateObjectVisibility(false); + } + else + { + // if GameObject can't be move in new cell/grid (not loaded) move it to repawn cell/grid + // GameObject coordinates will be updated and notifiers send + if (!GameObjectRespawnRelocation(go, false)) + { + // ... or unload (if respawn grid also not loaded) +#ifdef TRINITY_DEBUG + sLog->outDebug(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) cannot be move to unloaded respawn grid.", go->GetGUIDLow(), go->GetEntry()); +#endif + AddObjectToRemoveList(go); + } + } + } + _gameObjectsToMove.clear(); + _gameObjectsToMoveLock = false; +} + bool Map::CreatureCellRelocation(Creature* c, Cell new_cell) { Cell const& old_cell = c->GetCurrentCell(); @@ -913,6 +1139,67 @@ bool Map::CreatureCellRelocation(Creature* c, Cell new_cell) return false; } +bool Map::GameObjectCellRelocation(GameObject* 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(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) moved in grid[%u, %u] from cell[%u, %u] to cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), 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(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) moved in same grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), 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(LOG_FILTER_MAPS, "Active GameObject (GUID: %u Entry: %u) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), 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(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), 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(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) attempted to move from grid[%u, %u]cell[%u, %u] to unloaded grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), 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; @@ -943,6 +1230,31 @@ bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly) return false; } +bool Map::GameObjectRespawnRelocation(GameObject* go, bool diffGridOnly) +{ + float resp_x, resp_y, resp_z, resp_o; + go->GetRespawnPosition(resp_x, resp_y, resp_z, &resp_o); + Cell resp_cell(resp_x, resp_y); + + //GameObject will be unloaded with grid + if (diffGridOnly && !go->GetCurrentCell().DiffGrid(resp_cell)) + return true; + + #ifdef TRINITY_DEBUG + TC_LOG_DEBUG(LOG_FILTER_MAPS, "GameObject (GUID: %u Entry: %u) moved from grid[%u, %u]cell[%u, %u] to respawn grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), go->GetEntry(), go->GetCurrentCell().GridX(), go->GetCurrentCell().GridY(), go->GetCurrentCell().CellX(), go->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY()); + #endif + + // teleport it to respawn point (like normal respawn if player see) + if (GameObjectCellRelocation(go, resp_cell)) + { + go->Relocate(resp_x, resp_y, resp_z, resp_o); + go->UpdateObjectVisibility(false); + return true; + } + + return false; +} + bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) { const uint32 x = ngrid.getX(); @@ -966,6 +1278,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids // Must know real mob position before move MoveAllCreaturesInMoveList(); + MoveAllGameObjectsInMoveList(); // move creatures to respawn grids if this is diff.grid or to remove list ObjectGridEvacuator worker; @@ -974,6 +1287,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) // Finish creature moves, remove and delete all creatures with delayed remove before unload MoveAllCreaturesInMoveList(); + MoveAllGameObjectsInMoveList(); } { @@ -1041,6 +1355,7 @@ void Map::UnloadAll() { // clear all delayed moves, useless anyway do this moves before map unload. _creaturesToMove.clear(); + _gameObjectsToMove.clear(); for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end();) { @@ -2008,7 +2323,7 @@ void Map::SendInitSelf(Player* player) // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map if (Transport* transport = player->GetTransport()) { - for (Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) + for (std::set<WorldObject*>::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) { if (player != (*itr) && player->HaveAtClient(*itr)) { @@ -2025,24 +2340,10 @@ void Map::SendInitSelf(Player* player) void Map::SendInitTransports(Player* player) { // Hack to send out transports - MapManager::TransportMap& tmap = sMapMgr->m_TransportsByMap; - - // no transports at map - if (tmap.find(player->GetMapId()) == tmap.end()) - return; - UpdateData transData; - - MapManager::TransportSet& tset = tmap[player->GetMapId()]; - - for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i) - { - // send data for current transport in other place - if ((*i) != player->GetTransport() && (*i)->GetMapId() == GetId()) - { + for (TransportsContainer::const_iterator i = _transports.begin(); i != _transports.end(); ++i) + if (*i != player->GetTransport()) (*i)->BuildCreateUpdateBlockForPlayer(&transData, player); - } - } WorldPacket packet; transData.BuildPacket(&packet); @@ -2052,19 +2353,9 @@ void Map::SendInitTransports(Player* player) void Map::SendRemoveTransports(Player* player) { // Hack to send out transports - MapManager::TransportMap& tmap = sMapMgr->m_TransportsByMap; - - // no transports at map - if (tmap.find(player->GetMapId()) == tmap.end()) - return; - UpdateData transData; - - MapManager::TransportSet& tset = tmap[player->GetMapId()]; - - // except used transport - for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i) - if ((*i) != player->GetTransport() && (*i)->GetMapId() != GetId()) + for (TransportsContainer::const_iterator i = _transports.begin(); i != _transports.end(); ++i) + if (*i != player->GetTransport()) (*i)->BuildOutOfRangeUpdateBlock(&transData); WorldPacket packet; @@ -2116,7 +2407,7 @@ void Map::AddObjectToSwitchList(WorldObject* obj, bool on) ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId()); // i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses // the contained objects only if GetTypeId() == TYPEID_UNIT , so we can return in all other cases - if (obj->GetTypeId() != TYPEID_UNIT) + if (obj->GetTypeId() != TYPEID_UNIT && obj->GetTypeId() != TYPEID_GAMEOBJECT) return; std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.find(obj); @@ -2137,8 +2428,8 @@ void Map::RemoveAllObjectsInRemoveList() bool on = itr->second; i_objectsToSwitch.erase(itr); - if (obj->GetTypeId() == TYPEID_UNIT && !obj->IsPermanentWorldObject()) - SwitchGridContainers(obj->ToCreature(), on); + if ((obj->GetTypeId() == TYPEID_UNIT || obj->GetTypeId() == TYPEID_GAMEOBJECT) && !obj->IsPermanentWorldObject()) + SwitchGridContainers(obj, on); } //TC_LOG_DEBUG(LOG_FILTER_MAPS, "Object remover 1 check."); @@ -2233,6 +2524,13 @@ bool Map::ActiveObjectsNearGrid(NGridType const& ngrid) const return false; } +template<class T> +void Map::AddToActive(T* obj) +{ + AddToActiveHelper(obj); +} + +template <> void Map::AddToActive(Creature* c) { AddToActiveHelper(c); @@ -2254,6 +2552,18 @@ void Map::AddToActive(Creature* c) } } +template<> +void Map::AddToActive(DynamicObject* d) +{ + AddToActiveHelper(d); +} + +template<class T> +void Map::RemoveFromActive(T* /*obj*/) +{ +} + +template <> void Map::RemoveFromActive(Creature* c) { RemoveFromActiveHelper(c); @@ -2275,6 +2585,12 @@ void Map::RemoveFromActive(Creature* c) } } +template<> +void Map::RemoveFromActive(DynamicObject* obj) +{ + RemoveFromActiveHelper(obj); +} + template bool Map::AddToMap(Corpse*); template bool Map::AddToMap(Creature*); template bool Map::AddToMap(GameObject*); @@ -2772,6 +3088,15 @@ GameObject* Map::GetGameObject(uint64 guid) return ObjectAccessor::GetObjectInMap(guid, this, (GameObject*)NULL); } +Transport* Map::GetTransport(uint64 guid) +{ + if (GUID_HIPART(guid) != HIGHGUID_MO_TRANSPORT) + return NULL; + + GameObject* go = GetGameObject(guid); + return go ? go->ToTransport() : NULL; +} + DynamicObject* Map::GetDynamicObject(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (DynamicObject*)NULL); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 3deeb4e04b1..932f3e213ae 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -52,6 +52,7 @@ struct Position; class Battleground; class MapInstanced; class InstanceMap; +class Transport; namespace Trinity { struct ObjectUpdater; } struct ScriptAction @@ -278,6 +279,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); template<class T, class CONTAINER> void Visit(const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor); @@ -350,11 +352,13 @@ class Map : public GridRefManager<NGridType> } void MoveAllCreaturesInMoveList(); + void MoveAllGameObjectsInMoveList(); void RemoveAllObjectsInRemoveList(); virtual void RemoveAllPlayers(); // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader bool CreatureRespawnRelocation(Creature* c, bool diffGridOnly); + bool GameObjectRespawnRelocation(GameObject* go, bool diffGridOnly); // assert print helper bool CheckGridIntegrity(Creature* c, bool moved) const; @@ -415,17 +419,13 @@ class Map : public GridRefManager<NGridType> // must called with AddToWorld template<class T> - void AddToActive(T* obj) { AddToActiveHelper(obj); } - - void AddToActive(Creature* obj); + void AddToActive(T* obj); // must called with RemoveFromWorld template<class T> - void RemoveFromActive(T* obj) { RemoveFromActiveHelper(obj); } - - void RemoveFromActive(Creature* obj); + void RemoveFromActive(T* obj); - void SwitchGridContainers(Creature* creature, bool toWorldContainer); + template<class T> void SwitchGridContainers(T* obj, bool on); template<class NOTIFIER> void VisitAll(const float &x, const float &y, float radius, NOTIFIER ¬ifier); template<class NOTIFIER> void VisitFirstFound(const float &x, const float &y, float radius, NOTIFIER ¬ifier); template<class NOTIFIER> void VisitWorld(const float &x, const float &y, float radius, NOTIFIER ¬ifier); @@ -438,6 +438,7 @@ class Map : public GridRefManager<NGridType> void SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list = NULL); Creature* GetCreature(uint64 guid); GameObject* GetGameObject(uint64 guid); + Transport* GetTransport(uint64 guid); DynamicObject* GetDynamicObject(uint64 guid); MapInstanced* ToMapInstanced(){ if (Instanceable()) return reinterpret_cast<MapInstanced*>(this); else return NULL; } @@ -485,6 +486,9 @@ class Map : public GridRefManager<NGridType> static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId); + void SendInitTransports(Player* player); + void SendRemoveTransports(Player* player); + private: void LoadMapAndVMap(int gx, int gy); void LoadVMap(int gx, int gy); @@ -496,18 +500,21 @@ class Map : public GridRefManager<NGridType> void SendInitSelf(Player* player); - void SendInitTransports(Player* player); - void SendRemoveTransports(Player* player); - bool CreatureCellRelocation(Creature* creature, Cell new_cell); + bool GameObjectCellRelocation(GameObject* 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); bool _creatureToMoveLock; std::vector<Creature*> _creaturesToMove; + bool _gameObjectsToMoveLock; + std::vector<GameObject*> _gameObjectsToMove; + bool IsGridLoaded(const GridCoord &) const; void EnsureGridCreated(const GridCoord &); void EnsureGridCreated_i(const GridCoord &); @@ -516,9 +523,6 @@ class Map : public GridRefManager<NGridType> void buildNGridLinkage(NGridType* pNGridType) { pNGridType->link(this); } - template<class T> void AddType(T *obj); - template<class T> void RemoveType(T *obj, bool); - NGridType* getNGrid(uint32 x, uint32 y) const { ASSERT(x < MAX_NUMBER_OF_GRIDS && y < MAX_NUMBER_OF_GRIDS); @@ -555,6 +559,11 @@ class Map : public GridRefManager<NGridType> ActiveNonPlayers m_activeNonPlayers; ActiveNonPlayers::iterator m_activeNonPlayersIter; + // Objects that must update even in inactive grids without activating them + typedef std::set<Transport*> TransportsContainer; + TransportsContainer _transports; + TransportsContainer::iterator _transportsUpdateIter; + private: Player* _GetScriptPlayerSourceOrTarget(Object* source, Object* target, const ScriptInfo* scriptInfo) const; Creature* _GetScriptCreatureSourceOrTarget(Object* source, Object* target, const ScriptInfo* scriptInfo, bool bReverse = false) const; @@ -589,19 +598,17 @@ class Map : public GridRefManager<NGridType> // Type specific code for add/remove to/from grid template<class T> - void AddToGrid(T* object, Cell const& cell); + void AddToGrid(T* object, Cell const& cell); template<class T> - void DeleteFromWorld(T*); + void DeleteFromWorld(T*); - template<class T> - void AddToActiveHelper(T* obj) + void AddToActiveHelper(WorldObject* obj) { m_activeNonPlayers.insert(obj); } - template<class T> - void RemoveFromActiveHelper(T* obj) + void RemoveFromActiveHelper(WorldObject* obj) { // Map::Update for active object in proccess if (m_activeNonPlayersIter != m_activeNonPlayers.end()) diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index 60ebd3f8699..5aa8b85fbca 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -292,8 +292,6 @@ void MapManager::Update(uint32 diff) iter->second->DelayedUpdate(uint32(i_timer.GetCurrent())); sObjectAccessor->Update(uint32(i_timer.GetCurrent())); - for (TransportSet::iterator itr = m_Transports.begin(); itr != m_Transports.end(); ++itr) - (*itr)->Update(uint32(i_timer.GetCurrent())); i_timer.SetCurrent(0); } @@ -326,12 +324,6 @@ bool MapManager::IsValidMAP(uint32 mapid, bool startUp) void MapManager::UnloadAll() { - for (TransportSet::iterator i = m_Transports.begin(); i != m_Transports.end(); ++i) - { - (*i)->RemoveFromWorld(); - delete *i; - } - for (MapMapType::iterator iter = i_maps.begin(); iter != i_maps.end();) { iter->second->UnloadAll(); diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 8af609c61e2..230b4648f4a 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -107,15 +107,6 @@ class MapManager void DoDelayedMovesAndRemoves(); - void LoadTransports(); - void LoadTransportNPCs(); - - typedef std::set<Transport*> TransportSet; - TransportSet m_Transports; - - typedef std::map<uint32, TransportSet> TransportMap; - TransportMap m_TransportsByMap; - bool CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck = false); void InitializeVisibilityDistanceInfo(); diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp new file mode 100644 index 00000000000..bd79cd793d5 --- /dev/null +++ b/src/server/game/Maps/TransportMgr.cpp @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2008-2012 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 "TransportMgr.h" +#include "Transport.h" +#include "InstanceScript.h" +#include "MoveSpline.h" +#include "MapManager.h" + +TransportTemplate::~TransportTemplate() +{ + // Collect shared pointers into a set to avoid deleting the same memory more than once + std::set<TransportSpline*> splines; + for (size_t i = 0; i < keyFrames.size(); ++i) + splines.insert(keyFrames[i].Spline); + + for (std::set<TransportSpline*>::iterator itr = splines.begin(); itr != splines.end(); ++itr) + delete *itr; +} + +TransportMgr::TransportMgr() +{ +} + +TransportMgr::~TransportMgr() +{ +} + +void TransportMgr::Unload() +{ + _transportTemplates.clear(); +} + +void TransportMgr::LoadTransportTemplates() +{ + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.Query("SELECT entry FROM gameobject_template WHERE type = 15 ORDER BY entry ASC"); + + if (!result) + { + TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 transport templates. DB table `gameobject_template` has no transports!"); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); + GameObjectTemplate const* goInfo = sObjectMgr->GetGameObjectTemplate(entry); + if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size()) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "Transport %u (name: %s) has an invalid path specified in `gameobject_template`.`data0` (%u) field, skipped.", entry, goInfo->name.c_str(), goInfo->moTransport.taxiPathId); + continue; + } + + // paths are generated per template, saves us from generating it again in case of instanced transports + TransportTemplate& transport = _transportTemplates[entry]; + transport.entry = entry; + GeneratePath(goInfo, &transport); + + // transports in instance are only on one map + if (transport.inInstance) + _instanceTransports[*transport.mapsUsed.begin()].insert(entry); + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Loaded %u transport templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void TransportMgr::GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport) +{ + uint32 pathId = goInfo->moTransport.taxiPathId; + TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId]; + std::vector<KeyFrame>& keyFrames = transport->keyFrames; + Movement::PointsArray splinePath; + bool mapChange = false; + bool cyclic = true; + for (size_t i = 1; i < path.size() - 1; ++i) + { + if (!mapChange) + { + TaxiPathNodeEntry const& node_i = path[i]; + if (node_i.actionFlag == 1 || node_i.mapid != path[i + 1].mapid) + { + cyclic = false; + keyFrames.back().Teleport = true; + mapChange = true; + } + else + { + KeyFrame k(node_i); + keyFrames.push_back(k); + splinePath.push_back(G3D::Vector3(node_i.x, node_i.y, node_i.z)); + transport->mapsUsed.insert(k.Node->mapid); + } + } + else + mapChange = false; + } + + if (transport->mapsUsed.size() > 1) + { + for (std::set<uint32>::const_iterator itr = transport->mapsUsed.begin(); itr != transport->mapsUsed.end(); ++itr) + ASSERT(!sMapStore.LookupEntry(*itr)->Instanceable()); + + transport->inInstance = false; + } + else + transport->inInstance = sMapStore.LookupEntry(*transport->mapsUsed.begin())->Instanceable(); + + // last to first is always "teleport", even for closed paths + keyFrames.back().Teleport = true; + + const float speed = float(goInfo->moTransport.moveSpeed); + const float accel = float(goInfo->moTransport.accelRate); + const float accel_dist = 0.5f * speed * speed / accel; + + transport->accelTime = speed / accel; + transport->accelDist = accel_dist; + + int32 firstStop = -1; + int32 lastStop = -1; + + // first cell is arrived at by teleportation :S + keyFrames[0].DistFromPrev = 0; + keyFrames[0].Index = 1; + if (keyFrames[0].IsStopFrame()) + { + firstStop = 0; + lastStop = 0; + } + + // find the rest of the distances between key points + // Every path segment has its own spline + if (cyclic) + { + TransportSpline* spline = new TransportSpline(); + spline->init_cyclic_spline(&splinePath[0], splinePath.size(), Movement::SplineBase::ModeCatmullrom, 0); + spline->initLengths(); + keyFrames[0].DistFromPrev = spline->length(spline->last() - 2, spline->last() - 1); + keyFrames[0].Spline = spline; + for (size_t i = 1; i < keyFrames.size(); ++i) + { + keyFrames[i].Index = i + 1; + keyFrames[i].DistFromPrev = spline->length(i, i + 1); + keyFrames[i - 1].NextDistFromPrev = keyFrames[i].DistFromPrev; + keyFrames[i].Spline = spline; + if (keyFrames[i].IsStopFrame()) + { + // remember first stop frame + if (firstStop == -1) + firstStop = i; + lastStop = i; + } + } + } + else + { + size_t start = 0; + for (size_t i = 1; i < keyFrames.size(); ++i) + { + if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.size()) + { + size_t extra = !keyFrames[i - 1].Teleport ? 1 : 0; + TransportSpline* spline = new TransportSpline(); + spline->init_spline(&splinePath[start], i - start + extra, Movement::SplineBase::ModeCatmullrom); + spline->initLengths(); + for (size_t j = start; j < i + extra; ++j) + { + keyFrames[j].Index = j - start + 1; + keyFrames[j].DistFromPrev = spline->length(j - start, j + 1 - start); + if (j > 0) + keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev; + keyFrames[j].Spline = spline; + } + + if (keyFrames[i - 1].Teleport) + { + keyFrames[i].Index = i - start + 1; + keyFrames[i].DistFromPrev = 0.0f; + keyFrames[i - 1].NextDistFromPrev = 0.0f; + keyFrames[i].Spline = spline; + } + + start = i; + } + + if (keyFrames[i].IsStopFrame()) + { + // remember first stop frame + if (firstStop == -1) + firstStop = i; + lastStop = i; + } + } + } + + keyFrames.back().NextDistFromPrev = keyFrames.front().DistFromPrev; + + // at stopping keyframes, we define distSinceStop == 0, + // and distUntilStop is to the next stopping keyframe. + // this is required to properly handle cases of two stopping frames in a row (yes they do exist) + float tmpDist = 0.0f; + for (size_t i = 0; i < keyFrames.size(); ++i) + { + int32 j = (i + lastStop) % keyFrames.size(); + if (keyFrames[j].IsStopFrame()) + tmpDist = 0.0f; + else + tmpDist += keyFrames[j].DistFromPrev; + keyFrames[j].DistSinceStop = tmpDist; + } + + tmpDist = 0.0f; + for (int32 i = int32(keyFrames.size()) - 1; i >= 0; i--) + { + int32 j = (i + firstStop) % keyFrames.size(); + tmpDist += keyFrames[(j + 1) % keyFrames.size()].DistFromPrev; + keyFrames[j].DistUntilStop = tmpDist; + if (keyFrames[j].IsStopFrame()) + tmpDist = 0.0f; + } + + for (size_t i = 0; i < keyFrames.size(); ++i) + { + float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop; + if (total_dist < 2 * accel_dist) // won't reach full speed + { + if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating + { + // calculate accel+brake time for this short segment + float segment_time = 2.0f * sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel); + // substract acceleration time + keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel); + } + else // slowing down + keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel); + } + else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed) + { + // calculate accel + cruise + brake time for this long segment + float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel); + // substract acceleration time + keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel); + } + else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed) + keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel); + else // at full speed + keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel); + } + + // calculate tFrom times from tTo times + float segmentTime = 0.0f; + for (size_t i = 0; i < keyFrames.size(); ++i) + { + int32 j = (i + lastStop) % keyFrames.size(); + if (keyFrames[j].IsStopFrame()) + segmentTime = keyFrames[j].TimeTo; + keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo; + } + + // calculate path times + keyFrames[0].ArriveTime = 0; + float curPathTime = 0.0f; + if (keyFrames[0].IsStopFrame()) + { + curPathTime = float(keyFrames[0].Node->delay); + keyFrames[0].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); + } + + for (size_t i = 1; i < keyFrames.size(); ++i) + { + curPathTime += keyFrames[i-1].TimeTo; + if (keyFrames[i].IsStopFrame()) + { + keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); + keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; + curPathTime += (float)keyFrames[i].Node->delay; + keyFrames[i].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); + } + else + { + curPathTime -= keyFrames[i].TimeTo; + keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); + keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; + keyFrames[i].DepartureTime = keyFrames[i].ArriveTime; + } + } + keyFrames.back().NextArriveTime = keyFrames.back().DepartureTime; + + transport->pathTime = keyFrames.back().DepartureTime; + //WorldDatabase.DirectPExecute("UPDATE `transports` SET `period_gen`=%u WHERE `entry`=%u", transport->pathTime, transport->entry); +} + +void TransportMgr::AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const* node) +{ + TransportAnimation& animNode = _transportAnimations[transportEntry]; + if (animNode.TotalTime < timeSeg) + animNode.TotalTime = timeSeg; + + animNode.Path[timeSeg] = node; +} + +Transport* TransportMgr::CreateTransport(uint32 entry, uint32 guid /*= 0*/, Map* map /*= NULL*/) +{ + // instance case, execute GetGameObjectEntry hook + if (map) + { + // SetZoneScript() is called after adding to map, so fetch the script using map + if (map->IsDungeon()) + if (InstanceScript* instance = static_cast<InstanceMap*>(map)->GetInstanceScript()) + entry = instance->GetGameObjectEntry(0, entry); + + if (!entry) + return NULL; + } + + TransportTemplate const* tInfo = GetTransportTemplate(entry); + if (!tInfo) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "Transport %u will not be loaded, `transport_template` missing", entry); + return NULL; + } + + // create transport... + Transport* trans = new Transport(); + + // ...at first waypoint + TaxiPathNodeEntry const* startNode = tInfo->keyFrames.begin()->Node; + uint32 mapId = startNode->mapid; + float x = startNode->x; + float y = startNode->y; + float z = startNode->z; + float o = 0.0f; + + // initialize the gameobject base + uint32 guidLow = guid ? guid : sObjectMgr->GenerateLowGuid(HIGHGUID_MO_TRANSPORT); + if (!trans->Create(guidLow, entry, mapId, x, y, z, o, 255)) + { + delete trans; + return NULL; + } + + if (MapEntry const* mapEntry = sMapStore.LookupEntry(mapId)) + { + if (mapEntry->Instanceable() != tInfo->inInstance) + { + TC_LOG_ERROR(LOG_FILTER_TRANSPORTS, "Transport %u (name: %s) attempted creation in instance map (id: %u) but it is not an instanced transport!", entry, trans->GetName().c_str(), mapId); + delete trans; + return NULL; + } + } + + // use preset map for instances (need to know which instance) + trans->SetMap(map ? map : sMapMgr->CreateMap(mapId, NULL)); + if (map && map->IsDungeon()) + trans->m_zoneScript = map->ToInstanceMap()->GetInstanceScript(); + + // Passengers will be loaded once a player is near + + trans->GetMap()->AddToMap<Transport>(trans); + return trans; +} + +void TransportMgr::SpawnContinentTransports() +{ + if (_transportTemplates.empty()) + return; + + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.Query("SELECT guid, entry FROM transports"); + + uint32 count = 0; + if (result) + { + do + { + Field* fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + uint32 entry = fields[1].GetUInt32(); + + if (TransportTemplate const* tInfo = GetTransportTemplate(entry)) + if (!tInfo->inInstance) + if (CreateTransport(entry, guid)) + ++count; + + } while (result->NextRow()); + } + + TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, ">> Spawned %u continent transports in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void TransportMgr::CreateInstanceTransports(Map* map) +{ + TransportInstanceMap::const_iterator mapTransports = _instanceTransports.find(map->GetId()); + + // no transports here + if (mapTransports == _instanceTransports.end() || mapTransports->second.empty()) + return; + + // create transports + for (std::set<uint32>::const_iterator itr = mapTransports->second.begin(); itr != mapTransports->second.end(); ++itr) + CreateTransport(*itr, 0, map); +} + +TransportAnimationEntry const* TransportAnimation::GetAnimNode(uint32 time) const +{ + if (Path.empty()) + return NULL; + + for (TransportPathContainer::const_reverse_iterator itr2 = Path.rbegin(); itr2 != Path.rend(); ++itr2) + if (time >= itr2->first) + return itr2->second; + + return Path.begin()->second; +} + +G3D::Quat TransportAnimation::GetAnimRotation(uint32 time) const +{ + if (Rotations.empty()) + return G3D::Quat(0.0f, 0.0f, 0.0f, 1.0f); + + TransportRotationEntry const* rot = Rotations.begin()->second; + for (TransportPathRotationContainer::const_reverse_iterator itr2 = Rotations.rbegin(); itr2 != Rotations.rend(); ++itr2) + { + if (time >= itr2->first) + { + rot = itr2->second; + break; + } + } + + return G3D::Quat(rot->X, rot->Y, rot->Z, rot->W); +} diff --git a/src/server/game/Maps/TransportMgr.h b/src/server/game/Maps/TransportMgr.h new file mode 100644 index 00000000000..250a2c50bb1 --- /dev/null +++ b/src/server/game/Maps/TransportMgr.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRANSPORTMGR_H +#define TRANSPORTMGR_H + +#include <ace/Singleton.h> +#include <G3D/Quat.h> +#include "Spline.h" +#include "DBCStores.h" + +struct KeyFrame; +struct GameObjectTemplate; +struct TransportTemplate; +class Transport; +class Map; + +typedef Movement::Spline<double> TransportSpline; +typedef std::vector<KeyFrame> KeyFrameVec; +typedef UNORDERED_MAP<uint32, TransportTemplate> TransportTemplates; +typedef std::set<Transport*> TransportSet; +typedef UNORDERED_MAP<uint32, TransportSet> TransportMap; +typedef UNORDERED_MAP<uint32, std::set<uint32> > TransportInstanceMap; + +struct KeyFrame +{ + explicit KeyFrame(TaxiPathNodeEntry const& _node) : Node(&_node), + DistSinceStop(-1.0f), DistUntilStop(-1.0f), DistFromPrev(-1.0f), TimeFrom(0.0f), TimeTo(0.0f), + Teleport(false), ArriveTime(0), DepartureTime(0), Spline(NULL), NextDistFromPrev(0.0f), NextArriveTime(0) + { + } + + uint32 Index; + TaxiPathNodeEntry const* Node; + float DistSinceStop; + float DistUntilStop; + float DistFromPrev; + float TimeFrom; + float TimeTo; + bool Teleport; + uint32 ArriveTime; + uint32 DepartureTime; + TransportSpline* Spline; + + // Data needed for next frame + float NextDistFromPrev; + uint32 NextArriveTime; + + bool IsTeleportFrame() const { return Teleport; } + bool IsStopFrame() const { return Node->actionFlag == 2; } +}; + +struct TransportTemplate +{ + TransportTemplate() : pathTime(0), accelTime(0.0f), accelDist(0.0f) { } + ~TransportTemplate(); + + std::set<uint32> mapsUsed; + bool inInstance; + uint32 pathTime; + KeyFrameVec keyFrames; + float accelTime; + float accelDist; + uint32 entry; +}; + +typedef std::map<uint32, TransportAnimationEntry const*> TransportPathContainer; +typedef std::map<uint32, TransportRotationEntry const*> TransportPathRotationContainer; + +struct TransportAnimation +{ + TransportPathContainer Path; + TransportPathRotationContainer Rotations; + uint32 TotalTime; + + TransportAnimationEntry const* GetAnimNode(uint32 time) const; + G3D::Quat GetAnimRotation(uint32 time) const; +}; + +typedef std::map<uint32, TransportAnimation> TransportAnimationContainer; + +class TransportMgr +{ + friend class ACE_Singleton<TransportMgr, ACE_Thread_Mutex>; + friend void LoadDBCStores(std::string const&); + + public: + void Unload(); + + void LoadTransportTemplates(); + + // Creates a transport using given GameObject template entry + Transport* CreateTransport(uint32 entry, uint32 guid = 0, Map* map = NULL); + + // Spawns all continent transports, used at core startup + void SpawnContinentTransports(); + + // creates all transports for instance + void CreateInstanceTransports(Map* map); + + TransportTemplate const* GetTransportTemplate(uint32 entry) const + { + TransportTemplates::const_iterator itr = _transportTemplates.find(entry); + if (itr != _transportTemplates.end()) + return &itr->second; + return NULL; + } + + TransportAnimation const* GetTransportAnimInfo(uint32 entry) const + { + TransportAnimationContainer::const_iterator itr = _transportAnimations.find(entry); + if (itr != _transportAnimations.end()) + return &itr->second; + + return NULL; + } + + private: + TransportMgr(); + ~TransportMgr(); + TransportMgr(TransportMgr const&); + TransportMgr& operator=(TransportMgr const&); + + // Generates and precaches a path for transport to avoid generation each time transport instance is created + void GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport); + + void AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const* node); + + void AddPathRotationToTransport(uint32 transportEntry, uint32 timeSeg, TransportRotationEntry const* node) + { + _transportAnimations[transportEntry].Rotations[timeSeg] = node; + } + + // Container storing transport templates + TransportTemplates _transportTemplates; + + // Container storing transport entries to create for instanced maps + TransportInstanceMap _instanceTransports; + + TransportAnimationContainer _transportAnimations; +}; + +#define sTransportMgr ACE_Singleton<TransportMgr, ACE_Thread_Mutex>::instance() + +#endif // TRANSPORTMGR_H diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index c50db983845..327d7ed69fd 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -104,7 +104,7 @@ enum TrinityStrings LANG_RBAC_PERM_REVOKED = 78, LANG_RBAC_PERM_REVOKED_NOT_IN_LIST = 79, // Free 80 - 95 - + LANG_GUILD_RENAME_ALREADY_EXISTS = 96, LANG_GUILD_RENAME_DONE = 97, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index e561d37ed36..02f5965836f 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -1495,7 +1495,8 @@ enum GameObjectDynamicLowFlags GO_DYNFLAG_LO_ACTIVATE = 0x01, // enables interaction with GO GO_DYNFLAG_LO_ANIMATE = 0x02, // possibly more distinct animation of GO GO_DYNFLAG_LO_NO_INTERACT = 0x04, // appears to disable interaction (not fully verified) - GO_DYNFLAG_LO_SPARKLE = 0x08 // makes GO sparkle + GO_DYNFLAG_LO_SPARKLE = 0x08, // makes GO sparkle + GO_DYNFLAG_LO_STOPPED = 0x10 // Transport is stopped }; enum GameObjectDestructibleState diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 6311e629641..5506f74b221 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -20,6 +20,7 @@ //Extended headers #include "ObjectMgr.h" #include "World.h" +#include "Transport.h" //Flightmaster grid preloading #include "MapManager.h" //Creature-specific headers @@ -92,14 +93,37 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { if (!i_path || i_path->empty()) return false; + if (Stopped()) return true; + bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); + if (m_isArrivalDone) { if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint { - creature->SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature->GetOrientation()); + float x = i_path->at(i_currentNode)->x; + float y = i_path->at(i_currentNode)->y; + float z = i_path->at(i_currentNode)->z; + float o = creature->GetOrientation(); + + if (!transportPath) + creature->SetHomePosition(x, y, z, o); + else + { + if (Transport* trans = creature->GetTransport()) + { + o -= trans->GetOrientation(); + creature->SetTransportHomePosition(x, y, z, o); + trans->CalculatePassengerPosition(x, y, z, &o); + creature->SetHomePosition(x, y, z, o); + } + else + transportPath = false; + // else if (vehicle) - this should never happen, vehicle offsets are const + } + creature->GetMotionMaster()->Initialize(); return false; } @@ -113,7 +137,19 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); + Movement::Location formationDest(node->x, node->y, node->z, 0.0f); Movement::MoveSplineInit init(creature); + + //! If creature is on transport, we assume waypoints set in DB are already transport offsets + if (transportPath) + { + init.DisableTransportPathTransformations(); + if (TransportBase* trans = creature->GetDirectTransport()) + trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); + } + + //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call + //! but formationDest contains global coordinates init.MoveTo(node->x, node->y, node->z); //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table @@ -125,7 +161,7 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) //Call for creature group update if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(node->x, node->y, node->z); + creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z); return true; } diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Scripting/MapScripts.cpp index c37cdf80730..ff133272724 100644 --- a/src/server/game/Scripting/MapScripts.cpp +++ b/src/server/game/Scripting/MapScripts.cpp @@ -315,6 +315,7 @@ void Map::ScriptsProcess() case HIGHGUID_PLAYER: source = HashMapHolder<Player>::Find(step.sourceGUID); break; + case HIGHGUID_TRANSPORT: case HIGHGUID_GAMEOBJECT: source = HashMapHolder<GameObject>::Find(step.sourceGUID); break; @@ -322,15 +323,11 @@ void Map::ScriptsProcess() source = HashMapHolder<Corpse>::Find(step.sourceGUID); break; case HIGHGUID_MO_TRANSPORT: - for (MapManager::TransportSet::iterator itr2 = sMapMgr->m_Transports.begin(); itr2 != sMapMgr->m_Transports.end(); ++itr2) - { - if ((*itr2)->GetGUID() == step.sourceGUID) - { - source = *itr2; - break; - } - } + { + GameObject* go = HashMapHolder<GameObject>::Find(step.sourceGUID); + source = go ? go->ToTransport() : NULL; break; + } default: TC_LOG_ERROR(LOG_FILTER_TSCR, "%s source with unsupported high guid (GUID: " UI64FMTD ", high guid: %u).", step.script->GetDebugInfo().c_str(), step.sourceGUID, GUID_HIPART(step.sourceGUID)); @@ -353,12 +350,19 @@ void Map::ScriptsProcess() case HIGHGUID_PLAYER: // empty GUID case also target = HashMapHolder<Player>::Find(step.targetGUID); break; + case HIGHGUID_TRANSPORT: case HIGHGUID_GAMEOBJECT: target = HashMapHolder<GameObject>::Find(step.targetGUID); break; case HIGHGUID_CORPSE: target = HashMapHolder<Corpse>::Find(step.targetGUID); break; + case HIGHGUID_MO_TRANSPORT: + { + GameObject* go = HashMapHolder<GameObject>::Find(step.targetGUID); + target = go ? go->ToTransport() : NULL; + break; + } default: TC_LOG_ERROR(LOG_FILTER_TSCR, "%s target with unsupported high guid (GUID: " UI64FMTD ", high guid: %u).", step.script->GetDebugInfo().c_str(), step.targetGUID, GUID_HIPART(step.targetGUID)); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 966b3edfcb9..26b3bb5f78f 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1363,6 +1363,16 @@ void ScriptMgr::OnGroupDisband(Group* group) } // Unit +void ScriptMgr::OnHeal(Unit* healer, Unit* reciever, uint32& gain) +{ + FOREACH_SCRIPT(UnitScript)->OnHeal(healer, reciever, gain); +} + +void ScriptMgr::OnDamage(Unit* attacker, Unit* victim, uint32& damage) +{ + FOREACH_SCRIPT(UnitScript)->OnDamage(attacker, victim, damage); +} + void ScriptMgr::ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage) { FOREACH_SCRIPT(UnitScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index e7946501616..37a95c77254 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -396,6 +396,12 @@ class UnitScript : public ScriptObject UnitScript(const char* name, bool addToScripts = true); public: + // Called when a unit deals healing to another unit + virtual void OnHeal(Unit* /*healer*/, Unit* /*reciever*/, uint32& /*gain*/) { } + + // Called when a unit deals damage to another unit + virtual void OnDamage(Unit* /*attacker*/, Unit* /*victim*/, uint32& /*damage*/) { } + // Called when DoT's Tick Damage is being Dealt virtual void ModifyPeriodicDamageAurasTick(Unit* /*target*/, Unit* /*attacker*/, uint32& /*damage*/) { } @@ -1054,6 +1060,8 @@ class ScriptMgr public: /* UnitScript */ + void OnHeal(Unit* healer, Unit* reciever, uint32& gain); + void OnDamage(Unit* attacker, Unit* victim, uint32& damage); void ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage); void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage); void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index c60b09438c1..c33c12ae5d4 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -272,6 +272,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. + uint32 processedPackets = 0; + while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) @@ -383,6 +385,14 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) delete packet; deletePacket = true; + +#define MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE 100 + processedPackets++; + + //process only a max amout of packets in 1 Update() call. + //Any leftover will be processed in next update + if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE) + break; } if (m_Socket && !m_Socket->IsClosed() && _warden) @@ -786,10 +796,6 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) if (mi->HasExtraMovementFlag(MOVEMENTFLAG2_INTERPOLATED_MOVEMENT)) data >> mi->transport.time2; - - if (mi->pos.m_positionX != mi->transport.pos.m_positionX) - if (GetPlayer()->GetTransport()) - GetPlayer()->GetTransport()->UpdatePosition(mi); } if (mi->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || (mi->HasExtraMovementFlag(MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING))) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 866cd888c3e..2eee80924a4 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -643,7 +643,7 @@ void Spell::InitExplicitTargets(SpellCastTargets const& targets) if (Player* playerCaster = m_caster->ToPlayer()) { // selection has to be found and to be valid target for the spell - if (Unit* selectedUnit = ObjectAccessor::GetUnit(*m_caster, playerCaster->GetSelection())) + if (Unit* selectedUnit = ObjectAccessor::GetUnit(*m_caster, playerCaster->GetTarget())) if (m_spellInfo->CheckExplicitTarget(m_caster, selectedUnit) == SPELL_CAST_OK) unit = selectedUnit; } @@ -1777,9 +1777,9 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) { case SPELL_EFFECT_SUMMON_RAF_FRIEND: case SPELL_EFFECT_SUMMON_PLAYER: - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->GetSelection()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->GetTarget()) { - WorldObject* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetSelection()); + WorldObject* target = ObjectAccessor::FindPlayer(m_caster->GetTarget()); CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex)); @@ -3095,7 +3095,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered // set target for proper facing if ((m_casttime || m_spellInfo->IsChanneled()) && !(_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING)) if (m_caster->GetTypeId() == TYPEID_UNIT && m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) - m_caster->FocusTarget(this, m_targets.GetObjectTarget()); + m_caster->ToCreature()->FocusTarget(this, m_targets.GetObjectTarget()); if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD)) TriggerGlobalCooldown(); @@ -3661,8 +3661,8 @@ void Spell::finish(bool ok) ((Puppet*)charm)->UnSummon(); } - if (m_caster->GetTypeId() == TYPEID_UNIT) - m_caster->ReleaseFocus(this); + if (Creature* creatureCaster = m_caster->ToCreature()) + creatureCaster->ReleaseFocus(this); if (!ok) return; @@ -5323,10 +5323,10 @@ SpellCastResult Spell::CheckCast(bool strict) { if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - if (!m_caster->ToPlayer()->GetSelection()) + if (!m_caster->GetTarget()) return SPELL_FAILED_BAD_TARGETS; - Player* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetSelection()); + Player* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetTarget()); if (!target || m_caster->ToPlayer() == target || (!target->IsInSameRaidWith(m_caster->ToPlayer()) && m_spellInfo->Id != 48955)) // refer-a-friend spell return SPELL_FAILED_BAD_TARGETS; @@ -5358,10 +5358,10 @@ SpellCastResult Spell::CheckCast(bool strict) Player* playerCaster = m_caster->ToPlayer(); // - if (!(playerCaster->GetSelection())) + if (!(playerCaster->GetTarget())) return SPELL_FAILED_BAD_TARGETS; - Player* target = ObjectAccessor::FindPlayer(playerCaster->GetSelection()); + Player* target = playerCaster->GetSelectedPlayer(); if (!target || !(target->GetSession()->GetRecruiterId() == playerCaster->GetSession()->GetAccountId() || target->GetSession()->GetAccountId() == playerCaster->GetSession()->GetRecruiterId())) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 29a869cf458..ff89cc61f6f 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1855,7 +1855,7 @@ void SpellMgr::LoadSpellProcEvents() } if (!spellInfo->ProcFlags && !spellProcEvent.procFlags) - TC_LOG_ERROR(LOG_FILTER_SQL, "Spell %u listed in `spell_proc_event` probally not triggered spell", spellInfo->Id); + TC_LOG_ERROR(LOG_FILTER_SQL, "Spell %u listed in `spell_proc_event` probably not triggered spell", spellInfo->Id); mSpellProcEventMap[spellInfo->Id] = spellProcEvent; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 8ca2e1db56e..7ad7b68b210 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -80,6 +80,7 @@ #include "Warden.h" #include "CalendarMgr.h" #include "BattlefieldMgr.h" +#include "TransportMgr.h" ACE_Atomic_Op<ACE_Thread_Mutex, bool> World::m_stopEvent = false; uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; @@ -1374,6 +1375,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading Game Object Templates..."); // must be after LoadPageTexts sObjectMgr->LoadGameObjectTemplate(); + TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading Transport templates..."); + sTransportMgr->LoadTransportTemplates(); + TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading Spell Rank Data..."); sSpellMgr->LoadSpellRanks(); @@ -1784,10 +1788,7 @@ void World::SetInitialWorldSettings() sBattlefieldMgr->InitBattlefield(); TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading Transports..."); - sMapMgr->LoadTransports(); - - TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading Transport NPCs..."); - sMapMgr->LoadTransportNPCs(); + sTransportMgr->SpawnContinentTransports(); ///- Initialize Warden TC_LOG_INFO(LOG_FILTER_SERVER_LOADING, "Loading Warden Checks..."); diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index 332169ebd00..45ab88e9688 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -450,12 +450,12 @@ public: uint32 pwConfig = sWorld->getIntConfig(CONFIG_ACC_PASSCHANGESEC); // 0 - PW_NONE, 1 - PW_EMAIL, 2 - PW_RBAC // Command is supposed to be: .account password [$oldpassword] [$newpassword] [$newpasswordconfirmation] [$emailconfirmation] - char* oldPassword = strtok((char*)args, " "); // This extracts [$oldpassword] - char* newPassword = strtok(NULL, " "); // This extracts [$newpassword] - char* passwordConfirmation = strtok(NULL, " "); // This extracts [$newpasswordconfirmation] - const char* emailConfirmation; // This defines the emailConfirmation variable, which is optional depending on sec type. - if (!(emailConfirmation = strtok(NULL, " "))) // This extracts [$emailconfirmation]. If it doesn't exist, however... - emailConfirmation = ""; // ... it's simply "" for emailConfirmation. + char* oldPassword = strtok((char*)args, " "); // This extracts [$oldpassword] + char* newPassword = strtok(NULL, " "); // This extracts [$newpassword] + char* passwordConfirmation = strtok(NULL, " "); // This extracts [$newpasswordconfirmation] + char const* emailConfirmation = strtok(NULL, " "); // This defines the emailConfirmation variable, which is optional depending on sec type. + if (!emailConfirmation) // This extracts [$emailconfirmation]. If it doesn't exist, however... + emailConfirmation = ""; // ... it's simply "" for emailConfirmation. //Is any of those variables missing for any reason ? We return false. if (!oldPassword || !newPassword || !passwordConfirmation) diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 6c831520d59..f33e583522e 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "GossipDef.h" +#include "Transport.h" #include "Language.h" #include <fstream> @@ -91,6 +92,7 @@ public: { "areatriggers", rbac::RBAC_PERM_COMMAND_DEBUG_AREATRIGGERS, false, &HandleDebugAreaTriggersCommand, "", NULL }, { "los", rbac::RBAC_PERM_COMMAND_DEBUG_LOS, false, &HandleDebugLoSCommand, "", NULL }, { "moveflags", rbac::RBAC_PERM_COMMAND_DEBUG_MOVEFLAGS, false, &HandleDebugMoveflagsCommand, "", NULL }, + { "transport", rbac::RBAC_PERM_COMMAND_DEBUG_TRANSPORT, false, &HandleDebugTransportCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; static ChatCommand commandTable[] = @@ -179,7 +181,7 @@ public: return false; } - if (handler->GetSession()->GetPlayer()->GetSelection()) + if (handler->GetSession()->GetPlayer()->GetTarget()) unit->PlayDistanceSound(soundId, handler->GetSession()->GetPlayer()); else unit->PlayDirectSound(soundId, handler->GetSession()->GetPlayer()); @@ -1363,6 +1365,30 @@ public: handler->PSendSysMessage("Waypoint SQL written to SQL Developer log"); return true; } + + static bool HandleDebugTransportCommand(ChatHandler* handler, char const* args) + { + Transport* transport = handler->GetSession()->GetPlayer()->GetTransport(); + if (!transport) + return false; + + bool start = false; + if (!stricmp(args, "stop")) + transport->EnableMovement(false); + else if (!stricmp(args, "start")) + { + transport->EnableMovement(true); + start = true; + } + else + { + handler->PSendSysMessage("Transport %s is %s", transport->GetName().c_str(), transport->GetGoState() == GO_STATE_READY ? "stopped" : "moving"); + return true; + } + + handler->PSendSysMessage("Transport %s %s", transport->GetName().c_str(), start ? "started" : "stopped"); + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 513d378287b..3d531cee1f5 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -529,7 +529,7 @@ public: { Unit* target = handler->getSelectedUnit(); - if (!target || !handler->GetSession()->GetPlayer()->GetSelection()) + if (!target || !handler->GetSession()->GetPlayer()->GetTarget()) { handler->SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); handler->SetSentErrorMessage(true); @@ -597,7 +597,7 @@ public: static bool HandleGUIDCommand(ChatHandler* handler, char const* /*args*/) { - uint64 guid = handler->GetSession()->GetPlayer()->GetSelection(); + uint64 guid = handler->GetSession()->GetPlayer()->GetTarget(); if (guid == 0) { @@ -1762,7 +1762,7 @@ public: // accept only explicitly selected target (not implicitly self targeting case) Unit* target = handler->getSelectedUnit(); - if (player->GetSelection() && target) + if (player->GetTarget() && target) { if (target->GetTypeId() != TYPEID_UNIT || target->IsPet()) { @@ -2107,7 +2107,7 @@ public: } Unit* target = handler->getSelectedUnit(); - if (!target || !handler->GetSession()->GetPlayer()->GetSelection()) + if (!target || !handler->GetSession()->GetPlayer()->GetTarget()) { handler->SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); handler->SetSentErrorMessage(true); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index f10a929c675..a1b23fd5935 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -245,24 +245,22 @@ public: float o = chr->GetOrientation(); Map* map = chr->GetMap(); - if (chr->GetTransport()) + if (Transport* trans = chr->GetTransport()) { - uint32 tguid = chr->GetTransport()->AddNPCPassenger(0, id, chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO()); - if (tguid > 0) - { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE_TRANSPORT); + uint32 guid = sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT); + CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); + data.id = id; + data.phaseMask = chr->GetPhaseMaskForSpawn(); + data.posX = chr->GetTransOffsetX(); + data.posY = chr->GetTransOffsetY(); + data.posZ = chr->GetTransOffsetZ(); + data.orientation = chr->GetTransOffsetO(); - stmt->setInt32(0, int32(tguid)); - stmt->setInt32(1, int32(id)); - stmt->setInt32(2, int32(chr->GetTransport()->GetEntry())); - stmt->setFloat(3, chr->GetTransOffsetX()); - stmt->setFloat(4, chr->GetTransOffsetY()); - stmt->setFloat(5, chr->GetTransOffsetZ()); - stmt->setFloat(6, chr->GetTransOffsetO()); + Creature* creature = trans->CreateNPCPassenger(guid, &data); - WorldDatabase.Execute(stmt); - } + creature->SaveToDB(trans->GetGOInfo()->moTransport.mapID, 1 << map->GetSpawnMode(), chr->GetPhaseMaskForSpawn()); + sObjectMgr->AddCreatureToGrid(guid, &data); return true; } @@ -899,17 +897,6 @@ public: return false; } - if (target->GetTransport() && target->GetGUIDTransport()) - { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_TRANSPORT_EMOTE); - - stmt->setInt32(0, int32(emote)); - stmt->setInt32(1, target->GetTransport()->GetEntry()); - stmt->setInt32(2, target->GetGUIDTransport()); - - WorldDatabase.Execute(stmt); - } - target->SetUInt32Value(UNIT_NPC_EMOTESTATE, emote); return true; @@ -1297,22 +1284,17 @@ public: char* receiver_str = strtok((char*)args, " "); char* text = strtok(NULL, ""); - uint64 guid = handler->GetSession()->GetPlayer()->GetSelection(); - Creature* creature = handler->GetSession()->GetPlayer()->GetMap()->GetCreature(guid); - + Creature* creature = handler->getSelectedCreature(); if (!creature || !receiver_str || !text) - { return false; - } - uint64 receiver_guid= atol(receiver_str); + uint64 receiver_guid = atol(receiver_str); // check online security if (handler->HasLowerSecurity(ObjectAccessor::FindPlayer(receiver_guid), 0)) return false; creature->MonsterWhisper(text, receiver_guid); - return true; } @@ -1342,7 +1324,8 @@ public: { if (!*args) return false; - char* charID = strtok((char*)args, " "); + + char* charID = handler->extractKeyFromLink((char*)args, "Hcreature_entry"); if (!charID) return false; diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index fb2d8374c18..1b5d0a80eda 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -580,7 +580,11 @@ public: Map::PlayerList const& players = me->GetMap()->GetPlayers(); if (!players.isEmpty()) - sLFGMgr->FinishDungeon(players.begin()->GetSource()->GetGroup()->GetGUID(), 285); + { + if (Group* group = players.begin()->GetSource()->GetGroup()) + if (group->isLFGGroup()) + sLFGMgr->FinishDungeon(group->GetGUID(), 285); + } } void SpellHit(Unit* caster, const SpellInfo* spell) OVERRIDE diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp index d8dfa2f5cf6..19024172aa0 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/instance_the_black_morass.cpp @@ -196,7 +196,7 @@ public: TC_LOG_DEBUG(LOG_FILTER_TSCR, "Instance The Black Morass: Starting event."); InitWorldState(); m_auiEncounter[1] = IN_PROGRESS; - Events.ScheduleEvent(EVENT_NEXT_PORTAL, 15000); + ScheduleEventNextPortal(15000); } if (data == DONE) @@ -228,7 +228,7 @@ public: if (data == SPECIAL) { if (mRiftPortalCount < 7) - Events.ScheduleEvent(EVENT_NEXT_PORTAL, 5000); + ScheduleEventNextPortal(5000); } else m_auiEncounter[1] = data; @@ -336,10 +336,16 @@ public: ++mRiftPortalCount; DoUpdateWorldState(WORLD_STATE_BM_RIFT, mRiftPortalCount); DoSpawnPortal(); - Events.ScheduleEvent(EVENT_NEXT_PORTAL, RiftWaves[GetRiftWaveId()].NextPortalTime); + ScheduleEventNextPortal(RiftWaves[GetRiftWaveId()].NextPortalTime); } } + void ScheduleEventNextPortal(uint32 nextPortalTime) + { + if (nextPortalTime > 0) + Events.RescheduleEvent(EVENT_NEXT_PORTAL, nextPortalTime); + } + protected: EventMap Events; }; 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 2f01cc734e6..e0098570923 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp @@ -17,6 +17,7 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "Player.h" #include "ahnkahet.h" enum Spells @@ -29,11 +30,9 @@ enum Spells SPELL_FLAME_SPHERE_SPAWN_EFFECT = 55891, SPELL_FLAME_SPHERE_VISUAL = 55928, SPELL_FLAME_SPHERE_PERIODIC = 55926, - H_SPELL_FLAME_SPHERE_PERIODIC = 59508, SPELL_FLAME_SPHERE_DEATH_EFFECT = 55947, SPELL_BEAM_VISUAL = 60342, SPELL_EMBRACE_OF_THE_VAMPYR = 55959, - H_SPELL_EMBRACE_OF_THE_VAMPYR = 59513, SPELL_VANISH = 55964, CREATURE_FLAME_SPHERE = 30106, H_CREATURE_FLAME_SPHERE_1 = 31686, @@ -48,8 +47,8 @@ enum Misc DATA_SPHERE_DISTANCE = 15 }; -#define DATA_SPHERE_ANGLE_OFFSET 0.7f -#define DATA_GROUND_POSITION_Z 11.30809f +#define DATA_SPHERE_ANGLE_OFFSET 0.7f +#define DATA_GROUND_POSITION_Z 11.30809f enum Yells { @@ -61,315 +60,299 @@ enum Yells SAY_FEED = 5, SAY_VANISH = 6 }; -enum CombatPhase + +enum Events +{ + EVENT_CASTING_FLAME_SPHERES = 1, + EVENT_JUST_VANISHED, + EVENT_VANISHED, + EVENT_FEEDING, + + EVENT_BLOODTHIRST, + EVENT_FLAME_SPHERE, + EVENT_VANISH +}; + +enum Phase { - NORMAL, - CASTING_FLAME_SPHERES, - JUST_VANISHED, - VANISHED, - FEEDING + PHASE_NORMAL = 1, + PHASE_SPECIAL = 2 }; class boss_taldaram : public CreatureScript { -public: - boss_taldaram() : CreatureScript("boss_taldaram") { } + public: + boss_taldaram() : CreatureScript("boss_taldaram") { } - struct boss_taldaramAI : public ScriptedAI - { - boss_taldaramAI(Creature* creature) : ScriptedAI(creature) + struct boss_taldaramAI : public BossAI { - instance = creature->GetInstanceScript(); - me->SetDisableGravity(true); - } - - uint32 uiBloodthirstTimer; - uint32 uiVanishTimer; - uint32 uiWaitTimer; - uint32 uiEmbraceTimer; - uint32 uiEmbraceTakenDamage; - uint32 uiFlamesphereTimer; - uint32 uiPhaseTimer; + boss_taldaramAI(Creature* creature) : BossAI(creature, DATA_PRINCE_TALDARAM) + { + me->SetDisableGravity(true); + } - uint64 uiEmbraceTarget; + void Reset() OVERRIDE + { + _Reset(); + _embraceTargetGUID = 0; + _embraceTakenDamage = 0; + } - CombatPhase Phase; + void EnterCombat(Unit* /*who*/) OVERRIDE + { + _EnterCombat(); + Talk(SAY_AGGRO); + events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_BLOODTHIRST, 10000); + events.ScheduleEvent(EVENT_VANISH, urand(25000, 35000)); + events.ScheduleEvent(EVENT_FLAME_SPHERE, 5000); + } - InstanceScript* instance; + void UpdateAI(uint32 diff) OVERRIDE + { + if (!UpdateVictim()) + return; - void Reset() OVERRIDE - { - uiBloodthirstTimer = 10*IN_MILLISECONDS; - uiVanishTimer = urand(25*IN_MILLISECONDS, 35*IN_MILLISECONDS); - uiEmbraceTimer = 20*IN_MILLISECONDS; - uiFlamesphereTimer = 5*IN_MILLISECONDS; - uiEmbraceTakenDamage = 0; - Phase = NORMAL; - uiPhaseTimer = 0; - uiEmbraceTarget = 0; - if (instance) - instance->SetBossState(DATA_PRINCE_TALDARAM, NOT_STARTED); - } + events.Update(diff); - void EnterCombat(Unit* /*who*/) OVERRIDE - { - if (instance) - instance->SetBossState(DATA_PRINCE_TALDARAM, IN_PROGRESS); - Talk(SAY_AGGRO); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void UpdateAI(uint32 diff) OVERRIDE - { - if (!UpdateVictim()) - return; - if (uiPhaseTimer <= diff) - { - switch (Phase) + while (uint32 eventId = events.ExecuteEvent()) { - case CASTING_FLAME_SPHERES: + switch (eventId) { - Creature* pSpheres[3]; - - //DoCast(me, SPELL_FLAME_SPHERE_SUMMON_1); - pSpheres[0] = DoSpawnCreature(CREATURE_FLAME_SPHERE, 0, 0, 5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10*IN_MILLISECONDS); - Unit* pSphereTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); - if (pSphereTarget && pSpheres[0]) - { - float angle, x, y; - angle = pSpheres[0]->GetAngle(pSphereTarget); - x = pSpheres[0]->GetPositionX() + DATA_SPHERE_DISTANCE * std::cos(angle); - y = pSpheres[0]->GetPositionY() + DATA_SPHERE_DISTANCE * std::sin(angle); - pSpheres[0]->GetMotionMaster()->MovePoint(0, x, y, pSpheres[0]->GetPositionZ()); - } - if (IsHeroic()) + if (events.IsInPhase(PHASE_NORMAL)) { - //DoCast(me, H_SPELL_FLAME_SPHERE_SUMMON_1); - pSpheres[1] = DoSpawnCreature(H_CREATURE_FLAME_SPHERE_1, 0, 0, 5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10*IN_MILLISECONDS); - //DoCast(me, H_SPELL_FLAME_SPHERE_SUMMON_2); - pSpheres[2] = DoSpawnCreature(H_CREATURE_FLAME_SPHERE_2, 0, 0, 5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10*IN_MILLISECONDS); - if (pSphereTarget && pSpheres[1] && pSpheres[2]) + case EVENT_BLOODTHIRST: + DoCast(me, SPELL_BLOODTHIRST); + events.ScheduleEvent(EVENT_BLOODTHIRST, 10000); + break; + case EVENT_FLAME_SPHERE: + DoCastVictim(SPELL_CONJURE_FLAME_SPHERE); + events.SetPhase(PHASE_SPECIAL); + events.ScheduleEvent(EVENT_CASTING_FLAME_SPHERES, 3000); + events.ScheduleEvent(EVENT_FLAME_SPHERE, 15000); + break; + case EVENT_VANISH: { - float angle, x, y; - angle = pSpheres[1]->GetAngle(pSphereTarget) + DATA_SPHERE_ANGLE_OFFSET; - x = pSpheres[1]->GetPositionX() + DATA_SPHERE_DISTANCE/2 * std::cos(angle); - y = pSpheres[1]->GetPositionY() + DATA_SPHERE_DISTANCE/2 * std::sin(angle); - pSpheres[1]->GetMotionMaster()->MovePoint(0, x, y, pSpheres[1]->GetPositionZ()); - angle = pSpheres[2]->GetAngle(pSphereTarget) - DATA_SPHERE_ANGLE_OFFSET; - x = pSpheres[2]->GetPositionX() + DATA_SPHERE_DISTANCE/2 * std::cos(angle); - y = pSpheres[2]->GetPositionY() + DATA_SPHERE_DISTANCE/2 * std::sin(angle); - pSpheres[2]->GetMotionMaster()->MovePoint(0, x, y, pSpheres[2]->GetPositionZ()); + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + uint32 targets = 0; + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + { + Player* player = i->GetSource(); + if (player && player->IsAlive()) + ++targets; + } + + if (targets > 2) + { + Talk(SAY_VANISH); + DoCast(me, SPELL_VANISH); + events.SetPhase(PHASE_SPECIAL); + events.ScheduleEvent(EVENT_JUST_VANISHED, 500); + if (Unit* embraceTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + _embraceTargetGUID = embraceTarget->GetGUID(); + } + events.ScheduleEvent(EVENT_VANISH, urand(25000, 35000)); + break; } } - - Phase = NORMAL; - uiPhaseTimer = 0; - break; - } - case JUST_VANISHED: - if (Unit* pEmbraceTarget = GetEmbraceTarget()) - { - me->GetMotionMaster()->Clear(); - me->SetSpeed(MOVE_WALK, 2.0f, true); - me->GetMotionMaster()->MoveChase(pEmbraceTarget); - } - Phase = VANISHED; - uiPhaseTimer = 1300; - break; - case VANISHED: - if (Unit* pEmbraceTarget = GetEmbraceTarget()) - DoCast(pEmbraceTarget, DUNGEON_MODE(SPELL_EMBRACE_OF_THE_VAMPYR, H_SPELL_EMBRACE_OF_THE_VAMPYR)); - Talk(SAY_FEED); - me->GetMotionMaster()->Clear(); - me->SetSpeed(MOVE_WALK, 1.0f, true); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - Phase = FEEDING; - uiPhaseTimer = 20*IN_MILLISECONDS; - break; - case FEEDING: - Phase = NORMAL; - uiPhaseTimer = 0; - uiEmbraceTarget = 0; - break; - case NORMAL: - if (uiBloodthirstTimer <= diff) + case EVENT_CASTING_FLAME_SPHERES: { - DoCast(me, SPELL_BLOODTHIRST); - uiBloodthirstTimer = 10*IN_MILLISECONDS; - } else uiBloodthirstTimer -= diff; + events.SetPhase(PHASE_NORMAL); + Unit* sphereTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true); + if (!sphereTarget) + break; - if (uiFlamesphereTimer <= diff) - { - // because TARGET_UNIT_TARGET_ENEMY we need a target selected to cast - DoCastVictim(SPELL_CONJURE_FLAME_SPHERE); - Phase = CASTING_FLAME_SPHERES; - uiPhaseTimer = 3*IN_MILLISECONDS + diff; - uiFlamesphereTimer = 15*IN_MILLISECONDS; - } else uiFlamesphereTimer -= diff; - - if (uiVanishTimer <= diff) - { - //Count alive players - Unit* target = NULL; - std::list<HostileReference*> t_list = me->getThreatManager().getThreatList(); - std::vector<Unit*> target_list; - for (std::list<HostileReference*>::const_iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + float angle, x, y; + + //DoCast(me, SPELL_FLAME_SPHERE_SUMMON_1); + if (Creature* sphere = DoSpawnCreature(CREATURE_FLAME_SPHERE, 0, 0, 5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10 * IN_MILLISECONDS)) { - target = Unit::GetUnit(*me, (*itr)->getUnitGuid()); - // exclude pets & totems - if (target && target->GetTypeId() == TYPEID_PLAYER && target->IsAlive()) - target_list.push_back(target); - target = NULL; + angle = sphere->GetAngle(sphereTarget); + x = sphere->GetPositionX() + DATA_SPHERE_DISTANCE * std::cos(angle); + y = sphere->GetPositionY() + DATA_SPHERE_DISTANCE * std::sin(angle); + sphere->GetMotionMaster()->MovePoint(0, x, y, sphere->GetPositionZ()); } - //He only vanishes if there are 3 or more alive players - if (target_list.size() > 2) - { - Talk(SAY_VANISH); - DoCast(me, SPELL_VANISH); - Phase = JUST_VANISHED; - uiPhaseTimer = 500; - if (Unit* pEmbraceTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - uiEmbraceTarget = pEmbraceTarget->GetGUID(); + if (IsHeroic()) + { + //DoCast(me, H_SPELL_FLAME_SPHERE_SUMMON_1); + if (Creature* sphere = DoSpawnCreature(H_CREATURE_FLAME_SPHERE_1, 0, 0, 5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10 * IN_MILLISECONDS)) + { + angle = sphere->GetAngle(sphereTarget) + DATA_SPHERE_ANGLE_OFFSET; + x = sphere->GetPositionX() + DATA_SPHERE_DISTANCE/2 * std::cos(angle); + y = sphere->GetPositionY() + DATA_SPHERE_DISTANCE/2 * std::sin(angle); + sphere->GetMotionMaster()->MovePoint(0, x, y, sphere->GetPositionZ()); + } + + //DoCast(me, H_SPELL_FLAME_SPHERE_SUMMON_2); + if (Creature* sphere = DoSpawnCreature(H_CREATURE_FLAME_SPHERE_2, 0, 0, 5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10 * IN_MILLISECONDS)) + { + angle = sphere->GetAngle(sphereTarget) - DATA_SPHERE_ANGLE_OFFSET; + x = sphere->GetPositionX() + DATA_SPHERE_DISTANCE/2 * std::cos(angle); + y = sphere->GetPositionY() + DATA_SPHERE_DISTANCE/2 * std::sin(angle); + sphere->GetMotionMaster()->MovePoint(0, x, y, sphere->GetPositionZ()); + } } - uiVanishTimer = urand(25*IN_MILLISECONDS, 35*IN_MILLISECONDS); - } else uiVanishTimer -= diff; - - DoMeleeAttackIfReady(); - break; + break; + } + case EVENT_JUST_VANISHED: + if (Unit* embraceTarget = GetEmbraceTarget()) + { + me->GetMotionMaster()->Clear(); + me->SetSpeed(MOVE_WALK, 2.0f, true); + me->GetMotionMaster()->MoveChase(embraceTarget); + } + events.ScheduleEvent(EVENT_VANISHED, 1300); + break; + case EVENT_VANISHED: + if (Unit* embraceTarget = GetEmbraceTarget()) + DoCast(embraceTarget, SPELL_EMBRACE_OF_THE_VAMPYR); + Talk(SAY_FEED); + me->GetMotionMaster()->Clear(); + me->SetSpeed(MOVE_WALK, 1.0f, true); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + events.ScheduleEvent(EVENT_FEEDING, 20000); + break; + case EVENT_FEEDING: + _embraceTargetGUID = 0; + events.SetPhase(PHASE_NORMAL); + break; + default: + break; + } } - } else uiPhaseTimer -= diff; - } - void DamageTaken(Unit* /*done_by*/, uint32 &damage) OVERRIDE - { - Unit* pEmbraceTarget = GetEmbraceTarget(); + DoMeleeAttackIfReady(); + } - if (Phase == FEEDING && pEmbraceTarget && pEmbraceTarget->IsAlive()) + void DamageTaken(Unit* /*doneBy*/, uint32& damage) OVERRIDE { - uiEmbraceTakenDamage += damage; - if (uiEmbraceTakenDamage > (uint32) DUNGEON_MODE(DATA_EMBRACE_DMG, H_DATA_EMBRACE_DMG)) - { - Phase = NORMAL; - uiPhaseTimer = 0; - uiEmbraceTarget = 0; - me->CastStop(); - } + Unit* embraceTarget = GetEmbraceTarget(); + + if (events.IsInPhase(PHASE_SPECIAL) && embraceTarget && embraceTarget->IsAlive()) + { + _embraceTakenDamage += damage; + if (_embraceTakenDamage > DUNGEON_MODE<uint32>(DATA_EMBRACE_DMG, H_DATA_EMBRACE_DMG)) + { + _embraceTargetGUID = 0; + events.SetPhase(PHASE_NORMAL); + me->CastStop(); + } + } } - } - void JustDied(Unit* /*killer*/) OVERRIDE - { - Talk(SAY_DEATH); + void JustDied(Unit* /*killer*/) OVERRIDE + { + Talk(SAY_DEATH); + _JustDied(); + } - if (instance) - instance->SetBossState(DATA_PRINCE_TALDARAM, DONE); - } + void KilledUnit(Unit* victim) OVERRIDE + { + if (victim->GetTypeId() != TYPEID_PLAYER) + return; - void KilledUnit(Unit* victim) OVERRIDE - { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; + Unit* embraceTarget = GetEmbraceTarget(); + if (events.IsInPhase(PHASE_SPECIAL) && embraceTarget && victim == embraceTarget) + { + _embraceTargetGUID = 0; + events.SetPhase(PHASE_NORMAL); + } + Talk(SAY_SLAY); + } - Unit* pEmbraceTarget = GetEmbraceTarget(); - if (Phase == FEEDING && pEmbraceTarget && victim == pEmbraceTarget) + bool CheckSpheres() { - Phase = NORMAL; - uiPhaseTimer = 0; - uiEmbraceTarget = 0; - } - Talk(SAY_SLAY); - } + for (uint8 i = 0; i < 2; ++i) + if (!instance->GetData(DATA_SPHERE_1 + i)) + return false; - bool CheckSpheres() - { - for (uint8 i = 0; i < 2; ++i) - if (!instance->GetData(DATA_SPHERE_1 + i)) - return false; + RemovePrison(); + return true; + } - RemovePrison(); - return true; - } + Unit* GetEmbraceTarget() + { + if (_embraceTargetGUID) + return ObjectAccessor::GetUnit(*me, _embraceTargetGUID); - Unit* GetEmbraceTarget() - { - if (!uiEmbraceTarget) return NULL; + } - return Unit::GetUnit(*me, uiEmbraceTarget); - } + void RemovePrison() + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasDueToSpell(SPELL_BEAM_VISUAL); + me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), DATA_GROUND_POSITION_Z, me->GetOrientation()); + DoCast(SPELL_HOVER_FALL); + me->SetDisableGravity(false); + me->GetMotionMaster()->MovePoint(0, me->GetHomePosition()); + Talk(SAY_WARNING); + instance->HandleGameObject(instance->GetData64(DATA_PRINCE_TALDARAM_PLATFORM), true); + } + + private: + uint64 _embraceTargetGUID; + uint32 _embraceTakenDamage; + }; - void RemovePrison() + CreatureAI* GetAI(Creature* creature) const OVERRIDE { - if (!instance) - return; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveAurasDueToSpell(SPELL_BEAM_VISUAL); - me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), DATA_GROUND_POSITION_Z, me->GetOrientation()); - DoCast(SPELL_HOVER_FALL); - me->SetDisableGravity(false); - me->GetMotionMaster()->MovePoint(0, me->GetHomePosition()); - Talk(SAY_WARNING); - uint64 prison_GUID = instance->GetData64(DATA_PRINCE_TALDARAM_PLATFORM); - instance->HandleGameObject(prison_GUID, true); + return GetAhnKahetAI<boss_taldaramAI>(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return GetAhnKahetAI<boss_taldaramAI>(creature); - } }; class npc_taldaram_flamesphere : public CreatureScript { -public: - npc_taldaram_flamesphere() : CreatureScript("npc_taldaram_flamesphere") { } + public: + npc_taldaram_flamesphere() : CreatureScript("npc_taldaram_flamesphere") { } - struct npc_taldaram_flamesphereAI : public ScriptedAI - { - npc_taldaram_flamesphereAI(Creature* creature) : ScriptedAI(creature) + struct npc_taldaram_flamesphereAI : public ScriptedAI { - instance = creature->GetInstanceScript(); - } + npc_taldaram_flamesphereAI(Creature* creature) : ScriptedAI(creature) + { + } - uint32 uiDespawnTimer; - InstanceScript* instance; + void Reset() OVERRIDE + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //! HACK: Creature's can't have MOVEMENTFLAG_FLYING + me->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); + me->setFaction(16); + me->SetObjectScale(1.0f); + DoCast(me, SPELL_FLAME_SPHERE_VISUAL); + DoCast(me, SPELL_FLAME_SPHERE_SPAWN_EFFECT); + DoCast(me, SPELL_FLAME_SPHERE_PERIODIC); + _despawnTimer = 10 * IN_MILLISECONDS; + } - void Reset() OVERRIDE - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - //! HACK: Creature's can't have MOVEMENTFLAG_FLYING - me->AddUnitMovementFlag(MOVEMENTFLAG_FLYING); - me->setFaction(16); - me->SetObjectScale(1.0f); - DoCast(me, SPELL_FLAME_SPHERE_VISUAL); - DoCast(me, SPELL_FLAME_SPHERE_SPAWN_EFFECT); - DoCast(me, SPELL_FLAME_SPHERE_PERIODIC); - uiDespawnTimer = 10*IN_MILLISECONDS; - } + void EnterCombat(Unit* /*who*/) OVERRIDE { } + void MoveInLineOfSight(Unit* /*who*/) OVERRIDE { } - void EnterCombat(Unit* /*who*/) OVERRIDE {} - void MoveInLineOfSight(Unit* /*who*/) OVERRIDE {} + void JustDied(Unit* /*killer*/) OVERRIDE + { + DoCast(me, SPELL_FLAME_SPHERE_DEATH_EFFECT); + } + void UpdateAI(uint32 diff) OVERRIDE + { + if (_despawnTimer <= diff) + me->DisappearAndDie(); + else + _despawnTimer -= diff; + } - void JustDied(Unit* /*killer*/) OVERRIDE - { - DoCast(me, SPELL_FLAME_SPHERE_DEATH_EFFECT); - } + private: + uint32 _despawnTimer; + }; - void UpdateAI(uint32 diff) OVERRIDE + CreatureAI* GetAI(Creature* creature) const OVERRIDE { - if (uiDespawnTimer <= diff) - me->DisappearAndDie(); - else - uiDespawnTimer -= diff; + return new npc_taldaram_flamesphereAI(creature); } - }; - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_taldaram_flamesphereAI(creature); - } }; class prince_taldaram_sphere : public GameObjectScript @@ -377,7 +360,7 @@ class prince_taldaram_sphere : public GameObjectScript public: prince_taldaram_sphere() : GameObjectScript("prince_taldaram_sphere") { } - bool OnGossipHello(Player* /*player*/, GameObject* go) + bool OnGossipHello(Player* /*player*/, GameObject* go) OVERRIDE { InstanceScript* instance = go->GetInstanceScript(); if (!instance) 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 987807c2080..04145b8784e 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -181,7 +181,7 @@ enum Events EVENT_ESCAPE_13, EVENT_ESCAPE_14, EVENT_ESCAPE_15, - EVENT_ESCAPE_16, + //EVENT_ESCAPE_16, EVENT_ESCAPE_17, EVENT_ESCAPE_18, EVENT_ESCAPE_19, @@ -226,7 +226,7 @@ enum Spells SPELL_SOUL_REAPER = 69409, // Lich King Soul Reaper SPELL_FURY_OF_FROSTMOURNE = 70063, // Lich King Fury of FrostMourne SPELL_JAINA_DESTROY_ICE_WALL = 69784, // Jaina - SPELL_SYLVANAS_DESTROY_ICE_WALL = 70225, // Sylvanas + SPELL_SYLVANAS_DESTROY_ICE_WALL = 70224, // Sylvanas SPELL_SYLVANAS_JUMP = 68339, // Sylvanas Jump SPELL_RAISE_DEAD = 69818, SPELL_HARVEST_SOUL = 70070, @@ -234,16 +234,16 @@ enum Spells SPELL_SUMMON_LUMBERING_ABOMINATION = 69835, SPELL_SUMMON_ICE_WALL = 69768, // Visual effect and icewall summoning - //Raging gnoul + // Raging Ghoul SPELL_EMERGE_VISUAL = 50142, SPELL_GHOUL_JUMP = 70150, - //Witch Doctor + // Witch Doctor SPELL_COURSE_OF_DOOM = 70144, SPELL_SHADOW_BOLT_VOLLEY = 70145, SPELL_SHADOW_BOLT = 70080, - //Lumbering Abomination + // Lumbering Abomination SPELL_ABON_STRIKE = 40505, SPELL_VOMIT_SPRAY = 70176, }; @@ -344,6 +344,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript void UpdateAI(uint32 diff) OVERRIDE { _events.Update(diff); + switch (_events.ExecuteEvent()) { case EVENT_WALK_INTRO1: @@ -398,7 +399,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_5, 2000); break; case EVENT_INTRO_A2_5: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_1); _events.ScheduleEvent(EVENT_INTRO_A2_6, 3000); break; @@ -407,7 +408,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_7, 7000); break; case EVENT_INTRO_A2_7: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_2); _events.ScheduleEvent(EVENT_INTRO_A2_8, 7000); break; @@ -416,7 +417,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_9, 1200); break; case EVENT_INTRO_A2_9: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_3); _events.ScheduleEvent(EVENT_INTRO_A2_10, 11000); break; @@ -425,7 +426,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_11, 6000); break; case EVENT_INTRO_A2_11: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_4); _events.ScheduleEvent(EVENT_INTRO_A2_12, 12000); break; @@ -434,7 +435,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_13, 6000); break; case EVENT_INTRO_A2_13: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_5); _events.ScheduleEvent(EVENT_INTRO_A2_14, 13000); break; @@ -443,12 +444,12 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_15, 12000); break; case EVENT_INTRO_A2_15: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_6); _events.ScheduleEvent(EVENT_INTRO_A2_16, 25000); break; case EVENT_INTRO_A2_16: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_A2_7); _events.ScheduleEvent(EVENT_INTRO_A2_17, 6000); break; @@ -457,7 +458,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_A2_18, 5000); break; case EVENT_INTRO_A2_18: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) { uther->HandleEmoteCommand(EMOTE_ONESHOT_NO); uther->AI()->Talk(SAY_UTHER_INTRO_A2_8); @@ -494,7 +495,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_H2_5, 2000); break; case EVENT_INTRO_H2_5: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_H2_1); _events.ScheduleEvent(EVENT_INTRO_H2_6, 11000); break; @@ -503,7 +504,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_H2_7, 3000); break; case EVENT_INTRO_H2_7: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_H2_2); _events.ScheduleEvent(EVENT_INTRO_H2_8, 6000); break; @@ -512,7 +513,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_H2_9, 5000); break; case EVENT_INTRO_H2_9: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_H2_3); _events.ScheduleEvent(EVENT_INTRO_H2_10, 19000); break; @@ -521,7 +522,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_H2_11, 1500); break; case EVENT_INTRO_H2_11: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_H2_4); _events.ScheduleEvent(EVENT_INTRO_H2_12, 19500); break; @@ -530,7 +531,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_H2_13, 2000); break; case EVENT_INTRO_H2_13: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) { uther->HandleEmoteCommand(EMOTE_ONESHOT_NO); uther->AI()->Talk(SAY_UTHER_INTRO_H2_5); @@ -538,7 +539,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_H2_14, 12000); break; case EVENT_INTRO_H2_14: - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) uther->AI()->Talk(SAY_UTHER_INTRO_H2_6); _events.ScheduleEvent(EVENT_INTRO_H2_15, 8000); break; @@ -557,7 +558,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_OPEN_FROSTWORN_DOOR, 0); _events.ScheduleEvent(EVENT_CLOSE_FROSTWORN_DOOR, 4000); } - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) { uther->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_COWER); if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) @@ -568,13 +569,13 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_LK_2, 10000); break; case EVENT_INTRO_LK_2: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->AI()->Talk(SAY_LK_INTRO_1); _events.ScheduleEvent(EVENT_INTRO_LK_3, 1000); break; case EVENT_INTRO_LK_3: // The Lich King banishes Uther to the abyss. - if (Creature* uther = me->GetCreature(*me, _utherGUID)) + if (Creature* uther = ObjectAccessor::GetCreature(*me, _utherGUID)) { uther->CastSpell(uther, SPELL_UTHER_DESPAWN, true); uther->DespawnOrUnsummon(5000); @@ -584,7 +585,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript break; case EVENT_INTRO_LK_4: // He steps forward and removes the runeblade from the heap of skulls. - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (GameObject* frostmourne = ObjectAccessor::GetGameObject(*me, _instance->GetData64(DATA_FROSTMOURNE))) frostmourne->SetPhaseMask(2, true); @@ -594,7 +595,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_LK_5, 8000); break; case EVENT_INTRO_LK_5: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->AI()->Talk(SAY_LK_INTRO_2); _events.ScheduleEvent(EVENT_INTRO_LK_6, 10000); break; @@ -610,7 +611,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript marwyn->CastSpell(marwyn, SPELL_BOSS_SPAWN_AURA, true); marwyn->SetVisible(true); } - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->AI()->Talk(SAY_LK_INTRO_3); lichking->SetWalk(true); @@ -653,7 +654,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript _events.ScheduleEvent(EVENT_INTRO_LK_11, 5000); break; case EVENT_INTRO_LK_11: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) lichking->AI()->Talk(SAY_LK_JAINA_INTRO_END); @@ -666,7 +667,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript if (_instance) _instance->SetData(DATA_INTRO_EVENT, DONE); // Loralen or Koreln disappearAndDie() - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->DespawnOrUnsummon(5000); _lichkingGUID = 0; @@ -702,7 +703,7 @@ class npc_jaina_or_sylvanas_hor : public CreatureScript CreatureAI* GetAI(Creature* creature) const OVERRIDE { - return new npc_jaina_or_sylvanas_horAI(creature); + return GetHallsOfReflectionAI<npc_jaina_or_sylvanas_horAI>(creature); } }; @@ -716,9 +717,8 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript { npc_jaina_or_sylvanas_escape_horAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _lichkingGUID(0), _walltargetGUID(0), - _icewallGUID(0), _icewall(0), _isattackingwall(0) + _icewallGUID(0), _icewall(0) { - _instance = me->GetInstanceScript(); } InstanceScript* _instance; @@ -726,33 +726,29 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript uint64 _walltargetGUID; // dummy uint64 _icewallGUID; // object uint32 _icewall; // icewall number - uint32 _isattackingwall; //sylvannas attacking icewall EventMap _events; - void Reset() OVERRIDE { _events.Reset(); - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->DespawnOrUnsummon(1); _lichkingGUID = 0; _walltargetGUID = 0; _icewallGUID = 0; _icewall = 0; - _isattackingwall = false; _events.ScheduleEvent(EVENT_ESCAPE, 0); } void JustDied(Unit* /*Killer*/) OVERRIDE { - if (_instance) _instance->SetData(DATA_ESCAPE_EVENT, FAIL); } - void DoAction(int32 actionID) OVERRIDE + void DoAction(int32 actionId) OVERRIDE { - switch (actionID) + switch (actionId) { case ACTION_START_ESCAPING: // called by InstanceScript when we need to start the escaping event _events.ScheduleEvent(EVENT_ESCAPE_1, 1000); @@ -760,10 +756,9 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript case ACTION_WALL_BROKEN: _icewall++; if (_icewall != 4) - _events.ScheduleEvent(EVENT_ESCAPE_17,3000); + _events.ScheduleEvent(EVENT_ESCAPE_17, 3000); else - _events.ScheduleEvent(EVENT_ESCAPE_23,3000); - _isattackingwall = false; + _events.ScheduleEvent(EVENT_ESCAPE_23, 3000); break; } } @@ -781,6 +776,19 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript } } + void DestroyIceWall() + { + if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) + me->RemoveAurasDueToSpell(SPELL_JAINA_DESTROY_ICE_WALL); + else + me->RemoveAurasDueToSpell(SPELL_SYLVANAS_DESTROY_ICE_WALL); + + _instance->HandleGameObject(_icewallGUID, true); + + if (Creature* wallTarget = ObjectAccessor::GetCreature(*me, _walltargetGUID)) + wallTarget->DespawnOrUnsummon(); + } + void UpdateAI(uint32 diff) OVERRIDE { _events.Update(diff); @@ -792,21 +800,21 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript case EVENT_ESCAPE: if (Creature* lichking = me->SummonCreature(NPC_LICH_KING_PART2, LichKingSpawnPos2, TEMPSUMMON_MANUAL_DESPAWN)) { - me->Attack(lichking,true); - lichking->Attack(me,true); + me->Attack(lichking, true); + lichking->Attack(me, true); me->SetReactState(REACT_PASSIVE); lichking->SetReactState(REACT_PASSIVE); _lichkingGUID = lichking->GetGUID(); if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->DoCast(me, SPELL_JAINA_ICEBARRIER); + DoCast(me, SPELL_JAINA_ICEBARRIER); else - me->AI()->DoCast(me, SPELL_SYLVANAS_CLOAKOFDARKNESS); + DoCast(me, SPELL_SYLVANAS_CLOAKOFDARKNESS); } me->SetHealth(252000); break; case EVENT_ESCAPE_1: _instance->SetData(DATA_ESCAPE_EVENT, IN_PROGRESS); - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) lichking->AI()->Talk(SAY_LK_ESCAPE_1); @@ -817,30 +825,27 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript break; case EVENT_ESCAPE_2: if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->DoCast(me,SPELL_CAST_VISUAL,true); + DoCast(me, SPELL_CAST_VISUAL, true); else - me->AI()->DoCast(me,SPELL_SYLVANAS_JUMP,true); + DoCast(me, SPELL_SYLVANAS_JUMP, true); _events.ScheduleEvent(EVENT_ESCAPE_3, 1000); break; case EVENT_ESCAPE_3: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) - { - if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - lichking->AI()->DoCast(lichking, SPELL_JAINA_ICEPRISON, true); - else - lichking->AI()->DoCast(lichking, SPELL_SYLVANAS_DARKBINDING, true); - } + if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) + DoCastAOE(SPELL_JAINA_ICEPRISON, true); + else + DoCastAOE(SPELL_SYLVANAS_DARKBINDING, true); _events.ScheduleEvent(EVENT_ESCAPE_4, 2000); break; case EVENT_ESCAPE_4: if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->Talk(SAY_JAINA_ESCAPE_1); + Talk(SAY_JAINA_ESCAPE_1); else - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_1); + Talk(SAY_SYLVANAS_ESCAPE_1); _events.ScheduleEvent(EVENT_ESCAPE_5, 2000); break; case EVENT_ESCAPE_5: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->CombatStop(); me->GetMotionMaster()->MovePoint(0, JainaShadowThroneDoor); if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) @@ -853,7 +858,7 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP|UNIT_NPC_FLAG_QUESTGIVER); break; case EVENT_ESCAPE_7: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) lichking->RemoveAurasDueToSpell(SPELL_JAINA_ICEPRISON); @@ -863,13 +868,13 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript _events.ScheduleEvent(EVENT_ESCAPE_8, 1000); break; case EVENT_ESCAPE_8: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->HandleEmoteCommand(TEXT_EMOTE_ROAR); me->GetMotionMaster()->MovePoint(0, NpcJainaOrSylvanasEscapeRoute[0]); _events.ScheduleEvent(EVENT_ESCAPE_9, 3000); break; case EVENT_ESCAPE_9: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->GetMotionMaster()->MovePoint(0, NpcJainaOrSylvanasEscapeRoute[0]); _events.ScheduleEvent(EVENT_ESCAPE_10, 1000); break; @@ -879,39 +884,39 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript break; case EVENT_ESCAPE_11: me->GetMotionMaster()->MovePoint(0, NpcJainaOrSylvanasEscapeRoute[2]); - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->GetMotionMaster()->MovePoint(0, LichKingFirstSummon); _events.ScheduleEvent(EVENT_ESCAPE_12, 6000); break; case EVENT_ESCAPE_12: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->AI()->Talk(SAY_LK_ESCAPE_3); lichking->AI()->DoCast(me, SPELL_RAISE_DEAD); - lichking->Attack(me,true); + lichking->Attack(me, true); } _events.ScheduleEvent(EVENT_ESCAPE_13, 4000); break; case EVENT_ESCAPE_13: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->AI()->DoCast(lichking, SPELL_REMORSELESS_WINTER, true); lichking->AI()->DoCast(lichking, SPELL_SUMMON_RISE_WITCH_DOCTOR); lichking->GetMotionMaster()->MoveIdle(); lichking->GetMotionMaster()->MoveChase(me); } - if (Creature* walltarget = me->SummonCreature(NPC_ICE_WALL,IceWalls[0].GetPositionX(), IceWalls[0].GetPositionY(), IceWalls[0].GetPositionZ(), IceWalls[0].GetOrientation(), TEMPSUMMON_MANUAL_DESPAWN, 720000)) + if (Creature* walltarget = me->SummonCreature(NPC_ICE_WALL, IceWalls[0], TEMPSUMMON_MANUAL_DESPAWN, 720000)) { _walltargetGUID = walltarget->GetGUID(); walltarget->AI()->DoCast(walltarget, SPELL_SUMMON_ICE_WALL); walltarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->Attack(walltarget,false); + me->Attack(walltarget, false); } me->GetMotionMaster()->MovePoint(0, NpcJainaOrSylvanasEscapeRoute[3]); _events.ScheduleEvent(EVENT_ESCAPE_14, 8000); break; case EVENT_ESCAPE_14: - if (Creature* walltarget = me->GetCreature(*me, _walltargetGUID)) + if (Creature* walltarget = ObjectAccessor::GetCreature(*me, _walltargetGUID)) { if (GameObject* icewall = walltarget->FindNearestGameObject(GO_ICE_WALL, 50.00f)) { @@ -919,61 +924,42 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript icewall->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); _instance->HandleGameObject(0, false, icewall); if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->Talk(SAY_JAINA_ESCAPE_2); + Talk(SAY_JAINA_ESCAPE_2); else - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_2); + Talk(SAY_SYLVANAS_ESCAPE_2); } } _events.ScheduleEvent(EVENT_ESCAPE_15, 1000); break; case EVENT_ESCAPE_15: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->GetMotionMaster()->MoveIdle(); lichking->GetMotionMaster()->MoveChase(me); lichking->SetReactState(REACT_PASSIVE); } if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->DoCast(me, SPELL_JAINA_DESTROY_ICE_WALL, false); + DoCast(me, SPELL_JAINA_DESTROY_ICE_WALL, true); else - { - _isattackingwall = true; - me->AI()->DoCast(me, SPELL_SYLVANAS_DESTROY_ICE_WALL, false); - _events.ScheduleEvent(EVENT_ESCAPE_16, 1000); - } - break; - case EVENT_ESCAPE_16: - if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE) - { - me->AI()->DoCast(me, SPELL_SYLVANAS_DESTROY_ICE_WALL, false); - if (_isattackingwall) - _events.ScheduleEvent(EVENT_ESCAPE_16, 1000); - } + DoCast(me, SPELL_SYLVANAS_DESTROY_ICE_WALL, true); break; case EVENT_ESCAPE_17:// ICEWALL BROKEN me->GetMotionMaster()->MoveIdle(); - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->StopMoving(); lichking->AI()->Talk(SAY_LK_ESCAPE_3); lichking->AI()->DoCast(me, SPELL_RAISE_DEAD); } - if (Creature* walltarget = me->GetCreature(*me, _walltargetGUID)) - walltarget->DespawnOrUnsummon(0); - if (GameObject* icewall = ObjectAccessor::GetGameObject(*me, _icewallGUID)) - { - _instance->HandleGameObject(0 ,true, icewall); - if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->RemoveAurasDueToSpell(SPELL_JAINA_DESTROY_ICE_WALL); - else - me->RemoveAurasDueToSpell(SPELL_SYLVANAS_DESTROY_ICE_WALL); - } + + DestroyIceWall(); + if (_icewall && _icewall < 4) me->GetMotionMaster()->MovePoint(0, NpcJainaOrSylvanasEscapeRoute[_icewall + 3]); _events.ScheduleEvent(EVENT_ESCAPE_18, 2000); break; case EVENT_ESCAPE_18: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->GetMotionMaster()->MoveIdle(); lichking->GetMotionMaster()->MoveChase(me); @@ -981,29 +967,29 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript _events.ScheduleEvent(EVENT_ESCAPE_19, 6000); break; case EVENT_ESCAPE_19: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_icewall && _icewall < 4) lichking->AI()->DoCast(lichking, SPELL_SUMMON_RISE_WITCH_DOCTOR); lichking->GetMotionMaster()->MoveIdle(); lichking->GetMotionMaster()->MoveChase(me); lichking->SetReactState(REACT_PASSIVE); - lichking->Attack(me,true); + lichking->Attack(me, true); } if (_icewall < 4) { - if (Creature* walltarget = me->SummonCreature(NPC_ICE_WALL, IceWalls[_icewall].GetPositionX(), IceWalls[_icewall].GetPositionY(), IceWalls[_icewall].GetPositionZ(), IceWalls[_icewall].GetOrientation(), TEMPSUMMON_MANUAL_DESPAWN, 720000)) + if (Creature* walltarget = me->SummonCreature(NPC_ICE_WALL, IceWalls[_icewall], TEMPSUMMON_MANUAL_DESPAWN, 720000)) { _walltargetGUID = walltarget->GetGUID(); walltarget->AI()->DoCast(walltarget, SPELL_SUMMON_ICE_WALL); walltarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->Attack(walltarget,false); + me->Attack(walltarget, false); } } _events.ScheduleEvent(EVENT_ESCAPE_20, 3000); break; case EVENT_ESCAPE_20: - if (Creature* walltarget = me->GetCreature(*me, _walltargetGUID)) + if (Creature* walltarget = ObjectAccessor::GetCreature(*me, _walltargetGUID)) { if (GameObject* icewall = walltarget->FindNearestGameObject(GO_ICE_WALL, 50.00f)) { @@ -1013,24 +999,24 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) { if (_icewall == 1) - me->AI()->Talk(SAY_JAINA_ESCAPE_3); + Talk(SAY_JAINA_ESCAPE_3); else if (_icewall == 2) - me->AI()->Talk(SAY_JAINA_ESCAPE_4); + Talk(SAY_JAINA_ESCAPE_4); else if (_icewall == 3) - me->AI()->Talk(SAY_JAINA_ESCAPE_5); + Talk(SAY_JAINA_ESCAPE_5); } else if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE) { if (_icewall == 1) - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_3); + Talk(SAY_SYLVANAS_ESCAPE_3); else if (_icewall == 2) - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_4); + Talk(SAY_SYLVANAS_ESCAPE_4); else if (_icewall == 3) - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_5); + Talk(SAY_SYLVANAS_ESCAPE_5); } } } - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_icewall && _icewall < 3) lichking->AI()->DoCast(lichking, SPELL_SUMMON_RISE_WITCH_DOCTOR); @@ -1044,53 +1030,37 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript break; case EVENT_ESCAPE_21: if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->DoCast(me, SPELL_JAINA_DESTROY_ICE_WALL, false); + DoCast(me, SPELL_JAINA_DESTROY_ICE_WALL, true); else - { - me->AI()->DoCast(me, SPELL_SYLVANAS_DESTROY_ICE_WALL, false); - _isattackingwall = true; - _events.ScheduleEvent(EVENT_ESCAPE_22, 1000); - } - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + DoCast(me, SPELL_SYLVANAS_DESTROY_ICE_WALL, true); + + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_icewall == 1) lichking->AI()->DoCast(lichking, SPELL_SUMMON_LUMBERING_ABOMINATION); else if (_icewall > 1 && _icewall < 4) + { lichking->AI()->DoCast(lichking, SPELL_SUMMON_RISE_WITCH_DOCTOR); + _events.ScheduleEvent(EVENT_ESCAPE_22, 1000); + } } break; case EVENT_ESCAPE_22: - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { if (_icewall >= 2 && _icewall < 4) lichking->AI()->DoCast(lichking, SPELL_SUMMON_LUMBERING_ABOMINATION); } - if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE) - { - me->AI()->DoCast(me, SPELL_SYLVANAS_DESTROY_ICE_WALL, false); - if (_isattackingwall) - _events.ScheduleEvent(EVENT_ESCAPE_22, 1000); - } break; + case EVENT_ESCAPE_23: // FINAL PART + DestroyIceWall(); - case EVENT_ESCAPE_23:// FINAL PART - if (Creature* walltarget = me->GetCreature(*me, _walltargetGUID)) - walltarget->DespawnOrUnsummon(0); - if (GameObject* icewall = ObjectAccessor::GetGameObject(*me, _icewallGUID)) - { - _instance->HandleGameObject(0 ,true, icewall); - if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - { - me->RemoveAurasDueToSpell(SPELL_JAINA_DESTROY_ICE_WALL); - me->AI()->Talk(SAY_JAINA_ESCAPE_6); - } - else - { - me->RemoveAurasDueToSpell(SPELL_SYLVANAS_DESTROY_ICE_WALL); - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_6); - } - } - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) + Talk(SAY_JAINA_ESCAPE_6); + else + Talk(SAY_SYLVANAS_ESCAPE_6); + + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) { lichking->GetMotionMaster()->MovePoint(0, LichKingFinalPos); lichking->AI()->Talk(SAY_LK_ESCAPE_11); @@ -1100,21 +1070,21 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript break; case EVENT_ESCAPE_24: if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->Talk(SAY_JAINA_ESCAPE_8); + Talk(SAY_JAINA_ESCAPE_8); else - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_8); + Talk(SAY_SYLVANAS_ESCAPE_8); _events.ScheduleEvent(EVENT_ESCAPE_25, 5000); break; case EVENT_ESCAPE_25: - if (GameObject* cave = _instance->instance->GetGameObject(_instance->GetData64(DATA_CAVE_IN))) - cave->SetGoState(GO_STATE_READY); + if (GameObject* cave = ObjectAccessor::GetGameObject(*me, _instance->GetData64(DATA_CAVE_IN))) + cave->SetGoState(GO_STATE_READY); _events.ScheduleEvent(EVENT_ESCAPE_26, 4000); break; case EVENT_ESCAPE_26: if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE) - me->AI()->Talk(SAY_JAINA_ESCAPE_10); + Talk(SAY_JAINA_ESCAPE_10); else - me->AI()->Talk(SAY_SYLVANAS_ESCAPE_9); + Talk(SAY_SYLVANAS_ESCAPE_9); _events.ScheduleEvent(EVENT_ESCAPE_27, 4000); break; case EVENT_ESCAPE_27: @@ -1123,7 +1093,7 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript else me->SummonGameObject(GO_CAPTAIN_CHEST_3, ChestPos.GetPositionX(), ChestPos.GetPositionY(), ChestPos.GetPositionZ(), ChestPos.GetOrientation(), 0, 0, 0, 0, 720000); me->SummonGameObject(GO_PORTAL, FinalPortalPos.GetPositionX(), FinalPortalPos.GetPositionY(), FinalPortalPos.GetPositionZ(), FinalPortalPos.GetOrientation(), 0, 0, 0, 0, 720000); - if (Creature* lichking = me->GetCreature(*me, _lichkingGUID)) + if (Creature* lichking = ObjectAccessor::GetCreature(*me, _lichkingGUID)) lichking->DespawnOrUnsummon(1); break; } @@ -1133,7 +1103,7 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript CreatureAI* GetAI(Creature* creature) const OVERRIDE { - return new npc_jaina_or_sylvanas_escape_horAI(creature); + return GetHallsOfReflectionAI<npc_jaina_or_sylvanas_escape_horAI>(creature); } }; diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h index 3223ecf66a5..d8d4f2d5524 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h @@ -175,4 +175,10 @@ struct boss_horAI : ScriptedAI } }; +template<class AI> +AI* GetHallsOfReflectionAI(Creature* creature) +{ + return GetInstanceAI<AI>(creature, HoRScriptName); +} + #endif diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index 532ede3f849..3a2d5c592fe 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -1815,7 +1815,7 @@ class spell_fetch_ingredient_aura : public SpellScriptLoader } }; -enum StormCloud +enum StormCloud { STORM_COULD = 29939, HEALING_WINDS = 55549, @@ -1842,7 +1842,7 @@ public: { Reset(); } - + void SpellHit(Unit* caster, const SpellInfo* spell) OVERRIDE { if (spell->Id != GYMERS_GRAB) diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp index 41280471bd4..f27ee2b72e0 100644 --- a/src/server/shared/Database/Implementation/WorldDatabase.cpp +++ b/src/server/shared/Database/Implementation/WorldDatabase.cpp @@ -75,8 +75,6 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_O, "UPDATE waypoint_scripts SET o = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(WORLD_INS_CREATURE_TRANSPORT, "INSERT INTO creature_transport (guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO) values (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(WORLD_UPD_CREATURE_TRANSPORT_EMOTE, "UPDATE creature_transport SET emote = ? WHERE transport_entry = ? AND guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, permission, help FROM command", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, Health_mod, Mana_mod, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); diff --git a/src/server/shared/Database/Implementation/WorldDatabase.h b/src/server/shared/Database/Implementation/WorldDatabase.h index 171627bb83a..b17aa0dedbb 100644 --- a/src/server/shared/Database/Implementation/WorldDatabase.h +++ b/src/server/shared/Database/Implementation/WorldDatabase.h @@ -95,8 +95,6 @@ enum WorldDatabaseStatements WORLD_UPD_WAYPOINT_SCRIPT_O, WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, WORLD_DEL_CREATURE, - WORLD_INS_CREATURE_TRANSPORT, - WORLD_UPD_CREATURE_TRANSPORT_EMOTE, WORLD_SEL_COMMANDS, WORLD_SEL_CREATURE_TEMPLATE, WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp index be74357d1f0..c90a6e527f7 100644 --- a/src/tools/mesh_extractor/ContinentBuilder.cpp +++ b/src/tools/mesh_extractor/ContinentBuilder.cpp @@ -153,9 +153,9 @@ void ContinentBuilder::Build() } else { - params.maxPolys = 1 << STATIC_POLY_BITS; - params.maxTiles = TileMap->TileTable.size(); - rcVcopy(params.orig, bmin); + params.maxPolys = 32768; + params.maxTiles = 4096; + rcVcopy(params.orig, Constants::Origin); params.tileHeight = Constants::TileSize; params.tileWidth = Constants::TileSize; fwrite(¶ms, sizeof(dtNavMeshParams), 1, mmap); diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp index 658c312a6e8..df828dcd573 100644 --- a/src/tools/mesh_extractor/Geometry.cpp +++ b/src/tools/mesh_extractor/Geometry.cpp @@ -17,7 +17,7 @@ void Geometry::CalculateBoundingBox( float*& min, float*& max ) max = new float[3]; for (int i = 0; i < 3; ++i) { - max[i] = std::numeric_limits<float>::min(); + max[i] = std::numeric_limits<float>::lowest(); min[i] = std::numeric_limits<float>::max(); } @@ -43,7 +43,7 @@ void Geometry::CalculateBoundingBox( float*& min, float*& max ) void Geometry::CalculateMinMaxHeight( float& min, float& max ) { min = std::numeric_limits<float>::max(); - max = std::numeric_limits<float>::min(); + max = std::numeric_limits<float>::lowest(); for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr) { diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h index 2f8b082f526..30e11741550 100644 --- a/src/tools/mesh_extractor/MPQ.h +++ b/src/tools/mesh_extractor/MPQ.h @@ -26,7 +26,8 @@ public: libmpq__off_t size, transferred; libmpq__file_unpacked_size(mpq_a, filenum, &size); - char *buffer = new char[size]; + char* buffer = new char[size + 1]; + buffer[size] = '\0'; libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp index e1ce4b92bf9..4d3ab808a2e 100644 --- a/src/tools/mesh_extractor/MPQManager.cpp +++ b/src/tools/mesh_extractor/MPQManager.cpp @@ -109,5 +109,7 @@ FILE* MPQManager::GetFileFrom(const std::string& path, MPQArchive* file ) exit(1); } fwrite(buffer, sizeof(uint8), size, ret); + fseek(ret, 0, SEEK_SET); + delete[] buffer; return ret; } diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp index 3443b3d9e0a..0d9160a610b 100644 --- a/src/tools/mesh_extractor/MeshExtractor.cpp +++ b/src/tools/mesh_extractor/MeshExtractor.cpp @@ -76,7 +76,7 @@ void ExtractDBCs() std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt"; // Extract the component file - Utils::SaveToDisk(MPQHandler->GetFile(component), path + component); + Utils::SaveToDisk(MPQHandler->GetFileFrom(component, MPQHandler->LocaleFiles[*itr]), path + component); // Extract the DBC files for the given locale for (std::set<std::string>::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2) Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + folderLen)); @@ -378,8 +378,8 @@ int main(int argc, char* argv[]) if (extractFlags & Constants::EXTRACT_FLAG_TEST) { - float start[] = { -44.641f, -34.606f, -1.045f }; - float end[] = { -66.18f, 20.222f, -1.128f }; + float start[] = { 16226.200195f, 16257.000000f, 13.202200f }; + float end[] = { 16245.725586f, 16382.465820f, 47.384956f }; // float m_spos[3]; @@ -408,7 +408,7 @@ int main(int argc, char* argv[]) dtPolyRef m_startRef; dtPolyRef m_endRef; - FILE* mmap = fopen("mmaps/555.mmap", "rb"); + FILE* mmap = fopen("mmaps/001.mmap", "rb"); dtNavMeshParams params; int count = fread(¶ms, sizeof(dtNavMeshParams), 1, mmap); fclose(mmap); @@ -427,7 +427,7 @@ int main(int argc, char* argv[]) for (int j = 0; j <= 32; ++j) { char buff[100]; - sprintf(buff, "mmaps/555%02i%02i.mmtile", i, j); + sprintf(buff, "mmaps/001%02i%02i.mmtile", i, j); LoadTile(navMesh, buff); } } diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp index 13566456329..51df91d2652 100644 --- a/src/tools/mesh_extractor/TileBuilder.cpp +++ b/src/tools/mesh_extractor/TileBuilder.cpp @@ -283,6 +283,15 @@ uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); rcBuildPolyMeshDetail(Context, *pmesh, *chf, Config.detailSampleDist, Config.detailSampleMaxError, *dmesh); + // Set flags according to area types (e.g. Swim for Water) + for (int i = 0; i < pmesh->npolys; i++) + { + if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN) + pmesh->flags[i] = Constants::POLY_FLAG_WALK; + else if (pmesh->areas[i] == Constants::POLY_AREA_WATER) + pmesh->flags[i] = Constants::POLY_FLAG_SWIM; + } + dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); // PolyMesh data @@ -299,21 +308,25 @@ uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) params.detailVertsCount = dmesh->nverts; params.detailTris = dmesh->tris; params.detailTriCount = dmesh->ntris; - rcVcopy(params.bmin, pmesh->bmin); - rcVcopy(params.bmax, pmesh->bmax); // General settings - params.ch = InstanceConfig.ch; - params.cs = InstanceConfig.cs; - params.walkableClimb = InstanceConfig.walkableClimb * InstanceConfig.ch; - params.walkableHeight = InstanceConfig.walkableHeight * InstanceConfig.ch; - params.walkableRadius = InstanceConfig.walkableRadius * InstanceConfig.cs; + params.ch = Config.ch; + params.cs = Config.cs; + params.walkableClimb = Config.walkableClimb * Config.ch; + params.walkableHeight = Config.walkableHeight * Config.ch; + params.walkableRadius = Config.walkableRadius * Config.cs; params.tileX = X; params.tileY = Y; params.tileLayer = 0; params.buildBvTree = true; - rcVcopy(params.bmax, bmax); - rcVcopy(params.bmin, bmin); + // Recalculate the bounds with the added geometry + float* bmin2 = NULL, *bmax2 = NULL; + CalculateTileBounds(bmin2, bmax2, navMeshParams); + bmin2[1] = bmin[1]; + bmax2[1] = bmax[1]; + + rcVcopy(params.bmax, bmax2); + rcVcopy(params.bmin, bmin2); // Offmesh-connection settings params.offMeshConCount = 0; // none for now diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp index e7ffc0ed919..24cfb5cd1db 100644 --- a/src/tools/mesh_extractor/Utils.cpp +++ b/src/tools/mesh_extractor/Utils.cpp @@ -173,24 +173,38 @@ void Utils::SaveToDisk( FILE* stream, const std::string& path ) if (!disk) { printf("SaveToDisk: Could not save file %s to disk, please verify that you have write permissions on that directory\n", path.c_str()); + fclose(stream); return; } uint32 size = Utils::Size(stream); uint8* data = new uint8[size]; // Read the data to an array - if (fread(data, 1, size, stream) != 1) + size_t read = fread(data, size, 1, stream); + if (read != 1) { - printf("SaveToDisk: Error reading from Stream while trying to save file %s to disck.\n", path.c_str()); + printf("SaveToDisk: Error reading from Stream while trying to save file %s to disk.\n", path.c_str()); + fclose(disk); + fclose(stream); return; } + // And write it in the file - fwrite(data, 1, size, disk); + size_t wrote = fwrite(data, size, 1, disk); + if (wrote != 1) + { + printf("SaveToDisk: Error writing to the file while trying to save %s to disk.\n", path.c_str()); + fclose(stream); + fclose(disk); + return; + } // Close the filestream fclose(disk); + fclose(stream); + // Free the used memory - delete [] data; + delete[] data; } Vector3 Utils::ToWoWCoords(const Vector3& vec ) |