diff options
25 files changed, 973 insertions, 83 deletions
diff --git a/sql/updates/world/2012_09_26_00_world_phase_definitions_434.sql b/sql/updates/world/2012_09_26_00_world_phase_definitions_434.sql new file mode 100644 index 00000000000..b8e97278eb5 --- /dev/null +++ b/sql/updates/world/2012_09_26_00_world_phase_definitions_434.sql @@ -0,0 +1,66 @@ +DROP TABLE IF EXISTS `phase_definitions`; +CREATE TABLE `phase_definitions` ( + `zoneId` mediumint(7) unsigned NOT NULL DEFAULT '0', + `entry` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `phasemask` bigint(20) unsigned NOT NULL DEFAULT '0', + `phaseId` tinyint(3) unsigned NOT NULL DEFAULT '0', + `terrainswapmap` smallint(5) unsigned NOT NULL DEFAULT '0', + `flags` tinyint(3) unsigned DEFAULT '0', + `comment` text, + PRIMARY KEY (`zoneId`, `entry`) +) +AUTO_INCREMENT=1 +ENGINE=MyISAM +COLLATE='utf8_general_ci'; + +INSERT INTO `phase_definitions` (`zoneId`, `entry`, `phasemask`, `phaseId`, `terrainswapmap`, `flags`, `comment`) VALUES +(1519, 1, 129, 0, 0, 0, 'Stormwind: [A] Heros Call: Vashj''ir'), +(1519, 2, 257, 0, 0, 0, 'Stormwind: [A] Heros Call: Hyjal'), +(1519, 3, 513, 0, 0, 0, 'Stormwind: [A] Heros Call: Deepholm'), +(1519, 4, 1025, 0, 0, 0, 'Stormwind: [A] Heros Call: Uldum'), +(1519, 5, 2049, 0, 0, 0, 'Stormwind: [A] Heros Call: Twilight Highlands'), +(1637, 1, 129, 0, 0, 0, 'Orgrimmar: [H] Warchiefs Command: Vashj''ir'), +(1637, 2, 257, 0, 0, 0, 'Orgrimmar: [H] Warchiefs Command: Hyjal'), +(1637, 3, 513, 0, 0, 0, 'Orgrimmar: [H] Warchiefs Command: Deepholm'), +(1637, 4, 1025, 0, 0, 0, 'Orgrimmar: [H] Warchiefs Command: Uldum'), +(1637, 5, 2049, 0, 0, 0, 'Orgrimmar: [H] Warchiefs Command: Twilight Highlands'), +(616 , 1, 0 , 165, 719, 0, 'Mount Hyjal: Default Terrainswap'); + +DROP TABLE IF EXISTS `spell_phase`; +CREATE TABLE `spell_phase` ( + `id` mediumint(7) unsigned NOT NULL DEFAULT '0', + `phasemask` bigint(20) unsigned NOT NULL DEFAULT '1', + `terrainswapmap` smallint(5) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) +AUTO_INCREMENT=1 +ENGINE=MyISAM +COLLATE='utf8_general_ci'; + +DELETE FROM `trinity_string` WHERE `entry` BETWEEN 176 AND 182; +INSERT INTO `trinity_string` (`entry`, `content_default`) VALUES +(176, '|cff0099FFPhaseMgr: Report for player: %s, zoneId: %u, level: %u, team: %u, phaseupdateflag: %u|r'), +(177, '|cff663399PhaseMgr: There are no definitions defined for zoneId %u.|r'), +(178, '|cff0066FFPhaseMgr: Success (entry: %u) - added %s %u to the players phase.|r'), +(179, '|cffFF0000PhaseMgr: Condition for phase %u (entry: %u, zoneId: %u) failed.|r'), +(180, '|cffFF0000PhaseMgr: Condition for phase %u (entry: %u, zoneId: %u) has last phasemask flag. Skipped other definitions.|r'), +(181, '|cff6699FFPhaseMgr: The player gets phasemask %u through definitions, %u through phasing auras, and phase %u through custom phase.|r'), +(182, '|cff0099FFPhaseMgr: The player has phasemask %u (real: %u).|r'); + +DELETE FROM `command` WHERE `name` IN('debug phase', 'debug send setphaseshift'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('debug phase', 1, 'Syntax: .debug phase\r\n\r\nSends a phase debug report of a player to you.'); + +/* +Conditions (SourceGroup -> ZoneId, SourceEntry -> Entry) + +If you visit the Gm Island as a alliance race you are automatically phased into phase 2. + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=23 AND `SourceGroup`=876 AND `SourceEntry`=1; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(23, 876, 1, 0, 0, 6, 0, 469, 0, 0, 0, 0, '', 'Phase Definitions Example: Phase is only visible for Alliance Members'); + +DELETE FROM `phase_definitions` WHERE `zoneId`=876 AND `entry`=1; +INSERT INTO `phase_definitions` (`zoneId`, `entry`, `phasemask`, `phaseId`, `terrainswapmap`, `flags`, `comment`) VALUES +(876, 1, 2, 0, 0, 0, '[Example] Gm Island'); +*/ diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 917c4cd7f90..406fb7cb753 100755 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -611,7 +611,8 @@ bool ConditionMgr::CanHaveSourceGroupSet(ConditionSourceType sourceType) const sourceType == CONDITION_SOURCE_TYPE_VEHICLE_SPELL || sourceType == CONDITION_SOURCE_TYPE_SPELL_IMPLICIT_TARGET || sourceType == CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT || - sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT); + sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT || + sourceType == CONDITION_SOURCE_TYPE_PHASE_DEFINITION); } bool ConditionMgr::CanHaveSourceIdSet(ConditionSourceType sourceType) const @@ -687,6 +688,23 @@ ConditionList ConditionMgr::GetConditionsForSmartEvent(int32 entryOrGuid, uint32 return cond; } +ConditionList ConditionMgr::GetConditionsForPhaseDefinition(uint32 zone, uint32 entry) +{ + ConditionList cond; + PhaseDefinitionConditionContainer::const_iterator itr = PhaseDefinitionsConditionStore.find(zone); + if (itr != PhaseDefinitionsConditionStore.end()) + { + ConditionTypeContainer::const_iterator i = (*itr).second.find(entry); + if (i != (*itr).second.end()) + { + cond = (*i).second; + sLog->outDebug(LOG_FILTER_CONDITIONSYS, "GetConditionsForPhaseDefinition: found conditions for zone %u entry %u spell %u", zone, entry); + } + } + + return cond; +} + void ConditionMgr::LoadConditions(bool isReload) { uint32 oldMSTime = getMSTime(); @@ -898,6 +916,13 @@ void ConditionMgr::LoadConditions(bool isReload) ++count; continue; } + case CONDITION_SOURCE_TYPE_PHASE_DEFINITION: + { + PhaseDefinitionsConditionStore[cond->SourceGroup][cond->SourceEntry].push_back(cond); + valid = true; + ++count; + continue; + } default: break; } @@ -1389,6 +1414,13 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) return false; } break; + case CONDITION_SOURCE_TYPE_PHASE_DEFINITION: + if (!PhaseMgr::IsConditionTypeSupported(cond->ConditionType)) + { + sLog->outError(LOG_FILTER_SQL, "Condition source type `CONDITION_SOURCE_TYPE_PHASE_DEFINITION` does not support condition type %u, ignoring.", cond->ConditionType); + return false; + } + break; case CONDITION_SOURCE_TYPE_GOSSIP_MENU: case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: case CONDITION_SOURCE_TYPE_SMART_EVENT: @@ -1399,6 +1431,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) return true; } + bool ConditionMgr::isConditionTypeValid(Condition* cond) { if (cond->ConditionType == CONDITION_NONE || cond->ConditionType >= CONDITION_MAX) @@ -1940,6 +1973,19 @@ void ConditionMgr::Clean() SpellClickEventConditionStore.clear(); + for (PhaseDefinitionConditionContainer::iterator itr = PhaseDefinitionsConditionStore.begin(); itr != PhaseDefinitionsConditionStore.end(); ++itr) + { + for (ConditionTypeContainer::iterator it = itr->second.begin(); it != itr->second.end(); ++it) + { + for (ConditionList::const_iterator i = it->second.begin(); i != it->second.end(); ++i) + delete *i; + it->second.clear(); + } + itr->second.clear(); + } + + PhaseDefinitionsConditionStore.clear(); + // this is a BIG hack, feel free to fix it if you can figure out the ConditionMgr ;) for (std::list<Condition*>::const_iterator itr = AllocatedMemoryStore.begin(); itr != AllocatedMemoryStore.end(); ++itr) delete *itr; diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index 57af0562dcd..fe7f6a18af8 100755 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -124,7 +124,8 @@ enum ConditionSourceType CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK = 20, CONDITION_SOURCE_TYPE_VEHICLE_SPELL = 21, CONDITION_SOURCE_TYPE_SMART_EVENT = 22, - CONDITION_SOURCE_TYPE_MAX = 23 //MAX + CONDITION_SOURCE_TYPE_PHASE_DEFINITION = 23, + CONDITION_SOURCE_TYPE_MAX = 24 //MAX }; enum ComparisionType @@ -211,6 +212,7 @@ typedef std::map<uint32, ConditionList> ConditionTypeContainer; typedef std::map<ConditionSourceType, ConditionTypeContainer> ConditionContainer; typedef std::map<uint32, ConditionTypeContainer> CreatureSpellConditionContainer; typedef std::map<std::pair<int32, uint32 /*SAI source_type*/>, ConditionTypeContainer> SmartEventConditionContainer; +typedef std::map<int32 /*zoneId*/, ConditionTypeContainer> PhaseDefinitionConditionContainer; typedef std::map<uint32, ConditionList> ConditionReferenceContainer;//only used for references @@ -237,6 +239,7 @@ class ConditionMgr ConditionList GetConditionsForSpellClickEvent(uint32 creatureId, uint32 spellId); ConditionList GetConditionsForSmartEvent(int32 entryOrGuid, uint32 eventId, uint32 sourceType); ConditionList GetConditionsForVehicleSpell(uint32 creatureId, uint32 spellId); + ConditionList GetConditionsForPhaseDefinition(uint32 zone, uint32 entry); private: bool isSourceTypeValid(Condition* cond); @@ -254,6 +257,7 @@ class ConditionMgr CreatureSpellConditionContainer VehicleSpellConditionStore; CreatureSpellConditionContainer SpellClickEventConditionStore; SmartEventConditionContainer SmartEventConditionStore; + PhaseDefinitionConditionContainer PhaseDefinitionsConditionStore; }; template <class T> bool CompareValues(ComparisionType type, T val1, T val2) diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 308b9fa1a60..a1a0931dc5a 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1408,8 +1408,8 @@ struct LockEntry struct PhaseEntry { uint32 ID; // 0 - char* Name; // 1 - uint32 phaseShift; // 2 + char* Name; // 1 + uint32 flag; // 2 }; struct MailTemplateEntry diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9a0b21194cd..0582a879d5f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -649,7 +649,7 @@ void KillRewarder::Reward() #ifdef _MSC_VER #pragma warning(disable:4355) #endif -Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_reputationMgr(this) +Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_reputationMgr(this), phaseMgr(this) { #ifdef _MSC_VER #pragma warning(default:4355) @@ -2938,18 +2938,6 @@ void Player::SetGameMaster(bool on) } else { - // restore phase - uint32 newPhase = 0; - AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE); - if (!phases.empty()) - for (AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) - newPhase |= (*itr)->GetMiscValue(); - - if (!newPhase) - newPhase = PHASEMASK_NORMAL; - - SetPhaseMask(newPhase, false); - m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON; setFactionForRace(getRace()); RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); @@ -2970,6 +2958,9 @@ void Player::SetGameMaster(bool on) getHostileRefManager().setOnlineOfflineState(true); m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); + + phaseMgr.AddUpdateFlag(PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED); + phaseMgr.Update(); } UpdateObjectVisibility(); @@ -3202,6 +3193,11 @@ void Player::GiveLevel(uint8 level) UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL); + PhaseUpdateData phaseUdateData; + phaseUdateData.AddConditionType(CONDITION_LEVEL); + + phaseMgr.NotifyConditionChanged(phaseUdateData); + // Refer-A-Friend if (GetSession()->GetRecruiterId()) if (level < sWorld->getIntConfig(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL)) @@ -7628,6 +7624,8 @@ void Player::UpdateArea(uint32 newArea) // so apply them accordingly m_areaUpdateId = newArea; + phaseMgr.AddUpdateFlag(PHASE_UPDATE_FLAG_AREA_UPDATE); + AreaTableEntry const* area = GetAreaEntryByAreaID(newArea); pvpInfo.inFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA); UpdatePvPState(true); @@ -7644,10 +7642,14 @@ void Player::UpdateArea(uint32 newArea) } else RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); + + phaseMgr.RemoveUpdateFlag(PHASE_UPDATE_FLAG_AREA_UPDATE); } void Player::UpdateZone(uint32 newZone, uint32 newArea) { + phaseMgr.AddUpdateFlag(PHASE_UPDATE_FLAG_ZONE_UPDATE); + if (m_zoneUpdateId != newZone) { sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); @@ -7757,6 +7759,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) UpdateLocalChannels(newZone); UpdateZoneDependentAuras(newZone); + + phaseMgr.RemoveUpdateFlag(PHASE_UPDATE_FLAG_ZONE_UPDATE); } //If players are too far away from the duel flag... they lose the duel @@ -15010,6 +15014,11 @@ void Player::AddQuest(Quest const* quest, Object* questGiver) CastSpell(this, itr->second->spellId, true); } + PhaseUpdateData phaseUdateData; + phaseUdateData.AddQuestUpdate(quest_id); + + phaseMgr.NotifyConditionChanged(phaseUdateData); + UpdateForQuestWorldObjects(); } @@ -15178,6 +15187,11 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, m_RewardedQuests.insert(quest_id); m_RewardedQuestsSave[quest_id] = true; + PhaseUpdateData phaseUdateData; + phaseUdateData.AddQuestUpdate(quest_id); + + phaseMgr.NotifyConditionChanged(phaseUdateData); + // StoreNewItem, mail reward, etc. save data directly to the database // to prevent exploitable data desynchronisation we save the quest status to the database too // (to prevent rewarding this quest another time while rewards were already given out) @@ -15767,6 +15781,11 @@ void Player::SetQuestStatus(uint32 quest_id, QuestStatus status) m_QuestStatusSave[quest_id] = true; } + PhaseUpdateData phaseUdateData; + phaseUdateData.AddQuestUpdate(quest_id); + + phaseMgr.NotifyConditionChanged(phaseUdateData); + UpdateForQuestWorldObjects(); } @@ -15777,6 +15796,11 @@ void Player::RemoveActiveQuest(uint32 quest_id) { m_QuestStatus.erase(itr); m_QuestStatusSave[quest_id] = false; + + PhaseUpdateData phaseUdateData; + phaseUdateData.AddQuestUpdate(quest_id); + + phaseMgr.NotifyConditionChanged(phaseUdateData); return; } } @@ -15788,6 +15812,11 @@ void Player::RemoveRewardedQuest(uint32 quest_id) { m_RewardedQuests.erase(rewItr); m_RewardedQuestsSave[quest_id] = false; + + PhaseUpdateData phaseUdateData; + phaseUdateData.AddQuestUpdate(quest_id); + + phaseMgr.NotifyConditionChanged(phaseUdateData); } } @@ -24420,25 +24449,6 @@ void Player::_LoadSkills(PreparedQueryResult result) } } -uint32 Player::GetPhaseMaskForSpawn() const -{ - uint32 phase = PHASEMASK_NORMAL; - if (!isGameMaster()) - phase = GetPhaseMask(); - else - { - AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE); - if (!phases.empty()) - phase = phases.front()->GetMiscValue(); - } - - // some aura phases include 1 normal map in addition to phase itself - if (uint32 n_phase = phase & ~PHASEMASK_NORMAL) - return n_phase; - - return PHASEMASK_NORMAL; -} - InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const { ItemTemplate const* pProto = pItem->GetTemplate(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 4d91ac42879..f1f58231bef 100755..100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -37,6 +37,9 @@ #include "Unit.h" #include "Util.h" // for Tokens typedef #include "WorldSession.h" +#include "PhaseMgr.h" + +// for template #include "SpellMgr.h" #include<string> @@ -54,6 +57,7 @@ class PlayerMenu; class PlayerSocial; class SpellCastTargets; class UpdateMask; +class PhaseMgr; typedef std::deque<Mail*> PlayerMails; @@ -1342,7 +1346,8 @@ class Player : public Unit, public GridObject<Player> Pet* GetPet() const; Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime); void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false); - uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn + + PhaseMgr& GetPhaseMgr() { return phaseMgr; } void Say(const std::string& text, const uint32 language); void Yell(const std::string& text, const uint32 language); @@ -3083,6 +3088,8 @@ class Player : public Unit, public GridObject<Player> uint32 _pendingBindTimer; uint32 _activeCheats; + + PhaseMgr phaseMgr; }; void AddItemsSetItem(Player*player, Item* item); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 3945d3b76fe..dd2afa44a07 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8652,7 +8652,7 @@ void ObjectMgr::LoadFactionChangeTitles() } uint32 count = 0; - + do { Field* fields = result->Fetch(); @@ -8666,7 +8666,7 @@ void ObjectMgr::LoadFactionChangeTitles() sLog->outError(LOG_FILTER_SQL, "Title %u referenced in `player_factionchange_title` does not exist, pair skipped!", horde); else FactionChange_Titles[alliance] = horde; - + ++count; } while (result->NextRow()); @@ -8674,6 +8674,99 @@ void ObjectMgr::LoadFactionChangeTitles() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change title pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadPhaseDefinitions() +{ + _PhaseDefinitionStore.clear(); + + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT zoneId, entry, phasemask, phaseId, terrainswapmap, flags FROM `phase_definitions` ORDER BY `entry` ASC"); + + if (!result) + { + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 phasing definitions. DB table `phase_definitions` is empty."); + return; + } + + uint32 count = 0; + + do + { + Field *fields = result->Fetch(); + + PhaseDefinition PhaseDefinition; + + PhaseDefinition.zoneId = fields[0].GetUInt32(); + PhaseDefinition.entry = fields[1].GetUInt32(); + PhaseDefinition.phasemask = fields[2].GetUInt32(); + PhaseDefinition.phaseId = fields[3].GetUInt32(); + PhaseDefinition.terrainswapmap = fields[4].GetUInt32(); + PhaseDefinition.flags = fields[5].GetUInt32(); + + // Checks + if ((PhaseDefinition.flags & PHASE_FLAG_OVERWRITE_EXISTING) && (PhaseDefinition.flags & PHASE_FLAG_NEGATE_PHASE)) + { + sLog->outError(LOG_FILTER_SQL, "Flags defined in phase_definitions in zoneId %d and entry %u does contain PHASE_FLAG_OVERWRITE_EXISTING and PHASE_FLAG_NEGATE_PHASE. Setting flags to PHASE_FLAG_OVERWRITE_EXISTING", PhaseDefinition.zoneId, PhaseDefinition.entry); + PhaseDefinition.flags &= ~PHASE_FLAG_NEGATE_PHASE; + } + + _PhaseDefinitionStore[PhaseDefinition.zoneId].push_back(PhaseDefinition); + + ++count; + } + while (result->NextRow()); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u phasing definitions in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadSpellPhaseInfo() +{ + _SpellPhaseStore.clear(); + + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT id, phasemask, terrainswapmap FROM `spell_phase`"); + + if (!result) + { + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spell dbc infos. DB table `spell_phase` is empty."); + return; + } + + uint32 count = 0; + do + { + Field *fields = result->Fetch(); + + SpellPhaseInfo spellPhaseInfo; + spellPhaseInfo.spellId = fields[0].GetUInt32(); + + SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellPhaseInfo.spellId); + if (!spell) + { + sLog->outError(LOG_FILTER_SQL, "Spell %u defined in `spell_phase` does not exists, skipped.", spellPhaseInfo.spellId); + continue; + } + + if (!spell->HasAura(SPELL_AURA_PHASE)) + { + sLog->outError(LOG_FILTER_SQL, "Spell %u defined in `spell_phase` does not have aura effect type SPELL_AURA_PHASE, useless value.", spellPhaseInfo.spellId); + continue; + } + + spellPhaseInfo.phasemask = fields[1].GetUInt32(); + spellPhaseInfo.terrainswapmap = fields[2].GetUInt32(); + + _SpellPhaseStore[spellPhaseInfo.spellId] = spellPhaseInfo; + + ++count; + } + while (result->NextRow()); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u spell dbc infos in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); +} + GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry) { GameObjectTemplateContainer::const_iterator itr = _gameObjectTemplateStore.find(entry); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 61094d60c55..496d5ae224f 100755 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -42,8 +42,10 @@ #include <limits> #include "ConditionMgr.h" #include <functional> +#include "PhaseMgr.h" class Item; +class PhaseMgr; // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some platform #if defined(__GNUC__) @@ -906,6 +908,12 @@ class ObjectMgr void LoadTrainerSpell(); void AddSpellToTrainer(uint32 entry, uint32 spell, uint32 spellCost, uint32 reqSkill, uint32 reqSkillValue, uint32 reqLevel); + void LoadPhaseDefinitions(); + void LoadSpellPhaseInfo(); + + PhaseDefinitionStore const* GetPhaseDefinitionStore() { return &_PhaseDefinitionStore; } + SpellPhaseStore const* GetSpellPhaseStore() { return &_SpellPhaseStore; } + std::string GeneratePetName(uint32 entry); uint32 GetBaseXP(uint8 level); uint32 GetXPForLevel(uint8 level) const; @@ -1222,6 +1230,9 @@ class ObjectMgr PageTextContainer _pageTextStore; InstanceTemplateContainer _instanceTemplateStore; + PhaseDefinitionStore _PhaseDefinitionStore; + SpellPhaseStore _SpellPhaseStore; + private: void LoadScripts(ScriptsType type); void CheckScripts(ScriptsType type, std::set<int32>& ids); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 2acc8ffcf7b..1947baa43b6 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1753,10 +1753,51 @@ void WorldSession::HandleReadyForAccountDataTimes(WorldPacket& /*recvData*/) SendAccountDataTimes(GLOBAL_CACHE_MASK); } -void WorldSession::SendSetPhaseShift(uint32 PhaseShift) +void WorldSession::SendSetPhaseShift(std::set<uint32> const& phaseIds, std::set<uint32> const& terrainswaps) { - WorldPacket data(SMSG_SET_PHASE_SHIFT, 4); - data << uint32(PhaseShift); + ObjectGuid guid = _player->GetGUID(); + + WorldPacket data(SMSG_SET_PHASE_SHIFT, 1 + 8 + 4 + 4 + 4 + 4 + 2 * phaseIds.size() + 4 + terrainswaps.size() * 2); + data.WriteBit(guid[2]); + data.WriteBit(guid[3]); + data.WriteBit(guid[1]); + data.WriteBit(guid[6]); + data.WriteBit(guid[4]); + data.WriteBit(guid[5]); + data.WriteBit(guid[0]); + data.WriteBit(guid[7]); + + data.WriteByteSeq(guid[7]); + data.WriteByteSeq(guid[4]); + + data << uint32(0); + //for (uint8 i = 0; i < worldMapAreaCount; ++i) + // data << uint16(0); // WorldMapArea.dbc id (controls map display) + + data.WriteByteSeq(guid[1]); + + data << uint32(0); // flags (not phasemask) + + data.WriteByteSeq(guid[2]); + data.WriteByteSeq(guid[6]); + + data << uint32(0); // Inactive terrain swaps + //for (uint8 i = 0; i < inactiveSwapsCount; ++i) + // data << uint16(0); + + data << uint32(phaseIds.size()) * 2; // Phase.dbc ids + for (std::set<uint32>::const_iterator itr = phaseIds.begin(); itr != phaseIds.end(); ++itr) + data << uint16(*itr); + + data.WriteByteSeq(guid[3]); + data.WriteByteSeq(guid[0]); + + data << uint32(terrainswaps.size()) * 2; // Active terrain swaps + for (std::set<uint32>::const_iterator itr = terrainswaps.begin(); itr != terrainswaps.end(); ++itr) + data << uint16(*itr); + + data.WriteByteSeq(guid[5]); + SendPacket(&data); } diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index d967ece9cf2..f14dc3c8b14 100755 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -450,3 +450,14 @@ void InstanceScript::UpdateEncounterState(EncounterCreditType type, uint32 credi } } } + +void InstanceScript::UpdatePhasing() +{ + PhaseUpdateData phaseUdateData; + phaseUdateData.AddConditionType(CONDITION_INSTANCE_DATA); + + Map::PlayerList const& players = instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->getSource()) + player->GetPhaseMgr().NotifyConditionChanged(phaseUdateData); +} diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index ab37c1ab5e9..291ba3be52f 100755 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -217,6 +217,9 @@ class InstanceScript : public ZoneScript virtual void FillInitialWorldStates(WorldPacket& /*data*/) {} + // ReCheck PhaseTemplate related conditions + void UpdatePhasing(); + protected: void SetBossNumber(uint32 number) { bosses.resize(number); } void LoadDoorData(DoorData const* data); diff --git a/src/server/game/Maps/PhaseMgr.cpp b/src/server/game/Maps/PhaseMgr.cpp new file mode 100644 index 00000000000..63bdff2d094 --- /dev/null +++ b/src/server/game/Maps/PhaseMgr.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "PhaseMgr.h" +#include "Chat.h" + +////////////////////////////////////////////////////////////////// +// Updating + +PhaseMgr::PhaseMgr(Player* _player) : player(_player), phaseData(_player), _UpdateFlags(0) +{ + _PhaseDefinitionStore = sObjectMgr->GetPhaseDefinitionStore(); + _SpellPhaseStore = sObjectMgr->GetSpellPhaseStore(); +} + +void PhaseMgr::Update() +{ + if (IsUpdateInProgress()) + return; + + if (_UpdateFlags & PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED) + phaseData.SendPhaseshiftToPlayer(); + + if (_UpdateFlags & PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED) + phaseData.SendPhaseMaskToPlayer(); + + _UpdateFlags = 0; +} + +void PhaseMgr::RemoveUpdateFlag(PhaseUpdateFlag updateFlag) +{ + _UpdateFlags &= ~updateFlag; + + if (updateFlag == PHASE_UPDATE_FLAG_ZONE_UPDATE) + { + // Update zone changes + if (phaseData.HasActiveDefinitions()) + { + phaseData.ResetDefinitions(); + _UpdateFlags |= (PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED | PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED); + } + + if (_PhaseDefinitionStore->find(player->GetZoneId()) != _PhaseDefinitionStore->end()) + Recalculate(); + } + + Update(); +} + +///////////////////////////////////////////////////////////////// +// Notifier + +void PhaseMgr::NotifyConditionChanged(PhaseUpdateData const& updateData) +{ + if (NeedsPhaseUpdateWithData(updateData)) + { + Recalculate(); + Update(); + } +} + +////////////////////////////////////////////////////////////////// +// Phasing Definitions + +void PhaseMgr::Recalculate() +{ + if (phaseData.HasActiveDefinitions()) + { + phaseData.ResetDefinitions(); + _UpdateFlags |= (PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED | PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED); + } + + PhaseDefinitionStore::const_iterator itr = _PhaseDefinitionStore->find(player->GetZoneId()); + if (itr != _PhaseDefinitionStore->end()) + for (PhaseDefinitionContainer::const_iterator phase = itr->second.begin(); phase != itr->second.end(); ++phase) + if (CheckDefinition(&(*phase))) + { + phaseData.AddPhaseDefinition(&(*phase)); + + if (phase->phasemask) + _UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED; + + if (phase->phaseId || phase->terrainswapmap) + _UpdateFlags |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED; + + if (phase->IsLastDefinition()) + break; + } +} + +inline bool PhaseMgr::CheckDefinition(PhaseDefinition const* phaseDefinition) +{ + return sConditionMgr->IsObjectMeetToConditions(player, sConditionMgr->GetConditionsForPhaseDefinition(phaseDefinition->zoneId, phaseDefinition->entry)); +} + +bool PhaseMgr::NeedsPhaseUpdateWithData(PhaseUpdateData const updateData) const +{ + PhaseDefinitionStore::const_iterator itr = _PhaseDefinitionStore->find(player->GetZoneId()); + if (itr != _PhaseDefinitionStore->end()) + { + for (PhaseDefinitionContainer::const_iterator phase = itr->second.begin(); phase != itr->second.end(); ++phase) + { + ConditionList conditionList = sConditionMgr->GetConditionsForPhaseDefinition(phase->zoneId, phase->entry); + for (ConditionList::const_iterator condition = conditionList.begin(); condition != conditionList.end(); ++condition) + if (updateData.IsConditionRelated(*condition)) + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////// +// Auras + +void PhaseMgr::RegisterPhasingAuraEffect(AuraEffect const* auraEffect) +{ + PhaseInfo phaseInfo; + + if (auraEffect->GetMiscValue()) + { + _UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED; + phaseInfo.phasemask = auraEffect->GetMiscValue(); + } + else + { + SpellPhaseStore::const_iterator itr = _SpellPhaseStore->find(auraEffect->GetId()); + if (itr != _SpellPhaseStore->end()) + { + if (itr->second.phasemask) + { + _UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED; + phaseInfo.phasemask = itr->second.phasemask; + } + + if (itr->second.terrainswapmap) + phaseInfo.terrainswapmap = itr->second.terrainswapmap; + } + } + + phaseInfo.phaseId = auraEffect->GetMiscValueB(); + + if (phaseInfo.NeedsClientSideUpdate()) + _UpdateFlags |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED; + + phaseData.AddAuraInfo(auraEffect->GetId(), phaseInfo); + + Update(); +} + +void PhaseMgr::UnRegisterPhasingAuraEffect(AuraEffect const* auraEffect) +{ + _UpdateFlags |= phaseData.RemoveAuraInfo(auraEffect->GetId()); + + Update(); +} + +////////////////////////////////////////////////////////////////// +// Commands + +void PhaseMgr::SendDebugReportToPlayer(Player* const debugger) +{ + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_REPORT_STATUS, player->GetName(), player->GetZoneId(), player->getLevel(), player->GetTeamId(), _UpdateFlags); + + PhaseDefinitionStore::const_iterator itr = _PhaseDefinitionStore->find(player->GetZoneId()); + if (itr == _PhaseDefinitionStore->end()) + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_NO_DEFINITIONS, player->GetZoneId()); + else + { + for (PhaseDefinitionContainer::const_iterator phase = itr->second.begin(); phase != itr->second.end(); ++phase) + { + if (CheckDefinition(&(*phase))) + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_SUCCESS, phase->entry, phase->IsNegatingPhasemask() ? "negated Phase" : "Phase", phase->phasemask); + else + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_FAILED, phase->phasemask, phase->entry, phase->zoneId); + + if (phase->IsLastDefinition()) + { + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_LAST_PHASE, phase->phasemask, phase->entry, phase->zoneId); + break; + } + } + } + + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_LIST, phaseData._PhasemaskThroughDefinitions, phaseData._PhasemaskThroughAuras, phaseData._CustomPhasemask); + + ChatHandler(debugger).PSendSysMessage(LANG_PHASING_PHASEMASK, phaseData.GetPhaseMaskForSpawn(), player->GetPhaseMask()); +} + +void PhaseMgr::SetCustomPhase(uint32 const phaseMask) +{ + phaseData._CustomPhasemask = phaseMask; + + _UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED; + + Update(); +} + +////////////////////////////////////////////////////////////////// +// Phase Data + +uint32 PhaseData::GetCurrentPhasemask() const +{ + if (player->isGameMaster()) + return PHASEMASK_ANYWHERE; + + if (_CustomPhasemask) + return _CustomPhasemask; + + return GetPhaseMaskForSpawn(); +} + +inline uint32 PhaseData::GetPhaseMaskForSpawn() const +{ + uint32 const phase = (_PhasemaskThroughDefinitions | _PhasemaskThroughAuras); + return (phase ? phase : PHASEMASK_NORMAL); +} + +void PhaseData::SendPhaseMaskToPlayer() +{ + // Server side update + uint32 const phasemask = GetCurrentPhasemask(); + if (player->GetPhaseMask() == phasemask) + return; + + player->SetPhaseMask(phasemask, false); + + if (player->IsVisible()) + player->UpdateObjectVisibility(); +} + +void PhaseData::SendPhaseshiftToPlayer() +{ + // Client side update + std::set<uint32> phaseIds; + std::set<uint32> terrainswaps; + + for (PhaseInfoContainer::const_iterator itr = spellPhaseInfo.begin(); itr != spellPhaseInfo.end(); ++itr) + { + if (itr->second.terrainswapmap) + terrainswaps.insert(itr->second.terrainswapmap); + + if (itr->second.phaseId) + phaseIds.insert(itr->second.phaseId); + } + + // Phase Definitions + for (std::list<PhaseDefinition const*>::const_iterator itr = activePhaseDefinitions.begin(); itr != activePhaseDefinitions.end(); ++itr) + { + if ((*itr)->phaseId) + phaseIds.insert((*itr)->phaseId); + + if ((*itr)->terrainswapmap) + terrainswaps.insert((*itr)->terrainswapmap); + } + + player->GetSession()->SendSetPhaseShift(phaseIds, terrainswaps); +} + +void PhaseData::AddPhaseDefinition(PhaseDefinition const* phaseDefinition) +{ + if (phaseDefinition->IsOverwritingExistingPhases()) + { + activePhaseDefinitions.clear(); + _PhasemaskThroughDefinitions = phaseDefinition->phasemask; + } + else + { + if (phaseDefinition->IsNegatingPhasemask()) + _PhasemaskThroughDefinitions &= ~phaseDefinition->phasemask; + else + _PhasemaskThroughDefinitions |= phaseDefinition->phasemask; + } + + activePhaseDefinitions.push_back(phaseDefinition); +} + +void PhaseData::AddAuraInfo(uint32 const spellId, PhaseInfo phaseInfo) +{ + if (phaseInfo.phasemask) + _PhasemaskThroughAuras |= phaseInfo.phasemask; + + spellPhaseInfo[spellId] = phaseInfo; +} + +uint32 PhaseData::RemoveAuraInfo(uint32 const spellId) +{ + PhaseInfoContainer::const_iterator rAura = spellPhaseInfo.find(spellId); + if (rAura != spellPhaseInfo.end()) + { + uint32 updateflag = 0; + + if (rAura->second.NeedsClientSideUpdate()) + updateflag |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED; + + if (rAura->second.NeedsServerSideUpdate()) + { + _PhasemaskThroughAuras = 0; + + updateflag |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED; + + spellPhaseInfo.erase(rAura); + + for (PhaseInfoContainer::const_iterator itr = spellPhaseInfo.begin(); itr != spellPhaseInfo.end(); ++itr) + _PhasemaskThroughAuras |= itr->second.phasemask; + } + + return updateflag; + } + else + return 0; +} + +////////////////////////////////////////////////////////////////// +// Phase Update Data + +void PhaseUpdateData::AddQuestUpdate(uint32 const questId) +{ + AddConditionType(CONDITION_QUESTREWARDED); + AddConditionType(CONDITION_QUESTTAKEN); + AddConditionType(CONDITION_QUEST_COMPLETE); + AddConditionType(CONDITION_QUEST_NONE); + + _questId = questId; +} + +bool PhaseUpdateData::IsConditionRelated(Condition const* condition) const +{ + switch (condition->ConditionType) + { + case CONDITION_QUESTREWARDED: + case CONDITION_QUESTTAKEN: + case CONDITION_QUEST_COMPLETE: + case CONDITION_QUEST_NONE: + return condition->ConditionValue1 == _questId && ((1 << condition->ConditionType) & _conditionTypeFlags); + default: + return (1 << condition->ConditionType) & _conditionTypeFlags; + } +} + +bool PhaseMgr::IsConditionTypeSupported(ConditionTypes const conditionType) +{ + switch (conditionType) + { + case CONDITION_QUESTREWARDED: + case CONDITION_QUESTTAKEN: + case CONDITION_QUEST_COMPLETE: + case CONDITION_QUEST_NONE: + case CONDITION_TEAM: + case CONDITION_CLASS: + case CONDITION_RACE: + case CONDITION_INSTANCE_DATA: + case CONDITION_LEVEL: + return true; + default: + return false; + } +} diff --git a/src/server/game/Maps/PhaseMgr.h b/src/server/game/Maps/PhaseMgr.h new file mode 100644 index 00000000000..accb0cd3ea8 --- /dev/null +++ b/src/server/game/Maps/PhaseMgr.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_PHASEMGR_H +#define TRINITY_PHASEMGR_H + +#include "SharedDefines.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" + +class ObjectMgr; +class Player; + +// Phasing (visibility) +enum PhasingFlags +{ + PHASE_FLAG_OVERWRITE_EXISTING = 0x01, // don't stack with existing phases, overwrites existing phases + PHASE_FLAG_NO_MORE_PHASES = 0x02, // stop calculating phases after this phase was applied (no more phases will be applied) + PHASE_FLAG_NEGATE_PHASE = 0x04 // negate instead to add the phasemask +}; + +enum PhaseUpdateFlag +{ + PHASE_UPDATE_FLAG_ZONE_UPDATE = 0x01, + PHASE_UPDATE_FLAG_AREA_UPDATE = 0x02, + + // Internal flags + PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED = 0x08, + PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED = 0x10, +}; + +struct PhaseDefinition +{ + uint32 zoneId; + uint32 entry; + uint32 phasemask; + uint32 phaseId; + uint32 terrainswapmap; + uint8 flags; + + bool IsOverwritingExistingPhases() const { return flags & PHASE_FLAG_OVERWRITE_EXISTING; } + bool IsLastDefinition() const { return flags & PHASE_FLAG_NO_MORE_PHASES; } + bool IsNegatingPhasemask() const { return flags & PHASE_FLAG_NEGATE_PHASE; } +}; + +typedef std::list<PhaseDefinition> PhaseDefinitionContainer; +typedef UNORDERED_MAP<uint32 /*zoneId*/, PhaseDefinitionContainer> PhaseDefinitionStore; + +struct SpellPhaseInfo +{ + uint32 spellId; + uint32 phasemask; + uint32 terrainswapmap; +}; + +typedef UNORDERED_MAP<uint32 /*spellId*/, SpellPhaseInfo> SpellPhaseStore; + +struct PhaseInfo +{ + PhaseInfo() : phasemask(0), terrainswapmap(0), phaseId(0) {} + + uint32 phasemask; + uint32 terrainswapmap; + uint32 phaseId; + + bool NeedsServerSideUpdate() const { return phasemask; } + bool NeedsClientSideUpdate() const { return terrainswapmap || phaseId; } +}; + +typedef UNORDERED_MAP<uint32 /*spellId*/, PhaseInfo> PhaseInfoContainer; + +struct PhaseData +{ + PhaseData(Player* _player) : player(_player), _PhasemaskThroughDefinitions(0), _PhasemaskThroughAuras(0), _CustomPhasemask(0) {} + + uint32 _PhasemaskThroughDefinitions; + uint32 _PhasemaskThroughAuras; + uint32 _CustomPhasemask; + + uint32 GetCurrentPhasemask() const; + inline uint32 GetPhaseMaskForSpawn() const; + + void ResetDefinitions() { _PhasemaskThroughDefinitions = 0; activePhaseDefinitions.clear(); } + void AddPhaseDefinition(PhaseDefinition const* phaseDefinition); + bool HasActiveDefinitions() const { return !activePhaseDefinitions.empty(); } + + void AddAuraInfo(uint32 const spellId, PhaseInfo phaseInfo); + uint32 RemoveAuraInfo(uint32 const spellId); + + void SendPhaseMaskToPlayer(); + void SendPhaseshiftToPlayer(); + +private: + Player* player; + std::list<PhaseDefinition const*> activePhaseDefinitions; + PhaseInfoContainer spellPhaseInfo; +}; + +struct PhaseUpdateData +{ + void AddConditionType(ConditionTypes const conditionType) { _conditionTypeFlags |= (1 << conditionType); } + void AddQuestUpdate(uint32 const questId); + + bool IsConditionRelated(Condition const* condition) const; + +private: + uint32 _conditionTypeFlags; + uint32 _questId; +}; + +class PhaseMgr +{ +public: + PhaseMgr(Player* _player); + ~PhaseMgr() {} + + uint32 GetCurrentPhasemask() { return phaseData.GetCurrentPhasemask(); }; + inline uint32 GetPhaseMaskForSpawn() { return phaseData.GetCurrentPhasemask(); } + + // Phase definitions update handling + void NotifyConditionChanged(PhaseUpdateData const& updateData); + void NotifyStoresReloaded() { Recalculate(); Update(); } + + void Update(); + + // Aura phase effects + void RegisterPhasingAuraEffect(AuraEffect const* auraEffect); + void UnRegisterPhasingAuraEffect(AuraEffect const* auraEffect); + + // Update flags (delayed phasing) + void AddUpdateFlag(PhaseUpdateFlag const updateFlag) { _UpdateFlags |= updateFlag; } + void RemoveUpdateFlag(PhaseUpdateFlag const updateFlag); + + // Needed for modify phase command + void SetCustomPhase(uint32 const phaseMask); + + // Debug + void SendDebugReportToPlayer(Player* const debugger); + + static bool IsConditionTypeSupported(ConditionTypes const conditionType); + +private: + void Recalculate(); + + inline bool CheckDefinition(PhaseDefinition const* phaseDefinition); + + bool NeedsPhaseUpdateWithData(PhaseUpdateData const updateData) const; + + inline bool IsUpdateInProgress() const { return (_UpdateFlags & PHASE_UPDATE_FLAG_ZONE_UPDATE) || (_UpdateFlags & PHASE_UPDATE_FLAG_AREA_UPDATE); } + + PhaseDefinitionStore const* _PhaseDefinitionStore; + SpellPhaseStore const* _SpellPhaseStore; + + Player* player; + PhaseData phaseData; + uint8 _UpdateFlags; +}; + +#endif diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 5893bbd6564..c55c57481f4 100755 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -174,7 +174,15 @@ enum TrinityStrings LANG_YOU_CHANGE_RUNIC_POWER = 173, LANG_YOURS_RUNIC_POWER_CHANGED = 174, LANG_LIQUID_STATUS = 175, - // Room for more level 1 176-199 not used + + LANG_PHASING_REPORT_STATUS = 176, + LANG_PHASING_NO_DEFINITIONS = 177, // Phasing + LANG_PHASING_SUCCESS = 178, + LANG_PHASING_FAILED = 179, + LANG_PHASING_LAST_PHASE = 180, + LANG_PHASING_LIST = 181, + LANG_PHASING_PHASEMASK = 182, + // Room for more level 1 183-199 not used // level 2 chat LANG_NO_SELECTION = 200, diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 5b6af4b2de8..694b2cf2d7f 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -1157,7 +1157,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER(SMSG_SET_FLAT_SPELL_MODIFIER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SET_FORCED_REACTIONS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SET_PCT_SPELL_MODIFIER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); - DEFINE_OPCODE_HANDLER(SMSG_SET_PHASE_SHIFT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); + DEFINE_OPCODE_HANDLER(SMSG_SET_PHASE_SHIFT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); DEFINE_OPCODE_HANDLER(SMSG_SET_PROFICIENCY, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide ); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index e6c13557076..5c61385f537 100755 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -253,7 +253,7 @@ class WorldSession void SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName); void SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res, uint32 val = 0); void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2, 3); - void SendSetPhaseShift(uint32 phaseShift); + void SendSetPhaseShift(std::set<uint32> const& phaseIds, std::set<uint32> const& terrainswaps); void SendQueryTimeResponse(); void SendAuthResponse(uint8 code, bool queued, uint32 queuePos = 0); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 28ddaf05afc..d70c68b9be2 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1808,27 +1808,21 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app Unit* target = aurApp->GetTarget(); - // no-phase is also phase state so same code for apply and remove - uint32 newPhase = 0; - Unit::AuraEffectList const& phases = target->GetAuraEffectsByType(SPELL_AURA_PHASE); - if (!phases.empty()) - for (Unit::AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) - newPhase |= (*itr)->GetMiscValue(); - if (Player* player = target->ToPlayer()) { - if (!newPhase) - newPhase = PHASEMASK_NORMAL; - - // GM-mode have mask 0xFFFFFFFF - if (player->isGameMaster()) - newPhase = 0xFFFFFFFF; - - player->SetPhaseMask(newPhase, false); - player->GetSession()->SendSetPhaseShift(newPhase); + if (apply) + player->GetPhaseMgr().RegisterPhasingAuraEffect(this); + else + player->GetPhaseMgr().UnRegisterPhasingAuraEffect(this); } else { + uint32 newPhase = 0; + Unit::AuraEffectList const& phases = target->GetAuraEffectsByType(SPELL_AURA_PHASE); + if (!phases.empty()) + for (Unit::AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) + newPhase |= (*itr)->GetMiscValue(); + if (!newPhase) { newPhase = PHASEMASK_NORMAL; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 94961b7b930..371668e73d9 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1368,6 +1368,9 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Spell Group Stack Rules..."); sSpellMgr->LoadSpellGroupStackRules(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Spell Phase Dbc Info..."); + sObjectMgr->LoadSpellPhaseInfo(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading NPC Texts..."); sObjectMgr->LoadGossipText(); @@ -1616,6 +1619,9 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading World States..."); // must be loaded before battleground, outdoor PvP and conditions LoadWorldStates(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Phase definitions..."); + sObjectMgr->LoadPhaseDefinitions(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Conditions..."); sConditionMgr->LoadConditions(); @@ -3022,3 +3028,11 @@ CharacterNameData const* World::GetCharacterNameData(uint32 guid) const else return NULL; } + +void World::UpdatePhaseDefinitions() +{ + SessionMap::const_iterator itr; + for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) + if (itr->second && itr->second->GetPlayer() && itr->second->GetPlayer()->IsInWorld()) + itr->second->GetPlayer()->GetPhaseMgr().NotifyStoresReloaded(); +} diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 33ec159ec81..e1465f4897d 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -784,6 +784,8 @@ class World uint32 GetCleaningFlags() const { return m_CleaningFlags; } void SetCleaningFlags(uint32 flags) { m_CleaningFlags = flags; } void ResetEventSeasonalQuests(uint16 event_id); + + void UpdatePhaseDefinitions(); protected: void _UpdateGameTime(); // callback for UpdateRealmCharacters diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 67b4d780942..363258648f6 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -90,6 +90,7 @@ public: { "areatriggers", SEC_ADMINISTRATOR, false, &HandleDebugAreaTriggersCommand, "", NULL }, { "los", SEC_MODERATOR, false, &HandleDebugLoSCommand, "", NULL }, { "moveflags", SEC_ADMINISTRATOR, false, &HandleDebugMoveflagsCommand, "", NULL }, + { "phase", SEC_MODERATOR, false, &HandleDebugPhaseCommand, "", NULL }, { NULL, SEC_PLAYER, false, NULL, "", NULL } }; static ChatCommand commandTable[] = @@ -948,8 +949,21 @@ public: if (!*args) return false; - uint32 PhaseShift = atoi(args); - handler->GetSession()->SendSetPhaseShift(PhaseShift); + char* t = strtok((char*)args, " "); + char* p = strtok(NULL, " "); + + if (!t) + return false; + + std::set<uint32> terrainswap; + std::set<uint32> phaseId; + + terrainswap.insert((uint32)atoi(t)); + + if (p) + phaseId.insert((uint32)atoi(p)); + + handler->GetSession()->SendSetPhaseShift(phaseId, terrainswap); return true; } @@ -1330,6 +1344,17 @@ public: handler->PSendSysMessage("Waypoint SQL written to SQL Developer log"); return true; } + + static bool HandleDebugPhaseCommand(ChatHandler* handler, char const* args) + { + Unit* unit = handler->getSelectedUnit(); + Player* player = handler->GetSession()->GetPlayer(); + if(unit && unit->GetTypeId() == TYPEID_PLAYER) + player = unit->ToPlayer(); + + player->GetPhaseMgr().SendDebugReportToPlayer(handler->GetSession()->GetPlayer()); + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 6803354d29b..fd24e618ef0 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -149,7 +149,7 @@ public: GameObject* object = new GameObject; uint32 guidLow = sObjectMgr->GenerateLowGuid(HIGHGUID_GAMEOBJECT); - if (!object->Create(guidLow, objectInfo->entry, map, player->GetPhaseMaskForSpawn(), x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY)) + if (!object->Create(guidLow, objectInfo->entry, map, player->GetPhaseMgr().GetPhaseMaskForSpawn(), x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY)) { delete object; return false; @@ -162,7 +162,7 @@ public: } // fill the gameobject data and save to the db - object->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), player->GetPhaseMaskForSpawn()); + object->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), player->GetPhaseMgr().GetPhaseMaskForSpawn()); // this will generate a new guid if the object is in an instance if (!object->LoadGameObjectFromDB(guidLow, map)) diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index c370c70a94a..932a0cbc634 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -1277,14 +1277,15 @@ public: uint32 phasemask = (uint32)atoi((char*)args); Unit* target = handler->getSelectedUnit(); - if (!target) - target = handler->GetSession()->GetPlayer(); - - // check online security - else if (target->GetTypeId() == TYPEID_PLAYER && handler->HasLowerSecurity(target->ToPlayer(), 0)) - return false; - - target->SetPhaseMask(phasemask, true); + if (target) + { + if (target->GetTypeId() == TYPEID_PLAYER) + target->ToPlayer()->GetPhaseMgr().SetCustomPhase(phasemask); + else + target->SetPhaseMask(phasemask, true); + } + else + handler->GetSession()->GetPlayer()->GetPhaseMgr().SetCustomPhase(phasemask); return true; } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index e43760191df..30713728cf6 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -151,13 +151,13 @@ public: } Creature* creature = new Creature(); - if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, (uint32)teamval, x, y, z, o)) + if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, (uint32)teamval, x, y, z, o)) { delete creature; return false; } - creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMgr().GetPhaseMaskForSpawn()); uint32 db_guid = creature->GetDBTableGUIDLow(); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index ce0141290ff..14481884ec8 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -115,6 +115,7 @@ public: { "npc_trainer", SEC_ADMINISTRATOR, true, &HandleReloadNpcTrainerCommand, "", NULL }, { "npc_vendor", SEC_ADMINISTRATOR, true, &HandleReloadNpcVendorCommand, "", NULL }, { "page_text", SEC_ADMINISTRATOR, true, &HandleReloadPageTextsCommand, "", NULL }, + { "phasedefinitions", SEC_ADMINISTRATOR, true, &HandleReloadPhaseDefinitionsCommand, "", NULL }, { "pickpocketing_loot_template", SEC_ADMINISTRATOR, true, &HandleReloadLootTemplatesPickpocketingCommand, "", NULL}, { "points_of_interest", SEC_ADMINISTRATOR, true, &HandleReloadPointsOfInterestCommand, "", NULL }, { "prospecting_loot_template", SEC_ADMINISTRATOR, true, &HandleReloadLootTemplatesProspectingCommand, "", NULL }, @@ -1250,6 +1251,15 @@ public: handler->SendGlobalGMSysMessage("Vehicle template accessories reloaded."); return true; } + + static bool HandleReloadPhaseDefinitionsCommand(ChatHandler* handler, const char* /*args*/) + { + sLog->outInfo(LOG_FILTER_GENERAL, "Reloading phase_definitions table..."); + sObjectMgr->LoadPhaseDefinitions(); + sWorld->UpdatePhaseDefinitions(); + handler->SendGlobalGMSysMessage("Phase Definitions reloaded."); + return true; + } }; void AddSC_reload_commandscript() diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index e3cd185e4a3..05226fb55aa 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -688,7 +688,7 @@ public: } // re-create Creature* wpCreature2 = new Creature; - if (!wpCreature2->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, 0, 0, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation())) + if (!wpCreature2->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, 0, 0, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation())) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT); delete wpCreature2; @@ -696,7 +696,7 @@ public: return false; } - wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMgr().GetPhaseMaskForSpawn()); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); //TODO: Should we first use "Create" then use "LoadFromDB"? if (!wpCreature2->LoadCreatureFromDB(wpCreature2->GetDBTableGUIDLow(), map)) @@ -912,7 +912,7 @@ public: float o = chr->GetOrientation(); Creature* wpCreature = new Creature; - if (!wpCreature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) + if (!wpCreature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete wpCreature; @@ -928,7 +928,7 @@ public: WorldDatabase.Execute(stmt); - wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMgr().GetPhaseMaskForSpawn()); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); if (!wpCreature->LoadCreatureFromDB(wpCreature->GetDBTableGUIDLow(), map)) { @@ -976,14 +976,14 @@ public: Map* map = chr->GetMap(); Creature* creature = new Creature; - if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) + if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete creature; return false; } - creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMgr().GetPhaseMaskForSpawn()); if (!creature->LoadCreatureFromDB(creature->GetDBTableGUIDLow(), map)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); @@ -1025,14 +1025,14 @@ public: Map* map = chr->GetMap(); Creature* creature = new Creature; - if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) + if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) { handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); delete creature; return false; } - creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMgr().GetPhaseMaskForSpawn()); if (!creature->LoadCreatureFromDB(creature->GetDBTableGUIDLow(), map)) { handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); |