diff options
Diffstat (limited to 'src')
43 files changed, 2339 insertions, 111 deletions
diff --git a/src/common/Banner.cpp b/src/common/Banner.cpp new file mode 100644 index 00000000000..a9ba530baa9 --- /dev/null +++ b/src/common/Banner.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008-2016 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 "Banner.h" +#include "GitRevision.h" +#include "StringFormat.h" + +void Trinity::Banner::Show(char const* applicationName, void(*log)(char const* text), void(*logExtraInfo)()) +{ + log(Trinity::StringFormat("%s (%s)", GitRevision::GetFullVersion(), applicationName).c_str()); + log("<Ctrl-C> to stop.\n"); + log(" ______ __"); + log("/\\__ _\\ __ __/\\ \\__"); + log("\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\, _\\ __ __"); + log(" \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\"); + log(" \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\"); + log(" \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\"); + log(" \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); + log(" C O R E /\\___/"); + log("http://TrinityCore.org \\/__/\n"); + + if (logExtraInfo) + logExtraInfo(); +} diff --git a/src/common/Banner.h b/src/common/Banner.h new file mode 100644 index 00000000000..6e846b2fe9b --- /dev/null +++ b/src/common/Banner.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TrinityCore_Banner_h__ +#define TrinityCore_Banner_h__ + +#include "Define.h" + +namespace Trinity +{ + namespace Banner + { + TC_COMMON_API void Show(char const* applicationName, void(*log)(char const* text), void(*logExtraInfo)()); + } +} + +#endif // TrinityCore_Banner_h__ diff --git a/src/server/bnetserver/Main.cpp b/src/server/bnetserver/Main.cpp index e2397981818..b54249f9412 100644 --- a/src/server/bnetserver/Main.cpp +++ b/src/server/bnetserver/Main.cpp @@ -28,6 +28,7 @@ #include "ProcessPriority.h" #include "RealmList.h" #include "GitRevision.h" +#include "Banner.h" #include "SslContext.h" #include "DatabaseLoader.h" #include "LoginRESTService.h" @@ -108,11 +109,18 @@ int main(int argc, char** argv) sLog->RegisterAppender<AppenderDB>(); sLog->Initialize(nullptr); - TC_LOG_INFO("server.bnetserver", "%s (bnetserver)", GitRevision::GetFullVersion()); - TC_LOG_INFO("server.bnetserver", "<Ctrl-C> to stop.\n"); - TC_LOG_INFO("server.bnetserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); - TC_LOG_INFO("server.bnetserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); - TC_LOG_INFO("server.bnetserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); + Trinity::Banner::Show("bnetserver", + [](char const* text) + { + TC_LOG_INFO("server.bnetserver", "%s", text); + }, + []() + { + TC_LOG_INFO("server.bnetserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); + TC_LOG_INFO("server.bnetserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); + TC_LOG_INFO("server.bnetserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); + } + ); // Seed the OpenSSL's PRNG here. // That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 5789bab01c4..8ca023b1856 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -87,7 +87,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); - PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, extendState, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, extendState, resettime, entranceId FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, maxDuration, remainTime, remainCharges, castItemLevel FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURA_EFFECTS, "SELECT casterGuid, itemGuid, spell, effectMask, effectIndex, amount, baseAmount FROM character_aura_effect WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?", CONNECTION_ASYNC); @@ -311,8 +311,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_TUTORIALS, "DELETE FROM account_tutorial WHERE accountId = ?", CONNECTION_ASYNC); // Instance saves - PrepareStatement(CHAR_INS_INSTANCE_SAVE, "INSERT INTO instance (id, map, resettime, difficulty, completedEncounters, data) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_INSTANCE_DATA, "UPDATE instance SET completedEncounters=?, data=? WHERE id=?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_INSTANCE_SAVE, "INSERT INTO instance (id, map, resettime, difficulty, completedEncounters, data, entranceId) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_INSTANCE_DATA, "UPDATE instance SET completedEncounters=?, data=?, entranceId=? WHERE id=?", CONNECTION_ASYNC); // Game event saves PrepareStatement(CHAR_DEL_GAME_EVENT_SAVE, "DELETE FROM game_event_save WHERE eventEntry = ?", CONNECTION_ASYNC); @@ -487,7 +487,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters, entranceId FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE, "SELECT guid FROM character_instance WHERE instance = ? and permanent = 1", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH); diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h index 172e1f9246c..eb0c3895c8c 100644 --- a/src/server/game/Battlefield/Battlefield.h +++ b/src/server/game/Battlefield/Battlefield.h @@ -107,14 +107,14 @@ class TC_GAME_API BfCapturePoint virtual void SendChangePhase(); bool SetCapturePointData(GameObject* capturePoint); + bool DelCapturePoint(); GameObject* GetCapturePointGo(); uint32 GetCapturePointEntry() const { return m_capturePointEntry; } TeamId GetTeamId() const { return m_team; } + BattlefieldObjectiveStates GetObjectiveState() const { return m_State; } protected: - bool DelCapturePoint(); - // active Players in the area of the objective, 0 - alliance, 1 - horde GuidSet m_activePlayers[BG_TEAMS_COUNT]; diff --git a/src/server/game/Battlefield/BattlefieldMgr.cpp b/src/server/game/Battlefield/BattlefieldMgr.cpp index b92b2e64318..b4b4e269b28 100644 --- a/src/server/game/Battlefield/BattlefieldMgr.cpp +++ b/src/server/game/Battlefield/BattlefieldMgr.cpp @@ -17,6 +17,7 @@ #include "BattlefieldMgr.h" #include "BattlefieldWG.h" +#include "BattlefieldTB.h" #include "Player.h" BattlefieldMgr::BattlefieldMgr() @@ -53,8 +54,6 @@ void BattlefieldMgr::InitBattlefield() TC_LOG_INFO("bg.battlefield", "Battlefield: Wintergrasp successfully initiated."); } - /* - For Cataclysm: Tol Barad Battlefield* tb = new BattlefieldTB; // respawn, init variables if (!tb->SetupBattlefield()) @@ -67,7 +66,6 @@ void BattlefieldMgr::InitBattlefield() _battlefieldSet.push_back(tb); TC_LOG_DEBUG("bg.battlefield", "Battlefield: Tol Barad successfully initiated."); } - */ } void BattlefieldMgr::AddZone(uint32 zoneId, Battlefield* bf) diff --git a/src/server/game/Battlefield/Zones/BattlefieldTB.cpp b/src/server/game/Battlefield/Zones/BattlefieldTB.cpp new file mode 100644 index 00000000000..affc1e0db47 --- /dev/null +++ b/src/server/game/Battlefield/Zones/BattlefieldTB.cpp @@ -0,0 +1,869 @@ +/* +* Copyright (C) 2008-2016 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/>. +*/ + +// TO-DO: +// - Implement proper support for vehicles (Player::VehicleSpellInitialize()) +// - Siege Engine Turret (45564) crashing server (Auras: Unknown Shapeshift Type: 24) +// - Graveyard spirit phasing, ressurection, Spiritual Immunity aura for players nearby +// - Warn and teleport players out of the Baradin Hold instance (need sniffs; spell 94964?) +// - Not sure, but players should probably be able to ressurect from guide spirits when there's no battle +// - Check and script achievements + +#include "BattlefieldTB.h" +#include "AchievementMgr.h" +#include "CreatureTextMgr.h" +#include "Battleground.h" +#include "MapManager.h" +#include "ObjectMgr.h" +#include "Opcodes.h" +#include "Player.h" +#include "SpellAuras.h" +#include "TemporarySummon.h" +#include "WorldSession.h" + +BattlefieldTB::~BattlefieldTB() { } + +bool BattlefieldTB::SetupBattlefield() +{ + m_TypeId = BATTLEFIELD_TB; // See enum BattlefieldTypes + m_BattleId = BATTLEFIELD_BATTLEID_TB; + m_ZoneId = BATTLEFIELD_TB_ZONEID; + m_MapId = BATTLEFIELD_TB_MAPID; + m_Map = sMapMgr->CreateBaseMap(m_MapId); + + InitStalker(NPC_DEBUG_ANNOUNCER, TolBaradDebugAnnouncerPos); + + m_MaxPlayer = sWorld->getIntConfig(CONFIG_TOLBARAD_PLR_MAX); + m_IsEnabled = sWorld->getBoolConfig(CONFIG_TOLBARAD_ENABLE); + m_MinPlayer = sWorld->getIntConfig(CONFIG_TOLBARAD_PLR_MIN); + m_MinLevel = sWorld->getIntConfig(CONFIG_TOLBARAD_PLR_MIN_LVL); + m_BattleTime = sWorld->getIntConfig(CONFIG_TOLBARAD_BATTLETIME) * MINUTE * IN_MILLISECONDS; + m_BonusTime = sWorld->getIntConfig(CONFIG_TOLBARAD_BONUSTIME) * MINUTE * IN_MILLISECONDS; + m_NoWarBattleTime = sWorld->getIntConfig(CONFIG_TOLBARAD_NOBATTLETIME) * MINUTE * IN_MILLISECONDS; + m_RestartAfterCrash = sWorld->getIntConfig(CONFIG_TOLBARAD_RESTART_AFTER_CRASH) * MINUTE * IN_MILLISECONDS; + + m_TimeForAcceptInvite = 20; + m_StartGroupingTimer = 15 * MINUTE * IN_MILLISECONDS; + m_StartGrouping = false; + m_isActive = false; + + KickPosition.Relocate(-605.5f, 1181.31f, 95.96f, 6.177155f); + KickPosition.m_mapId = m_MapId; + + RegisterZone(m_ZoneId); + + m_Data32.resize(BATTLEFIELD_TB_DATA_MAX); + + m_saveTimer = 5 * MINUTE * IN_MILLISECONDS; + + updatedNPCAndObjects = true; + m_updateObjectsTimer = 0; + + // Was there a battle going on or time isn't set yet? Then use m_RestartAfterCrash + if (sWorld->getWorldState(TB_WS_STATE_BATTLE) == 1 || sWorld->getWorldState(TB_WS_TIME_NEXT_BATTLE) == 0) + sWorld->setWorldState(TB_WS_TIME_NEXT_BATTLE, m_RestartAfterCrash); + + // Set timer + m_Timer = sWorld->getWorldState(TB_WS_TIME_NEXT_BATTLE); + + // Defending team isn't set yet? Choose randomly. + if (sWorld->getWorldState(TB_WS_FACTION_CONTROLLING) == 0) + sWorld->setWorldState(TB_WS_FACTION_CONTROLLING, uint64(urand(1, 2))); + + // Set defender team + SetDefenderTeam(TeamId(sWorld->getWorldState(TB_WS_FACTION_CONTROLLING) - 1)); + + // Just to save world states + SendInitWorldStatesToAll(); + + // Create capture points + for (uint8 i = 0; i < TB_BASE_COUNT; i++) + { + TolBaradCapturePoint* capturePoint = new TolBaradCapturePoint(this, GetDefenderTeam()); + + //Spawn flag pole + if (GameObject* go = SpawnGameObject(TBCapturePoints[i].entryFlagPole[GetDefenderTeam()], TBCapturePoints[i].x, TBCapturePoints[i].y, TBCapturePoints[i].z, TBCapturePoints[i].o)) + { + go->SetGoArtKit(GetDefenderTeam() == TEAM_ALLIANCE ? TB_GO_ARTKIT_FLAG_ALLIANCE : TB_GO_ARTKIT_FLAG_HORDE); + capturePoint->SetCapturePointData(go); + } + AddCapturePoint(capturePoint); + } + + // Spawn towers + for (uint8 i = 0; i < TB_TOWERS_COUNT; i++) + if (GameObject* go = SpawnGameObject(TBTowers[i].entry, TBTowers[i].x, TBTowers[i].y, TBTowers[i].z, TBTowers[i].o)) + Towers.insert(go->GetGUID()); + + // Init Graveyards + SetGraveyardNumber(BATTLEFIELD_TB_GRAVEYARD_MAX); + + // Graveyards + for (uint8 i = 0; i < BATTLEFIELD_TB_GRAVEYARD_MAX; i++) + { + BfGraveyard* graveyard = new BfGraveyard(this); + + // When between games, the graveyard is controlled by the defending team + graveyard->Initialize(GetDefenderTeam(), TBGraveyards[i].gyid); + + // Spawn spirits + for (uint8 team = 0; team < 2; team++) + if (Creature* creature = SpawnCreature(TBGraveyards[i].spiritEntry[team], TBGraveyards[i].x, TBGraveyards[i].y, TBGraveyards[i].z, TBGraveyards[i].o, TeamId(team))) + graveyard->SetSpirit(creature, TeamId(team)); + + m_GraveyardList[i] = graveyard; + } + + // Time warning vars + warnedFiveMinutes = false; + warnedTwoMinutes = false; + warnedOneMinute = false; + + UpdateNPCsAndGameObjects(); + + return true; +} + +bool BattlefieldTB::Update(uint32 diff) +{ + bool m_return = Battlefield::Update(diff); + + // Minutes till battle preparation warnings + if (GetState() == BATTLEFIELD_INACTIVE) + { + if (m_Timer <= 5 * MINUTE * IN_MILLISECONDS + m_StartGroupingTimer && !warnedFiveMinutes) + { + warnedFiveMinutes = true; + SendWarning(TB_TEXT_PREPARATIONS_IN_5_MIN); + } + + if (m_Timer <= 2 * MINUTE * IN_MILLISECONDS + m_StartGroupingTimer && !warnedTwoMinutes) + { + warnedTwoMinutes = true; + SendWarning(TB_TEXT_PREPARATIONS_IN_2_MIN); + } + + if (m_Timer <= 1 * MINUTE * IN_MILLISECONDS + m_StartGroupingTimer && !warnedOneMinute) + { + warnedOneMinute = true; + SendWarning(TB_TEXT_PREPARATIONS_IN_1_MIN); + } + } + + if (!updatedNPCAndObjects) + { + if (m_updateObjectsTimer <= diff) + { + UpdateNPCsAndGameObjects(); + updatedNPCAndObjects = true; + } + else + m_updateObjectsTimer -= diff; + } + + if (m_saveTimer <= diff) + { + if (!IsWarTime()) + sWorld->setWorldState(TB_WS_TIME_NEXT_BATTLE, m_Timer); + m_saveTimer = 60 * IN_MILLISECONDS; + } + else + m_saveTimer -= diff; + + return m_return; +} + +void BattlefieldTB::OnPlayerEnterZone(Player* player) +{ + if (!m_isActive) + RemoveAurasFromPlayer(player); + + SendInitWorldStatesTo(player); +} + +void BattlefieldTB::OnPlayerLeaveZone(Player* player) +{ + if (!m_isActive) + RemoveAurasFromPlayer(player); +} + +void BattlefieldTB::OnPlayerJoinWar(Player* player) +{ + RemoveAurasFromPlayer(player); + + player->SetPvP(true); + + // Bonus damage buff for attackers + if (player->GetTeam() == GetAttackerTeam() && GetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED) > 0) + player->CastCustomSpell(SPELL_TOWER_ATTACK_BONUS, SPELLVALUE_AURA_STACK, GetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED), player, TRIGGERED_FULL_MASK); +} + + +void BattlefieldTB::OnPlayerLeaveWar(Player* player) +{ + RemoveAurasFromPlayer(player); +} + +void BattlefieldTB::RemoveAurasFromPlayer(Player* player) +{ + player->RemoveAurasDueToSpell(SPELL_TB_SLOW_FALL); + player->RemoveAurasDueToSpell(SPELL_TB_VETERAN); + player->RemoveAurasDueToSpell(SPELL_TOWER_ATTACK_BONUS); + player->RemoveAurasDueToSpell(SPELL_TB_SPIRITUAL_IMMUNITY); +} + +// 62 fields, [7]-[68] +void BattlefieldTB::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet) +{ + packet.Worldstates.emplace_back(uint32(TB_WS_ALLIANCE_ATTACKING_SHOW), int32(IsWarTime() && GetAttackerTeam() == TEAM_ALLIANCE ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TB_WS_HORDE_ATTACKING_SHOW), int32(IsWarTime() && GetAttackerTeam() == TEAM_HORDE ? 1 : 0)); + + // Not sure if TB + //packet.Worldstates.emplace_back(uint32(TB_WS_9_UNKNOWN), int32(1)); + + packet.Worldstates.emplace_back(uint32(TB_WS_SOUTH_DAMAGED_NEUTRAL), int32(0)); + packet.Worldstates.emplace_back(uint32(TB_WS_SOUTH_INTACT_NEUTRAL), int32(0)); + + packet.Worldstates.emplace_back(uint32(TB_WS_PROGRESS_SHOW), int32(0)); + + // Buildings/bases + for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr) + { + uint8 i = TB_BASE_COUNT; + switch (itr->second->GetCapturePointEntry()) + { + case GO_CAPTURE_POINT_NORTH_A_DEFENDING: + case GO_CAPTURE_POINT_NORTH_H_DEFENDING: + i = TB_BASE_IRONCLAD_GARRISON; + break; + case GO_CAPTURE_POINT_EAST_A_DEFENDING: + case GO_CAPTURE_POINT_EAST_H_DEFENDING: + i = TB_BASE_SLAGWORKS; + break; + case GO_CAPTURE_POINT_WEST_A_DEFENDING: + case GO_CAPTURE_POINT_WEST_H_DEFENDING: + i = TB_BASE_WARDENS_VIGIL; + break; + default: + continue; + } + + TeamId team = TEAM_NEUTRAL; + bool controlled = false; + bool capturing = false; + + switch (itr->second->GetObjectiveState()) + { + case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE: + controlled = true; + team = itr->second->GetTeamId(); + break; + case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE: + team = TEAM_ALLIANCE; + capturing = true; + break; + case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE: + team = TEAM_HORDE; + capturing = true; + break; + default: + team = TEAM_NEUTRAL; + break; + } + + packet.Worldstates.emplace_back(uint32(TBCapturePoints[i].wsControlled[TEAM_ALLIANCE]), int32(team == TEAM_ALLIANCE && controlled ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBCapturePoints[i].wsCapturing[TEAM_ALLIANCE]), int32(team == TEAM_ALLIANCE && capturing ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBCapturePoints[i].wsNeutral), int32(team == TEAM_NEUTRAL ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBCapturePoints[i].wsCapturing[TEAM_HORDE]), int32(team == TEAM_HORDE && capturing ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBCapturePoints[i].wsControlled[TEAM_HORDE]), int32(team == TEAM_HORDE && controlled ? 1 : 0)); + } + + packet.Worldstates.emplace_back(uint32(TB_WS_TOWERS_DESTROYED_SHOW), int32(GetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED))); + packet.Worldstates.emplace_back(uint32(TB_WS_BUILDINGS_CAPTURED_SHOW), int32(IsWarTime() ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TB_WS_BUILDINGS_CAPTURED), int32(GetData(BATTLEFIELD_TB_DATA_BUILDINGS_CAPTURED))); + packet.Worldstates.emplace_back(uint32(TB_WS_TOWERS_DESTROYED), int32(0)); + + packet.Worldstates.emplace_back(uint32(TB_WS_TIME_BATTLE_END_SHOW), int32(IsWarTime() ? 1 : 0)); + + packet.Worldstates.emplace_back(uint32(TB_WS_STATE_BATTLE), int32(IsWarTime() ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TB_WS_STATE_PREPARATIONS), int32(GetState() == BATTLEFIELD_WARMUP ? 1 : 0)); + + // Not sure if TB + //packet.Worldstates.emplace_back(uint32(TB_WS_35_UNKNOWN), int32(0)); + //packet.Worldstates.emplace_back(uint32(TB_WS_36_UNKNOWN), int32(0)); + //packet.Worldstates.emplace_back(uint32(TB_WS_37_UNKNOWN), int32(0)); + + // Unused tower icons + packet.Worldstates.emplace_back(uint32(TB_WS_WEST_DAMAGED_NEUTRAL), int32(0)); + packet.Worldstates.emplace_back(uint32(TB_WS_WEST_INTACT_NEUTRAL), int32(0)); + packet.Worldstates.emplace_back(uint32(TB_WS_EAST_DAMAGED_NEUTRAL), int32(0)); + packet.Worldstates.emplace_back(uint32(TB_WS_EAST_INTACT_NEUTRAL), int32(0)); + + // Towers/spires + for (uint8 i = 0; i < TB_TOWERS_COUNT; i++) + { + // Find gameobject + for (ObjectGuid guid : Towers) + { + GameObject* tower = GetGameObject(guid); + if (!tower || tower->GetEntry() != TBTowers[i].entry) + continue; + + TeamId team = GetDefenderTeam(); // 0-false -> alliance; 1-true -> horde + bool intact = tower->GetDestructibleState() == GO_DESTRUCTIBLE_INTACT; + bool damaged = tower->GetDestructibleState() == GO_DESTRUCTIBLE_DAMAGED; + bool destroyed = tower->GetDestructibleState() == GO_DESTRUCTIBLE_DESTROYED; + + packet.Worldstates.emplace_back(uint32(TBTowers[i].wsIntact[TEAM_ALLIANCE]), int32(!team && intact ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBTowers[i].wsDamaged[TEAM_ALLIANCE]), int32(!team && damaged ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBTowers[i].wsDestroyed), int32(destroyed ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBTowers[i].wsDamaged[TEAM_HORDE]), int32(team && damaged ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TBTowers[i].wsIntact[TEAM_HORDE]), int32(team && intact ? 1 : 0)); + } + } + + packet.Worldstates.emplace_back(uint32(TB_WS_TIME_NEXT_BATTLE_SHOW), int32(!IsWarTime() ? 1 : 0)); + + packet.Worldstates.emplace_back(uint32(TB_WS_ALLIANCE_CONTROLS_SHOW), int32(!IsWarTime() && GetDefenderTeam() == TEAM_ALLIANCE ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TB_WS_HORDE_CONTROLS_SHOW), int32(!IsWarTime() && GetDefenderTeam() == TEAM_HORDE ? 1 : 0)); + + packet.Worldstates.emplace_back(uint32(TB_WS_TIME_BATTLE_END), int32(IsWarTime() ? time(NULL) + (m_Timer / 1000) : 0)); + packet.Worldstates.emplace_back(uint32(TB_WS_TIME_NEXT_BATTLE), int32(!IsWarTime() ? time(NULL) + (m_Timer / 1000) : 0)); + + // Not sure if TB + //packet.Worldstates.emplace_back(uint32(TB_WS_65_UNKNOWN), int32(0)); + //packet.Worldstates.emplace_back(uint32(TB_WS_66_UNKNOWN), int32(0)); + + packet.Worldstates.emplace_back(uint32(TB_WS_KEEP_ALLIANCE), int32(GetDefenderTeam() == TEAM_ALLIANCE ? 1 : 0)); + packet.Worldstates.emplace_back(uint32(TB_WS_KEEP_HORDE), int32(GetDefenderTeam() == TEAM_HORDE ? 1 : 0)); +} + +void BattlefieldTB::SendInitWorldStatesTo(Player* player) +{ + WorldPackets::WorldState::InitWorldStates packet; + packet.AreaID = m_ZoneId; + packet.MapID = m_MapId; + packet.SubareaID = 0; + + FillInitialWorldStates(packet); + + player->SendDirectMessage(packet.Write()); +} + +void BattlefieldTB::SendInitWorldStatesToAll() +{ + // Save + sWorld->setWorldState(TB_WS_STATE_BATTLE, uint64(IsWarTime() ? 1 : 0)); + sWorld->setWorldState(TB_WS_ALLIANCE_CONTROLS_SHOW, uint64(!IsWarTime() && GetDefenderTeam() == TEAM_ALLIANCE ? 1 : 0)); + sWorld->setWorldState(TB_WS_HORDE_CONTROLS_SHOW, uint64(!IsWarTime() && GetDefenderTeam() == TEAM_HORDE ? 1 : 0)); + sWorld->setWorldState(TB_WS_ALLIANCE_ATTACKING_SHOW, uint64(IsWarTime() && GetAttackerTeam() == TEAM_ALLIANCE ? 1 : 0)); + sWorld->setWorldState(TB_WS_HORDE_ATTACKING_SHOW, uint64(IsWarTime() && GetAttackerTeam() == TEAM_HORDE ? 1 : 0)); + sWorld->setWorldState(TB_WS_TIME_NEXT_BATTLE, uint64(!IsWarTime() ? m_Timer : 0)); + sWorld->setWorldState(TB_WS_TIME_NEXT_BATTLE_SHOW, uint64(!IsWarTime() ? 1 : 0)); + + // Tol Barad + for (uint8 team = 0; team < 2; team++) + for (GuidSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + if (Player* player = ObjectAccessor::FindPlayer(*itr)) + SendInitWorldStatesTo(player); + + // Tol Barad Peninsula + Map::PlayerList const& players = m_Map->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()->ToPlayer()) + if (player->GetZoneId() == 5389) // ZONE_TOL_BARAD_PENINSULA + player->SendInitWorldStates(5389, player->GetAreaId()); +} + +void BattlefieldTB::OnStartGrouping() +{ + UpdateNPCsAndGameObjects(); + + SendUpdateWorldState(TB_WS_STATE_PREPARATIONS, uint32(1)); + + // Teleport players out of questing area + for (uint8 team = 0; team < BG_TEAMS_COUNT; ++team) + for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) + if (Player* player = ObjectAccessor::FindPlayer(*itr)) + if (player->GetAreaId() == TBQuestAreas[m_iCellblockRandom].entry) + player->CastSpell(player, TBQuestAreas[m_iCellblockRandom].teleportSpell, true); + + // Should we also teleport players out of Baradin Hold underground area? +}; + +void BattlefieldTB::OnBattleStart() +{ + SetData(BATTLEFIELD_TB_DATA_BUILDINGS_CAPTURED, uint32(0)); + SetData(BATTLEFIELD_TB_DATA_TOWERS_INTACT, uint32(3)); + SetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED, uint32(0)); + + UpdateNPCsAndGameObjects(); + + SendInitWorldStatesToAll(); +}; + +void BattlefieldTB::OnBattleEnd(bool endByTimer) +{ + if (!endByTimer) // Attackers win (but now they are defenders already) + SendWarning(GetDefenderTeam() == TEAM_ALLIANCE ? TB_TEXT_FORTRESS_CAPTURE_ALLIANCE : TB_TEXT_FORTRESS_CAPTURE_HORDE); + else // Defenders win + SendWarning(GetDefenderTeam() == TEAM_ALLIANCE ? TB_TEXT_FORTRESS_DEFEND_ALLIANCE : TB_TEXT_FORTRESS_DEFEND_HORDE); + + // UpdateNPCsAndGameObjects() must be called 1 minute after battle ends + m_updateObjectsTimer = 1 * MINUTE * IN_MILLISECONDS; + updatedNPCAndObjects = false; + + // Complete quest + TeamCastSpell(GetDefenderTeam(), GetDefenderTeam() == TEAM_ALLIANCE ? SPELL_VICTORY_ALLIANCE : SPELL_VICTORY_HORDE); + + // Rewards + TeamCastSpell(GetDefenderTeam(), GetDefenderTeam() == TEAM_ALLIANCE ? SPELL_REWARD_VICTORY_ALLIANCE : SPELL_REWARD_VICTORY_HORDE); + for (uint32 i = 0; i < GetData(BATTLEFIELD_TB_DATA_TOWERS_INTACT); i++) // Unsure, for each intact tower or only once for having any tower intact? + TeamCastSpell(GetDefenderTeam(), SPELL_REWARD_TOWER_INTACT); + TeamCastSpell(GetAttackerTeam(), SPELL_REWARD_DEFEAT); + + for (uint8 team = 0; team < 2; ++team) + { + for (GuidSet::const_iterator itr = m_PlayersInWar[team].begin(); itr != m_PlayersInWar[team].end(); ++itr) + if (Player* player = ObjectAccessor::FindPlayer(*itr)) + RemoveAurasFromPlayer(player); + + m_PlayersInWar[team].clear(); + } + + // Reset time warning vars + warnedFiveMinutes = false; + warnedTwoMinutes = false; + warnedOneMinute = false; +}; + +void BattlefieldTB::UpdateNPCsAndGameObjects() +{ + for (ObjectGuid guid : BattleInactiveNPCs) + if (Creature* creature = GetCreature(guid)) + HideNpc(creature); + + for (ObjectGuid guid : BattleInactiveGOs) + if (GameObject* gameobject = GetGameObject(guid)) + gameobject->SetRespawnTime(RESPAWN_ONE_DAY); + + for (ObjectGuid guid : TemporaryNPCs) + if (Creature* creature = GetCreature(guid)) + creature->RemoveFromWorld(); + TemporaryNPCs.clear(); + + for (ObjectGuid guid : TemporaryGOs) + if (GameObject* gameobject = GetGameObject(guid)) + gameobject->Delete(); + TemporaryGOs.clear(); + + // Tol Barad gates - closed during warmup + if (GameObject* gates = GetGameObject(TBGatesGUID)) + gates->SetGoState(GetState() == BATTLEFIELD_WARMUP ? GO_STATE_READY : GO_STATE_ACTIVE); + + // Baradin Hold door - open when inactive + if (GameObject* door = GetGameObject(TBDoorGUID)) + door->SetGoState(GetState() == BATTLEFIELD_INACTIVE ? GO_STATE_ACTIVE : GO_STATE_READY); + + // Decide which cellblock and questgiver will be active. + m_iCellblockRandom = GetState() == BATTLEFIELD_INACTIVE ? urand(0, CELLBLOCK_MAX - 1) : CELLBLOCK_NONE; + + // To The Hole gate + if (GameObject* door = GetGameObject(m_gateToTheHoleGUID)) + door->SetGoState(m_iCellblockRandom == CELLBLOCK_THE_HOLE ? GO_STATE_ACTIVE : GO_STATE_READY); + + // D-Block gate + if (GameObject* door = GetGameObject(m_gateDBlockGUID)) + door->SetGoState(m_iCellblockRandom == CELLBLOCK_D_BLOCK ? GO_STATE_ACTIVE : GO_STATE_READY); + + // Cursed Depths gate + if (GameObject* door = GetGameObject(m_gateCursedDepthsGUID)) + door->SetGoState(m_iCellblockRandom == CELLBLOCK_CURSED_DEPTHS ? GO_STATE_ACTIVE : GO_STATE_READY); + + if (GetState() == BATTLEFIELD_INACTIVE) + { + // Delete capture points + for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr) + itr->second->DelCapturePoint(); + m_capturePoints.clear(); + + // Create capture points + for (uint8 i = 0; i < TB_BASE_COUNT; i++) + { + TolBaradCapturePoint* capturePoint = new TolBaradCapturePoint(this, GetDefenderTeam()); + + //Spawn flag pole + if (GameObject* go = SpawnGameObject(TBCapturePoints[i].entryFlagPole[GetDefenderTeam()], TBCapturePoints[i].x, TBCapturePoints[i].y, TBCapturePoints[i].z, TBCapturePoints[i].o)) + { + go->SetGoArtKit(GetDefenderTeam() == TEAM_ALLIANCE ? TB_GO_ARTKIT_FLAG_ALLIANCE : TB_GO_ARTKIT_FLAG_HORDE); + capturePoint->SetCapturePointData(go); + } + + AddCapturePoint(capturePoint); + } + + for (ObjectGuid guid : BattleInactiveNPCs) + if (Creature* creature = GetCreature(guid)) + ShowNpc(creature, true); + + for (ObjectGuid guid : BattleInactiveGOs) + if (GameObject* gameobject = GetGameObject(guid)) + gameobject->SetRespawnTime(RESPAWN_IMMEDIATELY); + + for (uint8 i = 0; i < TB_QUEST_INFANTRY_MAX; i++) + { + uint32 entry = TB_QUEST_INFANTRY[GetDefenderTeam()][urand(0,3)]; + if (Creature* creature = SpawnCreature(entry, TBQuestInfantrySpawnData[i], GetDefenderTeam())) + TemporaryNPCs.insert(creature->GetGUID()); + } + + for (uint8 i = 0; i < TB_GUARDS_MAX; i++) + if (Creature* creature = SpawnCreature(GetDefenderTeam() == TEAM_ALLIANCE ? NPC_BARADIN_GUARD : NPC_HELLSCREAMS_SENTRY, GuardNPCSpawns[i], GetDefenderTeam())) + TemporaryNPCs.insert(creature->GetGUID()); + + for (uint8 i = 0; i < TB_FACTION_NPC_MAX; i++) + if (Creature* creature = SpawnCreature(GetDefenderTeam() == TEAM_ALLIANCE ? FactionNPCSpawns[i].entryAlliance : FactionNPCSpawns[i].entryHorde, FactionNPCSpawns[i].x, FactionNPCSpawns[i].y, FactionNPCSpawns[i].z, FactionNPCSpawns[i].o, GetDefenderTeam())) + TemporaryNPCs.insert(creature->GetGUID()); + + if (Creature* creature = SpawnCreature(RandomQuestgivers[GetDefenderTeam()][m_iCellblockRandom], RandomQuestgiverPos, GetDefenderTeam())) + TemporaryNPCs.insert(creature->GetGUID()); + + // Spawn portals + for (uint8 i = 0; i < TB_PORTAL_MAX; i++) + if (GameObject* go = SpawnGameObject(TBPortalEntry[GetDefenderTeam()], TBPortals[i].GetPositionX(), TBPortals[i].GetPositionY(), TBPortals[i].GetPositionZ(), TBPortals[i].GetOrientation())) + TemporaryGOs.insert(go->GetGUID()); + + // Update towers + for (ObjectGuid guid : Towers) + if (GameObject* go = GetGameObject(guid)) + go->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING); + } + else if (GetState() == BATTLEFIELD_IN_PROGRESS) + { + for (uint8 i = 0; i < TB_ABANDONED_SIEGE_ENGINE_COUNT; i++) + if (Creature* creature = SpawnCreature(NPC_ABANDONED_SIEGE_ENGINE, TBAbandonedSiegeEngineSpawnData[i], GetDefenderTeam())) + TemporaryNPCs.insert(creature->GetGUID()); + + for (ObjectGuid guid : Towers) + { + if (GameObject* go = GetGameObject(guid)) + { + go->SetDestructibleState(GO_DESTRUCTIBLE_INTACT); + go->ModifyHealth(go->GetGOValue()->Building.MaxHealth); + } + } + } + + // Spawn banners + for (uint8 i = 0; i < TB_BANNER_MAX; i++) + if (GameObject* go = SpawnGameObject(TBBannerEntry[GetDefenderTeam()], TBBanners[i].GetPositionX(), TBBanners[i].GetPositionY(), TBBanners[i].GetPositionZ(), TBBanners[i].GetOrientation())) + TemporaryGOs.insert(go->GetGUID()); + + // Set graveyard controls + for (uint8 i = 0; i < BATTLEFIELD_TB_GRAVEYARD_MAX; i++) + if (BfGraveyard* graveyard = GetGraveyardById(i)) + graveyard->GiveControlTo(!IsWarTime() || TBGraveyards[i].defenderControls ? GetDefenderTeam() : GetAttackerTeam()); +}; + +void BattlefieldTB::OnCreatureCreate(Creature* creature) +{ + switch (creature->GetEntry()) + { + // Store NPCs that need visibility toggling + case NPC_TOLBARAD_CAPTIVE_SPIRIT: + case NPC_TOLBARAD_CELLBLOCK_OOZE: + case NPC_TOLBARAD_ARCHMAGE_GALUS: + case NPC_TOLBARAD_GHASTLY_CONVICT: + case NPC_TOLBARAD_SHIVARRA_DESTROYER: + case NPC_TOLBARAD_CELL_WATCHER: + case NPC_TOLBARAD_SVARNOS: + case NPC_TOLBARAD_JAILED_WRATHGUARD: + case NPC_TOLBARAD_IMPRISONED_IMP: + case NPC_TOLBARAD_WARDEN_SILVA: + case NPC_TOLBARAD_WARDEN_GUARD: + case NPC_TOLBARAD_IMPRISONED_WORKER: + case NPC_TOLBARAD_EXILED_MAGE: + case NPC_CROCOLISK: + case NPC_PROBLIM: + BattleInactiveNPCs.insert(creature->GetGUID()); + if (GetState() == BATTLEFIELD_WARMUP) // If battle is about to start, we must hide these. + HideNpc(creature); + break; + case NPC_ABANDONED_SIEGE_ENGINE: + creature->setFaction(TBFactions[GetDefenderTeam()]); + creature->CastSpell(creature, SPELL_THICK_LAYER_OF_RUST, true); + break; + case NPC_SIEGE_ENGINE_TURRET: + if (Unit* vehiclebase = creature->GetCharmerOrOwner()->GetVehicleBase()) + creature->EnterVehicle(vehiclebase); + break; + case NPC_TOWER_RANGE_FINDER: + creature->CastSpell(creature, SPELL_TOWER_RANGE_FINDER_PERIODIC, true); + break; + case NPC_TB_GY_SPIRIT_BARADIN_HOLD_A: + case NPC_TB_GY_SPIRIT_BARADIN_HOLD_H: + case NPC_TB_GY_SPIRIT_IRONCLAD_GARRISON_A: + case NPC_TB_GY_SPIRIT_WARDENS_VIGIL_A: + case NPC_TB_GY_SPIRIT_EAST_SPIRE_A: + case NPC_TB_GY_SPIRIT_SOUTH_SPIRE_A: + case NPC_TB_GY_SPIRIT_WEST_SPIRE_A: + case NPC_TB_GY_SPIRIT_SLAGWORKS_A: + case NPC_TB_GY_SPIRIT_IRONCLAD_GARRISON_H: + case NPC_TB_GY_SPIRIT_WARDENS_VIGIL_H: + case NPC_TB_GY_SPIRIT_SLAGWORKS_H: + case NPC_TB_GY_SPIRIT_WEST_SPIRE_H: + case NPC_TB_GY_SPIRIT_EAST_SPIRE_H: + case NPC_TB_GY_SPIRIT_SOUTH_SPIRE_H: + creature->CastSpell(creature, SPELL_TB_SPIRITUAL_IMMUNITY, true); + creature->CastSpell(creature, SPELL_TB_SPIRIT_HEAL_CHANNEL, true); + break; + default: + break; + } +}; + +void BattlefieldTB::OnGameObjectCreate(GameObject* go) +{ + switch (go->GetEntry()) + { + case GO_TOLBARAD_GATES: + TBGatesGUID = go->GetGUID(); + go->SetGoState(GetState() == BATTLEFIELD_WARMUP ? GO_STATE_READY : GO_STATE_ACTIVE); + break; + case GO_TOLBARAD_DOOR: + TBDoorGUID = go->GetGUID(); + go->SetGoState(GetState() == BATTLEFIELD_INACTIVE ? GO_STATE_ACTIVE : GO_STATE_READY); + break; + case GO_GATE_TO_THE_HOLE: + m_gateToTheHoleGUID = go->GetGUID(); + go->SetGoState(m_iCellblockRandom == CELLBLOCK_THE_HOLE ? GO_STATE_ACTIVE : GO_STATE_READY); + break; + case GO_GATE_D_BLOCK: + m_gateDBlockGUID = go->GetGUID(); + go->SetGoState(m_iCellblockRandom == CELLBLOCK_D_BLOCK ? GO_STATE_ACTIVE : GO_STATE_READY); + break; + case GO_CURSED_DEPTHS_GATE: + m_gateCursedDepthsGUID = go->GetGUID(); + go->SetGoState(m_iCellblockRandom == CELLBLOCK_CURSED_DEPTHS ? GO_STATE_ACTIVE : GO_STATE_READY); + break; + case GO_CRATE_OF_CELLBLOCK_RATIONS: + case GO_CURSED_SHACKLES: + case GO_DUSTY_PRISON_JOURNAL: + case GO_TB_MEETING_STONE: + case GO_TB_INSTANCE_VISUAL_1: + case GO_TB_INSTANCE_VISUAL_2: + case GO_TB_INSTANCE_VISUAL_3: + case GO_TB_INSTANCE_VISUAL_4: + BattleInactiveGOs.insert(go->GetGUID()); + if (GetState() == BATTLEFIELD_WARMUP) // If battle is about to start, we must hide these. + go->SetRespawnTime(RESPAWN_ONE_DAY); + break; + default: + break; + } +}; + +void BattlefieldTB::ProcessEvent(WorldObject* obj, uint32 eventId) +{ + if (!IsWarTime()) + return; + + if (eventId == EVENT_COUNT_CAPTURED_BASE) + { + UpdateCapturedBaseCount(); + return; + } + + if (!obj) + return; + + GameObject* go = obj->ToGameObject(); + if (!go) + return; + + TBTowerId towerId; + switch (go->GetEntry()) + { + case GO_WEST_SPIRE: + towerId = TB_TOWER_WEST_SPIRE; + break; + case GO_EAST_SPIRE: + towerId = TB_TOWER_EAST_SPIRE; + break; + case GO_SOUTH_SPIRE: + towerId = TB_TOWER_SOUTH_SPIRE; + break; + default: + return; + } + + if (go->GetDestructibleState() == GO_DESTRUCTIBLE_DAMAGED) + TowerDamaged(towerId); + else if (go->GetDestructibleState() == GO_DESTRUCTIBLE_DESTROYED) + TowerDestroyed(towerId); +} + +void BattlefieldTB::TowerDamaged(TBTowerId tbTowerId) +{ + if (!IsWarTime()) + return; + + SendWarning(TBTowers[tbTowerId].textDamaged); + + SetData(BATTLEFIELD_TB_DATA_TOWERS_INTACT, GetData(BATTLEFIELD_TB_DATA_TOWERS_INTACT) - 1); + + SendUpdateWorldState(uint32(TBTowers[tbTowerId].wsIntact[GetDefenderTeam()]), int32(0)); + SendUpdateWorldState(uint32(TBTowers[tbTowerId].wsDamaged[GetDefenderTeam()]), int32(1)); + + TeamCastSpell(GetAttackerTeam(), SPELL_REWARD_TOWER_DAMAGED); +} + +void BattlefieldTB::TowerDestroyed(TBTowerId tbTowerId) +{ + if (!IsWarTime()) + return; + + // Add 5 minute bonus time + m_Timer += m_BonusTime; + + SendUpdateWorldState(TB_WS_TIME_BATTLE_END, uint32(time(NULL) + (m_Timer / 1000))); + + SendWarning(TBTowers[tbTowerId].textDamaged); + + SetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED, GetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED) + 1); + SendUpdateWorldState(uint32(TB_WS_TOWERS_DESTROYED), int32(GetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED))); + + SendUpdateWorldState(uint32(TBTowers[tbTowerId].wsDamaged[GetDefenderTeam()]), int32(0)); + SendUpdateWorldState(uint32(TBTowers[tbTowerId].wsDestroyed), int32(1)); + + // Attack bonus buff + for (GuidSet::const_iterator itr = m_PlayersInWar[GetAttackerTeam()].begin(); itr != m_PlayersInWar[GetAttackerTeam()].end(); ++itr) + if (Player* player = ObjectAccessor::FindPlayer(*itr)) + player->CastCustomSpell(SPELL_TOWER_ATTACK_BONUS, SPELLVALUE_AURA_STACK, GetData(BATTLEFIELD_TB_DATA_TOWERS_DESTROYED), player, TRIGGERED_FULL_MASK); + + // Honor reward + TeamCastSpell(GetAttackerTeam(), SPELL_REWARD_TOWER_DESTROYED); +} + +void BattlefieldTB::UpdateCapturedBaseCount() +{ + uint32 numCapturedBases = 0; // How many bases attacker has captured + + for (BfCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr) + if (itr->second->GetTeamId() == GetAttackerTeam()) + numCapturedBases += 1; + + SetData(BATTLEFIELD_TB_DATA_BUILDINGS_CAPTURED, numCapturedBases); + SendUpdateWorldState(TB_WS_BUILDINGS_CAPTURED, uint32(numCapturedBases)); + + // Check if attackers won + if (numCapturedBases == TB_BASE_COUNT) + EndBattle(false); +} + +// Called when player kill a unit in wg zone +void BattlefieldTB::HandleKill(Player* killer, Unit* victim) +{ + if (killer == victim || victim->GetTypeId() != TYPEID_PLAYER) + return; + + TeamId killerTeam = killer->GetTeamId(); + for (GuidSet::const_iterator itr = m_PlayersInWar[killerTeam].begin(); itr != m_PlayersInWar[killerTeam].end(); ++itr) + if (Player* player = ObjectAccessor::FindPlayer(*itr)) + if (player->GetDistance2d(killer) < 40.0f) + PromotePlayer(player); +} + +void BattlefieldTB::PromotePlayer(Player* killer) +{ + if (!m_isActive || killer->HasAura(SPELL_TB_VETERAN)) + return; + + killer->CastSpell(killer, SPELL_TB_VETERAN, true); +} + +TolBaradCapturePoint::TolBaradCapturePoint(BattlefieldTB* battlefield, TeamId teamInControl) : BfCapturePoint(battlefield) +{ + m_Bf = battlefield; + m_team = teamInControl; + m_value = teamInControl == TEAM_ALLIANCE ? m_maxValue : -m_maxValue; + m_State = teamInControl == TEAM_ALLIANCE ? BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE : BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE; +} + +void TolBaradCapturePoint::ChangeTeam(TeamId /*oldTeam*/) +{ + // Find out index + uint8 iBase = TB_BASE_COUNT; + for (uint8 i = 0; i < TB_BASE_COUNT; i++) + if (GetCapturePointEntry() == TBCapturePoints[i].entryFlagPole[m_Bf->GetDefenderTeam()]) + iBase = i; + + if (iBase == TB_BASE_COUNT) + return; + + // Turn off previous world state icon + switch (m_OldState) + { + case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE: + SendUpdateWorldState(TBCapturePoints[iBase].wsControlled[GetTeamId()], uint32(0)); + break; + case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE: + SendUpdateWorldState(TBCapturePoints[iBase].wsCapturing[TEAM_ALLIANCE], uint32(0)); + break; + case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE: + SendUpdateWorldState(TBCapturePoints[iBase].wsCapturing[TEAM_HORDE], uint32(0)); + break; + default: + break; + } + + // Turn on new world state icon and send warning + switch (m_State) + { + case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE: + case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE: + m_Bf->SendWarning(TBCapturePoints[iBase].textGained[GetTeamId()]); + SendUpdateWorldState(TBCapturePoints[iBase].wsControlled[GetTeamId()], uint32(1)); + GetCapturePointGo()->SetGoArtKit(GetTeamId() == TEAM_ALLIANCE ? TB_GO_ARTKIT_FLAG_ALLIANCE : TB_GO_ARTKIT_FLAG_HORDE); + break; + case BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE: + m_Bf->SendWarning(TBCapturePoints[iBase].textLost[TEAM_HORDE]); + //no break here! + case BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE: + SendUpdateWorldState(TBCapturePoints[iBase].wsCapturing[TEAM_ALLIANCE], uint32(1)); + GetCapturePointGo()->SetGoArtKit(TB_GO_ARTKIT_FLAG_NONE); + break; + case BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE: + m_Bf->SendWarning(TBCapturePoints[iBase].textLost[TEAM_ALLIANCE]); + //no break here! + case BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE: + SendUpdateWorldState(TBCapturePoints[iBase].wsCapturing[TEAM_HORDE], uint32(1)); + GetCapturePointGo()->SetGoArtKit(TB_GO_ARTKIT_FLAG_NONE); + break; + default: + break; + } + + // Update counter + m_Bf->ProcessEvent(NULL, EVENT_COUNT_CAPTURED_BASE); +} diff --git a/src/server/game/Battlefield/Zones/BattlefieldTB.h b/src/server/game/Battlefield/Zones/BattlefieldTB.h new file mode 100644 index 00000000000..06182c78e61 --- /dev/null +++ b/src/server/game/Battlefield/Zones/BattlefieldTB.h @@ -0,0 +1,728 @@ +/* +* Copyright (C) 2008-2016 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 BATTLEFIELD_TB_ +#define BATTLEFIELD_TB_ + +#include "Battlefield.h" + +class BattlefieldTB; +class TolBaradCapturePoint; + +enum TolBaradInfo +{ + BATTLEFIELD_TB_MAPID = 732, // Tol Barad + BATTLEFIELD_TB_ZONEID = 5095, // Tol Barad +}; + +enum TolBaradData +{ + BATTLEFIELD_TB_DATA_BUILDINGS_CAPTURED, + BATTLEFIELD_TB_DATA_TOWERS_INTACT, + BATTLEFIELD_TB_DATA_TOWERS_DESTROYED, + BATTLEFIELD_TB_DATA_MAX, +}; + +enum TolBaradSpells +{ + // Quest completion + SPELL_VICTORY_ALLIANCE = 94665, + SPELL_VICTORY_HORDE = 94763, + + // Rewards + SPELL_REWARD_VICTORY_ALLIANCE = 89789, + SPELL_REWARD_VICTORY_HORDE = 89791, + SPELL_REWARD_DEFEAT = 89793, + + SPELL_REWARD_TOWER_INTACT = 89794, + SPELL_REWARD_TOWER_DAMAGED = 89795, + SPELL_REWARD_TOWER_DESTROYED = 89796, + + // Player buffs + SPELL_TB_SLOW_FALL = 88473, + SPELL_TB_VETERAN = 84655, + SPELL_TOWER_ATTACK_BONUS = 82629, + SPELL_TB_SPIRIT_HEAL_CHANNEL = 22011, // this spell replaces m_LastResurrectTimer in Battlefield.cpp? + SPELL_TB_SPIRITUAL_IMMUNITY = 95332, + + // Vehicle + SPELL_THICK_LAYER_OF_RUST = 95330, + SPELL_RIDE_TOL_BARAD_VEHICLE = 84754, + SPELL_DEPLOY_SIEGE_MODE = 84974, + SPELL_SIEGE_CANNON_AURA = 85167, // aura, periodically triggers spell 85122 +// SPELL_SIEGE_CANNON_EFFECT = 85122, // targets random +// SPELL_SIEGE_CANNON_DAMAGE = 85125, + SPELL_LEAVE_SIEGE_MODE = 85078, + + SPELL_TOWER_RANGE_FINDER_PERIODIC = 85671, + SPELL_TOWER_RANGE_FINDER = 84979, + + // Teleportation spells + SPELL_TB_THE_HOLE_TELEPORT = 89035, + SPELL_TB_D_BLOCK_TELEPORT = 89037, + SPELL_TB_CURSED_DEPTHS_TELEPORT = 89038, +}; + +enum TolBaradNpcs +{ + // Cursed Depths area + NPC_TOLBARAD_CAPTIVE_SPIRIT = 47531, + NPC_TOLBARAD_CELLBLOCK_OOZE = 47534, + NPC_TOLBARAD_ARCHMAGE_GALUS = 47537, + NPC_TOLBARAD_GHASTLY_CONVICT = 47590, + + // D-Block area + NPC_TOLBARAD_SHIVARRA_DESTROYER = 47540, + NPC_TOLBARAD_CELL_WATCHER = 47542, + NPC_TOLBARAD_SVARNOS = 47544, + NPC_TOLBARAD_JAILED_WRATHGUARD = 47548, + NPC_TOLBARAD_IMPRISONED_IMP = 47549, + + // The Hole area + NPC_TOLBARAD_WARDEN_SILVA = 48036, + NPC_TOLBARAD_WARDEN_GUARD = 47561, + NPC_TOLBARAD_IMPRISONED_WORKER = 47550, + NPC_TOLBARAD_EXILED_MAGE = 47552, + + // Other + NPC_CROCOLISK = 47591, + NPC_PROBLIM = 47593, + + // Graveyard spirits + NPC_TB_GY_SPIRIT_BARADIN_HOLD_A = 45066, + NPC_TB_GY_SPIRIT_BARADIN_HOLD_H = 45067, + NPC_TB_GY_SPIRIT_IRONCLAD_GARRISON_A = 45068, + NPC_TB_GY_SPIRIT_WARDENS_VIGIL_A = 45069, + NPC_TB_GY_SPIRIT_EAST_SPIRE_A = 45070, + NPC_TB_GY_SPIRIT_SOUTH_SPIRE_A = 45071, + NPC_TB_GY_SPIRIT_WEST_SPIRE_A = 45072, + NPC_TB_GY_SPIRIT_SLAGWORKS_A = 45073, + NPC_TB_GY_SPIRIT_IRONCLAD_GARRISON_H = 45074, + NPC_TB_GY_SPIRIT_WARDENS_VIGIL_H = 45075, + NPC_TB_GY_SPIRIT_SLAGWORKS_H = 45076, + NPC_TB_GY_SPIRIT_WEST_SPIRE_H = 45077, + NPC_TB_GY_SPIRIT_EAST_SPIRE_H = 45078, + NPC_TB_GY_SPIRIT_SOUTH_SPIRE_H = 45079, + + // Stalker, dummies + NPC_DEBUG_ANNOUNCER = 43679, + NPC_TOWER_RANGE_FINDER = 45492, + NPC_TOWER_CANNON_TARGET = 45561, + NPC_SIEGE_ENGINE_TURRET = 45564, +}; + +enum TolBaradGOs +{ + // Towers + GO_WEST_SPIRE = 204588, + GO_EAST_SPIRE = 204589, + GO_SOUTH_SPIRE = 204590, + + GO_CAPTURE_POINT_NORTH_A_DEFENDING = 205068, + GO_CAPTURE_POINT_NORTH_H_DEFENDING = 205096, + GO_CAPTURE_POINT_EAST_A_DEFENDING = 205138, + GO_CAPTURE_POINT_EAST_H_DEFENDING = 205139, + GO_CAPTURE_POINT_WEST_A_DEFENDING = 205101, + GO_CAPTURE_POINT_WEST_H_DEFENDING = 205103, + + // Entrance gates and instance door + GO_TOLBARAD_GATES = 206598, + GO_TOLBARAD_DOOR = 206576, + + // Other + GO_TB_MEETING_STONE = 206668, + + GO_TB_INSTANCE_VISUAL_1 = 207746, + GO_TB_INSTANCE_VISUAL_2 = 207747, + GO_TB_INSTANCE_VISUAL_3 = 210114, + GO_TB_INSTANCE_VISUAL_4 = 210115, +}; + +enum TolBaradGOArtKit +{ + TB_GO_ARTKIT_FLAG_NONE = 0, + TB_GO_ARTKIT_FLAG_HORDE = 1, + TB_GO_ARTKIT_FLAG_ALLIANCE = 2, +}; + +enum TolBaradWorldStates +{ + TB_WS_ALLIANCE_CONTROLS_SHOW = 5385, + TB_WS_HORDE_CONTROLS_SHOW = 5384, + TB_WS_ALLIANCE_ATTACKING_SHOW = 5546, + TB_WS_HORDE_ATTACKING_SHOW = 5547, + + TB_WS_BUILDINGS_CAPTURED = 5348, + TB_WS_BUILDINGS_CAPTURED_SHOW = 5349, + TB_WS_TOWERS_DESTROYED = 5347, + TB_WS_TOWERS_DESTROYED_SHOW = 5350, + + TB_WS_FACTION_CONTROLLING = 5334, // 1 -> Alliance, 2 -> Horde + + TB_WS_TIME_NEXT_BATTLE = 5332, + TB_WS_TIME_NEXT_BATTLE_SHOW = 5387, + TB_WS_TIME_BATTLE_END = 5333, + TB_WS_TIME_BATTLE_END_SHOW = 5346, + + TB_WS_STATE_PREPARATIONS = 5684, + TB_WS_STATE_BATTLE = 5344, + + /* Not Sure if TB + TB_WS_0_UNKNOWN = 5587, + TB_WS_9_UNKNOWN = 5508, + TB_WS_35_UNKNOWN = 5679, + TB_WS_36_UNKNOWN = 5678, + TB_WS_37_UNKNOWN = 5677, + TB_WS_60_UNKNOWN = 5361, + TB_WS_61_UNKNOWN = 5360, + TB_WS_65_UNKNOWN = 5195, + TB_WS_66_UNKNOWN = 5193, + */ + + TB_WS_PROGRESS_SHOW = 5376, + TB_WS_PROGRESS = 5377, // 0 horde, 100 alliance + TB_WS_PROGRESS_PERCENT_GREY = 5378, + + TB_WS_KEEP_HORDE = 5469, + TB_WS_KEEP_ALLIANCE = 5470, + + TB_WS_GARRISON_HORDE_CONTROLLED = 5418, + TB_WS_GARRISON_HORDE_CAPTURING = 5419, + TB_WS_GARRISON_NEUTRAL = 5420, // unused + TB_WS_GARRISON_ALLIANCE_CAPTURING = 5421, + TB_WS_GARRISON_ALLIANCE_CONTROLLED = 5422, + + TB_WS_VIGIL_HORDE_CONTROLLED = 5423, + TB_WS_VIGIL_HORDE_CAPTURING = 5424, + TB_WS_VIGIL_NEUTRAL = 5425, // unused + TB_WS_VIGIL_ALLIANCE_CAPTURING = 5426, + TB_WS_VIGIL_ALLIANCE_CONTROLLED = 5427, + + TB_WS_SLAGWORKS_HORDE_CONTROLLED = 5428, + TB_WS_SLAGWORKS_HORDE_CAPTURING = 5429, + TB_WS_SLAGWORKS_NEUTRAL = 5430, // unused + TB_WS_SLAGWORKS_ALLIANCE_CAPTURING = 5431, + TB_WS_SLAGWORKS_ALLIANCE_CONTROLLED = 5432, + + TB_WS_WEST_INTACT_HORDE = 5433, + TB_WS_WEST_DAMAGED_HORDE = 5434, + TB_WS_WEST_DESTROYED_NEUTRAL = 5435, + TB_WS_WEST_INTACT_ALLIANCE = 5436, + TB_WS_WEST_DAMAGED_ALLIANCE = 5437, + TB_WS_WEST_INTACT_NEUTRAL = 5453, // unused + TB_WS_WEST_DAMAGED_NEUTRAL = 5454, // unused + + TB_WS_SOUTH_INTACT_HORDE = 5438, + TB_WS_SOUTH_DAMAGED_HORDE = 5439, + TB_WS_SOUTH_DESTROYED_NEUTRAL = 5440, + TB_WS_SOUTH_INTACT_ALLIANCE = 5441, + TB_WS_SOUTH_DAMAGED_ALLIANCE = 5442, + TB_WS_SOUTH_INTACT_NEUTRAL = 5455, // unused + TB_WS_SOUTH_DAMAGED_NEUTRAL = 5456, // unused + + TB_WS_EAST_INTACT_HORDE = 5443, + TB_WS_EAST_DAMAGED_HORDE = 5444, + TB_WS_EAST_DESTROYED_NEUTRAL = 5445, + TB_WS_EAST_INTACT_ALLIANCE = 5446, + TB_WS_EAST_DAMAGED_ALLIANCE = 5447, + TB_WS_EAST_INTACT_NEUTRAL = 5451, + TB_WS_EAST_DAMAGED_NEUTRAL = 5452, +}; + +enum TolBaradText +{ + // DEBUG Announcer + TB_TEXT_EAST_SPIRE_DAMAGED = 1, + TB_TEXT_EAST_SPIRE_DESTROYED = 2, + TB_TEXT_WEST_SPIRE_DAMAGED = 3, + TB_TEXT_WEST_SPIRE_DESTROYED = 4, + TB_TEXT_SOUTH_SPIRE_DAMAGED = 5, + TB_TEXT_SOUTH_SPIRE_DESTROYED = 6, + TB_TEXT_GARRISON_ALLIANCE_GAINED = 7, + TB_TEXT_GARRISON_ALLIANCE_LOST = 8, + TB_TEXT_GARRISON_HORDE_GAINED = 9, + TB_TEXT_GARRISON_HORDE_LOST = 10, + TB_TEXT_VIGIL_ALLIANCE_GAINED = 11, + TB_TEXT_VIGIL_ALLIANCE_LOST = 12, + TB_TEXT_VIGIL_HORDE_GAINED = 13, + TB_TEXT_VIGIL_HORDE_LOST = 14, + TB_TEXT_SLAGWORKS_ALLIANCE_GAINED = 15, + TB_TEXT_SLAGWORKS_ALLIANCE_LOST = 16, + TB_TEXT_SLAGWORKS_HORDE_GAINED = 17, + TB_TEXT_SLAGWORKS_HORDE_LOST = 18, + TB_TEXT_FORTRESS_DEFEND_ALLIANCE = 19, + TB_TEXT_FORTRESS_DEFEND_HORDE = 20, + TB_TEXT_FORTRESS_CAPTURE_ALLIANCE = 21, + TB_TEXT_FORTRESS_CAPTURE_HORDE = 22, + TB_TEXT_VEHICLE_OUTSIDE_WARNING = 23, + TB_TEXT_PREPARATIONS_IN_5_MIN = 24, + TB_TEXT_PREPARATIONS_IN_2_MIN = 25, + TB_TEXT_PREPARATIONS_IN_1_MIN = 26, +}; + +enum TolBaradEvent +{ + EVENT_COUNT_CAPTURED_BASE = 1, +}; + +const uint32 TBFactions[BG_TEAMS_COUNT] = { 1610, 1732 }; + +// Stalker +Position const TolBaradDebugAnnouncerPos = { -1234.25f, 961.903f, 159.4913f, 0.0f }; + +// Quest Infantry NPCs +enum TBQuestInfantryEntry +{ + NPC_ALLIANCE_WARRIOR_INFANTRY = 47599, + NPC_ALLIANCE_PALADIN_INFANTRY = 47600, + NPC_ALLIANCE_HUNTER_INFANTRY = 47595, + NPC_ALLIANCE_MAGE_INFANTRY = 47598, + + NPC_HORDE_DRUID_INFANTRY = 47607, + NPC_HORDE_MAGE_INFANTRY = 47608, + NPC_HORDE_ROGUE_INFANTRY = 47609, + NPC_HORDE_SHAMAN_INFANTRY = 47610, +}; + +uint32 const TB_QUEST_INFANTRY[BG_TEAMS_COUNT][4] = +{ + { NPC_HORDE_DRUID_INFANTRY, NPC_HORDE_MAGE_INFANTRY, NPC_HORDE_ROGUE_INFANTRY, NPC_HORDE_SHAMAN_INFANTRY }, + { NPC_ALLIANCE_WARRIOR_INFANTRY, NPC_ALLIANCE_PALADIN_INFANTRY, NPC_ALLIANCE_HUNTER_INFANTRY, NPC_ALLIANCE_MAGE_INFANTRY }, +}; + +uint8 const TB_QUEST_INFANTRY_MAX = 37; +Position const TBQuestInfantrySpawnData[TB_QUEST_INFANTRY_MAX] = +{ + { -930.4685f, 1020.178f, 121.5658f, 0.1537642f }, + { -831.5157f, 975.816f, 121.5255f, 5.022717f }, + { -837.0773f, 943.9008f, 121.5055f, 5.461119f }, + { -839.1646f, 1024.046f, 121.5505f, 4.782219f }, + { -881.283f, 1033.25f, 121.5243f, 0.0f }, + { -883.038f, 924.955f, 121.5243f, 0.0f }, + { -883.913f, 978.059f, 121.5243f, 3.388291f }, + { -883.6224f, 950.8459f, 121.5122f, 0.8307042f }, + { -895.181f, 1015.2f, 121.5505f, 2.652318f }, + { -943.4023f, 961.7462f, 121.5658f, 5.258394f }, + { -958.649f, 926.877f, 121.5243f, 0.0f }, + { -959.743f, 1029.09f, 121.5243f, 0.0f }, + { -964.6652f, 978.5373f, 121.5257f, 0.02025719f }, + { -1407.14f, 721.42f, 123.5033f, 0.0f }, + { -1414.46f, 671.66f, 123.5043f, 0.0f }, + { -1431.7f, 623.073f, 123.5043f, 0.0f }, + { -1434.162f, 655.8566f, 123.5051f, 4.84886f }, + { -1445.19f, 739.729f, 123.5457f, 5.767949f }, + { -1460.954f, 718.418f, 123.6453f, 5.178094f }, + { -1462.48f, 694.378f, 123.5463f, 0.3441857f }, + { -1372.23f, 683.707f, 123.5043f, 0.0f }, + { -1479.46f, 635.799f, 123.5043f, 0.0f }, + { -1491.259f, 734.5692f, 123.4525f, 1.529741f }, + { -1509.024f, 688.8625f, 123.5463f, 6.243045f }, + { -1419.311f, 1310.25f, 133.8389f, 0.0f }, + { -1444.24f, 1266.439f, 133.8229f, 0.0f }, + { -1450.569f, 1337.351f, 133.914f, 0.0f }, + { -1479.819f, 1331.34f, 153.2f, 0.0f }, + { -1497.62f, 1276.429f, 133.6676f, 3.147845f }, + { -1498.37f, 1379.689f, 133.827f, 0.0f }, + { -1499.97f, 1232.87f, 133.8239f, 0.0f }, + { -1505.7f, 1261.99f, 133.7089f, 0.6167698f }, + { -1531.84f, 1316.569f, 153.2f, 0.0f }, + { -1533.141f, 1267.66f, 133.836f, 0.0f }, + { -1547.59f, 1300.21f, 133.7094f, 1.908187f }, + { -1563.3f, 1325.79f, 133.6673f, 0.0f }, +}; + +// Guard NPCs +enum TBGuardEntry +{ + NPC_BARADIN_GUARD = 51165, + NPC_HELLSCREAMS_SENTRY = 51166, +}; + +uint8 const TB_GUARDS_MAX = 8; +Position const GuardNPCSpawns[TB_GUARDS_MAX] = +{ +// { -837.3768f, 1196.082f, 114.2994f, 3.036873f }, +// { -762.118f, 1195.259f, 107.2007f, 3.036873f }, +// { -837.809f, 1179.842f, 114.1356f, 3.159046f }, +// { -762.5504f, 1179.019f, 107.2137f, 3.159046f }, + { -1272.951f, 964.8854f, 119.5782f, 3.193953f }, + { -1274.394f, 997.6511f, 119.5743f, 3.193953f }, + { -1248.226f, 1018.476f, 119.8113f, 1.605703f }, + { -1218.948f, 943.5695f, 119.5994f, 4.625123f }, + { -1195.417f, 965.5364f, 119.8113f, 0.0f }, + { -1220.832f, 1018.497f, 119.8113f, 1.605703f }, + { -1196.151f, 999.5121f, 119.5966f, 0.0f }, + { -1249.304f, 942.9063f, 119.5782f, 4.625123f }, +}; + +enum TBFactionNPCEntry +{ + // Guards + NPC_BARADIN_GUARD_1 = 47324, + NPC_BARADIN_GUARD_2 = 47325, + NPC_BARADIN_GRUNT_1 = 47335, + NPC_BARADIN_GRUNT_2 = 47336, + + // Questgivers + NPC_SERGEANT_PARKER = 48066, // Everytime + NPC_COMMANDER_STEVENS = 48039, // One of these three + NPC_2ND_LIEUTENANT_WANSWORTH = 48061, + NPC_MARSHAL_FALLOWS = 48074, + + NPC_COMMANDER_ZANOTH = 48069, // Everytime! + NPC_STAFF_SERGEANT_LAZGAR = 48062, // One of these three + NPC_DRILLMASTER_RAZGOTH = 48070, + NPC_PRIVATE_GARNOTH = 48071, + + // Portal summoners + NPC_MAVEN_ZARA = 50173, + NPC_RHAGHA = 50167, +}; + +struct TBFactionNPCInfo +{ + float x; + float y; + float z; + float o; + uint32 entryAlliance; + uint32 entryHorde; +}; + +uint8 const TB_FACTION_NPC_MAX = 4; +TBFactionNPCInfo const FactionNPCSpawns[TB_FACTION_NPC_MAX] = +{ + { -1259.356f, 1057.108f, 107.0786f, 4.956735f, NPC_BARADIN_GUARD_1, NPC_BARADIN_GRUNT_1 }, + { -1254.174f, 1061.094f, 107.0772f, 5.445427f, NPC_BARADIN_GUARD_2, NPC_BARADIN_GRUNT_2 }, + { -1256.365f, 1058.47f, 107.0776f, 2.216568f, NPC_MAVEN_ZARA, NPC_RHAGHA }, + { -1231.38f, 985.681f, 121.2403f, 0.6108652f, NPC_SERGEANT_PARKER, NPC_COMMANDER_ZANOTH }, +}; + +// Questing +enum TBQuesting +{ + CELLBLOCK_THE_HOLE = 0, // The Hole area + CELLBLOCK_D_BLOCK = 1, // D-Block area + CELLBLOCK_CURSED_DEPTHS = 2, // Cursed Depths area + CELLBLOCK_MAX = 3, + CELLBLOCK_NONE, + + AREA_THE_HOLE = 5659, + AREA_D_BLOCK = 5657, + AREA_CURSED_DEPTHS = 5658, + + GO_GATE_TO_THE_HOLE = 206845, + GO_GATE_D_BLOCK = 206844, + GO_CURSED_DEPTHS_GATE = 206843, + + GO_CRATE_OF_CELLBLOCK_RATIONS = 206996, + GO_CURSED_SHACKLES = 206905, + GO_DUSTY_PRISON_JOURNAL = 206890, +}; + +Position const RandomQuestgiverPos = { -1228.93f, 975.038f, 121.7153f, 5.969026f }; + +struct TBQuestAreaInfo +{ + uint32 entry; + uint32 teleportSpell; +}; +TBQuestAreaInfo const TBQuestAreas[CELLBLOCK_MAX] = +{ + { AREA_THE_HOLE, SPELL_TB_THE_HOLE_TELEPORT }, + { AREA_D_BLOCK, SPELL_TB_D_BLOCK_TELEPORT }, + { AREA_CURSED_DEPTHS, SPELL_TB_CURSED_DEPTHS_TELEPORT }, +}; +uint32 const RandomQuestgivers[BG_TEAMS_COUNT][CELLBLOCK_MAX] = +{ + { NPC_MARSHAL_FALLOWS, NPC_2ND_LIEUTENANT_WANSWORTH, NPC_COMMANDER_STEVENS }, + { NPC_DRILLMASTER_RAZGOTH, NPC_STAFF_SERGEANT_LAZGAR, NPC_PRIVATE_GARNOTH }, +}; + +// Capture Points +enum TBCapturePointId +{ + TB_BASE_IRONCLAD_GARRISON = 0, + TB_BASE_WARDENS_VIGIL = 1, + TB_BASE_SLAGWORKS = 2, + TB_BASE_COUNT = 3, +}; + +struct TBCapturePointSpawnData +{ + float x; + float y; + float z; + float o; + TBCapturePointId id; + uint32 entryFlagPole[2]; + uint32 wsControlled[2]; + uint32 wsCapturing[2]; + uint32 wsNeutral; + uint32 textGained[2]; + uint32 textLost[2]; +}; + +TBCapturePointSpawnData const TBCapturePoints[TB_BASE_COUNT] = +{ + { -896.96f, 979.497f, 121.441f, 3.124123f, TB_BASE_IRONCLAD_GARRISON, { GO_CAPTURE_POINT_NORTH_A_DEFENDING, GO_CAPTURE_POINT_NORTH_H_DEFENDING }, { TB_WS_GARRISON_ALLIANCE_CONTROLLED, TB_WS_GARRISON_HORDE_CONTROLLED }, { TB_WS_GARRISON_ALLIANCE_CAPTURING, TB_WS_GARRISON_HORDE_CAPTURING }, TB_WS_GARRISON_NEUTRAL, { TB_TEXT_GARRISON_ALLIANCE_GAINED, TB_TEXT_GARRISON_HORDE_GAINED }, { TB_TEXT_GARRISON_ALLIANCE_LOST, TB_TEXT_GARRISON_HORDE_LOST } }, + { -1492.34f, 1309.87f, 152.961f, 5.462882f, TB_BASE_WARDENS_VIGIL, { GO_CAPTURE_POINT_WEST_A_DEFENDING, GO_CAPTURE_POINT_WEST_H_DEFENDING }, { TB_WS_VIGIL_ALLIANCE_CONTROLLED, TB_WS_VIGIL_HORDE_CONTROLLED }, { TB_WS_VIGIL_ALLIANCE_CAPTURING, TB_WS_VIGIL_HORDE_CAPTURING }, TB_WS_VIGIL_NEUTRAL, { TB_TEXT_VIGIL_ALLIANCE_GAINED, TB_TEXT_VIGIL_HORDE_GAINED }, { TB_TEXT_VIGIL_ALLIANCE_LOST, TB_TEXT_VIGIL_HORDE_LOST } }, + { -1437.f, 685.556f, 123.421f, 0.802851f, TB_BASE_SLAGWORKS, { GO_CAPTURE_POINT_EAST_A_DEFENDING, GO_CAPTURE_POINT_EAST_H_DEFENDING }, { TB_WS_SLAGWORKS_ALLIANCE_CONTROLLED, TB_WS_SLAGWORKS_HORDE_CONTROLLED }, { TB_WS_SLAGWORKS_ALLIANCE_CAPTURING, TB_WS_SLAGWORKS_HORDE_CAPTURING }, TB_WS_SLAGWORKS_NEUTRAL, { TB_TEXT_SLAGWORKS_ALLIANCE_GAINED, TB_TEXT_SLAGWORKS_HORDE_GAINED }, { TB_TEXT_SLAGWORKS_ALLIANCE_LOST, TB_TEXT_SLAGWORKS_HORDE_LOST } }, +}; + +// Towers +enum TBTowerId +{ + TB_TOWER_EAST_SPIRE = 0, + TB_TOWER_SOUTH_SPIRE = 1, + TB_TOWER_WEST_SPIRE = 2, + TB_TOWERS_COUNT = 3, +}; + +struct TBTowerInfo +{ + float x; + float y; + float z; + float o; + uint32 entry; + uint32 textDamaged; + uint32 textDestroyed; + uint32 wsIntact[BG_TEAMS_COUNT]; + uint32 wsDamaged[BG_TEAMS_COUNT]; + uint32 wsDestroyed; +}; + +TBTowerInfo const TBTowers[TB_TOWERS_COUNT] = +{ + { -1013.279f, 529.5382f, 146.427f, 1.97222f, GO_EAST_SPIRE, TB_TEXT_EAST_SPIRE_DAMAGED, TB_TEXT_EAST_SPIRE_DESTROYED, { TB_WS_EAST_INTACT_ALLIANCE, TB_WS_EAST_INTACT_HORDE }, { TB_WS_EAST_DAMAGED_ALLIANCE, TB_WS_EAST_DAMAGED_HORDE }, TB_WS_EAST_DESTROYED_NEUTRAL }, + { -1618.91f, 954.5417f, 168.601f, 0.06981169f, GO_SOUTH_SPIRE, TB_TEXT_SOUTH_SPIRE_DAMAGED, TB_TEXT_SOUTH_SPIRE_DESTROYED, { TB_WS_SOUTH_INTACT_ALLIANCE, TB_WS_SOUTH_INTACT_HORDE }, { TB_WS_SOUTH_DAMAGED_ALLIANCE, TB_WS_SOUTH_DAMAGED_HORDE }, TB_WS_SOUTH_DESTROYED_NEUTRAL }, + { -950.4097f, 1469.101f, 176.596f, 4.180066f, GO_WEST_SPIRE, TB_TEXT_WEST_SPIRE_DAMAGED, TB_TEXT_WEST_SPIRE_DESTROYED, { TB_WS_WEST_INTACT_ALLIANCE, TB_WS_WEST_INTACT_HORDE }, { TB_WS_WEST_DAMAGED_ALLIANCE, TB_WS_WEST_DAMAGED_HORDE }, TB_WS_WEST_DESTROYED_NEUTRAL }, +}; + +// Vehicles +enum TBVehicles +{ + NPC_ABANDONED_SIEGE_ENGINE = 45344, +}; + +int8 const TB_ABANDONED_SIEGE_ENGINE_COUNT = 6; +Position const TBAbandonedSiegeEngineSpawnData[TB_ABANDONED_SIEGE_ENGINE_COUNT] = +{ + { -1106.57f, 1196.34f, 121.8023f, 0.4014257f }, + { -1108.52f, 1111.33f, 121.2783f, 1.37881f }, + { -1213.01f, 782.236f, 121.4473f, 1.675516f }, + { -1258.26f, 780.497f, 122.4413f, 1.48353f }, + { -1438.3f, 1095.24f, 121.1363f, 5.288348f }, + { -1442.3f, 1141.07f, 123.6323f, 4.24115f }, +}; + +// Banners +enum TBFactionBannerEntry +{ + GO_BARADINS_WARDEN_BANNER = 207391, // Alliance banner + GO_HELLSCREAM_REACH_BANNER = 207400, // Horde banner +}; + +uint32 const TBBannerEntry[BG_TEAMS_COUNT] = { GO_BARADINS_WARDEN_BANNER, GO_HELLSCREAM_REACH_BANNER }; + +uint8 const TB_BANNER_MAX = 23; +Position const TBBanners[TB_BANNER_MAX] = +{ + { -987.6129f, 963.9861f, 121.4506f, 2.617989f }, + { -988.118f, 993.0087f, 121.6746f, 3.612838f }, + { -1195.941f, 964.342f, 119.728f, 0.8901166f }, + { -1196.892f, 1000.957f, 119.8211f, 5.445428f }, + { -1198.236f, 1081.898f, 120.2007f, 1.06465f }, + { -1089.337f, 1157.161f, 120.2749f, 3.036838f }, + { -1090.033f, 1143.476f, 120.2656f, 3.036838f }, + { -1217.495f, 944.0261f, 119.4949f, 1.989672f }, + { -1219.226f, 1018.168f, 119.728f, 2.251473f }, + { -1210.319f, 1081.885f, 120.2396f, 2.007128f }, + { -1226.903f, 786.7656f, 119.4592f, 1.553341f }, + { -1228.464f, 979.7379f, 119.3814f, 0.03490625f }, + { -1239.668f, 786.7899f, 119.4271f, 1.553341f }, + { -1250.262f, 1017.887f, 119.728f, 0.8377575f }, + { -1250.693f, 943.4496f, 119.4949f, 5.305802f }, + { -1272.29f, 963.5208f, 119.4949f, 2.617989f }, + { -1273.997f, 998.7934f, 119.4884f, 3.665196f }, + { -1378.363f, 725.0087f, 124.2978f, 1.326448f }, + { -1401.97f, 747.0972f, 123.2302f, 0.2443456f }, + { -1421.953f, 1263.559f, 133.6141f, 5.009095f }, + { -1446.497f, 1238.964f, 133.7601f, 5.969027f }, + { -1488.908f, 1118.747f, 124.9255f, 6.248279f }, + { -1488.533f, 1131.608f, 124.6363f, 6.248279f }, +}; + +// Portals +enum TBPortalEntry +{ + TB_PORTAL_ALLIANCE = 208227, // Portal to Stormwind + TB_PORTAL_HORDE = 208226, // Portal to Orgrimmar +}; + +uint32 const TBPortalEntry[BG_TEAMS_COUNT] = { TB_PORTAL_ALLIANCE, TB_PORTAL_HORDE }; + +uint8 const TB_PORTAL_MAX = 2; +Position const TBPortals[TB_PORTAL_MAX] = +{ + { -598.7656f, 1377.974f, 21.91898f, 0.0f }, + { -1257.729f, 1060.365f, 106.9938f, 5.462882f }, +}; + +/* ################### * + * Tol Barad graveyard * + * ################### */ + +enum TBGraveyardAreaId +{ + // Tol Barad + TB_GY_BARADIN_HOLD = 1789, + TB_GY_IRONCLAD_GARRISON = 1783, + TB_GY_WARDENS_VIGIL = 1785, + TB_GY_SLAGWORKS = 1787, + TB_GY_WEST_SPIRE = 1784, + TB_GY_SOUTH_SPIRE = 1786, + TB_GY_EAST_SPIRE = 1788, + BATTLEFIELD_TB_GRAVEYARD_MAX = 7, + + // Tol Barad Peninsula + TBP_GY_ALLIANCE_DAILY = 1808, + TBP_GY_HORDE_DAILY = 1807, +}; + +struct TBGraveyardInfo +{ + float x; + float y; + float z; + float o; + uint32 phaseId; + uint32 gyid; + uint32 spiritEntry[BG_TEAMS_COUNT]; + bool defenderControls; +}; + +TBGraveyardInfo const TBGraveyards[BATTLEFIELD_TB_GRAVEYARD_MAX] = +{ + { -1247.42f, 981.25f, 155.35f, 6.28f, 128, TB_GY_BARADIN_HOLD, { NPC_TB_GY_SPIRIT_BARADIN_HOLD_A, NPC_TB_GY_SPIRIT_BARADIN_HOLD_H }, true }, + { -974.28f, 1089.47f, 132.99f, 5.90f, 64, TB_GY_IRONCLAD_GARRISON, { NPC_TB_GY_SPIRIT_IRONCLAD_GARRISON_A, NPC_TB_GY_SPIRIT_IRONCLAD_GARRISON_H }, false }, + { -1570.44f, 1167.57f, 159.50f, 2.20f, 64, TB_GY_WARDENS_VIGIL, { NPC_TB_GY_SPIRIT_WARDENS_VIGIL_A, NPC_TB_GY_SPIRIT_WARDENS_VIGIL_H }, false }, + { -1343.32f, 565.24f, 139.04f, 1.66f, 64, TB_GY_SLAGWORKS, { NPC_TB_GY_SPIRIT_SLAGWORKS_A, NPC_TB_GY_SPIRIT_SLAGWORKS_H }, false }, + { -1052.02f, 1494.05f, 191.41f, 4.13f, 64, TB_GY_WEST_SPIRE, { NPC_TB_GY_SPIRIT_WEST_SPIRE_A, NPC_TB_GY_SPIRIT_WEST_SPIRE_H }, false }, + { -1603.34f, 874.29f, 193.69f, 5.27f, 64, TB_GY_SOUTH_SPIRE, { NPC_TB_GY_SPIRIT_SOUTH_SPIRE_A, NPC_TB_GY_SPIRIT_SOUTH_SPIRE_H }, false }, + { -943.66f, 572.36f, 157.54f, 1.74f, 64, TB_GY_EAST_SPIRE, { NPC_TB_GY_SPIRIT_EAST_SPIRE_A, NPC_TB_GY_SPIRIT_EAST_SPIRE_H }, false }, +}; + +/* ####################### * + * Tol Barad capture point * + * ####################### */ + +class TolBaradCapturePoint : public BfCapturePoint +{ + public: + TolBaradCapturePoint(BattlefieldTB* battlefield, TeamId teamInControl); + + void ChangeTeam(TeamId /*oldteam*/) override; +}; + +/* ##################### * + * Tol Barad battlefield * + * ##################### */ + +class TC_GAME_API BattlefieldTB : public Battlefield +{ + public: + ~BattlefieldTB(); + + void OnStartGrouping() override; + void OnBattleStart() override; + void OnBattleEnd(bool endByTimer) override; + + void OnPlayerEnterZone(Player* player) override; + void OnPlayerLeaveZone(Player* player) override; + + void OnPlayerJoinWar(Player* player) override; + void OnPlayerLeaveWar(Player* player) override; + + bool Update(uint32 diff) override; + + void OnCreatureCreate(Creature* creature) override; + //void OnCreatureRemove(Creature* creature) override; + + void OnGameObjectCreate(GameObject* go) override; + + void UpdateCapturedBaseCount(); + //void UpdatedDestroyedTowerCount(TeamId team); + + //void DoCompleteOrIncrementAchievement(uint32 achievement, Player* player, uint8 incrementNumber = 1) override; + + bool SetupBattlefield() override; + + void SendInitWorldStatesTo(Player* player); + void SendInitWorldStatesToAll() override; + void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet) override; + void UpdateWorldStates(); + + void HandleKill(Player* killer, Unit* victim) override; + //void OnUnitDeath(Unit* unit) override; + void PromotePlayer(Player* killer); + void RemoveAurasFromPlayer(Player* player); + + void ProcessEvent(WorldObject* obj, uint32 eventId) override; + + void TowerDamaged(TBTowerId tbTowerId); + void TowerDestroyed(TBTowerId tbTowerId); + + // returns the graveyardId in the specified area. + //uint8 GetSpiritGraveyardId(uint32 areaId) const; + + void UpdateNPCsAndGameObjects(); + void CreateCapturePoints(); + + protected: + // Minutes till battle preparation warnings + bool warnedFiveMinutes; + bool warnedTwoMinutes; + bool warnedOneMinute; + + uint32 m_saveTimer; + + bool updatedNPCAndObjects; + uint32 m_updateObjectsTimer; + + uint32 m_BonusTime; + + GuidSet BattleInactiveNPCs; + GuidSet BattleInactiveGOs; + + GuidSet TemporaryNPCs; + GuidSet TemporaryGOs; + + GuidSet Towers; + + uint8 m_iCellblockRandom; + + ObjectGuid TBGatesGUID; + ObjectGuid TBDoorGUID; + + ObjectGuid m_gateToTheHoleGUID; + ObjectGuid m_gateDBlockGUID; + ObjectGuid m_gateCursedDepthsGUID; +}; + +#endif diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 18c61a4c094..f45f14eb427 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -1268,7 +1268,7 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* std::size_t arrayBlockCount = UpdateMask::GetBlockCount(values.size()); *data << uint16(UpdateMask::EncodeDynamicFieldChangeType(arrayBlockCount, _dynamicChangesMask[index], updateType)); - if (_dynamicChangesMask[index] == UpdateMask::VALUE_AND_SIZE_CHANGED && updateType == UPDATETYPE_VALUES) + if (updateType == UPDATETYPE_VALUES && _dynamicChangesMask[index] == UpdateMask::VALUE_AND_SIZE_CHANGED) *data << uint32(values.size()); std::size_t arrayMaskPos = data->wpos(); @@ -1286,9 +1286,17 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* } else { + uint32 m = 0; + + // work around stupid item modifier field requirements - push back values mask by sizeof(m) bytes if size was not appended yet + if (updateType == UPDATETYPE_VALUES && _dynamicChangesMask[index] != UpdateMask::VALUE_AND_SIZE_CHANGED && _changesMask[ITEM_FIELD_MODIFIERS_MASK]) + { + *data << m; + arrayMaskPos += sizeof(m); + } + // in case of ITEM_DYNAMIC_FIELD_MODIFIERS it is ITEM_FIELD_MODIFIERS_MASK that controls index of each value, not updatemask // so we just have to write this starting from 0 index - uint32 m = 0; for (std::size_t v = 0; v < values.size(); ++v) { if (values[v]) @@ -1298,7 +1306,7 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* } } - if (_changesMask[ITEM_FIELD_MODIFIERS_MASK] && updateType == UPDATETYPE_VALUES) + if (updateType == UPDATETYPE_VALUES && _changesMask[ITEM_FIELD_MODIFIERS_MASK]) data->put(arrayMaskPos - sizeof(m), m); } } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index a78caf15e44..b4cc81dd735 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -110,7 +110,7 @@ namespace UpdateMask inline std::size_t EncodeDynamicFieldChangeType(std::size_t blockCount, DynamicFieldChangeType changeType, uint8 updateType) { - return blockCount | ((changeType & VALUE_AND_SIZE_CHANGED) * (3 - updateType /*this part evaluates to 0 if update type is not VALUES*/)); + return blockCount | ((changeType & VALUE_AND_SIZE_CHANGED) * ((3 - updateType /*this part evaluates to 0 if update type is not VALUES*/) / 3)); } template<typename T> diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e0d958d431f..8c804f0d94e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -24,6 +24,7 @@ #include "Battlefield.h" #include "BattlefieldMgr.h" #include "BattlefieldWG.h" +#include "BattlefieldTB.h" #include "Battleground.h" #include "BattlegroundMgr.h" #include "BattlegroundScore.h" @@ -1113,8 +1114,8 @@ void Player::Update(uint32 p_time) m_swingErrorMsg = 1; } } - //120 degrees of radiant range - else if (!HasInArc(2 * float(M_PI) / 3, victim)) + //120 degrees of radiant range, if player is not in boundary radius + else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim)) { setAttackTimer(BASE_ATTACK, 100); if (m_swingErrorMsg != 2) // send single time (client auto repeat) @@ -1142,8 +1143,10 @@ void Player::Update(uint32 p_time) { if (!IsWithinMeleeRange(victim)) setAttackTimer(OFF_ATTACK, 100); - else if (!HasInArc(2 * float(M_PI) / 3, victim)) - setAttackTimer(OFF_ATTACK, 100); + else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim)) + { + setAttackTimer(BASE_ATTACK, 100); + } else { // prevent base and off attack in same time, delay attack at 0.2 sec @@ -5031,35 +5034,7 @@ float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const void Player::ApplyRatingMod(CombatRating combatRating, int32 value, bool apply) { - float oldRating = m_baseRatingValue[combatRating]; m_baseRatingValue[combatRating] += (apply ? value : -value); - - // explicit affected values - float const multiplier = GetRatingMultiplier(combatRating); - float const oldVal = oldRating * multiplier; - float const newVal = m_baseRatingValue[combatRating] * multiplier; - switch (combatRating) - { - case CR_HASTE_MELEE: - ApplyAttackTimePercentMod(BASE_ATTACK, oldVal, false); - ApplyAttackTimePercentMod(OFF_ATTACK, oldVal, false); - ApplyAttackTimePercentMod(BASE_ATTACK, newVal, true); - ApplyAttackTimePercentMod(OFF_ATTACK, newVal, true); - if (getClass() == CLASS_DEATH_KNIGHT) - UpdateAllRunesRegen(); - break; - case CR_HASTE_RANGED: - ApplyAttackTimePercentMod(RANGED_ATTACK, oldVal, false); - ApplyAttackTimePercentMod(RANGED_ATTACK, newVal, true); - break; - case CR_HASTE_SPELL: - ApplyCastTimePercentMod(oldVal, false); - ApplyCastTimePercentMod(newVal, true); - break; - default: - break; - } - UpdateRating(combatRating); } @@ -5070,10 +5045,18 @@ void Player::UpdateRating(CombatRating cr) // stat used stored in miscValueB for this aura AuraEffectList const& modRatingFromStat = GetAuraEffectsByType(SPELL_AURA_MOD_RATING_FROM_STAT); for (AuraEffectList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i) - if ((*i)->GetMiscValue() & (1<<cr)) + if ((*i)->GetMiscValue() & (1 << cr)) amount += int32(CalculatePct(GetStat(Stats((*i)->GetMiscValueB())), (*i)->GetAmount())); + + AuraEffectList const& modRatingPct = GetAuraEffectsByType(SPELL_AURA_MOD_RATING_PCT); + for (AuraEffectList::const_iterator i = modRatingPct.begin(); i != modRatingPct.end(); ++i) + if ((*i)->GetMiscValue() & (1 << cr)) + amount += int32(CalculatePct(amount, (*i)->GetAmount())); + if (amount < 0) amount = 0; + + uint32 oldRating = GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr); SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount)); bool affectStats = CanModifyStats(); @@ -5123,10 +5106,37 @@ void Player::UpdateRating(CombatRating cr) case CR_RESILIENCE_CRIT_TAKEN: case CR_LIFESTEAL: break; - case CR_HASTE_MELEE: // Implemented in Player::ApplyRatingMod + case CR_HASTE_MELEE: case CR_HASTE_RANGED: case CR_HASTE_SPELL: + { + // explicit affected values + float const multiplier = GetRatingMultiplier(cr); + float const oldVal = oldRating * multiplier; + float const newVal = amount * multiplier; + switch (cr) + { + case CR_HASTE_MELEE: + ApplyAttackTimePercentMod(BASE_ATTACK, oldVal, false); + ApplyAttackTimePercentMod(OFF_ATTACK, oldVal, false); + ApplyAttackTimePercentMod(BASE_ATTACK, newVal, true); + ApplyAttackTimePercentMod(OFF_ATTACK, newVal, true); + if (getClass() == CLASS_DEATH_KNIGHT) + UpdateAllRunesRegen(); + break; + case CR_HASTE_RANGED: + ApplyAttackTimePercentMod(RANGED_ATTACK, oldVal, false); + ApplyAttackTimePercentMod(RANGED_ATTACK, newVal, true); + break; + case CR_HASTE_SPELL: + ApplyCastTimePercentMod(oldVal, false); + ApplyCastTimePercentMod(newVal, true); + break; + default: + break; + } break; + } case CR_AVOIDANCE: case CR_STURDINESS: case CR_UNUSED_7: @@ -9024,6 +9034,22 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) if (bg && bg->GetTypeID(true) == BATTLEGROUND_BFG) bg->FillInitialWorldStates(packet); break; + // Tol Barad Peninsula + case 5389: + if (sWorld->getBoolConfig(CONFIG_TOLBARAD_ENABLE)) + { + packet.Worldstates.emplace_back(5385, sWorld->getWorldState(5385)); // TB_WS_ALLIANCE_CONTROLS_SHOW + packet.Worldstates.emplace_back(5384, sWorld->getWorldState(5384)); // TB_WS_HORDE_CONTROLS_SHOW + packet.Worldstates.emplace_back(5387, sWorld->getWorldState(5387)); // TB_WS_TIME_NEXT_BATTLE_SHOW + packet.Worldstates.emplace_back(5546, sWorld->getWorldState(5546)); // TB_WS_ALLIANCE_ATTACKING_SHOW + packet.Worldstates.emplace_back(5547, sWorld->getWorldState(5547)); // TB_WS_HORDE_ATTACKING_SHOW + } + break; + // Tol Barad + case 5095: + if (bf && bf->GetTypeId() == BATTLEFIELD_TB) + bf->FillInitialWorldStates(packet); + break; // Wintergrasp case 4197: if (bf && bf->GetTypeId() == BATTLEFIELD_WG) @@ -9069,6 +9095,17 @@ void Player::SendBattlefieldWorldStates() const SendUpdateWorldState(ClockWorldState[1], uint32(time(nullptr) + timer)); } } + + if (sWorld->getBoolConfig(CONFIG_TOLBARAD_ENABLE)) + { + if (Battlefield* tb = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_TB)) + { + SendUpdateWorldState(TB_WS_FACTION_CONTROLLING, uint32(tb->GetDefenderTeam() + 1)); + uint32 timer = tb->GetTimer() / 1000; + SendUpdateWorldState(TB_WS_TIME_BATTLE_END, uint32(tb->IsWarTime() ? uint32(time(nullptr) + timer) : 0)); + SendUpdateWorldState(TB_WS_TIME_NEXT_BATTLE, uint32(!tb->IsWarTime() ? uint32(time(nullptr) + timer) : 0)); + } + } } uint32 Player::GetXPRestBonus(uint32 xp) @@ -15113,11 +15150,34 @@ void Player::FailQuest(uint32 questId) // Destroy quest items on quest failure. for (QuestObjective const& obj : quest->GetObjectives()) if (obj.Type == QUEST_OBJECTIVE_ITEM) - DestroyItemCount(obj.ObjectID, obj.Amount, true, true); + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(obj.ObjectID)) + if (itemTemplate->GetBonding() == BIND_QUEST_ITEM || itemTemplate->GetBonding() == BIND_QUEST_ITEM1) + DestroyItemCount(obj.ObjectID, obj.Amount, true, true); + // Destroy items received during the quest. for (uint8 i = 0; i < QUEST_ITEM_DROP_COUNT; ++i) - if (quest->ItemDrop[i] && quest->ItemDropQuantity[i]) - DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true); + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) + if (quest->ItemDropQuantity[i] && (itemTemplate->GetBonding() == BIND_QUEST_ITEM || itemTemplate->GetBonding() == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true); + } +} + +void Player::AbandonQuest(uint32 questId) +{ + if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId)) + { + // Destroy quest items on quest abandon. + for (QuestObjective const& obj : quest->GetObjectives()) + if (obj.Type == QUEST_OBJECTIVE_ITEM) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(obj.ObjectID)) + if (itemTemplate->GetBonding() == BIND_QUEST_ITEM || itemTemplate->GetBonding() == BIND_QUEST_ITEM1) + DestroyItemCount(obj.ObjectID, obj.Amount, true, true); + + // Destroy items received during the quest. + for (uint8 i = 0; i < QUEST_ITEM_DROP_COUNT; ++i) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) + if (quest->ItemDropQuantity[i] && (itemTemplate->GetBonding() == BIND_QUEST_ITEM || itemTemplate->GetBonding() == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true); } } @@ -18739,6 +18799,8 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) // so the value read from the DB may be wrong here but only if the InstanceSave is loaded // and in that case it is not used + uint32 entranceId = fields[6].GetUInt32(); + bool deleteInstance = false; MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); @@ -18786,7 +18848,7 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) } // since non permanent binds are always solo bind, they can always be reset - if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true)) + if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, entranceId, !perm, true)) BindToInstance(save, perm, extendState, true); } while (result->NextRow()); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 99591ea5729..faac57d2c28 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1514,6 +1514,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool CanRewardQuest(Quest const* quest, uint32 reward, bool msg); void AddQuestAndCheckCompletion(Quest const* quest, Object* questGiver); void AddQuest(Quest const* quest, Object* questGiver); + void AbandonQuest(uint32 quest_id); void CompleteQuest(uint32 quest_id); void IncompleteQuest(uint32 quest_id); uint32 GetQuestMoneyReward(Quest const* quest) const; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 1dde054a0a6..96565ac12f2 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -514,6 +514,16 @@ bool Unit::IsWithinMeleeRange(Unit const* obj) const return distsq <= maxdist * maxdist; } +bool Unit::IsWithinBoundaryRadius(const Unit* obj) const +{ + if (!obj || !IsInMap(obj) || !IsInPhase(obj)) + return false; + + float objBoundaryRadius = std::max(obj->GetBoundaryRadius(), MIN_MELEE_REACH); + + return IsInDist(obj, objBoundaryRadius); +} + void Unit::GetRandomContactPoint(const Unit* obj, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const { float combat_reach = GetCombatReach(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index e219d04908f..054720c2218 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1374,8 +1374,10 @@ class TC_GAME_API Unit : public WorldObject bool CanDualWield() const { return m_canDualWield; } virtual void SetCanDualWield(bool value) { m_canDualWield = value; } float GetCombatReach() const { return m_floatValues[UNIT_FIELD_COMBATREACH]; } + float GetBoundaryRadius() const { return m_floatValues[UNIT_FIELD_BOUNDINGRADIUS]; } bool IsWithinCombatRange(const Unit* obj, float dist2compare) const; bool IsWithinMeleeRange(Unit const* obj) const; + bool IsWithinBoundaryRadius(const Unit* obj) const; void GetRandomContactPoint(const Unit* target, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const; uint32 m_extraAttacks; bool m_canDualWield; diff --git a/src/server/game/Groups/GroupMgr.cpp b/src/server/game/Groups/GroupMgr.cpp index ad112c59a61..fc191e79532 100644 --- a/src/server/game/Groups/GroupMgr.cpp +++ b/src/server/game/Groups/GroupMgr.cpp @@ -200,8 +200,8 @@ void GroupMgr::LoadGroups() TC_LOG_INFO("server.loading", "Loading Group instance saves..."); { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 - QueryResult result = CharacterDatabase.Query("SELECT gi.guid, i.map, gi.instance, gi.permanent, i.difficulty, i.resettime, COUNT(g.guid) " + // 0 1 2 3 4 5 6 7 + QueryResult result = CharacterDatabase.Query("SELECT gi.guid, i.map, gi.instance, gi.permanent, i.difficulty, i.resettime, i.entranceId, COUNT(g.guid) " "FROM group_instance gi INNER JOIN instance i ON gi.instance = i.id " "LEFT JOIN character_instance ci LEFT JOIN groups g ON g.leaderGuid = ci.guid ON ci.instance = gi.instance AND ci.permanent = 1 GROUP BY gi.instance ORDER BY gi.guid"); if (!result) @@ -229,7 +229,7 @@ void GroupMgr::LoadGroups() if (!difficultyEntry || difficultyEntry->InstanceType != mapEntry->InstanceType) continue; - InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapEntry->ID, fields[2].GetUInt32(), Difficulty(diff), time_t(fields[5].GetUInt32()), fields[6].GetUInt64() != 0, true); + InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapEntry->ID, fields[2].GetUInt32(), Difficulty(diff), time_t(fields[5].GetUInt32()), fields[6].GetUInt32(), fields[7].GetUInt64() != 0, true); group->BindToInstance(save, fields[3].GetBool(), true); ++count; } diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index afcced346df..ab8bcc8e03a 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -618,7 +618,27 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& pack } if (!teleported) - player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT); + { + WorldSafeLocsEntry const* entranceLocation = nullptr; + InstanceSave* instanceSave = player->GetInstanceSave(at->target_mapId); + if (instanceSave) + { + // Check if we can contact the instancescript of the instance for an updated entrance location + if (Map* map = sMapMgr->FindMap(at->target_mapId, player->GetInstanceSave(at->target_mapId)->GetInstanceId())) + if (InstanceMap* instanceMap = map->ToInstanceMap()) + if (InstanceScript* instanceScript = instanceMap->GetInstanceScript()) + entranceLocation = sWorldSafeLocsStore.LookupEntry(instanceScript->GetEntranceLocation()); + + // Finally check with the instancesave for an entrance location if we did not get a valid one from the instancescript + if (!entranceLocation) + entranceLocation = sWorldSafeLocsStore.LookupEntry(instanceSave->GetEntranceLocation()); + } + + if (entranceLocation) + player->TeleportTo(entranceLocation->MapID, entranceLocation->Loc.X, entranceLocation->Loc.Y, entranceLocation->Loc.Z, entranceLocation->Facing * M_PI / 180, TELE_TO_NOT_LEAVE_TRANSPORT); + else + player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT); + } } void WorldSession::HandleUpdateAccountData(WorldPackets::ClientConfig::UserClientUpdateAccountData& packet) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 6ef966a2340..8430a30e86e 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -435,6 +435,7 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPackets::Quest::QuestLogRemove } _player->TakeQuestSourceItem(questId, true); // remove quest src item from player + _player->AbandonQuest(questId); // remove all quest items player received before abandoning quest. Note, this does not remove normal drop items that happen to be quest requirements. _player->RemoveActiveQuest(questId); _player->RemoveCriteriaTimer(CRITERIA_TIMED_TYPE_QUEST, questId); diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index 3a5c65a0932..4fa2682c7b6 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -71,7 +71,7 @@ void InstanceSaveManager::Unload() - adding instance into manager - called from InstanceMap::Add, _LoadBoundInstances, LoadGroups */ -InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load) +InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, uint32 entranceId, bool canReset, bool load) { if (InstanceSave* old_save = GetInstanceSave(instanceId)) return old_save; @@ -96,6 +96,12 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instance return NULL; } + if (entranceId && !sWorldSafeLocsStore.LookupEntry(entranceId)) + { + TC_LOG_WARN("misc", "InstanceSaveManager::AddInstanceSave: invalid entranceId = %d defined for instance save with mapid = %d, instanceid = %d!", entranceId, mapId, instanceId); + entranceId = 0; + } + if (!resetTime) { // initialize reset time @@ -112,7 +118,7 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instance TC_LOG_DEBUG("maps", "InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId, instanceId); - InstanceSave* save = new InstanceSave(mapId, instanceId, difficulty, resetTime, canReset); + InstanceSave* save = new InstanceSave(mapId, instanceId, difficulty, entranceId, resetTime, canReset); if (!load) save->SaveToDB(); @@ -173,9 +179,9 @@ void InstanceSaveManager::UnloadInstanceSave(uint32 InstanceId) save->UnloadIfEmpty(); } -InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset) +InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, uint32 entranceId, time_t resetTime, bool canReset) : m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId), - m_difficulty(difficulty), m_canReset(canReset), m_toDelete(false) { } + m_difficulty(difficulty), m_entranceId(entranceId), m_canReset(canReset), m_toDelete(false) { } InstanceSave::~InstanceSave() { @@ -200,6 +206,7 @@ void InstanceSave::SaveToDB() { data = instanceScript->GetSaveData(); completedEncounters = instanceScript->GetCompletedEncounterMask(); + m_entranceId = instanceScript->GetEntranceLocation(); } } @@ -210,6 +217,7 @@ void InstanceSave::SaveToDB() stmt->setUInt8(3, uint8(GetDifficultyID())); stmt->setUInt32(4, completedEncounters); stmt->setString(5, data); + stmt->setUInt32(6, m_entranceId); CharacterDatabase.Execute(stmt); } diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h index ff99736f2b7..6e0d34a88e6 100644 --- a/src/server/game/Instances/InstanceSaveMgr.h +++ b/src/server/game/Instances/InstanceSaveMgr.h @@ -49,7 +49,7 @@ class TC_GAME_API InstanceSave - any new instance is being generated - the first time a player bound to InstanceId logs in - when a group bound to the instance is loaded */ - InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset); + InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, uint32 entranceId, time_t resetTime, bool canReset); /* Unloaded when m_playerList and m_groupList become empty or when the instance is reset */ @@ -75,6 +75,9 @@ class TC_GAME_API InstanceSave void SetResetTime(time_t resetTime) { m_resetTime = resetTime; } time_t GetResetTimeForDB(); + uint32 GetEntranceLocation() const { return m_entranceId; } + void SetEntranceLocation(uint32 entranceId) { m_entranceId = entranceId; } + InstanceTemplate const* GetTemplate(); MapEntry const* GetMapEntry(); @@ -139,6 +142,7 @@ class TC_GAME_API InstanceSave uint32 m_instanceid; uint32 m_mapid; Difficulty m_difficulty; + uint32 m_entranceId; bool m_canReset; bool m_toDelete; @@ -211,7 +215,7 @@ class TC_GAME_API InstanceSaveManager void Update(); - InstanceSave* AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, + InstanceSave* AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, uint32 entranceId, bool canReset, bool load = false); void RemoveInstanceSave(uint32 InstanceId); void UnloadInstanceSave(uint32 InstanceId); diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index d8c99aa366d..9854e46f1cb 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -40,7 +40,7 @@ BossBoundaryData::~BossBoundaryData() } InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0), -_combatResurrectionTimer(0), _combatResurrectionCharges(0), _combatResurrectionTimerStarted(false) +_entranceId(0), _temporaryEntranceId(0), _combatResurrectionTimer(0), _combatResurrectionCharges(0), _combatResurrectionTimerStarted(false) { #ifdef TRINITY_API_USE_DYNAMIC_LINKING uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId; @@ -61,7 +61,8 @@ void InstanceScript::SaveToDB() PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_DATA); stmt->setUInt32(0, GetCompletedEncounterMask()); stmt->setString(1, data); - stmt->setUInt32(2, instance->GetInstanceId()); + stmt->setUInt32(2, _entranceId); + stmt->setUInt32(3, instance->GetInstanceId()); CharacterDatabase.Execute(stmt); } @@ -606,6 +607,13 @@ bool InstanceScript::CheckAchievementCriteriaMeet(uint32 criteria_id, Player con return false; } +void InstanceScript::SetEntranceLocation(uint32 worldSafeLocationId) +{ + _entranceId = worldSafeLocationId; + if (_temporaryEntranceId) + _temporaryEntranceId = 0; +} + void InstanceScript::SendEncounterUnit(uint32 type, Unit* unit /*= NULL*/, uint8 priority) { switch (type) diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index d858bb6b9c2..eb0579ef63e 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -246,6 +246,15 @@ class TC_GAME_API InstanceScript : public ZoneScript // Returns completed encounters mask for packets uint32 GetCompletedEncounterMask() const { return completedEncounters; } + // Sets the entrance location (WorldSafeLoc) id + void SetEntranceLocation(uint32 worldSafeLocationId); + + // Sets a temporary entrance that does not get saved to db + void SetTemporaryEntranceLocation(uint32 worldSafeLocationId) { _temporaryEntranceId = worldSafeLocationId; } + + // Get's the current entrance id + uint32 GetEntranceLocation() const { return _temporaryEntranceId ? _temporaryEntranceId : _entranceId; } + void SendEncounterUnit(uint32 type, Unit* unit = NULL, uint8 priority = 0); void SendEncounterStart(uint32 inCombatResCount = 0, uint32 maxInCombatResCount = 0, uint32 inCombatResChargeRecovery = 0, uint32 nextCombatResChargeTime = 0); void SendEncounterEnd(); @@ -309,6 +318,8 @@ class TC_GAME_API InstanceScript : public ZoneScript ObjectInfoMap _gameObjectInfo; ObjectGuidMap _objectGuids; uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets + uint32 _entranceId; + uint32 _temporaryEntranceId; uint32 _combatResurrectionTimer; uint8 _combatResurrectionCharges; // the counter for available battle resurrections bool _combatResurrectionTimerStarted; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 0b0868eddf4..0a7138616e0 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3135,7 +3135,7 @@ bool InstanceMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/) if (!mapSave) { TC_LOG_DEBUG("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); - mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), Difficulty(GetSpawnMode()), 0, true); + mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), Difficulty(GetSpawnMode()), 0, 0, true); } ASSERT(mapSave); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 14a4ea3590e..de13f63e099 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -986,7 +986,7 @@ void ScriptMgr::Initialize() FillSpellSummary(); // Load core scripts - SetScriptContext("___static___"); + SetScriptContext(GetNameOfStaticContext()); // SmartAI AddSC_SmartScripts(); @@ -1041,6 +1041,12 @@ void ScriptMgr::SwapScriptContext(bool initialize) _currentContext.clear(); } +std::string const& ScriptMgr::GetNameOfStaticContext() +{ + static std::string const name = "___static___"; + return name; +} + void ScriptMgr::ReleaseScriptContext(std::string const& context) { sScriptRegistryCompositum->ReleaseContext(context); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 9acfc28db9c..b3436c4773e 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -876,6 +876,9 @@ class TC_GAME_API ScriptMgr /// calls for better performance (bulk changes). void SwapScriptContext(bool initialize = false); + /// Returns the context name of the static context provided by the worldserver + static std::string const& GetNameOfStaticContext(); + /// Acquires a strong module reference to the module containing the given script name, /// which prevents the shared library which contains the script from unloading. /// The shared library is lazy unloaded as soon as all references to it are released. diff --git a/src/server/game/Scripting/ScriptReloadMgr.cpp b/src/server/game/Scripting/ScriptReloadMgr.cpp index d13fa9c30f0..b0c9b6821d2 100644 --- a/src/server/game/Scripting/ScriptReloadMgr.cpp +++ b/src/server/game/Scripting/ScriptReloadMgr.cpp @@ -194,6 +194,8 @@ public: static Optional<std::shared_ptr<ScriptModule>> CreateFromPath(fs::path const& path, Optional<fs::path> cache_path); + static void ScheduleDelayedDelete(ScriptModule* module); + char const* GetScriptModuleRevisionHash() const override { return _getScriptModuleRevisionHash(); @@ -287,8 +289,13 @@ Optional<std::shared_ptr<ScriptModule>> GetFunctionFromSharedLibrary(handle, "AddScripts", addScripts) && GetFunctionFromSharedLibrary(handle, "GetScriptModule", getScriptModule) && GetFunctionFromSharedLibrary(handle, "GetBuildDirective", getBuildDirective)) - return std::make_shared<ScriptModule>(std::move(holder), getScriptModuleRevisionHash, + { + auto module = new ScriptModule(std::move(holder), getScriptModuleRevisionHash, addScripts, getScriptModule, getBuildDirective, path); + + // Unload the module at the next update tick as soon as all references are removed + return std::shared_ptr<ScriptModule>(module, ScheduleDelayedDelete); + } else { TC_LOG_ERROR("scripts.hotswap", "Could not extract all required functions from the shared library \"%s\"!", @@ -937,13 +944,6 @@ private: } } - sScriptMgr->SetScriptContext(module_name); - (*module)->AddScripts(); - TC_LOG_TRACE("scripts.hotswap", ">> Registered all scripts of module %s.", module_name.c_str()); - - if (swap_context) - sScriptMgr->SwapScriptContext(); - // Create the source listener auto listener = Trinity::make_unique<SourceUpdateListener>( sScriptReloadMgr->GetSourceDirectory() / module_name, @@ -952,8 +952,16 @@ private: // Store the module _known_modules_build_directives.insert(std::make_pair(module_name, (*module)->GetBuildDirective())); _running_script_modules.insert(std::make_pair(module_name, - std::make_pair(std::move(*module), std::move(listener)))); + std::make_pair(*module, std::move(listener)))); _running_script_module_names.insert(std::make_pair(path, module_name)); + + // Process the script loading after the module was registered correctly (#17557). + sScriptMgr->SetScriptContext(module_name); + (*module)->AddScripts(); + TC_LOG_TRACE("scripts.hotswap", ">> Registered all scripts of module %s.", module_name.c_str()); + + if (swap_context) + sScriptMgr->SwapScriptContext(); } void ProcessReloadScriptModule(fs::path const& path) @@ -1435,6 +1443,26 @@ private: fs::path temporary_cache_path_; }; +class ScriptModuleDeleteMessage +{ +public: + explicit ScriptModuleDeleteMessage(ScriptModule* module) + : module_(module) { } + + void operator() (HotSwapScriptReloadMgr*) + { + module_.reset(); + } + +private: + std::unique_ptr<ScriptModule> module_; +}; + +void ScriptModule::ScheduleDelayedDelete(ScriptModule* module) +{ + sScriptReloadMgr->QueueMessage(ScriptModuleDeleteMessage(module)); +} + /// Maps efsw actions to strings static char const* ActionToString(efsw::Action action) { @@ -1592,11 +1620,15 @@ void SourceUpdateListener::handleFileAction(efsw::WatchID watchid, std::string c std::shared_ptr<ModuleReference> ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& context) { - auto const itr = sScriptReloadMgr->_running_script_modules.find(context); - if (itr != sScriptReloadMgr->_running_script_modules.end()) - return itr->second.first; - else + // Return empty references for the static context exported by the worldserver + if (context == ScriptMgr::GetNameOfStaticContext()) return { }; + + auto const itr = sScriptReloadMgr->_running_script_modules.find(context); + ASSERT(itr != sScriptReloadMgr->_running_script_modules.end() + && "Requested a reference to a non existent script context!"); + + return itr->second.first; } // Returns the full hot swap implemented ScriptReloadMgr diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index da8b43f37dc..fdd72e4ab6c 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -464,7 +464,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleModPowerDisplay, //402 SPELL_AURA_MOD_POWER_DISPLAY &AuraEffect::HandleNoImmediateEffect, //403 SPELL_AURA_OVERRIDE_SPELL_VISUAL implemented in Unit::GetCastSpellXSpellVisualId &AuraEffect::HandleOverrideAttackPowerBySpellPower, //404 SPELL_AURA_OVERRIDE_ATTACK_POWER_BY_SP_PCT - &AuraEffect::HandleNULL, //405 SPELL_AURA_MOD_RATING_PCT + &AuraEffect::HandleModRatingPct, //405 SPELL_AURA_MOD_RATING_PCT &AuraEffect::HandleNULL, //406 &AuraEffect::HandleNULL, //407 SPELL_AURA_MOD_FEAR_2 &AuraEffect::HandleNULL, //408 @@ -4471,7 +4471,7 @@ void AuraEffect::HandleModRating(AuraApplication const* aurApp, uint8 mode, bool target->ToPlayer()->ApplyRatingMod(CombatRating(rating), GetAmount(), apply); } -void AuraEffect::HandleModRatingFromStat(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModRatingFromStat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; @@ -4484,7 +4484,23 @@ void AuraEffect::HandleModRatingFromStat(AuraApplication const* aurApp, uint8 mo // Just recalculate ratings for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) if (GetMiscValue() & (1 << rating)) - target->ToPlayer()->ApplyRatingMod(CombatRating(rating), 0, apply); + target->ToPlayer()->UpdateRating(CombatRating(rating)); +} + +void AuraEffect::HandleModRatingPct(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const +{ + if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) + return; + + Unit* target = aurApp->GetTarget(); + + if (target->GetTypeId() != TYPEID_PLAYER) + return; + + // Just recalculate ratings + for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + if (GetMiscValue() & (1 << rating)) + target->ToPlayer()->UpdateRating(CombatRating(rating)); } /********************************/ diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index b599fe1df28..5ecf741d967 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -270,6 +270,7 @@ class TC_GAME_API AuraEffect // combat rating void HandleModRating(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleModRatingFromStat(AuraApplication const* aurApp, uint8 mode, bool apply) const; + void HandleModRatingPct(AuraApplication const* aurApp, uint8 mode, bool apply) const; // attack power void HandleAuraModAttackPower(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraModRangedAttackPower(AuraApplication const* aurApp, uint8 mode, bool apply) const; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index e613952e015..6f5e3d4a1af 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5905,7 +5905,8 @@ SpellCastResult Spell::CheckRange(bool strict) return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; if (m_caster->GetTypeId() == TYPEID_PLAYER && - (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target)) + (((m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target)) + && !m_caster->IsWithinBoundaryRadius(target))) return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_UNIT_NOT_INFRONT : SPELL_FAILED_DONT_REPORT; } @@ -7563,8 +7564,9 @@ bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) } else { - if (!_caster->isInFront(target, _coneAngle)) - return false; + if (!_caster->IsWithinBoundaryRadius(target->ToUnit())) + if (!_caster->isInFront(target, _coneAngle)) + return false; } return WorldObjectSpellAreaTargetCheck::operator ()(target); } diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 0ab8b919664..13d03c4ee57 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3164,6 +3164,10 @@ void SpellMgr::LoadSpellInfoCorrections() //! HACK: This spell break quest complete for alliance and on retail not used °_O const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->Effect = 0; break; + case 85123: // Siege Cannon (Tol Barad) + const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_200_YARDS); + const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->TargetA = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ENTRY); + break; // VIOLET HOLD SPELLS // case 54258: // Water Globule (Ichoron) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index bf536765901..e2c80f16ca8 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1414,6 +1414,16 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_WINTERGRASP_NOBATTLETIME] = sConfigMgr->GetIntDefault("Wintergrasp.NoBattleTimer", 150); m_int_configs[CONFIG_WINTERGRASP_RESTART_AFTER_CRASH] = sConfigMgr->GetIntDefault("Wintergrasp.CrashRestartTimer", 10); + // Tol Barad battlefield + m_bool_configs[CONFIG_TOLBARAD_ENABLE] = sConfigMgr->GetBoolDefault("TolBarad.Enable", true); + m_int_configs[CONFIG_TOLBARAD_PLR_MAX] = sConfigMgr->GetIntDefault("TolBarad.PlayerMax", 100); + m_int_configs[CONFIG_TOLBARAD_PLR_MIN] = sConfigMgr->GetIntDefault("TolBarad.PlayerMin", 0); + m_int_configs[CONFIG_TOLBARAD_PLR_MIN_LVL] = sConfigMgr->GetIntDefault("TolBarad.PlayerMinLvl", 85); + m_int_configs[CONFIG_TOLBARAD_BATTLETIME] = sConfigMgr->GetIntDefault("TolBarad.BattleTimer", 15); + m_int_configs[CONFIG_TOLBARAD_BONUSTIME] = sConfigMgr->GetIntDefault("TolBarad.BonusTime", 5); + m_int_configs[CONFIG_TOLBARAD_NOBATTLETIME] = sConfigMgr->GetIntDefault("TolBarad.NoBattleTimer", 150); + m_int_configs[CONFIG_TOLBARAD_RESTART_AFTER_CRASH] = sConfigMgr->GetIntDefault("TolBarad.CrashRestartTimer", 10); + // Stats limits m_bool_configs[CONFIG_STATS_LIMITS_ENABLE] = sConfigMgr->GetBoolDefault("Stats.Limits.Enable", false); m_float_configs[CONFIG_STATS_LIMITS_DODGE] = sConfigMgr->GetFloatDefault("Stats.Limits.Dodge", 95.0f); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index e37e0daa011..1851ffa6079 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -167,6 +167,7 @@ enum WorldBoolConfigs CONFIG_WARDEN_ENABLED, CONFIG_ENABLE_MMAPS, CONFIG_WINTERGRASP_ENABLE, + CONFIG_TOLBARAD_ENABLE, CONFIG_UI_QUESTLEVELS_IN_DIALOGS, // Should we add quest levels to the title in the NPC dialogs? CONFIG_EVENT_ANNOUNCE, CONFIG_STATS_LIMITS_ENABLE, @@ -363,6 +364,13 @@ enum WorldIntConfigs CONFIG_WINTERGRASP_BATTLETIME, CONFIG_WINTERGRASP_NOBATTLETIME, CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, + CONFIG_TOLBARAD_PLR_MAX, + CONFIG_TOLBARAD_PLR_MIN, + CONFIG_TOLBARAD_PLR_MIN_LVL, + CONFIG_TOLBARAD_BATTLETIME, + CONFIG_TOLBARAD_BONUSTIME, + CONFIG_TOLBARAD_NOBATTLETIME, + CONFIG_TOLBARAD_RESTART_AFTER_CRASH, CONFIG_GUILD_SAVE_INTERVAL, CONFIG_GUILD_UNDELETABLE_LEVEL, CONFIG_PACKET_SPOOF_POLICY, diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 6f27d733eb8..26385221d65 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -726,7 +726,7 @@ public: static bool HandleCooldownCommand(ChatHandler* handler, char const* args) { - Player* target = handler->getSelectedPlayerOrSelf(); + Unit* target = handler->getSelectedUnit(); if (!target) { handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); @@ -734,7 +734,14 @@ public: return false; } - std::string nameLink = handler->GetNameLink(target); + Player* owner = target->GetCharmerOrOwnerPlayerOrPlayerItself(); + if (!owner) + { + owner = handler->GetSession()->GetPlayer(); + target = owner; + } + + std::string nameLink = handler->GetNameLink(owner); if (!*args) { @@ -752,14 +759,14 @@ public: SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellIid); if (!spellInfo) { - handler->PSendSysMessage(LANG_UNKNOWN_SPELL, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); + handler->PSendSysMessage(LANG_UNKNOWN_SPELL, owner == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); handler->SetSentErrorMessage(true); return false; } target->GetSpellHistory()->ResetCooldown(spellIid, true); target->GetSpellHistory()->ResetCharges(spellInfo->ChargeCategoryId); - handler->PSendSysMessage(LANG_REMOVE_COOLDOWN, spellIid, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); + handler->PSendSysMessage(LANG_REMOVE_COOLDOWN, spellIid, owner == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); } return true; } diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index adf7219d2a3..123d4102794 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -193,6 +193,7 @@ void AddSC_stormwind_city(); void AddSC_stranglethorn_vale(); void AddSC_swamp_of_sorrows(); void AddSC_tirisfal_glades(); +void AddSC_tol_barad(); void AddSC_undercity(); void AddSC_western_plaguelands(); void AddSC_wetlands(); @@ -379,6 +380,7 @@ void AddEasternKingdomsScripts() AddSC_stranglethorn_vale(); AddSC_swamp_of_sorrows(); AddSC_tirisfal_glades(); + AddSC_tol_barad(); AddSC_undercity(); AddSC_western_plaguelands(); AddSC_wetlands(); diff --git a/src/server/scripts/EasternKingdoms/zone_tol_barad.cpp b/src/server/scripts/EasternKingdoms/zone_tol_barad.cpp new file mode 100644 index 00000000000..54534a3d301 --- /dev/null +++ b/src/server/scripts/EasternKingdoms/zone_tol_barad.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008-2016 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 "BattlefieldMgr.h" +#include "BattlefieldTB.h" +#include "Battlefield.h" +#include "ScriptSystem.h" +#include "WorldSession.h" +#include "ScriptedCreature.h" +#include "ScriptedGossip.h" +#include "SpellScript.h" +#include "Player.h" + +enum TBSpiritGuide +{ + SPELL_CHANNEL_SPIRIT_HEAL = 22011, + + GOSSIP_OPTION_ID_SLAGWORKS = 0, + GOSSIP_OPTION_ID_IRONCLAD_GARRISON = 1, + GOSSIP_OPTION_ID_WARDENS_VIGIL = 2, + GOSSIP_OPTION_ID_EAST_SPIRE = 3, + GOSSIP_OPTION_ID_WEST_SPIRE = 4, + GOSSIP_OPTION_ID_SOUTH_SPIRE = 5, +}; + +class npc_tb_spirit_guide : public CreatureScript +{ + public: + npc_tb_spirit_guide() : CreatureScript("npc_tb_spirit_guide") { } + + struct npc_tb_spirit_guideAI : public ScriptedAI + { + npc_tb_spirit_guideAI(Creature* creature) : ScriptedAI(creature) { } + + void UpdateAI(uint32 /*diff*/) override + { + if (!me->HasUnitState(UNIT_STATE_CASTING)) + DoCast(me, SPELL_CHANNEL_SPIRIT_HEAL); + } + + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + player->PlayerTalkClass->SendCloseGossip(); + + uint32 areaId = 0; + switch (gossipListId) + { + case GOSSIP_OPTION_ID_SLAGWORKS: + areaId = TB_GY_SLAGWORKS; + break; + case GOSSIP_OPTION_ID_IRONCLAD_GARRISON: + areaId = TB_GY_IRONCLAD_GARRISON; + break; + case GOSSIP_OPTION_ID_WARDENS_VIGIL: + areaId = TB_GY_WARDENS_VIGIL; + break; + case GOSSIP_OPTION_ID_EAST_SPIRE: + areaId = TB_GY_EAST_SPIRE; + break; + case GOSSIP_OPTION_ID_WEST_SPIRE: + areaId = TB_GY_WEST_SPIRE; + break; + case GOSSIP_OPTION_ID_SOUTH_SPIRE: + areaId = TB_GY_SOUTH_SPIRE; + break; + default: + return; + } + + if (WorldSafeLocsEntry const* safeLoc = sWorldSafeLocsStore.LookupEntry(areaId)) + player->TeleportTo(safeLoc->MapID, safeLoc->Loc.X, safeLoc->Loc.Y, safeLoc->Loc.Z, 0); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_tb_spirit_guideAI(creature); + } +}; + +// 85123 - Siege Cannon - selects random target +class spell_siege_cannon : public SpellScriptLoader +{ +public: + spell_siege_cannon() : SpellScriptLoader("spell_siege_cannon") { } + + class spell_siege_cannon_SpellScript : public SpellScript + { + PrepareSpellScript(spell_siege_cannon_SpellScript); + + void SelectRandomTarget(std::list<WorldObject*>& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_siege_cannon_SpellScript::SelectRandomTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_siege_cannon_SpellScript(); + } +}; + +void AddSC_tol_barad() +{ + new npc_tb_spirit_guide(); + new spell_siege_cannon(); +} diff --git a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp index 2b670b467e2..712b3597f1b 100644 --- a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp +++ b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp @@ -238,10 +238,40 @@ public: } }; +// 151159 - Darkness Calls +class spell_subjugator_korul_darkness_calls : public SpellScriptLoader +{ +public: + spell_subjugator_korul_darkness_calls() : SpellScriptLoader("spell_subjugator_korul_darkness_calls") { } + + class spell_subjugator_korul_darkness_calls_SpellScript : public SpellScript + { + PrepareSpellScript(spell_subjugator_korul_darkness_calls_SpellScript); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* hitUnit = GetHitUnit()) + GetCaster()->CastSpell(hitUnit, uint32(GetEffectValue()), true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_subjugator_korul_darkness_calls_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + OnEffectHitTarget += SpellEffectFn(spell_subjugator_korul_darkness_calls_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_subjugator_korul_darkness_calls_SpellScript(); + } +}; + void AddSC_blackfathom_deeps() { new go_blackfathom_altar(); new go_blackfathom_fire(); new npc_blackfathom_deeps_event(); new npc_morridune(); + new spell_subjugator_korul_darkness_calls(); } diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index 352421a7188..a3ef98d00a1 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -113,6 +113,105 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader } }; +enum HallowEndCandysSpells +{ + SPELL_HALLOWS_END_CANDY_ORANGE_GIANT = 24924, // Effect 1: Apply Aura: Mod Size, Value: 30% + SPELL_HALLOWS_END_CANDY_SKELETON = 24925, // Effect 1: Apply Aura: Change Model (Skeleton). Effect 2: Apply Aura: Underwater Breathing + SPELL_HALLOWS_END_CANDY_PIRATE = 24926, // Effect 1: Apply Aura: Increase Swim Speed, Value: 50% + SPELL_HALLOWS_END_CANDY_GHOST = 24927, // Effect 1: Apply Aura: Levitate / Hover. Effect 2: Apply Aura: Slow Fall, Effect 3: Apply Aura: Water Walking + SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE = 44742, // Effect 1: Apply Aura: Change Model (Defias Pirate, Female). Effect 2: Increase Swim Speed, Value: 50% + SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE = 44743 // Effect 1: Apply Aura: Change Model (Defias Pirate, Male). Effect 2: Increase Swim Speed, Value: 50% +}; + +// 24930 - Hallow's End Candy +class spell_hallow_end_candy : public SpellScriptLoader +{ + public: + spell_hallow_end_candy() : SpellScriptLoader("spell_hallow_end_candy") { } + + class spell_hallow_end_candy_SpellScript : public SpellScript + { + PrepareSpellScript(spell_hallow_end_candy_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + for (uint32 spellId : spells) + if (!sSpellMgr->GetSpellInfo(spellId)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetCaster(), spells[urand(0, 3)], true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_hallow_end_candy_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + + private: + static uint32 const spells[4]; + }; + + SpellScript* GetSpellScript() const override + { + return new spell_hallow_end_candy_SpellScript(); + } +}; + +uint32 const spell_hallow_end_candy::spell_hallow_end_candy_SpellScript::spells[4] = +{ + SPELL_HALLOWS_END_CANDY_ORANGE_GIANT, + SPELL_HALLOWS_END_CANDY_SKELETON, + SPELL_HALLOWS_END_CANDY_PIRATE, + SPELL_HALLOWS_END_CANDY_GHOST +}; + +// 24926 - Hallow's End Candy +class spell_hallow_end_candy_pirate : public SpellScriptLoader +{ + public: + spell_hallow_end_candy_pirate() : SpellScriptLoader("spell_hallow_end_candy_pirate") { } + + class spell_hallow_end_candy_pirate_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hallow_end_candy_pirate_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE) + || !sSpellMgr->GetSpellInfo(SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE)) + return false; + return true; + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + uint32 spell = GetTarget()->getGender() == GENDER_FEMALE ? SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE : SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE; + GetTarget()->CastSpell(GetTarget(), spell, true); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + uint32 spell = GetTarget()->getGender() == GENDER_FEMALE ? SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE : SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE; + GetTarget()->RemoveAurasDueToSpell(spell); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_hallow_end_candy_pirate_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_INCREASE_SWIM_SPEED, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_hallow_end_candy_pirate_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_INCREASE_SWIM_SPEED, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hallow_end_candy_pirate_AuraScript(); + } +}; + // 24750 Trick enum TrickSpells { @@ -1189,6 +1288,8 @@ void AddSC_holiday_spell_scripts() // Love is in the Air new spell_love_is_in_the_air_romantic_picnic(); // Hallow's End + new spell_hallow_end_candy(); + new spell_hallow_end_candy_pirate(); new spell_hallow_end_trick(); new spell_hallow_end_trick_or_treat(); new spell_hallow_end_tricky_treat(); diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 2d14e3301a3..68c48853bad 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -39,6 +39,7 @@ #include "BattlegroundMgr.h" #include "TCSoap.h" #include "CliRunnable.h" +#include "Banner.h" #include "GitRevision.h" #include "WorldSocket.h" #include "WorldSocketMgr.h" @@ -132,20 +133,18 @@ extern int main(int argc, char** argv) // If logs are supposed to be handled async then we need to pass the io_service into the Log singleton sLog->Initialize(sConfigMgr->GetBoolDefault("Log.Async.Enable", false) ? &_ioService : nullptr); - TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", GitRevision::GetFullVersion()); - TC_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n"); - TC_LOG_INFO("server.worldserver", " ______ __"); - TC_LOG_INFO("server.worldserver", "/\\__ _\\ __ __/\\ \\__"); - TC_LOG_INFO("server.worldserver", "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\, _\\ __ __"); - TC_LOG_INFO("server.worldserver", " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\"); - TC_LOG_INFO("server.worldserver", " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\"); - TC_LOG_INFO("server.worldserver", " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\"); - TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); - TC_LOG_INFO("server.worldserver", " C O R E /\\___/"); - TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n"); - TC_LOG_INFO("server.worldserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); - TC_LOG_INFO("server.worldserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); - TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); + Trinity::Banner::Show("worldserver-daemon", + [](char const* text) + { + TC_LOG_INFO("server.worldserver", "%s", text); + }, + []() + { + TC_LOG_INFO("server.worldserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); + TC_LOG_INFO("server.worldserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); + TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); + } + ); OpenSSLCrypto::threadsSetup(); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 5bf03012ec1..49ddb92df24 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2532,6 +2532,64 @@ Wintergrasp.NoBattleTimer = 150 Wintergrasp.CrashRestartTimer = 10 # +# TolBarad.Enable +# Description: Enable the Tol Barad battlefield. +# Default: 0 - (Disabled) +# 1 - (Enabled, Experimental as in incomplete, bugged and with crashes) + +TolBarad.Enable = 0 + +# +# TolBarad.PlayerMax +# Description: Maximum number of players allowed in Tol Barad. +# Default: 100 + +TolBarad.PlayerMax = 100 + +# +# TolBarad.PlayerMin +# Description: Minimum number of players required for Tol Barad. +# Default: 0 + +TolBarad.PlayerMin = 0 + +# +# TolBarad.PlayerMinLvl +# Description: Required character level for the Tol Barad battle. +# Default: 85 + +TolBarad.PlayerMinLvl = 85 + +# +# TolBarad.BattleTimer +# Description: Time (in minutes) for the Tol Barad battle to last. +# Default: 15 + +TolBarad.BattleTimer = 15 + +# +# TolBarad.BonusTime +# Description: Bonus time (in minutes) for each tower destroyed in Tol Barad battle. +# Default: 5 + +TolBarad.BonusTime = 5 + +# +# TolBarad.NoBattleTimer +# Description: Time (in minutes) between TolBarad battles. +# Default: 150 + +TolBarad.NoBattleTimer = 150 + +# +# TolBarad.CrashRestartTimer +# Description: Time (in minutes) to delay the restart of TolBarad if the world server +# crashed during a running battle. +# Default: 10 + +TolBarad.CrashRestartTimer = 10 + +# ################################################################################################### ################################################################################################### diff --git a/src/tools/connection_patcher/Program.cpp b/src/tools/connection_patcher/Program.cpp index 0f2a829d71b..354cfa17191 100644 --- a/src/tools/connection_patcher/Program.cpp +++ b/src/tools/connection_patcher/Program.cpp @@ -25,7 +25,8 @@ #include "Patterns/Mac.hpp" #include "Patterns/Windows.hpp" -#include <CompilerDefs.h> +#include "Banner.h" +#include "CompilerDefs.h" #include <boost/algorithm/string/replace.hpp> #include <boost/program_options.hpp> @@ -131,6 +132,8 @@ int main(int argc, char** argv) try { + Trinity::Banner::Show("connection_patcher", [](char const* text) { std::cout << text << std::endl; }, nullptr); + auto vm = GetConsoleArguments(argc, argv); // exit if help is enabled diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index 22a8cc72c3c..91efa8ce287 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -35,6 +35,7 @@ #include "DBFilesClientList.h" #include "CascLib.h" #include "dbcfile.h" +#include "Banner.h" #include "StringFormat.h" #include "adt.h" @@ -1179,8 +1180,7 @@ bool OpenCascStorage(int locale) int main(int argc, char * arg[]) { - printf("Map & DBC Extractor\n"); - printf("===================\n"); + Trinity::Banner::Show("Map & DBC Extractor", [](char const* text) { printf("%s\n", text); }, nullptr); boost::filesystem::path current(boost::filesystem::current_path()); strcpy(input_path, current.string().c_str()); diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp index 7bec37a64e8..29b0715fdbf 100644 --- a/src/tools/mmaps_generator/PathGenerator.cpp +++ b/src/tools/mmaps_generator/PathGenerator.cpp @@ -19,6 +19,7 @@ #include "PathCommon.h" #include "MapBuilder.h" #include "Timer.h" +#include "Banner.h" using namespace MMAP; @@ -242,6 +243,8 @@ int finish(const char* message, int returnValue) int main(int argc, char** argv) { + Trinity::Banner::Show("MMAP generator", [](char const* text) { printf("%s\n", text); }, nullptr); + int threads = 3, mapnum = -1; float maxAngle = 70.0f; int tileX = -1, tileY = -1; diff --git a/src/tools/vmap4_assembler/VMapAssembler.cpp b/src/tools/vmap4_assembler/VMapAssembler.cpp index efe705e8b6c..24889e7fa63 100644 --- a/src/tools/vmap4_assembler/VMapAssembler.cpp +++ b/src/tools/vmap4_assembler/VMapAssembler.cpp @@ -20,9 +20,12 @@ #include <iostream> #include "TileAssembler.h" +#include "Banner.h" int main(int argc, char* argv[]) { + Trinity::Banner::Show("VMAP assembler", [](char const* text) { std::cout << text << std::endl; }, nullptr); + if (argc != 3) { std::cout << "usage: " << argv[0] << " <raw data dir> <vmap dest dir>" << std::endl; diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp index 55f2e6efde4..e4177052751 100644 --- a/src/tools/vmap4_extractor/vmapexport.cpp +++ b/src/tools/vmap4_extractor/vmapexport.cpp @@ -49,7 +49,7 @@ #include "mpqfile.h" #include "vmapexport.h" - +#include "Banner.h" #include <boost/filesystem/path.hpp> #include <boost/filesystem/operations.hpp> @@ -457,6 +457,8 @@ bool processArgv(int argc, char ** argv, const char *versionString) int main(int argc, char ** argv) { + Trinity::Banner::Show("VMAP data extractor", [](char const* text) { printf("%s\n", text); }, nullptr); + bool success = true; const char *versionString = "V4.03 2015_05"; |