diff --git a/sql/updates/world/4.3.4/2021_06_13_00_world.sql b/sql/updates/world/4.3.4/2021_06_13_00_world.sql new file mode 100644 index 00000000000..0b6cb990c2c --- /dev/null +++ b/sql/updates/world/4.3.4/2021_06_13_00_world.sql @@ -0,0 +1 @@ +DELETE FROM `achievement_criteria_data` WHERE `ScriptName`= 'achievement_dont_need_to_break_eggs'; diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 6bbee347152..9032355c010 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -2472,6 +2472,11 @@ bool AchievementMgr::ConditionsSatisfied(AchievementCriteriaEntry const* crit } } + if (criteria->RequiredWorldStateID != 0) + if (Map* map = referencePlayer->GetMap()) + if (map->GetWorldStateValue(criteria->RequiredWorldStateID) != criteria->RequiredWorldStateValue) + return false; + return true; } diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 11fab570e28..c2b62f8eaf7 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -614,8 +614,8 @@ struct AchievementCriteriaEntry uint32 Timer_asset_ID; // 12 Alway appears with timed events, used internally to start the achievement, store uint32 Timer_time; // 13 time limit in seconds uint32 Ui_order; // 14 also used in achievement shift-links as index in state bitmask - //uint32 unk1; // 15 only one value, still unknown - //uint32 unk2; // 16 all zeros + uint32 RequiredWorldStateID; // 15 + int32 RequiredWorldStateValue; // 16 uint32 AdditionalConditionType[MAX_ADDITIONAL_CRITERIA_CONDITIONS]; // 17-19 uint32 AdditionalConditionValue[MAX_ADDITIONAL_CRITERIA_CONDITIONS]; // 20-22 }; diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 8fade86b74c..2848c82bdef 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -25,7 +25,7 @@ char const Achievementfmt[] = "niixsxiixixxii"; const std::string CustomAchievementfmt = "pppaaaapapaapp"; const std::string CustomAchievementIndex = "ID"; char const AnimKitfmt[] = "nxx"; -char const AchievementCriteriafmt[] = "niiiliiiisiiiiixxiiiiii"; +char const AchievementCriteriafmt[] = "niiiliiiisiiiiiiiiiiiii"; char const AreaTableEntryfmt[] = "niiiiiiiiiisiiiiiffiiiiiii"; char const AreaGroupEntryfmt[] = "niiiiiii"; char const AreaPOIEntryfmt[] = "nxiiiiiiiiixffixixxixx"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 5af7c8a3c63..0caa8ad14ca 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9204,6 +9204,10 @@ void Player::SendInitWorldStates(uint32 zoneId, uint32 areaId) break; } + // insert map world states + if (Map* map = GetMap()) + map->AppendWorldStates(packet.Worldstates); + SendDirectMessage(packet.Write()); SendBGWeekendWorldStates(); SendBattlefieldWorldStates(); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 3d58a172f9a..cfb9e78ebca 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -49,6 +49,7 @@ #include "Weather.h" #include "WeatherMgr.h" #include "World.h" +#include "WorldStatePackets.h" #include #include @@ -4772,3 +4773,37 @@ void Map::UpdateAreaDependentAuras() player->UpdateZoneDependentAuras(player->GetZoneId()); } } + +void Map::SetWorldState(uint32 worldStateId, int32 value, bool withUpdatePacket /*= true*/) +{ + _worldStates[worldStateId] = value; + + if (!withUpdatePacket) + return; + + // Notify all players on the map + WorldPackets::WorldState::UpdateWorldState updateWorldState; + updateWorldState.VariableID = worldStateId; + updateWorldState.Value = value; + updateWorldState.Write(); + + Map::PlayerList const& players = GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()) + player->SendDirectMessage(updateWorldState.GetRawPacket()); +} + +int32 Map::GetWorldStateValue(uint32 worldStateId) const +{ + std::unordered_map::const_iterator itr = _worldStates.find(worldStateId); + if (itr != _worldStates.end()) + return itr->second; + + return 0; +} + +void Map::AppendWorldStates(std::vector& worldStates) +{ + for (std::pair worldState : _worldStates) + worldStates.emplace_back(worldState.first, worldState.second); +} diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 5aea49f9428..93e99e5d605 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -67,6 +67,14 @@ namespace Trinity { struct ObjectUpdater; } namespace G3D { class Plane; } namespace VMAP { enum class ModelIgnoreFlags : uint32; } +namespace WorldPackets +{ + namespace WorldState + { + struct WorldStateInfo; + } +} + struct ScriptAction { ObjectGuid sourceGUID; @@ -853,6 +861,10 @@ class TC_GAME_API Map : public GridRefManager // This will not affect any already-present creatures in the group void SetSpawnGroupInactive(uint32 groupId) { SetSpawnGroupActive(groupId, false); } + // Sets and stores world state values that will be used by the AchievementMgr to check additional criterias that require world state values + void SetWorldState(uint32 worldStateId, int32 value, bool withUpdatePacket = true); + int32 GetWorldStateValue(uint32 worldStateId) const; + void AppendWorldStates(std::vector& worldStates); private: // Type specific code for add/remove to/from grid @@ -939,6 +951,8 @@ class TC_GAME_API Map : public GridRefManager std::unordered_set _corpseBones; std::unordered_set _updateObjects; + + std::unordered_map _worldStates; }; enum InstanceResetMethod diff --git a/src/server/game/Server/Packets/WorldStatePackets.h b/src/server/game/Server/Packets/WorldStatePackets.h index 533938f9400..570e89abf82 100644 --- a/src/server/game/Server/Packets/WorldStatePackets.h +++ b/src/server/game/Server/Packets/WorldStatePackets.h @@ -24,18 +24,18 @@ namespace WorldPackets { namespace WorldState { + struct WorldStateInfo + { + WorldStateInfo(int32 variableID, int32 value) + : VariableID(variableID), Value(value) { } + + int32 VariableID; + int32 Value; + }; + class InitWorldStates final : public ServerPacket { public: - struct WorldStateInfo - { - WorldStateInfo(int32 variableID, int32 value) - : VariableID(variableID), Value(value) { } - - int32 VariableID; - int32 Value; - }; - InitWorldStates(); WorldPacket const* Write() override; diff --git a/src/server/scripts/EasternKingdoms/GrimBatol/boss_erudax.cpp b/src/server/scripts/EasternKingdoms/GrimBatol/boss_erudax.cpp index f5d6992b82e..6e232ebca86 100644 --- a/src/server/scripts/EasternKingdoms/GrimBatol/boss_erudax.cpp +++ b/src/server/scripts/EasternKingdoms/GrimBatol/boss_erudax.cpp @@ -88,13 +88,7 @@ enum Events enum Actions { ACTION_FINISH_CORRUPTION = 0, - ACTION_DESPAWN = 1, - ACTION_FAIL_ACHIEVEMENT = 0 -}; - -enum Data -{ - DATA_ACHIEVEMT_ENLIGIBLE = 1 + ACTION_DESPAWN = 1 }; Position const facelessCorruptorPositions1[] = @@ -140,13 +134,14 @@ enum ModelIds struct boss_erudax : public BossAI { - boss_erudax(Creature* creature) : BossAI(creature, DATA_ERUDAX), _achievementEnligible(true) { } + boss_erudax(Creature* creature) : BossAI(creature, DATA_ERUDAX) { } void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); Talk(SAY_AGGRO); instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); + instance->instance->SetWorldState(WORLD_STATE_ID_DONT_NEED_TO_BREAK_EGGS, 0); events.ScheduleEvent(EVENT_BINDING_SHADOWS, 10s + 500ms); events.ScheduleEvent(EVENT_ENFEEBLING_BLOW, 19s); events.ScheduleEvent(EVENT_SHADOW_GALE, 26s); @@ -176,13 +171,6 @@ struct boss_erudax : public BossAI summons.DespawnAll(); } - - void DoAction(int32 action) override - { - if (action == ACTION_FAIL_ACHIEVEMENT) - _achievementEnligible = false; - } - void JustSummoned(Creature* summon) override { summons.RemoveNotExisting(); // keeping the summon container clean @@ -202,14 +190,6 @@ struct boss_erudax : public BossAI } } - uint32 GetData(uint32 type) const override - { - if (type == DATA_ACHIEVEMT_ENLIGIBLE) - return _achievementEnligible; - - return 0; - } - void OnSpellCastFinished(SpellInfo const* spell, SpellFinishReason reason) override { if (spell->Id == SPELL_ENFEEBLING_BLOW && reason == SPELL_FINISHED_SUCCESSFUL_CAST) @@ -294,7 +274,6 @@ private: summons.DoAction(ACTION_DESPAWN, pred2); } - bool _achievementEnligible; ObjectGuid _shadowGaleStalkerGUID; }; @@ -387,8 +366,8 @@ struct npc_erudax_faceless_corruptor : public ScriptedAI _instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); break; case EVENT_TWILIGHT_CORRUPTION: - if (Creature* erudax = _instance->GetCreature(DATA_ERUDAX)) - erudax->AI()->DoAction(ACTION_FAIL_ACHIEVEMENT); + // Fail achievement by setting the required world state to 1 (0 is required for successful achievement) + _instance->instance->SetWorldState(WORLD_STATE_ID_DONT_NEED_TO_BREAK_EGGS, 1); DoCastAOE(SPELL_TWILIGHT_CORRUPTION); break; case EVENT_UMBRAL_MENDING: @@ -547,23 +526,6 @@ class spell_erudax_twilight_corruption_AuraScript : public AuraScript } }; -class achievement_dont_need_to_break_eggs : public AchievementCriteriaScript -{ - public: - achievement_dont_need_to_break_eggs() : AchievementCriteriaScript("achievement_dont_need_to_break_eggs") { } - - bool OnCheck(Player* /*source*/, Unit* target) - { - if (!target) - return false; - - if (target->GetMap()->IsHeroic()) - return target->GetAI()->GetData(DATA_ACHIEVEMT_ENLIGIBLE); - - return false; - } -}; - void AddSC_boss_erudax() { RegisterGrimBatolCreatureAI(boss_erudax); @@ -572,5 +534,4 @@ void AddSC_boss_erudax() RegisterSpellScript(spell_erudax_shadow_gale); RegisterSpellScript(spell_erudax_shadow_gale_aura); RegisterSpellAndAuraScriptPair(spell_erudax_twilight_corruption, spell_erudax_twilight_corruption_AuraScript); - new achievement_dont_need_to_break_eggs(); } diff --git a/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.h b/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.h index 923c6c5b3ff..fa2763a0e7e 100644 --- a/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.h +++ b/src/server/scripts/EasternKingdoms/GrimBatol/grim_batol.h @@ -92,6 +92,11 @@ enum GBSummonGroups SUMMON_GROUP_BATTERED_DRAKES = 0 }; +enum GBWorldStates +{ + WORLD_STATE_ID_DONT_NEED_TO_BREAK_EGGS = 5117 // Erduax Achievement required world state +}; + template AI* GetGrimBatolAI(T* creature) { diff --git a/src/server/scripts/EasternKingdoms/GrimBatol/instance_grim_batol.cpp b/src/server/scripts/EasternKingdoms/GrimBatol/instance_grim_batol.cpp index 90f83f000f7..83b8686a5b7 100644 --- a/src/server/scripts/EasternKingdoms/GrimBatol/instance_grim_batol.cpp +++ b/src/server/scripts/EasternKingdoms/GrimBatol/instance_grim_batol.cpp @@ -60,6 +60,7 @@ class instance_grim_batol : public InstanceMapScript SetHeaders(DataHeader); SetBossNumber(EncounterCount); LoadObjectData(creatureData, nullptr); + instance->SetWorldState(WORLD_STATE_ID_DONT_NEED_TO_BREAK_EGGS, 0, false); _initialized = false; _destroyedNets = 0; _batteredRedDrakeState = STATE_EMPRISONED;