aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Collision/DynamicTree.cpp2
-rw-r--r--src/common/Collision/Management/MMapFactory.h1
-rw-r--r--src/common/Collision/Maps/MapTree.cpp5
-rw-r--r--src/common/Debugging/WheatyExceptionReport.cpp19
-rw-r--r--src/common/Debugging/WheatyExceptionReport.h4
-rw-r--r--src/common/GitRevision.cpp31
-rw-r--r--src/common/Metric/Metric.cpp235
-rw-r--r--src/common/Metric/Metric.h141
-rw-r--r--src/common/Utilities/Containers.h9
-rw-r--r--src/common/Utilities/EventProcessor.cpp80
-rw-r--r--src/common/Utilities/EventProcessor.h36
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.cpp2
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h2
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp4
-rw-r--r--src/server/database/Updater/DBUpdater.cpp17
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp66
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h23
-rw-r--r--src/server/game/AI/CreatureAI.cpp4
-rw-r--r--src/server/game/AI/CreatureAI.h4
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp1189
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.h64
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp52
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h6
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp83
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp22
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h9
-rw-r--r--src/server/game/Accounts/AccountMgr.cpp9
-rw-r--r--src/server/game/Accounts/RBAC.h7
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp2
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h2
-rw-r--r--src/server/game/Battlefield/Battlefield.cpp9
-rw-r--r--src/server/game/Battlegrounds/ArenaTeamMgr.cpp2
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp19
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp6
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp4
-rw-r--r--src/server/game/Chat/Channels/Channel.cpp296
-rw-r--r--src/server/game/Chat/Channels/Channel.h194
-rw-r--r--src/server/game/Chat/Channels/ChannelMgr.cpp8
-rw-r--r--src/server/game/Chat/Chat.cpp104
-rw-r--r--src/server/game/Chat/Chat.h30
-rw-r--r--src/server/game/Chat/ChatLink.cpp26
-rw-r--r--src/server/game/Chat/ChatLink.h18
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp15
-rw-r--r--src/server/game/Conditions/ConditionMgr.h3
-rw-r--r--src/server/game/DataStores/DBCStores.cpp30
-rw-r--r--src/server/game/DataStores/DBCStores.h2
-rw-r--r--src/server/game/DataStores/M2Stores.cpp2
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp20
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp19
-rw-r--r--src/server/game/Entities/Creature/Creature.h18
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp159
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h62
-rw-r--r--src/server/game/Entities/Item/Container/Bag.h2
-rw-r--r--src/server/game/Entities/Item/Item.h2
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.cpp (renamed from src/server/game/Entities/Item/ItemPrototype.cpp)99
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h (renamed from src/server/game/Entities/Item/ItemPrototype.h)26
-rw-r--r--src/server/game/Entities/Object/Object.cpp76
-rw-r--r--src/server/game/Entities/Object/Object.h11
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp5
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.cpp3
-rw-r--r--src/server/game/Entities/Player/Player.cpp329
-rw-r--r--src/server/game/Entities/Player/Player.h23
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp3
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp818
-rw-r--r--src/server/game/Entities/Unit/Unit.h29
-rwxr-xr-xsrc/server/game/Entities/Vehicle/Vehicle.cpp18
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp186
-rw-r--r--src/server/game/Globals/ObjectMgr.h2
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h7
-rw-r--r--src/server/game/Guilds/Guild.cpp10
-rw-r--r--src/server/game/Handlers/BattleGroundHandler.cpp2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp55
-rw-r--r--src/server/game/Handlers/ChatHandler.cpp72
-rw-r--r--src/server/game/Handlers/GroupHandler.cpp14
-rw-r--r--src/server/game/Handlers/GuildHandler.cpp4
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp2
-rw-r--r--src/server/game/Handlers/LootHandler.cpp9
-rw-r--r--src/server/game/Handlers/NPCHandler.cpp24
-rw-r--r--src/server/game/Handlers/PetHandler.cpp2
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp42
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp2
-rw-r--r--src/server/game/Loot/LootMgr.cpp21
-rw-r--r--src/server/game/Loot/LootMgr.h1
-rw-r--r--src/server/game/Mails/Mail.cpp7
-rw-r--r--src/server/game/Mails/Mail.h1
-rw-r--r--src/server/game/Maps/Map.cpp8
-rw-r--r--src/server/game/Maps/Map.h2
-rw-r--r--src/server/game/Maps/MapManager.h8
-rw-r--r--src/server/game/Miscellaneous/Language.h4
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h72
-rw-r--r--src/server/game/Movement/MotionMaster.cpp14
-rw-r--r--src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp6
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp11
-rw-r--r--src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp18
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp4
-rw-r--r--src/server/game/Movement/PathGenerator.cpp8
-rw-r--r--src/server/game/Movement/PathGenerator.h1
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.cpp2
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.cpp15
-rw-r--r--src/server/game/Quests/QuestDef.cpp36
-rw-r--r--src/server/game/Quests/QuestDef.h5
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp12
-rw-r--r--src/server/game/Scripting/ScriptMgr.h3
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.cpp58
-rw-r--r--src/server/game/Server/WorldSession.cpp38
-rw-r--r--src/server/game/Server/WorldSocket.cpp26
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h4
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp28
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp34
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h10
-rw-r--r--src/server/game/Spells/Spell.cpp188
-rw-r--r--src/server/game/Spells/Spell.h2
-rw-r--r--src/server/game/Spells/SpellEffects.cpp55
-rw-r--r--src/server/game/Spells/SpellInfo.cpp6
-rw-r--r--src/server/game/Spells/SpellMgr.cpp175
-rw-r--r--src/server/game/Spells/SpellMgr.h26
-rw-r--r--src/server/game/World/World.cpp36
-rw-r--r--src/server/game/World/World.h6
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp52
-rw-r--r--src/server/scripts/Commands/cs_gobject.cpp41
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp27
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp4
-rw-r--r--src/server/scripts/Commands/cs_pet.cpp6
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp2
-rw-r--r--src/server/scripts/Commands/cs_ticket.cpp7
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp6
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp3
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp11
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp45
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp14
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp474
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp45
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/karazhan.h3
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp14
-rw-r--r--src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/zone_loch_modan.cpp106
-rw-r--r--src/server/scripts/Events/childrens_week.cpp19
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp12
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp2
-rw-r--r--src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp37
-rw-r--r--src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp2
-rw-r--r--src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp6
-rw-r--r--src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp4
-rw-r--r--src/server/scripts/Kalimdor/zone_felwood.cpp73
-rw-r--r--src/server/scripts/Kalimdor/zone_feralas.cpp62
-rw-r--r--src/server/scripts/Kalimdor/zone_moonglade.cpp131
-rw-r--r--src/server/scripts/Kalimdor/zone_orgrimmar.cpp550
-rw-r--r--src/server/scripts/Kalimdor/zone_silithus.cpp717
-rw-r--r--src/server/scripts/Kalimdor/zone_tanaris.cpp116
-rw-r--r--src/server/scripts/Kalimdor/zone_thousand_needles.cpp184
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp3
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp2
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp580
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp14
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h1
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp6
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp10
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp34
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp74
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp217
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp14
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp2
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp94
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h6
-rw-r--r--src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp4
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp6
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp28
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp1
-rw-r--r--src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp53
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp20
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp6
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp15
-rw-r--r--src/server/scripts/Northrend/Naxxramas/naxxramas.h6
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp19
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp6
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp38
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp24
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp8
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp10
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp2
-rw-r--r--src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp1
-rw-r--r--src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp2
-rw-r--r--src/server/scripts/Northrend/zone_borean_tundra.cpp7
-rw-r--r--src/server/scripts/Northrend/zone_grizzly_hills.cpp6
-rw-r--r--src/server/scripts/Northrend/zone_sholazar_basin.cpp6
-rw-r--r--src/server/scripts/Northrend/zone_storm_peaks.cpp10
-rw-r--r--src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp4
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp16
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_illidan.cpp492
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp4
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp17
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp1211
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp32
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp2
-rw-r--r--src/server/scripts/Outland/BlackTemple/illidari_council.cpp15
-rw-r--r--src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp15
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp170
-rw-r--r--src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp2
-rw-r--r--src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp2
-rw-r--r--src/server/scripts/Outland/zone_blades_edge_mountains.cpp2
-rw-r--r--src/server/scripts/Outland/zone_netherstorm.cpp53
-rw-r--r--src/server/scripts/Outland/zone_shadowmoon_valley.cpp125
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp192
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp231
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp58
-rw-r--r--src/server/scripts/Spells/spell_holiday.cpp168
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp153
-rw-r--r--src/server/scripts/Spells/spell_item.cpp405
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp38
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp57
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp46
-rw-r--r--src/server/scripts/Spells/spell_quest.cpp42
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp38
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp109
-rw-r--r--src/server/scripts/Spells/spell_warlock.cpp98
-rw-r--r--src/server/scripts/World/go_scripts.cpp24
-rw-r--r--src/server/scripts/World/item_scripts.cpp2
-rw-r--r--src/server/scripts/World/npcs_special.cpp412
-rw-r--r--src/server/worldserver/Main.cpp11
-rw-r--r--src/server/worldserver/worldserver.conf.dist90
238 files changed, 9196 insertions, 5306 deletions
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp
index 5d2e18b1a2e..111606644de 100644
--- a/src/common/Collision/DynamicTree.cpp
+++ b/src/common/Collision/DynamicTree.cpp
@@ -248,7 +248,7 @@ bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, flo
float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
{
- G3D::Vector3 v(x, y, z);
+ G3D::Vector3 v(x, y, z + 0.5f);
G3D::Ray r(v, G3D::Vector3(0, 0, -1));
DynamicTreeIntersectionCallback callback(phasemask);
impl->intersectZAllignedRay(r, callback, maxSearchDist);
diff --git a/src/common/Collision/Management/MMapFactory.h b/src/common/Collision/Management/MMapFactory.h
index 6dda7a40a22..f89f44ba4df 100644
--- a/src/common/Collision/Management/MMapFactory.h
+++ b/src/common/Collision/Management/MMapFactory.h
@@ -43,7 +43,6 @@ namespace MMAP
public:
static MMapManager* createOrGetMMapManager();
static void clear();
- static bool IsPathfindingEnabled(uint32 mapId);
};
}
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp
index 4d0996e1961..5907971efb9 100644
--- a/src/common/Collision/Maps/MapTree.cpp
+++ b/src/common/Collision/Maps/MapTree.cpp
@@ -22,6 +22,7 @@
#include "VMapDefinitions.h"
#include "Log.h"
#include "Errors.h"
+#include "Metric.h"
#include <string>
#include <sstream>
@@ -415,6 +416,8 @@ namespace VMAP
}
else
iLoadedTiles[packTileID(tileX, tileY)] = false;
+ TC_METRIC_EVENT("map_events", "LoadMapTile",
+ "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
return result;
}
@@ -473,6 +476,8 @@ namespace VMAP
}
}
iLoadedTiles.erase(tile);
+ TC_METRIC_EVENT("map_events", "UnloadMapTile",
+ "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
}
void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
diff --git a/src/common/Debugging/WheatyExceptionReport.cpp b/src/common/Debugging/WheatyExceptionReport.cpp
index 5b9a1b1bd6c..72abec161b9 100644
--- a/src/common/Debugging/WheatyExceptionReport.cpp
+++ b/src/common/Debugging/WheatyExceptionReport.cpp
@@ -901,7 +901,7 @@ unsigned /*cbBuffer*/)
// will return true.
bool bHandled;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, pSym->ModBase, pSym->TypeIndex,
- 0, pVariable, bHandled, pSym->Name, "", false, true);
+ pVariable, bHandled, pSym->Name, "", false, true);
if (!bHandled)
{
@@ -934,7 +934,6 @@ char * WheatyExceptionReport::DumpTypeIndex(
char * pszCurrBuffer,
DWORD64 modBase,
DWORD dwTypeIndex,
-unsigned nestingLevel,
DWORD_PTR offset,
bool & bHandled,
const char* Name,
@@ -1022,14 +1021,14 @@ bool logChildren)
FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer));
symbolDetails.top().Value = buffer;
- if (nestingLevel >= WER_MAX_NESTING_LEVEL)
+ if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL)
logChildren = false;
// no need to log any children since the address is invalid anyway
if (address == NULL || address == DWORD_PTR(-1))
logChildren = false;
- pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID,
address, bHandled, Name, addressStr, false, logChildren);
if (!bHandled)
@@ -1074,19 +1073,19 @@ bool logChildren)
switch (innerTypeTag)
{
case SymTagUDT:
- if (nestingLevel >= WER_MAX_NESTING_LEVEL)
+ if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL)
logChildren = false;
- pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID,
offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
break;
case SymTagPointerType:
if (Name != NULL && Name[0] != '\0')
symbolDetails.top().Name = Name;
- pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID,
offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
break;
case SymTagArrayType:
- pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID,
offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
break;
default:
@@ -1100,7 +1099,7 @@ bool logChildren)
symbolDetails.top().HasChildren = true;
BasicType basicType = btNoType;
- pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1,
+ pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID,
offset, bHandled, Name, "", false, false);
// Set Value back to an empty string since the Array object itself has no value, only its elements have
@@ -1222,7 +1221,7 @@ bool logChildren)
DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase,
- children.ChildId[i], nestingLevel+1,
+ children.ChildId[i],
dwFinalOffset, bHandled2, ""/*Name */, "", true, true);
// If the child wasn't a UDT, format it appropriately
diff --git a/src/common/Debugging/WheatyExceptionReport.h b/src/common/Debugging/WheatyExceptionReport.h
index eb62d8bceef..34919b19e01 100644
--- a/src/common/Debugging/WheatyExceptionReport.h
+++ b/src/common/Debugging/WheatyExceptionReport.h
@@ -13,7 +13,7 @@
#define countof _countof
#define WER_MAX_ARRAY_ELEMENTS_COUNT 10
-#define WER_MAX_NESTING_LEVEL 5
+#define WER_MAX_NESTING_LEVEL 4
#define WER_LARGE_BUFFER_SIZE 1024 * 128
enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK
@@ -173,7 +173,7 @@ class WheatyExceptionReport
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *, char * pszBuffer, unsigned cbBuffer);
- static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, const char*, char*, bool, bool);
+ static char * DumpTypeIndex(char *, DWORD64, DWORD, DWORD_PTR, bool &, const char*, char*, bool, bool);
static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride = 0);
diff --git a/src/common/GitRevision.cpp b/src/common/GitRevision.cpp
index 2162e36847f..702cd01d84b 100644
--- a/src/common/GitRevision.cpp
+++ b/src/common/GitRevision.cpp
@@ -41,19 +41,30 @@ char const* GitRevision::GetFullDatabase()
return _FULL_DATABASE;
}
-#define _PACKAGENAME "TrinityCore"
-
-char const* GitRevision::GetFullVersion()
-{
#if PLATFORM == PLATFORM_WINDOWS
-# ifdef _WIN64
- return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Win64, " _BUILD_DIRECTIVE ")";
-# else
- return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Win32, " _BUILD_DIRECTIVE ")";
-# endif
+# ifdef _WIN64
+# define TRINITY_PLATFORM_STR "Win64"
+# else
+# define TRINITY_PLATFORM_STR "Win32"
+# endif
+#elif PLATFORM == PLATFORM_APPLE
+# define TRINITY_PLATFORM_STR "MacOSX"
+#elif PLATFORM == PLATFORM_INTEL
+# define TRINITY_PLATFORM_STR "Intel"
+#else // PLATFORM_UNIX
+# define TRINITY_PLATFORM_STR "Unix"
+#endif
+
+#ifndef TRINITY_API_USE_DYNAMIC_LINKING
+# define TRINITY_LINKAGE_TYPE_STR "Static"
#else
- return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Unix, " _BUILD_DIRECTIVE ")";
+# define TRINITY_LINKAGE_TYPE_STR "Dynamic"
#endif
+
+char const* GitRevision::GetFullVersion()
+{
+ return "TrinityCore rev. " VER_PRODUCTVERSION_STR
+ " (" TRINITY_PLATFORM_STR ", " _BUILD_DIRECTIVE ", " TRINITY_LINKAGE_TYPE_STR ")";
}
char const* GitRevision::GetCompanyNameStr()
diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp
new file mode 100644
index 00000000000..9484cebcc72
--- /dev/null
+++ b/src/common/Metric/Metric.cpp
@@ -0,0 +1,235 @@
+/*
+* 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 "Metric.h"
+#include "Log.h"
+#include "Config.h"
+#include "Util.h"
+
+void Metric::Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger)
+{
+ _realmName = realmName;
+ _batchTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
+ _overallStatusTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
+ _overallStatusLogger = overallStatusLogger;
+ LoadFromConfigs();
+}
+
+bool Metric::Connect()
+{
+ _dataStream.connect(_hostname, _port);
+ auto error = _dataStream.error();
+ if (error)
+ {
+ TC_LOG_ERROR("metric", "Error connecting to '%s:%s', disabling Metric. Error message : %s",
+ _hostname.c_str(), _port.c_str(), error.message().c_str());
+ _enabled = false;
+ return false;
+ }
+ _dataStream.clear();
+ return true;
+}
+
+void Metric::LoadFromConfigs()
+{
+ bool previousValue = _enabled;
+ _enabled = sConfigMgr->GetBoolDefault("Metric.Enable", false);
+ _updateInterval = sConfigMgr->GetIntDefault("Metric.Interval", 10);
+ if (_updateInterval < 1)
+ {
+ TC_LOG_ERROR("metric", "'Metric.Interval' config set to %d, overriding to 1.", _updateInterval);
+ _updateInterval = 1;
+ }
+
+ _overallStatusTimerInterval = sConfigMgr->GetIntDefault("Metric.OverallStatusInterval", 1);
+ if (_overallStatusTimerInterval < 1)
+ {
+ TC_LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to %d, overriding to 1.", _overallStatusTimerInterval);
+ _overallStatusTimerInterval = 1;
+ }
+
+ // Schedule a send at this point only if the config changed from Disabled to Enabled.
+ // Cancel any scheduled operation if the config changed from Enabled to Disabled.
+ if (_enabled && !previousValue)
+ {
+ std::string connectionInfo = sConfigMgr->GetStringDefault("Metric.ConnectionInfo", "");
+ if (connectionInfo.empty())
+ {
+ TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file.");
+ return;
+ }
+
+ Tokenizer tokens(connectionInfo, ';');
+ if (tokens.size() != 3)
+ {
+ TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file.");
+ return;
+ }
+
+ _hostname.assign(tokens[0]);
+ _port.assign(tokens[1]);
+ _databaseName.assign(tokens[2]);
+ Connect();
+
+ ScheduleSend();
+ ScheduleOverallStatusLog();
+ }
+}
+
+void Metric::Update()
+{
+ if (_overallStatusTimerTriggered)
+ {
+ _overallStatusTimerTriggered = false;
+ _overallStatusLogger();
+ }
+}
+
+void Metric::LogEvent(std::string const& category, std::string const& title, std::string const& description)
+{
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_EVENT;
+ data->Title = title;
+ data->Text = description;
+
+ _queuedData.Enqueue(data);
+}
+
+void Metric::SendBatch()
+{
+ using namespace std::chrono;
+
+ std::stringstream batchedData;
+ MetricData* data;
+ bool firstLoop = true;
+ while (_queuedData.Dequeue(data))
+ {
+ if (!firstLoop)
+ batchedData << "\n";
+
+ batchedData << data->Category;
+ if (!_realmName.empty())
+ batchedData << ",realm=" << _realmName;
+
+ batchedData << " ";
+
+ switch (data->Type)
+ {
+ case METRIC_DATA_VALUE:
+ batchedData << "value=" << data->Value;
+ break;
+ case METRIC_DATA_EVENT:
+ batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
+ break;
+ }
+
+ batchedData << " ";
+
+ batchedData << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count());
+
+ firstLoop = false;
+ delete data;
+ }
+
+ // Check if there's any data to send
+ if (batchedData.tellp() == std::streampos(0))
+ {
+ ScheduleSend();
+ return;
+ }
+
+ if (!_dataStream.good() && !Connect())
+ return;
+
+ _dataStream << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
+ _dataStream << "Host: " << _hostname << ":" << _port << "\r\n";
+ _dataStream << "Accept: */*\r\n";
+ _dataStream << "Content-Type: application/octet-stream\r\n";
+ _dataStream << "Content-Transfer-Encoding: binary\r\n";
+
+ _dataStream << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
+ _dataStream << batchedData.rdbuf();
+
+ std::string http_version;
+ _dataStream >> http_version;
+ unsigned int status_code = 0;
+ _dataStream >> status_code;
+ if (status_code != 204)
+ {
+ TC_LOG_ERROR("metric", "Error sending data, returned HTTP code: %u", status_code);
+ }
+
+ // Read and ignore the status description
+ std::string status_description;
+ std::getline(_dataStream, status_description);
+ // Read headers
+ std::string header;
+ while (std::getline(_dataStream, header) && header != "\r")
+ {
+ if (header == "Connection: close\r")
+ _dataStream.close();
+ }
+
+ ScheduleSend();
+}
+
+void Metric::ScheduleSend()
+{
+ if (_enabled)
+ {
+ _batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
+ _batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
+ }
+ else
+ {
+ _dataStream.close();
+ MetricData* data;
+ // Clear the queue
+ while (_queuedData.Dequeue(data))
+ ;
+ }
+}
+
+void Metric::ForceSend()
+{
+ // Send what's queued only if io_service is stopped (so only on shutdown)
+ if (_enabled && _batchTimer->get_io_service().stopped())
+ SendBatch();
+}
+
+void Metric::ScheduleOverallStatusLog()
+{
+ if (_enabled)
+ {
+ _overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval));
+ _overallStatusTimer->async_wait([this](const boost::system::error_code&)
+ {
+ _overallStatusTimerTriggered = true;
+ ScheduleOverallStatusLog();
+ });
+ }
+}
+
+Metric* Metric::instance()
+{
+ static Metric instance;
+ return &instance;
+}
diff --git a/src/common/Metric/Metric.h b/src/common/Metric/Metric.h
new file mode 100644
index 00000000000..1855e1d0098
--- /dev/null
+++ b/src/common/Metric/Metric.h
@@ -0,0 +1,141 @@
+/*
+* 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 METRIC_H__
+#define METRIC_H__
+
+#include "Common.h"
+#include "Threading/MPSCQueue.h"
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/algorithm/string.hpp>
+#include <type_traits>
+
+enum MetricDataType
+{
+ METRIC_DATA_VALUE,
+ METRIC_DATA_EVENT
+};
+
+struct MetricData
+{
+ std::string Category;
+ std::chrono::time_point<std::chrono::system_clock> Timestamp;
+ MetricDataType Type;
+
+ // LogValue-specific fields
+ std::string Value;
+
+ // LogEvent-specific fields
+ std::string Title;
+ std::string Text;
+};
+
+class TC_COMMON_API Metric
+{
+private:
+ boost::asio::ip::tcp::iostream _dataStream;
+ MPSCQueue<MetricData> _queuedData;
+ std::unique_ptr<boost::asio::deadline_timer> _batchTimer;
+ std::unique_ptr<boost::asio::deadline_timer> _overallStatusTimer;
+ int32 _updateInterval = 0;
+ int32 _overallStatusTimerInterval = 0;
+ bool _enabled = false;
+ bool _overallStatusTimerTriggered = false;
+ std::string _hostname;
+ std::string _port;
+ std::string _databaseName;
+ std::function<void()> _overallStatusLogger;
+ std::string _realmName;
+
+ bool Connect();
+ void SendBatch();
+ void ScheduleSend();
+ void ScheduleOverallStatusLog();
+
+ template<class T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+ static std::string FormatInfluxDBValue(T value) { return std::to_string(value) + 'i'; }
+
+ static std::string FormatInfluxDBValue(std::string const& value)
+ {
+ return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
+ }
+
+ static std::string FormatInfluxDBValue(bool value) { return value ? "t" : "f"; }
+ static std::string FormatInfluxDBValue(const char* value) { return FormatInfluxDBValue(std::string(value)); }
+ static std::string FormatInfluxDBValue(double value) { return std::to_string(value); }
+ static std::string FormatInfluxDBValue(float value) { return FormatInfluxDBValue(double(value)); }
+
+public:
+ static Metric* instance();
+
+ void Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger = [](){});
+ void LoadFromConfigs();
+ void Update();
+
+ template<class T>
+ void LogValue(std::string const& category, T value)
+ {
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_VALUE;
+ data->Value = FormatInfluxDBValue(value);
+
+ _queuedData.Enqueue(data);
+ }
+
+ void LogEvent(std::string const& category, std::string const& title, std::string const& description);
+
+ void ForceSend();
+ bool IsEnabled() const { return _enabled; }
+};
+
+#define sMetric Metric::instance()
+
+#if PLATFORM != PLATFORM_WINDOWS
+#define TC_METRIC_EVENT(category, title, description) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogEvent(category, title, description); \
+ } while (0)
+#define TC_METRIC_VALUE(category, value) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogValue(category, value); \
+ } while (0)
+#else
+#define TC_METRIC_EVENT(category, title, description) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogEvent(category, title, description); \
+ } while (0) \
+ __pragma(warning(pop))
+#define TC_METRIC_VALUE(category, value) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogValue(category, value); \
+ } while (0) \
+ __pragma(warning(pop))
+#endif
+
+#endif // METRIC_H__
diff --git a/src/common/Utilities/Containers.h b/src/common/Utilities/Containers.h
index 0f83b52f9d0..5edb245fd87 100644
--- a/src/common/Utilities/Containers.h
+++ b/src/common/Utilities/Containers.h
@@ -31,9 +31,9 @@ namespace Trinity
namespace Containers
{
template<class T>
- void RandomResizeList(std::list<T> &list, uint32 size)
+ void RandomResizeList(std::list<T>& list, uint32 size)
{
- size_t list_size = list.size();
+ uint32 list_size = uint32(list.size());
while (list_size > size)
{
@@ -56,7 +56,7 @@ namespace Trinity
if (size)
RandomResizeList(listCopy, size);
- list = listCopy;
+ list = std::move(listCopy);
}
/*
@@ -68,7 +68,7 @@ namespace Trinity
typename C::value_type const& SelectRandomContainerElement(C const& container)
{
typename C::const_iterator it = container.begin();
- std::advance(it, urand(0, container.size() - 1));
+ std::advance(it, urand(0, uint32(container.size()) - 1));
return *it;
}
@@ -170,7 +170,6 @@ namespace Trinity
++itr;
}
}
-
}
//! namespace Containers
}
diff --git a/src/common/Utilities/EventProcessor.cpp b/src/common/Utilities/EventProcessor.cpp
index be74d58b790..2341d0a0872 100644
--- a/src/common/Utilities/EventProcessor.cpp
+++ b/src/common/Utilities/EventProcessor.cpp
@@ -17,11 +17,20 @@
*/
#include "EventProcessor.h"
+#include "Errors.h"
-EventProcessor::EventProcessor()
+void BasicEvent::ScheduleAbort()
{
- m_time = 0;
- m_aborting = false;
+ ASSERT(IsRunning()
+ && "Tried to scheduled the abortion of an event twice!");
+ m_abortState = AbortState::STATE_ABORT_SCHEDULED;
+}
+
+void BasicEvent::SetAborted()
+{
+ ASSERT(!IsAborted()
+ && "Tried to abort an already aborted event!");
+ m_abortState = AbortState::STATE_ABORTED;
}
EventProcessor::~EventProcessor()
@@ -39,55 +48,73 @@ void EventProcessor::Update(uint32 p_time)
while (((i = m_events.begin()) != m_events.end()) && i->first <= m_time)
{
// get and remove event from queue
- BasicEvent* Event = i->second;
+ BasicEvent* event = i->second;
m_events.erase(i);
- if (!Event->to_Abort)
+ if (event->IsRunning())
{
- if (Event->Execute(m_time, p_time))
+ if (event->Execute(m_time, p_time))
{
// completely destroy event if it is not re-added
- delete Event;
+ delete event;
}
+ continue;
}
- else
+
+ if (event->IsAbortScheduled())
{
- Event->Abort(m_time);
- delete Event;
+ event->Abort(m_time);
+ // Mark the event as aborted
+ event->SetAborted();
}
+
+ if (event->IsDeletable())
+ {
+ delete event;
+ continue;
+ }
+
+ // Reschedule non deletable events to be checked at
+ // the next update tick
+ AddEvent(event, CalculateTime(1), false);
}
}
void EventProcessor::KillAllEvents(bool force)
{
- // prevent event insertions
- m_aborting = true;
-
- // first, abort all existing events
- for (EventList::iterator i = m_events.begin(); i != m_events.end();)
+ for (auto itr = m_events.begin(); itr != m_events.end();)
{
- EventList::iterator i_old = i;
- ++i;
-
- i_old->second->to_Abort = true;
- i_old->second->Abort(m_time);
- if (force || i_old->second->IsDeletable())
+ // Abort events which weren't aborted already
+ if (!itr->second->IsAborted())
{
- delete i_old->second;
+ itr->second->SetAborted();
+ itr->second->Abort(m_time);
+ }
- if (!force) // need per-element cleanup
- m_events.erase (i_old);
+ // Skip non-deletable events when we are
+ // not forcing the event cancellation.
+ if (!force && !itr->second->IsDeletable())
+ {
+ ++itr;
+ continue;
}
+
+ delete itr->second;
+
+ if (force)
+ ++itr; // Clear the whole container when forcing
+ else
+ itr = m_events.erase(itr);
}
- // fast clear event list (in force case)
if (force)
m_events.clear();
}
void EventProcessor::AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime)
{
- if (set_addtime) Event->m_addTime = m_time;
+ if (set_addtime)
+ Event->m_addTime = m_time;
Event->m_execTime = e_time;
m_events.insert(std::pair<uint64, BasicEvent*>(e_time, Event));
}
@@ -96,4 +123,3 @@ uint64 EventProcessor::CalculateTime(uint64 t_offset) const
{
return(m_time + t_offset);
}
-
diff --git a/src/common/Utilities/EventProcessor.h b/src/common/Utilities/EventProcessor.h
index e10558e6a21..57f3065f323 100644
--- a/src/common/Utilities/EventProcessor.h
+++ b/src/common/Utilities/EventProcessor.h
@@ -20,20 +20,27 @@
#define __EVENTPROCESSOR_H
#include "Define.h"
-
#include <map>
+class EventProcessor;
+
// Note. All times are in milliseconds here.
class TC_COMMON_API BasicEvent
{
+ friend class EventProcessor;
+
+ enum class AbortState : uint8
+ {
+ STATE_RUNNING,
+ STATE_ABORT_SCHEDULED,
+ STATE_ABORTED
+ };
+
public:
BasicEvent()
- {
- to_Abort = false;
- m_addTime = 0;
- m_execTime = 0;
- }
+ : m_abortState(AbortState::STATE_RUNNING), m_addTime(0), m_execTime(0) { }
+
virtual ~BasicEvent() { } // override destructor to perform some actions on event removal
// this method executes when the event is triggered
@@ -45,8 +52,16 @@ class TC_COMMON_API BasicEvent
virtual void Abort(uint64 /*e_time*/) { } // this method executes when the event is aborted
- bool to_Abort; // set by externals when the event is aborted, aborted events don't execute
- // and get Abort call when deleted
+ // Aborts the event at the next update tick
+ void ScheduleAbort();
+
+ private:
+ void SetAborted();
+ bool IsRunning() const { return (m_abortState == AbortState::STATE_RUNNING); }
+ bool IsAbortScheduled() const { return (m_abortState == AbortState::STATE_ABORT_SCHEDULED); }
+ bool IsAborted() const { return (m_abortState == AbortState::STATE_ABORTED); }
+
+ AbortState m_abortState; // set by externals when the event is aborted, aborted events don't execute
// these can be used for time offset control
uint64 m_addTime; // time when the event was added to queue, filled by event handler
@@ -58,16 +73,17 @@ typedef std::multimap<uint64, BasicEvent*> EventList;
class TC_COMMON_API EventProcessor
{
public:
- EventProcessor();
+ EventProcessor() : m_time(0) { }
~EventProcessor();
void Update(uint32 p_time);
void KillAllEvents(bool force);
void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true);
uint64 CalculateTime(uint64 t_offset) const;
+
protected:
uint64 m_time;
EventList m_events;
- bool m_aborting;
};
+
#endif
diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp
index ba2a4256919..6fc4dc21a51 100644
--- a/src/server/database/Database/DatabaseWorkerPool.cpp
+++ b/src/server/database/Database/DatabaseWorkerPool.cpp
@@ -28,7 +28,7 @@ DatabaseWorkerPool<T>::DatabaseWorkerPool()
{
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1");
- WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).",
+ WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s). Search on forum for TCE00011.",
mysql_get_client_info(), MYSQL_SERVER_VERSION);
}
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index ce01b0eb245..bb0e2bb09d4 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -56,7 +56,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_RACE, "SELECT race FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_LEVEL, "SELECT level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level, name FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH);
@@ -157,11 +157,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_GIFT, "DELETE FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ACCOUNT_BY_NAME, "SELECT account FROM characters WHERE name = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID, "SELECT account, name, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, "DELETE FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_NAME_CLASS, "SELECT name, class FROM characters WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHARACTER_NAME, "SELECT name FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT account, COUNT(guid) FROM characters WHERE account = ? GROUP BY account", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 430243a8f1e..0cac6d35b55 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -146,13 +146,11 @@ enum CharacterDatabaseStatements
CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES,
CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES,
CHAR_SEL_CHARACTER_NAME_CLASS,
- CHAR_SEL_CHARACTER_NAME,
CHAR_SEL_MATCH_MAKER_RATING,
CHAR_SEL_CHARACTER_COUNT,
CHAR_UPD_NAME,
CHAR_UPD_NAME_BY_GUID,
CHAR_DEL_DECLINED_NAME,
- CHAR_SEL_CHARACTER_DATA_BY_GUID,
CHAR_INS_GUILD,
CHAR_DEL_GUILD,
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index 7a183d5bf78..83720c1a996 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -30,8 +30,8 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_SMARTAI_WP, "SELECT entry, pointid, position_x, position_y, position_z FROM waypoints ORDER BY entry, pointid", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_GAMEOBJECT, "DELETE FROM gameobject WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_EVENT_GAMEOBJECT, "DELETE FROM game_event_gameobject WHERE guid = ?", CONNECTION_ASYNC);
- PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO game_graveyard_zone (id, ghost_zone, faction) VALUES (?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM game_graveyard_zone WHERE id = ? AND ghost_zone = ? AND faction = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO graveyard_zone (ID, GhostZone, Faction) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM graveyard_zone WHERE ID = ? AND GhostZone = ? AND Faction = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_INS_GAME_TELE, "INSERT INTO game_tele (id, position_x, position_y, position_z, orientation, map, name) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_GAME_TELE, "DELETE FROM game_tele WHERE name = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_INS_NPC_VENDOR, "INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index 0d51f8ed060..8515da9f6f8 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -55,7 +55,7 @@ bool DBUpdaterUtil::CheckExecutable()
}
}
- TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
+ TC_LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
absolute(exe).generic_string().c_str());
return false;
@@ -178,7 +178,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
// Path of temp file
static Path const temp("create_table.sql");
- // Create temporary query to use external mysql cli
+ // Create temporary query to use external MySQL CLi
std::ofstream file(temp.generic_string());
if (!file.is_open())
{
@@ -197,7 +197,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
}
catch (UpdateException&)
{
- TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str());
+ TC_LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str());
boost::filesystem::remove(temp);
return false;
}
@@ -280,7 +280,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
{
case LOCATION_REPOSITORY:
{
- TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.",
+ TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing. Try fixing it by cloning the source again.",
base.generic_string().c_str());
break;
@@ -288,7 +288,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
case LOCATION_DOWNLOAD:
{
TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"https://github.com/TrinityCore/TrinityCore/releases\"" \
- " and place it in your server directory.", base.filename().generic_string().c_str());
+ " uncompress it and place the file TDB_full_world_(a_variable_name).sql in your worldserver directory.", base.filename().generic_string().c_str());
break;
}
}
@@ -355,7 +355,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!std::isdigit(port_or_socket[0]))
{
- // We can't check here if host == "." because is named localhost if socket option is enabled
+ // We can't check if host == "." here, because it is named localhost if socket option is enabled
args.push_back("-P0");
args.push_back("--protocol=SOCKET");
args.push_back("-S" + port_or_socket);
@@ -383,7 +383,10 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (ret != EXIT_SUCCESS)
{
TC_LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \
- " If you are an user pull the latest revision from the repository. If you are a developer fix your sql query.",
+ " If you are a user, please pull the latest revision from the repository. "
+ "Also make sure you have not applied any of the databases with your sql client. "
+ "You cannot use auto-update system and import sql files from TrinityCore repository with your sql client. "
+ "If you are a developer, please fix your sql query.",
path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str());
throw UpdateException("update failed");
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index 844bd45ffeb..037d9e59cf2 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -267,35 +267,59 @@ SpellTargetSelector::SpellTargetSelector(Unit* caster, uint32 spellId) :
bool SpellTargetSelector::operator()(Unit const* target) const
{
- if (!target)
- return false;
-
- if (_spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK)
+ if (!target || _spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK)
return false;
// copypasta from Spell::CheckRange
- uint32 range_type = _spellInfo->RangeEntry ? _spellInfo->RangeEntry->type : 0;
- float max_range = _caster->GetSpellMaxRangeForTarget(target, _spellInfo);
- float min_range = _caster->GetSpellMinRangeForTarget(target, _spellInfo);
-
-
- if (target && target != _caster)
+ float minRange = 0.0f;
+ float maxRange = 0.0f;
+ float rangeMod = 0.0f;
+ if (_spellInfo->RangeEntry)
{
- if (range_type == SPELL_RANGE_MELEE)
+ if (_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE)
{
- // Because of lag, we can not check too strictly here.
- if (!_caster->IsWithinMeleeRange(target, max_range))
- return false;
- }
- else if (!_caster->IsWithinCombatRange(target, max_range))
- return false;
+ rangeMod = _caster->GetCombatReach() + 4.0f / 3.0f;
+ rangeMod += target->GetCombatReach();
- if (range_type == SPELL_RANGE_RANGED)
+ rangeMod = std::max(rangeMod, NOMINAL_MELEE_RANGE);
+ }
+ else
{
- if (_caster->IsWithinMeleeRange(target))
- return false;
+ float meleeRange = 0.0f;
+ if (_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED)
+ {
+ meleeRange = _caster->GetCombatReach() + 4.0f / 3.0f;
+ meleeRange += target->GetCombatReach();
+
+ meleeRange = std::max(meleeRange, NOMINAL_MELEE_RANGE);
+ }
+
+ minRange = _caster->GetSpellMinRangeForTarget(target, _spellInfo) + meleeRange;
+ maxRange = _caster->GetSpellMaxRangeForTarget(target, _spellInfo);
+
+ rangeMod = _caster->GetCombatReach();
+ rangeMod += target->GetCombatReach();
+
+ if (minRange > 0.0f && !(_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED))
+ minRange += rangeMod;
}
- else if (min_range && _caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0
+
+ if (_caster->isMoving() && target->isMoving() && !_caster->IsWalking() && !target->IsWalking() &&
+ (_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER))
+ rangeMod += 5.0f / 3.0f;
+ }
+
+ maxRange += rangeMod;
+
+ minRange *= minRange;
+ maxRange *= maxRange;
+
+ if (target != _caster)
+ {
+ if (_caster->GetExactDistSq(target) > maxRange)
+ return false;
+
+ if (minRange > 0.0f && _caster->GetExactDistSq(target) < minRange)
return false;
}
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index 344ccc06249..0dd09bc8051 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -148,15 +148,29 @@ class TC_GAME_API UnitAI
{
ThreatContainer::StorageType const& threatlist = me->getThreatManager().getThreatList();
if (position >= threatlist.size())
- return NULL;
+ return nullptr;
std::list<Unit*> targetList;
+ Unit* currentVictim = nullptr;
+ if (auto currentVictimReference = me->getThreatManager().getCurrentVictim())
+ {
+ currentVictim = currentVictimReference->getTarget();
+
+ // Current victim always goes first
+ if (currentVictim && predicate(currentVictim))
+ targetList.push_back(currentVictim);
+ }
+
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
- if (predicate((*itr)->getTarget()))
+ {
+ if (currentVictim != nullptr && (*itr)->getTarget() != currentVictim && predicate((*itr)->getTarget()))
targetList.push_back((*itr)->getTarget());
+ else if (currentVictim == nullptr && predicate((*itr)->getTarget()))
+ targetList.push_back((*itr)->getTarget());
+ }
if (position >= targetList.size())
- return NULL;
+ return nullptr;
if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
targetList.sort(Trinity::ObjectDistanceOrderPred(me));
@@ -187,7 +201,7 @@ class TC_GAME_API UnitAI
break;
}
- return NULL;
+ return nullptr;
}
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
@@ -242,6 +256,7 @@ class TC_GAME_API UnitAI
void DoAddAuraToAllHostilePlayers(uint32 spellid);
void DoCast(uint32 spellId);
void DoCast(Unit* victim, uint32 spellId, bool triggered = false);
+ void DoCastSelf(uint32 spellId, bool triggered = false) { DoCast(me, spellId, triggered); }
void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false);
void DoCastVictim(uint32 spellId, bool triggered = false);
void DoCastAOE(uint32 spellId, bool triggered = false);
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index d52306b8a2e..15bbff2793f 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -46,7 +46,7 @@ void CreatureAI::Talk(uint8 id, WorldObject const* whisperTarget /*= nullptr*/)
sCreatureTextMgr->SendChat(me, id, whisperTarget);
}
-void CreatureAI::DoZoneInCombat(Creature* creature /*= NULL*/, float maxRangeToNearestTarget /* = 50.0f*/)
+void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRangeToNearestTarget /* = 250.0f*/)
{
if (!creature)
creature = me;
@@ -265,7 +265,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
me->DeleteThreatList();
me->CombatStop(true);
me->LoadCreaturesAddon();
- me->SetLootRecipient(NULL);
+ me->SetLootRecipient(nullptr);
me->ResetPlayerDamageReq();
me->SetLastDamagedTime(0);
me->SetCannotReachTarget(false);
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index 31b53b888a6..f8fa6ba532b 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -112,7 +112,7 @@ class TC_GAME_API CreatureAI : public UnitAI
// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
- // Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
+ // Called for reaction at enter to combat if not in combat yet (enemy can be nullptr)
virtual void EnterCombat(Unit* /*victim*/) { }
// Called when the creature is killed
@@ -149,7 +149,7 @@ class TC_GAME_API CreatureAI : public UnitAI
// Called at reaching home after evade
virtual void JustReachedHome() { }
- void DoZoneInCombat(Creature* creature = NULL, float maxRangeToNearestTarget = 50.0f);
+ void DoZoneInCombat(Creature* creature = nullptr, float maxRangeToNearestTarget = 250.0f);
// Called at text emote receive from player
virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) { }
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 12bfb86251b..d5c17cce3ad 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -19,6 +19,51 @@
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
+static const uint8 NUM_TALENT_TREES = 3;
+static const uint8 NUM_SPEC_ICONICS = 3;
+
+enum Specs
+{
+ SPEC_WARRIOR_ARMS = 0,
+ SPEC_WARRIOR_FURY = 1,
+ SPEC_WARRIOR_PROTECTION = 2,
+
+ SPEC_PALADIN_HOLY = 0,
+ SPEC_PALADIN_PROTECTION = 1,
+ SPEC_PALADIN_RETRIBUTION = 2,
+
+ SPEC_HUNTER_BEAST_MASTERY = 0,
+ SPEC_HUNTER_MARKSMANSHIP = 1,
+ SPEC_HUNTER_SURVIVAL = 2,
+
+ SPEC_ROGUE_ASSASSINATION = 0,
+ SPEC_ROGUE_COMBAT = 1,
+ SPEC_ROGUE_SUBLETY = 2,
+
+ SPEC_PRIEST_DISCIPLINE = 0,
+ SPEC_PRIEST_HOLY = 1,
+ SPEC_PRIEST_SHADOW = 2,
+
+ SPEC_DEATH_KNIGHT_BLOOD = 0,
+ SPEC_DEATH_KNIGHT_FROST = 1,
+ SPEC_DEATH_KNIGHT_UNHOLY = 2,
+
+ SPEC_SHAMAN_ELEMENTAL = 0,
+ SPEC_SHAMAN_ENHANCEMENT = 1,
+ SPEC_SHAMAN_RESTORATION = 2,
+
+ SPEC_MAGE_ARCANE = 0,
+ SPEC_MAGE_FIRE = 1,
+ SPEC_MAGE_FROST = 2,
+
+ SPEC_WARLOCK_AFFLICTION = 0,
+ SPEC_WARLOCK_DEMONOLOGY = 1,
+ SPEC_WARLOCK_DESTRUCTION = 2,
+
+ SPEC_DRUID_BALANCE = 0,
+ SPEC_DRUID_FERAL = 1,
+ SPEC_DRUID_RESTORATION = 2
+};
enum Spells
{
/* Generic */
@@ -27,29 +72,449 @@ enum Spells
SPELL_THROW = 2764,
SPELL_SHOOT_WAND = 5019,
- /* Paladin */
- PASSIVE_ILLUMINATION = 20215,
+ /* Warrior - Generic */
+ SPELL_BATTLE_STANCE = 2457,
+ SPELL_BERSERKER_STANCE = 2458,
+ SPELL_DEFENSIVE_STANCE = 71,
+ SPELL_CHARGE = 11578,
+ SPELL_INTERCEPT = 20252,
+ SPELL_ENRAGED_REGEN = 55694,
+ SPELL_INTIMIDATING_SHOUT= 5246,
+ SPELL_PUMMEL = 6552,
+ SPELL_SHIELD_BASH = 72,
+ SPELL_BLOODRAGE = 2687,
+
+ /* Warrior - Arms */
+ SPELL_SWEEPING_STRIKES = 12328,
+ SPELL_MORTAL_STRIKE = 12294,
+ SPELL_BLADESTORM = 46924,
+ SPELL_REND = 47465,
+ SPELL_RETALIATION = 20230,
+ SPELL_SHATTERING_THROW = 64382,
+ SPELL_THUNDER_CLAP = 47502,
+
+ /* Warrior - Fury */
+ SPELL_DEATH_WISH = 12292,
+ SPELL_BLOODTHIRST = 23881,
+ PASSIVE_TITANS_GRIP = 46917,
+ SPELL_DEMO_SHOUT = 47437,
+ SPELL_EXECUTE = 47471,
+ SPELL_HEROIC_FURY = 60970,
+ SPELL_RECKLESSNESS = 1719,
+ SPELL_PIERCING_HOWL = 12323,
+
+ /* Warrior - Protection */
+ SPELL_VIGILANCE = 50720,
+ SPELL_DEVASTATE = 20243,
+ SPELL_SHOCKWAVE = 46968,
+ SPELL_CONCUSSION_BLOW = 12809,
+ SPELL_DISARM = 676,
+ SPELL_LAST_STAND = 12975,
+ SPELL_SHIELD_BLOCK = 2565,
+ SPELL_SHIELD_SLAM = 47488,
+ SPELL_SHIELD_WALL = 871,
+ SPELL_SPELL_REFLECTION = 23920,
+
+ /* Paladin - Generic */
+ SPELL_AURA_MASTERY = 31821,
+ SPELL_LAY_ON_HANDS = 48788,
+ SPELL_BLESSING_OF_MIGHT = 48932,
+ SPELL_AVENGING_WRATH = 31884,
+ SPELL_DIVINE_PROTECTION = 498,
+ SPELL_DIVINE_SHIELD = 642,
+ SPELL_HAMMER_OF_JUSTICE = 10308,
+ SPELL_HAND_OF_FREEDOM = 1044,
+ SPELL_HAND_OF_PROTECTION = 10278,
+ SPELL_HAND_OF_SACRIFICE = 6940,
+
+ /* Paladin - Holy*/
+ PASSIVE_ILLUMINATION = 20215,
+ SPELL_HOLY_SHOCK = 20473,
+ SPELL_BEACON_OF_LIGHT = 53563,
+ SPELL_CONSECRATION = 48819,
+ SPELL_FLASH_OF_LIGHT = 48785,
+ SPELL_HOLY_LIGHT = 48782,
+ SPELL_DIVINE_FAVOR = 20216,
+ SPELL_DIVINE_ILLUMINATION = 31842,
+
+ /* Paladin - Protection */
+ SPELL_BLESS_OF_SANC = 20911,
+ SPELL_HOLY_SHIELD = 20925,
+ SPELL_AVENGERS_SHIELD = 48827,
+ SPELL_DIVINE_SACRIFICE = 64205,
+ SPELL_HAMMER_OF_RIGHTEOUS = 53595,
+ SPELL_RIGHTEOUS_FURY = 25780,
+ SPELL_SHIELD_OF_RIGHTEOUS = 61411,
+
+ /* Paladin - Retribution */
+ SPELL_SEAL_OF_COMMAND = 20375,
+ SPELL_CRUSADER_STRIKE = 35395,
+ SPELL_DIVINE_STORM = 53385,
+ SPELL_JUDGEMENT = 20271,
+ SPELL_HAMMER_OF_WRATH = 48806,
+
+ /* Hunter - Generic */
+ SPELL_DETERRENCE = 19263,
+ SPELL_EXPLOSIVE_TRAP = 49067,
+ SPELL_FREEZING_ARROW = 60192,
+ SPELL_RAPID_FIRE = 3045,
+ SPELL_KILL_SHOT = 61006,
+ SPELL_MULTI_SHOT = 49048,
+ SPELL_VIPER_STING = 3034,
+
+ /* Hunter - Beast Mastery */
+ SPELL_BESTIAL_WRATH = 19574,
+ PASSIVE_BEAST_WITHIN = 34692,
+ PASSIVE_BEAST_MASTERY = 53270,
+
+ /* Hunter - Marksmanship */
+ SPELL_AIMED_SHOT = 19434,
+ PASSIVE_TRUESHOT_AURA = 19506,
+ SPELL_CHIMERA_SHOT = 53209,
+ SPELL_ARCANE_SHOT = 49045,
+ SPELL_STEADY_SHOT = 49052,
+ SPELL_READINESS = 23989,
+ SPELL_SILENCING_SHOT = 34490,
- /* Priest */
- SPELL_SOUL_WARDING = 63574,
- SPELL_SPIRIT_REDEMPTION = 20711,
- SPELL_SHADOWFORM = 15473,
+ /* Hunter - Survival */
+ PASSIVE_LOCK_AND_LOAD = 56344,
+ SPELL_WYVERN_STING = 19386,
+ SPELL_EXPLOSIVE_SHOT = 53301,
+ SPELL_BLACK_ARROW = 3674,
- /* Shaman */
- SPELL_TIDAL_FORCE = 582,
- SPELL_MANA_TIDE_TOTEM = 590,
- SPELL_SHA_NATURE_SWIFT = 591,
+ /* Rogue - Generic */
+ SPELL_DISMANTLE = 51722,
+ SPELL_EVASION = 26669,
+ SPELL_KICK = 1766,
+ SPELL_VANISH = 26889,
+ SPELL_BLIND = 2094,
+ SPELL_CLOAK_OF_SHADOWS = 31224,
+
+ /* Rogue - Assassination */
+ SPELL_COLD_BLOOD = 14177,
+ SPELL_MUTILATE = 1329,
+ SPELL_HUNGER_FOR_BLOOD = 51662,
+ SPELL_ENVENOM = 57993,
+
+ /* Rogue - Combat */
+ SPELL_SINISTER_STRIKE = 48637,
+ SPELL_BLADE_FLURRY = 13877,
+ SPELL_ADRENALINE_RUSH = 13750,
+ SPELL_KILLING_SPREE = 51690,
+ SPELL_EVISCERATE = 48668,
+
+ /* Rogue - Sublety */
+ SPELL_HEMORRHAGE = 16511,
+ SPELL_PREMEDITATION = 14183,
+ SPELL_SHADOW_DANCE = 51713,
+ SPELL_PREPARATION = 14185,
+ SPELL_SHADOWSTEP = 36554,
+
+ /* Priest - Generic */
+ SPELL_FEAR_WARD = 6346,
+ SPELL_POWER_WORD_FORT = 48161,
+ SPELL_DIVINE_SPIRIT = 48073,
+ SPELL_SHADOW_PROTECTION = 48169,
+ SPELL_DIVINE_HYMN = 64843,
+ SPELL_HYMN_OF_HOPE = 64901,
+ SPELL_SHADOW_WORD_DEATH = 48158,
+ SPELL_PSYCHIC_SCREAM = 10890,
+
+ /* Priest - Discipline */
+ PASSIVE_SOUL_WARDING = 63574,
+ SPELL_POWER_INFUSION = 10060,
+ SPELL_PENANCE = 47540,
+ SPELL_PAIN_SUPPRESSION = 33206,
+ SPELL_INNER_FOCUS = 14751,
+ SPELL_POWER_WORD_SHIELD = 48066,
+
+ /* Priest - Holy */
+ PASSIVE_SPIRIT_REDEMPTION = 20711,
+ SPELL_DESPERATE_PRAYER = 19236,
+ SPELL_GUARDIAN_SPIRIT = 47788,
+ SPELL_FLASH_HEAL = 48071,
+ SPELL_RENEW = 48068,
+
+ /* Priest - Shadow */
+ SPELL_VAMPIRIC_EMBRACE = 15286,
+ SPELL_SHADOWFORM = 15473,
+ SPELL_VAMPIRIC_TOUCH = 34914,
+ SPELL_MIND_FLAY = 15407,
+ SPELL_MIND_BLAST = 48127,
+ SPELL_SHADOW_WORD_PAIN = 48125,
+ SPELL_DEVOURING_PLAGUE = 48300,
+ SPELL_DISPERSION = 47585,
+
+ /* Death Knight - Generic */
+ SPELL_DEATH_GRIP = 49576,
+ SPELL_STRANGULATE = 47476,
+ SPELL_EMPOWER_RUNE_WEAP = 47568,
+ SPELL_ICEBORN_FORTITUDE = 48792,
+ SPELL_ANTI_MAGIC_SHELL = 48707,
+ SPELL_DEATH_COIL_DK = 49895,
+ SPELL_MIND_FREEZE = 47528,
+ SPELL_ICY_TOUCH = 49909,
+ AURA_FROST_FEVER = 55095,
+ SPELL_PLAGUE_STRIKE = 49921,
+ AURA_BLOOD_PLAGUE = 55078,
+ SPELL_PESTILENCE = 50842,
+
+ /* Death Knight - Blood */
+ SPELL_RUNE_TAP = 48982,
+ SPELL_HYSTERIA = 49016,
+ SPELL_HEART_STRIKE = 55050,
+ SPELL_DEATH_STRIKE = 49924,
+ SPELL_BLOOD_STRIKE = 49930,
+ SPELL_MARK_OF_BLOOD = 49005,
+ SPELL_VAMPIRIC_BLOOD = 55233,
+
+ /* Death Knight - Frost */
+ PASSIVE_ICY_TALONS = 50887,
+ SPELL_FROST_STRIKE = 49143,
+ SPELL_HOWLING_BLAST = 49184,
+ SPELL_UNBREAKABLE_ARMOR = 51271,
+ SPELL_OBLITERATE = 51425,
+ SPELL_DEATHCHILL = 49796,
+
+ /* Death Knight - Unholy */
+ PASSIVE_UNHOLY_BLIGHT = 49194,
+ PASSIVE_MASTER_OF_GHOUL = 52143,
+ SPELL_SCOURGE_STRIKE = 55090,
+ SPELL_DEATH_AND_DECAY = 49938,
+ SPELL_ANTI_MAGIC_ZONE = 51052,
+ SPELL_SUMMON_GARGOYLE = 49206,
+
+ /* Shaman - Generic */
+ SPELL_HEROISM = 32182,
+ SPELL_BLOODLUST = 2825,
+ SPELL_GROUNDING_TOTEM = 8177,
+
+ /* Shaman - Elemental*/
+ PASSIVE_ELEMENTAL_FOCUS = 16164,
+ SPELL_TOTEM_OF_WRATH = 30706,
+ SPELL_THUNDERSTORM = 51490,
+ SPELL_LIGHTNING_BOLT = 49238,
+ SPELL_EARTH_SHOCK = 49231,
+ SPELL_FLAME_SHOCK = 49233,
+ SPELL_LAVA_BURST = 60043,
+ SPELL_CHAIN_LIGHTNING = 49271,
+ SPELL_ELEMENTAL_MASTERY = 16166,
+
+ /* Shaman - Enhancement */
+ PASSIVE_SPIRIT_WEAPONS = 16268,
+ SPELL_LAVA_LASH = 60103,
+ SPELL_FERAL_SPIRIT = 51533,
+ AURA_MAELSTROM_WEAPON = 53817,
SPELL_STORMSTRIKE = 17364,
+ SPELL_SHAMANISTIC_RAGE = 30823,
+
+ /* Shaman - Restoration*/
+ SPELL_SHA_NATURE_SWIFT = 591,
+ SPELL_MANA_TIDE_TOTEM = 590,
+ SPELL_EARTH_SHIELD = 49284,
+ SPELL_RIPTIDE = 61295,
+ SPELL_HEALING_WAVE = 49273,
+ SPELL_LESSER_HEAL_WAVE = 49276,
+ SPELL_TIDAL_FORCE = 55198,
+
+ /* Mage - Generic */
+ SPELL_DAMPEN_MAGIC = 43015,
+ SPELL_EVOCATION = 12051,
+ SPELL_MANA_SHIELD = 43020,
+ SPELL_MIRROR_IMAGE = 55342,
+ SPELL_SPELLSTEAL = 30449,
+ SPELL_COUNTERSPELL = 2139,
+ SPELL_ICE_BLOCK = 45438,
+
+ /* Mage - Arcane */
+ SPELL_FOCUS_MAGIC = 54646,
+ SPELL_ARCANE_POWER = 12042,
+ SPELL_ARCANE_BARRAGE = 44425,
+ SPELL_ARCANE_BLAST = 42897,
+ AURA_ARCANE_BLAST = 36032,
+ SPELL_ARCANE_MISSILES = 42846,
+ SPELL_PRESENCE_OF_MIND = 12043,
+
+ /* Mage - Fire */
+ SPELL_PYROBLAST = 11366,
+ SPELL_COMBUSTION = 11129,
+ SPELL_LIVING_BOMB = 44457,
+ SPELL_FIREBALL = 42833,
+ SPELL_FIRE_BLAST = 42873,
+ SPELL_DRAGONS_BREATH = 31661,
+ SPELL_BLAST_WAVE = 11113,
+
+ /* Mage - Frost */
+ SPELL_ICY_VEINS = 12472,
+ SPELL_ICE_BARRIER = 11426,
+ SPELL_DEEP_FREEZE = 44572,
+ SPELL_FROST_NOVA = 42917,
+ SPELL_FROSTBOLT = 42842,
+ SPELL_COLD_SNAP = 11958,
+ SPELL_ICE_LANCE = 42914,
+
+ /* Warlock - Generic */
+ SPELL_FEAR = 6215,
+ SPELL_HOWL_OF_TERROR = 17928,
+ SPELL_CORRUPTION = 47813,
+ SPELL_DEATH_COIL_W = 47860,
+ SPELL_SHADOW_BOLT = 47809,
+ SPELL_INCINERATE = 47838,
+ SPELL_IMMOLATE = 47811,
+ SPELL_SEED_OF_CORRUPTION = 47836,
+
+ /* Warlock - Affliction */
+ PASSIVE_SIPHON_LIFE = 63108,
+ SPELL_UNSTABLE_AFFLICTION = 30108,
+ SPELL_HAUNT = 48181,
+ SPELL_CURSE_OF_AGONY = 47864,
+ SPELL_DRAIN_SOUL = 47855,
- /* Druid */
- SPELL_MOONKIN_FORM = 24858,
- SPELL_SWIFTMEND = 18562,
- SPELL_DRU_NATURE_SWIFT = 17116,
- SPELL_TREE_OF_LIFE = 33891
+ /* Warlock - Demonology */
+ SPELL_SOUL_LINK = 19028,
+ SPELL_DEMONIC_EMPOWERMENT = 47193,
+ SPELL_METAMORPHOSIS = 59672,
+ SPELL_IMMOLATION_AURA = 50589,
+ SPELL_DEMON_CHARGE = 54785,
+ AURA_DECIMATION = 63167,
+ AURA_MOLTEN_CORE = 71165,
+ SPELL_SOUL_FIRE = 47825,
+
+ /* Warlock - Destruction */
+ SPELL_SHADOWBURN = 17877,
+ SPELL_CONFLAGRATE = 17962,
+ SPELL_CHAOS_BOLT = 50796,
+ SPELL_SHADOWFURY = 47847,
+
+ /* Druid - Generic */
+ SPELL_BARKSKIN = 22812,
+ SPELL_INNERVATE = 29166,
+
+ /* Druid - Balance */
+ SPELL_INSECT_SWARM = 5570,
+ SPELL_MOONKIN_FORM = 24858,
+ SPELL_STARFALL = 48505,
+ SPELL_TYPHOON = 61384,
+ AURA_ECLIPSE_LUNAR = 48518,
+ SPELL_MOONFIRE = 48463,
+ SPELL_STARFIRE = 48465,
+ SPELL_WRATH = 48461,
+
+ /* Druid - Feral */
+ SPELL_CAT_FORM = 768,
+ SPELL_SURVIVAL_INSTINCTS = 61336,
+ SPELL_MANGLE = 33917,
+ SPELL_BERSERK = 50334,
+ SPELL_MANGLE_CAT = 48566,
+ SPELL_FERAL_CHARGE_CAT = 49376,
+ SPELL_RAKE = 48574,
+ SPELL_RIP = 49800,
+ SPELL_SAVAGE_ROAR = 52610,
+ SPELL_TIGER_FURY = 50213,
+ SPELL_CLAW = 48570,
+ SPELL_DASH = 33357,
+ SPELL_MAIM = 49802,
+
+ /* Druid - Restoration */
+ SPELL_SWIFTMEND = 18562,
+ SPELL_TREE_OF_LIFE = 33891,
+ SPELL_WILD_GROWTH = 48438,
+ SPELL_NATURE_SWIFTNESS = 17116,
+ SPELL_TRANQUILITY = 48447,
+ SPELL_NOURISH = 50464,
+ SPELL_HEALING_TOUCH = 48378,
+ SPELL_REJUVENATION = 48441,
+ SPELL_REGROWTH = 48443,
+ SPELL_LIFEBLOOM = 48451
};
+// As it turns out, finding out "how many points does the player have in spec X" is actually really expensive to do frequently
+// So instead, we just check for a handful of spells that, realistically, no spec is gonna go without (and "has spell" is cheap!)
+// Can players deliberately trick this check? Yes.
+// Is it worth doing? No.
+// Close enough.
+static const uint32 SPEC_ICONICS[MAX_CLASSES][NUM_TALENT_TREES][NUM_SPEC_ICONICS] = {
+ { // CLASS_NONE
+ {0,0,0},
+ {0,0,0},
+ {0,0,0}
+ },
+ { // CLASS_WARRIOR
+ {SPELL_BLADESTORM, SPELL_MORTAL_STRIKE, SPELL_SWEEPING_STRIKES}, // Arms
+ {PASSIVE_TITANS_GRIP, SPELL_BLOODTHIRST, SPELL_DEATH_WISH}, // Fury
+ {SPELL_SHOCKWAVE, SPELL_DEVASTATE, SPELL_VIGILANCE} // Protection
+ },
+ { // CLASS_PALADIN
+ {SPELL_BEACON_OF_LIGHT, SPELL_HOLY_SHOCK, PASSIVE_ILLUMINATION}, // Holy
+ {SPELL_HAMMER_OF_RIGHTEOUS, SPELL_HOLY_SHIELD, SPELL_BLESS_OF_SANC}, // Protection
+ {SPELL_DIVINE_STORM, SPELL_CRUSADER_STRIKE, SPELL_SEAL_OF_COMMAND} // Retribution
+ },
+ { // CLASS_HUNTER
+ {PASSIVE_BEAST_MASTERY, PASSIVE_BEAST_WITHIN, SPELL_BESTIAL_WRATH}, // Beast Mastery
+ {SPELL_CHIMERA_SHOT, PASSIVE_TRUESHOT_AURA, SPELL_AIMED_SHOT}, // Marksmanship
+ {SPELL_EXPLOSIVE_SHOT, SPELL_WYVERN_STING, PASSIVE_LOCK_AND_LOAD} // Survival
+ },
+ { // CLASS_ROGUE
+ {SPELL_HUNGER_FOR_BLOOD, SPELL_MUTILATE, SPELL_COLD_BLOOD}, // Assassination
+ {SPELL_KILLING_SPREE, SPELL_ADRENALINE_RUSH, SPELL_BLADE_FLURRY}, // Combat
+ {SPELL_SHADOW_DANCE, SPELL_PREMEDITATION, SPELL_HEMORRHAGE} // Sublety
+ },
+ { // CLASS_PRIEST
+ {SPELL_PENANCE, SPELL_POWER_INFUSION, PASSIVE_SOUL_WARDING}, // Discipline
+ {SPELL_GUARDIAN_SPIRIT, PASSIVE_SPIRIT_REDEMPTION, SPELL_DESPERATE_PRAYER}, // Holy
+ {SPELL_VAMPIRIC_TOUCH, SPELL_SHADOWFORM, SPELL_VAMPIRIC_EMBRACE} // Shadow
+ },
+ { // CLASS_DEATH_KNIGHT
+ {SPELL_HEART_STRIKE, SPELL_HYSTERIA, SPELL_RUNE_TAP}, // Blood
+ {SPELL_HOWLING_BLAST, SPELL_FROST_STRIKE, PASSIVE_ICY_TALONS}, // Frost
+ {SPELL_SCOURGE_STRIKE, PASSIVE_MASTER_OF_GHOUL, PASSIVE_UNHOLY_BLIGHT} // Unholy
+ },
+ { // CLASS_SHAMAN
+ {SPELL_THUNDERSTORM, SPELL_TOTEM_OF_WRATH, PASSIVE_ELEMENTAL_FOCUS}, // Elemental
+ {SPELL_FERAL_SPIRIT, SPELL_LAVA_LASH, PASSIVE_SPIRIT_WEAPONS}, // Enhancement
+ {SPELL_RIPTIDE, SPELL_MANA_TIDE_TOTEM, SPELL_SHA_NATURE_SWIFT} // Restoration
+ },
+ { // CLASS_MAGE
+ {SPELL_ARCANE_BARRAGE, SPELL_ARCANE_POWER, SPELL_FOCUS_MAGIC}, // Arcane
+ {SPELL_LIVING_BOMB, SPELL_COMBUSTION, SPELL_PYROBLAST}, // Fire
+ {SPELL_DEEP_FREEZE, SPELL_ICE_BARRIER, SPELL_ICY_VEINS} // Frost
+ },
+ { // CLASS_WARLOCK
+ {SPELL_HAUNT, SPELL_UNSTABLE_AFFLICTION, PASSIVE_SIPHON_LIFE}, // Affliction
+ {SPELL_METAMORPHOSIS, SPELL_DEMONIC_EMPOWERMENT, SPELL_SOUL_LINK}, // Demonology
+ {SPELL_CHAOS_BOLT, SPELL_CONFLAGRATE, SPELL_SHADOWBURN} // Destruction
+ },
+ { // CLASS_UNK
+ {0,0,0},
+ {0,0,0},
+ {0,0,0}
+ },
+ { // CLASS_DRUID
+ {SPELL_STARFALL, SPELL_MOONKIN_FORM, SPELL_INSECT_SWARM}, // Balance
+ {SPELL_BERSERK, SPELL_MANGLE, SPELL_SURVIVAL_INSTINCTS}, // Feral
+ {SPELL_WILD_GROWTH, SPELL_TREE_OF_LIFE, SPELL_SWIFTMEND} // Restoration
+ }
+};
+
+uint8 PlayerAI::GetPlayerSpec(Player const* who)
+{
+ if (!who)
+ return 0;
+
+ uint8 wClass = who->getClass();
+ for (uint8 tier = 0; tier < NUM_SPEC_ICONICS; ++tier)
+ for (uint8 tree = 0; tree < NUM_TALENT_TREES; ++tree)
+ if (SPEC_ICONICS[wClass][tree][tier] && who->HasSpell(SPEC_ICONICS[wClass][tree][tier]))
+ return tree;
+
+ return 0;
+}
+
bool PlayerAI::IsPlayerHealer(Player const* who)
{
+ if (!who)
+ return false;
+
switch (who->getClass())
{
case CLASS_WARRIOR:
@@ -61,18 +526,21 @@ bool PlayerAI::IsPlayerHealer(Player const* who)
default:
return false;
case CLASS_PALADIN:
- return who->HasSpell(PASSIVE_ILLUMINATION);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_PALADIN_HOLY);
case CLASS_PRIEST:
- return who->HasSpell(SPELL_SOUL_WARDING) || who->HasSpell(SPELL_SPIRIT_REDEMPTION);
+ return (PlayerAI::GetPlayerSpec(who) != SPEC_PRIEST_SHADOW);
case CLASS_SHAMAN:
- return who->HasSpell(SPELL_MANA_TIDE_TOTEM) || who->HasSpell(SPELL_SHA_NATURE_SWIFT) || who->HasSpell(SPELL_TIDAL_FORCE);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_RESTORATION);
case CLASS_DRUID:
- return who->HasSpell(SPELL_SWIFTMEND) || who->HasSpell(SPELL_DRU_NATURE_SWIFT) || who->HasSpell(SPELL_TREE_OF_LIFE);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_RESTORATION);
}
}
bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
{
+ if (!who)
+ return false;
+
switch (who->getClass())
{
case CLASS_WARRIOR:
@@ -94,12 +562,106 @@ bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
return false;
}
case CLASS_PRIEST:
- return who->HasSpell(SPELL_SHADOWFORM);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_PRIEST_SHADOW);
case CLASS_SHAMAN:
- return !who->HasSpell(SPELL_STORMSTRIKE);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_ELEMENTAL);
case CLASS_DRUID:
- return who->HasSpell(SPELL_MOONKIN_FORM);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_BALANCE);
+ }
+}
+
+PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, Unit* target)
+{
+ // Find highest spell rank that we know
+ uint32 knownRank, nextRank;
+ if (me->HasSpell(spellId))
+ {
+ // this will save us some lookups if the player has the highest rank (expected case)
+ knownRank = spellId;
+ nextRank = sSpellMgr->GetNextSpellInChain(spellId);
+ }
+ else
+ {
+ knownRank = 0;
+ nextRank = sSpellMgr->GetFirstSpellInChain(spellId);
+ }
+
+ while (nextRank && me->HasSpell(nextRank))
+ {
+ knownRank = nextRank;
+ nextRank = sSpellMgr->GetNextSpellInChain(knownRank);
+ }
+
+ if (!knownRank)
+ return {};
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(knownRank);
+ if (!spellInfo)
+ return {};
+
+ if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo))
+ return {};
+
+ Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE);
+ if (spell->CanAutoCast(target))
+ return{ spell, target };
+
+ delete spell;
+ return {};
+}
+
+PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, SpellTarget target)
+{
+ Unit* pTarget = nullptr;
+ switch (target)
+ {
+ case TARGET_NONE:
+ break;
+ case TARGET_VICTIM:
+ pTarget = me->GetVictim();
+ if (!pTarget)
+ return {};
+ break;
+ case TARGET_CHARMER:
+ pTarget = me->GetCharmer();
+ if (!pTarget)
+ return {};
+ break;
+ case TARGET_SELF:
+ pTarget = me;
+ break;
+ }
+
+ return VerifySpellCast(spellId, pTarget);
+}
+
+PlayerAI::TargetedSpell PlayerAI::SelectSpellCast(PossibleSpellVector& spells)
+{
+ uint32 totalWeights = 0;
+ for (PossibleSpell const& wSpell : spells)
+ totalWeights += wSpell.second;
+
+ TargetedSpell selected;
+ uint32 randNum = urand(0, totalWeights - 1);
+ for (PossibleSpell const& wSpell : spells)
+ {
+ if (selected)
+ {
+ delete wSpell.first.first;
+ continue;
+ }
+
+ if (randNum < wSpell.second)
+ selected = wSpell.first;
+ else
+ {
+ randNum -= wSpell.second;
+ delete wSpell.first.first;
+ }
}
+
+ spells.clear();
+ return selected;
}
void PlayerAI::DoRangedAttackIfReady()
@@ -150,6 +712,29 @@ void PlayerAI::DoAutoAttackIfReady()
DoMeleeAttackIfReady();
}
+void PlayerAI::CancelAllShapeshifts()
+{
+ std::list<AuraEffect*> const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT);
+ std::set<Aura*> removableShapeshifts;
+ for (AuraEffect* auraEff : shapeshiftAuras)
+ {
+ Aura* aura = auraEff->GetBase();
+ if (!aura)
+ continue;
+ SpellInfo const* auraInfo = aura->GetSpellInfo();
+ if (!auraInfo)
+ continue;
+ if (auraInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL))
+ continue;
+ if (!auraInfo->IsPositive() || auraInfo->IsPassive())
+ continue;
+ removableShapeshifts.insert(aura);
+ }
+
+ for (Aura* aura : removableShapeshifts)
+ me->RemoveOwnedAura(aura, AURA_REMOVE_BY_CANCEL);
+}
+
struct UncontrolledTargetSelectPredicate : public std::unary_function<Unit*, bool>
{
bool operator()(Unit const* target) const
@@ -164,7 +749,516 @@ Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const
return nullptr;
}
-void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
+PlayerAI::TargetedSpell SimpleCharmedPlayerAI::SelectAppropriateCastForSpec()
+{
+ PossibleSpellVector spells;
+
+ switch (me->getClass())
+ {
+ case CLASS_WARRIOR:
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_CHARGE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_INTERCEPT, TARGET_VICTIM, 10);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_ENRAGED_REGEN, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INTIMIDATING_SHOUT, TARGET_VICTIM, 4);
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_PUMMEL, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_BASH, TARGET_VICTIM, 15);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_BLOODRAGE, TARGET_NONE, 5);
+ switch (GetSpec())
+ {
+ case SPEC_WARRIOR_PROTECTION:
+ VerifyAndPushSpellCast(spells, SPELL_SHOCKWAVE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_CONCUSSION_BLOW, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DISARM, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LAST_STAND, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_BLOCK, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_SLAM, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_WALL, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SPELL_REFLECTION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DEVASTATE, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 1);
+ break;
+ case SPEC_WARRIOR_ARMS:
+ VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_MORTAL_STRIKE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_BLADESTORM, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_RETALIATION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SHATTERING_THROW, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15);
+ break;
+ case SPEC_WARRIOR_FURY:
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_WISH, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_BLOODTHIRST, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_HEROIC_FURY, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_RECKLESSNESS, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_PIERCING_HOWL, TARGET_VICTIM, 2);
+ break;
+ }
+ break;
+ case CLASS_PALADIN:
+ VerifyAndPushSpellCast(spells, SPELL_AURA_MASTERY, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_LAY_ON_HANDS, TARGET_CHARMER, 8);
+ VerifyAndPushSpellCast(spells, SPELL_BLESSING_OF_MIGHT, TARGET_CHARMER, 8);
+ VerifyAndPushSpellCast(spells, SPELL_AVENGING_WRATH, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_PROTECTION, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SHIELD, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_JUSTICE, TARGET_VICTIM, 6);
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_FREEDOM, TARGET_SELF, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, TARGET_SELF, 1);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ {
+ if (creatureCharmer->IsDungeonBoss() || creatureCharmer->isWorldBoss())
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_SACRIFICE, creatureCharmer, 10);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, creatureCharmer, 3);
+ }
+
+ switch (GetSpec())
+ {
+ case SPEC_PALADIN_PROTECTION:
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_RIGHTEOUS, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SACRIFICE, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_OF_RIGHTEOUS, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CONSECRATION, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHIELD, TARGET_NONE, 1);
+ break;
+ case SPEC_PALADIN_HOLY:
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_OF_LIGHT, TARGET_CHARMER, 4);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_LIGHT, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_FAVOR, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_ILLUMINATION, TARGET_NONE, 3);
+ break;
+ case SPEC_PALADIN_RETRIBUTION:
+ VerifyAndPushSpellCast(spells, SPELL_CRUSADER_STRIKE, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_STORM, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_WRATH, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_RIGHTEOUS_FURY, TARGET_NONE, 2);
+ break;
+ }
+ break;
+ case CLASS_HUNTER:
+ VerifyAndPushSpellCast(spells, SPELL_DETERRENCE, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_TRAP, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_FREEZING_ARROW, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_RAPID_FIRE, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_KILL_SHOT, TARGET_VICTIM, 10);
+ if (me->GetVictim() && me->GetVictim()->getPowerType() == POWER_MANA && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_VIPER_STING, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_VIPER_STING, TARGET_VICTIM, 5);
+
+ switch (GetSpec())
+ {
+ case SPEC_HUNTER_BEAST_MASTERY:
+ VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 2);
+ break;
+ case SPEC_HUNTER_MARKSMANSHIP:
+ VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CHIMERA_SHOT, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_READINESS, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_SILENCING_SHOT, TARGET_VICTIM, 5);
+ break;
+ case SPEC_HUNTER_SURVIVAL:
+ VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_SHOT, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_BLACK_ARROW, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 1);
+ break;
+ }
+ break;
+ case CLASS_ROGUE:
+ {
+ VerifyAndPushSpellCast(spells, SPELL_DISMANTLE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_EVASION, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_VANISH, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_BLIND, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CLOAK_OF_SHADOWS, TARGET_NONE, 2);
+
+ uint32 builder = 0, finisher = 0;
+ switch (GetSpec())
+ {
+ case SPEC_ROGUE_ASSASSINATION:
+ builder = SPELL_MUTILATE, finisher = SPELL_ENVENOM;
+ VerifyAndPushSpellCast(spells, SPELL_COLD_BLOOD, TARGET_NONE, 20);
+ break;
+ case SPEC_ROGUE_COMBAT:
+ builder = SPELL_SINISTER_STRIKE, finisher = SPELL_EVISCERATE;
+ VerifyAndPushSpellCast(spells, SPELL_ADRENALINE_RUSH, TARGET_NONE, 6);
+ VerifyAndPushSpellCast(spells, SPELL_BLADE_FLURRY, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_KILLING_SPREE, TARGET_NONE, 25);
+ break;
+ case SPEC_ROGUE_SUBLETY:
+ builder = SPELL_HEMORRHAGE, finisher = SPELL_EVISCERATE;
+ VerifyAndPushSpellCast(spells, SPELL_PREPARATION, TARGET_NONE, 10);
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWSTEP, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_DANCE, TARGET_NONE, 10);
+ break;
+ }
+
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_KICK, TARGET_VICTIM, 25);
+
+ uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0;
+ if (cp >= 4)
+ VerifyAndPushSpellCast(spells, finisher, TARGET_VICTIM, 10);
+ if (cp <= 4)
+ VerifyAndPushSpellCast(spells, builder, TARGET_VICTIM, 5);
+ }
+ break;
+ }
+ case CLASS_PRIEST:
+ VerifyAndPushSpellCast(spells, SPELL_FEAR_WARD, TARGET_SELF, 2);
+ VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_FORT, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SPIRIT, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_PROTECTION, TARGET_CHARMER, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_HYMN, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_HYMN_OF_HOPE, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_DEATH, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_PSYCHIC_SCREAM, TARGET_VICTIM, 3);
+ switch (GetSpec())
+ {
+ case SPEC_PRIEST_DISCIPLINE:
+ VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_SHIELD, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INNER_FOCUS, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_PAIN_SUPPRESSION, TARGET_CHARMER, 15);
+ VerifyAndPushSpellCast(spells, SPELL_POWER_INFUSION, TARGET_CHARMER, 10);
+ VerifyAndPushSpellCast(spells, SPELL_PENANCE, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1);
+ break;
+ case SPEC_PRIEST_HOLY:
+ VerifyAndPushSpellCast(spells, SPELL_DESPERATE_PRAYER, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_GUARDIAN_SPIRIT, TARGET_CHARMER, 5);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_RENEW, TARGET_CHARMER, 3);
+ break;
+ case SPEC_PRIEST_SHADOW:
+ if (!me->HasAura(SPELL_SHADOWFORM))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWFORM, TARGET_NONE, 100);
+ break;
+ }
+ if (Unit* victim = me->GetVictim())
+ {
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_VAMPIRIC_TOUCH, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_TOUCH, TARGET_VICTIM, 4);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_SHADOW_WORD_PAIN, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_PAIN, TARGET_VICTIM, 3);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_DEVOURING_PLAGUE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_DEVOURING_PLAGUE, TARGET_VICTIM, 4);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_MIND_BLAST, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_MIND_FLAY, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DISPERSION, TARGET_NONE, 10);
+ break;
+ }
+ break;
+ case CLASS_DEATH_KNIGHT:
+ {
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_GRIP, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_STRANGULATE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_EMPOWER_RUNE_WEAP, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ICEBORN_FORTITUDE, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_SHELL, TARGET_NONE, 10);
+
+ bool hasFF = false, hasBP = false;
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_MIND_FREEZE, TARGET_VICTIM, 25);
+
+ hasFF = !!victim->GetAuraApplicationOfRankedSpell(AURA_FROST_FEVER, me->GetGUID()), hasBP = !!victim->GetAuraApplicationOfRankedSpell(AURA_BLOOD_PLAGUE, me->GetGUID());
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_PESTILENCE, TARGET_VICTIM, 3);
+ if (!hasFF)
+ VerifyAndPushSpellCast(spells, SPELL_ICY_TOUCH, TARGET_VICTIM, 4);
+ if (!hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_PLAGUE_STRIKE, TARGET_VICTIM, 4);
+ }
+ switch (GetSpec())
+ {
+ case SPEC_DEATH_KNIGHT_BLOOD:
+ VerifyAndPushSpellCast(spells, SPELL_RUNE_TAP, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, TARGET_SELF, 5);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ if (!creatureCharmer->IsDungeonBoss() && !creatureCharmer->isWorldBoss())
+ VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, creatureCharmer, 15);
+ VerifyAndPushSpellCast(spells, SPELL_HEART_STRIKE, TARGET_VICTIM, 2);
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_STRIKE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_MARK_OF_BLOOD, TARGET_VICTIM, 20);
+ VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_BLOOD, TARGET_NONE, 10);
+ break;
+ case SPEC_DEATH_KNIGHT_FROST:
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_OBLITERATE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_HOWLING_BLAST, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_UNBREAKABLE_ARMOR, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_DEATHCHILL, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_FROST_STRIKE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1);
+ break;
+ case SPEC_DEATH_KNIGHT_UNHOLY:
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_SCOURGE_STRIKE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_AND_DECAY, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_ZONE, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_SUMMON_GARGOYLE, TARGET_VICTIM, 7);
+ VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 3);
+ break;
+ }
+ break;
+ }
+ case CLASS_SHAMAN:
+ VerifyAndPushSpellCast(spells, SPELL_HEROISM, TARGET_NONE, 25);
+ VerifyAndPushSpellCast(spells, SPELL_BLOODLUST, TARGET_NONE, 25);
+ VerifyAndPushSpellCast(spells, SPELL_GROUNDING_TOTEM, TARGET_NONE, 2);
+ switch (GetSpec())
+ {
+ case SPEC_SHAMAN_RESTORATION:
+ if (Unit* charmer = me->GetCharmer())
+ if (!charmer->GetAuraApplicationOfRankedSpell(SPELL_EARTH_SHIELD, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_EARTH_SHIELD, charmer, 2);
+ if (me->HasAura(SPELL_SHA_NATURE_SWIFT))
+ VerifyAndPushSpellCast(spells, SPELL_HEALING_WAVE, TARGET_CHARMER, 20);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_LESSER_HEAL_WAVE, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_TIDAL_FORCE, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_SHA_NATURE_SWIFT, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_MANA_TIDE_TOTEM, TARGET_NONE, 3);
+ break;
+ case SPEC_SHAMAN_ELEMENTAL:
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->GetAuraOfRankedSpell(SPELL_FLAME_SHOCK, GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_LAVA_BURST, TARGET_VICTIM, 5);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_FLAME_SHOCK, TARGET_VICTIM, 3);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_CHAIN_LIGHTNING, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ELEMENTAL_MASTERY, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDERSTORM, TARGET_NONE, 3);
+ break;
+ case SPEC_SHAMAN_ENHANCEMENT:
+ if (Aura const* maelstrom = me->GetAura(AURA_MAELSTROM_WEAPON))
+ if (maelstrom->GetStackAmount() == 5)
+ VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_STORMSTRIKE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_EARTH_SHOCK, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LAVA_LASH, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHAMANISTIC_RAGE, TARGET_NONE, 10);
+ break;
+ }
+ break;
+ case CLASS_MAGE:
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_COUNTERSPELL, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_DAMPEN_MAGIC, TARGET_CHARMER, 2);
+ VerifyAndPushSpellCast(spells, SPELL_EVOCATION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_MANA_SHIELD, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_MIRROR_IMAGE, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SPELLSTEAL, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ICE_BLOCK, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ICY_VEINS, TARGET_NONE, 3);
+ switch (GetSpec())
+ {
+ case SPEC_MAGE_ARCANE:
+ if (Aura* abAura = me->GetAura(AURA_ARCANE_BLAST))
+ if (abAura->GetStackAmount() >= 3)
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_MISSILES, TARGET_VICTIM, 7);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_BLAST, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_BARRAGE, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_POWER, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_PRESENCE_OF_MIND, TARGET_NONE, 7);
+ break;
+ case SPEC_MAGE_FIRE:
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_LIVING_BOMB))
+ VerifyAndPushSpellCast(spells, SPELL_LIVING_BOMB, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_COMBUSTION, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FIREBALL, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_FIRE_BLAST, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DRAGONS_BREATH, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_BLAST_WAVE, TARGET_VICTIM, 1);
+ break;
+ case SPEC_MAGE_FROST:
+ VerifyAndPushSpellCast(spells, SPELL_DEEP_FREEZE, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_FROST_NOVA, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FROSTBOLT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_COLD_SNAP, TARGET_VICTIM, 5);
+ if (me->GetVictim() && me->GetVictim()->HasAuraState(AURA_STATE_FROZEN, nullptr, me))
+ VerifyAndPushSpellCast(spells, SPELL_ICE_LANCE, TARGET_VICTIM, 5);
+ break;
+ }
+ break;
+ case CLASS_WARLOCK:
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_W, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_FEAR, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_SEED_OF_CORRUPTION, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_HOWL_OF_TERROR, TARGET_NONE, 2);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_CORRUPTION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CORRUPTION, TARGET_VICTIM, 10);
+ switch (GetSpec())
+ {
+ case SPEC_WARLOCK_AFFLICTION:
+ if (Unit* victim = me->GetVictim())
+ {
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_UNSTABLE_AFFLICTION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_UNSTABLE_AFFLICTION, TARGET_VICTIM, 8);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_HAUNT, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_HAUNT, TARGET_VICTIM, 8);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_CURSE_OF_AGONY, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CURSE_OF_AGONY, TARGET_VICTIM, 4);
+ if (victim->HealthBelowPct(25))
+ VerifyAndPushSpellCast(spells, SPELL_DRAIN_SOUL, TARGET_VICTIM, 100);
+ }
+ break;
+ case SPEC_WARLOCK_DEMONOLOGY:
+ VerifyAndPushSpellCast(spells, SPELL_METAMORPHOSIS, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7);
+ if (me->HasAura(AURA_DECIMATION))
+ VerifyAndPushSpellCast(spells, SPELL_SOUL_FIRE, TARGET_VICTIM, 100);
+ if (me->HasAura(SPELL_METAMORPHOSIS))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATION_AURA, TARGET_NONE, 30);
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_DEMON_CHARGE, TARGET_VICTIM, 20);
+ }
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 5);
+ if (me->HasAura(AURA_MOLTEN_CORE))
+ VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 10);
+ break;
+ case SPEC_WARLOCK_DESTRUCTION:
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 8);
+ if (me->GetVictim() && me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CONFLAGRATE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWFURY, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_CHAOS_BOLT, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWBURN, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 7);
+ break;
+ }
+ break;
+ case CLASS_DRUID:
+ VerifyAndPushSpellCast(spells, SPELL_INNERVATE, TARGET_CHARMER, 5);
+ VerifyAndPushSpellCast(spells, SPELL_BARKSKIN, TARGET_NONE, 5);
+ switch (GetSpec())
+ {
+ case SPEC_DRUID_RESTORATION:
+ if (!me->HasAura(SPELL_TREE_OF_LIFE))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_TREE_OF_LIFE, TARGET_NONE, 100);
+ break;
+ }
+ VerifyAndPushSpellCast(spells, SPELL_TRANQUILITY, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_NATURE_SWIFTNESS, TARGET_NONE, 7);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_NOURISH, creatureCharmer, 5);
+ VerifyAndPushSpellCast(spells, SPELL_WILD_GROWTH, creatureCharmer, 5);
+ if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_REJUVENATION, creatureCharmer, 8);
+ if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_REGROWTH, creatureCharmer, 8);
+ uint8 lifebloomStacks = 0;
+ if (Aura const* lifebloom = creatureCharmer->GetAura(SPELL_LIFEBLOOM, me->GetGUID()))
+ lifebloomStacks = lifebloom->GetStackAmount();
+ if (lifebloomStacks < 3)
+ VerifyAndPushSpellCast(spells, SPELL_LIFEBLOOM, creatureCharmer, 5);
+ if (creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION) ||
+ creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH))
+ VerifyAndPushSpellCast(spells, SPELL_SWIFTMEND, creatureCharmer, 10);
+ if (me->HasAura(SPELL_NATURE_SWIFTNESS))
+ VerifyAndPushSpellCast(spells, SPELL_HEALING_TOUCH, creatureCharmer, 100);
+ }
+ break;
+ case SPEC_DRUID_BALANCE:
+ {
+ if (!me->HasAura(SPELL_MOONKIN_FORM))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_MOONKIN_FORM, TARGET_NONE, 100);
+ break;
+ }
+ uint32 const mainAttackSpell = me->HasAura(AURA_ECLIPSE_LUNAR) ? SPELL_STARFIRE : SPELL_WRATH;
+ VerifyAndPushSpellCast(spells, SPELL_STARFALL, TARGET_NONE, 20);
+ VerifyAndPushSpellCast(spells, mainAttackSpell, TARGET_VICTIM, 10);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_INSECT_SWARM, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_INSECT_SWARM, TARGET_VICTIM, 7);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_MOONFIRE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_MOONFIRE, TARGET_VICTIM, 5);
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_TYPHOON, TARGET_NONE, 15);
+ break;
+ }
+ case SPEC_DRUID_FERAL:
+ if (!me->HasAura(SPELL_CAT_FORM))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_CAT_FORM, TARGET_NONE, 100);
+ break;
+ }
+ VerifyAndPushSpellCast(spells, SPELL_BERSERK, TARGET_NONE, 20);
+ VerifyAndPushSpellCast(spells, SPELL_SURVIVAL_INSTINCTS, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_TIGER_FURY, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_DASH, TARGET_NONE, 5);
+ if (Unit* victim = me->GetVictim())
+ {
+ uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0;
+ if (victim->HasUnitState(UNIT_STATE_CASTING) && cp >= 1)
+ VerifyAndPushSpellCast(spells, SPELL_MAIM, TARGET_VICTIM, 25);
+ if (!me->IsWithinMeleeRange(victim))
+ VerifyAndPushSpellCast(spells, SPELL_FERAL_CHARGE_CAT, TARGET_VICTIM, 25);
+ if (cp >= 4)
+ VerifyAndPushSpellCast(spells, SPELL_RIP, TARGET_VICTIM, 50);
+ if (cp <= 4)
+ {
+ VerifyAndPushSpellCast(spells, SPELL_MANGLE_CAT, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_CLAW, TARGET_VICTIM, 5);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_RAKE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_RAKE, TARGET_VICTIM, 8);
+ if (!me->HasAura(SPELL_SAVAGE_ROAR))
+ VerifyAndPushSpellCast(spells, SPELL_SAVAGE_ROAR, TARGET_NONE, 15);
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ return SelectSpellCast(spells);
+}
+
+static const float CASTER_CHASE_DISTANCE = 28.0f;
+void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff)
{
Creature* charmer = me->GetCharmer() ? me->GetCharmer()->ToCreature() : nullptr;
if (!charmer)
@@ -192,10 +1286,54 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
return;
if (IsRangedAttacker())
- AttackStartCaster(target, 28.0f);
+ {
+ _chaseCloser = !me->IsWithinLOSInMap(target);
+ if (_chaseCloser)
+ AttackStart(target);
+ else
+ AttackStartCaster(target, CASTER_CHASE_DISTANCE);
+ }
else
AttackStart(target);
+ _forceFacing = true;
}
+
+ if (me->IsStopped() && !me->HasUnitState(UNIT_STATE_CANNOT_TURN))
+ {
+ float targetAngle = me->GetAngle(target);
+ if (_forceFacing || fabs(me->GetOrientation() - targetAngle) > 0.4f)
+ {
+ me->SetFacingTo(targetAngle);
+ _forceFacing = false;
+ }
+ }
+
+ if (_castCheckTimer <= diff)
+ {
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ _castCheckTimer = 0;
+ else
+ {
+ if (IsRangedAttacker())
+ { // chase to zero if the target isn't in line of sight
+ bool inLOS = me->IsWithinLOSInMap(target);
+ if (_chaseCloser != !inLOS)
+ {
+ _chaseCloser = !inLOS;
+ if (_chaseCloser)
+ AttackStart(target);
+ else
+ AttackStartCaster(target, CASTER_CHASE_DISTANCE);
+ }
+ }
+ if (TargetedSpell shouldCast = SelectAppropriateCastForSpec())
+ DoCastAtTarget(shouldCast);
+ _castCheckTimer = 500;
+ }
+ }
+ else
+ _castCheckTimer -= diff;
+
DoAutoAttackIfReady();
}
else
@@ -214,6 +1352,9 @@ void SimpleCharmedPlayerAI::OnCharmed(bool apply)
{
me->CastStop();
me->AttackStop();
+ me->StopMoving();
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MovePoint(0, me->GetPosition(), false); // force re-sync of current position for all clients
}
else
{
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h
index b717816f9a3..18f65485161 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.h
+++ b/src/server/game/AI/PlayerAI/PlayerAI.h
@@ -20,41 +20,99 @@
#include "UnitAI.h"
#include "Player.h"
+#include "Spell.h"
#include "Creature.h"
class TC_GAME_API PlayerAI : public UnitAI
{
public:
- explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
+ explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _selfSpec(PlayerAI::GetPlayerSpec(player)), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
void OnCharmed(bool /*apply*/) override { } // charm AI application for players is handled by Unit::SetCharmedBy / Unit::RemoveCharmedBy
// helper functions to determine player info
+ // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned.
+ static uint8 GetPlayerSpec(Player const* who);
+ // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned.
+ uint8 GetSpec(Player const* who = nullptr) const { return (!who || who == me) ? _selfSpec : GetPlayerSpec(who); }
static bool IsPlayerHealer(Player const* who);
bool IsHealer(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who); }
static bool IsPlayerRangedAttacker(Player const* who);
bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); }
protected:
+ struct TargetedSpell : public std::pair<Spell*, Unit*>
+ {
+ TargetedSpell() : pair<Spell*, Unit*>() { }
+ TargetedSpell(Spell* first, Unit* second) : pair<Spell*, Unit*>(first, second) { }
+ explicit operator bool() { return !!first; }
+ };
+ typedef std::pair<TargetedSpell, uint32> PossibleSpell;
+ typedef std::vector<PossibleSpell> PossibleSpellVector;
+
Player* const me;
void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection
+ enum SpellTarget
+ {
+ TARGET_NONE,
+ TARGET_VICTIM,
+ TARGET_CHARMER,
+ TARGET_SELF
+ };
+ /* Check if the specified spell can be cast on that target.
+ Caller is responsible for cleaning up created Spell object from pointer. */
+ TargetedSpell VerifySpellCast(uint32 spellId, Unit* target);
+ /* Check if the specified spell can be cast on that target.
+ Caller is responsible for cleaning up created Spell object from pointer. */
+ TargetedSpell VerifySpellCast(uint32 spellId, SpellTarget target);
+
+ /* Helper method - checks spell cast, then pushes it onto provided vector if valid. */
+ template<typename T> inline void VerifyAndPushSpellCast(PossibleSpellVector& spells, uint32 spellId, T target, uint32 weight)
+ {
+ if (TargetedSpell spell = VerifySpellCast(spellId, target))
+ spells.push_back({ spell,weight });
+ }
+
+ /* Helper method - selects one spell from the vector and returns it, while deleting everything else.
+ This invalidates the vector, and empties it to prevent accidental misuse. */
+ TargetedSpell SelectSpellCast(PossibleSpellVector& spells);
+ /* Helper method - casts the included spell at the included target */
+ inline void DoCastAtTarget(TargetedSpell spell)
+ {
+ SpellCastTargets targets;
+ targets.SetUnitTarget(spell.second);
+ spell.first->prepare(&targets);
+ }
+
virtual Unit* SelectAttackTarget() const { return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; }
void DoRangedAttackIfReady();
void DoAutoAttackIfReady();
+ // Cancels all shapeshifts that the player could voluntarily cancel
+ void CancelAllShapeshifts();
+
private:
- bool _isSelfHealer;
+ uint8 const _selfSpec;
+ bool const _isSelfHealer;
bool _isSelfRangedAttacker;
};
class SimpleCharmedPlayerAI : public PlayerAI
{
public:
- SimpleCharmedPlayerAI(Player* player) : PlayerAI(player) { }
+ SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { }
void UpdateAI(uint32 diff) override;
void OnCharmed(bool apply) override;
+
+ protected:
Unit* SelectAttackTarget() const override;
+
+ private:
+ TargetedSpell SelectAppropriateCastForSpec();
+ uint32 _castCheckTimer;
+ bool _chaseCloser;
+ bool _forceFacing;
};
#endif
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 9ce37fd0c32..316a9704cac 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -32,7 +32,7 @@ struct TSpellSummary
uint8 Effects; // set of enum SelectEffect
} extern* SpellSummary;
-void SummonList::DoZoneInCombat(uint32 entry)
+void SummonList::DoZoneInCombat(uint32 entry, float maxRangeToNearestTarget)
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
@@ -41,7 +41,7 @@ void SummonList::DoZoneInCombat(uint32 entry)
if (summon && summon->IsAIEnabled
&& (!entry || summon->GetEntry() == entry))
{
- summon->AI()->DoZoneInCombat();
+ summon->AI()->DoZoneInCombat(nullptr, maxRangeToNearestTarget);
}
}
}
@@ -183,11 +183,11 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec
{
//No target so we can't cast
if (!target)
- return NULL;
+ return nullptr;
//Silenced so we can't cast
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
- return NULL;
+ return nullptr;
//Using the extended script system we first create a list of viable spells
SpellInfo const* apSpell[MAX_CREATURE_SPELLS];
@@ -195,7 +195,7 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec
uint32 spellCount = 0;
- SpellInfo const* tempSpell = NULL;
+ SpellInfo const* tempSpell = nullptr;
//Check if each spell is viable(set it to null if not)
for (uint32 i = 0; i < MAX_CREATURE_SPELLS; i++)
@@ -251,7 +251,7 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec
//We got our usable spells so now lets randomly pick one
if (!spellCount)
- return NULL;
+ return nullptr;
return apSpell[urand(0, spellCount - 1)];
}
@@ -327,7 +327,7 @@ void ScriptedAI::DoTeleportAll(float x, float y, float z, float o)
Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 minHPDiff)
{
- Unit* unit = NULL;
+ Unit* unit = nullptr;
Trinity::MostHPMissingInRange u_check(me, range, minHPDiff);
Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check);
me->VisitNearbyObject(range, searcher);
@@ -357,7 +357,7 @@ std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 u
Player* ScriptedAI::GetPlayerAtMinimumRange(float minimumRange)
{
- Player* player = NULL;
+ Player* player = nullptr;
CellCoord pair(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
Cell cell(pair);
@@ -507,6 +507,14 @@ void BossAI::_EnterCombat()
ScheduleTasks();
}
+bool BossAI::CanRespawn()
+{
+ if (instance && instance->GetBossState(_bossId) == DONE)
+ return false;
+
+ return true;
+}
+
void BossAI::TeleportCheaters()
{
float x, y, z;
@@ -547,7 +555,7 @@ void BossAI::UpdateAI(uint32 diff)
DoMeleeAttackIfReady();
}
-void BossAI::_DespawnAtEvade(uint32 delayToRespawn)
+void BossAI::_DespawnAtEvade(uint32 delayToRespawn, Creature* who)
{
if (delayToRespawn < 2)
{
@@ -555,18 +563,28 @@ void BossAI::_DespawnAtEvade(uint32 delayToRespawn)
delayToRespawn = 2;
}
- uint32 corpseDelay = me->GetCorpseDelay();
- uint32 respawnDelay = me->GetRespawnDelay();
+ if (!who)
+ who = me;
- me->SetCorpseDelay(1);
- me->SetRespawnDelay(delayToRespawn - 1);
+ if (TempSummon* whoSummon = who->ToTempSummon())
+ {
+ TC_LOG_WARN("scripts", "_DespawnAtEvade called on a temporary summon.");
+ whoSummon->UnSummon();
+ return;
+ }
- me->DespawnOrUnsummon();
+ uint32 corpseDelay = who->GetCorpseDelay();
+ uint32 respawnDelay = who->GetRespawnDelay();
- me->SetCorpseDelay(corpseDelay);
- me->SetRespawnDelay(respawnDelay);
+ who->SetCorpseDelay(1);
+ who->SetRespawnDelay(delayToRespawn - 1);
- if (instance)
+ who->DespawnOrUnsummon();
+
+ who->SetCorpseDelay(corpseDelay);
+ who->SetRespawnDelay(respawnDelay);
+
+ if (instance && who == me)
instance->SetBossState(_bossId, FAIL);
}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 42befa26a23..bbc7e8beadb 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -114,7 +114,7 @@ public:
}
}
- void DoZoneInCombat(uint32 entry = 0);
+ void DoZoneInCombat(uint32 entry = 0, float maxRangeToNearestTarget = 250.0f);
void RemoveNotExisting();
bool HasEntry(uint32 entry) const;
@@ -361,13 +361,15 @@ class TC_GAME_API BossAI : public ScriptedAI
void JustReachedHome() override { _JustReachedHome(); }
bool CanAIAttack(Unit const* target) const override { return CheckBoundary(target); }
+ bool CanRespawn() override;
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
- void _DespawnAtEvade(uint32 delayToRespawn = 30);
+ void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr);
+ void _DespawnAtEvade(Milliseconds const& time, Creature* who = nullptr) { _DespawnAtEvade(uint32(time.count()), who); }
void TeleportCheaters();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index fa80634e0f0..1b8b472b805 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -72,7 +72,7 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* who)
return false;
//experimental (unknown) flag not present
- if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
+ if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
//not a player
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index 778edf8cab5..bd07e688fb0 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -69,7 +69,7 @@ bool FollowerAI::AssistPlayerInCombat(Unit* who)
return false;
//experimental (unknown) flag not present
- if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
+ if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
//not a player
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 60df4fe6d5a..e21f59fe582 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -468,7 +468,7 @@ bool SmartAI::AssistPlayerInCombat(Unit* who)
return false;
//experimental (unknown) flag not present
- if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
+ if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
//not a player
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index a18f0a6574b..75b9752a193 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -929,9 +929,21 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
}
- instance->SetData(e.action.setInstanceData.field, e.action.setInstanceData.data);
- TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: Field: %u, data: %u",
- e.action.setInstanceData.field, e.action.setInstanceData.data);
+ switch (e.action.setInstanceData.type)
+ {
+ case 0:
+ instance->SetData(e.action.setInstanceData.field, e.action.setInstanceData.data);
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetData Field: %u, data: %u",
+ e.action.setInstanceData.field, e.action.setInstanceData.data);
+ break;
+ case 1:
+ instance->SetBossState(e.action.setInstanceData.field, static_cast<EncounterState>(e.action.setInstanceData.data));
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetBossState BossId: %u, State: %u (%s)",
+ e.action.setInstanceData.field, e.action.setInstanceData.data, InstanceScript::GetBossStateName(e.action.setInstanceData.data).c_str());
+ break;
+ default: // Static analysis
+ break;
+ }
break;
}
case SMART_ACTION_SET_INST_DATA64:
@@ -1037,10 +1049,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
target->DespawnOrUnsummon(e.action.forceDespawn.delay);
}
else if (GameObject* goTarget = (*itr)->ToGameObject())
- {
- if (IsSmartGO(goTarget))
- goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1);
- }
+ goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1);
}
delete targets;
@@ -1126,22 +1135,29 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
delete targets;
break;
}
- case SMART_ACTION_MOVE_FORWARD:
+ case SMART_ACTION_MOVE_OFFSET:
{
- if (!me)
- break;
+ if (ObjectList* targets = GetTargets(e, unit))
+ {
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ {
+ if (!IsCreature(*itr))
+ continue;
- float x, y, z;
- me->GetClosePoint(x, y, z, me->GetObjectSize() / 3, (float)e.action.moveRandom.distance);
- me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z);
- break;
- }
- case SMART_ACTION_RISE_UP:
- {
- if (!me)
- break;
+ Position pos = (*itr)->GetPosition();
+
+ // Use forward/backward/left/right cartesian plane movement
+ float x, y, z, o;
+ o = pos.GetOrientation();
+ x = pos.GetPositionX() + (std::cos(o - (M_PI / 2))*e.target.x) + (std::cos(o)*e.target.y);
+ y = pos.GetPositionY() + (std::sin(o - (M_PI / 2))*e.target.x) + (std::sin(o)*e.target.y);
+ z = pos.GetPositionZ() + e.target.z;
+ (*itr)->ToCreature()->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z);
+ }
+
+ delete targets;
+ }
- me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + (float)e.action.moveRandom.distance);
break;
}
case SMART_ACTION_SET_VISIBILITY:
@@ -1215,18 +1231,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
ObjectList* targets = GetTargets(e, unit);
if (targets)
{
- float x, y, z, o;
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
{
if (!IsUnit(*itr))
continue;
- (*itr)->GetPosition(x, y, z, o);
- x += e.target.x;
- y += e.target.y;
- z += e.target.z;
- o += e.target.o;
- GetBaseObject()->SummonGameObject(e.action.summonGO.entry, x, y, z, o, 0, 0, 0, 0, e.action.summonGO.despawnTime);
+ Position pos = (*itr)->GetPositionWithOffset(Position(e.target.x, e.target.y, e.target.z, e.target.o));
+ GetBaseObject()->SummonGameObject(e.action.summonGO.entry, pos, G3D::Quat(), e.action.summonGO.despawnTime);
}
delete targets;
@@ -1235,7 +1246,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.GetTargetType() != SMART_TARGET_POSITION)
break;
- GetBaseObject()->SummonGameObject(e.action.summonGO.entry, e.target.x, e.target.y, e.target.z, e.target.o, 0, 0, 0, 0, e.action.summonGO.despawnTime);
+ GetBaseObject()->SummonGameObject(e.action.summonGO.entry, Position(e.target.x, e.target.y, e.target.z, e.target.o), G3D::Quat(), e.action.summonGO.despawnTime);
break;
}
case SMART_ACTION_KILL_UNIT:
@@ -1483,10 +1494,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (TransportBase* trans = me->GetDirectTransport())
trans->CalculatePassengerPosition(dest.x, dest.y, dest.z);
- me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, dest.x, dest.y, dest.z);
+ me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, dest.x, dest.y, dest.z, e.action.MoveToPos.disablePathfinding == 0);
}
else
- me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
+ me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), e.action.MoveToPos.disablePathfinding == 0);
break;
}
case SMART_ACTION_RESPAWN_TARGET:
@@ -2409,7 +2420,7 @@ void SmartScript::InstallTemplate(SmartScriptHolder const& e)
AddEvent(SMART_EVENT_DATA_SET, 0, 0, 0, 0, 0, SMART_ACTION_SET_RUN, e.action.installTtemplate.param3, 0, 0, 0, 0, 0, SMART_TARGET_NONE, 0, 0, 0, 0);
AddEvent(SMART_EVENT_DATA_SET, 0, 0, 0, 0, 0, SMART_ACTION_SET_EVENT_PHASE, 1, 0, 0, 0, 0, 0, SMART_TARGET_NONE, 0, 0, 0, 0);
- AddEvent(SMART_EVENT_UPDATE, SMART_EVENT_FLAG_NOT_REPEATABLE, 1000, 1000, 0, 0, SMART_ACTION_MOVE_FORWARD, e.action.installTtemplate.param4, 0, 0, 0, 0, 0, SMART_TARGET_NONE, 0, 0, 0, 1);
+ AddEvent(SMART_EVENT_UPDATE, SMART_EVENT_FLAG_NOT_REPEATABLE, 1000, 1000, 0, 0, SMART_ACTION_MOVE_OFFSET, 0, 0, 0, 0, 0, 0, SMART_TARGET_SELF, 0, e.action.installTtemplate.param4, 0, 1);
//phase 1: give quest credit on movepoint reached
AddEvent(SMART_EVENT_MOVEMENTINFORM, 0, POINT_MOTION_TYPE, SMART_RANDOM_POINT, 0, 0, SMART_ACTION_SET_DATA, 0, 0, 0, 0, 0, 0, SMART_TARGET_STORED, 1, 0, 0, 1);
//phase 1: despawn after time on movepoint reached
@@ -3359,6 +3370,16 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
}
}
+ // Delay flee for assist event if stunned or rooted
+ if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST)
+ {
+ if (me && me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ {
+ e.timer = 1;
+ return;
+ }
+ }
+
e.active = true;//activate events with cooldown
switch (e.GetEventType())//process ONLY timed events
{
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 86aaf45af88..f0709eb9216 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -19,6 +19,7 @@
#include "ObjectMgr.h"
#include "GridDefines.h"
#include "GridNotifiers.h"
+#include "InstanceScript.h"
#include "SpellMgr.h"
#include "Cell.h"
#include "GameEventMgr.h"
@@ -1156,6 +1157,23 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
}
break;
}
+ case SMART_ACTION_SET_INST_DATA:
+ {
+ if (e.action.setInstanceData.type > 1)
+ {
+ TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u uses invalid data type %u (value range 0-1), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setInstanceData.type);
+ return false;
+ }
+ else if (e.action.setInstanceData.type == 1)
+ {
+ if (e.action.setInstanceData.data > TO_BE_DECIDED)
+ {
+ TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u uses invalid boss state %u (value range 0-5), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setInstanceData.data);
+ return false;
+ }
+ }
+ break;
+ }
case SMART_ACTION_START_CLOSEST_WAYPOINT:
case SMART_ACTION_FOLLOW:
case SMART_ACTION_SET_ORIENTATION:
@@ -1173,13 +1191,11 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_ATTACK_START:
case SMART_ACTION_THREAT_ALL_PCT:
case SMART_ACTION_THREAT_SINGLE_PCT:
- case SMART_ACTION_SET_INST_DATA:
case SMART_ACTION_SET_INST_DATA64:
case SMART_ACTION_AUTO_ATTACK:
case SMART_ACTION_ALLOW_COMBAT_MOVEMENT:
case SMART_ACTION_CALL_FOR_HELP:
case SMART_ACTION_SET_DATA:
- case SMART_ACTION_MOVE_FORWARD:
case SMART_ACTION_SET_VISIBILITY:
case SMART_ACTION_WP_PAUSE:
case SMART_ACTION_SET_FLY:
@@ -1225,7 +1241,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_ADD_GO_FLAG:
case SMART_ACTION_REMOVE_GO_FLAG:
case SMART_ACTION_SUMMON_CREATURE_GROUP:
- case SMART_ACTION_RISE_UP:
+ case SMART_ACTION_MOVE_OFFSET:
case SMART_ACTION_SET_CORPSE_DELAY:
break;
default:
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 3105f087bf7..8b55c3e138c 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -469,7 +469,7 @@ enum SMART_ACTION
SMART_ACTION_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax
SMART_ACTION_RESET_GOBJECT = 32, //
SMART_ACTION_CALL_KILLEDMONSTER = 33, // CreatureId,
- SMART_ACTION_SET_INST_DATA = 34, // Field, Data
+ SMART_ACTION_SET_INST_DATA = 34, // Field, Data, Type (0 = SetData, 1 = SetBossState)
SMART_ACTION_SET_INST_DATA64 = 35, // Field,
SMART_ACTION_UPDATE_TEMPLATE = 36, // Entry
SMART_ACTION_DIE = 37, // No Params
@@ -481,7 +481,6 @@ enum SMART_ACTION
SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL = 43, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to dismount)
SMART_ACTION_SET_INGAME_PHASE_MASK = 44, // mask
SMART_ACTION_SET_DATA = 45, // Field, Data (only creature @todo)
- SMART_ACTION_MOVE_FORWARD = 46, // distance
SMART_ACTION_SET_VISIBILITY = 47, // on/off
SMART_ACTION_SET_ACTIVE = 48, // on/off
SMART_ACTION_ATTACK_START = 49, //
@@ -504,7 +503,7 @@ enum SMART_ACTION
SMART_ACTION_SET_ORIENTATION = 66, //
SMART_ACTION_CREATE_TIMED_EVENT = 67, // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance
SMART_ACTION_PLAYMOVIE = 68, // entry
- SMART_ACTION_MOVE_TO_POS = 69, // PointId, xyz
+ SMART_ACTION_MOVE_TO_POS = 69, // PointId, transport, disablePathfinding
SMART_ACTION_RESPAWN_TARGET = 70, //
SMART_ACTION_EQUIP = 71, // entry, slotmask slot1, slot2, slot3 , only slots with mask set will be sent to client, bits are 1, 2, 4, leaving mask 0 is defaulted to mask 7 (send all), slots1-3 are only used if no entry is set
SMART_ACTION_CLOSE_GOSSIP = 72, // none
@@ -549,7 +548,7 @@ enum SMART_ACTION
SMART_ACTION_GAME_EVENT_STOP = 111, // GameEventId
SMART_ACTION_GAME_EVENT_START = 112, // GameEventId
SMART_ACTION_START_CLOSEST_WAYPOINT = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7
- SMART_ACTION_RISE_UP = 114, // distance
+ SMART_ACTION_MOVE_OFFSET = 114,
SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf
SMART_ACTION_SET_CORPSE_DELAY = 116, // timer
@@ -716,6 +715,7 @@ struct SmartAction
{
uint32 field;
uint32 data;
+ uint32 type;
} setInstanceData;
struct
@@ -958,6 +958,7 @@ struct SmartAction
{
uint32 pointId;
uint32 transport;
+ uint32 disablePathfinding;
} MoveToPos;
struct
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp
index 1b93d072fb6..fa3e8818fe0 100644
--- a/src/server/game/Accounts/AccountMgr.cpp
+++ b/src/server/game/Accounts/AccountMgr.cpp
@@ -487,19 +487,20 @@ void AccountMgr::UpdateAccountAccess(rbac::RBACData* rbac, uint32 accountId, uin
if (rbac && securityLevel == rbac->GetSecurityLevel())
rbac->SetSecurityLevel(securityLevel);
+ SQLTransaction trans = LoginDatabase.BeginTransaction();
// Delete old security level from DB
if (realmId == -1)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS);
stmt->setUInt32(0, accountId);
- LoginDatabase.Execute(stmt);
+ trans->Append(stmt);
}
else
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM);
stmt->setUInt32(0, accountId);
stmt->setUInt32(1, realmId);
- LoginDatabase.Execute(stmt);
+ trans->Append(stmt);
}
// Add new security level
@@ -509,8 +510,10 @@ void AccountMgr::UpdateAccountAccess(rbac::RBACData* rbac, uint32 accountId, uin
stmt->setUInt32(0, accountId);
stmt->setUInt8(1, securityLevel);
stmt->setInt32(2, realmId);
- LoginDatabase.Execute(stmt);
+ trans->Append(stmt);
}
+
+ LoginDatabase.CommitTransaction(trans);
}
rbac::RBACPermission const* AccountMgr::GetRBACPermission(uint32 permissionId) const
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index 9dd56ec8180..741983431dd 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -59,9 +59,9 @@ enum RBACPermissions
// 7 - reuse
// 8 - reuse
// 9 - reuse
- // 10 - reuse
+ // 10 - 7.x only
RBAC_PERM_LOG_GM_TRADE = 11,
- // 12 - reuse
+ // 12 - 7.x only
RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES = 13,
RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_TEAMMASK = 14,
RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_CLASSMASK = 15,
@@ -540,7 +540,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE = 642,
RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS = 643,
RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE = 644,
- RBAC_PERM_COMMAND_RELOAD_GAME_GRAVEYARD_ZONE = 645,
+ RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE = 645,
RBAC_PERM_COMMAND_RELOAD_GAME_TELE = 646,
RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER = 647,
RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE = 648,
@@ -701,6 +701,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_PET_LEVEL = 838,
RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE = 839,
RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840,
+ RBAC_PERM_COMMAND_NEARGRAVEYARD = 841,
// custom permissions 1000+
RBAC_PERM_MAX
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp
index 7f05cd2efd7..6b1dcb85bec 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp
@@ -17,7 +17,7 @@
#include "Log.h"
#include "Item.h"
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
#include "AuctionHouseBotBuyer.h"
AuctionBotBuyer::AuctionBotBuyer() : _checkInterval(20 * MINUTE)
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h
index 563c2cda04a..3e6f263707f 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h
@@ -19,7 +19,7 @@
#define AUCTION_HOUSE_BOT_SELLER_H
#include "Define.h"
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
#include "AuctionHouseBot.h"
struct ItemToSell
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index e69d0f3085c..f44099f6037 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -797,18 +797,21 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl
GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z, float o)
{
// Get map object
- Map* map = sMapMgr->CreateBaseMap(571); // *vomits*
+ Map* map = sMapMgr->CreateBaseMap(m_MapId);
if (!map)
return 0;
+ // Calculate rotation
+ G3D::Quat rot = G3D::Matrix3::fromEulerAnglesZYX(o, 0.f, 0.f);
+
// Create gameobject
GameObject* go = new GameObject;
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, PHASEMASK_NORMAL, x, y, z, o, 0, 0, 0, 0, 100, GO_STATE_READY))
+ if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, GO_STATE_READY))
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u could not be found in the database! Battlefield has not been created!", entry);
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry);
delete go;
- return NULL;
+ return nullptr;
}
// Add to world
diff --git a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp
index 5dd0b6f29c3..37e26d7e448 100644
--- a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp
+++ b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp
@@ -99,7 +99,7 @@ void ArenaTeamMgr::LoadArenaTeams()
uint32 oldMSTime = getMSTime();
// Clean out the trash before loading anything
- CharacterDatabase.Execute("DELETE FROM arena_team_member WHERE arenaTeamId NOT IN (SELECT arenaTeamId FROM arena_team)"); // One-time query
+ CharacterDatabase.DirectExecute("DELETE FROM arena_team_member WHERE arenaTeamId NOT IN (SELECT arenaTeamId FROM arena_team)"); // One-time query
// 0 1 2 3 4 5 6 7 8
QueryResult result = CharacterDatabase.Query("SELECT arenaTeamId, name, captainGuid, type, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor, "
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 100e2e6c7cc..2e66c587e15 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -283,8 +283,12 @@ inline void Battleground::_CheckSafePositions(uint32 diff)
m_ValidStartPositionTimer = 0;
for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
+ {
if (Player* player = ObjectAccessor::FindPlayer(itr->first))
{
+ if (player->IsGameMaster())
+ continue;
+
Position pos = player->GetPosition();
Position const* startPos = GetTeamStartPosition(Battleground::GetTeamIndexByTeamId(player->GetBGTeam()));
if (pos.GetExactDistSq(startPos) > maxDist)
@@ -293,6 +297,7 @@ inline void Battleground::_CheckSafePositions(uint32 diff)
player->TeleportTo(GetMapId(), startPos->GetPositionX(), startPos->GetPositionY(), startPos->GetPositionZ(), startPos->GetOrientation());
}
}
+ }
}
}
@@ -1345,12 +1350,22 @@ bool Battleground::AddObject(uint32 type, uint32 entry, float x, float y, float
Map* map = FindBgMap();
if (!map)
return false;
+
+ G3D::Quat rot(rotation0, rotation1, rotation2, rotation3);
+ // Temporally add safety check for bad spawns and send log (object rotations need to be rechecked in sniff)
+ if (!rotation0 && !rotation1 && !rotation2 && !rotation3)
+ {
+ TC_LOG_DEBUG("bg.battleground", "Battleground::AddObject: gameoobject [entry: %u, object type: %u] for BG (map: %u) has zeroed rotation fields, "
+ "orientation used temporally, but please fix the spawn", entry, type, m_MapId);
+
+ rot = G3D::Matrix3::fromEulerAnglesZYX(o, 0.f, 0.f);
+ }
+
// Must be created this way, adding to godatamap would add it to the base map of the instance
// and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
// So we must create it specific for this instance
GameObject* go = new GameObject;
- if (!go->Create(GetBgMap()->GenerateLowGuid<HighGuid::GameObject>(), entry, GetBgMap(),
- PHASEMASK_NORMAL, x, y, z, o, rotation0, rotation1, rotation2, rotation3, 100, goState))
+ if (!go->Create(GetBgMap()->GenerateLowGuid<HighGuid::GameObject>(), entry, GetBgMap(), PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, goState))
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: cannot create gameobject (entry: %u) for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
index 52e0deaf86f..8c144cbc8be 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
@@ -142,8 +142,8 @@ void BattlegroundIC::PostUpdateImpl(uint32 diff)
{
if (siege->IsAlive())
{
- if (siege->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_UNK_14|UNIT_FLAG_IMMUNE_TO_PC))
- // following sniffs the vehicle always has UNIT_FLAG_UNK_14
+ if (siege->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_CANNOT_SWIM|UNIT_FLAG_IMMUNE_TO_PC))
+ // following sniffs the vehicle always has UNIT_FLAG_CANNOT_SWIM
siege->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_IMMUNE_TO_PC);
else
siege->SetHealth(siege->GetMaxHealth());
@@ -762,7 +762,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture)
if (Creature* siegeEngine = GetBGCreature(siegeType))
{
- siegeEngine->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_UNK_14|UNIT_FLAG_IMMUNE_TO_PC);
+ siegeEngine->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_CANNOT_SWIM|UNIT_FLAG_IMMUNE_TO_PC);
siegeEngine->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]);
}
}
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
index 1942ac9d648..450f5cd9144 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp
@@ -151,8 +151,8 @@ bool BattlegroundSA::ResetObjs()
}
// MAD props for Kiper for discovering those values - 4 hours of his work.
- GetBGObject(BG_SA_BOAT_ONE)->UpdateRotationFields(1.0f, 0.0002f);
- GetBGObject(BG_SA_BOAT_TWO)->UpdateRotationFields(1.0f, 0.00001f);
+ GetBGObject(BG_SA_BOAT_ONE)->SetParentRotation(G3D::Quat(0.f, 0.f, 1.0f, 0.0002f));
+ GetBGObject(BG_SA_BOAT_TWO)->SetParentRotation(G3D::Quat(0.f, 0.f, 1.0f, 0.00001f));
SpawnBGObject(BG_SA_BOAT_ONE, RESPAWN_IMMEDIATELY);
SpawnBGObject(BG_SA_BOAT_TWO, RESPAWN_IMMEDIATELY);
diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp
index 0875ceefff5..a34f0f4d9a2 100644
--- a/src/server/game/Chat/Channels/Channel.cpp
+++ b/src/server/game/Chat/Channels/Channel.cpp
@@ -25,66 +25,66 @@
#include "AccountMgr.h"
#include "Player.h"
-Channel::Channel(std::string const& name, uint32 channelId, uint32 team):
- _announce(true),
- _ownership(true),
- _IsSaved(false),
- _flags(0),
+Channel::Channel(std::string const& name, uint32 channelId, uint32 team /*= 0*/):
+ _announceEnabled(true),
+ _ownershipEnabled(true),
+ _persistentChannel(false),
+ _channelFlags(0),
_channelId(channelId),
- _Team(team),
- _ownerGUID(),
- _name(name),
- _password("")
+ _channelTeam(team),
+ _ownerGuid(),
+ _channelName(name),
+ _channelPassword()
{
// set special flags if built-in channel
if (ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(channelId)) // check whether it's a built-in channel
{
- _announce = false; // no join/leave announces
- _ownership = false; // no ownership handout
+ _announceEnabled = false; // no join/leave announces
+ _ownershipEnabled = false; // no ownership handout
- _flags |= CHANNEL_FLAG_GENERAL; // for all built-in channels
+ _channelFlags |= CHANNEL_FLAG_GENERAL; // for all built-in channels
if (ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel
- _flags |= CHANNEL_FLAG_TRADE;
+ _channelFlags |= CHANNEL_FLAG_TRADE;
if (ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels
- _flags |= CHANNEL_FLAG_CITY;
+ _channelFlags |= CHANNEL_FLAG_CITY;
if (ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel
- _flags |= CHANNEL_FLAG_LFG;
+ _channelFlags |= CHANNEL_FLAG_LFG;
else // for all other channels
- _flags |= CHANNEL_FLAG_NOT_LFG;
+ _channelFlags |= CHANNEL_FLAG_NOT_LFG;
}
else // it's custom channel
{
- _flags |= CHANNEL_FLAG_CUSTOM;
+ _channelFlags |= CHANNEL_FLAG_CUSTOM;
// If storing custom channels in the db is enabled either load or save the channel
if (sWorld->getBoolConfig(CONFIG_PRESERVE_CUSTOM_CHANNELS))
{
PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHANNEL);
stmt->setString(0, name);
- stmt->setUInt32(1, _Team);
+ stmt->setUInt32(1, _channelTeam);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (result) //load
{
Field* fields = result->Fetch();
- _announce = fields[0].GetBool();
- _ownership = fields[1].GetBool();
- _password = fields[2].GetString();
- const char* db_BannedList = fields[3].GetCString();
+ _announceEnabled = fields[0].GetBool();
+ _ownershipEnabled = fields[1].GetBool();
+ _channelPassword = fields[2].GetString();
+ char const* db_BannedList = fields[3].GetCString();
if (db_BannedList)
{
Tokenizer tokens(db_BannedList, ' ');
for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i)
{
- ObjectGuid banned_guid(uint64(strtoull(*i, NULL, 10)));
+ ObjectGuid banned_guid(uint64(atoull(*i)));
if (banned_guid)
{
TC_LOG_DEBUG("chat.system", "Channel(%s) loaded bannedStore %s", name.c_str(), banned_guid.ToString().c_str());
- bannedStore.insert(banned_guid);
+ _bannedStore.insert(banned_guid);
}
}
}
@@ -93,45 +93,44 @@ Channel::Channel(std::string const& name, uint32 channelId, uint32 team):
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL);
stmt->setString(0, name);
- stmt->setUInt32(1, _Team);
+ stmt->setUInt32(1, _channelTeam);
CharacterDatabase.Execute(stmt);
TC_LOG_DEBUG("chat.system", "Channel(%s) saved in database", name.c_str());
}
- _IsSaved = true;
+ _persistentChannel = true;
}
}
}
void Channel::UpdateChannelInDB() const
{
- if (_IsSaved)
+ if (_persistentChannel)
{
std::ostringstream banlist;
- BannedContainer::const_iterator iter;
- for (iter = bannedStore.begin(); iter != bannedStore.end(); ++iter)
+ for (BannedContainer::const_iterator iter = _bannedStore.begin(); iter != _bannedStore.end(); ++iter)
banlist << iter->GetRawValue() << ' ';
std::string banListStr = banlist.str();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL);
- stmt->setBool(0, _announce);
- stmt->setBool(1, _ownership);
- stmt->setString(2, _password);
+ stmt->setBool(0, _announceEnabled);
+ stmt->setBool(1, _ownershipEnabled);
+ stmt->setString(2, _channelPassword);
stmt->setString(3, banListStr);
- stmt->setString(4, _name);
- stmt->setUInt32(5, _Team);
+ stmt->setString(4, _channelName);
+ stmt->setUInt32(5, _channelTeam);
CharacterDatabase.Execute(stmt);
- TC_LOG_DEBUG("chat.system", "Channel(%s) updated in database", _name.c_str());
+ TC_LOG_DEBUG("chat.system", "Channel(%s) updated in database", _channelName.c_str());
}
}
void Channel::UpdateChannelUseageInDB() const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_USAGE);
- stmt->setString(0, _name);
- stmt->setUInt32(1, _Team);
+ stmt->setString(0, _channelName);
+ stmt->setUInt32(1, _channelTeam);
CharacterDatabase.Execute(stmt);
}
@@ -170,7 +169,7 @@ void Channel::JoinChannel(Player* player, std::string const& pass)
return;
}
- if (!_password.empty() && pass != _password)
+ if (!_channelPassword.empty() && pass != _channelPassword)
{
WorldPacket data;
MakeWrongPassword(&data);
@@ -191,17 +190,15 @@ void Channel::JoinChannel(Player* player, std::string const& pass)
player->JoinedChannel(this);
- if (_announce && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
+ if (_announceEnabled && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
{
WorldPacket data;
MakeJoined(&data, guid);
SendToAll(&data);
}
- PlayerInfo pinfo;
- pinfo.player = guid;
+ PlayerInfo& pinfo = _playersStore[guid];
pinfo.flags = MEMBER_FLAG_NONE;
- playersStore[guid] = pinfo;
WorldPacket data;
MakeYouJoined(&data);
@@ -213,14 +210,13 @@ void Channel::JoinChannel(Player* player, std::string const& pass)
if (!IsConstant())
{
// Update last_used timestamp in db
- if (!playersStore.empty())
- UpdateChannelUseageInDB();
+ UpdateChannelUseageInDB();
// If the channel has no owner yet and ownership is allowed, set the new owner.
- if (!_ownerGUID && _ownership)
+ if (!_ownerGuid && _ownershipEnabled)
{
- SetOwner(guid, playersStore.size() > 1);
- playersStore[guid].SetModerator(true);
+ SetOwner(guid, _playersStore.size() > 1);
+ pinfo.SetModerator(true);
}
}
}
@@ -248,11 +244,11 @@ void Channel::LeaveChannel(Player* player, bool send)
data.clear();
}
- bool changeowner = playersStore[guid].IsOwner();
-
- playersStore.erase(guid);
+ PlayerInfo& info = _playersStore.at(guid);
+ bool changeowner = info.IsOwner();
+ _playersStore.erase(guid);
- if (_announce && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
+ if (_announceEnabled && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
{
WorldPacket data;
MakeLeft(&data, guid);
@@ -266,11 +262,12 @@ void Channel::LeaveChannel(Player* player, bool send)
// Update last_used timestamp in db
UpdateChannelUseageInDB();
- // If the channel owner left and there are still playersStore inside, pick a new owner
- if (changeowner && _ownership && !playersStore.empty())
+ // If the channel owner left and there are still players inside, pick a new owner
+ if (changeowner && _ownershipEnabled && !_playersStore.empty())
{
- ObjectGuid newowner = playersStore.begin()->second.player;
- playersStore[newowner].SetModerator(true);
+ auto itr = _playersStore.begin();
+ ObjectGuid newowner = itr->first;
+ itr->second.SetModerator(true);
SetOwner(newowner);
}
}
@@ -288,7 +285,8 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
return;
}
- if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
+ PlayerInfo& info = _playersStore.at(good);
+ if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -306,9 +304,9 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
return;
}
- bool changeowner = _ownerGUID == victim;
+ bool changeowner = _ownerGuid == victim;
- if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && changeowner && good != _ownerGUID)
+ if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && changeowner && good != _ownerGuid)
{
WorldPacket data;
MakeNotOwner(&data);
@@ -318,7 +316,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
if (ban && !IsBanned(victim))
{
- bannedStore.insert(victim);
+ _bannedStore.insert(victim);
UpdateChannelInDB();
if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
@@ -335,13 +333,13 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
SendToAll(&data);
}
- playersStore.erase(victim);
+ _playersStore.erase(victim);
bad->LeftChannel(this);
- if (changeowner && _ownership && !playersStore.empty())
+ if (changeowner && _ownershipEnabled && !_playersStore.empty())
{
ObjectGuid newowner = good;
- playersStore[newowner].SetModerator(true);
+ info.SetModerator(true);
SetOwner(newowner);
}
}
@@ -358,7 +356,8 @@ void Channel::UnBan(Player const* player, std::string const& badname)
return;
}
- if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
+ PlayerInfo& info = _playersStore.at(good);
+ if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -377,7 +376,7 @@ void Channel::UnBan(Player const* player, std::string const& badname)
return;
}
- bannedStore.erase(victim);
+ _bannedStore.erase(victim);
WorldPacket data;
MakePlayerUnbanned(&data, victim, good);
@@ -399,7 +398,8 @@ void Channel::Password(Player const* player, std::string const& pass)
return;
}
- if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
+ PlayerInfo& info = _playersStore.at(guid);
+ if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -407,7 +407,7 @@ void Channel::Password(Player const* player, std::string const& pass)
return;
}
- _password = pass;
+ _channelPassword = pass;
WorldPacket data;
MakePasswordChanged(&data, guid);
@@ -428,7 +428,8 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo
return;
}
- if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
+ PlayerInfo& info = _playersStore.at(guid);
+ if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -436,7 +437,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo
return;
}
- if (guid == _ownerGUID && std::string(p2n) == player->GetName() && mod)
+ if (guid == _ownerGuid && std::string(p2n) == player->GetName() && mod)
return;
Player* newp = ObjectAccessor::FindConnectedPlayerByName(p2n);
@@ -453,7 +454,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo
return;
}
- if (_ownerGUID == victim && _ownerGUID != guid)
+ if (_ownerGuid == victim && _ownerGuid != guid)
{
WorldPacket data;
MakeNotOwner(&data);
@@ -479,7 +480,7 @@ void Channel::SetOwner(Player const* player, std::string const& newname)
return;
}
- if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGUID)
+ if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGuid)
{
WorldPacket data;
MakeNotOwner(&data);
@@ -501,7 +502,8 @@ void Channel::SetOwner(Player const* player, std::string const& newname)
return;
}
- playersStore[victim].SetModerator(true);
+ PlayerInfo& info = _playersStore.at(victim);
+ info.SetModerator(true);
SetOwner(victim);
}
@@ -515,7 +517,7 @@ void Channel::SendWhoOwner(ObjectGuid guid)
SendToOne(&data, guid);
}
-void Channel::List(Player const* player)
+void Channel::List(Player const* player) const
{
ObjectGuid guid = player->GetGUID();
@@ -530,7 +532,7 @@ void Channel::List(Player const* player)
TC_LOG_DEBUG("chat.system", "SMSG_CHANNEL_LIST %s Channel: %s",
player->GetSession()->GetPlayerInfo().c_str(), GetName().c_str());
- WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+playersStore.size()*(8+1));
+ WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+_playersStore.size()*(8+1));
data << uint8(1); // channel type?
data << GetName(); // channel name
data << uint8(GetFlags()); // channel flags?
@@ -541,7 +543,7 @@ void Channel::List(Player const* player)
uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST);
uint32 count = 0;
- for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i)
+ for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i)
{
Player* member = ObjectAccessor::FindConnectedPlayer(i->first);
@@ -575,7 +577,8 @@ void Channel::Announce(Player const* player)
return;
}
- if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
+ PlayerInfo& info = _playersStore.at(guid);
+ if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -583,10 +586,10 @@ void Channel::Announce(Player const* player)
return;
}
- _announce = !_announce;
+ _announceEnabled = !_announceEnabled;
WorldPacket data;
- if (_announce)
+ if (_announceEnabled)
MakeAnnouncementsOn(&data, guid);
else
MakeAnnouncementsOff(&data, guid);
@@ -595,7 +598,7 @@ void Channel::Announce(Player const* player)
UpdateChannelInDB();
}
-void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang)
+void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) const
{
if (what.empty())
return;
@@ -612,7 +615,8 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang)
return;
}
- if (playersStore[guid].IsMuted())
+ PlayerInfo const& info = _playersStore.at(guid);
+ if (info.IsMuted())
{
WorldPacket data;
MakeMuted(&data);
@@ -622,11 +626,11 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang)
WorldPacket data;
if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
- ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), player, player, what, 0, _name);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), player, player, what, 0, _channelName);
else
- ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), guid, guid, what, 0, "", "", 0, false, _name);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), guid, guid, what, 0, "", "", 0, false, _channelName);
- SendToAll(&data, !playersStore[guid].IsModerator() ? guid : ObjectGuid::Empty);
+ SendToAll(&data, !info.IsModerator() ? guid : ObjectGuid::Empty);
}
void Channel::Invite(Player const* player, std::string const& newname)
@@ -691,28 +695,31 @@ void Channel::Invite(Player const* player, std::string const& newname)
void Channel::SetOwner(ObjectGuid guid, bool exclaim)
{
- if (_ownerGUID)
+ if (_ownerGuid)
{
- // [] will re-add player after it possible removed
- PlayerContainer::iterator p_itr = playersStore.find(_ownerGUID);
- if (p_itr != playersStore.end())
- p_itr->second.SetOwner(false);
+ auto itr = _playersStore.find(_ownerGuid);
+ if (itr != _playersStore.end())
+ itr->second.SetOwner(false);
}
- _ownerGUID = guid;
- if (_ownerGUID)
+ _ownerGuid = guid;
+ if (_ownerGuid)
{
- uint8 oldFlag = GetPlayerFlags(_ownerGUID);
- playersStore[_ownerGUID].SetModerator(true);
- playersStore[_ownerGUID].SetOwner(true);
+ uint8 oldFlag = GetPlayerFlags(_ownerGuid);
+ auto itr = _playersStore.find(_ownerGuid);
+ if (itr == _playersStore.end())
+ return;
+
+ itr->second.SetModerator(true);
+ itr->second.SetOwner(true);
WorldPacket data;
- MakeModeChange(&data, _ownerGUID, oldFlag);
+ MakeModeChange(&data, _ownerGuid, oldFlag);
SendToAll(&data);
if (exclaim)
{
- MakeOwnerChanged(&data, _ownerGUID);
+ MakeOwnerChanged(&data, _ownerGuid);
SendToAll(&data);
}
@@ -720,58 +727,58 @@ void Channel::SetOwner(ObjectGuid guid, bool exclaim)
}
}
-void Channel::SendToAll(WorldPacket* data, ObjectGuid guid)
+void Channel::SendToAll(WorldPacket* data, ObjectGuid guid) const
{
- for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i)
+ for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i)
if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first))
if (!guid || !player->GetSocial()->HasIgnore(guid.GetCounter()))
player->GetSession()->SendPacket(data);
}
-void Channel::SendToAllButOne(WorldPacket* data, ObjectGuid who)
+void Channel::SendToAllButOne(WorldPacket* data, ObjectGuid who) const
{
- for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i)
+ for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i)
if (i->first != who)
if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first))
player->GetSession()->SendPacket(data);
}
-void Channel::SendToOne(WorldPacket* data, ObjectGuid who)
+void Channel::SendToOne(WorldPacket* data, ObjectGuid who) const
{
if (Player* player = ObjectAccessor::FindConnectedPlayer(who))
player->GetSession()->SendPacket(data);
}
-void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/)
+void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) const
{
}
-void Channel::DeVoice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/)
+void Channel::DeVoice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) const
{
}
-void Channel::MakeNotifyPacket(WorldPacket* data, uint8 notify_type)
+void Channel::MakeNotifyPacket(WorldPacket* data, uint8 notify_type) const
{
- data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + _name.size());
+ data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + _channelName.size());
*data << uint8(notify_type);
- *data << _name;
+ *data << _channelName;
}
-void Channel::MakeJoined(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeJoined(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_JOINED_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeLeft(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeLeft(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_LEFT_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeYouJoined(WorldPacket* data)
+void Channel::MakeYouJoined(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE);
*data << uint8(GetFlags());
@@ -779,63 +786,66 @@ void Channel::MakeYouJoined(WorldPacket* data)
*data << uint32(0);
}
-void Channel::MakeYouLeft(WorldPacket* data)
+void Channel::MakeYouLeft(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE);
*data << uint32(GetChannelId());
*data << uint8(IsConstant());
}
-void Channel::MakeWrongPassword(WorldPacket* data)
+void Channel::MakeWrongPassword(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE);
}
-void Channel::MakeNotMember(WorldPacket* data)
+void Channel::MakeNotMember(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE);
}
-void Channel::MakeNotModerator(WorldPacket* data)
+void Channel::MakeNotModerator(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE);
}
-void Channel::MakePasswordChanged(WorldPacket* data, ObjectGuid guid)
+void Channel::MakePasswordChanged(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeOwnerChanged(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE);
*data << uint64(guid);
}
-void Channel::MakePlayerNotFound(WorldPacket* data, std::string const& name)
+void Channel::MakePlayerNotFound(WorldPacket* data, std::string const& name) const
{
MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE);
*data << name;
}
-void Channel::MakeNotOwner(WorldPacket* data)
+void Channel::MakeNotOwner(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE);
}
-void Channel::MakeChannelOwner(WorldPacket* data)
+void Channel::MakeChannelOwner(WorldPacket* data) const
{
- std::string name = "";
+ std::string name;
- if (!sObjectMgr->GetPlayerNameByGUID(_ownerGUID, name) || name.empty())
+ CharacterInfo const* cInfo = sWorld->GetCharacterInfo(_ownerGuid);
+ if (!cInfo || cInfo->Name.empty())
name = "PLAYER_NOT_FOUND";
+ else
+ name = cInfo->Name;
MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE);
- *data << ((IsConstant() || !_ownerGUID) ? "Nobody" : name);
+ *data << ((IsConstant() || !_ownerGuid) ? "Nobody" : name);
}
-void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags)
+void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) const
{
MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE);
*data << uint64(guid);
@@ -843,127 +853,127 @@ void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags)
*data << uint8(GetPlayerFlags(guid));
}
-void Channel::MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeMuted(WorldPacket* data)
+void Channel::MakeMuted(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_MUTED_NOTICE);
}
-void Channel::MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good)
+void Channel::MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const
{
MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE);
*data << uint64(bad);
*data << uint64(good);
}
-void Channel::MakeBanned(WorldPacket* data)
+void Channel::MakeBanned(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_BANNED_NOTICE);
}
-void Channel::MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good)
+void Channel::MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const
{
MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE);
*data << uint64(bad);
*data << uint64(good);
}
-void Channel::MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good)
+void Channel::MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const
{
MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE);
*data << uint64(bad);
*data << uint64(good);
}
-void Channel::MakePlayerNotBanned(WorldPacket* data, const std::string &name)
+void Channel::MakePlayerNotBanned(WorldPacket* data, const std::string &name) const
{
MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE);
*data << name;
}
-void Channel::MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid)
+void Channel::MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeInvite(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeInvite(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_INVITE_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeInviteWrongFaction(WorldPacket* data)
+void Channel::MakeInviteWrongFaction(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE);
}
-void Channel::MakeWrongFaction(WorldPacket* data)
+void Channel::MakeWrongFaction(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE);
}
-void Channel::MakeInvalidName(WorldPacket* data)
+void Channel::MakeInvalidName(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE);
}
-void Channel::MakeNotModerated(WorldPacket* data)
+void Channel::MakeNotModerated(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE);
}
-void Channel::MakePlayerInvited(WorldPacket* data, const std::string& name)
+void Channel::MakePlayerInvited(WorldPacket* data, std::string const& name) const
{
MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE);
*data << name;
}
-void Channel::MakePlayerInviteBanned(WorldPacket* data, const std::string& name)
+void Channel::MakePlayerInviteBanned(WorldPacket* data, std::string const& name) const
{
MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE);
*data << name;
}
-void Channel::MakeThrottled(WorldPacket* data)
+void Channel::MakeThrottled(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE);
}
-void Channel::MakeNotInArea(WorldPacket* data)
+void Channel::MakeNotInArea(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE);
}
-void Channel::MakeNotInLfg(WorldPacket* data)
+void Channel::MakeNotInLfg(WorldPacket* data) const
{
MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE);
}
-void Channel::MakeVoiceOn(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeVoiceOn(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE);
*data << uint64(guid);
}
-void Channel::MakeVoiceOff(WorldPacket* data, ObjectGuid guid)
+void Channel::MakeVoiceOff(WorldPacket* data, ObjectGuid guid) const
{
MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE);
*data << uint64(guid);
}
-void Channel::JoinNotify(ObjectGuid guid)
+void Channel::JoinNotify(ObjectGuid guid) const
{
WorldPacket data(IsConstant() ? SMSG_USERLIST_ADD : SMSG_USERLIST_UPDATE, 8 + 1 + 1 + 4 + GetName().size());
data << uint64(guid);
@@ -978,7 +988,7 @@ void Channel::JoinNotify(ObjectGuid guid)
SendToAll(&data);
}
-void Channel::LeaveNotify(ObjectGuid guid)
+void Channel::LeaveNotify(ObjectGuid guid) const
{
WorldPacket data(SMSG_USERLIST_REMOVE, 8 + 1 + 4 + GetName().size());
data << uint64(guid);
diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h
index 319105d2a8d..23f9e5ae28f 100644
--- a/src/server/game/Chat/Channels/Channel.h
+++ b/src/server/game/Chat/Channels/Channel.h
@@ -19,10 +19,6 @@
#ifndef _CHANNEL_H
#define _CHANNEL_H
-#include <list>
-#include <map>
-#include <string>
-
#include "Common.h"
#include "WorldSession.h"
@@ -53,10 +49,10 @@ enum ChatNotify
CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s.";
CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak.";
CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s.";
- CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are bannedStore from that channel.";
- CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s bannedStore by %s.";
+ CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel.";
+ CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s.";
CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s.";
- CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not bannedStore.";
+ CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned.";
CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel.";
CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'.";
CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s.";
@@ -64,7 +60,7 @@ enum ChatNotify
CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name";
CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated";
CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel";
- CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been bannedStore.";
+ CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned.";
CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message.";
CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone.
CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels.
@@ -85,7 +81,7 @@ enum ChannelFlags
CHANNEL_FLAG_VOICE = 0x80
// General 0x18 = 0x10 | 0x08
// Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04
- // LocalDefence 0x18 = 0x10 | 0x08
+ // LocalDefense 0x18 = 0x10 | 0x08
// GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08
// LookingForGroup 0x50 = 0x40 | 0x10
};
@@ -122,23 +118,25 @@ class TC_GAME_API Channel
{
struct PlayerInfo
{
- ObjectGuid player;
uint8 flags;
bool HasFlag(uint8 flag) const { return (flags & flag) != 0; }
void SetFlag(uint8 flag) { flags |= flag; }
+
bool IsOwner() const { return (flags & MEMBER_FLAG_OWNER) != 0; }
void SetOwner(bool state)
{
if (state) flags |= MEMBER_FLAG_OWNER;
else flags &= ~MEMBER_FLAG_OWNER;
}
+
bool IsModerator() const { return (flags & MEMBER_FLAG_MODERATOR) != 0; }
void SetModerator(bool state)
{
if (state) flags |= MEMBER_FLAG_MODERATOR;
else flags &= ~MEMBER_FLAG_MODERATOR;
}
+
bool IsMuted() const { return (flags & MEMBER_FLAG_MUTED) != 0; }
void SetMuted(bool state)
{
@@ -148,106 +146,121 @@ class TC_GAME_API Channel
};
public:
- Channel(std::string const& name, uint32 channel_id, uint32 Team = 0);
- std::string const& GetName() const { return _name; }
+ Channel(std::string const& name, uint32 channel_id, uint32 team = 0);
+
+ std::string const& GetName() const { return _channelName; }
+
uint32 GetChannelId() const { return _channelId; }
bool IsConstant() const { return _channelId != 0; }
- bool IsAnnounce() const { return _announce; }
bool IsLFG() const { return (GetFlags() & CHANNEL_FLAG_LFG) != 0; }
- std::string const& GetPassword() const { return _password; }
- void SetPassword(std::string const& npassword) { _password = npassword; }
- void SetAnnounce(bool nannounce) { _announce = nannounce; }
- uint32 GetNumPlayers() const { return playersStore.size(); }
- uint8 GetFlags() const { return _flags; }
- bool HasFlag(uint8 flag) const { return (_flags & flag) != 0; }
+
+ bool IsAnnounce() const { return _announceEnabled; }
+ void SetAnnounce(bool nannounce) { _announceEnabled = nannounce; }
+
+ std::string const& GetPassword() const { return _channelPassword; }
+ void SetPassword(std::string const& npassword) { _channelPassword = npassword; }
+
+ uint32 GetNumPlayers() const { return _playersStore.size(); }
+
+ uint8 GetFlags() const { return _channelFlags; }
+ bool HasFlag(uint8 flag) const { return (_channelFlags & flag) != 0; }
void JoinChannel(Player* player, std::string const& pass);
void LeaveChannel(Player* player, bool send = true);
+
void KickOrBan(Player const* player, std::string const& badname, bool ban);
void Kick(Player const* player, std::string const& badname) { KickOrBan(player, badname, false); }
void Ban(Player const* player, std::string const& badname) { KickOrBan(player, badname, true); }
+
void UnBan(Player const* player, std::string const& badname);
void Password(Player const* player, std::string const& pass);
void SetMode(Player const* player, std::string const& p2n, bool mod, bool set);
- void SetOwner(ObjectGuid guid, bool exclaim = true);
- void SetOwner(Player const* player, std::string const& name);
- void SendWhoOwner(ObjectGuid guid);
+
void SetModerator(Player const* player, std::string const& newname) { SetMode(player, newname, true, true); }
void UnsetModerator(Player const* player, std::string const& newname) { SetMode(player, newname, true, false); }
void SetMute(Player const* player, std::string const& newname) { SetMode(player, newname, false, true); }
void UnsetMute(Player const* player, std::string const& newname) { SetMode(player, newname, false, false); }
- void List(Player const* player);
+
+ void SetOwner(ObjectGuid guid, bool exclaim = true);
+ void SetOwner(Player const* player, std::string const& name);
+ void SendWhoOwner(ObjectGuid guid);
+
+ void List(Player const* player) const;
void Announce(Player const* player);
- void Say(ObjectGuid guid, std::string const& what, uint32 lang);
+ void Say(ObjectGuid guid, std::string const& what, uint32 lang) const;
void Invite(Player const* player, std::string const& newp);
- void Voice(ObjectGuid guid1, ObjectGuid guid2);
- void DeVoice(ObjectGuid guid1, ObjectGuid guid2);
- void JoinNotify(ObjectGuid guid); // invisible notify
- void LeaveNotify(ObjectGuid guid); // invisible notify
- void SetOwnership(bool ownership) { _ownership = ownership; }
+ void Voice(ObjectGuid guid1, ObjectGuid guid2) const;
+ void DeVoice(ObjectGuid guid1, ObjectGuid guid2) const;
+ void JoinNotify(ObjectGuid guid) const; // invisible notify
+ void LeaveNotify(ObjectGuid guid) const; // invisible notify
+ void SetOwnership(bool ownership) { _ownershipEnabled = ownership; }
static void CleanOldChannelsInDB();
private:
// initial packet data (notify type and channel name)
- void MakeNotifyPacket(WorldPacket* data, uint8 notify_type);
+ void MakeNotifyPacket(WorldPacket* data, uint8 notify_type) const;
// type specific packet data
- void MakeJoined(WorldPacket* data, ObjectGuid guid); //+ 0x00
- void MakeLeft(WorldPacket* data, ObjectGuid guid); //+ 0x01
- void MakeYouJoined(WorldPacket* data); //+ 0x02
- void MakeYouLeft(WorldPacket* data); //+ 0x03
- void MakeWrongPassword(WorldPacket* data); //? 0x04
- void MakeNotMember(WorldPacket* data); //? 0x05
- void MakeNotModerator(WorldPacket* data); //? 0x06
- void MakePasswordChanged(WorldPacket* data, ObjectGuid guid); //+ 0x07
- void MakeOwnerChanged(WorldPacket* data, ObjectGuid guid); //? 0x08
- void MakePlayerNotFound(WorldPacket* data, std::string const& name); //+ 0x09
- void MakeNotOwner(WorldPacket* data); //? 0x0A
- void MakeChannelOwner(WorldPacket* data); //? 0x0B
- void MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags);//+ 0x0C
- void MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid); //+ 0x0D
- void MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid); //+ 0x0E
- void MakeMuted(WorldPacket* data); //? 0x11
- void MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good);//? 0x12
- void MakeBanned(WorldPacket* data); //? 0x13
- void MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good);//? 0x14
- void MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good);//? 0x15
- void MakePlayerNotBanned(WorldPacket* data, std::string const& name); //? 0x16
- void MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid); //+ 0x17
- void MakeInvite(WorldPacket* data, ObjectGuid guid); //? 0x18
- void MakeInviteWrongFaction(WorldPacket* data); //? 0x19
- void MakeWrongFaction(WorldPacket* data); //? 0x1A
- void MakeInvalidName(WorldPacket* data); //? 0x1B
- void MakeNotModerated(WorldPacket* data); //? 0x1C
- void MakePlayerInvited(WorldPacket* data, std::string const& name); //+ 0x1D
- void MakePlayerInviteBanned(WorldPacket* data, std::string const& name);//? 0x1E
- void MakeThrottled(WorldPacket* data); //? 0x1F
- void MakeNotInArea(WorldPacket* data); //? 0x20
- void MakeNotInLfg(WorldPacket* data); //? 0x21
- void MakeVoiceOn(WorldPacket* data, ObjectGuid guid); //+ 0x22
- void MakeVoiceOff(WorldPacket* data, ObjectGuid guid); //+ 0x23
-
- void SendToAll(WorldPacket* data, ObjectGuid guid = ObjectGuid::Empty);
- void SendToAllButOne(WorldPacket* data, ObjectGuid who);
- void SendToOne(WorldPacket* data, ObjectGuid who);
-
- bool IsOn(ObjectGuid who) const { return playersStore.find(who) != playersStore.end(); }
- bool IsBanned(ObjectGuid guid) const { return bannedStore.find(guid) != bannedStore.end(); }
+ void MakeJoined(WorldPacket* data, ObjectGuid guid) const; //+ 0x00
+ void MakeLeft(WorldPacket* data, ObjectGuid guid) const; //+ 0x01
+ void MakeYouJoined(WorldPacket* data) const; //+ 0x02
+ void MakeYouLeft(WorldPacket* data) const; //+ 0x03
+ void MakeWrongPassword(WorldPacket* data) const; //? 0x04
+ void MakeNotMember(WorldPacket* data) const; //? 0x05
+ void MakeNotModerator(WorldPacket* data) const; //? 0x06
+ void MakePasswordChanged(WorldPacket* data, ObjectGuid guid) const; //+ 0x07
+ void MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) const; //? 0x08
+ void MakePlayerNotFound(WorldPacket* data, std::string const& name) const; //+ 0x09
+ void MakeNotOwner(WorldPacket* data) const; //? 0x0A
+ void MakeChannelOwner(WorldPacket* data) const; //? 0x0B
+ void MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) const; //+ 0x0C
+ void MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) const; //+ 0x0D
+ void MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) const; //+ 0x0E
+ void MakeMuted(WorldPacket* data) const; //? 0x11
+ void MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x12
+ void MakeBanned(WorldPacket* data) const; //? 0x13
+ void MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x14
+ void MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x15
+ void MakePlayerNotBanned(WorldPacket* data, std::string const& name) const; //? 0x16
+ void MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) const; //+ 0x17
+ void MakeInvite(WorldPacket* data, ObjectGuid guid) const; //? 0x18
+ void MakeInviteWrongFaction(WorldPacket* data) const; //? 0x19
+ void MakeWrongFaction(WorldPacket* data) const; //? 0x1A
+ void MakeInvalidName(WorldPacket* data) const; //? 0x1B
+ void MakeNotModerated(WorldPacket* data) const; //? 0x1C
+ void MakePlayerInvited(WorldPacket* data, std::string const& name) const; //+ 0x1D
+ void MakePlayerInviteBanned(WorldPacket* data, std::string const& name) const; //? 0x1E
+ void MakeThrottled(WorldPacket* data) const; //? 0x1F
+ void MakeNotInArea(WorldPacket* data) const; //? 0x20
+ void MakeNotInLfg(WorldPacket* data) const; //? 0x21
+ void MakeVoiceOn(WorldPacket* data, ObjectGuid guid) const; //+ 0x22
+ void MakeVoiceOff(WorldPacket* data, ObjectGuid guid) const; //+ 0x23
+
+ void SendToAll(WorldPacket* data, ObjectGuid guid = ObjectGuid::Empty) const;
+ void SendToAllButOne(WorldPacket* data, ObjectGuid who) const;
+ void SendToOne(WorldPacket* data, ObjectGuid who) const;
+
+ bool IsOn(ObjectGuid who) const { return _playersStore.count(who) != 0; }
+ bool IsBanned(ObjectGuid guid) const { return _bannedStore.count(guid) != 0; }
void UpdateChannelInDB() const;
void UpdateChannelUseageInDB() const;
uint8 GetPlayerFlags(ObjectGuid guid) const
{
- PlayerContainer::const_iterator itr = playersStore.find(guid);
- return itr != playersStore.end() ? itr->second.flags : 0;
+ PlayerContainer::const_iterator itr = _playersStore.find(guid);
+ return itr != _playersStore.end() ? itr->second.flags : 0;
}
void SetModerator(ObjectGuid guid, bool set)
{
- if (playersStore[guid].IsModerator() != set)
+ if (!IsOn(guid))
+ return;
+
+ PlayerInfo& playerInfo = _playersStore.at(guid);
+ if (playerInfo.IsModerator() != set)
{
uint8 oldFlag = GetPlayerFlags(guid);
- playersStore[guid].SetModerator(set);
+ playerInfo.SetModerator(set);
WorldPacket data;
MakeModeChange(&data, guid, oldFlag);
@@ -257,10 +270,14 @@ class TC_GAME_API Channel
void SetMute(ObjectGuid guid, bool set)
{
- if (playersStore[guid].IsMuted() != set)
+ if (!IsOn(guid))
+ return;
+
+ PlayerInfo& playerInfo = _playersStore.at(guid);
+ if (playerInfo.IsMuted() != set)
{
uint8 oldFlag = GetPlayerFlags(guid);
- playersStore[guid].SetMuted(set);
+ playerInfo.SetMuted(set);
WorldPacket data;
MakeModeChange(&data, guid, oldFlag);
@@ -269,19 +286,20 @@ class TC_GAME_API Channel
}
typedef std::map<ObjectGuid, PlayerInfo> PlayerContainer;
- typedef GuidSet BannedContainer;
+ typedef GuidUnorderedSet BannedContainer;
+
+ bool _announceEnabled; //< Whether we should broadcast a packet whenever a player joins/exits the channel
+ bool _ownershipEnabled; //< Whether the channel has to maintain an owner
+ bool _persistentChannel; //< Whether the channel is saved to DB
- bool _announce;
- bool _ownership;
- bool _IsSaved;
- uint8 _flags;
+ uint8 _channelFlags;
uint32 _channelId;
- uint32 _Team;
- ObjectGuid _ownerGUID;
- std::string _name;
- std::string _password;
- PlayerContainer playersStore;
- BannedContainer bannedStore;
+ uint32 _channelTeam;
+ ObjectGuid _ownerGuid;
+ std::string _channelName;
+ std::string _channelPassword;
+ PlayerContainer _playersStore;
+ BannedContainer _bannedStore;
};
#endif
diff --git a/src/server/game/Chat/Channels/ChannelMgr.cpp b/src/server/game/Chat/Channels/ChannelMgr.cpp
index 3e9a633729a..043d4bdc2bc 100644
--- a/src/server/game/Chat/Channels/ChannelMgr.cpp
+++ b/src/server/game/Chat/Channels/ChannelMgr.cpp
@@ -39,14 +39,14 @@ ChannelMgr* ChannelMgr::forTeam(uint32 team)
if (team == HORDE)
return &hordeChannelMgr;
- return NULL;
+ return nullptr;
}
Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId)
{
std::wstring wname;
if (!Utf8toWStr(name, wname))
- return NULL;
+ return nullptr;
wstrToLower(wname);
@@ -66,7 +66,7 @@ Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pk
{
std::wstring wname;
if (!Utf8toWStr(name, wname))
- return NULL;
+ return nullptr;
wstrToLower(wname);
@@ -81,7 +81,7 @@ Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pk
player->GetSession()->SendPacket(&data);
}
- return NULL;
+ return nullptr;
}
return i->second;
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index ec90a5f7efb..27fa7ffb409 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -81,7 +81,7 @@ bool ChatHandler::isAvailable(ChatCommand const& cmd) const
bool ChatHandler::HasLowerSecurity(Player* target, ObjectGuid guid, bool strong)
{
- WorldSession* target_session = NULL;
+ WorldSession* target_session = nullptr;
uint32 target_account = 0;
if (target)
@@ -116,7 +116,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac
else if (target_account)
target_sec = AccountMgr::GetSecurity(target_account, realm.Id.Realm);
else
- return true; // caller must report error for (target == NULL && target_account == 0)
+ return true; // caller must report error for (target == nullptr && target_account == 0)
AccountTypes target_ac_sec = AccountTypes(target_sec);
if (m_session->GetSecurity() < target_ac_sec || (strong && m_session->GetSecurity() <= target_ac_sec))
@@ -129,7 +129,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac
return false;
}
-bool ChatHandler::hasStringAbbr(const char* name, const char* part)
+bool ChatHandler::hasStringAbbr(char const* name, char const* part)
{
// non "" command
if (*name)
@@ -166,7 +166,7 @@ void ChatHandler::SendSysMessage(const char *str, bool escapeCharacters)
{
size_t startPos = 0;
std::ostringstream o;
- while (const char* charPos = strchr(str + startPos, '|'))
+ while (char const* charPos = strchr(str + startPos, '|'))
{
o.write(str + startPos, charPos - str - startPos);
o << "||";
@@ -184,7 +184,7 @@ void ChatHandler::SendSysMessage(const char *str, bool escapeCharacters)
while (char* line = LineFromMessage(pos))
{
- BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line);
+ BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
m_session->SendPacket(&data);
}
@@ -202,7 +202,7 @@ void ChatHandler::SendGlobalSysMessage(const char *str)
while (char* line = LineFromMessage(pos))
{
- BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line);
+ BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
sWorld->SendGlobalMessage(&data);
}
@@ -220,7 +220,7 @@ void ChatHandler::SendGlobalGMSysMessage(const char *str)
while (char* line = LineFromMessage(pos))
{
- BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line);
+ BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line);
sWorld->SendGlobalGMMessage(&data);
}
@@ -232,7 +232,7 @@ void ChatHandler::SendSysMessage(uint32 entry)
SendSysMessage(GetTrinityString(entry));
}
-bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, const char* text, std::string const& fullcmd)
+bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, char const* text, std::string const& fullcmd)
{
char const* oldtext = text;
std::string cmd = "";
@@ -450,7 +450,7 @@ Valid examples:
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3)
{
const char validSequence[6] = "cHhhr";
- const char* validSequenceIterator = validSequence;
+ char const* validSequenceIterator = validSequence;
const std::string validCommands = "cHhr|";
while (*message)
@@ -525,7 +525,7 @@ bool ChatHandler::ShowHelpForSubCommands(std::vector<ChatCommand> const& table,
return true;
}
-bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd)
+bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, char const* cmd)
{
if (*cmd)
{
@@ -539,7 +539,7 @@ bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, cons
continue;
// have subcommand
- char const* subcmd = (*cmd) ? strtok(NULL, " ") : "";
+ char const* subcmd = (*cmd) ? strtok(nullptr, " ") : "";
if (!table[i].ChildCommands.empty() && subcmd && *subcmd)
{
@@ -694,7 +694,7 @@ size_t ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg chatType, Languag
Player* ChatHandler::getSelectedPlayer()
{
if (!m_session)
- return NULL;
+ return nullptr;
ObjectGuid selected = m_session->GetPlayer()->GetTarget();
if (!selected)
@@ -706,7 +706,7 @@ Player* ChatHandler::getSelectedPlayer()
Unit* ChatHandler::getSelectedUnit()
{
if (!m_session)
- return NULL;
+ return nullptr;
if (Unit* selected = m_session->GetPlayer()->GetSelectedUnit())
return selected;
@@ -717,7 +717,7 @@ Unit* ChatHandler::getSelectedUnit()
WorldObject* ChatHandler::getSelectedObject()
{
if (!m_session)
- return NULL;
+ return nullptr;
ObjectGuid guid = m_session->GetPlayer()->GetTarget();
@@ -730,7 +730,7 @@ WorldObject* ChatHandler::getSelectedObject()
Creature* ChatHandler::getSelectedCreature()
{
if (!m_session)
- return NULL;
+ return nullptr;
return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(), m_session->GetPlayer()->GetTarget());
}
@@ -738,7 +738,7 @@ Creature* ChatHandler::getSelectedCreature()
Player* ChatHandler::getSelectedPlayerOrSelf()
{
if (!m_session)
- return NULL;
+ return nullptr;
ObjectGuid selected = m_session->GetPlayer()->GetTarget();
if (!selected)
@@ -757,14 +757,14 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** s
{
// skip empty
if (!text)
- return NULL;
+ return nullptr;
// skip spaces
while (*text == ' '||*text == '\t'||*text == '\b')
++text;
if (!*text)
- return NULL;
+ return nullptr;
// return non link case
if (text[0] != '|')
@@ -776,28 +776,28 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** s
char* check = strtok(text, "|"); // skip color
if (!check)
- return NULL; // end of data
+ return nullptr; // end of data
- char* cLinkType = strtok(NULL, ":"); // linktype
+ char* cLinkType = strtok(nullptr, ":"); // linktype
if (!cLinkType)
- return NULL; // end of data
+ return nullptr; // end of data
if (strcmp(cLinkType, linkType) != 0)
{
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after retturn from function
+ strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after retturn from function
SendSysMessage(LANG_WRONG_LINK_TYPE);
- return NULL;
+ return nullptr;
}
- char* cKeys = strtok(NULL, "|"); // extract keys and values
- char* cKeysTail = strtok(NULL, "");
+ char* cKeys = strtok(nullptr, "|"); // extract keys and values
+ char* cKeysTail = strtok(nullptr, "");
char* cKey = strtok(cKeys, ":|"); // extract key
if (something1)
- *something1 = strtok(NULL, ":|"); // extract something
+ *something1 = strtok(nullptr, ":|"); // extract something
strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after return from function
+ strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after return from function
return cKey;
}
@@ -805,14 +805,14 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes,
{
// skip empty
if (!text)
- return NULL;
+ return nullptr;
// skip spaces
while (*text == ' '||*text == '\t'||*text == '\b')
++text;
if (!*text)
- return NULL;
+ return nullptr;
// return non link case
if (text[0] != '|')
@@ -830,48 +830,48 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes,
{
char* check = strtok(text, "|"); // skip color
if (!check)
- return NULL; // end of data
+ return nullptr; // end of data
- tail = strtok(NULL, ""); // tail
+ tail = strtok(nullptr, ""); // tail
}
else
tail = text+1; // skip first |
char* cLinkType = strtok(tail, ":"); // linktype
if (!cLinkType)
- return NULL; // end of data
+ return nullptr; // end of data
for (int i = 0; linkTypes[i]; ++i)
{
if (strcmp(cLinkType, linkTypes[i]) == 0)
{
- char* cKeys = strtok(NULL, "|"); // extract keys and values
- char* cKeysTail = strtok(NULL, "");
+ char* cKeys = strtok(nullptr, "|"); // extract keys and values
+ char* cKeysTail = strtok(nullptr, "");
char* cKey = strtok(cKeys, ":|"); // extract key
if (something1)
- *something1 = strtok(NULL, ":|"); // extract something
+ *something1 = strtok(nullptr, ":|"); // extract something
strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after return from function
+ strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after return from function
if (found_idx)
*found_idx = i;
return cKey;
}
}
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after return from function
+ strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after return from function
SendSysMessage(LANG_WRONG_LINK_TYPE);
- return NULL;
+ return nullptr;
}
GameObject* ChatHandler::GetNearbyGameObject()
{
if (!m_session)
- return NULL;
+ return nullptr;
Player* pl = m_session->GetPlayer();
- GameObject* obj = NULL;
+ GameObject* obj = nullptr;
Trinity::NearestGameObjectCheck check(*pl);
Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectCheck> searcher(pl, obj, check);
pl->VisitNearbyGridObject(SIZE_OF_GRIDS, searcher);
@@ -881,7 +881,7 @@ GameObject* ChatHandler::GetNearbyGameObject()
GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(ObjectGuid::LowType lowguid, uint32 entry)
{
if (!m_session)
- return NULL;
+ return nullptr;
Player* pl = m_session->GetPlayer();
@@ -926,7 +926,7 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text)
// number or [name] Shift-click form |color|Htalent:talent_id, rank|h[name]|h|r
// number or [name] Shift-click form |color|Htrade:spell_id, skill_id, max_value, cur_value|h[name]|h|r
int type = 0;
- char* param1_str = NULL;
+ char* param1_str = nullptr;
char* idS = extractKeyFromLink(text, spellKeys, &type, &param1_str);
if (!idS)
return 0;
@@ -974,7 +974,7 @@ GameTele const* ChatHandler::extractGameTeleFromLink(char* text)
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
char* cId = extractKeyFromLink(text, "Htele");
if (!cId)
- return NULL;
+ return nullptr;
// id case (explicit or from shift link)
if (cId[0] >= '0' || cId[0] >= '9')
@@ -1064,7 +1064,7 @@ std::string ChatHandler::extractPlayerNameFromLink(char* text)
return name;
}
-bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid /*=NULL*/, std::string* player_name /*= NULL*/)
+bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid /*=nullptr*/, std::string* player_name /*= nullptr*/)
{
if (args && *args)
{
@@ -1120,12 +1120,12 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* p
void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2)
{
char* p1 = strtok(args, " ");
- char* p2 = strtok(NULL, " ");
+ char* p2 = strtok(nullptr, " ");
if (!p2)
{
p2 = p1;
- p1 = NULL;
+ p1 = nullptr;
}
if (arg1)
@@ -1138,7 +1138,7 @@ void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2)
char* ChatHandler::extractQuotedArg(char* args)
{
if (!args || !*args)
- return NULL;
+ return nullptr;
if (*args == '"')
return strtok(args+1, "\"");
@@ -1151,9 +1151,9 @@ char* ChatHandler::extractQuotedArg(char* args)
continue;
}
- // return NULL if we reached the end of the string
+ // return nullptr if we reached the end of the string
if (!*args)
- return NULL;
+ return nullptr;
// since we skipped all spaces, we expect another token now
if (*args == '"')
@@ -1171,7 +1171,7 @@ char* ChatHandler::extractQuotedArg(char* args)
return strtok(args + 1, "\"");
}
else
- return NULL;
+ return nullptr;
}
}
@@ -1223,9 +1223,9 @@ bool CliHandler::needReportToTarget(Player* /*chr*/) const
return true;
}
-bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline)
+bool ChatHandler::GetPlayerGroupAndGUIDByName(char const* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline)
{
- player = NULL;
+ player = nullptr;
guid.Clear();
if (cname)
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index 1c9368275ad..a700afdf97d 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -67,7 +67,7 @@ class TC_GAME_API ChatHandler
// Builds chat packet and returns receiver guid position in the packet to substitute in whisper builders
static size_t BuildChatPacket(WorldPacket& data, ChatMsg chatType, Language language, WorldObject const* sender, WorldObject const* receiver, std::string const& message, uint32 achievementId = 0, std::string const& channelName = "", LocaleConstant locale = DEFAULT_LOCALE);
- static char* LineFromMessage(char*& pos) { char* start = strtok(pos, "\n"); pos = NULL; return start; }
+ static char* LineFromMessage(char*& pos) { char* start = strtok(pos, "\n"); pos = nullptr; return start; }
// function with different implementation for chat/console
virtual char const* GetTrinityString(uint32 entry) const;
@@ -76,7 +76,7 @@ class TC_GAME_API ChatHandler
void SendSysMessage(uint32 entry);
template<typename... Args>
- void PSendSysMessage(const char* fmt, Args&&... args)
+ void PSendSysMessage(char const* fmt, Args&&... args)
{
SendSysMessage(Trinity::StringFormat(fmt, std::forward<Args>(args)...).c_str());
}
@@ -93,15 +93,15 @@ class TC_GAME_API ChatHandler
return Trinity::StringFormat(GetTrinityString(entry), std::forward<Args>(args)...);
}
- bool ParseCommands(const char* text);
+ bool ParseCommands(char const* text);
static std::vector<ChatCommand> const& getCommandTable();
static void invalidateCommandTable();
- bool isValidChatMessage(const char* msg);
+ bool isValidChatMessage(char const* msg);
void SendGlobalSysMessage(const char *str);
- bool hasStringAbbr(const char* name, const char* part);
+ bool hasStringAbbr(char const* name, char const* part);
// function with different implementation for chat/console
virtual bool isAvailable(ChatCommand const& cmd) const;
@@ -122,20 +122,20 @@ class TC_GAME_API ChatHandler
// Returns either the selected player or self if there is no selected player
Player* getSelectedPlayerOrSelf();
- char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL);
- char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL);
+ char* extractKeyFromLink(char* text, char const* linkType, char** something1 = nullptr);
+ char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = nullptr);
- // if args have single value then it return in arg2 and arg1 == NULL
+ // if args have single value then it return in arg2 and arg1 == nullptr
void extractOptFirstArg(char* args, char** arg1, char** arg2);
char* extractQuotedArg(char* args);
uint32 extractSpellIdFromLink(char* text);
ObjectGuid extractGuidFromLink(char* text);
GameTele const* extractGameTeleFromLink(char* text);
- bool GetPlayerGroupAndGUIDByName(const char* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline = false);
+ bool GetPlayerGroupAndGUIDByName(char const* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline = false);
std::string extractPlayerNameFromLink(char* text);
// select by arg (name/link) or in-game selection online/offline player or self if a creature is selected
- bool extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid = NULL, std::string* player_name = NULL);
+ bool extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid = nullptr, std::string* player_name = nullptr);
std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; }
std::string GetNameLink(Player* chr) const;
@@ -145,15 +145,15 @@ class TC_GAME_API ChatHandler
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val){ sentErrorMessage = val; }
- bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd);
+ bool ShowHelpForCommand(std::vector<ChatCommand> const& table, char const* cmd);
protected:
- explicit ChatHandler() : m_session(NULL), sentErrorMessage(false) { } // for CLI subclass
- static bool SetDataForCommandInTable(std::vector<ChatCommand>& table, const char* text, uint32 permission, std::string const& help, std::string const& fullcommand);
- bool ExecuteCommandInTable(std::vector<ChatCommand> const& table, const char* text, std::string const& fullcmd);
+ explicit ChatHandler() : m_session(nullptr), sentErrorMessage(false) { } // for CLI subclass
+ static bool SetDataForCommandInTable(std::vector<ChatCommand>& table, char const* text, uint32 permission, std::string const& help, std::string const& fullcommand);
+ bool ExecuteCommandInTable(std::vector<ChatCommand> const& table, char const* text, std::string const& fullcmd);
bool ShowHelpForSubCommands(std::vector<ChatCommand> const& table, char const* cmd, char const* subcmd);
private:
- WorldSession* m_session; // != NULL for chat command call and NULL for CLI command
+ WorldSession* m_session; // != nullptr for chat command call and nullptr for CLI command
// common global flag
bool sentErrorMessage;
diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp
index 3602b54bc44..92d69edb5e8 100644
--- a/src/server/game/Chat/ChatLink.cpp
+++ b/src/server/game/Chat/ChatLink.cpp
@@ -71,7 +71,7 @@ inline std::string ReadSkip(std::istringstream& iss, char term)
return res;
}
-inline bool CheckDelimiter(std::istringstream& iss, char delimiter, const char* context)
+inline bool CheckDelimiter(std::istringstream& iss, char delimiter, char const* context)
{
char c = iss.peek();
if (c != delimiter)
@@ -96,7 +96,7 @@ inline bool ReadHex(std::istringstream& iss, uint32& res, uint32 length)
#define DELIMITER ':'
#define PIPE_CHAR '|'
-bool ChatLink::ValidateName(char* buffer, const char* /*context*/)
+bool ChatLink::ValidateName(char* buffer, char const* /*context*/)
{
_name = buffer;
return true;
@@ -170,7 +170,7 @@ bool ItemChatLink::Initialize(std::istringstream& iss)
inline std::string ItemChatLink::FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const
{
std::stringstream ss;
- if (locale == NULL || index >= locale->Name.size())
+ if (locale == nullptr || index >= locale->Name.size())
ss << _item->Name1;
else
ss << locale->Name[index];
@@ -179,13 +179,13 @@ inline std::string ItemChatLink::FormatName(uint8 index, ItemLocale const* local
return ss.str();
}
-bool ItemChatLink::ValidateName(char* buffer, const char* context)
+bool ItemChatLink::ValidateName(char* buffer, char const* context)
{
ChatLink::ValidateName(buffer, context);
- char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : NULL);
+ char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : nullptr);
- bool res = (FormatName(LOCALE_enUS, NULL, suffixStrings) == buffer);
+ bool res = (FormatName(LOCALE_enUS, nullptr, suffixStrings) == buffer);
if (!res)
{
ItemLocale const* il = sObjectMgr->GetItemLocale(_item->ItemId);
@@ -239,7 +239,7 @@ bool QuestChatLink::Initialize(std::istringstream& iss)
return true;
}
-bool QuestChatLink::ValidateName(char* buffer, const char* context)
+bool QuestChatLink::ValidateName(char* buffer, char const* context)
{
ChatLink::ValidateName(buffer, context);
@@ -280,7 +280,7 @@ bool SpellChatLink::Initialize(std::istringstream& iss)
return true;
}
-bool SpellChatLink::ValidateName(char* buffer, const char* context)
+bool SpellChatLink::ValidateName(char* buffer, char const* context)
{
ChatLink::ValidateName(buffer, context);
@@ -373,7 +373,7 @@ bool AchievementChatLink::Initialize(std::istringstream& iss)
return true;
}
-bool AchievementChatLink::ValidateName(char* buffer, const char* context)
+bool AchievementChatLink::ValidateName(char* buffer, char const* context)
{
ChatLink::ValidateName(buffer, context);
@@ -537,7 +537,7 @@ bool GlyphChatLink::Initialize(std::istringstream& iss)
return true;
}
-LinkExtractor::LinkExtractor(const char* msg) : _iss(msg) { }
+LinkExtractor::LinkExtractor(char const* msg) : _iss(msg) { }
LinkExtractor::~LinkExtractor()
{
@@ -549,19 +549,19 @@ LinkExtractor::~LinkExtractor()
bool LinkExtractor::IsValidMessage()
{
const char validSequence[6] = "cHhhr";
- const char* validSequenceIterator = validSequence;
+ char const* validSequenceIterator = validSequence;
char buffer[256];
std::istringstream::pos_type startPos = 0;
uint32 color = 0;
- ChatLink* link = NULL;
+ ChatLink* link = nullptr;
while (!_iss.eof())
{
if (validSequence == validSequenceIterator)
{
- link = NULL;
+ link = nullptr;
_iss.ignore(255, PIPE_CHAR);
startPos = _iss.tellg() - std::istringstream::pos_type(1);
}
diff --git a/src/server/game/Chat/ChatLink.h b/src/server/game/Chat/ChatLink.h
index 0d413ce49df..32ea4b73698 100644
--- a/src/server/game/Chat/ChatLink.h
+++ b/src/server/game/Chat/ChatLink.h
@@ -44,7 +44,7 @@ public:
void SetBounds(std::istringstream::pos_type startPos, std::istringstream::pos_type endPos) { _startPos = startPos; _endPos = endPos; }
virtual bool Initialize(std::istringstream& iss) = 0;
- virtual bool ValidateName(char* buffer, const char* context) = 0;
+ virtual bool ValidateName(char* buffer, char const* context) = 0;
protected:
uint32 _color;
@@ -57,12 +57,12 @@ protected:
class TC_GAME_API ItemChatLink : public ChatLink
{
public:
- ItemChatLink() : ChatLink(), _item(NULL), _suffix(NULL), _property(NULL)
+ ItemChatLink() : ChatLink(), _item(nullptr), _suffix(nullptr), _property(nullptr)
{
memset(_data, 0, sizeof(_data));
}
virtual bool Initialize(std::istringstream& iss) override;
- virtual bool ValidateName(char* buffer, const char* context) override;
+ virtual bool ValidateName(char* buffer, char const* context) override;
protected:
std::string FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const;
@@ -79,7 +79,7 @@ class TC_GAME_API QuestChatLink : public ChatLink
public:
QuestChatLink() : ChatLink(), _quest(nullptr), _questLevel(0) { }
virtual bool Initialize(std::istringstream& iss) override;
- virtual bool ValidateName(char* buffer, const char* context) override;
+ virtual bool ValidateName(char* buffer, char const* context) override;
protected:
Quest const* _quest;
@@ -92,7 +92,7 @@ class TC_GAME_API SpellChatLink : public ChatLink
public:
SpellChatLink() : ChatLink(), _spell(nullptr) { }
virtual bool Initialize(std::istringstream& iss) override;
- virtual bool ValidateName(char* buffer, const char* context) override;
+ virtual bool ValidateName(char* buffer, char const* context) override;
protected:
SpellInfo const* _spell;
@@ -102,12 +102,12 @@ protected:
class TC_GAME_API AchievementChatLink : public ChatLink
{
public:
- AchievementChatLink() : ChatLink(), _guid(0), _achievement(NULL)
+ AchievementChatLink() : ChatLink(), _guid(0), _achievement(nullptr)
{
memset(_data, 0, sizeof(_data));
}
virtual bool Initialize(std::istringstream& iss) override;
- virtual bool ValidateName(char* buffer, const char* context) override;
+ virtual bool ValidateName(char* buffer, char const* context) override;
protected:
uint32 _guid;
@@ -152,7 +152,7 @@ public:
class TC_GAME_API GlyphChatLink : public SpellChatLink
{
public:
- GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(NULL) { }
+ GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(nullptr) { }
virtual bool Initialize(std::istringstream& iss) override;
private:
uint32 _slotId;
@@ -162,7 +162,7 @@ private:
class TC_GAME_API LinkExtractor
{
public:
- explicit LinkExtractor(const char* msg);
+ explicit LinkExtractor(char const* msg);
~LinkExtractor();
bool IsValidMessage();
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index afb6255079b..6317fae2767 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -103,7 +103,8 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND
{ "Realm Achievement", true, false, false },
{ "In Water", false, false, false },
{ "Terrain Swap", false, false, false },
- { "Sit/stand state", true, true, false }
+ { "Sit/stand state", true, true, false },
+ { "Daily Quest Completed",true, false, false }
};
// Checks if object meets the condition
@@ -448,6 +449,12 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
}
break;
}
+ case CONDITION_DAILY_QUEST_DONE:
+ {
+ if (Player* player = object->ToPlayer())
+ condMeets = player->IsDailyQuestDone(ConditionValue1);
+ break;
+ }
default:
condMeets = false;
break;
@@ -621,6 +628,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition() const
case CONDITION_STAND_STATE:
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER;
break;
+ case CONDITION_DAILY_QUEST_DONE:
+ mask |= GRID_MAP_TYPE_MASK_PLAYER;
+ break;
default:
ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!");
break;
@@ -738,7 +748,7 @@ bool ConditionMgr::IsObjectMeetToConditionList(ConditionSourceInfo& sourceInfo,
//! If not found, add an entry in the store and set to true (placeholder)
if (itr == elseGroupStore.end())
elseGroupStore[condition->ElseGroup] = true;
- else if (!(*itr).second)
+ else if (!(*itr).second) //! If another condition in this group was unmatched before this, don't bother checking (the group is false anyway)
continue;
if (condition->ReferenceId)//handle reference
@@ -1791,6 +1801,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const
case CONDITION_QUESTTAKEN:
case CONDITION_QUEST_NONE:
case CONDITION_QUEST_COMPLETE:
+ case CONDITION_DAILY_QUEST_DONE:
{
if (!sObjectMgr->GetQuestTemplate(cond->ConditionValue1))
{
diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h
index 83e714781c3..4ec2be0753a 100644
--- a/src/server/game/Conditions/ConditionMgr.h
+++ b/src/server/game/Conditions/ConditionMgr.h
@@ -73,7 +73,8 @@ enum ConditionTypes
CONDITION_IN_WATER = 40, // 0 0 0 true if unit in water
CONDITION_TERRAIN_SWAP = 41, // only for 6.x
CONDITION_STAND_STATE = 42, // stateType state 0 true if unit matches specified sitstate (0,x: has exactly state x; 1,0: any standing state; 1,1: any sitting state;)
- CONDITION_MAX = 43 // MAX
+ CONDITION_DAILY_QUEST_DONE = 43, // quest id 0 0 true if daily quest has been completed for the day
+ CONDITION_MAX = 44 // MAX
};
/*! Documentation on implementing a new ConditionSourceType:
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 36ec418ed56..051b5e824bf 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -25,7 +25,7 @@
#include "Timer.h"
#include "ObjectDefines.h"
-#include <boost/regex.hpp>
+#include <regex>
#include <map>
typedef std::map<uint16, uint32> AreaFlagByAreaID;
@@ -145,7 +145,7 @@ DBCStorage <MovieEntry> sMovieStore(MovieEntryfmt);
DBCStorage<NamesProfanityEntry> sNamesProfanityStore(NamesProfanityEntryfmt);
DBCStorage<NamesReservedEntry> sNamesReservedStore(NamesReservedEntryfmt);
-typedef std::array<std::vector<boost::regex>, TOTAL_LOCALES> NameValidationRegexContainer;
+typedef std::array<std::vector<std::wregex>, TOTAL_LOCALES> NameValidationRegexContainer;
NameValidationRegexContainer NamesProfaneValidators;
NameValidationRegexContainer NamesReservedValidators;
@@ -264,7 +264,7 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt
if (FILE* f = fopen(dbcFilename.c_str(), "rb"))
{
std::ostringstream stream;
- stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten.";
+ stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten. Search on forum for TCE00008 for more info.";
std::string buf = stream.str();
errors.push_back(buf);
fclose(f);
@@ -410,11 +410,14 @@ void LoadDBCStores(const std::string& dataPath)
continue;
ASSERT(namesProfanity->Language < TOTAL_LOCALES || namesProfanity->Language == -1);
+ std::wstring wname;
+ ASSERT(Utf8toWStr(namesProfanity->Name, wname));
+
if (namesProfanity->Language != -1)
- NamesProfaneValidators[namesProfanity->Language].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
+ NamesProfaneValidators[namesProfanity->Language].emplace_back(wname, std::regex::icase | std::regex::optimize);
else
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
- NamesProfaneValidators[i].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
+ NamesProfaneValidators[i].emplace_back(wname, std::regex::icase | std::regex::optimize);
}
for (uint32 i = 0; i < sNamesReservedStore.GetNumRows(); ++i)
@@ -424,11 +427,14 @@ void LoadDBCStores(const std::string& dataPath)
continue;
ASSERT(namesReserved->Language < TOTAL_LOCALES || namesReserved->Language == -1);
+ std::wstring wname;
+ ASSERT(Utf8toWStr(namesReserved->Name, wname));
+
if (namesReserved->Language != -1)
- NamesReservedValidators[namesReserved->Language].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
+ NamesReservedValidators[namesReserved->Language].emplace_back(wname, std::regex::icase | std::regex::optimize);
else
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
- NamesReservedValidators[i].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
+ NamesReservedValidators[i].emplace_back(wname, std::regex::icase | std::regex::optimize);
}
@@ -1000,18 +1006,18 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u
return NULL;
}
-ResponseCodes ValidateName(std::string const& name, LocaleConstant locale)
+ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale)
{
if (locale >= TOTAL_LOCALES)
return RESPONSE_FAILURE;
- for (boost::regex const& regex : NamesProfaneValidators[locale])
- if (boost::regex_search(name, regex))
+ for (std::wregex const& regex : NamesProfaneValidators[locale])
+ if (std::regex_search(name, regex))
return CHAR_NAME_PROFANE;
// regexes at TOTAL_LOCALES are loaded from NamesReserved which is not locale specific
- for (boost::regex const& regex : NamesReservedValidators[locale])
- if (boost::regex_search(name, regex))
+ for (std::wregex const& regex : NamesReservedValidators[locale])
+ if (std::regex_search(name, regex))
return CHAR_NAME_RESERVED;
return CHAR_NAME_SUCCESS;
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index 00b4141555f..128ff7ad1b8 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -75,7 +75,7 @@ typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRac
typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
TC_GAME_API SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
-TC_GAME_API ResponseCodes ValidateName(std::string const& name, LocaleConstant locale);
+TC_GAME_API ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale);
TC_GAME_API EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender);
diff --git a/src/server/game/DataStores/M2Stores.cpp b/src/server/game/DataStores/M2Stores.cpp
index 5cff66e6107..69581f16549 100644
--- a/src/server/game/DataStores/M2Stores.cpp
+++ b/src/server/game/DataStores/M2Stores.cpp
@@ -193,7 +193,7 @@ void LoadM2Cameras(std::string const& dataPath)
{
if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i))
{
- std::string filenameWork = dataPath.c_str();
+ std::string filenameWork = dataPath;
filenameWork.append(dbcentry->Model);
// Replace slashes (always to forward slash, because boost!)
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index e72859c23e7..4ca1a68c048 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -82,19 +82,19 @@ void LFGMgr::_SaveToDB(ObjectGuid guid, uint32 db_guid)
if (!guid.IsGroup())
return;
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_LFG_DATA);
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_LFG_DATA);
stmt->setUInt32(0, db_guid);
-
- CharacterDatabase.Execute(stmt);
+ trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LFG_DATA);
stmt->setUInt32(0, db_guid);
-
stmt->setUInt32(1, GetDungeon(guid));
stmt->setUInt32(2, GetState(guid));
+ trans->Append(stmt);
- CharacterDatabase.Execute(stmt);
+ CharacterDatabase.CommitTransaction(trans);
}
/// Load rewards for completing dungeons
@@ -1904,8 +1904,16 @@ bool LFGMgr::AllQueued(GuidList const& check)
return false;
for (GuidList::const_iterator it = check.begin(); it != check.end(); ++it)
- if (GetState(*it) != LFG_STATE_QUEUED)
+ {
+ LfgState state = GetState(*it);
+ if (state != LFG_STATE_QUEUED)
+ {
+ if (state != LFG_STATE_PROPOSAL)
+ TC_LOG_DEBUG("lfg.allqueued", "Unexpected state found while trying to form new group. Guid: %s, State: %s", (*it).ToString().c_str(), GetStateString(state).c_str());
+
return false;
+ }
+ }
return true;
}
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 450cf2396a8..4a5f70165fc 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -367,13 +367,13 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
// Load creature equipment
- if (data && data->equipmentId != 0)
+ if (!data || data->equipmentId == 0)
+ LoadEquipment(); // use default equipment (if available)
+ else if (data && data->equipmentId != 0) // override, 0 means no equipment
{
m_originalEquipmentId = data->equipmentId;
LoadEquipment(data->equipmentId);
}
- else
- LoadEquipment(0, true);
SetName(normalInfo->Name); // at normal entry always
@@ -481,6 +481,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
}
+ if (cInfo->InhabitType & INHABIT_ROOT)
+ SetControlled(true, UNIT_STATE_ROOT);
+
UpdateMovementFlags();
LoadCreaturesAddon();
return true;
@@ -863,15 +866,17 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
Relocate(x, y, z, ang);
- if (!CreateFromProto(guidlow, entry, data, vehId))
- return false;
-
+ // Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at
+ // invalid position, triggering a crash about Auras not removed in the destructor
if (!IsPositionValid())
{
TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, x, y, z, ang);
return false;
}
+ if (!CreateFromProto(guidlow, entry, data, vehId))
+ return false;
+
switch (GetCreatureTemplate()->rank)
{
case CREATURE_ELITE_RARE:
@@ -1823,7 +1828,7 @@ bool Creature::isWorldBoss() const
if (IsPet())
return false;
- return (GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_BOSS) != 0;
+ return (GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0;
}
SpellInfo const* Creature::reachWithSpellAttack(Unit* victim)
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index bb43bcb5ff1..cfe637c4b21 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -22,7 +22,7 @@
#include "Common.h"
#include "Unit.h"
#include "UpdateMask.h"
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
#include "LootMgr.h"
#include "DatabaseEnv.h"
#include "Cell.h"
@@ -107,7 +107,7 @@ struct TC_GAME_API CreatureTemplate
uint32 unit_flags; // enum UnitFlags mask values
uint32 unit_flags2; // enum UnitFlags2 mask values
uint32 dynamicflags;
- uint32 family; // enum CreatureFamily values (optional)
+ CreatureFamily family; // enum CreatureFamily values (optional)
uint32 trainer_type;
uint32 trainer_spell;
uint32 trainer_class;
@@ -146,11 +146,11 @@ struct TC_GAME_API CreatureTemplate
// helpers
SkillType GetRequiredLootSkill() const
{
- if (type_flags & CREATURE_TYPEFLAGS_HERBLOOT)
+ if (type_flags & CREATURE_TYPE_FLAG_HERB_SKINNING_SKILL)
return SKILL_HERBALISM;
- else if (type_flags & CREATURE_TYPEFLAGS_MININGLOOT)
+ else if (type_flags & CREATURE_TYPE_FLAG_MINING_SKINNING_SKILL)
return SKILL_MINING;
- else if (type_flags & CREATURE_TYPEFLAGS_ENGINEERLOOT)
+ else if (type_flags & CREATURE_TYPE_FLAG_ENGINEERING_SKINNING_SKILL)
return SKILL_ENGINEERING;
else
return SKILL_SKINNING; // normal case
@@ -158,12 +158,12 @@ struct TC_GAME_API CreatureTemplate
bool IsExotic() const
{
- return (type_flags & CREATURE_TYPEFLAGS_EXOTIC) != 0;
+ return (type_flags & CREATURE_TYPE_FLAG_EXOTIC_PET) != 0;
}
bool IsTameable(bool canTameExotic) const
{
- if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPEFLAGS_TAMEABLE) == 0)
+ if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_PET) == 0)
return false;
// if can tame exotic then can tame any tameable
@@ -294,7 +294,8 @@ enum InhabitTypeValues
INHABIT_GROUND = 1,
INHABIT_WATER = 2,
INHABIT_AIR = 4,
- INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
+ INHABIT_ROOT = 8,
+ INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR | INHABIT_ROOT
};
// Enums used by StringTextData::Type (CreatureEventAI)
@@ -597,6 +598,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
void RemoveCorpse(bool setSpawnTime = true);
void DespawnOrUnsummon(uint32 msTimeToDespawn = 0);
+ void DespawnOrUnsummon(Milliseconds const& time) { DespawnOrUnsummon(uint32(time.count())); }
time_t const& GetRespawnTime() const { return m_respawnTime; }
time_t GetRespawnTimeEx() const;
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 7f922f89572..ba336303a35 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -34,7 +34,7 @@
#include "Transport.h"
GameObject::GameObject() : WorldObject(false), MapObject(),
- m_model(NULL), m_goValue(), m_AI(NULL)
+ m_model(nullptr), m_goValue(), m_AI(nullptr)
{
m_objectType |= TYPEMASK_GAMEOBJECT;
m_objectTypeId = TYPEID_GAMEOBJECT;
@@ -49,11 +49,11 @@ GameObject::GameObject() : WorldObject(false), MapObject(),
m_usetimes = 0;
m_spellId = 0;
m_cooldownTime = 0;
- m_goInfo = NULL;
- m_goData = NULL;
+ m_goInfo = nullptr;
+ m_goData = nullptr;
+ m_packedRotation = 0;
m_spawnId = 0;
- m_rotation = 0;
m_lootRecipientGroup = 0;
m_groupLootTimer = 0;
@@ -173,16 +173,16 @@ void GameObject::RemoveFromWorld()
}
}
-bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit)
+bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/)
{
ASSERT(map);
SetMap(map);
- Relocate(x, y, z, ang);
- m_stationaryPosition.Relocate(x, y, z, ang);
+ Relocate(pos);
+ m_stationaryPosition.Relocate(pos);
if (!IsPositionValid())
{
- TC_LOG_ERROR("misc", "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, x, y);
+ TC_LOG_ERROR("misc", "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, pos.GetPositionX(), pos.GetPositionY());
return false;
}
@@ -199,7 +199,7 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(name_id);
if (!goinfo)
{
- TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", guidlow, name_id, map->GetId(), x, y, z);
+ TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", guidlow, name_id, map->GetId(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
return false;
}
@@ -222,10 +222,15 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
return false;
}
- SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0);
- SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1);
+ SetWorldRotation(rotation);
+ GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId());
- UpdateRotationFields(rotation2, rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3
+ // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation
+ G3D::Quat parentRotation;
+ if (gameObjectAddon)
+ parentRotation = gameObjectAddon->ParentRotation;
+
+ SetParentRotation(parentRotation);
SetObjectScale(goinfo->size);
@@ -285,13 +290,10 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
break;
}
- if (GameObjectAddon const* addon = sObjectMgr->GetGameObjectAddon(GetSpawnId()))
+ if (gameObjectAddon && gameObjectAddon->InvisibilityValue)
{
- if (addon->InvisibilityValue)
- {
- m_invisibility.AddFlag(addon->invisibilityType);
- m_invisibility.AddValue(addon->invisibilityType, addon->InvisibilityValue);
- }
+ m_invisibility.AddFlag(gameObjectAddon->invisibilityType);
+ m_invisibility.AddValue(gameObjectAddon->invisibilityType, gameObjectAddon->InvisibilityValue);
}
LastUsedScriptID = GetGOInfo()->ScriptId;
@@ -496,7 +498,7 @@ void GameObject::Update(uint32 diff)
radius = goInfo->trap.diameter / 2.f;
// Pointer to appropriate target if found any
- Unit* target = NULL;
+ Unit* target = nullptr;
/// @todo this hack with search required until GO casting not implemented
if (Unit* owner = GetOwner())
@@ -511,7 +513,7 @@ void GameObject::Update(uint32 diff)
else
{
// Environmental trap: Any player
- Player* player = NULL;
+ Player* player = nullptr;
Trinity::AnyPlayerInObjectRangeCheck checker(this, radius);
Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, player, checker);
VisitNearbyWorldObject(radius, searcher);
@@ -571,8 +573,8 @@ void GameObject::Update(uint32 diff)
GameObjectTemplate const* goInfo = GetGOInfo();
if (goInfo->trap.type == 2 && goInfo->trap.spellId)
{
- /// @todo NULL target won't work for target type 1
- CastSpell(NULL, goInfo->trap.spellId);
+ /// @todo nullptr target won't work for target type 1
+ CastSpell(nullptr, goInfo->trap.spellId);
SetLootState(GO_JUST_DEACTIVATED);
}
else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID))
@@ -780,10 +782,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
data.posY = GetPositionY();
data.posZ = GetPositionZ();
data.orientation = GetOrientation();
- data.rotation0 = GetFloatValue(GAMEOBJECT_PARENTROTATION+0);
- data.rotation1 = GetFloatValue(GAMEOBJECT_PARENTROTATION+1);
- data.rotation2 = GetFloatValue(GAMEOBJECT_PARENTROTATION+2);
- data.rotation3 = GetFloatValue(GAMEOBJECT_PARENTROTATION+3);
+ data.rotation = m_worldRotation;
data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
data.animprogress = GetGoAnimProgress();
data.go_state = GetGoState();
@@ -809,10 +808,10 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
stmt->setFloat(index++, GetPositionY());
stmt->setFloat(index++, GetPositionZ());
stmt->setFloat(index++, GetOrientation());
- stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION));
- stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+1));
- stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+2));
- stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+3));
+ stmt->setFloat(index++, m_worldRotation.x);
+ stmt->setFloat(index++, m_worldRotation.y);
+ stmt->setFloat(index++, m_worldRotation.z);
+ stmt->setFloat(index++, m_worldRotation.w);
stmt->setInt32(index++, int32(m_respawnDelayTime));
stmt->setUInt8(index++, GetGoAnimProgress());
stmt->setUInt8(index++, uint8(GetGoState()));
@@ -834,22 +833,14 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
uint32 entry = data->id;
//uint32 map_id = data->mapid; // already used before call
uint32 phaseMask = data->phaseMask;
- float x = data->posX;
- float y = data->posY;
- float z = data->posZ;
- float ang = data->orientation;
-
- float rotation0 = data->rotation0;
- float rotation1 = data->rotation1;
- float rotation2 = data->rotation2;
- float rotation3 = data->rotation3;
+ Position pos(data->posX, data->posY, data->posZ, data->orientation);
uint32 animprogress = data->animprogress;
GOState go_state = data->go_state;
uint32 artKit = data->artKit;
m_spawnId = spawnId;
- if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, artKit))
+ if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, pos, data->rotation, animprogress, go_state, artKit))
return false;
if (data->spawntimesecs >= 0)
@@ -868,7 +859,7 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
m_respawnTime = GetMap()->GetGORespawnTime(m_spawnId);
// ready to respawn
- if (m_respawnTime && m_respawnTime <= time(NULL))
+ if (m_respawnTime && m_respawnTime <= time(nullptr))
{
m_respawnTime = 0;
GetMap()->RemoveGORespawnTime(m_spawnId);
@@ -1093,7 +1084,7 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target)
float range = float(target->GetSpellMaxRangeForTarget(GetOwner(), trapSpell));
// search nearest linked GO
- GameObject* trapGO = NULL;
+ GameObject* trapGO = nullptr;
{
// using original GO distance
CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY()));
@@ -1113,7 +1104,7 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target)
GameObject* GameObject::LookupFishingHoleAround(float range)
{
- GameObject* ok = NULL;
+ GameObject* ok = nullptr;
CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY()));
Cell cell(p);
@@ -1136,7 +1127,7 @@ void GameObject::ResetDoorOrButton()
m_cooldownTime = 0;
}
-void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=NULL*/)
+void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=nullptr*/)
{
if (m_lootState != GO_READY)
return;
@@ -1160,7 +1151,7 @@ void GameObject::SetGoArtKit(uint8 kit)
void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType lowguid)
{
- const GameObjectData* data = NULL;
+ const GameObjectData* data = nullptr;
if (go)
{
go->SetGoArtKit(artkit);
@@ -1386,7 +1377,7 @@ void GameObject::Use(Unit* user)
// cast this spell later if provided
spellId = info->goober.spellId;
- spellCaster = NULL;
+ spellCaster = nullptr;
break;
}
@@ -1453,11 +1444,11 @@ void GameObject::Use(Unit* user)
TC_LOG_DEBUG("misc", "Fishing check (skill: %i zone min skill: %i chance %i roll: %i", skill, zone_skill, chance, roll);
- // but you will likely cause junk in areas that require a high fishing skill (not yet implemented)
+ player->UpdateFishingSkill();
+
+ // but you will likely cause junk in areas that require a high fishing skill
if (chance >= roll)
{
- player->UpdateFishingSkill();
-
/// @todo I do not understand this hack. Need some explanation.
// prevent removing GO at spell cancel
RemoveFromOwner();
@@ -1505,7 +1496,7 @@ void GameObject::Use(Unit* user)
GameObjectTemplate const* info = GetGOInfo();
- Player* m_ritualOwner = NULL;
+ Player* m_ritualOwner = nullptr;
if (m_ritualOwnerGUID)
m_ritualOwner = ObjectAccessor::FindPlayer(m_ritualOwnerGUID);
@@ -1869,7 +1860,7 @@ bool GameObject::IsInRange(float x, float y, float z, float radius) const
&& dz < info->maxZ + radius && dz > info->minZ - radius;
}
-void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= NULL*/)
+void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= nullptr*/)
{
if (!eventId)
return;
@@ -1899,37 +1890,41 @@ std::string const & GameObject::GetNameForLocaleIdx(LocaleConstant loc_idx) cons
return GetName();
}
-void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/)
+void GameObject::UpdatePackedRotation()
{
- static double const atan_pow = atan(pow(2.0f, -20.0f));
-
- double f_rot1 = std::sin(GetOrientation() / 2.0f);
- double f_rot2 = std::cos(GetOrientation() / 2.0f);
+ static const int32 PACK_YZ = 1 << 20;
+ static const int32 PACK_X = PACK_YZ << 1;
- int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f));
- int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF;
+ static const int32 PACK_YZ_MASK = (PACK_YZ << 1) - 1;
+ static const int32 PACK_X_MASK = (PACK_X << 1) - 1;
- //float f_rot2 = std::sin(0.0f / 2.0f);
- //int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f));
- //rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000;
-
- //float f_rot3 = std::sin(0.0f / 2.0f);
- //int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f));
- //rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000;
+ int8 w_sign = (m_worldRotation.w >= 0.f ? 1 : -1);
+ int64 x = int32(m_worldRotation.x * PACK_X) * w_sign & PACK_X_MASK;
+ int64 y = int32(m_worldRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK;
+ int64 z = int32(m_worldRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK;
+ m_packedRotation = z | (y << 21) | (x << 42);
+}
- m_rotation = rotation;
+void GameObject::SetWorldRotation(G3D::Quat const& rot)
+{
+ m_worldRotation = rot.toUnit();
+ UpdatePackedRotation();
+}
- if (rotation2 == 0.0f && rotation3 == 0.0f)
- {
- rotation2 = (float)f_rot1;
- rotation3 = (float)f_rot2;
- }
+void GameObject::SetParentRotation(G3D::Quat const& rotation)
+{
+ SetFloatValue(GAMEOBJECT_PARENTROTATION + 0, rotation.x);
+ SetFloatValue(GAMEOBJECT_PARENTROTATION + 1, rotation.y);
+ SetFloatValue(GAMEOBJECT_PARENTROTATION + 2, rotation.z);
+ SetFloatValue(GAMEOBJECT_PARENTROTATION + 3, rotation.w);
+}
- SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2);
- SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3);
+void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot)
+{
+ SetWorldRotation(G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot)));
}
-void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, uint32 spellId /*= 0*/)
+void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/)
{
if (!m_goValue.Building.MaxHealth || !change)
return;
@@ -1948,7 +1943,7 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, u
// Set the health bar, value = 255 * healthPct;
SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth);
- Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : NULL;
+ Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr;
// dealing damage, send packet
if (player)
@@ -1979,7 +1974,7 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, u
SetDestructibleState(newState, player, false);
}
-void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= NULL*/, bool setHealth /*= false*/)
+void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= nullptr*/, bool setHealth /*= false*/)
{
// the user calling this must know he is already operating on destructible gameobject
ASSERT(GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING);
@@ -2004,7 +1999,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player*
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED);
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED);
- uint32 modelId = m_goInfo->building.damagedDisplayId;
+ uint32 modelId = m_goInfo->displayId;
if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData))
if (modelData->DamagedDisplayId)
modelId = modelData->DamagedDisplayId;
@@ -2032,7 +2027,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player*
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED);
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED);
- uint32 modelId = m_goInfo->building.destroyedDisplayId;
+ uint32 modelId = m_goInfo->displayId;
if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData))
if (modelData->DestroyedDisplayId)
modelId = modelData->DestroyedDisplayId;
@@ -2161,14 +2156,14 @@ void GameObject::UpdateModel()
Player* GameObject::GetLootRecipient() const
{
if (!m_lootRecipient)
- return NULL;
+ return nullptr;
return ObjectAccessor::FindConnectedPlayer(m_lootRecipient);
}
Group* GameObject::GetLootRecipientGroup() const
{
if (!m_lootRecipientGroup)
- return NULL;
+ return nullptr;
return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
}
@@ -2176,7 +2171,7 @@ void GameObject::SetLootRecipient(Unit* unit)
{
// set the player whose group should receive the right
// to loot the creature after it dies
- // should be set to NULL after the loot disappears
+ // should be set to nullptr after the loot disappears
if (!unit)
{
@@ -2295,7 +2290,7 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t
data->append(fieldBuffer);
}
-void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = NULL*/) const
+void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const
{
if (m_spawnId)
{
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 2b092427178..a271512629b 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -25,6 +25,7 @@
#include "Object.h"
#include "LootMgr.h"
#include "DatabaseEnv.h"
+#include <G3D/Quat.h>
class GameObjectAI;
class Group;
@@ -57,6 +58,7 @@ struct GameObjectTemplate
uint32 openTextID; //4 can be used to replace castBarCaption?
uint32 closeTextID; //5
uint32 ignoredByPathing; //6
+ uint32 conditionID1; //7
} door;
//1 GAMEOBJECT_TYPE_BUTTON
struct
@@ -70,6 +72,7 @@ struct GameObjectTemplate
uint32 openTextID; //6 can be used to replace castBarCaption?
uint32 closeTextID; //7
uint32 losOK; //8
+ uint32 conditionID1; //9
} button;
//2 GAMEOBJECT_TYPE_QUESTGIVER
struct
@@ -84,6 +87,7 @@ struct GameObjectTemplate
uint32 losOK; //7
uint32 allowMounted; //8 Is usable while on mount/vehicle. (0/1)
uint32 large; //9
+ uint32 conditionID1; //10
} questgiver;
//3 GAMEOBJECT_TYPE_CHEST
struct
@@ -105,6 +109,7 @@ struct GameObjectTemplate
uint32 openTextID; //14 can be used to replace castBarCaption?
uint32 groupLootRules; //15
uint32 floatingTooltip; //16
+ uint32 conditionID1; //17
} chest;
//4 GAMEOBJECT_TYPE_BINDER - empty
//5 GAMEOBJECT_TYPE_GENERIC
@@ -116,6 +121,7 @@ struct GameObjectTemplate
uint32 large; //3
uint32 floatOnWater; //4
int32 questID; //5
+ uint32 conditionID1; //6
} _generic;
//6 GAMEOBJECT_TYPE_TRAP
struct
@@ -135,6 +141,7 @@ struct GameObjectTemplate
uint32 openTextID; //12 can be used to replace castBarCaption?
uint32 closeTextID; //13
uint32 ignoreTotems; //14
+ uint32 conditionID1; //15
} trap;
//7 GAMEOBJECT_TYPE_CHAIR
struct
@@ -143,6 +150,7 @@ struct GameObjectTemplate
uint32 height; //1
uint32 onlyCreatorUse; //2
uint32 triggeredEvent; //3
+ uint32 conditionID1; //4
} chair;
//8 GAMEOBJECT_TYPE_SPELL_FOCUS
struct
@@ -154,6 +162,8 @@ struct GameObjectTemplate
uint32 questID; //4
uint32 large; //5
uint32 floatingTooltip; //6
+ uint32 floatOnWater; //7
+ uint32 conditionID1; //8
} spellFocus;
//9 GAMEOBJECT_TYPE_TEXT
struct
@@ -162,6 +172,7 @@ struct GameObjectTemplate
uint32 language; //1
uint32 pageMaterial; //2
uint32 allowMounted; //3 Is usable while on mount/vehicle. (0/1)
+ uint32 conditionID1; //4
} text;
//10 GAMEOBJECT_TYPE_GOOBER
struct
@@ -187,6 +198,8 @@ struct GameObjectTemplate
uint32 floatingTooltip; //18
uint32 gossipID; //19
uint32 WorldStateSetsState; //20
+ uint32 floatOnWater; //21
+ uint32 conditionID1; //22
} goober;
//11 GAMEOBJECT_TYPE_TRANSPORT
struct
@@ -196,6 +209,7 @@ struct GameObjectTemplate
uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
uint32 pause1EventID; //3
uint32 pause2EventID; //4
+ uint32 mapID; //5
} transport;
//12 GAMEOBJECT_TYPE_AREADAMAGE
struct
@@ -216,6 +230,7 @@ struct GameObjectTemplate
uint32 cinematicId; //1
uint32 eventID; //2
uint32 openTextID; //3 can be used to replace castBarCaption?
+ uint32 conditionID1; //4
} camera;
//14 GAMEOBJECT_TYPE_MAPOBJECT - empty
//15 GAMEOBJECT_TYPE_MO_TRANSPORT
@@ -244,9 +259,14 @@ struct GameObjectTemplate
uint32 casterTargetSpellTargets; //5
uint32 castersGrouped; //6
uint32 ritualNoTargetCheck; //7
+ uint32 conditionID1; //8
} summoningRitual;
- //19 GAMEOBJECT_TYPE_MAILBOX - empty
- //20 GAMEOBJECT_TYPE_DONOTUSE - empty
+ //19 GAMEOBJECT_TYPE_MAILBOX
+ struct
+ {
+ uint32 conditionID1; //0
+ } mailbox;
+ //20 GAMEOBJECT_TYPE_DO_NOT_USE - empty
//21 GAMEOBJECT_TYPE_GUARDPOST
struct
{
@@ -261,6 +281,7 @@ struct GameObjectTemplate
uint32 partyOnly; //2
uint32 allowMounted; //3 Is usable while on mount/vehicle. (0/1)
uint32 large; //4
+ uint32 conditionID1; //5
} spellcaster;
//23 GAMEOBJECT_TYPE_MEETINGSTONE
struct
@@ -280,6 +301,7 @@ struct GameObjectTemplate
uint32 noDamageImmune; //5
uint32 openTextID; //6
uint32 losOK; //7
+ uint32 conditionID1; //8
} flagstand;
//25 GAMEOBJECT_TYPE_FISHINGHOLE
struct
@@ -358,21 +380,21 @@ struct GameObjectTemplate
{
uint32 intactNumHits; //0
uint32 creditProxyCreature; //1
- uint32 state1Name; //2
+ uint32 empty1; //2
uint32 intactEvent; //3
- uint32 damagedDisplayId; //4
+ uint32 empty2; //4
uint32 damagedNumHits; //5
uint32 empty3; //6
uint32 empty4; //7
uint32 empty5; //8
uint32 damagedEvent; //9
- uint32 destroyedDisplayId; //10
+ uint32 empty6; //10
uint32 empty7; //11
uint32 empty8; //12
uint32 empty9; //13
uint32 destroyedEvent; //14
uint32 empty10; //15
- uint32 debuildingTimeSecs; //16
+ uint32 rebuildingTimeSecs; //16
uint32 empty11; //17
uint32 destructibleData; //18
uint32 rebuildingEvent; //19
@@ -381,7 +403,11 @@ struct GameObjectTemplate
uint32 damageEvent; //22
uint32 empty14; //23
} building;
- //34 GAMEOBJECT_TYPE_GUILDBANK - empty
+ //34 GAMEOBJECT_TYPE_GUILDBANK
+ struct
+ {
+ uint32 conditionID1; //0
+ } guildbank;
//35 GAMEOBJECT_TYPE_TRAPDOOR
struct
{
@@ -578,6 +604,7 @@ struct GameObjectLocale
// `gameobject_addon` table
struct GameObjectAddon
{
+ G3D::Quat ParentRotation;
InvisibilityType invisibilityType;
uint32 InvisibilityValue;
};
@@ -597,8 +624,7 @@ enum GOState
// from `gameobject`
struct GameObjectData
{
- explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f),
- rotation0(0.0f), rotation1(0.0f), rotation2(0.0f), rotation3(0.0f), spawntimesecs(0),
+ explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0),
animprogress(0), go_state(GO_STATE_ACTIVE), spawnMask(0), artKit(0), dbData(true) { }
uint32 id; // entry in gamobject_template
uint16 mapid;
@@ -607,10 +633,7 @@ struct GameObjectData
float posY;
float posZ;
float orientation;
- float rotation0;
- float rotation1;
- float rotation2;
- float rotation3;
+ G3D::Quat rotation;
int32 spawntimesecs;
uint32 animprogress;
GOState go_state;
@@ -652,7 +675,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void RemoveFromWorld() override;
void CleanupsBeforeDelete(bool finalCleanup = true) override;
- bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit = 0);
+ bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0);
void Update(uint32 p_time) override;
GameObjectTemplate const* GetGOInfo() const { return m_goInfo; }
GameObjectData const* GetGOData() const { return m_goData; }
@@ -664,7 +687,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
ObjectGuid::LowType GetSpawnId() const { return m_spawnId; }
- void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f);
+ // z_rot, y_rot, x_rot - rotation angles around z, y and x axes
+ void SetWorldRotationAngles(float z_rot, float y_rot, float x_rot);
+ void SetWorldRotation(G3D::Quat const& rot);
+ void SetParentRotation(G3D::Quat const& rotation); // transforms(rotates) transport's path
+ int64 GetPackedWorldRotation() const { return m_packedRotation; }
// overwrite WorldObject function for proper name localization
std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override;
@@ -822,7 +849,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void EventInform(uint32 eventId, WorldObject* invoker = NULL);
- uint64 GetRotation() const { return m_rotation; }
virtual uint32 GetScriptId() const { return GetGOInfo()->ScriptId; }
GameObjectAI* AI() const { return m_AI; }
@@ -877,7 +903,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
GameObjectData const* m_goData;
GameObjectValue m_goValue;
- uint64 m_rotation;
+ int64 m_packedRotation;
+ G3D::Quat m_worldRotation;
Position m_stationaryPosition;
ObjectGuid m_lootRecipient;
@@ -886,6 +913,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
private:
void RemoveFromOwner();
void SwitchDoorOrButton(bool activate, bool alternative = false);
+ void UpdatePackedRotation();
//! Object distance/size - overridden from Object::_IsWithinDist. Needs to take in account proper GO size.
bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool /*is3D*/) const override
diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h
index 89615f94e52..8f84dc3e5b3 100644
--- a/src/server/game/Entities/Item/Container/Bag.h
+++ b/src/server/game/Entities/Item/Container/Bag.h
@@ -23,7 +23,7 @@
#define MAX_BAG_SIZE 36 // 2.0.12
#include "Item.h"
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
class TC_GAME_API Bag : public Item
{
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index 8658200c28c..5e00a816cab 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -22,7 +22,7 @@
#include "Common.h"
#include "Object.h"
#include "LootMgr.h"
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
#include "DatabaseEnv.h"
class SpellInfo;
diff --git a/src/server/game/Entities/Item/ItemPrototype.cpp b/src/server/game/Entities/Item/ItemTemplate.cpp
index 3bb7b128b08..1a8bc2291ae 100644
--- a/src/server/game/Entities/Item/ItemPrototype.cpp
+++ b/src/server/game/Entities/Item/ItemTemplate.cpp
@@ -15,23 +15,23 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
bool ItemTemplate::CanChangeEquipStateInCombat() const
{
switch (InventoryType)
{
- case INVTYPE_RELIC:
- case INVTYPE_SHIELD:
- case INVTYPE_HOLDABLE:
- return true;
+ case INVTYPE_RELIC:
+ case INVTYPE_SHIELD:
+ case INVTYPE_HOLDABLE:
+ return true;
}
switch (Class)
{
- case ITEM_CLASS_WEAPON:
- case ITEM_CLASS_PROJECTILE:
- return true;
+ case ITEM_CLASS_WEAPON:
+ case ITEM_CLASS_PROJECTILE:
+ return true;
}
return false;
@@ -40,82 +40,85 @@ bool ItemTemplate::CanChangeEquipStateInCombat() const
float ItemTemplate::getDPS() const
{
- if (Delay == 0)
- return 0;
- float temp = 0;
- for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
+ if (!Delay)
+ return 0.f;
+
+ float temp = 0.f;
+ for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
temp += Damage[i].DamageMin + Damage[i].DamageMax;
- return temp * 500 / Delay;
-}
+ return temp * 500.f / Delay;
+}
int32 ItemTemplate::getFeralBonus(int32 extraDPS /*= 0*/) const
{
// 0x02A5F3 - is mask for Melee weapon from ItemSubClassMask.dbc
if (Class == ITEM_CLASS_WEAPON && (1 << SubClass) & 0x02A5F3)
{
- int32 bonus = int32((extraDPS + getDPS())*14.0f) - 767;
+ int32 bonus = int32((extraDPS + getDPS()) * 14.0f) - 767;
if (bonus < 0)
return 0;
return bonus;
}
+
return 0;
}
float ItemTemplate::GetItemLevelIncludingQuality() const
{
- float itemLevel = (float)ItemLevel;
+ float itemLevel(ItemLevel);
switch (Quality)
{
- case ITEM_QUALITY_POOR:
- case ITEM_QUALITY_NORMAL:
- case ITEM_QUALITY_UNCOMMON:
- case ITEM_QUALITY_ARTIFACT:
- case ITEM_QUALITY_HEIRLOOM:
- itemLevel -= 13; // leaving this as a separate statement since we do not know the real behavior in this case
- break;
- case ITEM_QUALITY_RARE:
- itemLevel -= 13;
- break;
- case ITEM_QUALITY_EPIC:
- case ITEM_QUALITY_LEGENDARY:
- default:
- break;
+ case ITEM_QUALITY_POOR:
+ case ITEM_QUALITY_NORMAL:
+ case ITEM_QUALITY_UNCOMMON:
+ case ITEM_QUALITY_ARTIFACT:
+ case ITEM_QUALITY_HEIRLOOM:
+ itemLevel -= 13.f; // leaving this as a separate statement since we do not know the real behavior in this case
+ break;
+ case ITEM_QUALITY_RARE:
+ itemLevel -= 13.f;
+ break;
+ case ITEM_QUALITY_EPIC:
+ case ITEM_QUALITY_LEGENDARY:
+ default:
+ break;
}
+
return std::max<float>(0.f, itemLevel);
}
uint32 ItemTemplate::GetSkill() const
{
- const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] =
+ static uint32 const itemWeaponSkills[MAX_ITEM_SUBCLASS_WEAPON] =
{
- SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES,
- SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0,
- SKILL_STAVES, 0, 0, SKILL_FIST_WEAPONS, 0,
- SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS,
+ SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES,
+ SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0,
+ SKILL_STAVES, 0, 0, SKILL_FIST_WEAPONS, 0,
+ SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS,
SKILL_FISHING
};
- const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] =
+ static uint32 const itemArmorSkills[MAX_ITEM_SUBCLASS_ARMOR] =
{
0, SKILL_CLOTH, SKILL_LEATHER, SKILL_MAIL, SKILL_PLATE_MAIL, 0, SKILL_SHIELD, 0, 0, 0, 0
};
switch (Class)
{
- case ITEM_CLASS_WEAPON:
- if (SubClass >= MAX_ITEM_SUBCLASS_WEAPON)
- return 0;
- else
- return item_weapon_skills[SubClass];
+ case ITEM_CLASS_WEAPON:
+ if (SubClass >= MAX_ITEM_SUBCLASS_WEAPON)
+ return 0;
+ else
+ return itemWeaponSkills[SubClass];
- case ITEM_CLASS_ARMOR:
- if (SubClass >= MAX_ITEM_SUBCLASS_ARMOR)
- return 0;
- else
- return item_armor_skills[SubClass];
+ case ITEM_CLASS_ARMOR:
+ if (SubClass >= MAX_ITEM_SUBCLASS_ARMOR)
+ return 0;
+ else
+ return itemArmorSkills[SubClass];
- default:
- return 0;
+ default:
+ return 0;
}
}
diff --git a/src/server/game/Entities/Item/ItemPrototype.h b/src/server/game/Entities/Item/ItemTemplate.h
index 9b0f4628364..0ff3f00a08b 100644
--- a/src/server/game/Entities/Item/ItemPrototype.h
+++ b/src/server/game/Entities/Item/ItemTemplate.h
@@ -107,20 +107,20 @@ enum ItemBondingType
enum ItemProtoFlags
{
- ITEM_PROTO_FLAG_UNK1 = 0x00000001, // ?
+ ITEM_PROTO_FLAG_NO_PICKUP = 0x00000001, // ?
ITEM_PROTO_FLAG_CONJURED = 0x00000002, // Conjured item
- ITEM_PROTO_FLAG_OPENABLE = 0x00000004, // Item can be right clicked to open for loot
+ ITEM_PROTO_FLAG_HAS_LOOT = 0x00000004, // Item can be right clicked to open for loot
ITEM_PROTO_FLAG_HEROIC = 0x00000008, // Makes green "Heroic" text appear on item
ITEM_PROTO_FLAG_DEPRECATED = 0x00000010, // Cannot equip or use
ITEM_PROTO_FLAG_INDESTRUCTIBLE = 0x00000020, // Item can not be destroyed, except by using spell (item can be reagent for spell)
- ITEM_PROTO_FLAG_UNK2 = 0x00000040, // ?
+ ITEM_PROTO_FLAG_PLAYER_CAST = 0x00000040, // Item's spells are castable by players
ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN = 0x00000080, // No default 30 seconds cooldown when equipped
- ITEM_PROTO_FLAG_UNK3 = 0x00000100, // ?
- ITEM_PROTO_FLAG_WRAPPER = 0x00000200, // Item can wrap other items
- ITEM_PROTO_FLAG_UNK4 = 0x00000400, // ?
- ITEM_PROTO_FLAG_PARTY_LOOT = 0x00000800, // Looting this item does not remove it from available loot
+ ITEM_PROTO_FLAG_INT_BONUS_INSTEAD = 0x00000100, // ?
+ ITEM_PROTO_FLAG_IS_WRAPPER = 0x00000200, // Item can wrap other items
+ ITEM_PROTO_FLAG_USES_RESOURCES = 0x00000400, // ?
+ ITEM_PROTO_FLAG_MULTI_DROP = 0x00000800, // Looting this item does not remove it from available loot
ITEM_PROTO_FLAG_REFUNDABLE = 0x00001000, // Item can be returned to vendor for its original cost (extended cost)
- ITEM_PROTO_FLAG_CHARTER = 0x00002000, // Item is guild or arena charter
+ ITEM_PROTO_FLAG_PETITION = 0x00002000, // Item is guild or arena charter
ITEM_PROTO_FLAG_UNK5 = 0x00004000, // Only readable items have this (but not all)
ITEM_PROTO_FLAG_UNK6 = 0x00008000, // ?
ITEM_PROTO_FLAG_UNK7 = 0x00010000, // ?
@@ -537,12 +537,7 @@ inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemS
return 0;
}
-// 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__)
-#pragma pack(1)
-#else
#pragma pack(push, 1)
-#endif
struct _Damage
{
@@ -573,12 +568,7 @@ struct _Socket
uint32 Content;
};
-// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
-#if defined(__GNUC__)
-#pragma pack()
-#else
#pragma pack(pop)
-#endif
#define MAX_ITEM_PROTO_DAMAGES 2 // changed in 3.1.0
#define MAX_ITEM_PROTO_SOCKETS 3
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index a2f519a681c..20b88c5e3a9 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -480,7 +480,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const
// 0x200
if (flags & UPDATEFLAG_ROTATION)
- *data << int64(ToGameObject()->GetRotation());
+ *data << int64(ToGameObject()->GetPackedWorldRotation());
}
void Object::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const
@@ -1154,9 +1154,13 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const
if (!IsInMap(obj))
return false;
- float ox, oy, oz;
- obj->GetPosition(ox, oy, oz);
- return IsWithinLOS(ox, oy, oz);
+ float x, y, z;
+ if (obj->GetTypeId() == TYPEID_PLAYER)
+ obj->GetPosition(x, y, z);
+ else
+ obj->GetHitSpherePointFor(GetPosition(), x, y, z);
+
+ return IsWithinLOS(x, y, z);
}
float WorldObject::GetDistance(const WorldObject* obj) const
@@ -1240,11 +1244,36 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const
VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);*/
if (IsInWorld())
- return GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ()+2.f, ox, oy, oz+2.f, GetPhaseMask());
+ {
+ float x, y, z;
+ if (GetTypeId() == TYPEID_PLAYER)
+ GetPosition(x, y, z);
+ else
+ GetHitSpherePointFor({ ox, oy, oz }, x, y, z);
+
+ return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask());
+ }
return true;
}
+Position WorldObject::GetHitSpherePointFor(Position const& dest) const
+{
+ G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ());
+ G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
+ G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * GetObjectSize();
+
+ return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y));
+}
+
+void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const
+{
+ Position pos = GetHitSpherePointFor(dest);
+ x = pos.GetPositionX();
+ y = pos.GetPositionY();
+ z = pos.GetPositionZ();
+}
+
bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D /* = true */) const
{
float dx1 = GetPositionX() - obj1->GetPositionX();
@@ -1374,7 +1403,8 @@ void WorldObject::GetRandomPoint(const Position &pos, float distance, float &ran
// angle to face `obj` to `this`
float angle = (float)rand_norm()*static_cast<float>(2*M_PI);
- float new_dist = (float)rand_norm()*static_cast<float>(distance);
+ float new_dist = (float)rand_norm() + (float)rand_norm();
+ new_dist = distance * (new_dist > 1 ? new_dist - 2 : new_dist);
rand_x = pos.m_positionX + new_dist * std::cos(angle);
rand_y = pos.m_positionY + new_dist * std::sin(angle);
@@ -1416,7 +1446,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
bool canSwim = ToCreature()->CanSwim();
float ground_z = z;
float max_z = canSwim
- ? GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK))
+ ? GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK))
: ((ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true)));
if (max_z > INVALID_HEIGHT)
{
@@ -1440,7 +1470,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
if (!ToPlayer()->CanFly())
{
float ground_z = z;
- float max_z = GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK));
+ float max_z = GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK));
if (max_z > INVALID_HEIGHT)
{
if (z > max_z)
@@ -1951,7 +1981,7 @@ void WorldObject::ClearZoneScript()
m_zoneScript = NULL;
}
-TempSummon* WorldObject::SummonCreature(uint32 entry, const Position &pos, TempSummonType spwtype, uint32 duration, uint32 /*vehId*/) const
+TempSummon* WorldObject::SummonCreature(uint32 entry, Position const& pos, TempSummonType spwtype /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 duration /*= 0*/, uint32 /*vehId = 0*/) const
{
if (Map* map = FindMap())
{
@@ -1962,7 +1992,7 @@ TempSummon* WorldObject::SummonCreature(uint32 entry, const Position &pos, TempS
}
}
- return NULL;
+ return nullptr;
}
TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang /*= 0*/, TempSummonType spwtype /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 despwtime /*= 0*/) const
@@ -1972,29 +2002,30 @@ TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, fl
GetClosePoint(x, y, z, GetObjectSize());
ang = GetOrientation();
}
+
Position pos;
pos.Relocate(x, y, z, ang);
return SummonCreature(id, pos, spwtype, despwtime, 0);
}
-GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime)
+GameObject* WorldObject::SummonGameObject(uint32 entry, Position const& pos, G3D::Quat const& rot, uint32 respawnTime)
{
if (!IsInWorld())
- return NULL;
+ return nullptr;
GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry);
if (!goinfo)
{
TC_LOG_ERROR("sql.sql", "Gameobject template %u not found in database!", entry);
- return NULL;
+ return nullptr;
}
Map* map = GetMap();
GameObject* go = new GameObject();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, GetPhaseMask(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3, 100, GO_STATE_READY))
+ if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, GetPhaseMask(), pos, rot, 255, GO_STATE_READY))
{
delete go;
- return NULL;
+ return nullptr;
}
go->SetRespawnTime(respawnTime);
@@ -2007,6 +2038,18 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float
return go;
}
+GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, G3D::Quat const& rot, uint32 respawnTime)
+{
+ if (!x && !y && !z)
+ {
+ GetClosePoint(x, y, z, GetObjectSize());
+ ang = GetOrientation();
+ }
+
+ Position pos(x, y, z, ang);
+ return SummonGameObject(entry, pos, rot, respawnTime);
+}
+
Creature* WorldObject::SummonTrigger(float x, float y, float z, float ang, uint32 duration, CreatureAI* (*GetAI)(Creature*))
{
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
@@ -2038,7 +2081,10 @@ void WorldObject::SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list
std::vector<TempSummonData> const* data = sObjectMgr->GetSummonGroup(GetEntry(), GetTypeId() == TYPEID_GAMEOBJECT ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group);
if (!data)
+ {
+ TC_LOG_WARN("scripts", "%s (%s) tried to summon non-existing summon group %u.", GetName().c_str(), GetGUID().ToString().c_str(), group);
return;
+ }
for (std::vector<TempSummonData>::const_iterator itr = data->begin(); itr != data->end(); ++itr)
if (TempSummon* summon = SummonCreature(itr->entry, itr->pos, itr->type, itr->time))
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index f406b354293..87b158ab81a 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -488,6 +488,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true) const;
bool IsWithinLOS(float x, float y, float z) const;
bool IsWithinLOSInMap(WorldObject const* obj) const;
+ Position GetHitSpherePointFor(Position const& dest) const;
+ void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const;
bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const;
bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const;
bool IsInRange2d(float x, float y, float minRange, float maxRange) const;
@@ -540,9 +542,10 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void ClearZoneScript();
ZoneScript* GetZoneScript() const { return m_zoneScript; }
- TempSummon* SummonCreature(uint32 id, Position const &pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0) const;
+ TempSummon* SummonCreature(uint32 id, Position const& pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0) const;
TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0) const;
- GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime /* s */);
+ GameObject* SummonGameObject(uint32 entry, Position const& pos, G3D::Quat const& rot, uint32 respawnTime /* s */);
+ GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, G3D::Quat const& rot, uint32 respawnTime /* s */);
Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = NULL);
void SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list = NULL);
@@ -550,8 +553,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
GameObject* FindNearestGameObject(uint32 entry, float range) const;
GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const;
- void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) const;
- void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) const;
+ void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const;
+ void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const;
void GetPlayerListInGrid(std::list<Player*>& lList, float fMaxSearchRange) const;
void DestroyForNearbyPlayers();
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 37fb23b38f5..482fcb315f2 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -329,6 +329,9 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
_LoadSpellCooldowns();
LearnPetPassives();
InitLevelupSpellsForLevel();
+ if (map->IsBattleArena())
+ RemoveArenaAuras();
+
CastPetAuras(current);
}
@@ -748,7 +751,7 @@ void Pet::GivePetLevel(uint8 level)
if (!level || level == getLevel())
return;
- if (getPetType()==HUNTER_PET)
+ if (getPetType() == HUNTER_PET)
{
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32(sObjectMgr->GetXPForLevel(level)*PET_XP_FACTOR));
diff --git a/src/server/game/Entities/Player/CinematicMgr.cpp b/src/server/game/Entities/Player/CinematicMgr.cpp
index 07bf733c9ff..cc5a62300ad 100644
--- a/src/server/game/Entities/Player/CinematicMgr.cpp
+++ b/src/server/game/Entities/Player/CinematicMgr.cpp
@@ -27,6 +27,7 @@ CinematicMgr::CinematicMgr(Player* playerref)
m_cinematicDiff = 0;
m_lastCinematicCheck = 0;
m_activeCinematicCameraId = 0;
+ m_cinematicLength = 0;
m_cinematicCamera = nullptr;
m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f);
m_CinematicObject = nullptr;
@@ -70,6 +71,8 @@ void CinematicMgr::BeginCinematic()
FlyByCameraCollection::const_reverse_iterator camrevitr = m_cinematicCamera->rbegin();
if (camrevitr != m_cinematicCamera->rend())
m_cinematicLength = camrevitr->timeStamp;
+ else
+ m_cinematicLength = 0;
}
}
}
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 9ed101bf52e..bc95ea1107e 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -567,6 +567,7 @@ Player::~Player()
delete m_runes;
delete m_achievementMgr;
delete m_reputationMgr;
+ delete _cinematicMgr;
sWorld->DecreasePlayerCount();
}
@@ -1710,7 +1711,7 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
// Pets info
uint32 petDisplayId = 0;
uint32 petLevel = 0;
- uint32 petFamily = 0;
+ CreatureFamily petFamily = CREATURE_FAMILY_NONE;
// show pet at selection character in character list only for non-ghost character
if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT))
@@ -2418,11 +2419,11 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflag
return nullptr;
// Deathstate checks
- if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_GHOST))
+ if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE))
return nullptr;
// alive or spirit healer
- if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_DEAD_INTERACT))
+ if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD))
return nullptr;
// appropriate npc type
@@ -2793,6 +2794,8 @@ void Player::GiveLevel(uint8 level)
if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
UpdateSkillsToMaxSkillsForLevel();
+ _ApplyAllLevelScaleItemMods(true);
+
// set current level health and mana/energy to maximum after applying all mods.
SetFullHealth();
SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
@@ -2802,8 +2805,6 @@ void Player::GiveLevel(uint8 level)
SetPower(POWER_FOCUS, 0);
SetPower(POWER_HAPPINESS, 0);
- _ApplyAllLevelScaleItemMods(true);
-
// update level to hunter/summon pet
if (Pet* pet = GetPet())
pet->SynchronizeLevelWithOwner();
@@ -2996,7 +2997,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
// cleanup unit flags (will be re-applied if need at aura load).
RemoveFlag(UNIT_FIELD_FLAGS,
- UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
+ UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_ATTACKABLE_1 |
UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_LOOTING |
UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
@@ -3201,10 +3202,9 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
}
}
- PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
PlayerTalent* newtalent = new PlayerTalent();
- newtalent->state = state;
+ newtalent->state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
newtalent->spec = spec;
(*m_talents[spec])[spellId] = newtalent;
@@ -4211,7 +4211,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
// close player ticket if any
GmTicket* ticket = sTicketMgr->GetTicketByPlayer(playerguid);
if (ticket)
- ticket->SetClosedBy(playerguid);
+ sTicketMgr->CloseTicket(ticket->GetId(), playerguid);
// remove from arena teams
LeaveAllArenaTeams(playerguid);
@@ -4940,6 +4940,9 @@ void Player::DurabilityPointsLossAll(int32 points, bool inventory)
void Player::DurabilityPointsLoss(Item* item, int32 points)
{
+ if (HasAuraType(SPELL_AURA_PREVENT_DURABILITY_LOSS))
+ return;
+
int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
int32 pNewDurability = pOldDurability - points;
@@ -5173,71 +5176,67 @@ void Player::UpdateLocalChannels(uint32 newZone)
return;
std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()];
-
for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i)
{
- if (ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i))
- {
- Channel* usedChannel = nullptr;
+ ChatChannelsEntry const* channelEntry = sChatChannelsStore.LookupEntry(i);
+ if (!channelEntry)
+ continue;
- for (JoinedChannelsList::iterator itr = m_channels.begin(); itr != m_channels.end(); ++itr)
+ Channel* usedChannel = nullptr;
+ for (Channel* channel : m_channels)
+ {
+ if (channel->GetChannelId() == i)
{
- if ((*itr)->GetChannelId() == i)
- {
- usedChannel = *itr;
- break;
- }
+ usedChannel = channel;
+ break;
}
+ }
- Channel* removeChannel = nullptr;
- Channel* joinChannel = nullptr;
- bool sendRemove = true;
+ Channel* removeChannel = nullptr;
+ Channel* joinChannel = nullptr;
+ bool sendRemove = true;
- if (CanJoinConstantChannelInZone(channel, current_zone))
+ if (CanJoinConstantChannelInZone(channelEntry, current_zone))
+ {
+ if (!(channelEntry->flags & CHANNEL_DBC_FLAG_GLOBAL))
{
- if (!(channel->flags & CHANNEL_DBC_FLAG_GLOBAL))
- {
- if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY && usedChannel)
- continue; // Already on the channel, as city channel names are not changing
-
- char new_channel_name_buf[100];
- char const* currentNameExt;
-
- if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY)
- currentNameExt = sObjectMgr->GetTrinityStringForDBCLocale(LANG_CHANNEL_CITY);
- else
- currentNameExt = current_zone_name.c_str();
+ if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY && usedChannel)
+ continue; // Already on the channel, as city channel names are not changing
- snprintf(new_channel_name_buf, 100, channel->pattern[m_session->GetSessionDbcLocale()], currentNameExt);
+ std::string currentNameExt;
+ if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY)
+ currentNameExt = sObjectMgr->GetTrinityStringForDBCLocale(LANG_CHANNEL_CITY);
+ else
+ currentNameExt = current_zone_name;
- joinChannel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID);
- if (usedChannel)
+ std::string newChannelName = Trinity::StringFormat(channelEntry->pattern[m_session->GetSessionDbcLocale()], currentNameExt.c_str());
+ joinChannel = cMgr->GetJoinChannel(newChannelName, channelEntry->ChannelID);
+ if (usedChannel)
+ {
+ if (joinChannel != usedChannel)
{
- if (joinChannel != usedChannel)
- {
- removeChannel = usedChannel;
- sendRemove = false; // Do not send leave channel, it already replaced at client
- }
- else
- joinChannel = nullptr;
+ removeChannel = usedChannel;
+ sendRemove = false; // Do not send leave channel, it already replaced at client
}
+ else
+ joinChannel = nullptr;
}
- else
- joinChannel = cMgr->GetJoinChannel(channel->pattern[m_session->GetSessionDbcLocale()], channel->ChannelID);
}
else
- removeChannel = usedChannel;
+ joinChannel = cMgr->GetJoinChannel(channelEntry->pattern[m_session->GetSessionDbcLocale()], channelEntry->ChannelID);
+ }
+ else
+ removeChannel = usedChannel;
- if (joinChannel)
- joinChannel->JoinChannel(this, ""); // Changed Channel: ... or Joined Channel: ...
+ if (joinChannel)
+ joinChannel->JoinChannel(this, ""); // Changed Channel: ... or Joined Channel: ...
- if (removeChannel)
- {
- removeChannel->LeaveChannel(this, sendRemove); // Leave old channel
- std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel
- LeftChannel(removeChannel); // Remove from player's channel list
- cMgr->LeftChannel(name); // Delete if empty
- }
+ if (removeChannel)
+ {
+ removeChannel->LeaveChannel(this, sendRemove); // Leave old channel
+ std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel
+ LeftChannel(removeChannel); // Remove from player's channel list
+ cMgr->LeftChannel(name); // Delete if empty
}
}
}
@@ -6254,14 +6253,14 @@ bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) co
case ACTION_BUTTON_SPELL:
if (!sSpellMgr->GetSpellInfo(action))
{
- TC_LOG_ERROR("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): spell not exist",
+ TC_LOG_DEBUG("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): spell does not exist. This can be due to a character imported from a different expansion",
action, button, GetName().c_str(), GetGUID().ToString().c_str());
return false;
}
if (!HasSpell(action))
{
- TC_LOG_ERROR("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): player don't known this spell",
+ TC_LOG_DEBUG("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): player does not known this spell, this can be due to a player changing their talents",
action, button, GetName().c_str(), GetGUID().ToString().c_str());
return false;
}
@@ -7207,6 +7206,13 @@ void Player::DuelComplete(DuelCompleteType type)
if (!duel)
return;
+ // Check if DuelComplete() has been called already up in the stack and in that case don't do anything else here
+ if (duel->isCompleted || ASSERT_NOTNULL(duel->opponent->duel)->isCompleted)
+ return;
+
+ duel->isCompleted = true;
+ duel->opponent->duel->isCompleted = true;
+
TC_LOG_DEBUG("entities.unit", "Player::DuelComplete: Player '%s' (%s), Opponent: '%s' (%s)",
GetName().c_str(), GetGUID().ToString().c_str(), duel->opponent->GetName().c_str(), duel->opponent->GetGUID().ToString().c_str());
@@ -12141,7 +12147,8 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update)
// remove item dependent auras and casts (only weapon and armor slots)
if (slot < EQUIPMENT_SLOT_END)
{
- RemoveItemDependentAurasAndCasts(pItem);
+ if (update)
+ RemoveItemDependentAurasAndCasts(pItem);
// remove held enchantments, update expertise
if (slot == EQUIPMENT_SLOT_MAINHAND)
@@ -12320,7 +12327,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
// Delete rolled money / loot from db.
// MUST be done before RemoveFromWorld() or GetTemplate() fails
if (ItemTemplate const* pTmp = pItem->GetTemplate())
- if (pTmp->Flags & ITEM_PROTO_FLAG_OPENABLE)
+ if (pTmp->Flags & ITEM_PROTO_FLAG_HAS_LOOT)
pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB();
if (IsInWorld() && update)
@@ -14003,8 +14010,11 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
break;
case GOSSIP_OPTION_TRAINER:
if (getClass() != creature->GetCreatureTemplate()->trainer_class && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS)
+ {
TC_LOG_ERROR("sql.sql", "GOSSIP_OPTION_TRAINER:: Player %s (GUID: %u) requested wrong gossip menu: %u with wrong class: %u at Creature: %s (Entry: %u, Trainer Class: %u)",
- GetName().c_str(), GetGUID().GetCounter(), menu->GetGossipMenu().GetMenuId(), getClass(), creature->GetName().c_str(), creature->GetEntry(), creature->GetCreatureTemplate()->trainer_class);
+ GetName().c_str(), GetGUID().GetCounter(), menu->GetGossipMenu().GetMenuId(), getClass(), creature->GetName().c_str(), creature->GetEntry(), creature->GetCreatureTemplate()->trainer_class);
+ canTalk = false;
+ }
// no break;
case GOSSIP_OPTION_GOSSIP:
case GOSSIP_OPTION_SPIRITGUIDE:
@@ -14808,7 +14818,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id);
- SendQuestUpdate(quest_id);
+ SendQuestUpdate();
if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled
{
@@ -14835,12 +14845,8 @@ void Player::CompleteQuest(uint32 quest_id)
SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
- {
if (qInfo->HasFlag(QUEST_FLAGS_TRACKING))
RewardQuest(qInfo, 0, this, false);
- else
- SendQuestComplete(quest_id);
- }
}
if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled
@@ -14876,15 +14882,23 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
uint32 quest_id = quest->GetQuestId();
for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
- if (quest->RequiredItemId[i])
- DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true);
-
+ {
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
+ {
+ if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1))
+ DestroyItemCount(quest->RequiredItemId[i], 9999, true, true);
+ else
+ DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true);
+ }
+ }
for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
{
- if (quest->ItemDrop[i])
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i]))
{
- uint32 count = quest->ItemDropQuantity[i];
- DestroyItemCount(quest->ItemDrop[i], count ? count : 9999, true);
+ if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1))
+ DestroyItemCount(quest->ItemDrop[i], 9999, true, true);
+ else
+ DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true);
}
}
@@ -14980,7 +14994,10 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
{
/// @todo Poor design of mail system
SQLTransaction trans = CharacterDatabase.BeginTransaction();
- MailDraft(mail_template_id).SendMailTo(trans, this, questGiver, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs());
+ if (uint32 questMailSender = quest->GetRewMailSenderEntry())
+ MailDraft(mail_template_id).SendMailTo(trans, this, questMailSender, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs());
+ else
+ MailDraft(mail_template_id).SendMailTo(trans, this, questGiver, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs());
CharacterDatabase.CommitTransaction(trans);
}
@@ -15001,8 +15018,11 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
SetSeasonalQuestStatus(quest_id);
RemoveActiveQuest(quest_id, false);
- m_RewardedQuests.insert(quest_id);
- m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE;
+ if (quest->CanIncreaseRewardedQuestCounters())
+ {
+ m_RewardedQuests.insert(quest_id);
+ m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE;
+ }
// StoreNewItem, mail reward, etc. save data directly to the database
// to prevent exploitable data desynchronisation we save the quest status to the database too
@@ -15048,7 +15068,9 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
UpdatePvPState();
}
- SendQuestUpdate(quest_id);
+ SendQuestUpdate();
+
+ SendQuestGiverStatusMultiple();
//lets remove flag for delayed teleports
SetCanDelayTeleport(false);
@@ -15086,14 +15108,32 @@ void Player::FailQuest(uint32 questId)
SendQuestFailed(questId);
// Destroy quest items on quest failure.
- for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- if (quest->RequiredItemId[i] > 0 && quest->RequiredItemCount[i] > 0)
- // Destroy items received on starting the quest.
- DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true);
+ for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
+ if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1))
+ DestroyItemCount(quest->RequiredItemId[i], 9999, true, true);
+
for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
- if (quest->ItemDrop[i] > 0 && quest->ItemDropQuantity[i] > 0)
- // Destroy items received during the quest.
- DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true);
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i]))
+ if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1))
+ DestroyItemCount(quest->ItemDrop[i], 9999, true, true);
+ }
+}
+
+void Player::AbandonQuest(uint32 questId)
+{
+ if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
+ {
+ // Destroy quest items on quest abandon.
+ for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
+ if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1))
+ DestroyItemCount(quest->RequiredItemId[i], 9999, true, true);
+
+ for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i]))
+ if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1))
+ DestroyItemCount(quest->ItemDrop[i], 9999, true, true);
}
}
@@ -15683,14 +15723,16 @@ bool Player::CanShareQuest(uint32 quest_id) const
void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*= true*/)
{
- if (sObjectMgr->GetQuestTemplate(questId))
+ if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
{
m_QuestStatus[questId].Status = status;
- m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
+
+ if (!quest->IsAutoComplete())
+ m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
}
if (update)
- SendQuestUpdate(questId);
+ SendQuestUpdate();
sScriptMgr->OnQuestStatusChange(this, questId, status);
}
@@ -15705,7 +15747,7 @@ void Player::RemoveActiveQuest(uint32 questId, bool update /*= true*/)
}
if (update)
- SendQuestUpdate(questId);
+ SendQuestUpdate();
}
void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/)
@@ -15718,33 +15760,26 @@ void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/)
}
if (update)
- SendQuestUpdate(questId);
+ SendQuestUpdate();
}
-void Player::SendQuestUpdate(uint32 questId)
+void Player::SendQuestUpdate()
{
uint32 zone = 0, area = 0;
+ GetZoneAndAreaId(zone, area);
- SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(questId);
- if (saBounds.first != saBounds.second)
- {
- GetZoneAndAreaId(zone, area);
-
- for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
- if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area))
- if (!HasAura(itr->second->spellId))
- CastSpell(this, itr->second->spellId, true);
- }
+ SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(area);
- saBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(questId);
if (saBounds.first != saBounds.second)
{
- if (!zone || !area)
- GetZoneAndAreaId(zone, area);
-
for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
+ {
if (!itr->second->IsFitToRequirements(this, zone, area))
RemoveAurasDueToSpell(itr->second->spellId);
+ else if (itr->second->autocast)
+ if (!HasAura(itr->second->spellId))
+ CastSpell(this, itr->second->spellId, true);
+ }
}
UpdateForQuestWorldObjects();
@@ -16593,6 +16628,50 @@ void Player::SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint
SetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT, GetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT) + add_count);
}
+void Player::SendQuestGiverStatusMultiple()
+{
+ uint32 count = 0;
+
+ WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4);
+ data << uint32(count); // placeholder
+
+ for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
+ {
+ uint32 questStatus = DIALOG_STATUS_NONE;
+
+ if (itr->IsAnyTypeCreature())
+ {
+ // need also pet quests case support
+ Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
+ if (!questgiver || questgiver->IsHostileTo(this))
+ continue;
+ if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
+ continue;
+
+ questStatus = GetQuestDialogStatus(questgiver);
+
+ data << uint64(questgiver->GetGUID());
+ data << uint8(questStatus);
+ ++count;
+ }
+ else if (itr->IsGameObject())
+ {
+ GameObject* questgiver = GetMap()->GetGameObject(*itr);
+ if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
+ continue;
+
+ questStatus = GetQuestDialogStatus(questgiver);
+
+ data << uint64(questgiver->GetGUID());
+ data << uint8(questStatus);
+ ++count;
+ }
+ }
+
+ data.put<uint32>(0, count); // write real count
+ GetSession()->SendPacket(&data);
+}
+
bool Player::HasPvPForcingQuest() const
{
for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
@@ -17518,6 +17597,8 @@ bool Player::isAllowedToLoot(const Creature* creature)
const Loot* loot = &creature->loot;
if (loot->isLooted()) // nothing to loot or everything looted.
return false;
+ if (!loot->hasItemForAll() && !loot->hasItemFor(this)) // no loot in creature for this player
+ return false;
if (loot->loot_type == LOOT_SKINNING)
return creature->GetSkinner() == GetGUID();
@@ -17574,7 +17655,7 @@ void Player::_LoadActions(PreparedQueryResult result)
ab->uState = ACTIONBUTTON_UNCHANGED;
else
{
- TC_LOG_ERROR("entities.player", "Player::_LoadActions: Player '%s' (%s) has an invalid action button (Button: %u, Action: %u, Type: %u). It will be deleted at next save.",
+ TC_LOG_DEBUG("entities.player", "Player::_LoadActions: Player '%s' (%s) has an invalid action button (Button: %u, Action: %u, Type: %u). It will be deleted at next save. This can be due to a player changing their talents.",
GetName().c_str(), GetGUID().ToString().c_str(), button, action, type);
// Will be deleted in DB at next save (it can create data until save but marked as deleted).
@@ -18219,9 +18300,10 @@ void Player::_LoadQuestStatusRewarded(PreparedQueryResult result)
if (quest->GetBonusTalents())
m_questRewardTalentCount += quest->GetBonusTalents();
- }
- m_RewardedQuests.insert(quest_id);
+ if (quest->CanIncreaseRewardedQuestCounters())
+ m_RewardedQuests.insert(quest_id);
+ }
}
while (result->NextRow());
}
@@ -18780,7 +18862,7 @@ bool Player::CheckInstanceValidity(bool /*isLogin*/)
return true;
// non-instances are always valid
- Map* map = GetMap();
+ Map* map = FindMap();
if (!map || !map->IsDungeon())
return true;
@@ -20940,7 +21022,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
return false;
}
- if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL))
return false;
// taximaster case
@@ -21139,7 +21221,7 @@ void Player::CleanupAfterTaxiFlight()
{
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
Dismount();
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT);
getHostileRefManager().setOnlineOfflineState(true);
}
@@ -21944,8 +22026,8 @@ void Player::ReportedAfkBy(Player* reporter)
if (m_bgData.bgAfkReporter.find(reporter->GetGUID().GetCounter()) == m_bgData.bgAfkReporter.end() && !HasAura(43680) && !HasAura(43681) && reporter->CanReportAfkDueToLimit())
{
m_bgData.bgAfkReporter.insert(reporter->GetGUID().GetCounter());
- // 3 players have to complain to apply debuff
- if (m_bgData.bgAfkReporter.size() >= 3)
+ // by default 3 players have to complain to apply debuff
+ if (m_bgData.bgAfkReporter.size() >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_REPORT_AFK))
{
// cast 'Idle' spell
CastSpell(this, 43680, true);
@@ -22870,6 +22952,24 @@ void Player::SetDailyQuestStatus(uint32 quest_id)
}
}
+bool Player::IsDailyQuestDone(uint32 quest_id)
+{
+ bool found = false;
+ if (sObjectMgr->GetQuestTemplate(quest_id))
+ {
+ for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ {
+ if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx) == quest_id)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
void Player::SetWeeklyQuestStatus(uint32 quest_id)
{
m_weeklyquests.insert(quest_id);
@@ -24016,6 +24116,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
if (target->isType(TYPEMASK_UNIT) && target != GetVehicleBase())
static_cast<Unit*>(target)->AddPlayerToVision(this);
+ SetSeer(target);
}
else
{
@@ -24031,7 +24132,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
static_cast<Unit*>(target)->RemovePlayerFromVision(this);
//must immediately set seer back otherwise may crash
- m_seer = this;
+ SetSeer(this);
//WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
//GetSession()->SendPacket(&data);
@@ -26319,7 +26420,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction());
pet->setPowerType(POWER_MANA);
- pet->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
pet->InitStatsForLevel(getLevel());
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 372a49b4f9d..38fff9b8d83 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -269,6 +269,7 @@ struct PlayerInfo
uint16 displayId_f;
PlayerCreateInfoItems item;
PlayerCreateInfoSpells customSpells;
+ PlayerCreateInfoSpells castSpells;
PlayerCreateInfoActions action;
PlayerCreateInfoSkills skills;
@@ -288,7 +289,7 @@ struct PvPInfo
struct DuelInfo
{
- DuelInfo() : initiator(nullptr), opponent(nullptr), startTimer(0), startTime(0), outOfBound(0), isMounted(false) { }
+ DuelInfo() : initiator(nullptr), opponent(nullptr), startTimer(0), startTime(0), outOfBound(0), isMounted(false), isCompleted(false) { }
Player* initiator;
Player* opponent;
@@ -296,6 +297,7 @@ struct DuelInfo
time_t startTime;
time_t outOfBound;
bool isMounted;
+ bool isCompleted;
};
struct Areas
@@ -1333,6 +1335,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);
void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true);
@@ -1361,10 +1364,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SetQuestStatus(uint32 questId, QuestStatus status, bool update = true);
void RemoveActiveQuest(uint32 questId, bool update = true);
void RemoveRewardedQuest(uint32 questId, bool update = true);
- void SendQuestUpdate(uint32 questId);
+ void SendQuestUpdate();
QuestGiverStatus GetQuestDialogStatus(Object* questGiver);
void SetDailyQuestStatus(uint32 quest_id);
+ bool IsDailyQuestDone(uint32 quest_id);
void SetWeeklyQuestStatus(uint32 quest_id);
void SetMonthlyQuestStatus(uint32 quest_id);
void SetSeasonalQuestStatus(uint32 quest_id);
@@ -1413,6 +1417,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendQuestUpdateAddItem(Quest const* quest, uint32 itemIdx, uint16 count) const;
void SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGOIdx, uint16 oldCount, uint16 addCount);
void SendQuestUpdateAddPlayer(Quest const* quest, uint16 oldCount, uint16 addCount);
+ void SendQuestGiverStatusMultiple();
ObjectGuid GetDivider() const { return m_divider; }
void SetDivider(ObjectGuid guid) { m_divider = guid; }
@@ -1596,7 +1601,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void AddSpellMod(SpellModifier* mod, bool apply);
bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr) const;
- template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell = nullptr);
+ template <class T>
+ void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr);
void RemoveSpellMods(Spell* spell);
void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr);
void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr);
@@ -2604,11 +2610,13 @@ TC_GAME_API void AddItemsSetItem(Player* player, Item* item);
TC_GAME_API void RemoveItemsSetItem(Player* player, ItemTemplate const* proto);
// "the bodies of template functions must be made available in a header file"
-template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell)
+template <class T>
+void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
- return 0;
+ return;
+
float totalmul = 1.0f;
int32 totalflat = 0;
@@ -2644,9 +2652,8 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas
DropModCharge(mod, spell);
}
- float diff = (float)basevalue * (totalmul - 1.0f) + (float)totalflat;
- basevalue = T((float)basevalue + diff);
- return T(diff);
+
+ basevalue = T(float(basevalue + totalflat) * totalmul);
}
#endif
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index 5e5c6901471..0d97e120fff 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -92,7 +92,8 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid,
SetGoType(GAMEOBJECT_TYPE_MO_TRANSPORT);
SetGoAnimProgress(animprogress);
SetName(goinfo->name);
- UpdateRotationFields(0.0f, 1.0f);
+ SetWorldRotation(G3D::Quat());
+ SetParentRotation(G3D::Quat());
m_model = CreateModel();
return true;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index ee815f199bd..ac924f39a03 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -459,12 +459,6 @@ void Unit::resetAttackTimer(WeaponAttackType type)
m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]);
}
-float Unit::GetMeleeReach() const
-{
- float reach = m_floatValues[UNIT_FIELD_COMBATREACH];
- return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH;
-}
-
bool Unit::IsWithinCombatRange(const Unit* obj, float dist2compare) const
{
if (!obj || !IsInMap(obj) || !InSamePhase(obj))
@@ -481,7 +475,7 @@ bool Unit::IsWithinCombatRange(const Unit* obj, float dist2compare) const
return distsq < maxdist * maxdist;
}
-bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const
+bool Unit::IsWithinMeleeRange(Unit const* obj) const
{
if (!obj || !IsInMap(obj) || !InSamePhase(obj))
return false;
@@ -491,10 +485,9 @@ bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const
float dz = GetPositionZMinusOffset() - obj->GetPositionZMinusOffset();
float distsq = dx*dx + dy*dy + dz*dz;
- float sizefactor = GetMeleeReach() + obj->GetMeleeReach();
- float maxdist = dist + sizefactor;
+ float maxdist = GetCombatReach() + obj->GetCombatReach() + 4.0f / 3.0f;
- return distsq < maxdist * maxdist;
+ return distsq <= maxdist * maxdist;
}
void Unit::GetRandomContactPoint(const Unit* obj, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const
@@ -599,11 +592,14 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam
if (victim->GetTypeId() == TYPEID_PLAYER && this != victim)
{
- // Signal to pets that their owner was attacked
- Pet* pet = victim->ToPlayer()->GetPet();
+ // Signal to pets that their owner was attacked - except when DOT.
+ if (damagetype != DOT)
+ {
+ Pet* pet = victim->ToPlayer()->GetPet();
- if (pet && pet->IsAlive())
- pet->AI()->OwnerAttackedBy(this);
+ if (pet && pet->IsAlive())
+ pet->AI()->OwnerAttackedBy(this);
+ }
if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD))
return 0;
@@ -624,6 +620,17 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam
else
victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, 0);
+ // interrupt spells with SPELL_INTERRUPT_FLAG_ABORT_ON_DMG on absorbed damage (no dots)
+ if (!damage && damagetype != DOT && cleanDamage && cleanDamage->absorbed_damage)
+ if (victim != this && victim->GetTypeId() == TYPEID_PLAYER)
+ if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
+ if (spell->getState() == SPELL_STATE_PREPARING)
+ {
+ uint32 interruptFlags = spell->m_spellInfo->InterruptFlags;
+ if ((interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) != 0)
+ victim->InterruptNonMeleeSpells(false);
+ }
+
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vCopyDamageCopy(victim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT));
@@ -1355,7 +1362,8 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
if (damageInfo->blocked_amount && damageInfo->TargetState != VICTIMSTATE_BLOCKS)
victim->HandleEmoteCommand(EMOTE_ONESHOT_PARRY_SHIELD);
- if (damageInfo->TargetState == VICTIMSTATE_PARRY)
+ if (damageInfo->TargetState == VICTIMSTATE_PARRY &&
+ (GetTypeId() != TYPEID_UNIT || (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN) == 0))
{
// Get attack timers
float offtime = float(victim->getAttackTimer(OFF_ATTACK));
@@ -2253,7 +2261,7 @@ float Unit::CalculateLevelPenalty(SpellInfo const* spellProto) const
float LvlPenalty = 0.0f;
if (spellProto->SpellLevel < 20)
- LvlPenalty = 20.0f - spellProto->SpellLevel * 3.75f;
+ LvlPenalty = (20.0f - spellProto->SpellLevel) * 3.75f;
float LvlFactor = (float(spellProto->SpellLevel) + 6.0f) / float(getLevel());
if (LvlFactor > 1.0f)
LvlFactor = 1.0f;
@@ -2387,37 +2395,19 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
if (roll < tmp)
return SPELL_MISS_MISS;
- // Chance resist mechanic (select max value from every mechanic spell effect)
- int32 resist_mech = 0;
- // Get effects mechanic and chance
- for (uint8 eff = 0; eff < MAX_SPELL_EFFECTS; ++eff)
- {
- int32 effect_mech = spellInfo->GetEffectMechanic(eff);
- if (effect_mech)
- {
- int32 temp = victim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech);
- if (resist_mech < temp * 100)
- resist_mech = temp * 100;
- }
- }
- // Roll chance
- tmp += resist_mech;
+ // Chance resist mechanic
+ int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100;
+ tmp += resist_chance;
if (roll < tmp)
return SPELL_MISS_RESIST;
- bool canDodge = true;
- bool canParry = true;
- bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL);
-
- // Same spells cannot be parry/dodge
+ // Same spells cannot be parried/dodged
if (spellInfo->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK))
return SPELL_MISS_NONE;
- // Chance resist mechanic
- int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100;
- tmp += resist_chance;
- if (roll < tmp)
- return SPELL_MISS_RESIST;
+ bool canDodge = true;
+ bool canParry = true;
+ bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL);
// Ranged attacks can only miss, resist and deflect
if (attType == RANGED_ATTACK)
@@ -2854,6 +2844,15 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victi
else
crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);
+ AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER);
+ for (AuraEffect const* aurEff : critChanceForCaster)
+ {
+ if (aurEff->GetCasterGUID() != GetGUID())
+ continue;
+
+ crit += aurEff->GetAmount();
+ }
+
crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
// reduce crit chance from Rating for players
@@ -3189,6 +3188,15 @@ int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const
return 0;
}
+bool Unit::CanMoveDuringChannel() const
+{
+ if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
+ if (spell->getState() != SPELL_STATE_FINISHED)
+ return spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) && spell->IsChannelActive();
+
+ return false;
+}
+
bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const
{
return IsWithinDistInMap(target, distance) && HasInArc(arc, target);
@@ -5287,7 +5295,7 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType
}
//victim may be NULL
-bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown)
+bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown)
{
SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
uint32 effIndex = triggeredByAura->GetEffIndex();
@@ -5297,8 +5305,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL;
uint32 triggered_spell_id = 0;
- uint32 cooldown_spell_id = 0; // for random trigger, will be one of the triggered spell to avoid repeatable triggers
- // otherwise, it's the triggered_spell_id by default
Unit* target = victim;
int32 basepoints0 = 0;
ObjectGuid originalCaster;
@@ -5374,24 +5380,20 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
case CLASS_PALADIN: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409
case CLASS_DRUID: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409
triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409);
- cooldown_spell_id = 39511;
break;
case CLASS_ROGUE: // 39511, 40997, 40998, 41002, 41005, 41011
case CLASS_WARRIOR: // 39511, 40997, 40998, 41002, 41005, 41011
case CLASS_DEATH_KNIGHT:
triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011);
- cooldown_spell_id = 39511;
break;
case CLASS_PRIEST: // 40999, 41002, 41005, 41009, 41011, 41406, 41409
case CLASS_SHAMAN: // 40999, 41002, 41005, 41009, 41011, 41406, 41409
case CLASS_MAGE: // 40999, 41002, 41005, 41009, 41011, 41406, 41409
case CLASS_WARLOCK: // 40999, 41002, 41005, 41009, 41011, 41406, 41409
triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409);
- cooldown_spell_id = 40999;
break;
case CLASS_HUNTER: // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409
triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409);
- cooldown_spell_id = 40997;
break;
default:
return false;
@@ -5621,11 +5623,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
uint8 rand_spell = urand(0, (RandomSpells.size() - 1));
CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster);
- for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr)
- {
- if (!GetSpellHistory()->HasCooldown(*itr))
- GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown));
- }
break;
}
case 71562: // Deathbringer's Will Heroic
@@ -5667,11 +5664,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
uint8 rand_spell = urand(0, (RandomSpells.size() - 1));
CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster);
- for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr)
- {
- if (!GetSpellHistory()->HasCooldown(*itr))
- GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown));
- }
break;
}
case 65032: // Boom aura (321 Boombot)
@@ -6309,24 +6301,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return true;
}
}
- // Eclipse
- if (dummySpell->SpellIconID == 2856 && GetTypeId() == TYPEID_PLAYER)
- {
- if (!procSpell || effIndex != 0)
- return false;
-
- bool isWrathSpell = (procSpell->SpellFamilyFlags[0] & 1);
-
- if (!roll_chance_f(dummySpell->ProcChance * (isWrathSpell ? 0.6f : 1.0f)))
- return false;
-
- target = this;
- if (target->HasAura(isWrathSpell ? 48517 : 48518))
- return false;
-
- triggered_spell_id = isWrathSpell ? 48518 : 48517;
- break;
- }
break;
}
case SPELLFAMILY_ROGUE:
@@ -6491,6 +6465,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
// 4 healing tick
basepoints0 = triggerAmount * damage / 400;
triggered_spell_id = 54203;
+ // Add remaining ticks to healing done
+ basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL);
break;
}
switch (dummySpell->Id)
@@ -6507,7 +6483,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
// Item - Paladin T8 Holy 4P Bonus
if (Unit* caster = triggeredByAura->GetCaster())
if (AuraEffect const* aurEff = caster->GetAuraEffect(64895, 0))
- cooldown = aurEff->GetAmount();
+ cooldown = Milliseconds(aurEff->GetAmount());
target = this;
break;
@@ -6822,10 +6798,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (!player || !castItem || !castItem->IsEquipped() || !victim || !victim->IsAlive())
return false;
- // custom cooldown processing case
- if (cooldown && GetSpellHistory()->HasCooldown(dummySpell->Id))
- return false;
-
if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID())
return false;
@@ -6888,8 +6860,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
triggered_spell_id = 33750;
// apply cooldown before cast to prevent processing itself
- if (cooldown)
- player->GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown));
+ triggeredByAura->GetBase()->AddProcCooldown(std::chrono::steady_clock::now() + cooldown);
+ cooldown = Milliseconds::zero();
// Attack Twice
for (uint32 i = 0; i < 2; ++i)
@@ -7132,10 +7104,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (!procSpell || GetTypeId() != TYPEID_PLAYER || !victim)
return false;
- // custom cooldown processing case
- if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(dummySpell->Id))
- return false;
-
uint32 spellId = 0;
// Every Lightning Bolt and Chain Lightning spell have duplicate vs half damage and zero cost
switch (procSpell->Id)
@@ -7181,10 +7149,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
}
CastSpell(victim, spellId, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown));
-
return true;
}
// Static Shock
@@ -7486,26 +7450,17 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return false;
}
- if (cooldown_spell_id == 0)
- cooldown_spell_id = triggered_spell_id;
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(cooldown_spell_id))
- return false;
-
if (basepoints0)
CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura, originalCaster);
else
CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster);
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- GetSpellHistory()->AddCooldown(cooldown_spell_id, 0, std::chrono::seconds(cooldown));
-
return true;
}
// Used in case when access to whole aura is needed
// All procs should be handled like this...
-bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, uint32 cooldown, bool * handled)
+bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, bool* handled)
{
SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
@@ -7717,12 +7672,6 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp
case 49222:
{
*handled = true;
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- {
- if (GetSpellHistory()->HasCooldown(100000))
- return false;
- GetSpellHistory()->AddCooldown(100000, 0, std::chrono::seconds(cooldown));
- }
return true;
}
// Hungering Cold aura drop
@@ -7765,7 +7714,7 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp
return false;
}
-bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown)
+bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx)
{
// Get triggered aura spell info
SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo();
@@ -7791,95 +7740,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
{
switch (auraSpellInfo->SpellFamilyName)
{
- case SPELLFAMILY_GENERIC:
- switch (auraSpellInfo->Id)
- {
- case 43820: // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket)
- // Pct value stored in dummy
- basepoints0 = victim->GetCreateHealth() * auraSpellInfo->Effects[1].CalcValue() / 100;
- target = victim;
- break;
- case 57345: // Darkmoon Card: Greatness
- {
- float stat = 0.0f;
- // strength
- if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 60229;stat = GetStat(STAT_STRENGTH); }
- // agility
- if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 60233;stat = GetStat(STAT_AGILITY); }
- // intellect
- if (GetStat(STAT_INTELLECT)> stat) { trigger_spell_id = 60234;stat = GetStat(STAT_INTELLECT);}
- // spirit
- if (GetStat(STAT_SPIRIT) > stat) { trigger_spell_id = 60235; }
- break;
- }
- case 64568: // Blood Reserve
- {
- if (HealthBelowPctDamaged(35, damage))
- {
- CastCustomSpell(this, 64569, &triggerAmount, NULL, NULL, true);
- RemoveAura(64568);
- }
- return false;
- }
- case 67702: // Death's Choice, Item - Coliseum 25 Normal Melee Trinket
- {
- float stat = 0.0f;
- // strength
- if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67708;stat = GetStat(STAT_STRENGTH); }
- // agility
- if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67703; }
- break;
- }
- case 67771: // Death's Choice (heroic), Item - Coliseum 25 Heroic Melee Trinket
- {
- float stat = 0.0f;
- // strength
- if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67773;stat = GetStat(STAT_STRENGTH); }
- // agility
- if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67772; }
- break;
- }
- // Mana Drain Trigger
- case 27522:
- case 40336:
- {
- // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target.
- if (IsAlive())
- CastSpell(this, 29471, true, castItem, triggeredByAura);
- if (victim && victim->IsAlive())
- CastSpell(victim, 27526, true, castItem, triggeredByAura);
- return true;
- }
- // Evasive Maneuvers
- case 50240:
- {
- // Remove a Evasive Charge
- Aura* charge = GetAura(50241);
- if (charge && charge->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL))
- RemoveAurasDueToSpell(50240);
- break;
- }
- // Battle Experience
- // already handled in gunship battle script
- case 71201:
- return false;
- }
- break;
- case SPELLFAMILY_MAGE:
- if (auraSpellInfo->SpellIconID == 2127) // Blazing Speed
- {
- switch (auraSpellInfo->Id)
- {
- case 31641: // Rank 1
- case 31642: // Rank 2
- trigger_spell_id = 31643;
- break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed", auraSpellInfo->Id);
- return false;
- }
- }
- break;
case SPELLFAMILY_WARLOCK:
{
// Drain Soul
@@ -7902,325 +7762,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
// Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura
return false;
}
- // Nether Protection
- else if (auraSpellInfo->SpellIconID == 1985)
- {
- if (!procSpell)
- return false;
- switch (GetFirstSchoolInMask(procSpell->GetSchoolMask()))
- {
- case SPELL_SCHOOL_NORMAL:
- return false; // ignore
- case SPELL_SCHOOL_HOLY: trigger_spell_id = 54370; break;
- case SPELL_SCHOOL_FIRE: trigger_spell_id = 54371; break;
- case SPELL_SCHOOL_NATURE: trigger_spell_id = 54375; break;
- case SPELL_SCHOOL_FROST: trigger_spell_id = 54372; break;
- case SPELL_SCHOOL_SHADOW: trigger_spell_id = 54374; break;
- case SPELL_SCHOOL_ARCANE: trigger_spell_id = 54373; break;
- default:
- return false;
- }
- }
- break;
- }
- case SPELLFAMILY_PRIEST:
- {
- // Blessed Recovery
- if (auraSpellInfo->SpellIconID == 1875)
- {
- switch (auraSpellInfo->Id)
- {
- case 27811: trigger_spell_id = 27813; break;
- case 27815: trigger_spell_id = 27817; break;
- case 27816: trigger_spell_id = 27818; break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u not handled in BR", auraSpellInfo->Id);
- return false;
- }
- basepoints0 = CalculatePct(int32(damage), triggerAmount) / 3;
- target = this;
- // Add remaining ticks to healing done
- basepoints0 += GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_HEAL);
- }
- break;
- }
- case SPELLFAMILY_DRUID:
- {
- switch (auraSpellInfo->Id)
- {
- // Druid Forms Trinket
- case 37336:
- {
- switch (GetShapeshiftForm())
- {
- case FORM_NONE: trigger_spell_id = 37344; break;
- case FORM_CAT: trigger_spell_id = 37341; break;
- case FORM_BEAR:
- case FORM_DIREBEAR: trigger_spell_id = 37340; break;
- case FORM_TREE: trigger_spell_id = 37342; break;
- case FORM_MOONKIN: trigger_spell_id = 37343; break;
- default:
- return false;
- }
- break;
- }
- // Druid T9 Feral Relic (Lacerate, Swipe, Mangle, and Shred)
- case 67353:
- {
- switch (GetShapeshiftForm())
- {
- case FORM_CAT: trigger_spell_id = 67355; break;
- case FORM_BEAR:
- case FORM_DIREBEAR: trigger_spell_id = 67354; break;
- default:
- return false;
- }
- break;
- }
- default:
- break;
- }
- break;
- }
- case SPELLFAMILY_HUNTER:
- {
- if (auraSpellInfo->SpellIconID == 3247) // Piercing Shots
- {
- switch (auraSpellInfo->Id)
- {
- case 53234: // Rank 1
- case 53237: // Rank 2
- case 53238: // Rank 3
- trigger_spell_id = 63468;
- break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Piercing Shots", auraSpellInfo->Id);
- return false;
- }
- SpellInfo const* TriggerPS = sSpellMgr->GetSpellInfo(trigger_spell_id);
- if (!TriggerPS)
- return false;
-
- basepoints0 = CalculatePct(int32(damage), triggerAmount) / (TriggerPS->GetMaxDuration() / TriggerPS->Effects[0].Amplitude);
- basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_DAMAGE);
- break;
- }
- // Item - Hunter T9 4P Bonus
- if (auraSpellInfo->Id == 67151)
- {
- trigger_spell_id = 68130;
- target = this;
- break;
- }
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- switch (auraSpellInfo->Id)
- {
- // Soul Preserver
- case 60510:
- {
- switch (getClass())
- {
- case CLASS_DRUID:
- trigger_spell_id = 60512;
- break;
- case CLASS_PALADIN:
- trigger_spell_id = 60513;
- break;
- case CLASS_PRIEST:
- trigger_spell_id = 60514;
- break;
- case CLASS_SHAMAN:
- trigger_spell_id = 60515;
- break;
- }
-
- target = this;
- break;
- }
- case 37657: // Lightning Capacitor
- case 54841: // Thunder Capacitor
- case 67712: // Item - Coliseum 25 Normal Caster Trinket
- case 67758: // Item - Coliseum 25 Heroic Caster Trinket
- {
- if (!victim || !victim->IsAlive() || GetTypeId() != TYPEID_PLAYER)
- return false;
-
- uint32 stack_spell_id = 0;
- switch (auraSpellInfo->Id)
- {
- case 37657:
- stack_spell_id = 37658;
- trigger_spell_id = 37661;
- break;
- case 54841:
- stack_spell_id = 54842;
- trigger_spell_id = 54843;
- break;
- case 67712:
- stack_spell_id = 67713;
- trigger_spell_id = 67714;
- break;
- case 67758:
- stack_spell_id = 67759;
- trigger_spell_id = 67760;
- break;
- }
-
- CastSpell(this, stack_spell_id, true, NULL, triggeredByAura);
-
- Aura* dummy = GetAura(stack_spell_id);
- if (!dummy || dummy->GetStackAmount() < triggerAmount)
- return false;
-
- RemoveAurasDueToSpell(stack_spell_id);
- target = victim;
- break;
- }
- default:
- // Illumination
- if (auraSpellInfo->SpellIconID == 241)
- {
- if (!procSpell)
- return false;
- // procspell is triggered spell but we need mana cost of original cast spell
- uint32 originalSpellId = procSpell->Id;
- // Holy Shock heal
- if (procSpell->SpellFamilyFlags[1] & 0x00010000)
- {
- switch (procSpell->Id)
- {
- case 25914: originalSpellId = 20473; break;
- case 25913: originalSpellId = 20929; break;
- case 25903: originalSpellId = 20930; break;
- case 27175: originalSpellId = 27174; break;
- case 33074: originalSpellId = 33072; break;
- case 48820: originalSpellId = 48824; break;
- case 48821: originalSpellId = 48825; break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u not handled in HShock", procSpell->Id);
- return false;
- }
- }
- SpellInfo const* originalSpell = sSpellMgr->GetSpellInfo(originalSpellId);
- if (!originalSpell)
- {
- TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu", originalSpellId);
- return false;
- }
- // percent stored in effect 1 (class scripts) base points
- int32 cost = int32(originalSpell->ManaCost + CalculatePct(GetCreateMana(), originalSpell->ManaCostPercentage));
- basepoints0 = CalculatePct(cost, auraSpellInfo->Effects[1].CalcValue());
- trigger_spell_id = 20272;
- target = this;
- }
- break;
- }
- break;
- }
- case SPELLFAMILY_SHAMAN:
- {
- switch (auraSpellInfo->Id)
- {
- case 30881: // Nature's Guardian Rank 1
- case 30883: // Nature's Guardian Rank 2
- case 30884: // Nature's Guardian Rank 3
- case 30885: // Nature's Guardian Rank 4
- case 30886: // Nature's Guardian Rank 5
- {
- if (HealthBelowPct(30))
- {
- basepoints0 = int32(auraSpellInfo->Effects[EFFECT_0].CalcValue() * GetMaxHealth() / 100.0f);
- target = this;
- trigger_spell_id = 31616;
- /// @todo Threat part
- }
- else
- return false;
- break;
- }
- default:
- {
- // Lightning Shield (overwrite non existing triggered spell call in spell.dbc
- if (auraSpellInfo->SpellFamilyFlags[0] & 0x400)
- {
- trigger_spell_id = sSpellMgr->GetSpellWithRank(26364, auraSpellInfo->GetRank());
- }
- // Nature's Guardian
- else if (auraSpellInfo->SpellIconID == 2013)
- {
- // Check health condition - should drop to less 30% (damage deal after this!)
- if (!HealthBelowPctDamaged(30, damage))
- return false;
-
- if (victim && victim->IsAlive())
- victim->getThreatManager().modifyThreatPercent(this, -10);
-
- basepoints0 = int32(CountPctFromMaxHealth(triggerAmount));
- trigger_spell_id = 31616;
- target = this;
- }
- }
- }
- break;
- }
- case SPELLFAMILY_DEATHKNIGHT:
- {
- // Acclimation
- if (auraSpellInfo->SpellIconID == 1930)
- {
- if (!procSpell)
- return false;
- switch (GetFirstSchoolInMask(procSpell->GetSchoolMask()))
- {
- case SPELL_SCHOOL_NORMAL:
- return false; // ignore
- case SPELL_SCHOOL_HOLY: trigger_spell_id = 50490; break;
- case SPELL_SCHOOL_FIRE: trigger_spell_id = 50362; break;
- case SPELL_SCHOOL_NATURE: trigger_spell_id = 50488; break;
- case SPELL_SCHOOL_FROST: trigger_spell_id = 50485; break;
- case SPELL_SCHOOL_SHADOW: trigger_spell_id = 50489; break;
- case SPELL_SCHOOL_ARCANE: trigger_spell_id = 50486; break;
- default:
- return false;
- }
- }
- // Blood Presence (Improved)
- else if (auraSpellInfo->Id == 63611)
- {
- if (GetTypeId() != TYPEID_PLAYER)
- return false;
-
- trigger_spell_id = 50475;
- basepoints0 = CalculatePct(int32(damage), triggerAmount);
- }
- // Item - Death Knight T10 Melee 4P Bonus
- else if (auraSpellInfo->Id == 70656)
- {
- if (GetTypeId() != TYPEID_PLAYER || getClass() != CLASS_DEATH_KNIGHT)
- return false;
-
- for (uint8 i = 0; i < MAX_RUNES; ++i)
- if (ToPlayer()->GetRuneCooldown(i) == 0)
- return false;
- }
- break;
- }
- case SPELLFAMILY_ROGUE:
- {
- switch (auraSpellInfo->Id)
- {
- // Rogue T10 2P bonus, should only proc on caster
- case 70805:
- {
- if (victim != this)
- return false;
- break;
- }
- }
- break;
}
default:
break;
@@ -8254,7 +7795,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
float averageDmg = 0;
// now compute approximate weapon damage by formula from wowwiki.com
- if (procFlags & PROC_FLAG_DONE_OFFHAND_ATTACK)
+ if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK)
averageDmg = (GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f;
else
averageDmg = (GetFloatValue(UNIT_FIELD_MINDAMAGE) + GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f;
@@ -8331,7 +7872,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 53232:
{
// This effect only from Rapid Fire (ability cast)
- if (!(procSpell->SpellFamilyFlags[0] & 0x20))
+ if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x20))
return false;
break;
}
@@ -8349,7 +7890,9 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 15337: // Improved Spirit Tap (Rank 1)
case 15338: // Improved Spirit Tap (Rank 2)
{
- ASSERT(procSpell);
+ if (!procSpell)
+ return false;
+
if (procSpell->SpellFamilyFlags[0] & 0x800000)
if ((procSpell->Id != 58381) || !roll_chance_i(50))
return false;
@@ -8416,13 +7959,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
if (!target)
return false;
- if (cooldown && target->GetTypeId() == TYPEID_PLAYER && target->GetSpellHistory()->HasCooldown(trigger_spell_id))
- return false;
-
target->CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown));
return true;
}
// Cast positive spell on enemy target
@@ -8531,7 +8068,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
// Astral Shift
case 52179:
{
- if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim)
+ if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim)
return false;
// Need stun, fear or silence mechanic
@@ -8553,7 +8090,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 56453:
{
// Proc only from Frost/Freezing trap activation or from Freezing Arrow (the periodic dmg proc handled elsewhere)
- if (!(procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION) || !procSpell || !(procSpell->SchoolMask & SPELL_SCHOOL_MASK_FROST) || !roll_chance_i(triggerAmount))
+ if (!(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION) || !procSpell || !(procSpell->SchoolMask & SPELL_SCHOOL_MASK_FROST) || !roll_chance_i(triggerAmount))
return false;
break;
}
@@ -8583,13 +8120,15 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 65081:
{
// Proc only from PW:S cast
- if (!(procSpell->SpellFamilyFlags[0] & 0x00000001))
+ if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x00000001))
return false;
break;
}
// Culling the Herd
case 70893:
{
+ if (!procSpell)
+ return false;
// check if we're doing a critical hit
if (!(procSpell->SpellFamilyFlags[1] & 0x10000000) && (procEx != PROC_EX_CRITICAL_HIT))
return false;
@@ -8600,29 +8139,23 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
}
}
- if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(trigger_spell_id))
- return false;
-
// extra attack should hit same target
if (triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
target = victim;
// try detect target manually if not set
if (target == NULL)
- target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim;
+ target = !(procFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim;
if (basepoints0)
CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura);
else
CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura);
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown));
-
return true;
}
-bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown)
+bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell)
{
int32 scriptId = triggeredByAura->GetMiscValue();
@@ -8713,14 +8246,7 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, Au
return false;
}
- if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(triggered_spell_id))
- return false;
-
CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- GetSpellHistory()->AddCooldown(triggered_spell_id, 0, std::chrono::seconds(cooldown));
-
return true;
}
@@ -9073,6 +8599,9 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
ToCreature()->SendAIReaction(AI_REACTION_HOSTILE);
ToCreature()->CallAssistance();
+
+ // Remove emote state - will be restored on creature reset
+ SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
}
// delay offhand weapon attack to next attack time
@@ -10739,6 +10268,19 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto
if (Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
+ // for this types the bonus was already added in GetUnitCriticalChance, do not add twice
+ if (spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellProto->DmgClass != SPELL_DAMAGE_CLASS_RANGED)
+ {
+ AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER);
+ for (AuraEffect const* aurEff : critChanceForCaster)
+ {
+ if (aurEff->GetCasterGUID() != GetGUID() || !aurEff->IsAffectedOnSpell(spellProto))
+ continue;
+
+ crit_chance += aurEff->GetAmount();
+ }
+ }
+
return crit_chance > 0.0f ? crit_chance : 0.0f;
}
@@ -11874,10 +11416,8 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
if (enemy)
{
if (IsAIEnabled)
- {
creature->AI()->EnterCombat(enemy);
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // unit has engaged in combat, remove immunity so players can fight back
- }
+
if (creature->GetFormation())
creature->GetFormation()->MemberAttackStart(creature, enemy);
}
@@ -11889,7 +11429,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
UpdateSpeed(MOVE_FLIGHT);
}
- if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_MOUNTED_COMBAT))
+ if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED))
Dismount();
}
@@ -11918,9 +11458,6 @@ void Unit::ClearInCombat()
// Player's state will be cleared in Player::UpdateContestedPvP
if (Creature* creature = ToCreature())
{
- if (creature->GetCreatureTemplate() && creature->GetCreatureTemplate()->unit_flags & UNIT_FLAG_IMMUNE_TO_PC)
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // set immunity state to the one from db on evade
-
ClearUnitState(UNIT_STATE_ATTACK_PLAYER);
if (HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED))
SetUInt32Value(UNIT_DYNAMIC_FLAGS, creature->GetCreatureTemplate()->dynamicflags);
@@ -11948,7 +11485,7 @@ bool Unit::isTargetableForAttack(bool checkFakeDeath) const
return false;
if (HasFlag(UNIT_FIELD_FLAGS,
- UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC))
+ UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
return false;
if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->IsGameMaster())
@@ -12042,7 +11579,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
}
Creature const* creatureAttacker = ToCreature();
- if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)
+ if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)
return false;
// check duel - before sanctuary checks
@@ -12126,11 +11663,14 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
// can't assist non-friendly targets
if (GetReactionTo(target) < REP_NEUTRAL
&& target->GetReactionTo(this) < REP_NEUTRAL
- && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)))
+ && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)))
return false;
+ // Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction)
+ if (GetTypeId() == TYPEID_PLAYER && IsCharmed() && GetCharmerGUID().IsCreature())
+ return true;
// PvP case
- if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
+ else if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
{
Player const* targetPlayerOwner = target->GetAffectingPlayer();
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
@@ -12160,7 +11700,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
&& !((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP)))
{
if (Creature const* creatureTarget = target->ToCreature())
- return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS;
+ return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST;
}
return true;
}
@@ -12511,7 +12051,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
pet->SetSpeedRate(mtype, m_speed_rate[mtype]);
}
- if (m_movedPlayer) // unit controlled by a player.
+ if (Player* playerMover = GetPlayerMover()) // unit controlled by a player.
{
// Send notification to self. this packet is only sent to one client (the client of the player concerned by the change).
WorldPacket self;
@@ -12521,7 +12061,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
if (mtype == MOVE_RUN)
self << uint8(1); // unknown byte added in 2.1.0
self << float(GetSpeed(mtype));
- m_movedPlayer->GetSession()->SendPacket(&self);
+ playerMover->GetSession()->SendPacket(&self);
// Send notification to other players. sent to every clients (if in range) except one: the client of the player concerned by the change.
WorldPacket data;
@@ -12529,7 +12069,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
data << GetPackGUID();
BuildMovementPacket(&data);
data << float(GetSpeed(mtype));
- SendMessageToSet(&data, false);
+ playerMover->SendMessageToSet(&data, false);
}
else // unit controlled by AI.
{
@@ -12649,7 +12189,7 @@ float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask)
void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell)
{
// Only mobs can manage threat lists
- if (CanHaveThreatList())
+ if (CanHaveThreatList() && !HasUnitState(UNIT_STATE_EVADE))
m_ThreatManager.addThreat(victim, fThreat, schoolMask, threatSpell);
}
@@ -13757,6 +13297,7 @@ void Unit::UpdateCharmAI()
if (!newAI) // otherwise, we default to the generic one
newAI = new SimpleCharmedPlayerAI(ToPlayer());
i_AI = newAI;
+ newAI->OnCharmed(true);
}
else
{
@@ -14054,6 +13595,20 @@ void CharmInfo::SetSpellAutocast(SpellInfo const* spellInfo, bool state)
}
}
+Unit* Unit::GetMover() const
+{
+ if (Player const* player = ToPlayer())
+ return player->m_mover;
+ return nullptr;
+}
+
+Player* Unit::GetPlayerMover() const
+{
+ if (Unit* mover = GetMover())
+ return mover->ToPlayer();
+ return nullptr;
+}
+
bool Unit::isFrozen() const
{
return HasAuraState(AURA_STATE_FROZEN);
@@ -14254,6 +13809,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL);
ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo);
+ std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
ProcTriggeredList procTriggered;
// Fill procTriggered list
for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr)
@@ -14261,6 +13817,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
// Do not allow auras to proc from effect triggered by itself
if (procAura && procAura->Id == itr->first)
continue;
+
+ if (itr->second->GetBase()->IsProcOnCooldown(now))
+ continue;
+
ProcTriggeredData triggerData(itr->second->GetBase());
// Defensive procs are active on absorbs (so absorption effects are not a hindrance)
bool active = damage || (procExtra & PROC_EX_BLOCK && isVictim);
@@ -14285,6 +13845,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo))
continue;
+ bool procSuccess = RollProcResult(target, triggerData.aura, attType, isVictim, triggerData.spellProcEvent);
+ if (!procSuccess)
+ continue;
+
// Triggered spells not triggering additional spells
bool triggered = !spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ?
(procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false;
@@ -14337,10 +13901,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo);
- // For players set spell cooldown if need
- uint32 cooldown = 0;
- if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown)
- cooldown = i->spellProcEvent->cooldown;
+ Milliseconds cooldown = Milliseconds::zero();
+ if (prepare && i->spellProcEvent && i->spellProcEvent->cooldown)
+ cooldown = Seconds(i->spellProcEvent->cooldown);
// Note: must SetCantProc(false) before return
if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC))
@@ -14349,9 +13912,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo);
// "handled" is needed as long as proc can be handled in multiple places
- if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled))
+ if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, &handled))
{
- TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), Id);
+ TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), Id);
takeCharges = true;
}
@@ -14359,7 +13922,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
{
for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
{
- if (!(i->effMask & (1<<effIndex)))
+ if (!(i->effMask & (1 << effIndex)))
continue;
AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex);
@@ -14376,9 +13939,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
{
case SPELL_AURA_PROC_TRIGGER_SPELL:
{
- TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)",
+ spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
// Don`t drop charge or add cooldown for not started trigger
- if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
+ if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra))
takeCharges = true;
break;
}
@@ -14395,7 +13959,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
case SPELL_AURA_MANA_SHIELD:
case SPELL_AURA_DUMMY:
{
- TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)",
+ spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
takeCharges = true;
break;
@@ -14404,20 +13969,22 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
case SPELL_AURA_MOD_MELEE_HASTE:
- TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId());
+ TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)",
+ spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId());
takeCharges = true;
break;
case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
{
- TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
- if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell, cooldown))
+ TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)",
+ spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
+ if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell))
takeCharges = true;
break;
}
case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE:
{
TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)",
- (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
HandleAuraRaidProcFromChargeWithValue(triggeredByAura);
takeCharges = true;
@@ -14426,7 +13993,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
case SPELL_AURA_RAID_PROC_FROM_CHARGE:
{
TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)",
- (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
HandleAuraRaidProcFromCharge(triggeredByAura);
takeCharges = true;
@@ -14434,9 +14001,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
}
case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE:
{
- TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)",
+ spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId());
- if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
+ if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra))
takeCharges = true;
break;
}
@@ -14521,6 +14089,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
} // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
} // if (!handled)
+ if (prepare && takeCharges && cooldown != Milliseconds::zero())
+ i->aura->AddProcCooldown(now + cooldown);
+
// Remove charge (aura can be removed by triggers)
if (prepare && useCharges && takeCharges)
{
@@ -14549,6 +14120,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo)
{
+ std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
+
// use provided list of auras which can proc
if (procAuras)
{
@@ -14556,9 +14129,9 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge
{
ASSERT((*itr)->GetTarget() == this);
if (!(*itr)->GetRemoveMode())
- if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo))
+ if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo, now))
{
- (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo);
+ (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo, now);
aurasTriggeringProc.push_back(*itr);
}
}
@@ -14568,9 +14141,9 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge
{
for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr)
{
- if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo))
+ if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now))
{
- itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo);
+ itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now);
aurasTriggeringProc.push_back(itr->second);
}
}
@@ -15192,23 +14765,23 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
return true;
}
-bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent)
+bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent)
{
- SpellInfo const* spellProto = aura->GetSpellInfo();
+ SpellInfo const* spellInfo = aura->GetSpellInfo();
// let the aura be handled by new proc system if it has new entry
- if (sSpellMgr->GetSpellProcEntry(spellProto->Id))
+ if (sSpellMgr->GetSpellProcEntry(spellInfo->Id))
return false;
// Get proc Event Entry
- spellProcEvent = sSpellMgr->GetSpellProcEvent(spellProto->Id);
+ spellProcEvent = sSpellMgr->GetSpellProcEvent(spellInfo->Id);
// Get EventProcFlag
uint32 EventProcFlag;
if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags
EventProcFlag = spellProcEvent->procFlags;
else
- EventProcFlag = spellProto->ProcFlags; // else get from spell proto
+ EventProcFlag = spellInfo->ProcFlags; // else get from spell proto
// Continue if no trigger exist
if (!EventProcFlag)
return false;
@@ -15216,12 +14789,12 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const
// Additional checks for triggered spells (ignore trap casts)
if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION))
{
- if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED))
+ if (!spellInfo->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED))
return false;
}
// Check spellProcEvent data requirements
- if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellProto, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active))
+ if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellInfo, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active))
return false;
// In most cases req get honor or XP from kill
if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER)
@@ -15239,15 +14812,15 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const
}
// Aura added by spell can`t trigger from self (prevent drop charges/do triggers)
// But except periodic and kill triggers (can triggered from self)
- if (procSpell && procSpell->Id == spellProto->Id
- && !(spellProto->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL)))
+ if (procSpell && procSpell->Id == spellInfo->Id
+ && !(spellInfo->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL)))
return false;
// Check if current equipment allows aura to proc
if (!isVictim && GetTypeId() == TYPEID_PLAYER)
{
Player* player = ToPlayer();
- if (spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
+ if (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON)
{
Item* item = NULL;
if (attType == BASE_ATTACK)
@@ -15260,19 +14833,26 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const
if (player->IsInFeralForm())
return false;
- if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellProto->EquippedItemSubClassMask))
+ if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask))
return false;
}
- else if (spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
+ else if (spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR)
{
// Check if player is wearing shield
Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
- if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellProto->EquippedItemSubClassMask))
+ if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask))
return false;
}
}
+
+ return true;
+}
+
+bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent)
+{
+ SpellInfo const* spellInfo = aura->GetSpellInfo();
// Get chance from spell
- float chance = float(spellProto->ProcChance);
+ float chance = float(spellInfo->ProcChance);
// If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance;
if (spellProcEvent && spellProcEvent->customChance)
chance = spellProcEvent->customChance;
@@ -15282,19 +14862,18 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const
if (!isVictim)
{
uint32 weaponSpeed = GetAttackTime(attType);
- chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto);
+ chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo);
}
else if (victim)
{
uint32 weaponSpeed = victim->GetAttackTime(attType);
- chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto);
+ chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo);
}
}
// Apply chance modifer aura
if (Player* modOwner = GetSpellModOwner())
- {
- modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance);
- }
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance);
+
return roll_chance_f(chance);
}
@@ -16025,7 +15604,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
{
// change AI to charmed AI on next Update tick
NeedChangeAI = true;
- IsAIEnabled = false;
+ if (IsAIEnabled)
+ {
+ IsAIEnabled = false;
+ player->AI()->OnCharmed(true);
+ }
}
player->SetClientControl(this, false);
}
@@ -16057,7 +15640,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
case CHARM_TYPE_POSSESS:
AddUnitState(UNIT_STATE_POSSESSED);
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
- charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
playerCharmer->SetClientControl(this, true);
playerCharmer->PossessSpellInitialize();
break;
@@ -16160,7 +15743,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
case CHARM_TYPE_POSSESS:
playerCharmer->SetClientControl(this, false);
playerCharmer->SetClientControl(charmer, true);
- charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
ClearUnitState(UNIT_STATE_POSSESSED);
break;
@@ -16300,8 +15883,8 @@ bool Unit::IsInPartyWith(Unit const* unit) const
if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER)
return u1->ToPlayer()->IsInSameGroupWith(u2->ToPlayer());
- else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) ||
- (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER))
+ else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) ||
+ (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))
return true;
else
return false;
@@ -16319,8 +15902,8 @@ bool Unit::IsInRaidWith(Unit const* unit) const
if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER)
return u1->ToPlayer()->IsInSameRaidWith(u2->ToPlayer());
- else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) ||
- (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER))
+ else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) ||
+ (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))
return true;
else
return false;
@@ -17202,7 +16785,7 @@ void Unit::_ExitVehicle(Position const* exitPosition)
Movement::MoveSplineInit init(this);
// Creatures without inhabit type air should begin falling after exiting the vehicle
- if (GetTypeId() == TYPEID_UNIT && !CanFly() && height > GetMap()->GetWaterOrGroundLevel(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), &height) + 0.1f)
+ if (GetTypeId() == TYPEID_UNIT && !CanFly() && height > GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), &height) + 0.1f)
init.SetFall();
init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), height, false);
@@ -17679,7 +17262,6 @@ bool Unit::SetWalk(bool enable)
AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
-
return true;
}
@@ -17694,15 +17276,7 @@ bool Unit::SetDisableGravity(bool disable, bool /*packetOnly = false*/)
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
}
else
- {
RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
- if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
- {
- m_movementInfo.SetFallTime(0);
- AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
- }
- }
-
return true;
}
@@ -17715,7 +17289,6 @@ bool Unit::SetSwim(bool enable)
AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
-
return true;
}
@@ -17730,15 +17303,7 @@ bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */)
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
}
else
- {
RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_MASK_MOVING_FLY);
- if (!IsLevitating())
- {
- m_movementInfo.SetFallTime(0);
- AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
- }
- }
-
return true;
}
@@ -17751,7 +17316,6 @@ bool Unit::SetWaterWalking(bool enable, bool /*packetOnly = false */)
AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
-
return true;
}
@@ -17764,7 +17328,6 @@ bool Unit::SetFeatherFall(bool enable, bool /*packetOnly = false */)
AddUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
-
return true;
}
@@ -17792,7 +17355,6 @@ bool Unit::SetHover(bool enable, bool /*packetOnly = false*/)
UpdateHeight(newZ);
}
}
-
return true;
}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index d49d2fd9842..0f48f31b9c0 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -615,7 +615,7 @@ enum UnitFlags : uint32
{
UNIT_FLAG_SERVER_CONTROLLED = 0x00000001, // set only when unit movement is controlled by server - by SPLINE/MONSTER_MOVE packets, together with UNIT_FLAG_STUNNED; only set to units controlled by client; client function CGUnit_C::IsClientControlled returns false when set for owner
UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable
- UNIT_FLAG_DISABLE_MOVE = 0x00000004,
+ UNIT_FLAG_REMOVE_CLIENT_CONTROL = 0x00000004, // This is a legacy flag used to disable movement player's movement while controlling other units, SMSG_CLIENT_CONTROL replaces this functionality clientside now. CONFUSED and FLEEING flags have the same effect on client movement asDISABLE_MOVE_CONTROL in addition to preventing spell casts/autoattack (they all allow climbing steeper hills and emotes while moving)
UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state
UNIT_FLAG_RENAME = 0x00000010,
UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP
@@ -627,7 +627,7 @@ enum UnitFlags : uint32
UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3
UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
- UNIT_FLAG_UNK_14 = 0x00004000, // 2.0.8
+ UNIT_FLAG_CANNOT_SWIM = 0x00004000, // 2.0.8
UNIT_FLAG_UNK_15 = 0x00008000,
UNIT_FLAG_UNK_16 = 0x00010000,
UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok
@@ -739,14 +739,13 @@ enum MovementFlags
MOVEMENTFLAG_FALLING_SLOW = 0x20000000, // active rogue safe fall spell (passive)
MOVEMENTFLAG_HOVER = 0x40000000, // hover, cannot jump
- /// @todo Check if PITCH_UP and PITCH_DOWN really belong here..
MOVEMENTFLAG_MASK_MOVING =
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT |
- MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN | MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR | MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING |
+ MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR | MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING |
MOVEMENTFLAG_SPLINE_ELEVATION,
MOVEMENTFLAG_MASK_TURNING =
- MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT,
+ MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN,
MOVEMENTFLAG_MASK_MOVING_FLY =
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING,
@@ -1290,9 +1289,8 @@ 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 GetMeleeReach() const;
bool IsWithinCombatRange(const Unit* obj, float dist2compare) const;
- bool IsWithinMeleeRange(const Unit* obj, float dist = MELEE_RANGE) const;
+ bool IsWithinMeleeRange(Unit const* obj) const;
void GetRandomContactPoint(const Unit* target, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const;
uint32 m_extraAttacks;
bool m_canDualWield;
@@ -1685,7 +1683,8 @@ class TC_GAME_API Unit : public WorldObject
CharmInfo* InitCharmInfo();
void DeleteCharmInfo();
void UpdateCharmAI();
- //Player* GetMoverSource() const;
+ Unit* GetMover() const;
+ Player* GetPlayerMover() const;
Player* m_movedPlayer;
SharedVisionList const& GetSharedVisionList() { return m_sharedVision; }
void AddPlayerToVision(Player* player);
@@ -1858,6 +1857,9 @@ class TC_GAME_API Unit : public WorldObject
Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
int32 GetCurrentSpellCastTime(uint32 spell_id) const;
+ // Check if our current channel spell has attribute SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING
+ bool CanMoveDuringChannel() const;
+
SpellHistory* GetSpellHistory() { return m_spellHistory; }
SpellHistory const* GetSpellHistory() const { return m_spellHistory; }
@@ -2252,11 +2254,12 @@ class TC_GAME_API Unit : public WorldObject
void DisableSpline();
private:
- bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent);
- bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
- bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled);
- bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
- bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown);
+ bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent);
+ bool RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent);
+ bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown);
+ bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, bool* handled);
+ bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx);
+ bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell);
bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura);
bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura);
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index 9cf485322f2..1c5299f4798 100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -238,7 +238,7 @@ void Vehicle::RemoveAllPassengers()
while (!_pendingJoinEvents.empty())
{
VehicleJoinEvent* e = _pendingJoinEvents.front();
- e->to_Abort = true;
+ e->ScheduleAbort();
e->Target = eventVehicle;
_pendingJoinEvents.pop_front();
}
@@ -429,7 +429,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
if (seat == Seats.end()) // no available seat
{
- e->to_Abort = true;
+ e->ScheduleAbort();
return false;
}
@@ -441,7 +441,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
seat = Seats.find(seatId);
if (seat == Seats.end())
{
- e->to_Abort = true;
+ e->ScheduleAbort();
return false;
}
@@ -532,6 +532,9 @@ void Vehicle::RelocatePassengers()
{
ASSERT(_me->GetMap());
+ std::vector<std::pair<Unit*, Position>> seatRelocation;
+ seatRelocation.reserve(Seats.size());
+
// not sure that absolute position calculation is correct, it must depend on vehicle pitch angle
for (SeatMap::const_iterator itr = Seats.begin(); itr != Seats.end(); ++itr)
{
@@ -542,9 +545,12 @@ void Vehicle::RelocatePassengers()
float px, py, pz, po;
passenger->m_movementInfo.transport.pos.GetPosition(px, py, pz, po);
CalculatePassengerPosition(px, py, pz, &po);
- passenger->UpdatePosition(px, py, pz, po);
+ seatRelocation.emplace_back(passenger, Position(px, py, pz, po));
}
}
+
+ for (auto const& pair : seatRelocation)
+ pair.first->UpdatePosition(pair.second);
}
/**
@@ -701,7 +707,7 @@ void Vehicle::RemovePendingEventsForSeat(int8 seatId)
{
if ((*itr)->Seat->first == seatId)
{
- (*itr)->to_Abort = true;
+ (*itr)->ScheduleAbort();
_pendingJoinEvents.erase(itr++);
}
else
@@ -726,7 +732,7 @@ void Vehicle::RemovePendingEventsForPassenger(Unit* passenger)
{
if ((*itr)->Passenger == passenger)
{
- (*itr)->to_Abort = true;
+ (*itr)->ScheduleAbort();
_pendingJoinEvents.erase(itr++);
}
else
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index fc0abf5a8c7..bbd0cee2a51 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -473,7 +473,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
creatureTemplate.unit_flags = fields[29].GetUInt32();
creatureTemplate.unit_flags2 = fields[30].GetUInt32();
creatureTemplate.dynamicflags = fields[31].GetUInt32();
- creatureTemplate.family = fields[32].GetUInt8();
+ creatureTemplate.family = CreatureFamily(fields[32].GetUInt8());
creatureTemplate.trainer_type = fields[33].GetUInt8();
creatureTemplate.trainer_spell = fields[34].GetUInt32();
creatureTemplate.trainer_class = fields[35].GetUInt8();
@@ -888,7 +888,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
{
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family);
- const_cast<CreatureTemplate*>(cInfo)->family = 0;
+ const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE;
}
if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
@@ -1050,8 +1050,8 @@ void ObjectMgr::LoadGameObjectAddons()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2
- QueryResult result = WorldDatabase.Query("SELECT guid, invisibilityType, invisibilityValue FROM gameobject_addon");
+ // 0 1 2 3 4 5 6
+ QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue FROM gameobject_addon");
if (!result)
{
@@ -1066,7 +1066,7 @@ void ObjectMgr::LoadGameObjectAddons()
ObjectGuid::LowType guid = fields[0].GetUInt32();
- const GameObjectData* goData = GetGOData(guid);
+ GameObjectData const* goData = GetGOData(guid);
if (!goData)
{
TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid);
@@ -1074,12 +1074,13 @@ void ObjectMgr::LoadGameObjectAddons()
}
GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid];
- gameObjectAddon.invisibilityType = InvisibilityType(fields[1].GetUInt8());
- gameObjectAddon.InvisibilityValue = fields[2].GetUInt32();
+ gameObjectAddon.ParentRotation = G3D::Quat(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
+ gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8());
+ gameObjectAddon.InvisibilityValue = fields[6].GetUInt32();
if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES)
{
- TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`", guid);
+ TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid);
gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL;
gameObjectAddon.InvisibilityValue = 0;
}
@@ -1090,6 +1091,12 @@ void ObjectMgr::LoadGameObjectAddons()
gameObjectAddon.InvisibilityValue = 1;
}
+ if (!gameObjectAddon.ParentRotation.isUnit())
+ {
+ TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid path rotation, set to default", guid);
+ gameObjectAddon.ParentRotation = G3D::Quat();
+ }
+
++count;
}
while (result->NextRow());
@@ -1883,10 +1890,10 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl
data.posY = y;
data.posZ = z;
data.orientation = o;
- data.rotation0 = rotation0;
- data.rotation1 = rotation1;
- data.rotation2 = rotation2;
- data.rotation3 = rotation3;
+ data.rotation.x = rotation0;
+ data.rotation.y = rotation1;
+ data.rotation.z = rotation2;
+ data.rotation.w = rotation3;
data.spawntimesecs = spawntimedelay;
data.animprogress = 100;
data.spawnMask = 1;
@@ -2036,10 +2043,10 @@ void ObjectMgr::LoadGameobjects()
data.posY = fields[4].GetFloat();
data.posZ = fields[5].GetFloat();
data.orientation = fields[6].GetFloat();
- data.rotation0 = fields[7].GetFloat();
- data.rotation1 = fields[8].GetFloat();
- data.rotation2 = fields[9].GetFloat();
- data.rotation3 = fields[10].GetFloat();
+ data.rotation.x = fields[7].GetFloat();
+ data.rotation.y = fields[8].GetFloat();
+ data.rotation.z = fields[9].GetFloat();
+ data.rotation.w = fields[10].GetFloat();
data.spawntimesecs = fields[11].GetInt32();
MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
@@ -2080,15 +2087,27 @@ void ObjectMgr::LoadGameobjects()
data.orientation = Position::NormalizeOrientation(data.orientation);
}
- if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
+ if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationX (%f) value, skip", guid, data.id, data.rotation.x);
+ continue;
+ }
+
+ if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationY (%f) value, skip", guid, data.id, data.rotation.y);
+ continue;
+ }
+
+ if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
{
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2);
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationZ (%f) value, skip", guid, data.id, data.rotation.z);
continue;
}
- if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
+ if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
{
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3);
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationW (%f) value, skip", guid, data.id, data.rotation.w);
continue;
}
@@ -2178,26 +2197,12 @@ ObjectGuid ObjectMgr::GetPlayerGUIDByName(std::string const& name) const
bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid, std::string& name) const
{
- // prevent DB access for online player
- if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
- {
- name = player->GetName();
- return true;
- }
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME);
-
- stmt->setUInt32(0, guid.GetCounter());
-
- PreparedQueryResult result = CharacterDatabase.Query(stmt);
-
- if (result)
- {
- name = (*result)[0].GetString();
- return true;
- }
+ CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(guid);
+ if (!characterInfo)
+ return false;
- return false;
+ name = characterInfo->Name;
+ return true;
}
uint32 ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid) const
@@ -3529,6 +3534,61 @@ void ObjectMgr::LoadPlayerInfo()
}
}
+ // Load playercreate cast spell
+ TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
+ {
+ uint32 oldMSTime = getMSTime();
+
+ QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell");
+
+ if (!result)
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
+ else
+ {
+ uint32 count = 0;
+
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 raceMask = fields[0].GetUInt32();
+ uint32 classMask = fields[1].GetUInt32();
+ uint32 spellId = fields[2].GetUInt32();
+
+ if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
+ {
+ TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_cast_spell` table, ignoring.", raceMask);
+ continue;
+ }
+
+ if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
+ {
+ TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_cast_spell` table, ignoring.", classMask);
+ continue;
+ }
+
+ for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
+ {
+ if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
+ {
+ for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
+ {
+ if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
+ {
+ if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
+ {
+ info->castSpells.push_back(spellId);
+ ++count;
+ }
+ }
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u player create cast spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ }
+ }
+
// Load playercreate actions
TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
{
@@ -4056,8 +4116,8 @@ void ObjectMgr::LoadQuests()
// Load `quest_template_addon`
// 0 1 2 3 4 5 6 7 8
result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay, "
- //9 10 11 12 13 14 15 16
- "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags FROM quest_template_addon");
+ //9 10 11 12 13 14 15 16 17
+ "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId");
if (!result)
{
@@ -4561,6 +4621,7 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId);
qinfo->RewardMailTemplateId = 0; // no mail will send to player
qinfo->RewardMailDelay = 0; // no mail will send to player
+ qinfo->RewardMailSenderEntry = 0;
}
else if (usedMailTemplates.find(qinfo->RewardMailTemplateId) != usedMailTemplates.end())
{
@@ -4569,6 +4630,7 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId, used_mt_itr->second);
qinfo->RewardMailTemplateId = 0; // no mail will send to player
qinfo->RewardMailDelay = 0; // no mail will send to player
+ qinfo->RewardMailSenderEntry = 0;
}
else
usedMailTemplates[qinfo->RewardMailTemplateId] = qinfo->GetQuestId();
@@ -5946,14 +6008,14 @@ void ObjectMgr::LoadGraveyardZones()
{
uint32 oldMSTime = getMSTime();
- GraveYardStore.clear(); // need for reload case
+ GraveYardStore.clear(); // need for reload case
- // 0 1 2
- QueryResult result = WorldDatabase.Query("SELECT id, ghost_zone, faction FROM game_graveyard_zone");
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone");
if (!result)
{
- TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `game_graveyard_zone` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
return;
}
@@ -5972,31 +6034,31 @@ void ObjectMgr::LoadGraveyardZones()
WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
if (!entry)
{
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: %u), skipped.", safeLocId);
continue;
}
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
if (!areaEntry)
{
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: %u), skipped.", zoneId);
continue;
}
if (areaEntry->zone != 0)
{
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for subzone id (%u) instead of zone, skipped.", zoneId);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for SubZone (ID: %u) instead of zone, skipped.", zoneId);
continue;
}
if (team != 0 && team != HORDE && team != ALLIANCE)
{
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for non player faction (%u), skipped.", team);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction (%u), skipped.", team);
continue;
}
if (!AddGraveYardLink(safeLocId, zoneId, team, false))
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
} while (result->NextRow());
TC_LOG_INFO("server.loading", ">> Loaded %u graveyard-zone links in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
@@ -6045,7 +6107,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float
if (range.first == range.second && !map->IsBattlegroundOrArena())
{
if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
return GetDefaultGraveYard(team);
}
@@ -6071,7 +6133,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float
WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
if (!entry)
{
- TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId);
+ TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID %u), skipped.", data.safeLocId);
continue;
}
@@ -6186,7 +6248,7 @@ void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool
GraveYardMapBoundsNonConst range = GraveYardStore.equal_range(zoneId);
if (range.first == range.second)
{
- //TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
+ //TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
return;
}
@@ -6494,7 +6556,7 @@ uint32 ObjectMgr::GenerateAuctionID()
{
if (_auctionId >= 0xFFFFFFFE)
{
- TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. ");
+ TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _auctionId++;
@@ -6504,7 +6566,7 @@ uint64 ObjectMgr::GenerateEquipmentSetGuid()
{
if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
{
- TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. ");
+ TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _equipmentSetGuid++;
@@ -6514,7 +6576,7 @@ uint32 ObjectMgr::GenerateMailID()
{
if (_mailId >= 0xFFFFFFFE)
{
- TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. ");
+ TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _mailId++;
@@ -6524,7 +6586,7 @@ uint32 ObjectMgr::GeneratePetNumber()
{
if (_hiPetNumber >= 0xFFFFFFFE)
{
- TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. ");
+ TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
World::StopNow(ERROR_EXIT_CODE);
}
return _hiPetNumber++;
@@ -6534,7 +6596,7 @@ uint32 ObjectMgr::GenerateCreatureSpawnId()
{
if (_creatureSpawnId >= uint32(0xFFFFFF))
{
- TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. ");
+ TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
World::StopNow(ERROR_EXIT_CODE);
}
return _creatureSpawnId++;
@@ -6544,7 +6606,7 @@ uint32 ObjectMgr::GenerateGameObjectSpawnId()
{
if (_gameObjectSpawnId >= uint32(0xFFFFFF))
{
- TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. ");
+ TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
World::StopNow(ERROR_EXIT_CODE);
}
return _gameObjectSpawnId++;
@@ -7639,7 +7701,7 @@ ResponseCodes ObjectMgr::CheckPlayerName(std::string const& name, LocaleConstant
if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
return CHAR_NAME_THREE_CONSECUTIVE;
- return ValidateName(name, locale);
+ return ValidateName(wname, locale);
}
bool ObjectMgr::IsValidCharterName(const std::string& name)
@@ -7677,7 +7739,7 @@ PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name, LocaleCons
if (!isValidString(wname, strictMask, false))
return PET_NAME_MIXED_LANGUAGES;
- switch (ValidateName(name, locale))
+ switch (ValidateName(wname, locale))
{
case CHAR_NAME_PROFANE:
return PET_NAME_PROFANE;
@@ -7763,7 +7825,7 @@ bool ObjectMgr::LoadTrinityStrings()
QueryResult result = WorldDatabase.Query("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8 FROM trinity_string");
if (!result)
{
- TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty.");
+ TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum.");
return false;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 576a8d2ccac..f06c9faf58b 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -28,7 +28,7 @@
#include "TemporarySummon.h"
#include "Corpse.h"
#include "QuestDef.h"
-#include "ItemPrototype.h"
+#include "ItemTemplate.h"
#include "NPCHandler.h"
#include "DatabaseEnv.h"
#include "Mail.h"
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 84aa29f96b7..5283805c59d 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -620,6 +620,9 @@ namespace Trinity
if (go->GetGOInfo()->spellFocus.focusId != i_focusId)
return false;
+ if (!go->isSpawned())
+ return false;
+
float dist = go->GetGOInfo()->spellFocus.dist / 2.f;
return go->IsWithinDistInMap(i_unit, dist);
@@ -1237,7 +1240,7 @@ namespace Trinity
AllGameObjectsWithEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { }
bool operator() (GameObject* go)
{
- if (go->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(go, m_fRange, false))
+ if ((!m_uiEntry || go->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(go, m_fRange, false))
return true;
return false;
@@ -1254,7 +1257,7 @@ namespace Trinity
AllCreaturesOfEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { }
bool operator() (Unit* unit)
{
- if (unit->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(unit, m_fRange, false))
+ if ((!m_uiEntry || unit->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(unit, m_fRange, false))
return true;
return false;
diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp
index 87494e78a28..121afc014ce 100644
--- a/src/server/game/Guilds/Guild.cpp
+++ b/src/server/game/Guilds/Guild.cpp
@@ -1443,17 +1443,17 @@ void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string
_BroadcastEvent(GE_BANK_TAB_UPDATED, ObjectGuid::Empty, aux, name.c_str(), icon.c_str());
}
-void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool isPublic)
+void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool officer)
{
// Player must have rights to set public/officer note
- if (!_HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EPNOTE : GR_RIGHT_EOFFNOTE))
+ if (!_HasRankRight(session->GetPlayer(), officer ? GR_RIGHT_EOFFNOTE : GR_RIGHT_EPNOTE))
SendCommandResult(session, GUILD_COMMAND_PUBLIC_NOTE, ERR_GUILD_PERMISSIONS);
else if (Member* member = GetMember(name))
{
- if (isPublic)
- member->SetPublicNote(note);
- else
+ if (officer)
member->SetOfficerNote(note);
+ else
+ member->SetPublicNote(note);
HandleRoster(session);
}
diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp
index e91da35c06b..55bfb93c1c4 100644
--- a/src/server/game/Handlers/BattleGroundHandler.cpp
+++ b/src/server/game/Handlers/BattleGroundHandler.cpp
@@ -777,7 +777,7 @@ void WorldSession::HandleReportPvPAFK(WorldPacket& recvData)
if (!reportedPlayer)
{
- TC_LOG_DEBUG("bg.battleground", "WorldSession::HandleReportPvPAFK: player not found");
+ TC_LOG_INFO("bg.reportpvpafk", "WorldSession::HandleReportPvPAFK: %s [IP: %s] reported %s [IP: %s]", _player->GetName().c_str(), _player->GetSession()->GetRemoteAddress().c_str(), reportedPlayer->GetName().c_str(), reportedPlayer->GetSession()->GetRemoteAddress().c_str());
return;
}
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 19638ec1bf8..812a828ddce 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -45,6 +45,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
+#include "Metric.h"
class LoginQueryHolder : public SQLQueryHolder
@@ -686,17 +687,17 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData)
return;
}
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID);
- stmt->setUInt32(0, guid.GetCounter());
-
- if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
+ CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(guid);
+ if (!characterInfo)
{
- Field* fields = result->Fetch();
- accountId = fields[0].GetUInt32();
- name = fields[1].GetString();
- level = fields[2].GetUInt8();
+ sScriptMgr->OnPlayerFailedDelete(guid, initAccountId);
+ return;
}
+ accountId = characterInfo->AccountId;
+ name = characterInfo->Name;
+ level = characterInfo->Level;
+
// prevent deleting other players' characters using cheating tools
if (accountId != initAccountId)
{
@@ -969,8 +970,14 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
bool firstLogin = pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST);
if (firstLogin)
+ {
pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST);
+ PlayerInfo const* info = sObjectMgr->GetPlayerInfo(pCurrChar->getRace(), pCurrChar->getClass());
+ for (uint32 spellId : info->castSpells)
+ pCurrChar->CastSpell(pCurrChar, spellId, true);
+ }
+
// show time before shutdown if shutdown planned.
if (sWorld->IsShuttingDown())
sWorld->ShutdownMsg(true, pCurrChar);
@@ -995,6 +1002,8 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
sScriptMgr->OnPlayerLogin(pCurrChar, firstLogin);
+ TC_METRIC_EVENT("player_events", "Login", pCurrChar->GetName());
+
delete holder;
}
@@ -1361,6 +1370,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData)
uint8 plrRace = fields[0].GetUInt8();
uint8 plrClass = fields[1].GetUInt8();
uint8 plrGender = fields[2].GetUInt8();
+ std::string oldName = fields[4].GetString();
if (!Player::ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo.HairStyle, customizeInfo.HairColor, customizeInfo.Face, customizeInfo.FacialHair, customizeInfo.Skin, true))
{
@@ -1389,6 +1399,13 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData)
return;
}
+ // prevent character rename
+ if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (customizeInfo.Name != oldName))
+ {
+ SendCharCustomize(CHAR_NAME_FAILURE, customizeInfo);
+ return;
+ }
+
// prevent character rename to invalid name
if (!normalizePlayerName(customizeInfo.Name))
{
@@ -1420,17 +1437,6 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData)
}
}
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME);
- stmt->setUInt32(0, customizeInfo.Guid.GetCounter());
- result = CharacterDatabase.Query(stmt);
-
- if (result)
- {
- std::string oldname = result->Fetch()[0].GetString();
- TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character[%s] (%s) Customized to: %s",
- GetAccountId(), GetRemoteAddress().c_str(), oldname.c_str(), customizeInfo.Guid.ToString().c_str(), customizeInfo.Name.c_str());
- }
-
SQLTransaction trans = CharacterDatabase.BeginTransaction();
Player::Customize(&customizeInfo, trans);
@@ -1452,6 +1458,9 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData)
sWorld->UpdateCharacterInfo(customizeInfo.Guid, customizeInfo.Name, customizeInfo.Gender);
SendCharCustomize(RESPONSE_SUCCESS, customizeInfo);
+
+ TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character[%s] (%s) Customized to: %s",
+ GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), customizeInfo.Guid.ToString().c_str(), customizeInfo.Name.c_str());
}
void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData)
@@ -1605,6 +1614,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData)
return;
}
+ std::string oldName = nameData->Name;
uint8 oldRace = nameData->Race;
uint8 playerClass = nameData->Class;
uint8 level = nameData->Level;
@@ -1647,6 +1657,13 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData)
}
}
+ // prevent character rename
+ if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (factionChangeInfo.Name != oldName))
+ {
+ SendCharFactionChange(CHAR_NAME_FAILURE, factionChangeInfo);
+ return;
+ }
+
// prevent character rename to invalid name
if (!normalizePlayerName(factionChangeInfo.Name))
{
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index 346adf6f091..2d94d423142 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -239,8 +239,6 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
switch (type)
{
case CHAT_MSG_SAY:
- case CHAT_MSG_EMOTE:
- case CHAT_MSG_YELL:
{
// Prevent cheating
if (!sender->IsAlive())
@@ -252,13 +250,39 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
return;
}
- if (type == CHAT_MSG_SAY)
- sender->Say(msg, Language(lang));
- else if (type == CHAT_MSG_EMOTE)
- sender->TextEmote(msg);
- else if (type == CHAT_MSG_YELL)
- sender->Yell(msg, Language(lang));
- } break;
+ sender->Say(msg, Language(lang));
+ break;
+ }
+ case CHAT_MSG_EMOTE:
+ {
+ // Prevent cheating
+ if (!sender->IsAlive())
+ return;
+
+ if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_EMOTE_LEVEL_REQ))
+ {
+ SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld->getIntConfig(CONFIG_CHAT_EMOTE_LEVEL_REQ));
+ return;
+ }
+
+ sender->TextEmote(msg);
+ break;
+ }
+ case CHAT_MSG_YELL:
+ {
+ // Prevent cheating
+ if (!sender->IsAlive())
+ return;
+
+ if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_YELL_LEVEL_REQ))
+ {
+ SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld->getIntConfig(CONFIG_CHAT_YELL_LEVEL_REQ));
+ return;
+ }
+
+ sender->Yell(msg, Language(lang));
+ break;
+ }
case CHAT_MSG_WHISPER:
{
if (!normalizePlayerName(to))
@@ -298,7 +322,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
sender->AddWhisperWhiteList(receiver->GetGUID());
GetPlayer()->Whisper(msg, Language(lang), receiver);
- } break;
+ break;
+ }
case CHAT_MSG_PARTY:
case CHAT_MSG_PARTY_LEADER:
{
@@ -319,7 +344,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
WorldPacket data;
ChatHandler::BuildChatPacket(data, ChatMsg(type), Language(lang), sender, NULL, msg);
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
- } break;
+ break;
+ }
case CHAT_MSG_GUILD:
{
if (GetPlayer()->GetGuildId())
@@ -331,7 +357,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
guild->BroadcastToGuild(this, false, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
}
}
- } break;
+ break;
+ }
case CHAT_MSG_OFFICER:
{
if (GetPlayer()->GetGuildId())
@@ -343,7 +370,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
guild->BroadcastToGuild(this, true, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
}
}
- } break;
+ break;
+ }
case CHAT_MSG_RAID:
{
// if player is in battleground, he cannot say to battleground members by /ra
@@ -360,7 +388,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, Language(lang), sender, NULL, msg);
group->BroadcastPacket(&data, false);
- } break;
+ break;
+ }
case CHAT_MSG_RAID_LEADER:
{
// if player is in battleground, he cannot say to battleground members by /ra
@@ -377,7 +406,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_LEADER, Language(lang), sender, NULL, msg);
group->BroadcastPacket(&data, false);
- } break;
+ break;
+ }
case CHAT_MSG_RAID_WARNING:
{
Group* group = GetPlayer()->GetGroup();
@@ -390,7 +420,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
//in battleground, raid warning is sent only to players in battleground - code is ok
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_WARNING, Language(lang), sender, NULL, msg);
group->BroadcastPacket(&data, false);
- } break;
+ break;
+ }
case CHAT_MSG_BATTLEGROUND:
{
//battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
@@ -403,7 +434,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_BATTLEGROUND, Language(lang), sender, NULL, msg);
group->BroadcastPacket(&data, false);
- } break;
+ break;
+ }
case CHAT_MSG_BATTLEGROUND_LEADER:
{
// battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
@@ -416,7 +448,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_BATTLEGROUND_LEADER, Language(lang), sender, NULL, msg);;
group->BroadcastPacket(&data, false);
- } break;
+ break;
+ }
case CHAT_MSG_CHANNEL:
{
if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ))
@@ -436,7 +469,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
chn->Say(sender->GetGUID(), msg.c_str(), lang);
}
}
- } break;
+ break;
+ }
case CHAT_MSG_AFK:
{
if (!sender->IsInCombat())
diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp
index 1c1982600ca..5ea81718a5d 100644
--- a/src/server/game/Handlers/GroupHandler.cpp
+++ b/src/server/game/Handlers/GroupHandler.cpp
@@ -82,6 +82,13 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData)
return;
}
+ // player trying to invite himself (most likely cheating)
+ if (player == GetPlayer())
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S);
+ return;
+ }
+
// restrict invite to GMs
if (!sWorld->getBoolConfig(CONFIG_ALLOW_GM_GROUP) && !GetPlayer()->IsGameMaster() && player->IsGameMaster())
{
@@ -113,6 +120,12 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData)
return;
}
+ if (!player->GetSocial()->HasFriend(GetPlayer()->GetGUID().GetCounter()) && GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_PARTY_LEVEL_REQ))
+ {
+ SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED);
+ return;
+ }
+
Group* group = GetPlayer()->GetGroup();
if (group && group->isBGGroup())
group = GetPlayer()->GetOriginalGroup();
@@ -170,6 +183,7 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData)
}
if (!group->AddInvite(player))
{
+ group->RemoveAllInvites();
delete group;
return;
}
diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp
index e8f8372d679..3801e974dba 100644
--- a/src/server/game/Handlers/GuildHandler.cpp
+++ b/src/server/game/Handlers/GuildHandler.cpp
@@ -178,7 +178,7 @@ void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket)
if (normalizePlayerName(playerName))
if (Guild* guild = GetPlayer()->GetGuild())
- guild->HandleSetMemberNote(this, playerName, note, true);
+ guild->HandleSetMemberNote(this, playerName, note, false);
}
void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
@@ -192,7 +192,7 @@ void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
if (normalizePlayerName(playerName))
if (Guild* guild = GetPlayer()->GetGuild())
- guild->HandleSetMemberNote(this, playerName, note, false);
+ guild->HandleSetMemberNote(this, playerName, note, true);
}
void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 7f5d882a912..42ce01708eb 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -1095,7 +1095,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData)
return;
}
- if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_WRAPPER)) // cheating: non-wrapper wrapper
+ if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_IS_WRAPPER)) // cheating: non-wrapper wrapper
{
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL);
return;
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index 82fdc2e54c7..b0b06b517c8 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -338,7 +338,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid)
else
{
// Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item
- if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_OPENABLE))
+ if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_HAS_LOOT))
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
}
return; // item can be looted only single player
@@ -370,13 +370,10 @@ void WorldSession::DoLootRelease(ObjectGuid lguid)
loot->roundRobinPlayer.Clear();
if (Group* group = player->GetGroup())
- {
group->SendLooter(creature, NULL);
-
- // force update of dynamic flags, otherwise other group's players still not able to loot.
- creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
- }
}
+ // force dynflag update to update looter and lootable info
+ creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
}
}
diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp
index 1e00c25a0c3..d02611971cc 100644
--- a/src/server/game/Handlers/NPCHandler.cpp
+++ b/src/server/game/Handlers/NPCHandler.cpp
@@ -236,8 +236,8 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData)
recvData >> guid >> spellId;
TC_LOG_DEBUG("network", "WORLD: Received CMSG_TRAINER_BUY_SPELL %s, learn spell id is: %u", guid.ToString().c_str(), spellId);
- Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
- if (!unit)
+ Creature* trainer = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
+ if (!trainer)
{
TC_LOG_DEBUG("network", "WORLD: HandleTrainerBuySpellOpcode - %s not found or you can not interact with him.", guid.ToString().c_str());
return;
@@ -247,8 +247,20 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+ // check race for mount trainers
+ if (trainer->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_MOUNTS)
+ {
+ if (uint32 trainerRace = trainer->GetCreatureTemplate()->trainer_race)
+ if (_player->getRace() != trainerRace)
+ return;
+ }
+
+ // check class for class trainers
+ if (_player->getClass() != trainer->GetCreatureTemplate()->trainer_class && trainer->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS)
+ return;
+
// check present spell in trainer spell list
- TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
+ TrainerSpellData const* trainer_spells = trainer->GetTrainerSpells();
if (!trainer_spells)
return;
@@ -262,7 +274,7 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData)
return;
// apply reputation discount
- uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit)));
+ uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(trainer)));
// check money requirement
if (!_player->HasEnoughMoney(nSpellCost))
@@ -270,8 +282,8 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData)
_player->ModifyMoney(-int32(nSpellCost));
- unit->SendPlaySpellVisual(179); // 53 SpellCastDirected
- unit->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute
+ trainer->SendPlaySpellVisual(179); // 53 SpellCastDirected
+ trainer->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute
// learn explicitly or cast explicitly
if (trainer_spell->IsCastable())
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 8bfb0070605..17c9c4a58d7 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -595,7 +595,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
// check it!
- if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
+ if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ||
pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
return;
diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
index a7db18deddb..b7aee1778a9 100644
--- a/src/server/game/Handlers/QuestHandler.cpp
+++ b/src/server/game/Handlers/QuestHandler.cpp
@@ -426,6 +426,7 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recvData)
}
_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->RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, questId);
@@ -640,46 +641,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket
{
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
- uint32 count = 0;
-
- WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4);
- data << uint32(count); // placeholder
-
- for (auto itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr)
- {
- uint32 questStatus = DIALOG_STATUS_NONE;
-
- if (itr->IsAnyTypeCreature())
- {
- // need also pet quests case support
- Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(), *itr);
- if (!questgiver || questgiver->IsHostileTo(_player))
- continue;
- if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
- continue;
-
- questStatus = _player->GetQuestDialogStatus(questgiver);
-
- data << uint64(questgiver->GetGUID());
- data << uint8(questStatus);
- ++count;
- }
- else if (itr->IsGameObject())
- {
- GameObject* questgiver = GetPlayer()->GetMap()->GetGameObject(*itr);
- if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
- continue;
-
- questStatus = _player->GetQuestDialogStatus(questgiver);
-
- data << uint64(questgiver->GetGUID());
- data << uint8(questStatus);
- ++count;
- }
- }
-
- data.put<uint32>(0, count); // write real count
- SendPacket(&data);
+ _player->SendQuestGiverStatusMultiple();
}
void WorldSession::HandleQueryQuestsCompleted(WorldPacket & /*recvData*/)
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index e07e10ab00c..93e986b0339 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -196,7 +196,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
}
// Verify that the bag is an actual bag or wrapped item that can be used "normally"
- if (!(proto->Flags & ITEM_PROTO_FLAG_OPENABLE) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
+ if (!(proto->Flags & ITEM_PROTO_FLAG_HAS_LOOT) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
{
pUser->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, item, NULL);
TC_LOG_ERROR("network", "Possible hacking attempt: Player %s [guid: %u] tried to open item [guid: %u, entry: %u] which is not openable!",
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 5cede9a32d5..260e7ff464f 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -353,7 +353,7 @@ LootItem::LootItem(LootStoreItem const& li)
conditions = li.conditions;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
- freeforall = proto && (proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT);
+ freeforall = proto && (proto->Flags & ITEM_PROTO_FLAG_MULTI_DROP);
follow_loot_rules = proto && (proto->FlagsCu & ITEM_FLAGS_CU_FOLLOW_LOOT_RULES);
needs_quest = li.needs_quest;
@@ -429,7 +429,7 @@ void Loot::AddItem(LootStoreItem const& item)
// non-conditional one-player only items are counted here,
// free for all items are counted in FillFFALoot(),
// non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
- if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT))
+ if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_PROTO_FLAG_MULTI_DROP))
++unlootedCount;
}
}
@@ -704,7 +704,7 @@ void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID)
if (_itr->itemid != itemID)
continue;
- _itr->canSave = true;
+ _itr->canSave = false;
break;
}
}
@@ -786,6 +786,19 @@ uint32 Loot::GetMaxSlotInLootFor(Player* player) const
return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0);
}
+// return true if there is any item that is lootable for any player (not quest item, FFA or conditional)
+bool Loot::hasItemForAll() const
+{
+ // Gold is always lootable
+ if (gold)
+ return true;
+
+ for (LootItem const& item : items)
+ if (!item.is_looted && !item.freeforall && item.conditions.empty())
+ return true;
+ return false;
+}
+
// return true if there is any FFA, quest or conditional item for the player.
bool Loot::hasItemFor(Player* player) const
{
@@ -1640,7 +1653,7 @@ void LoadLootTemplates_Item()
// remove real entries and check existence loot
ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore();
for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr)
- if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end() && itr->second.Flags & ITEM_PROTO_FLAG_OPENABLE)
+ if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end() && itr->second.Flags & ITEM_PROTO_FLAG_HAS_LOOT)
lootIdSet.erase(itr->second.ItemId);
// output error for any still listed (not referenced from appropriate table) ids
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index 0afb327d7f0..a66b8f0b4c5 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -382,6 +382,7 @@ struct TC_GAME_API Loot
LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL);
uint32 GetMaxSlotInLootFor(Player* player) const;
+ bool hasItemForAll() const;
bool hasItemFor(Player* player) const;
bool hasOverThresholdItem() const;
diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp
index bc31a3c8c06..0fc49eba05e 100644
--- a/src/server/game/Mails/Mail.cpp
+++ b/src/server/game/Mails/Mail.cpp
@@ -70,6 +70,13 @@ MailSender::MailSender(Player* sender)
m_senderId = sender->GetGUID().GetCounter();
}
+MailSender::MailSender(uint32 senderEntry)
+{
+ m_messageType = MAIL_CREATURE;
+ m_senderId = senderEntry;
+ m_stationery = MAIL_STATIONERY_DEFAULT;
+}
+
MailReceiver::MailReceiver(Player* receiver) : m_receiver(receiver), m_receiver_lowguid(receiver->GetGUID().GetCounter()) { }
MailReceiver::MailReceiver(Player* receiver, ObjectGuid::LowType receiver_lowguid) : m_receiver(receiver), m_receiver_lowguid(receiver_lowguid)
diff --git a/src/server/game/Mails/Mail.h b/src/server/game/Mails/Mail.h
index ee1a0aae2c9..ad14e3cecf9 100644
--- a/src/server/game/Mails/Mail.h
+++ b/src/server/game/Mails/Mail.h
@@ -90,6 +90,7 @@ class TC_GAME_API MailSender
MailSender(CalendarEvent* sender);
MailSender(AuctionEntry* sender);
MailSender(Player* sender);
+ MailSender(uint32 senderEntry);
public: // Accessors
MailMessageType GetMailMessageType() const { return m_messageType; }
ObjectGuid::LowType GetSenderId() const { return m_senderId; }
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 794be12ee7c..23ca8cbba1a 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -90,7 +90,7 @@ bool Map::ExistMap(uint32 mapid, int gx, int gy)
if (fread(&header, sizeof(header), 1, pf) == 1)
{
if (header.mapMagic.asUInt != MapMagic.asUInt || header.versionMagic.asUInt != MapVersionMagic.asUInt)
- TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please recreate using the mapextractor.",
+ TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please pull your source, recompile tools and recreate maps using the updated mapextractor, then replace your old map files with new files. If you still have problems search on forum for error TCE00018.",
fileName, 4, header.mapMagic.asChar, 4, header.versionMagic.asChar, 4, MapMagic.asChar, 4, MapVersionMagic.asChar);
else
ret = true;
@@ -1731,7 +1731,7 @@ bool GridMap::loadData(const char* filename)
return true;
}
- TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please recreate using the mapextractor.",
+ TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please pull your source, recompile tools and recreate maps using the updated mapextractor, then replace your old map files with new files. If you still have problems search on forum for error TCE00018.",
filename, 4, header.mapMagic.asChar, 4, header.versionMagic.asChar, 4, MapMagic.asChar, 4, MapVersionMagic.asChar);
fclose(in);
return false;
@@ -2299,12 +2299,12 @@ inline GridMap* Map::GetGrid(float x, float y)
return GridMaps[gx][gy];
}
-float Map::GetWaterOrGroundLevel(float x, float y, float z, float* ground /*= NULL*/, bool /*swim = false*/) const
+float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground /*= NULL*/, bool /*swim = false*/) const
{
if (const_cast<Map*>(this)->GetGrid(x, y))
{
// we need ground level (including grid height version) for proper return water level in point
- float ground_z = GetHeight(PHASEMASK_NORMAL, x, y, z, true, 50.0f);
+ float ground_z = GetHeight(phasemask, x, y, z, true, 50.0f);
if (ground)
*ground = ground_z;
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 5f134613e85..48864180b84 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -494,7 +494,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
BattlegroundMap* ToBattlegroundMap() { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap*>(this); else return NULL; }
BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return NULL; }
- float GetWaterOrGroundLevel(float x, float y, float z, float* ground = NULL, bool swim = false) const;
+ float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = NULL, bool swim = false) const;
float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const;
void Balance() { _dynamicTree.balance(); }
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index a7fdc37d324..91a26c29150 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -126,9 +126,9 @@ class TC_GAME_API MapManager
template<typename Worker>
void DoForAllMapsWithMapId(uint32 mapId, Worker&& worker);
- uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; }
+ void IncreaseScheduledScriptsCount() { ++_scheduledScripts; }
+ void DecreaseScheduledScriptCount() { --_scheduledScripts; }
+ void DecreaseScheduledScriptCount(std::size_t count) { _scheduledScripts -= count; }
bool IsScriptScheduled() const { return _scheduledScripts > 0; }
private:
@@ -157,7 +157,7 @@ class TC_GAME_API MapManager
MapUpdater m_updater;
// atomic op counter for active scripts amount
- std::atomic<uint32> _scheduledScripts;
+ std::atomic<std::size_t> _scheduledScripts;
};
template<typename Worker>
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index d6af5d81432..143f535ddf5 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -112,7 +112,9 @@ enum TrinityStrings
LANG_RBAC_PERM_REVOKED_NOT_IN_LIST = 79,
LANG_PVPSTATS = 80,
LANG_PVPSTATS_DISABLED = 81,
- // Free 82 - 95
+ LANG_COMMAND_NEARGRAVEYARD = 82,
+ LANG_COMMAND_NEARGRAVEYARD_NOTFOUND = 83,
+ // Free 84 - 95
LANG_GUILD_RENAME_ALREADY_EXISTS = 96,
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index f9a98cffd0e..7f77453d00b 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -456,7 +456,7 @@ enum SpellAttr4
enum SpellAttr5
{
- SPELL_ATTR5_UNK0 = 0x00000001, // 0
+ SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING = 0x00000001, // 0 available casting channel spell when moving
SPELL_ATTR5_NO_REAGENT_WHILE_PREP = 0x00000002, // 1 not need reagents if UNIT_FLAG_PREPARATION
SPELL_ATTR5_UNK2 = 0x00000004, // 2
SPELL_ATTR5_USABLE_WHILE_STUNNED = 0x00000008, // 3 usable while stunned
@@ -1490,11 +1490,12 @@ enum GameObjectFlags
{
GO_FLAG_IN_USE = 0x00000001, // disables interaction while animated
GO_FLAG_LOCKED = 0x00000002, // require key, spell, event, etc to be opened. Makes "Locked" appear in tooltip
- GO_FLAG_INTERACT_COND = 0x00000004, // cannot interact (condition to interact)
+ GO_FLAG_INTERACT_COND = 0x00000004, // cannot interact (condition to interact - requires GO_DYNFLAG_LO_ACTIVATE to enable interaction clientside)
GO_FLAG_TRANSPORT = 0x00000008, // any kind of transport? Object can transport (elevator, boat, car)
GO_FLAG_NOT_SELECTABLE = 0x00000010, // not selectable even in GM mode
GO_FLAG_NODESPAWN = 0x00000020, // never despawn, typically for doors, they just change state
- GO_FLAG_TRIGGERED = 0x00000040, // typically, summoned objects. Triggered by spell or other events
+ GO_FLAG_AI_OBSTACLE = 0x00000040, // makes the client register the object in something called AIObstacleMgr, unknown what it does
+ GO_FLAG_FREEZE_ANIMATION = 0x00000080,
GO_FLAG_DAMAGED = 0x00000200,
GO_FLAG_DESTROYED = 0x00000400
};
@@ -2531,6 +2532,7 @@ uint32 const CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL = (1 << (CREATURE_TYPE_ME
// CreatureFamily.dbc
enum CreatureFamily
{
+ CREATURE_FAMILY_NONE = 0,
CREATURE_FAMILY_WOLF = 1,
CREATURE_FAMILY_CAT = 2,
CREATURE_FAMILY_SPIDER = 3,
@@ -2576,38 +2578,38 @@ enum CreatureFamily
enum CreatureTypeFlags
{
- CREATURE_TYPEFLAGS_TAMEABLE = 0x00000001, // Tameable by any hunter
- CREATURE_TYPEFLAGS_GHOST = 0x00000002, // Creature are also visible for not alive player. Allow gossip interaction if npcflag allow?
- CREATURE_TYPEFLAGS_BOSS = 0x00000004,
- CREATURE_TYPEFLAGS_UNK3 = 0x00000008,
- CREATURE_TYPEFLAGS_UNK4 = 0x00000010,
- CREATURE_TYPEFLAGS_UNK5 = 0x00000020,
- CREATURE_TYPEFLAGS_UNK6 = 0x00000040,
- CREATURE_TYPEFLAGS_DEAD_INTERACT = 0x00000080, // Player can interact with the creature if its dead (not player dead)
- CREATURE_TYPEFLAGS_HERBLOOT = 0x00000100, // Can be looted by herbalist
- CREATURE_TYPEFLAGS_MININGLOOT = 0x00000200, // Can be looted by miner
- CREATURE_TYPEFLAGS_DONT_LOG_DEATH = 0x00000400, // Death event will not show up in combat log
- CREATURE_TYPEFLAGS_MOUNTED_COMBAT = 0x00000800, // Creature can remain mounted when entering combat
- CREATURE_TYPEFLAGS_AID_PLAYERS = 0x00001000, // ? Can aid any player in combat if in range?
- CREATURE_TYPEFLAGS_UNK13 = 0x00002000,
- CREATURE_TYPEFLAGS_UNK14 = 0x00004000, // ? Possibly not in use
- CREATURE_TYPEFLAGS_ENGINEERLOOT = 0x00008000, // Can be looted by engineer
- CREATURE_TYPEFLAGS_EXOTIC = 0x00010000, // Can be tamed by hunter as exotic pet
- CREATURE_TYPEFLAGS_UNK17 = 0x00020000, // ? Related to vehicles/pvp?
- CREATURE_TYPEFLAGS_UNK18 = 0x00040000, // ? Related to vehicle/siege weapons?
- CREATURE_TYPEFLAGS_PROJECTILE_COLLISION = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ
- CREATURE_TYPEFLAGS_UNK20 = 0x00100000,
- CREATURE_TYPEFLAGS_UNK21 = 0x00200000,
- CREATURE_TYPEFLAGS_UNK22 = 0x00400000,
- CREATURE_TYPEFLAGS_UNK23 = 0x00800000, // ? First seen in 3.2.2. Related to banner/backpack of creature/companion?
- CREATURE_TYPEFLAGS_UNK24 = 0x01000000,
- CREATURE_TYPEFLAGS_UNK25 = 0x02000000,
- CREATURE_TYPEFLAGS_PARTY_MEMBER = 0x04000000, //! Creature can be targeted by spells that require target to be in caster's party/raid
- CREATURE_TYPEFLAGS_UNK27 = 0x08000000,
- CREATURE_TYPEFLAGS_UNK28 = 0x10000000,
- CREATURE_TYPEFLAGS_UNK29 = 0x20000000,
- CREATURE_TYPEFLAGS_UNK30 = 0x40000000,
- CREATURE_TYPEFLAGS_UNK31 = 0x80000000
+ CREATURE_TYPE_FLAG_TAMEABLE_PET = 0x00000001, // Makes the mob tameable (must also be a beast and have family set)
+ CREATURE_TYPE_FLAG_GHOST_VISIBLE = 0x00000002, // Creature are also visible for not alive player. Allow gossip interaction if npcflag allow?
+ CREATURE_TYPE_FLAG_BOSS_MOB = 0x00000004, // Changes creature's visible level to "??" in the creature's portrait - Immune Knockback.
+ CREATURE_TYPE_FLAG_DO_NOT_PLAY_WOUND_PARRY_ANIMATION = 0x00000008,
+ CREATURE_TYPE_FLAG_HIDE_FACTION_TOOLTIP = 0x00000010,
+ CREATURE_TYPE_FLAG_UNK5 = 0x00000020, // Sound related
+ CREATURE_TYPE_FLAG_SPELL_ATTACKABLE = 0x00000040,
+ CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD = 0x00000080, // Player can interact with the creature if its dead (not player dead)
+ CREATURE_TYPE_FLAG_HERB_SKINNING_SKILL = 0x00000100, // Can be looted by herbalist
+ CREATURE_TYPE_FLAG_MINING_SKINNING_SKILL = 0x00000200, // Can be looted by miner
+ CREATURE_TYPE_FLAG_DO_NOT_LOG_DEATH = 0x00000400, // Death event will not show up in combat log
+ CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED = 0x00000800, // Creature can remain mounted when entering combat
+ CREATURE_TYPE_FLAG_CAN_ASSIST = 0x00001000, // ? Can aid any player in combat if in range?
+ CREATURE_TYPE_FLAG_IS_PET_BAR_USED = 0x00002000,
+ CREATURE_TYPE_FLAG_MASK_UID = 0x00004000,
+ CREATURE_TYPE_FLAG_ENGINEERING_SKINNING_SKILL = 0x00008000, // Can be looted by engineer
+ CREATURE_TYPE_FLAG_EXOTIC_PET = 0x00010000, // Can be tamed by hunter as exotic pet
+ CREATURE_TYPE_FLAG_USE_DEFAULT_COLLISION_BOX = 0x00020000, // Collision related. (always using default collision box?)
+ CREATURE_TYPE_FLAG_IS_SIEGE_WEAPON = 0x00040000,
+ CREATURE_TYPE_FLAG_CAN_COLLIDE_WITH_MISSILES = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ
+ CREATURE_TYPE_FLAG_HIDE_NAME_PLATE = 0x00100000,
+ CREATURE_TYPE_FLAG_DO_NOT_PLAY_MOUNTED_ANIMATIONS = 0x00200000,
+ CREATURE_TYPE_FLAG_IS_LINK_ALL = 0x00400000,
+ CREATURE_TYPE_FLAG_INTERACT_ONLY_WITH_CREATOR = 0x00800000,
+ CREATURE_TYPE_FLAG_DO_NOT_PLAY_UNIT_EVENT_SOUNDS = 0x01000000,
+ CREATURE_TYPE_FLAG_HAS_NO_SHADOW_BLOB = 0x02000000,
+ CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT = 0x04000000, // ! Creature can be targeted by spells that require target to be in caster's party/raid
+ CREATURE_TYPE_FLAG_FORCE_GOSSIP = 0x08000000, // Allows the creature to display a single gossip option.
+ CREATURE_TYPE_FLAG_DO_NOT_SHEATHE = 0x10000000,
+ CREATURE_TYPE_FLAG_DO_NOT_TARGET_ON_INTERACTION = 0x20000000,
+ CREATURE_TYPE_FLAG_DO_NOT_RENDER_OBJECT_NAME = 0x40000000,
+ CREATURE_TYPE_FLAG_UNIT_IS_QUEST_BOSS = 0x80000000 // Not verified
};
enum CreatureEliteType
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index f27e47fba21..26873451649 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -241,7 +241,7 @@ void MotionMaster::MoveConfused()
void MotionMaster::MoveChase(Unit* target, float dist, float angle)
{
// ignore movement request if target not exist
- if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ if (!target || target == _owner)
return;
//_owner->ClearUnitState(UNIT_STATE_FOLLOW);
@@ -266,7 +266,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
{
// ignore movement request if target not exist
- if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ if (!target || target == _owner)
return;
//_owner->AddUnitState(UNIT_STATE_FOLLOW);
@@ -479,11 +479,12 @@ void MotionMaster::MoveFall(uint32 id /*=0*/)
if (std::fabs(_owner->GetPositionZ() - tz) < 0.1f)
return;
+ _owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
+ _owner->m_movementInfo.SetFallTime(0);
+
+ // don't run spline movement for players
if (_owner->GetTypeId() == TYPEID_PLAYER)
- {
- _owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
- _owner->m_movementInfo.SetFallTime(0);
- }
+ return;
Movement::MoveSplineInit init(_owner);
init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false);
@@ -534,6 +535,7 @@ void MotionMaster::MoveSeekAssistance(float x, float y, float z)
TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) seek assistance (X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUID().GetCounter(), x, y, z);
_owner->AttackStop();
+ _owner->CastStop();
_owner->ToCreature()->SetReactState(REACT_PASSIVE);
Mutate(new AssistanceMovementGenerator(x, y, z), MOTION_SLOT_ACTIVE);
}
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
index 6cdd29986bb..4cf896c6d98 100644
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -38,6 +38,12 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
+ if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel())
+ {
+ owner->CastStop();
+ return;
+ }
+
owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
float x, y, z;
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 421678ded17..feea0ce734d 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -29,6 +29,12 @@
template<>
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
+ if (creature->HasUnitState(UNIT_STATE_CASTING) && !creature->CanMoveDuringChannel())
+ {
+ creature->CastStop();
+ return;
+ }
+
float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ;
creature->GetHomePosition(respX, respY, respZ, respO);
Map const* map = creature->GetBaseMap();
@@ -57,7 +63,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
// Limit height change
const float distanceZ = float(rand_norm()) * travelDistZ/2.0f;
destZ = respZ + distanceZ;
- float levelZ = map->GetWaterOrGroundLevel(destX, destY, destZ-2.0f);
+ float levelZ = map->GetWaterOrGroundLevel(creature->GetPhaseMask(), destX, destY, destZ-2.5f);
// Problem here, we must fly above the ground and water, not under. Let's try on next tick
if (levelZ >= destZ)
@@ -141,6 +147,9 @@ void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature)
template<>
bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff)
{
+ if (!creature || !creature->IsAlive())
+ return false;
+
if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
{
i_nextMoveTime.Reset(0); // Expire the timer
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index 403ccf684e7..2a95952d793 100644
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -36,6 +36,9 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
return;
+ if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel())
+ return;
+
if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature()))
{
owner->ToCreature()->SetCannotReachTarget(true);
@@ -70,8 +73,8 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up
// doing a "dance" while fighting
if (owner->IsPet() && i_target->GetTypeId() == TYPEID_PLAYER)
{
- dist = i_target->GetCombatReach();
- size = i_target->GetCombatReach() - i_target->GetObjectSize();
+ dist = 1.0f; //i_target->GetCombatReach();
+ size = 1.0f; //i_target->GetCombatReach() - i_target->GetObjectSize();
}
else
{
@@ -146,7 +149,7 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff)
}
// prevent movement while casting spells with cast time or channel time
- if (owner->HasUnitState(UNIT_STATE_CASTING))
+ if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel())
{
if (!owner->IsStopped())
owner->StopMoving();
@@ -165,8 +168,15 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff)
if (i_recheckDistance.Passed())
{
i_recheckDistance.Reset(100);
+
//More distance let have better performance, less distance let have more sensitive reaction at target move.
- float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+ float allowed_dist = 0.0f;
+
+ if (owner->IsPet() && (owner->GetCharmerOrOwnerGUID() == i_target->GetGUID()))
+ allowed_dist = 1.0f; // pet following owner
+ else
+ allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+
G3D::Vector3 dest = owner->movespline->FinalDestination();
if (owner->movespline->onTransport)
if (TransportBase* transport = owner->GetDirectTransport())
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index dd1cf494325..2ffa1a644eb 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -315,7 +315,7 @@ void FlightPathMovementGenerator::DoFinalize(Player* player)
player->ClearUnitState(UNIT_STATE_IN_FLIGHT);
player->Dismount();
- player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT);
if (player->m_taxi.empty())
{
@@ -335,7 +335,7 @@ void FlightPathMovementGenerator::DoReset(Player* player)
{
player->getHostileRefManager().setOnlineOfflineState(false);
player->AddUnitState(UNIT_STATE_IN_FLIGHT);
- player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT);
Movement::MoveSplineInit init(player);
uint32 end = GetPathAtMapEnd();
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
index 3b4f19adb0b..3f8e7d44ff2 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -25,6 +25,7 @@
#include "DisableMgr.h"
#include "DetourCommon.h"
#include "DetourNavMeshQuery.h"
+#include "Metric.h"
////////////////// PathGenerator //////////////////
PathGenerator::PathGenerator(const Unit* owner) :
@@ -61,6 +62,8 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo
if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z))
return false;
+ TC_METRIC_EVENT("mmap_events", "CalculatePath", "");
+
G3D::Vector3 dest(destX, destY, destZ);
SetEndPosition(dest);
@@ -939,3 +942,8 @@ void PathGenerator::ReducePathLenghtByDist(float dist)
nextVec = currVec; // we're going backwards
}
}
+
+bool PathGenerator::IsInvalidDestinationZ(Unit const* target) const
+{
+ return (target->GetPositionZ() - GetActualEndPosition().z) > 5.0f;
+}
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
index 3cad62abf25..a3e88b8a705 100644
--- a/src/server/game/Movement/PathGenerator.h
+++ b/src/server/game/Movement/PathGenerator.h
@@ -58,6 +58,7 @@ class TC_GAME_API PathGenerator
// Calculate the path from owner to given destination
// return: true if new path was calculated, false otherwise (no change needed)
bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false, bool straightLine = false);
+ bool IsInvalidDestinationZ(Unit const* target) const;
// option setters - use optional
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp
index d8173aab331..991a4eacd29 100644
--- a/src/server/game/Movement/Spline/MoveSpline.cpp
+++ b/src/server/game/Movement/Spline/MoveSpline.cpp
@@ -206,7 +206,7 @@ bool MoveSplineInitArgs::Validate(Unit* unit) const
return false;\
}
CHECK(path.size() > 1);
- CHECK(velocity > 0.1f);
+ CHECK(velocity > 0.01f);
CHECK(time_perc >= 0.f && time_perc <= 1.f);
//CHECK(_checkPathBounds());
return true;
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
index d329ab27de9..1343e0966c1 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
@@ -95,7 +95,7 @@ void OPvPCapturePoint::AddGO(uint32 type, ObjectGuid::LowType guid, uint32 entry
entry = data->id;
}
- m_Objects[type] = ObjectGuid(HighGuid::GameObject, entry, guid);
+ m_Objects[type] = guid;
m_ObjectTypes[m_Objects[type]] = type;
}
@@ -109,7 +109,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr
entry = data->id;
}
- m_Creatures[type] = ObjectGuid(HighGuid::Unit, entry, guid);
+ m_Creatures[type] = guid;
m_CreatureTypes[m_Creatures[type]] = type;
}
@@ -190,7 +190,8 @@ bool OPvPCapturePoint::DelCreature(uint32 type)
// delete respawn time for this creature
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
stmt->setUInt32(0, spawnId);
- stmt->setUInt16(1, m_PvP->GetMap()->GetId()); stmt->setUInt32(2, 0); // instance id, always 0 for world maps
+ stmt->setUInt16(1, m_PvP->GetMap()->GetId());
+ stmt->setUInt32(2, 0); // instance id, always 0 for world maps
CharacterDatabase.Execute(stmt);
sObjectMgr->DeleteCreatureData(spawnId);
@@ -236,9 +237,9 @@ bool OPvPCapturePoint::DelCapturePoint()
void OPvPCapturePoint::DeleteSpawns()
{
- for (std::map<uint32, uint32>::iterator i = m_Objects.begin(); i != m_Objects.end(); ++i)
+ for (std::map<uint32, ObjectGuid::LowType>::iterator i = m_Objects.begin(); i != m_Objects.end(); ++i)
DelObject(i->first);
- for (std::map<uint32, uint32>::iterator i = m_Creatures.begin(); i != m_Creatures.end(); ++i)
+ for (std::map<uint32, ObjectGuid::LowType>::iterator i = m_Creatures.begin(); i != m_Creatures.end(); ++i)
DelCreature(i->first);
DelCapturePoint();
}
@@ -581,7 +582,7 @@ bool OPvPCapturePoint::HandleDropFlag(Player* /*player*/, uint32 /*id*/)
int32 OPvPCapturePoint::HandleOpenGo(Player* /*player*/, GameObject* go)
{
- std::map<uint32, uint32>::iterator itr = m_ObjectTypes.find(go->GetSpawnId());
+ std::map<ObjectGuid::LowType, uint32>::iterator itr = m_ObjectTypes.find(go->GetSpawnId());
if (itr != m_ObjectTypes.end())
return itr->second;
@@ -596,7 +597,7 @@ bool OutdoorPvP::HandleAreaTrigger(Player* /*player*/, uint32 /*trigger*/)
void OutdoorPvP::BroadcastPacket(WorldPacket &data) const
{
// This is faster than sWorld->SendZoneMessage
- for (uint32 team = 0; team < 2; ++team)
+ for (uint32 team = 0; team < BG_TEAMS_COUNT; ++team)
for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->GetSession()->SendPacket(&data);
diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp
index 4a4ee9bff9c..91a7b87eec7 100644
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -132,7 +132,15 @@ Quest::Quest(Field* questRecord)
void Quest::LoadQuestDetails(Field* fields)
{
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ {
+ if (!sEmotesStore.LookupEntry(fields[1+i].GetUInt16()))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `quest_details` has non-existing Emote%i (%u) set for quest %u. Skipped.", 1+i, fields[1+i].GetUInt16(), fields[0].GetUInt32());
+ continue;
+ }
+
DetailsEmote[i] = fields[1+i].GetUInt16();
+ }
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
DetailsEmoteDelay[i] = fields[5+i].GetUInt32();
@@ -142,13 +150,28 @@ void Quest::LoadQuestRequestItems(Field* fields)
{
EmoteOnComplete = fields[1].GetUInt16();
EmoteOnIncomplete = fields[2].GetUInt16();
+
+ if (!sEmotesStore.LookupEntry(EmoteOnComplete))
+ TC_LOG_ERROR("sql.sql", "Table `quest_request_items` has non-existing EmoteOnComplete (%u) set for quest %u.", EmoteOnComplete, fields[0].GetUInt32());
+
+ if (!sEmotesStore.LookupEntry(EmoteOnIncomplete))
+ TC_LOG_ERROR("sql.sql", "Table `quest_request_items` has non-existing EmoteOnIncomplete (%u) set for quest %u.", EmoteOnIncomplete, fields[0].GetUInt32());
+
RequestItemsText = fields[3].GetString();
}
void Quest::LoadQuestOfferReward(Field* fields)
{
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ {
+ if (!sEmotesStore.LookupEntry(fields[1+i].GetUInt16()))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `quest_offer_reward` has non-existing Emote%i (%u) set for quest %u. Skipped.", 1+i, fields[1+i].GetUInt16(), fields[0].GetUInt32());
+ continue;
+ }
+
OfferRewardEmote[i] = fields[1+i].GetUInt16();
+ }
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
OfferRewardEmoteDelay[i] = fields[5+i].GetUInt32();
@@ -173,7 +196,8 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
RequiredMinRepValue = fields[13].GetInt32();
RequiredMaxRepValue = fields[14].GetInt32();
StartItemCount = fields[15].GetUInt8();
- SpecialFlags = fields[16].GetUInt8();
+ RewardMailSenderEntry = fields[16].GetUInt32();
+ SpecialFlags = fields[17].GetUInt8();
if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT)
Flags |= QUEST_FLAGS_AUTO_ACCEPT;
@@ -254,6 +278,9 @@ bool Quest::IsRaidQuest(Difficulty difficulty) const
break;
}
+ if ((Flags & QUEST_FLAGS_RAID) != 0)
+ return true;
+
return false;
}
@@ -285,3 +312,10 @@ uint32 Quest::CalculateHonorGain(uint8 level) const
return honor;
}
+
+bool Quest::CanIncreaseRewardedQuestCounters() const
+{
+ // Dungeon Finder/Daily/Repeatable (if not weekly, monthly or seasonal) quests are never considered rewarded serverside.
+ // This affects counters and client requests for completed quests.
+ return (!IsDFQuest() && !IsDaily() && (!IsRepeatable() || IsWeekly() || IsMonthly() || IsSeasonal()));
+}
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index 5e3bb4889ab..dc6d22deab0 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -134,7 +134,7 @@ enum QuestFlags
QUEST_FLAGS_SHARABLE = 0x00000008, // Can be shared: Player::CanShareQuest()
QUEST_FLAGS_HAS_CONDITION = 0x00000010, // Not used currently
QUEST_FLAGS_HIDE_REWARD_POI = 0x00000020, // Not used currently: Unsure of content
- QUEST_FLAGS_RAID = 0x00000040, // Not used currently
+ QUEST_FLAGS_RAID = 0x00000040, // Can be completed while in raid
QUEST_FLAGS_TBC = 0x00000080, // Not used currently: Available if TBC expansion enabled only
QUEST_FLAGS_NO_MONEY_FROM_XP = 0x00000100, // Not used currently: Experience is not converted to gold at max level
QUEST_FLAGS_HIDDEN_REWARDS = 0x00000200, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
@@ -257,6 +257,7 @@ class TC_GAME_API Quest
int32 GetRewSpellCast() const { return RewardSpell; }
uint32 GetRewMailTemplateId() const { return RewardMailTemplateId; }
uint32 GetRewMailDelaySecs() const { return RewardMailDelay; }
+ uint32 GetRewMailSenderEntry() const { return RewardMailSenderEntry; }
uint32 GetPOIContinent() const { return POIContinent; }
float GetPOIx() const { return POIx; }
float GetPOIy() const { return POIy; }
@@ -276,6 +277,7 @@ class TC_GAME_API Quest
bool IsAllowedInRaid(Difficulty difficulty) const;
bool IsDFQuest() const { return (SpecialFlags & QUEST_SPECIAL_FLAGS_DF_QUEST) != 0; }
uint32 CalculateHonorGain(uint8 level) const;
+ bool CanIncreaseRewardedQuestCounters() const;
// multiple values
std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
@@ -373,6 +375,7 @@ class TC_GAME_API Quest
uint32 RequiredMaxRepFaction = 0;
int32 RequiredMaxRepValue = 0;
uint32 StartItemCount = 0;
+ uint32 RewardMailSenderEntry = 0;
uint32 SpecialFlags = 0; // custom flags, not sniffed/WDB
};
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index ac440da3f24..bd3f9cf2bfe 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -368,7 +368,9 @@ class CreatureGameObjectScriptRegistrySwapHooks
// Hook which is called before a creature is swapped
static void UnloadStage1(Creature* creature)
{
- creature->m_Events.KillAllEvents(true);
+ // Remove deletable events only,
+ // otherwise it causes crashes with non-deletable spell events.
+ creature->m_Events.KillAllEvents(false);
if (creature->IsCharmed())
creature->RemoveCharmedBy(nullptr);
@@ -985,7 +987,7 @@ void ScriptMgr::Initialize()
FillSpellSummary();
// Load core scripts
- SetScriptContext("___static___");
+ SetScriptContext(GetNameOfStaticContext());
// SmartAI
AddSC_SmartScripts();
@@ -1040,6 +1042,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 cc1b65fa593..233e56aadb2 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/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index ddcc10b53dd..f65cd750ea9 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -43,6 +43,7 @@
#include "WardenWin.h"
#include "MoveSpline.h"
#include "WardenMac.h"
+#include "Metric.h"
#include <zlib.h>
@@ -380,6 +381,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
break;
}
+ TC_METRIC_VALUE("processed_packets", processedPackets);
+
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
if (m_Socket && m_Socket->IsOpen() && _warden)
@@ -536,6 +539,8 @@ void WorldSession::LogoutPlayer(bool save)
//! Call script hook before deletion
sScriptMgr->OnPlayerLogout(_player);
+ TC_METRIC_EVENT("player_events", "Logout", _player->GetName());
+
//! Remove the player from the world
// the player may not be in the world when logging out
// e.g if he got disconnected during a transfer to another map
@@ -823,32 +828,15 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi)
mi->RemoveMovementFlag((maskToRemove));
#endif
+ if (!GetPlayer()->GetVehicleBase() || !(GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION))
+ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), MOVEMENTFLAG_ROOT);
+
/*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid
in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD.
It will freeze clients that receive this player's movement info.
*/
- // Only adjust movement flag removal for vehicles with the VEHICLE_FLAG_FIXED_POSITION flag, or the hard coded exceptions below:
- // 30236 | Argent Cannon
- // 39759 | Tankbuster Cannon
- if (GetPlayer()->GetVehicleBase() && ((GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || GetPlayer()->GetVehicleBase()->GetEntry() == 30236 || GetPlayer()->GetVehicleBase()->GetEntry() == 39759))
- {
- // Actually players in rooted vehicles still send commands, don't clear root for these!
- // Check specifically for the following conditions:
- // MOVEMENTFLAG_ROOT + no other flags (0x800)
- // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_LEFT (0x810)
- // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_RIGHT (0x820)
- // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_UP (0x840)
- // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_DOWN (0x880)
- // If none of these are true, clear the root
- REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN),
- MOVEMENTFLAG_MASK_MOVING);
- }
- else
- {
- // Only remove here for non vehicles
- REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
- MOVEMENTFLAG_ROOT);
- }
+ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_MASK_MOVING),
+ MOVEMENTFLAG_MASK_MOVING);
//! Cannot hover without SPELL_AURA_HOVER
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER),
@@ -874,8 +862,10 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi)
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FORWARD) && mi->HasMovementFlag(MOVEMENTFLAG_BACKWARD),
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
- //! Cannot walk on water without SPELL_AURA_WATER_WALK
- REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && !GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK),
+ //! Cannot walk on water without SPELL_AURA_WATER_WALK except for ghosts
+ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) &&
+ !GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
+ !GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
MOVEMENTFLAG_WATERWALKING);
//! Cannot feather fall without SPELL_AURA_FEATHER_FALL
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 1c77283d812..a8a639d9b7f 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -324,9 +324,20 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
switch (opcode)
{
case CMSG_PING:
+ {
LogOpcodeText(opcode, sessionGuard);
- return HandlePing(packet) ? ReadDataHandlerResult::Ok : ReadDataHandlerResult::Error;
+ try
+ {
+ return HandlePing(packet) ? ReadDataHandlerResult::Ok : ReadDataHandlerResult::Error;
+ }
+ catch (ByteBufferPositionException const&)
+ {
+ }
+ TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_PING", GetRemoteIpAddress().to_string().c_str());
+ return ReadDataHandlerResult::Error;
+ }
case CMSG_AUTH_SESSION:
+ {
LogOpcodeText(opcode, sessionGuard);
if (_authed)
{
@@ -336,8 +347,17 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
return ReadDataHandlerResult::Error;
}
- HandleAuthSession(packet);
- return ReadDataHandlerResult::WaitingForQuery;
+ try
+ {
+ HandleAuthSession(packet);
+ return ReadDataHandlerResult::WaitingForQuery;
+ }
+ catch (ByteBufferPositionException const&)
+ {
+ }
+ TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string().c_str());
+ return ReadDataHandlerResult::Error;
+ }
case CMSG_KEEP_ALIVE:
LogOpcodeText(opcode, sessionGuard);
break;
diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h
index e0daf59fcc2..43f6b51031a 100644
--- a/src/server/game/Spells/Auras/SpellAuraDefines.h
+++ b/src/server/game/Spells/Auras/SpellAuraDefines.h
@@ -348,7 +348,7 @@ enum AuraType
SPELL_AURA_ABILITY_PERIODIC_CRIT = 286,
SPELL_AURA_DEFLECT_SPELLS = 287,
SPELL_AURA_IGNORE_HIT_DIRECTION = 288,
- SPELL_AURA_289 = 289,
+ SPELL_AURA_PREVENT_DURABILITY_LOSS = 289,
SPELL_AURA_MOD_CRIT_PCT = 290,
SPELL_AURA_MOD_XP_QUEST_PCT = 291,
SPELL_AURA_OPEN_STABLE = 292,
@@ -367,7 +367,7 @@ enum AuraType
SPELL_AURA_MOD_MINIMUM_SPEED = 305,
SPELL_AURA_306 = 306,
SPELL_AURA_HEAL_ABSORB_TEST = 307,
- SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER = 308, // NYI
+ SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER = 308,
SPELL_AURA_309 = 309,
SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE = 310,
SPELL_AURA_311 = 311,
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index ffe79293430..94fe6778ba1 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -346,7 +346,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNoImmediateEffect, //286 SPELL_AURA_ABILITY_PERIODIC_CRIT implemented in AuraEffect::PeriodicTick
&AuraEffect::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult
&AuraEffect::HandleNoImmediateEffect, //288 SPELL_AURA_IGNORE_HIT_DIRECTION implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult Unit::RollMeleeOutcomeAgainst
- &AuraEffect::HandleNULL, //289 unused (3.2.0)
+ &AuraEffect::HandleNoImmediateEffect, //289 SPELL_AURA_PREVENT_DURABILITY_LOSS implemented in Player::DurabilityPointsLoss
&AuraEffect::HandleAuraModCritPct, //290 SPELL_AURA_MOD_CRIT_PCT
&AuraEffect::HandleNoImmediateEffect, //291 SPELL_AURA_MOD_XP_QUEST_PCT implemented in Player::RewardQuest
&AuraEffect::HandleAuraOpenStable, //292 SPELL_AURA_OPEN_STABLE
@@ -365,7 +365,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleAuraModIncreaseSpeed, //305 SPELL_AURA_MOD_MINIMUM_SPEED
&AuraEffect::HandleUnused, //306 0 spells in 3.3.5
&AuraEffect::HandleUnused, //307 0 spells in 3.3.5
- &AuraEffect::HandleNULL, //308 new aura for hunter traps
+ &AuraEffect::HandleNoImmediateEffect, //308 SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER implemented in Unit::GetUnitCriticalChance and Unit::GetUnitSpellCriticalChance
&AuraEffect::HandleUnused, //309 0 spells in 3.3.5
&AuraEffect::HandleNoImmediateEffect, //310 SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE implemented in Spell::CalculateDamageDone
&AuraEffect::HandleNULL, //311 0 spells in 3.3.5
@@ -846,16 +846,6 @@ void AuraEffect::UpdatePeriodic(Unit* caster)
case 59911: // Tenacity (vehicle)
GetBase()->RefreshDuration();
break;
- case 66823: case 67618: case 67619: case 67620: // Paralytic Toxin
- // Get 0 effect aura
- if (AuraEffect* slow = GetBase()->GetEffect(0))
- {
- int32 newAmount = slow->GetAmount() - 10;
- if (newAmount < -100)
- newAmount = -100;
- slow->ChangeAmount(newAmount);
- }
- break;
default:
break;
}
@@ -2545,10 +2535,9 @@ void AuraEffect::HandleAuraAllowFlight(AuraApplication const* aurApp, uint8 mode
return;
}
- target->SetCanFly(apply);
-
- if (!apply && target->GetTypeId() == TYPEID_UNIT && !target->IsLevitating())
- target->GetMotionMaster()->MoveFall();
+ if (target->SetCanFly(apply))
+ if (!apply && !target->IsLevitating())
+ target->GetMotionMaster()->MoveFall();
}
void AuraEffect::HandleAuraWaterWalk(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2938,10 +2927,9 @@ void AuraEffect::HandleAuraModIncreaseFlightSpeed(AuraApplication const* aurApp,
// do not remove unit flag if there are more than this auraEffect of that kind on unit on unit
if (mode & AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK && (apply || (!target->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !target->HasAuraType(SPELL_AURA_FLY))))
{
- target->SetCanFly(apply);
-
- if (!apply && target->GetTypeId() == TYPEID_UNIT && !target->IsLevitating())
- target->GetMotionMaster()->MoveFall();
+ if (target->SetCanFly(apply))
+ if (!apply && !target->IsLevitating())
+ target->GetMotionMaster()->MoveFall();
}
//! Someone should clean up these hacks and remove it from this function. It doesn't even belong here.
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 1ca5df6b327..35606989240 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -337,7 +337,8 @@ m_spellInfo(spellproto), m_casterGuid(casterGUID ? casterGUID : caster->GetGUID(
m_castItemGuid(castItem ? castItem->GetGUID() : ObjectGuid::Empty), m_applyTime(time(NULL)),
m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0),
m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1),
-m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr)
+m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr),
+m_procCooldown(std::chrono::steady_clock::time_point::min())
{
if (m_spellInfo->ManaPerSecond || m_spellInfo->ManaPerSecondPerLevel)
m_timeCla = 1 * IN_MILLISECONDS;
@@ -469,7 +470,7 @@ void Aura::_Remove(AuraRemoveMode removeMode)
if (m_dropEvent)
{
- m_dropEvent->to_Abort = true;
+ m_dropEvent->ScheduleAbort();
m_dropEvent = nullptr;
}
}
@@ -789,7 +790,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const
{
uint32 maxProcCharges = m_spellInfo->ProcCharges;
if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()))
- maxProcCharges = procEntry->charges;
+ maxProcCharges = procEntry->Charges;
if (caster)
if (Player* modOwner = caster->GetSpellModOwner())
@@ -1851,22 +1852,17 @@ bool Aura::CanStackWith(Aura const* existingAura) const
return true;
}
-bool Aura::IsProcOnCooldown() const
+bool Aura::IsProcOnCooldown(std::chrono::steady_clock::time_point now) const
{
- /*if (m_procCooldown)
- {
- if (m_procCooldown > time(NULL))
- return true;
- }*/
- return false;
+ return m_procCooldown > now;
}
-void Aura::AddProcCooldown(uint32 /*msec*/)
+void Aura::AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd)
{
- //m_procCooldown = time(NULL) + msec;
+ m_procCooldown = cooldownEnd;
}
-void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo)
+void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now)
{
bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo);
if (!prepare)
@@ -1884,10 +1880,10 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf
ASSERT(procEntry);
// cooldowns should be added to the whole aura (see 51698 area aura)
- AddProcCooldown(procEntry->cooldown);
+ AddProcCooldown(now + procEntry->Cooldown);
}
-bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const
+bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const
{
SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId());
// only auras with spell proc entry can trigger proc
@@ -1899,7 +1895,7 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
return false;
// check proc cooldown
- if (IsProcOnCooldown())
+ if (IsProcOnCooldown(now))
return false;
/// @todo
@@ -1963,16 +1959,16 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
{
- float chance = procEntry.chance;
+ float chance = procEntry.Chance;
// calculate chances depending on unit with caster's data
// so talents modifying chances and judgements will have properly calculated proc chance
if (Unit* caster = GetCaster())
{
// calculate ppm chance if present and we're using weapon
- if (eventInfo.GetDamageInfo() && procEntry.ratePerMinute != 0)
+ if (eventInfo.GetDamageInfo() && procEntry.ProcsPerMinute != 0)
{
uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType());
- chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ratePerMinute, GetSpellInfo());
+ chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ProcsPerMinute, GetSpellInfo());
}
// apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell)
if (Player* modOwner = caster->GetSpellModOwner())
diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h
index 750110dc146..a147957f258 100644
--- a/src/server/game/Spells/Auras/SpellAuras.h
+++ b/src/server/game/Spells/Auras/SpellAuras.h
@@ -203,12 +203,12 @@ class TC_GAME_API Aura
// this subsystem is not yet in use - the core of it is functional, but still some research has to be done
// and some dependant problems fixed before it can replace old proc system (for example cooldown handling)
// currently proc system functionality is implemented in Unit::ProcDamageAndSpell
- bool IsProcOnCooldown() const;
- void AddProcCooldown(uint32 msec);
+ bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const;
+ void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd);
bool IsUsingCharges() const { return m_isUsingCharges; }
void SetUsingCharges(bool val) { m_isUsingCharges = val; }
- void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo);
- bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const;
+ void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now);
+ bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const;
float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const;
void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo);
@@ -269,6 +269,8 @@ class TC_GAME_API Aura
ChargeDropEvent* m_dropEvent;
+ std::chrono::steady_clock::time_point m_procCooldown;
+
private:
Unit::AuraApplicationList m_removedApplications;
};
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 4a36e6d3d72..5e58a602a43 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1564,7 +1564,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex)
if (Creature* creatureTarget = unit->ToCreature())
{
- if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PROJECTILE_COLLISION))
+ if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_COLLIDE_WITH_MISSILES))
continue;
}
}
@@ -2320,8 +2320,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
}
}
+ bool enablePvP = false; // need to check PvP state before spell effects, but act on it afterwards
+
if (spellHitTarget)
{
+ // if target is flagged for pvp also flag caster if a player
+ if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER)
+ enablePvP = true; // Decide on PvP flagging now, but act on it later.
+
SpellMissInfo missInfo2 = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura);
if (missInfo2 != SPELL_MISS_NONE)
{
@@ -2461,6 +2467,10 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
unit->SetStandState(UNIT_STAND_STATE_STAND);
}
+ // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER
+ if (m_spellInfo->HasAttribute(SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER) && unit->GetTypeId() != TYPEID_PLAYER)
+ caster->CastSpell(unit, SPELL_INTERRUPT_NONPLAYER, true);
+
if (spellHitTarget)
{
//AI functions
@@ -2474,8 +2484,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
// Needs to be called after dealing damage/healing to not remove breaking on damage auras
DoTriggersOnSpellHit(spellHitTarget, mask);
- // if target is fallged for pvp also flag caster if a player
- if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER)
+ if (enablePvP)
m_caster->ToPlayer()->UpdatePvP(true);
CallScriptAfterHitHandlers();
@@ -2910,7 +2919,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetSpellModTakingSpell(this, true);
- // Fill cost data (not use power for item casts
+ // Fill cost data (do not use power for item casts)
m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask);
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
@@ -2986,12 +2995,17 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
}
// don't allow channeled spells / spells with cast time to be cast while moving
+ // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
- if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)
+ if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
{
- SendCastResult(SPELL_FAILED_MOVING);
- finish(false);
- return;
+ // 1. Is a channel spell, 2. Has no casttime, 3. And has flag to allow movement during channel
+ if (!(m_spellInfo->IsChanneled() && !m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING)))
+ {
+ SendCastResult(SPELL_FAILED_MOVING);
+ finish(false);
+ return;
+ }
}
// set timer base at cast time
@@ -3219,6 +3233,10 @@ void Spell::cast(bool skipCheck)
return;
}
+ if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET))
+ if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, m_caster->GetPetGUID()))
+ pet->DespawnOrUnsummon();
+
PrepareTriggersExecutedOnHit();
CallScriptOnCastHandlers();
@@ -3528,12 +3546,18 @@ void Spell::update(uint32 difftime)
// check if the player caster has moved before the spell finished
if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) &&
- m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) &&
- (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))
+ m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT &&
+ (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))))
{
// don't cancel for melee, autorepeat, triggered and instant spells
- if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered())
- cancel();
+ if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING)))
+ {
+ // if charmed by creature, trust the AI not to cheat and allow the cast to proceed
+ // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now
+ // however, checking what type of movement the spline is for every single spline would be really expensive
+ if (!m_caster->GetCharmerGUID().IsCreature())
+ cancel();
+ }
}
switch (m_spellState)
@@ -3840,7 +3864,7 @@ void Spell::SendSpellStart()
if (schoolImmunityMask || mechanicImmunityMask)
castFlags |= CAST_FLAG_IMMUNITY;
- if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell)
+ if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count)
castFlags |= CAST_FLAG_PENDING;
if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
@@ -3893,7 +3917,7 @@ void Spell::SendSpellGo()
uint32 castFlags = CAST_FLAG_UNKNOWN_9;
// triggered spells with spell visual != 0
- if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell)
+ if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count)
castFlags |= CAST_FLAG_PENDING;
if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
@@ -4835,7 +4859,7 @@ SpellCastResult Spell::CheckCast(bool strict)
// cancel autorepeat spells if cast start when moving
// (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving())
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()))
{
// skip stuck spell to allow use it in falling case and apply spell limitations at movement
if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) &&
@@ -5173,7 +5197,11 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_OUT_OF_RANGE;
else if (!result || m_preGeneratedPath.GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE))
return SPELL_FAILED_NOPATH;
+ else if (m_preGeneratedPath.IsInvalidDestinationZ(target)) // Check position z, if not in a straight line
+ return SPELL_FAILED_NOPATH;
}
+ else if (m_preGeneratedPath.IsInvalidDestinationZ(target)) // Check position z, if in a straight line
+ return SPELL_FAILED_NOPATH;
m_preGeneratedPath.ReducePathLenghtByDist(objSize); // move back
}
@@ -5289,7 +5317,7 @@ SpellCastResult Spell::CheckCast(bool strict)
switch (SummonProperties->Category)
{
case SUMMON_CATEGORY_PET:
- if (m_caster->GetPetGUID())
+ if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
// intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET
case SUMMON_CATEGORY_PUPPET:
@@ -5305,7 +5333,7 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if (m_targets.GetUnitTarget()->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_BAD_TARGETS;
- if (m_targets.GetUnitTarget()->GetPetGUID())
+ if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_targets.GetUnitTarget()->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
}
break;
@@ -5314,13 +5342,13 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if (m_caster->GetPetGUID()) //let warlock do a replacement summon
{
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK)
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
if (Pet* pet = m_caster->ToPlayer()->GetPet())
pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());
}
- else
+ else if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET))
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
}
@@ -5447,7 +5475,7 @@ SpellCastResult Spell::CheckCast(bool strict)
if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_CHARM
|| m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS)
{
- if (m_caster->GetPetGUID())
+ if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
if (m_caster->GetCharmGUID())
@@ -5477,7 +5505,7 @@ SpellCastResult Spell::CheckCast(bool strict)
}
case SPELL_AURA_MOUNTED:
{
- if (m_caster->IsInWater())
+ if (m_caster->IsInWater() && m_spellInfo->HasAura(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED))
return SPELL_FAILED_ONLY_ABOVEWATER;
// Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells
@@ -5593,7 +5621,14 @@ SpellCastResult Spell::CheckPetCast(Unit* target)
m_targets.SetUnitTarget(target);
}
- // cooldown
+ // check power requirement
+ // this would be zero until ::prepare normally, we set it here (it gets reset in ::prepare)
+ m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask);
+ SpellCastResult failReason = CheckPower();
+ if (failReason != SPELL_CAST_OK)
+ return failReason;
+
+ // check cooldown
if (Creature* creatureCaster = m_caster->ToCreature())
if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo))
return SPELL_FAILED_NOT_READY;
@@ -5736,6 +5771,9 @@ SpellCastResult Spell::CheckCasterAuras() const
bool Spell::CanAutoCast(Unit* target)
{
+ if (!target)
+ return (CheckPetCast(target) == SPELL_CAST_OK);
+
ObjectGuid targetguid = target->GetGUID();
// check if target already has the same or a more powerful aura
@@ -5772,16 +5810,19 @@ bool Spell::CanAutoCast(Unit* target)
}
SpellCastResult result = CheckPetCast(target);
-
if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
{
+ // do not check targets for ground-targeted spells (we target them on top of the intended target anyway)
+ if (GetSpellInfo()->ExplicitTargetMask & TARGET_FLAG_DEST_LOCATION)
+ return true;
SelectSpellTargets();
//check if among target units, our WANTED target is as well (->only self cast spells return false)
- for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
+ for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
if (ihit->targetGUID == targetguid)
return true;
}
- return false; //target invalid
+ // either the cast failed or the intended target wouldn't be hit
+ return false;
}
SpellCastResult Spell::CheckRange(bool strict)
@@ -5790,43 +5831,76 @@ SpellCastResult Spell::CheckRange(bool strict)
if (!strict && m_casttime == 0)
return SPELL_CAST_OK;
- uint32 range_type = 0;
+ Unit* target = m_targets.GetUnitTarget();
+ float minRange = 0.0f;
+ float maxRange = 0.0f;
+ float rangeMod = 0.0f;
+ if (strict && IsNextMeleeSwingSpell())
+ maxRange = 100.0f;
+ else if (m_spellInfo->RangeEntry)
+ {
+ if (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE)
+ {
+ rangeMod = m_caster->GetCombatReach() + 4.0f / 3.0f;
+ if (target)
+ rangeMod += target->GetCombatReach();
+ else
+ rangeMod += m_caster->GetCombatReach();
- if (m_spellInfo->RangeEntry)
- {
- // check needed by 68766 51693 - both spells are cast on enemies and have 0 max range
- // these are triggered by other spells - possibly we should omit range check in that case?
- if (m_spellInfo->RangeEntry->ID == 1)
- return SPELL_CAST_OK;
+ rangeMod = std::max(rangeMod, NOMINAL_MELEE_RANGE);
+ }
+ else
+ {
+ float meleeRange = 0.0f;
+ if (m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED)
+ {
+ meleeRange = m_caster->GetCombatReach() + 4.0f / 3.0f;
+ if (target)
+ meleeRange += target->GetCombatReach();
+ else
+ meleeRange += m_caster->GetCombatReach();
+
+ meleeRange = std::max(meleeRange, NOMINAL_MELEE_RANGE);
+ }
+
+ minRange = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange;
+ maxRange = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo);
+
+ if (target || m_targets.GetCorpseTarget())
+ {
+ rangeMod = m_caster->GetCombatReach();
+ if (target)
+ rangeMod += target->GetCombatReach();
- range_type = m_spellInfo->RangeEntry->type;
+ if (minRange > 0.0f && !(m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED))
+ minRange += rangeMod;
+ }
+ }
+
+ if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() &&
+ (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER))
+ rangeMod += 5.0f / 3.0f;
}
- Unit* target = m_targets.GetUnitTarget();
- float max_range = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo);
- float min_range = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo);
+ if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && m_caster->GetTypeId() == TYPEID_PLAYER)
+ if (Item* ranged = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK, true))
+ maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f;
if (Player* modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, this);
+
+ maxRange += rangeMod;
+
+ minRange *= minRange;
+ maxRange *= maxRange;
if (target && target != m_caster)
{
- if (range_type == SPELL_RANGE_MELEE)
- {
- // Because of lag, we can not check too strictly here.
- if (!m_caster->IsWithinMeleeRange(target, max_range))
- return SPELL_FAILED_OUT_OF_RANGE;
- }
- else if (!m_caster->IsWithinCombatRange(target, max_range))
- return SPELL_FAILED_OUT_OF_RANGE; //0x5A;
+ if (m_caster->GetExactDistSq(target) > maxRange)
+ return SPELL_FAILED_OUT_OF_RANGE;
- if (range_type == SPELL_RANGE_RANGED)
- {
- if (m_caster->IsWithinMeleeRange(target))
- return SPELL_FAILED_TOO_CLOSE;
- }
- else if (min_range && m_caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0
- return SPELL_FAILED_TOO_CLOSE;
+ if (minRange > 0.0f && m_caster->GetExactDistSq(target) < minRange)
+ return SPELL_FAILED_OUT_OF_RANGE;
if (m_caster->GetTypeId() == TYPEID_PLAYER &&
(m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target))
@@ -5835,10 +5909,10 @@ SpellCastResult Spell::CheckRange(bool strict)
if (m_targets.HasDst() && !m_targets.HasTraj())
{
- if (!m_caster->IsWithinDist3d(m_targets.GetDstPos(), max_range))
- return SPELL_FAILED_OUT_OF_RANGE;
- if (min_range && m_caster->IsWithinDist3d(m_targets.GetDstPos(), min_range))
- return SPELL_FAILED_TOO_CLOSE;
+ if (m_caster->GetExactDistSq(m_targets.GetDstPos()) > maxRange)
+ return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT;
+ if (minRange > 0.0f && m_caster->GetExactDistSq(m_targets.GetDstPos()) < minRange)
+ return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT;
}
return SPELL_CAST_OK;
@@ -6928,7 +7002,7 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk
reqSkillValue = lockInfo->Skill[j];
// castitem check: rogue using skeleton keys. the skill values should not be added in this case.
- skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ?
+ skillValue = m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER ?
0 : m_caster->ToPlayer()->GetSkillValue(skillId);
// skill bonus provided by casting spell (mostly item spells)
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index b7134283ccb..46384fc54ec 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -218,6 +218,8 @@ enum SpellEffectHandleMode
typedef std::list<std::pair<uint32, ObjectGuid>> DispelList;
+static const uint32 SPELL_INTERRUPT_NONPLAYER = 32747;
+
class TC_GAME_API Spell
{
friend void Unit::SetCurrentCastSpell(Spell* pSpell);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 51b6db197db..b0e3b6deeee 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -556,7 +556,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex)
if (uint32 combo = player->GetComboPoints())
{
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
- damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f));
+ damage += std::lroundf(ap * combo * 0.07f);
// Eviscerate and Envenom Bonus Damage (item set effect)
if (m_caster->HasAura(37169))
@@ -596,17 +596,13 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex)
if (Player* caster = m_caster->ToPlayer())
{
// Add Ammo and Weapon damage plus RAP * 0.1
- if (Item* item = caster->GetWeaponForAttack(RANGED_ATTACK))
- {
- ItemTemplate const* weaponTemplate = item->GetTemplate();
- float dmg_min = weaponTemplate->Damage[0].DamageMin;
- float dmg_max = weaponTemplate->Damage[0].DamageMax;
- if (dmg_max == 0.0f && dmg_min > dmg_max)
- damage += int32(dmg_min);
- else
- damage += irand(int32(dmg_min), int32(dmg_max));
- damage += int32(caster->GetAmmoDPS() * weaponTemplate->Delay * 0.001f);
- }
+ float dmg_min = caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE);
+ float dmg_max = caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE);
+ if (dmg_max == 0.0f && dmg_min > dmg_max)
+ damage += int32(dmg_min);
+ else
+ damage += irand(int32(dmg_min), int32(dmg_max));
+ damage += int32(caster->GetAmmoDPS() * caster->GetAttackTime(RANGED_ATTACK) * 0.001f);
}
}
break;
@@ -1683,6 +1679,8 @@ void Spell::EffectCreateItem2(SpellEffIndex effIndex)
}
else
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell); // create some random items
+
+ player->UpdateCraftSkill(m_spellInfo->Id);
}
/// @todo ExecuteLogEffectCreateItem(i, m_spellInfo->Effects[i].ItemType);
}
@@ -3019,6 +3017,12 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
//OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
//OldSummon->SetMap(owner->GetMap());
//owner->GetMap()->Add(OldSummon->ToCreature());
+ if (OldSummon->getPetType() == SUMMON_PET)
+ {
+ OldSummon->SetHealth(OldSummon->GetMaxHealth());
+ OldSummon->SetPower(OldSummon->getPowerType(),
+ OldSummon->GetMaxPower(OldSummon->getPowerType()));
+ }
if (owner->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled())
owner->ToPlayer()->PetSpellInitialize();
@@ -3505,7 +3509,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex)
Map* map = target->GetMap();
if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, map,
- m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ m_caster->GetPhaseMask(), Position(x, y, z, target->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY))
{
delete pGameObj;
return;
@@ -3530,7 +3534,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex)
{
GameObject* linkedGO = new GameObject;
if (linkedGO->Create(map->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, map,
- m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ m_caster->GetPhaseMask(), Position(x, y, z, target->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY))
{
linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
linkedGO->SetSpellId(m_spellInfo->Id);
@@ -4119,13 +4123,16 @@ void Spell::EffectDuel(SpellEffIndex effIndex)
uint32 gameobject_id = m_spellInfo->Effects[effIndex].MiscValue;
- Map* map = m_caster->GetMap();
- if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id,
- map, m_caster->GetPhaseMask(),
- m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2,
- m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2,
+ Position const pos =
+ {
+ m_caster->GetPositionX() + (unitTarget->GetPositionX() - m_caster->GetPositionX()) / 2,
+ m_caster->GetPositionY() + (unitTarget->GetPositionY() - m_caster->GetPositionY()) / 2,
m_caster->GetPositionZ(),
- m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
+ m_caster->GetOrientation()
+ };
+
+ Map* map = m_caster->GetMap();
+ if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, map, m_caster->GetPhaseMask(), pos, G3D::Quat(), 0, GO_STATE_READY))
{
delete pGameObj;
return;
@@ -4458,7 +4465,7 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex)
Map* map = m_caster->GetMap();
if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), go_id, map,
- m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
+ m_caster->GetPhaseMask(), Position(x, y, z, m_caster->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY))
{
delete go;
return;
@@ -5087,7 +5094,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex)
GameObject* pGameObj = new GameObject;
if (!pGameObj->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), name_id, cMap,
- m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ m_caster->GetPhaseMask(), Position(fx, fy, fz, m_caster->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY))
{
delete pGameObj;
return;
@@ -5153,7 +5160,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex)
{
GameObject* linkedGO = new GameObject;
if (linkedGO->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, cMap,
- m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ m_caster->GetPhaseMask(), Position(fx, fy, fz, m_caster->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY))
{
linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
//linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel());
@@ -5475,7 +5482,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l)
{
// Check if both runes are on cd as that is the only time when this needs to come into effect
- if ((player->GetRuneCooldown(l) && player->GetCurrentRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetCurrentRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)))
+ if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetBaseRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)))
{
// Should always update the rune with the lowest cd
if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1))
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index f6078bd063f..ac157b48783 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -525,10 +525,10 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const
float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const
{
- float multiplier = DamageMultiplier;
+ float multiplierPercent = DamageMultiplier * 100.0f;
if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL))
- modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplier, spell);
- return multiplier;
+ modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell);
+ return multiplierPercent / 100.0f;
}
bool SpellEffectInfo::HasRadius() const
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 50f750f2ca5..ed0d95f0a58 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -955,11 +955,11 @@ SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const
bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
{
// proc type doesn't match
- if (!(eventInfo.GetTypeMask() & procEntry.typeMask))
+ if (!(eventInfo.GetTypeMask() & procEntry.ProcFlags))
return false;
// check XP or honor target requirement
- if (procEntry.attributesMask & PROC_ATTR_REQ_EXP_OR_HONOR)
+ if (procEntry.AttributesMask & PROC_ATTR_REQ_EXP_OR_HONOR)
if (Player* actor = eventInfo.GetActor()->ToPlayer())
if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget()))
return false;
@@ -969,7 +969,7 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
return true;
// check school mask (if set) for other trigger types
- if (procEntry.schoolMask && !(eventInfo.GetSchoolMask() & procEntry.schoolMask))
+ if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask))
return false;
// check spell family name/flags (if set) for spells
@@ -977,31 +977,31 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
{
SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo();
- if (procEntry.spellFamilyName && eventSpellInfo && (procEntry.spellFamilyName != eventSpellInfo->SpellFamilyName))
+ if (procEntry.SpellFamilyName && eventSpellInfo && (procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName))
return false;
- if (procEntry.spellFamilyMask && eventSpellInfo && !(procEntry.spellFamilyMask & eventSpellInfo->SpellFamilyFlags))
+ if (procEntry.SpellFamilyMask && eventSpellInfo && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags))
return false;
}
// check spell type mask (if set)
if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))
{
- if (procEntry.spellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.spellTypeMask))
+ if (procEntry.SpellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.SpellTypeMask))
return false;
}
// check spell phase mask
if (eventInfo.GetTypeMask() & REQ_SPELL_PHASE_PROC_FLAG_MASK)
{
- if (!(eventInfo.GetSpellPhaseMask() & procEntry.spellPhaseMask))
+ if (!(eventInfo.GetSpellPhaseMask() & procEntry.SpellPhaseMask))
return false;
}
// check hit mask (on taken hit or on done hit, but not on spell cast phase)
if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) || ((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) && !(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST)))
{
- uint32 hitMask = procEntry.hitMask;
+ uint32 hitMask = procEntry.HitMask;
// get default values if hit mask not set
if (!hitMask)
{
@@ -1929,8 +1929,11 @@ void SpellMgr::LoadSpellProcs()
mSpellProcMap.clear(); // need for reload case
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
- QueryResult result = WorldDatabase.Query("SELECT spellId, schoolMask, spellFamilyName, spellFamilyMask0, spellFamilyMask1, spellFamilyMask2, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc");
+ // 0 1 2 3 4 5
+ QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, "
+ // 6 7 8 9 10 11 12 13 14
+ "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
+
if (!result)
{
TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty.");
@@ -1972,21 +1975,20 @@ void SpellMgr::LoadSpellProcs()
SpellProcEntry baseProcEntry;
- baseProcEntry.schoolMask = fields[1].GetInt8();
- baseProcEntry.spellFamilyName = fields[2].GetUInt16();
- baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32();
- baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32();
- baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32();
- baseProcEntry.typeMask = fields[6].GetUInt32();
- baseProcEntry.spellTypeMask = fields[7].GetUInt32();
- baseProcEntry.spellPhaseMask = fields[8].GetUInt32();
- baseProcEntry.hitMask = fields[9].GetUInt32();
- baseProcEntry.attributesMask = fields[10].GetUInt32();
- baseProcEntry.ratePerMinute = fields[11].GetFloat();
- baseProcEntry.chance = fields[12].GetFloat();
- float cooldown = fields[13].GetFloat();
- baseProcEntry.cooldown = uint32(cooldown);
- baseProcEntry.charges = fields[14].GetUInt32();
+ baseProcEntry.SchoolMask = fields[1].GetInt8();
+ baseProcEntry.SpellFamilyName = fields[2].GetUInt16();
+ baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32();
+ baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32();
+ baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32();
+ baseProcEntry.ProcFlags = fields[6].GetUInt32();
+ baseProcEntry.SpellTypeMask = fields[7].GetUInt32();
+ baseProcEntry.SpellPhaseMask = fields[8].GetUInt32();
+ baseProcEntry.HitMask = fields[9].GetUInt32();
+ baseProcEntry.AttributesMask = fields[10].GetUInt32();
+ baseProcEntry.ProcsPerMinute = fields[11].GetFloat();
+ baseProcEntry.Chance = fields[12].GetFloat();
+ baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32());
+ baseProcEntry.Charges = fields[14].GetUInt8();
while (spellInfo)
{
@@ -1999,56 +2001,46 @@ void SpellMgr::LoadSpellProcs()
SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
// take defaults from dbcs
- if (!procEntry.typeMask)
- procEntry.typeMask = spellInfo->ProcFlags;
- if (!procEntry.charges)
- procEntry.charges = spellInfo->ProcCharges;
- if (!procEntry.chance && !procEntry.ratePerMinute)
- procEntry.chance = float(spellInfo->ProcChance);
+ if (!procEntry.ProcFlags)
+ procEntry.ProcFlags = spellInfo->ProcFlags;
+ if (!procEntry.Charges)
+ procEntry.Charges = spellInfo->ProcCharges;
+ if (!procEntry.Chance && !procEntry.ProcsPerMinute)
+ procEntry.Chance = float(spellInfo->ProcChance);
// validate data
- if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellInfo->Id, procEntry.schoolMask);
- if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16))
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellInfo->Id, procEntry.spellFamilyName);
- if (procEntry.chance < 0)
- {
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `chance` field", spellInfo->Id);
- procEntry.chance = 0;
- }
- if (procEntry.ratePerMinute < 0)
+ if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask);
+ if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16))
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName);
+ if (procEntry.Chance < 0)
{
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ratePerMinute` field", spellInfo->Id);
- procEntry.ratePerMinute = 0;
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id);
+ procEntry.Chance = 0;
}
- if (cooldown < 0)
+ if (procEntry.ProcsPerMinute < 0)
{
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `cooldown` field", spellInfo->Id);
- procEntry.cooldown = 0;
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id);
+ procEntry.ProcsPerMinute = 0;
}
- if (procEntry.chance == 0 && procEntry.ratePerMinute == 0)
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `chance` and `ratePerMinute` values defined, proc will not be triggered", spellInfo->Id);
- if (procEntry.charges > 99)
- {
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a too big `charges` field value.", spellInfo->Id);
- procEntry.charges = 99;
- }
- if (!procEntry.typeMask)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `typeMask` value defined, proc will not be triggered.", spellInfo->Id);
- if (procEntry.spellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellInfo->Id, procEntry.spellTypeMask);
- if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it will not be used for the defined `typeMask` value.", spellInfo->Id);
- if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `spellPhaseMask` value defined, but it is required for the defined `typeMask` value. Proc will not be triggered.", spellInfo->Id);
- if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellInfo->Id, procEntry.spellPhaseMask);
- if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK))
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `spellPhaseMask` value defined, but it will not be used for the defined `typeMask` value.", spellInfo->Id);
- if (procEntry.hitMask & ~PROC_HIT_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", spellInfo->Id, procEntry.hitMask);
- if (procEntry.hitMask && !(procEntry.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH)))))
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `hitMask` value defined, but it will not be used for defined `typeMask` and `spellPhaseMask` values.", spellInfo->Id);
+ if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0)
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id);
+ if (!procEntry.ProcFlags)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id);
+ if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask);
+ if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id);
+ if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id);
+ if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask);
+ if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK))
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id);
+ if (procEntry.HitMask & ~PROC_HIT_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask);
+ if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH)))))
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id);
mSpellProcMap[spellInfo->Id] = procEntry;
@@ -3081,6 +3073,13 @@ void SpellMgr::LoadSpellInfoCorrections()
case 53385: // Divine Storm (Damage)
spellInfo->MaxAffectedTargets = 4;
break;
+ case 53480: // Roar of Sacrifice
+ // missing spell effect 2 data, taken from 4.3.4
+ spellInfo->Effects[EFFECT_1].Effect = SPELL_EFFECT_APPLY_AURA;
+ spellInfo->Effects[EFFECT_1].ApplyAuraName = SPELL_AURA_DUMMY;
+ spellInfo->Effects[EFFECT_1].MiscValue = 127;
+ spellInfo->Effects[EFFECT_1].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ALLY);
+ break;
case 42005: // Bloodboil
case 38296: // Spitfire Totem
case 37676: // Insidious Whisper
@@ -3346,6 +3345,24 @@ void SpellMgr::LoadSpellInfoCorrections()
//! HACK: This spell break quest complete for alliance and on retail not used °_O
spellInfo->Effects[EFFECT_0].Effect = 0;
break;
+ case 47476: // Deathknight - Strangulate
+ case 15487: // Priest - Silence
+ case 5211: // Druid - Bash - R1
+ case 6798: // Druid - Bash - R2
+ case 8983: // Druid - Bash - R3
+ spellInfo->AttributesEx7 |= SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER;
+ break;
+ case 42490: // Energized!
+ case 42492: // Cast Energized
+ spellInfo->AttributesEx |= SPELL_ATTR1_NO_THREAT;
+ break;
+ case 46842: // Flame Ring
+ case 46836: // Flame Patch
+ spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo();
+ break;
+ case 29726: // Test Ribbon Pole Channel
+ spellInfo->InterruptFlags &= ~AURA_INTERRUPT_FLAG_CAST;
+ break;
// VIOLET HOLD SPELLS
//
case 54258: // Water Globule (Ichoron)
@@ -3704,9 +3721,17 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE;
spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS;
break;
- case 75888: // Awaken Flames
- case 75889: // Awaken Flames
- spellInfo->AttributesEx |= SPELL_ATTR1_CANT_TARGET_SELF;
+ case 75875: // Combustion and Consumption Heroic versions lacks radius data
+ spellInfo->Effects[EFFECT_0].Mechanic = MECHANIC_NONE;
+ spellInfo->Effects[EFFECT_1].Mechanic = MECHANIC_SNARE;
+ spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_6_YARDS);
+ break;
+ case 75884:
+ spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_6_YARDS);
+ // No break
+ case 75883:
+ case 75876:
+ spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_6_YARDS);
break;
// ENDOF RUBY SANCTUM SPELLS
//
@@ -3755,12 +3780,6 @@ void SpellMgr::LoadSpellInfoCorrections()
case 24314: // Threatening Gaze
spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_JUMP;
break;
- case 45257: // Using Steam Tonk Controller
- case 45440: // Steam Tonk Controller
- case 60256: // Collect Sample
- // Crashes client on pressing ESC
- spellInfo->AttributesEx4 &= ~SPELL_ATTR4_CAN_CAST_WHILE_CASTING;
- break;
// ISLE OF CONQUEST SPELLS
//
case 66551: // Teleport
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index fea7513b092..75da933636c 100644
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -266,7 +266,7 @@ enum ProcFlagsHit
PROC_HIT_REFLECT = 0x0000800,
PROC_HIT_INTERRUPT = 0x0001000, // (not used atm)
PROC_HIT_FULL_BLOCK = 0x0002000,
- PROC_HIT_MASK_ALL = 0x2FFF
+ PROC_HIT_MASK_ALL = 0x0002FFF
};
enum ProcAttributes
@@ -290,18 +290,18 @@ typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap;
struct SpellProcEntry
{
- uint32 schoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
- uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName
- flag96 spellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags
- uint32 typeMask; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags
- uint32 spellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
- uint32 spellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase
- uint32 hitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit
- uint32 attributesMask; // bitmask, see ProcAttributes
- float ratePerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60
- float chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if perMinuteRate set
- uint32 cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
- uint32 charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
+ uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
+ uint32 SpellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName
+ flag96 SpellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags
+ uint32 ProcFlags; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags
+ uint32 SpellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
+ uint32 SpellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase
+ uint32 HitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit
+ uint32 AttributesMask; // bitmask, see ProcAttributes
+ float ProcsPerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60
+ float Chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if ProcsPerMinute set
+ Milliseconds Cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
+ uint32 Charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
};
typedef std::unordered_map<uint32, SpellProcEntry> SpellProcMap;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index d84fe11383f..19abfbe3152 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -58,6 +58,7 @@
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "SmartAI.h"
+#include "Metric.h"
#include "TicketMgr.h"
#include "TransportMgr.h"
#include "Unit.h"
@@ -396,6 +397,7 @@ void World::LoadConfigSettings(bool reload)
return;
}
sLog->LoadFromConfig();
+ sMetric->LoadFromConfigs();
}
///- Read the player limit and the Message of the day from the config file
@@ -593,7 +595,10 @@ void World::LoadConfigSettings(bool reload)
}
m_int_configs[CONFIG_CHAT_CHANNEL_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Channel", 1);
m_int_configs[CONFIG_CHAT_WHISPER_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Whisper", 1);
+ m_int_configs[CONFIG_CHAT_EMOTE_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Emote", 1);
m_int_configs[CONFIG_CHAT_SAY_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Say", 1);
+ m_int_configs[CONFIG_CHAT_YELL_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Yell", 1);
+ m_int_configs[CONFIG_PARTY_LEVEL_REQ] = sConfigMgr->GetIntDefault("PartyLevelReq", 1);
m_int_configs[CONFIG_TRADE_LEVEL_REQ] = sConfigMgr->GetIntDefault("LevelReq.Trade", 1);
m_int_configs[CONFIG_TICKET_LEVEL_REQ] = sConfigMgr->GetIntDefault("LevelReq.Ticket", 1);
m_int_configs[CONFIG_AUCTION_LEVEL_REQ] = sConfigMgr->GetIntDefault("LevelReq.Auction", 1);
@@ -659,9 +664,8 @@ void World::LoadConfigSettings(bool reload)
m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f);
m_float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetFloatDefault("MaxRecruitAFriendBonusDistance", 100.0f);
- /// @todo Add MonsterSight and GuarderSight (with meaning) in worldserver.conf or put them as define
+ /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define
m_float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetFloatDefault("MonsterSight", 50.0f);
- m_float_configs[CONFIG_SIGHT_GUARDER] = sConfigMgr->GetFloatDefault("GuarderSight", 50.0f);
if (reload)
{
@@ -1059,6 +1063,17 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false);
m_bool_configs[CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE] = sConfigMgr->GetBoolDefault("Battleground.StoreStatistics.Enable", false);
m_bool_configs[CONFIG_BATTLEGROUND_TRACK_DESERTERS] = sConfigMgr->GetBoolDefault("Battleground.TrackDeserters.Enable", false);
+ m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = sConfigMgr->GetIntDefault("Battleground.ReportAFK", 3);
+ if (m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] < 1)
+ {
+ TC_LOG_ERROR("server.loading", "Battleground.ReportAFK (%d) must be >0. Using 3 instead.", m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK]);
+ m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = 3;
+ }
+ if (m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] > 9)
+ {
+ TC_LOG_ERROR("server.loading", "Battleground.ReportAFK (%d) must be <10. Using 3 instead.", m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK]);
+ m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = 3;
+ }
m_int_configs[CONFIG_BATTLEGROUND_INVITATION_TYPE] = sConfigMgr->GetIntDefault ("Battleground.InvitationType", 0);
m_int_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfigMgr->GetIntDefault ("Battleground.PrematureFinishTimer", 5 * MINUTE * IN_MILLISECONDS);
m_int_configs[CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH] = sConfigMgr->GetIntDefault ("Battleground.PremadeGroupWaitForMatch", 30 * MINUTE * IN_MILLISECONDS);
@@ -1330,6 +1345,9 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_HOTSWAP_INSTALL_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableInstall", true);
m_bool_configs[CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnablePrefixCorrection", true);
+ // prevent character rename on character customization
+ m_bool_configs[CONFIG_PREVENT_RENAME_CUSTOMIZATION] = sConfigMgr->GetBoolDefault("PreventRenameCharacterOnCustomization", false);
+
// call ScriptMgr if we're reloading the configuration
if (reload)
sScriptMgr->OnConfigLoad(reload);
@@ -1938,6 +1956,8 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.worldserver", "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000));
+ TC_METRIC_EVENT("events", "World initialized", "World initialized in " + std::to_string(startupDuration / 60000) + " minutes " + std::to_string((startupDuration % 60000) / 1000) + " seconds");
+
if (uint32 realmId = sConfigMgr->GetIntDefault("RealmID", 0)) // 0 reserved for auth
sLog->SetRealmId(realmId);
}
@@ -2252,6 +2272,10 @@ void World::Update(uint32 diff)
ProcessCliCommands();
sScriptMgr->OnWorldUpdate(diff);
+
+ // Stats logger update
+ sMetric->Update();
+ TC_METRIC_VALUE("update_time_diff", diff);
}
void World::ForceGameEventUpdate()
@@ -2877,16 +2901,20 @@ void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount)
uint32 accountId = fields[0].GetUInt32();
uint8 charCount = uint8(fields[1].GetUInt64());
+ SQLTransaction trans = LoginDatabase.BeginTransaction();
+
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM);
stmt->setUInt32(0, accountId);
stmt->setUInt32(1, realm.Id.Realm);
- LoginDatabase.Execute(stmt);
+ trans->Append(stmt);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS);
stmt->setUInt8(0, charCount);
stmt->setUInt32(1, accountId);
stmt->setUInt32(2, realm.Id.Realm);
- LoginDatabase.Execute(stmt);
+ trans->Append(stmt);
+
+ LoginDatabase.CommitTransaction(trans);
}
}
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 330e78cf510..5850063bdc8 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -175,6 +175,7 @@ enum WorldBoolConfigs
CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED,
CONFIG_HOTSWAP_INSTALL_ENABLED,
CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED,
+ CONFIG_PREVENT_RENAME_CUSTOMIZATION,
BOOL_CONFIG_VALUE_COUNT
};
@@ -183,7 +184,6 @@ enum WorldFloatConfigs
CONFIG_GROUP_XP_DISTANCE = 0,
CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE,
CONFIG_SIGHT_MONSTER,
- CONFIG_SIGHT_GUARDER,
CONFIG_LISTEN_RANGE_SAY,
CONFIG_LISTEN_RANGE_TEXTEMOTE,
CONFIG_LISTEN_RANGE_YELL,
@@ -281,7 +281,10 @@ enum WorldIntConfigs
CONFIG_CHAT_STRICT_LINK_CHECKING_KICK,
CONFIG_CHAT_CHANNEL_LEVEL_REQ,
CONFIG_CHAT_WHISPER_LEVEL_REQ,
+ CONFIG_CHAT_EMOTE_LEVEL_REQ,
CONFIG_CHAT_SAY_LEVEL_REQ,
+ CONFIG_CHAT_YELL_LEVEL_REQ,
+ CONFIG_PARTY_LEVEL_REQ,
CONFIG_TRADE_LEVEL_REQ,
CONFIG_TICKET_LEVEL_REQ,
CONFIG_AUCTION_LEVEL_REQ,
@@ -297,6 +300,7 @@ enum WorldIntConfigs
CONFIG_BATTLEGROUND_INVITATION_TYPE,
CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER,
CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH,
+ CONFIG_BATTLEGROUND_REPORT_AFK,
CONFIG_ARENA_MAX_RATING_DIFFERENCE,
CONFIG_ARENA_RATING_DISCARD_TIMER,
CONFIG_ARENA_RATED_UPDATE_TIMER,
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index ca4dd814e01..570f4587ae8 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -35,8 +35,10 @@ EndScriptData */
#include "Language.h"
#include "MapManager.h"
#include "M2Stores.h"
+#include "BattlefieldMgr.h"
#include <fstream>
+#include <limits>
class debug_commandscript : public CommandScript
{
@@ -95,7 +97,8 @@ public:
{ "transport", rbac::RBAC_PERM_COMMAND_DEBUG_TRANSPORT, false, &HandleDebugTransportCommand, "" },
{ "loadcells", rbac::RBAC_PERM_COMMAND_DEBUG_LOADCELLS, false, &HandleDebugLoadCellsCommand, "" },
{ "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" },
- { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" }
+ { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" },
+ { "neargraveyard", rbac::RBAC_PERM_COMMAND_NEARGRAVEYARD, false, &HandleDebugNearGraveyard, "" },
};
static std::vector<ChatCommand> commandTable =
{
@@ -1485,6 +1488,53 @@ public:
sInstanceSaveMgr->ForceGlobalReset(mEntry->MapID, Difficulty(difficulty));
return true;
}
+
+ static bool HandleDebugNearGraveyard(ChatHandler* handler, char const* args)
+ {
+ Player* player = handler->GetSession()->GetPlayer();
+ const WorldSafeLocsEntry* nearestLoc = nullptr;
+
+ if (stricmp(args, "linked"))
+ {
+ if (Battleground* bg = player->GetBattleground())
+ nearestLoc = bg->GetClosestGraveYard(player);
+ else
+ {
+ if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetZoneId()))
+ nearestLoc = bf->GetClosestGraveYard(player);
+ else
+ nearestLoc = sObjectMgr->GetClosestGraveYard(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam());
+ }
+ }
+ else
+ {
+ float x = player->GetPositionX();
+ float y = player->GetPositionY();
+ float z = player->GetPositionZ();
+ float distNearest = std::numeric_limits<float>::max();
+
+ for (uint32 i = 0; i < sWorldSafeLocsStore.GetNumRows(); ++i)
+ {
+ const WorldSafeLocsEntry* loc = sWorldSafeLocsStore.LookupEntry(i);
+ if (loc && loc->map_id == player->GetMapId())
+ {
+ float dist = (loc->x - x) * (loc->x - x) + (loc->y - y) * (loc->y - y) + (loc->z - z) * (loc->z - z);
+ if (dist < distNearest)
+ {
+ distNearest = dist;
+ nearestLoc = loc;
+ }
+ }
+ }
+ }
+
+ if (nearestLoc)
+ handler->PSendSysMessage(LANG_COMMAND_NEARGRAVEYARD, nearestLoc->ID, nearestLoc->x, nearestLoc->y, nearestLoc->z);
+ else
+ handler->PSendSysMessage(LANG_COMMAND_NEARGRAVEYARD_NOTFOUND);
+
+ 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 729dac0ad6f..c4ed6f650b9 100644
--- a/src/server/scripts/Commands/cs_gobject.cpp
+++ b/src/server/scripts/Commands/cs_gobject.cpp
@@ -139,16 +139,12 @@ public:
}
Player* player = handler->GetSession()->GetPlayer();
- float x = float(player->GetPositionX());
- float y = float(player->GetPositionY());
- float z = float(player->GetPositionZ());
- float o = float(player->GetOrientation());
Map* map = player->GetMap();
GameObject* object = new GameObject;
ObjectGuid::LowType guidLow = map->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->GetPhaseMaskForSpawn(), *player, G3D::Quat(), 255, GO_STATE_READY))
{
delete object;
return false;
@@ -179,7 +175,7 @@ public:
/// @todo is it really necessary to add both the real and DB table guid here ?
sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGOData(guidLow));
- handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, x, y, z);
+ handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
return true;
}
@@ -201,14 +197,7 @@ public:
if (spawntime)
spawntm = atoi((char*)spawntime);
- float x = player->GetPositionX();
- float y = player->GetPositionY();
- float z = player->GetPositionZ();
- float ang = player->GetOrientation();
-
- float rot2 = std::sin(ang/2);
- float rot3 = std::cos(ang/2);
-
+ G3D::Quat rotation = G3D::Matrix3::fromEulerAnglesZYX(player->GetOrientation(), 0.f, 0.f);
uint32 objectId = atoi(id);
if (!sObjectMgr->GetGameObjectTemplate(objectId))
@@ -218,7 +207,7 @@ public:
return false;
}
- player->SummonGameObject(objectId, x, y, z, ang, 0, 0, rot2, rot3, spawntm);
+ player->SummonGameObject(objectId, *player, rotation, spawntm);
return true;
}
@@ -415,20 +404,30 @@ public:
}
char* orientation = strtok(NULL, " ");
- float o;
+ float oz = 0.f, oy = 0.f, ox = 0.f;
if (orientation)
- o = (float)atof(orientation);
+ {
+ oz = float(atof(orientation));
+
+ orientation = strtok(NULL, " ");
+ if (orientation)
+ {
+ oy = float(atof(orientation));
+ orientation = strtok(NULL, " ");
+ if (orientation)
+ ox = float(atof(orientation));
+ }
+ }
else
{
Player* player = handler->GetSession()->GetPlayer();
- o = player->GetOrientation();
+ oz = player->GetOrientation();
}
Map* map = object->GetMap();
-
- object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), o);
- object->UpdateRotationFields();
+ object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ());
+ object->SetWorldRotationAngles(oz, oy, ox);
object->SaveToDB();
// Generate a completely new spawn with new guid
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index a98f9f4bf9c..16217fbaea6 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -703,7 +703,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);
@@ -711,7 +711,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)
{
@@ -728,13 +735,13 @@ 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);
- 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;
}
@@ -2124,17 +2131,9 @@ public:
}
return true;
}
- /*
- ComeToMe command REQUIRED for 3rd party scripting library to have access to PointMovementGenerator
- Without this function 3rd party scripting library will get linking errors (unresolved external)
- when attempting to use the PointMovementGenerator
- */
- static bool HandleComeToMeCommand(ChatHandler* handler, char const* args)
- {
- char const* newFlagStr = strtok((char*)args, " ");
- if (!newFlagStr)
- return false;
+ static bool HandleComeToMeCommand(ChatHandler* handler, char const* /*args*/)
+ {
Creature* caster = handler->getSelectedCreature();
if (!caster)
{
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 438d05c5687..38aa96a6a66 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -113,7 +113,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] =
{
CREATE_NAMED_ENUM(UNIT_FLAG_SERVER_CONTROLLED),
CREATE_NAMED_ENUM(UNIT_FLAG_NON_ATTACKABLE),
- CREATE_NAMED_ENUM(UNIT_FLAG_DISABLE_MOVE),
+ CREATE_NAMED_ENUM(UNIT_FLAG_REMOVE_CLIENT_CONTROL),
CREATE_NAMED_ENUM(UNIT_FLAG_PVP_ATTACKABLE),
CREATE_NAMED_ENUM(UNIT_FLAG_RENAME),
CREATE_NAMED_ENUM(UNIT_FLAG_PREPARATION),
@@ -125,7 +125,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] =
CREATE_NAMED_ENUM(UNIT_FLAG_PET_IN_COMBAT),
CREATE_NAMED_ENUM(UNIT_FLAG_PVP),
CREATE_NAMED_ENUM(UNIT_FLAG_SILENCED),
- CREATE_NAMED_ENUM(UNIT_FLAG_UNK_14),
+ CREATE_NAMED_ENUM(UNIT_FLAG_CANNOT_SWIM),
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_15),
CREATE_NAMED_ENUM(UNIT_FLAG_UNK_16),
CREATE_NAMED_ENUM(UNIT_FLAG_PACIFIED),
diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp
index 787265cc19b..a86de766117 100644
--- a/src/server/scripts/Commands/cs_pet.cpp
+++ b/src/server/scripts/Commands/cs_pet.cpp
@@ -69,10 +69,10 @@ public:
}
CreatureTemplate const* creatureTemplate = creatureTarget->GetCreatureTemplate();
- // Creatures with family 0 crashes the server
- if (!creatureTemplate->family)
+ // Creatures with family CREATURE_FAMILY_NONE crashes the server
+ if (creatureTemplate->family == CREATURE_FAMILY_NONE)
{
- handler->PSendSysMessage("This creature cannot be tamed. (family id: 0).");
+ handler->PSendSysMessage("This creature cannot be tamed. Family id: 0 (CREATURE_FAMILY_NONE).");
handler->SetSentErrorMessage(true);
return false;
}
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index eb28a8adae4..4470fa7de42 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -91,7 +91,7 @@ public:
{ "disenchant_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesDisenchantCommand, "" },
{ "event_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS, true, &HandleReloadEventScriptsCommand, "" },
{ "fishing_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesFishingCommand, "" },
- { "game_graveyard_zone", rbac::RBAC_PERM_COMMAND_RELOAD_GAME_GRAVEYARD_ZONE, true, &HandleReloadGameGraveyardZoneCommand, "" },
+ { "graveyard_zone", rbac::RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE, true, &HandleReloadGameGraveyardZoneCommand, "" },
{ "game_tele", rbac::RBAC_PERM_COMMAND_RELOAD_GAME_TELE, true, &HandleReloadGameTeleCommand, "" },
{ "gameobject_questender", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER, true, &HandleReloadGOQuestEnderCommand, "" },
{ "gameobject_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesGameobjectCommand, "" },
diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp
index 899c5615206..26f712eb8ef 100644
--- a/src/server/scripts/Commands/cs_ticket.cpp
+++ b/src/server/scripts/Commands/cs_ticket.cpp
@@ -199,7 +199,12 @@ public:
ticket->SaveToDB(trans);
sTicketMgr->UpdateLastChange();
- std::string msg = ticket->FormatMessageString(*handler, NULL, ticket->GetAssignedToName().c_str(), NULL, NULL, NULL);
+ std::string msg = [&] {
+ std::string const assignedName = ticket->GetAssignedToName();
+ return ticket->FormatMessageString(*handler, nullptr,
+ assignedName.empty() ? nullptr : assignedName.c_str(), nullptr, nullptr, nullptr);
+ }();
+
msg += handler->PGetParseString(LANG_COMMAND_TICKETLISTADDCOMMENT, player ? player->GetName().c_str() : "Console", comment);
handler->SendGlobalGMSysMessage(msg.c_str());
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp
index 817aaf0a253..f962a019da6 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp
@@ -187,6 +187,12 @@ public:
events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(17000, 19000));
}
+ void IsSummonedBy(Unit* /*summoner*/) override
+ {
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ DoZoneInCombat();
+ }
+
void JustDied(Unit* /*killer*/) override
{
_JustDied();
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
index 4a6fee12098..995fc44abd0 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
@@ -206,9 +206,10 @@ public:
Talk(SAY_GAMESBEGIN_2);
me->setFaction(103);
- me->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
DoCast(me, SPELL_NEFARIANS_BARRIER);
me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
AttackStart(target);
events.ScheduleEvent(EVENT_SHADOW_BOLT, urand(3000, 10000));
events.ScheduleEvent(EVENT_FEAR, urand(10000, 20000));
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp
index e58bded801b..e97b7cba388 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp
@@ -32,6 +32,7 @@ enum Say
enum Spells
{
+ // @todo orb uses the wrong spell, this needs sniffs
SPELL_MINDCONTROL = 42013,
SPELL_CHANNEL = 45537,
SPELL_EGG_DESTROY = 19873,
@@ -103,7 +104,7 @@ public:
secondPhase = true;
me->RemoveAllAuras();
- me->SetHealth(me->GetMaxHealth());
+ me->SetFullHealth();
}
void DoAction(int32 action) override
@@ -114,6 +115,7 @@ public:
void DamageTaken(Unit* /*who*/, uint32& damage) override
{
+ // @todo this is wrong - razorgore should still take damage, he should just nuke the whole room and respawn if he dies during P1
if (!secondPhase)
damage = 0;
}
@@ -146,6 +148,7 @@ public:
break;
case EVENT_CONFLAGRATION:
DoCastVictim(SPELL_CONFLAGRATION);
+ // @todo is this even necessary? pretty sure AI ignores targets with disorient by default
if (me->GetVictim() && me->EnsureVictim()->HasAura(SPELL_CONFLAGRATION))
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true))
me->TauntApply(target);
@@ -175,10 +178,10 @@ public:
{
if (InstanceScript* instance = go->GetInstanceScript())
if (instance->GetData(DATA_EGG_EVENT) != DONE)
- if (Creature* razor = instance->GetCreature(DATA_RAZORGORE_THE_UNTAMED))
+ if (Creature* razorgore = instance->GetCreature(DATA_RAZORGORE_THE_UNTAMED))
{
- razor->Attack(player, true);
- player->CastSpell(razor, SPELL_MINDCONTROL);
+ razorgore->Attack(player, true);
+ player->CastSpell(razorgore, SPELL_MINDCONTROL);
}
return true;
}
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp
index 9b487f7b5f9..8cff67d9f28 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp
@@ -44,16 +44,20 @@ enum Texts
enum Spells
{
- SPELL_MAGIC_REFLECTION = 20619,
- SPELL_DAMAGE_REFLECTION = 21075,
+ SPELL_SUMMON_RAGNAROS = 19774,
SPELL_BLAST_WAVE = 20229,
- SPELL_AEGIS_OF_RAGNAROS = 20620,
SPELL_TELEPORT = 20618,
- SPELL_SUMMON_RAGNAROS = 19774,
+ SPELL_MAGIC_REFLECTION = 20619,
+ SPELL_AEGIS_OF_RAGNAROS = 20620,
+ SPELL_DAMAGE_REFLECTION = 21075
};
-#define GOSSIP_HELLO 4995
-#define GOSSIP_SELECT "Tell me more."
+enum Extras
+{
+ OPTION_ID_YOU_CHALLENGED_US = 0,
+ FACTION_FRIENDLY = 35,
+ MENU_OPTION_YOU_CHALLENGED_US = 4108
+};
enum Events
{
@@ -64,7 +68,7 @@ enum Events
EVENT_OUTRO_1 = 5,
EVENT_OUTRO_2 = 6,
- EVENT_OUTRO_3 = 7,
+ EVENT_OUTRO_3 = 7
};
class boss_majordomo : public CreatureScript
@@ -106,7 +110,7 @@ class boss_majordomo : public CreatureScript
if (!me->FindNearestCreature(NPC_FLAMEWAKER_HEALER, 100.0f) && !me->FindNearestCreature(NPC_FLAMEWAKER_ELITE, 100.0f))
{
instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, me->GetEntry(), me);
- me->setFaction(35);
+ me->setFaction(FACTION_FRIENDLY);
EnterEvadeMode();
Talk(SAY_DEFEAT);
_JustDied();
@@ -184,25 +188,20 @@ class boss_majordomo : public CreatureScript
}
else if (action == ACTION_START_RAGNAROS_ALT)
{
- me->setFaction(35);
+ me->setFaction(FACTION_FRIENDLY);
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
}
- };
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SELECT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- player->SEND_GOSSIP_MENU(GOSSIP_HELLO, creature->GetGUID());
- return true;
- }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override
- {
- player->CLOSE_GOSSIP_MENU();
- creature->AI()->DoAction(ACTION_START_RAGNAROS);
- return true;
- }
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
+ {
+ if (menuId == MENU_OPTION_YOU_CHALLENGED_US && gossipListId == OPTION_ID_YOU_CHALLENGED_US)
+ {
+ player->CLOSE_GOSSIP_MENU();
+ DoAction(ACTION_START_RAGNAROS);
+ }
+ }
+ };
CreatureAI* GetAI(Creature* creature) const override
{
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp
index e0cae87051f..a89a6b491e8 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp
@@ -159,7 +159,7 @@ class boss_ragnaros : public CreatureScript
break;
case EVENT_INTRO_5:
me->SetReactState(REACT_AGGRESSIVE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
_introState = 2;
break;
default:
diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
index 05d964124b1..c8eb645845b 100644
--- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
+++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
@@ -305,7 +305,7 @@ public:
me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[9], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000);
break;
case 2:
- if (GameObject* go = me->SummonGameObject(183410, -533.140f, -105.322f, -156.016f, 0, 0, 0, 0, 0, 1))
+ if (GameObject* go = me->SummonGameObject(183410, -533.140f, -105.322f, -156.016f, 0.f, G3D::Quat(), 1))
{
GoSummonList.push_back(go->GetGUID());
go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it!
@@ -320,7 +320,7 @@ public:
Talk(SAY_BLASTMASTER_7);
break;
case 4:
- if (GameObject* go = me->SummonGameObject(183410, -542.199f, -96.854f, -155.790f, 0, 0, 0, 0, 0, 1))
+ if (GameObject* go = me->SummonGameObject(183410, -542.199f, -96.854f, -155.790f, 0.f, G3D::Quat(), 1))
{
GoSummonList.push_back(go->GetGUID());
go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
@@ -334,7 +334,7 @@ public:
me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[14], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000);
break;
case 6:
- if (GameObject* go = me->SummonGameObject(183410, -507.820f, -103.333f, -151.353f, 0, 0, 0, 0, 0, 1))
+ if (GameObject* go = me->SummonGameObject(183410, -507.820f, -103.333f, -151.353f, 0.f, G3D::Quat(), 1))
{
GoSummonList.push_back(go->GetGUID());
go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it!
@@ -342,7 +342,7 @@ public:
}
break;
case 7:
- if (GameObject* go = me->SummonGameObject(183410, -511.829f, -86.249f, -151.431f, 0, 0, 0, 0, 0, 1))
+ if (GameObject* go = me->SummonGameObject(183410, -511.829f, -86.249f, -151.431f, 0.f, G3D::Quat(), 1))
{
GoSummonList.push_back(go->GetGUID());
go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it!
@@ -354,9 +354,9 @@ public:
me->SummonCreature(NPC_CHOMPER, SpawnPosition[16], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000);
break;
case 9:
- me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[17].GetPositionX(), SpawnPosition[17].GetPositionY(), SpawnPosition[17].GetPositionZ(), SpawnPosition[17].GetOrientation(), 0, 0, 0, 0, 7200);
- me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[18].GetPositionX(), SpawnPosition[18].GetPositionY(), SpawnPosition[18].GetPositionZ(), SpawnPosition[18].GetOrientation(), 0, 0, 0, 0, 7200);
- me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[19].GetPositionX(), SpawnPosition[19].GetPositionY(), SpawnPosition[19].GetPositionZ(), SpawnPosition[19].GetOrientation(), 0, 0, 0, 0, 7200);
+ me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[17], G3D::Quat(), 7200);
+ me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[18], G3D::Quat(), 7200);
+ me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[19], G3D::Quat(), 7200);
break;
}
}
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp
index 43ef7e006ef..7e7edfb7832 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp
@@ -28,25 +28,42 @@ EndScriptData */
#include "SpellInfo.h"
#include "karazhan.h"
-enum Midnight
+enum Texts
{
- SAY_MIDNIGHT_KILL = 0,
- SAY_APPEAR = 1,
- SAY_MOUNT = 2,
-
- SAY_KILL = 0,
- SAY_DISARMED = 1,
- SAY_DEATH = 2,
- SAY_RANDOM = 3,
-
- SPELL_SHADOWCLEAVE = 29832,
- SPELL_INTANGIBLE_PRESENCE = 29833,
- SPELL_BERSERKER_CHARGE = 26561, //Only when mounted
+ SAY_KILL = 0,
+ SAY_RANDOM = 1,
+ SAY_DISARMED = 2,
+ SAY_MIDNIGHT_KILL = 3,
+ SAY_APPEAR = 4,
+ SAY_MOUNT = 5,
+
+ SAY_DEATH = 3,
+
+ // Midnight
+ EMOTE_CALL_ATTUMEN = 0,
+ EMOTE_MOUNT_UP = 1
+};
- MOUNTED_DISPLAYID = 16040,
+enum Spells
+{
+ // Attumen
+ SPELL_SHADOWCLEAVE = 29832,
+ SPELL_INTANGIBLE_PRESENCE = 29833,
+ SPELL_SPAWN_SMOKE = 10389,
+ SPELL_CHARGE = 29847,
+
+ // Midnight
+ SPELL_KNOCKDOWN = 29711,
+ SPELL_SUMMON_ATTUMEN = 29714,
+ SPELL_MOUNT = 29770,
+ SPELL_SUMMON_ATTUMEN_MOUNTED = 29799
+};
- //Attumen (@todo Use the summoning spell instead of Creature id. It works, but is not convenient for us)
- SUMMON_ATTUMEN = 15550,
+enum Phases
+{
+ PHASE_NONE,
+ PHASE_ATTUMEN_ENGAGES,
+ PHASE_MOUNTED
};
class boss_attumen : public CreatureScript
@@ -54,75 +71,212 @@ class boss_attumen : public CreatureScript
public:
boss_attumen() : CreatureScript("boss_attumen") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new boss_attumenAI(creature);
- }
-
- struct boss_attumenAI : public ScriptedAI
+ struct boss_attumenAI : public BossAI
{
- boss_attumenAI(Creature* creature) : ScriptedAI(creature)
+ boss_attumenAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN)
{
Initialize();
-
- Phase = 1;
-
- CleaveTimer = urand(10000, 15000);
- CurseTimer = 30000;
- RandomYellTimer = urand(30000, 60000); //Occasionally yell
- ChargeTimer = 20000;
- }
+ }
void Initialize()
{
- ResetTimer = 0;
- Midnight.Clear();
+ _midnightGUID.Clear();
+ _phase = PHASE_NONE;
}
- ObjectGuid Midnight;
- uint8 Phase;
- uint32 CleaveTimer;
- uint32 CurseTimer;
- uint32 RandomYellTimer;
- uint32 ChargeTimer; //only when mounted
- uint32 ResetTimer;
-
void Reset() override
{
Initialize();
+ BossAI::Reset();
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID))
+ BossAI::_DespawnAtEvade(10, midnight);
+
+ me->DespawnOrUnsummon();
}
- void EnterEvadeMode(EvadeReason why) override
+ void ScheduleTasks() override
{
- ScriptedAI::EnterEvadeMode(why);
- ResetTimer = 2000;
+ scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_SHADOWCLEAVE);
+ task.Repeat(Seconds(15), Seconds(25));
+ });
+
+ scheduler.Schedule(Seconds(25), Seconds(45), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ DoCast(target,SPELL_INTANGIBLE_PRESENCE);
+
+ task.Repeat(Seconds(25), Seconds(45));
+ });
+
+ scheduler.Schedule(Seconds(30), Seconds(60), [this](TaskContext task)
+ {
+ Talk(SAY_RANDOM);
+ task.Repeat(Seconds(30), Seconds(60));
+ });
}
- void EnterCombat(Unit* /*who*/) override { }
+ void DamageTaken(Unit* /*attacker*/, uint32 &damage) override
+ {
+ // Attumen does not die until he mounts Midnight, let health fall to 1 and prevent further damage.
+ if (damage >= me->GetHealth() && _phase != PHASE_MOUNTED)
+ damage = me->GetHealth() - 1;
+
+ if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage))
+ {
+ _phase = PHASE_NONE;
+
+ if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID))
+ midnight->AI()->DoCastAOE(SPELL_MOUNT, true);
+ }
+ }
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_KILL);
}
- void JustDied(Unit* /*killer*/) override
+ void JustSummoned(Creature* summon) override
+ {
+ if (summon->GetEntry() == NPC_ATTUMEN_MOUNTED)
+ if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID))
+ {
+ if (midnight->GetHealth() > me->GetHealth())
+ summon->SetHealth(midnight->GetHealth());
+ else
+ summon->SetHealth(me->GetHealth());
+
+ summon->AI()->DoZoneInCombat();
+ summon->AI()->SetGUID(_midnightGUID, NPC_MIDNIGHT);
+ }
+
+ BossAI::JustSummoned(summon);
+ }
+
+ void IsSummonedBy(Unit* summoner) override
+ {
+ if (summoner->GetEntry() == NPC_MIDNIGHT)
+ _phase = PHASE_ATTUMEN_ENGAGES;
+
+ if (summoner->GetEntry() == NPC_ATTUMEN_UNMOUNTED)
+ {
+ _phase = PHASE_MOUNTED;
+ DoCastSelf(SPELL_SPAWN_SMOKE);
+
+ scheduler.Schedule(Seconds(10), Seconds(25), [this](TaskContext task)
+ {
+ Unit* target = nullptr;
+ ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList();
+ std::vector<Unit*> target_list;
+
+ for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr)
+ {
+ target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
+ if (target && !target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false))
+ target_list.push_back(target);
+
+ target = nullptr;
+ }
+
+ if (!target_list.empty())
+ target = Trinity::Containers::SelectRandomContainerElement(target_list);
+
+ DoCast(target, SPELL_CHARGE);
+ task.Repeat(Seconds(10), Seconds(25));
+ });
+
+ scheduler.Schedule(Seconds(25), Seconds(35), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_KNOCKDOWN);
+ task.Repeat(Seconds(25), Seconds(35));
+ });
+ }
+ }
+
+ void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
- if (Unit* midnight = ObjectAccessor::GetUnit(*me, Midnight))
+ if (Unit* midnight = ObjectAccessor::GetUnit(*me, _midnightGUID))
midnight->KillSelf();
- if (InstanceScript* instance = me->GetInstanceScript())
- instance->SetBossState(DATA_ATTUMEN, DONE);
+ BossAI::JustDied(killer);
}
- void UpdateAI(uint32 diff) override;
+ void SetGUID(ObjectGuid guid, int32 data) override
+ {
+ if (data == NPC_MIDNIGHT)
+ _midnightGUID = guid;
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim() && _phase != PHASE_NONE)
+ return;
+
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
void SpellHit(Unit* /*source*/, const SpellInfo* spell) override
{
if (spell->Mechanic == MECHANIC_DISARM)
Talk(SAY_DISARMED);
+
+ if (spell->Id == SPELL_MOUNT)
+ {
+ if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID))
+ {
+ _phase = PHASE_NONE;
+ scheduler.CancelAll();
+
+ midnight->AttackStop();
+ midnight->RemoveAllAttackers();
+ midnight->SetReactState(REACT_PASSIVE);
+ midnight->GetMotionMaster()->MoveChase(me);
+ midnight->AI()->Talk(EMOTE_MOUNT_UP);
+
+ me->AttackStop();
+ me->RemoveAllAttackers();
+ me->SetReactState(REACT_PASSIVE);
+ me->GetMotionMaster()->MoveChase(midnight);
+ Talk(SAY_MOUNT);
+
+ scheduler.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID))
+ {
+ if (me->IsWithinMeleeRange(midnight))
+ {
+ DoCastAOE(SPELL_SUMMON_ATTUMEN_MOUNTED);
+ me->SetVisible(false);
+ midnight->SetVisible(false);
+ }
+ else
+ {
+ midnight->GetMotionMaster()->MoveChase(me);
+ me->GetMotionMaster()->MoveChase(midnight);
+ task.Repeat(Seconds(3));
+ }
+ }
+ });
+ }
+ }
}
+
+ private:
+ ObjectGuid _midnightGUID;
+ uint8 _phase;
};
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new boss_attumenAI(creature);
+ }
};
class boss_midnight : public CreatureScript
@@ -130,214 +284,102 @@ class boss_midnight : public CreatureScript
public:
boss_midnight() : CreatureScript("boss_midnight") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new boss_midnightAI(creature);
- }
-
- struct boss_midnightAI : public ScriptedAI
+ struct boss_midnightAI : public BossAI
{
- boss_midnightAI(Creature* creature) : ScriptedAI(creature)
+ boss_midnightAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN)
{
Initialize();
}
void Initialize()
{
- Phase = 1;
- Attumen.Clear();
- Mount_Timer = 0;
+ _phase = PHASE_NONE;
}
- ObjectGuid Attumen;
- uint8 Phase;
- uint32 Mount_Timer;
-
void Reset() override
{
Initialize();
-
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ BossAI::Reset();
me->SetVisible(true);
+ me->SetReactState(REACT_DEFENSIVE);
}
- void EnterCombat(Unit* /*who*/) override
+ void DamageTaken(Unit* /*attacker*/, uint32 &damage) override
{
- if (InstanceScript* instance = me->GetInstanceScript())
- instance->SetBossState(DATA_ATTUMEN, IN_PROGRESS);
- }
+ // Midnight never dies, let health fall to 1 and prevent further damage.
+ if (damage >= me->GetHealth())
+ damage = me->GetHealth() - 1;
- void KilledUnit(Unit* /*victim*/) override
- {
- if (Phase == 2)
+ if (_phase == PHASE_NONE && me->HealthBelowPctDamaged(95, damage))
{
- if (Unit* unit = ObjectAccessor::GetUnit(*me, Attumen))
- Talk(SAY_MIDNIGHT_KILL, unit);
+ _phase = PHASE_ATTUMEN_ENGAGES;
+ Talk(EMOTE_CALL_ATTUMEN);
+ DoCastAOE(SPELL_SUMMON_ATTUMEN);
+ }
+ else if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage))
+ {
+ _phase = PHASE_MOUNTED;
+ DoCastAOE(SPELL_MOUNT, true);
}
}
- void UpdateAI(uint32 diff) override
+ void JustSummoned(Creature* summon) override
{
- if (!UpdateVictim())
- return;
-
- if (Phase == 1 && HealthBelowPct(95))
+ if (summon->GetEntry() == NPC_ATTUMEN_UNMOUNTED)
{
- Phase = 2;
- if (Creature* attumen = me->SummonCreature(SUMMON_ATTUMEN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000))
- {
- Attumen = attumen->GetGUID();
- attumen->AI()->AttackStart(me->GetVictim());
- SetMidnight(attumen, me->GetGUID());
- Talk(SAY_APPEAR, attumen);
- }
- }
- else if (Phase == 2 && HealthBelowPct(25))
- {
- if (Unit* pAttumen = ObjectAccessor::GetUnit(*me, Attumen))
- Mount(pAttumen);
- }
- else if (Phase == 3)
- {
- if (Mount_Timer)
- {
- if (Mount_Timer <= diff)
- {
- Mount_Timer = 0;
- me->SetVisible(false);
- me->GetMotionMaster()->MoveIdle();
- if (Unit* pAttumen = ObjectAccessor::GetUnit(*me, Attumen))
- {
- pAttumen->SetDisplayId(MOUNTED_DISPLAYID);
- pAttumen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- if (pAttumen->GetVictim())
- {
- pAttumen->GetMotionMaster()->MoveChase(pAttumen->GetVictim());
- pAttumen->SetTarget(pAttumen->EnsureVictim()->GetGUID());
- }
- pAttumen->SetObjectScale(1);
- }
- } else Mount_Timer -= diff;
- }
+ _attumenGUID = summon->GetGUID();
+ summon->AI()->SetGUID(me->GetGUID(), NPC_MIDNIGHT);
+ summon->AI()->AttackStart(me->GetVictim());
+ summon->AI()->Talk(SAY_APPEAR);
}
- if (Phase != 3)
- DoMeleeAttackIfReady();
+ BossAI::JustSummoned(summon);
}
- void Mount(Unit* pAttumen)
+ void EnterCombat(Unit* who) override
{
- Talk(SAY_MOUNT, pAttumen);
- Phase = 3;
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- pAttumen->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- float angle = me->GetAngle(pAttumen);
- float distance = me->GetDistance2d(pAttumen);
- float newX = me->GetPositionX() + std::cos(angle)*(distance/2);
- float newY = me->GetPositionY() + std::sin(angle)*(distance/2);
- float newZ = 50;
- //me->Relocate(newX, newY, newZ, angle);
- //me->SendMonsterMove(newX, newY, newZ, 0, true, 1000);
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MovePoint(0, newX, newY, newZ);
- distance += 10;
- newX = me->GetPositionX() + std::cos(angle)*(distance/2);
- newY = me->GetPositionY() + std::sin(angle)*(distance/2);
- pAttumen->GetMotionMaster()->Clear();
- pAttumen->GetMotionMaster()->MovePoint(0, newX, newY, newZ);
- //pAttumen->Relocate(newX, newY, newZ, -angle);
- //pAttumen->SendMonsterMove(newX, newY, newZ, 0, true, 1000);
- Mount_Timer = 1000;
+ BossAI::EnterCombat(who);
+
+ scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_KNOCKDOWN);
+ task.Repeat(Seconds(15), Seconds(25));
+ });
}
- void SetMidnight(Creature* pAttumen, ObjectGuid value)
+ void EnterEvadeMode(EvadeReason /*why*/) override
{
- ENSURE_AI(boss_attumen::boss_attumenAI, pAttumen->AI())->Midnight = value;
+ BossAI::_DespawnAtEvade(10);
}
- };
-};
-void boss_attumen::boss_attumenAI::UpdateAI(uint32 diff)
-{
- if (ResetTimer)
- {
- if (ResetTimer <= diff)
+ void KilledUnit(Unit* /*victim*/) override
{
- ResetTimer = 0;
- Unit* pMidnight = ObjectAccessor::GetUnit(*me, Midnight);
- if (pMidnight)
+ if (_phase == PHASE_ATTUMEN_ENGAGES)
{
- pMidnight->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- pMidnight->SetVisible(true);
+ if (Unit* unit = ObjectAccessor::GetUnit(*me, _attumenGUID))
+ Talk(SAY_MIDNIGHT_KILL, unit);
}
- Midnight.Clear();
- me->SetVisible(false);
- me->KillSelf();
- } else ResetTimer -= diff;
- }
-
- //Return since we have no target
- if (!UpdateVictim())
- return;
-
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
- return;
-
- if (CleaveTimer <= diff)
- {
- DoCastVictim(SPELL_SHADOWCLEAVE);
- CleaveTimer = urand(10000, 15000);
- } else CleaveTimer -= diff;
+ }
- if (CurseTimer <= diff)
- {
- DoCastVictim(SPELL_INTANGIBLE_PRESENCE);
- CurseTimer = 30000;
- } else CurseTimer -= diff;
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim() || _phase == PHASE_MOUNTED)
+ return;
- if (RandomYellTimer <= diff)
- {
- Talk(SAY_RANDOM);
- RandomYellTimer = urand(30000, 60000);
- } else RandomYellTimer -= diff;
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
- if (me->GetUInt32Value(UNIT_FIELD_DISPLAYID) == MOUNTED_DISPLAYID)
- {
- if (ChargeTimer <= diff)
- {
- Unit* target = NULL;
- ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList();
- std::vector<Unit*> target_list;
- for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr)
- {
- target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
- if (target && !target->IsWithinDist(me, ATTACK_DISTANCE, false))
- target_list.push_back(target);
- target = NULL;
- }
- if (!target_list.empty())
- target = *(target_list.begin() + rand32() % target_list.size());
+ private:
+ ObjectGuid _attumenGUID;
+ uint8 _phase;
+ };
- DoCast(target, SPELL_BERSERKER_CHARGE);
- ChargeTimer = 20000;
- } else ChargeTimer -= diff;
- }
- else
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (HealthBelowPct(25))
- {
- Creature* pMidnight = ObjectAccessor::GetCreature(*me, Midnight);
- if (pMidnight && pMidnight->GetTypeId() == TYPEID_UNIT)
- {
- ENSURE_AI(boss_midnight::boss_midnightAI, (pMidnight->AI()))->Mount(me);
- me->SetHealth(pMidnight->GetHealth());
- DoResetThreat();
- }
- }
+ return new boss_midnightAI(creature);
}
-
- DoMeleeAttackIfReady();
-}
+};
void AddSC_boss_attumen()
{
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp
index e667141fa29..29c78d3b388 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp
@@ -298,7 +298,7 @@ public:
if (Blind_Timer <= diff)
{
std::list<Unit*> targets;
- SelectTargetList(targets, 5, SELECT_TARGET_RANDOM, me->GetMeleeReach()*5, true);
+ SelectTargetList(targets, 5, SELECT_TARGET_RANDOM, me->GetCombatReach()*5, true);
for (std::list<Unit*>::const_iterator i = targets.begin(); i != targets.end(); ++i)
if (!me->IsWithinMeleeRange(*i))
{
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp
index 3f236c060d7..2e4ac11d3fa 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp
@@ -797,46 +797,43 @@ enum RedRidingHood
SAY_WOLF_AGGRO = 0,
SAY_WOLF_SLAY = 1,
SAY_WOLF_HOOD = 2,
+ OPTION_WHAT_PHAT_LEWTS_YOU_HAVE = 7443,
SOUND_WOLF_DEATH = 9275,
SPELL_LITTLE_RED_RIDING_HOOD = 30768,
SPELL_TERRIFYING_HOWL = 30752,
SPELL_WIDE_SWIPE = 30761,
- CREATURE_BIG_BAD_WOLF = 17521,
+ CREATURE_BIG_BAD_WOLF = 17521
};
-
-#define GOSSIP_GRANDMA "What phat lewtz you have grandmother?"
-
-
-
class npc_grandmother : public CreatureScript
{
-public:
- npc_grandmother() : CreatureScript("npc_grandmother") { }
+ public:
+ npc_grandmother() : CreatureScript("npc_grandmother") { }
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF)
+ struct npc_grandmotherAI : public ScriptedAI
{
- if (Creature* pBigBadWolf = creature->SummonCreature(CREATURE_BIG_BAD_WOLF, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILLISECONDS))
- pBigBadWolf->AI()->AttackStart(player);
+ npc_grandmotherAI(Creature* creature) : ScriptedAI(creature) { }
- creature->DespawnOrUnsummon();
- }
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
+ {
+ if (menuId == OPTION_WHAT_PHAT_LEWTS_YOU_HAVE && gossipListId == 0)
+ {
+ player->CLOSE_GOSSIP_MENU();
- return true;
- }
+ if (Creature* pBigBadWolf = me->SummonCreature(CREATURE_BIG_BAD_WOLF, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILLISECONDS))
+ pBigBadWolf->AI()->AttackStart(player);
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_GRANDMA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
- player->SEND_GOSSIP_MENU(8990, creature->GetGUID());
+ me->DespawnOrUnsummon();
+ }
+ }
+ };
- return true;
- }
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_grandmotherAI(creature);
+ }
};
class boss_bigbadwolf : public CreatureScript
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h
index 05de9e43a91..2dc3750dc5b 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h
+++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h
@@ -70,6 +70,9 @@ enum MiscCreatures
NPC_SHADIKITH_THE_GLIDER = 16180,
NPC_TERESTIAN_ILLHOOF = 15688,
NPC_MOROES = 15687,
+ NPC_ATTUMEN_UNMOUNTED = 15550,
+ NPC_ATTUMEN_MOUNTED = 16152,
+ NPC_MIDNIGHT = 16151,
// Trash
NPC_COLDMIST_WIDOW = 16171,
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
index d25a225717a..ea6d2cb0b6f 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
@@ -1116,7 +1116,7 @@ public:
case 48: // Show the cleansing effect (dawn of light)
//if (GameObject* go = me->GetMap()->GetGameObject(uiDawnofLightGUID))
// go->SetPhaseMask(128, true);
- me->SummonGameObject(GO_LIGHT_OF_DAWN, 2283.896f, -5287.914f, 83.066f, 0, 0, 0, 0, 0, 30);
+ me->SummonGameObject(GO_LIGHT_OF_DAWN, 2283.896f, -5287.914f, 83.066f, 0.f, G3D::Quat(), 30);
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiTirionGUID))
{
if (temp->HasAura(SPELL_REBIRTH_OF_THE_ASHBRINGER))
diff --git a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp
index 6a6a0b0994b..241710627b0 100644
--- a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp
+++ b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp
@@ -176,14 +176,14 @@ public:
void UseStatue(GameObject* go)
{
- go->SummonGameObject(GO_ATALAI_LIGHT1, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ(), 0, 0, 0, 0, 0, 0);
+ go->SummonGameObject(GO_ATALAI_LIGHT1, *go, G3D::Quat(), 0);
go->SetUInt32Value(GAMEOBJECT_FLAGS, 4);
}
void UseLastStatue(GameObject* go)
{
for (uint8 i = 0; i < nStatues; ++i)
- go->SummonGameObject(GO_ATALAI_LIGHT2, statuePositions[i].GetPositionX(), statuePositions[i].GetPositionY(), statuePositions[i].GetPositionZ(), statuePositions[i].GetOrientation(), 0, 0, 0, 0, 0);
+ go->SummonGameObject(GO_ATALAI_LIGHT2, statuePositions[i], G3D::Quat(), 0);
go->SummonCreature(NPC_ATALALARION, atalalarianPos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 7200);
}
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
index 7563a880f0a..040d8cb2a37 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
@@ -1361,7 +1361,7 @@ public:
DoCastVictim(SPELL_SR_SHOOT, false);
uiTimer[2] = urand(4000, 6000);
}
- if (me->IsWithinMeleeRange(me->GetVictim(), 6))
+ if (me->IsWithinMeleeRange(me->GetVictim()))
{
if (uiTimer[0] <= diff)
{
diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp
index a5fed30a6c6..68391c65655 100644
--- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp
+++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp
@@ -98,7 +98,7 @@ class boss_archaedas : public CreatureScript
instance->SetData(0, 5); // respawn any dead minions
me->setFaction(35);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->AddAura(SPELL_FREEZE_ANIM, me);
}
@@ -111,7 +111,7 @@ class boss_archaedas : public CreatureScript
DoCast(minion, SPELL_AWAKEN_VAULT_WALKER, flag);
minion->CastSpell(minion, SPELL_ARCHAEDAS_AWAKEN, true);
minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
minion->setFaction(14);
minion->RemoveAura(SPELL_MINION_FREEZE_ANIM);
}
@@ -121,7 +121,7 @@ class boss_archaedas : public CreatureScript
{
me->setFaction(14);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
}
void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override
@@ -261,7 +261,7 @@ class npc_archaedas_minions : public CreatureScript
me->setFaction(35);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->RemoveAllAuras();
me->AddAura(SPELL_MINION_FREEZE_ANIM, me);
}
@@ -271,7 +271,7 @@ class npc_archaedas_minions : public CreatureScript
me->setFaction (14);
me->RemoveAllAuras();
me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
bAmIAwake = true;
}
@@ -350,7 +350,7 @@ class npc_stonekeepers : public CreatureScript
{
me->setFaction(35);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->RemoveAllAuras();
me->AddAura(SPELL_MINION_FREEZE_ANIM, me);
}
@@ -359,7 +359,7 @@ class npc_stonekeepers : public CreatureScript
{
me->setFaction(14);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
}
void UpdateAI(uint32 /*diff*/) override
diff --git a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp
index 8c78fb7ff1b..28a3a4eb4e0 100644
--- a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp
+++ b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp
@@ -147,7 +147,7 @@ class instance_uldaman : public InstanceMapScript
creature->setFaction(35);
creature->RemoveAllAuras();
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
creature->AddAura(SPELL_MINION_FREEZE_ANIM, creature);
}
@@ -178,7 +178,7 @@ class instance_uldaman : public InstanceMapScript
Creature* target = instance->GetCreature(*i);
if (!target || !target->IsAlive())
continue;
- target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
target->setFaction(14);
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
target->RemoveAura(SPELL_MINION_FREEZE_ANIM);
@@ -202,7 +202,7 @@ class instance_uldaman : public InstanceMapScript
Creature* target = instance->GetCreature(*i);
if (!target || !target->IsAlive() || target->getFaction() == 14)
continue;
- target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
target->setFaction(14);
target->RemoveAura(SPELL_MINION_FREEZE_ANIM);
@@ -268,7 +268,7 @@ class instance_uldaman : public InstanceMapScript
return;
ironaya->setFaction(415);
- ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
ironaya->GetMotionMaster()->Clear();
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
index 972acdc3f9f..9b668169dca 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
@@ -162,7 +162,7 @@ class npc_zulaman_hostage : public CreatureScript
{
if (HostageEntry[i] == entry)
{
- creature->SummonGameObject(ChestEntry[i], x-2, y, z, 0, 0, 0, 0, 0, 0);
+ creature->SummonGameObject(ChestEntry[i], Position(x - 2, y, z, 0.f), G3D::Quat(), 0);
break;
}
}
diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
index b794a653791..adcb4f9fc9a 100644
--- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
+++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
@@ -179,7 +179,6 @@ void AddSC_eastern_plaguelands();
void AddSC_ghostlands();
void AddSC_hinterlands();
void AddSC_isle_of_queldanas();
-void AddSC_loch_modan();
void AddSC_redridge_mountains();
void AddSC_silverpine_forest();
void AddSC_stormwind_city();
@@ -357,7 +356,6 @@ void AddEasternKingdomsScripts()
AddSC_ghostlands();
AddSC_hinterlands();
AddSC_isle_of_queldanas();
- AddSC_loch_modan();
AddSC_redridge_mountains();
AddSC_silverpine_forest();
AddSC_stormwind_city();
diff --git a/src/server/scripts/EasternKingdoms/zone_loch_modan.cpp b/src/server/scripts/EasternKingdoms/zone_loch_modan.cpp
deleted file mode 100644
index 151f8270c47..00000000000
--- a/src/server/scripts/EasternKingdoms/zone_loch_modan.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
- *
- * 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/>.
- */
-
-/* ScriptData
-SDName: Loch_Modan
-SD%Complete: 100
-SDComment: Quest support: 3181
-SDCategory: Loch Modan
-EndScriptData */
-
-/* ContentData
-npc_mountaineer_pebblebitty
-EndContentData */
-
-#include "ScriptMgr.h"
-#include "ScriptedCreature.h"
-#include "ScriptedGossip.h"
-#include "Player.h"
-
-/*######
-## npc_mountaineer_pebblebitty
-######*/
-
-#define GOSSIP_MP "Open the gate please, i need to get to Searing Gorge"
-
-#define GOSSIP_MP1 "But i need to get there, now open the gate!"
-#define GOSSIP_MP2 "Ok, so what is this other way?"
-#define GOSSIP_MP3 "Doesn't matter, i'm invulnerable."
-#define GOSSIP_MP4 "Yes..."
-#define GOSSIP_MP5 "Ok, i'll try to remember that."
-#define GOSSIP_MP6 "A key? Ok!"
-
-class npc_mountaineer_pebblebitty : public CreatureScript
-{
-public:
- npc_mountaineer_pebblebitty() : CreatureScript("npc_mountaineer_pebblebitty") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF+1:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
- player->SEND_GOSSIP_MENU(1833, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+2:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
- player->SEND_GOSSIP_MENU(1834, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+3:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
- player->SEND_GOSSIP_MENU(1835, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+4:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
- player->SEND_GOSSIP_MENU(1836, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+5:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
- player->SEND_GOSSIP_MENU(1837, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+6:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
- player->SEND_GOSSIP_MENU(1838, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+7:
- player->CLOSE_GOSSIP_MENU();
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (!player->GetQuestRewardStatus(3181) == 1)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-};
-
-void AddSC_loch_modan()
-{
- new npc_mountaineer_pebblebitty();
-}
diff --git a/src/server/scripts/Events/childrens_week.cpp b/src/server/scripts/Events/childrens_week.cpp
index 4b93f5b4fbb..f61e175b8fc 100644
--- a/src/server/scripts/Events/childrens_week.cpp
+++ b/src/server/scripts/Events/childrens_week.cpp
@@ -639,6 +639,12 @@ class npc_elder_kekek : public CreatureScript
}
};
+enum TheEtymidian
+{
+ SAY_ACTIVATION = 0,
+ QUEST_THE_ACTIVATION_RUNE = 12547
+};
+
/*######
## npc_the_etymidian
## @todo A red crystal as a gift for the great one should be spawned during the event.
@@ -668,10 +674,20 @@ class npc_the_etymidian : public CreatureScript
Initialize();
}
+ void sQuestReward(Player* /*player*/, Quest const* quest, uint32 /*opt*/) override
+ {
+ if (quest->GetQuestId() != QUEST_THE_ACTIVATION_RUNE)
+ return;
+
+ Talk(SAY_ACTIVATION);
+ }
+
void MoveInLineOfSight(Unit* who) override
{
if (!phase && who && who->GetDistance2d(me) < 10.0f)
+ {
if (Player* player = who->ToPlayer())
+ {
if (player->GetQuestStatus(QUEST_MEETING_A_GREAT_ONE) == QUEST_STATUS_INCOMPLETE)
{
playerGUID = player->GetGUID();
@@ -679,6 +695,8 @@ class npc_the_etymidian : public CreatureScript
if (orphanGUID)
phase = 1;
}
+ }
+ }
}
void UpdateAI(uint32 diff) override
@@ -734,7 +752,6 @@ class npc_the_etymidian : public CreatureScript
int8 phase;
ObjectGuid playerGUID;
ObjectGuid orphanGUID;
-
};
CreatureAI* GetAI(Creature* creature) const override
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp
index 6b2142a8095..c326710e820 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp
@@ -650,7 +650,7 @@ void hyjalAI::SpawnVeins()
return;
for (uint8 i = 0; i < 7; ++i)
{
- GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7], 0);
+ GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], G3D::Quat(VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7]), 0);
if (gem)
VeinGUID[i]=gem->GetGUID();
}
@@ -662,7 +662,7 @@ void hyjalAI::SpawnVeins()
return;
for (uint8 i = 7; i < 14; ++i)
{
- GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7], 0);
+ GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], G3D::Quat(VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7]), 0);
if (gem)
VeinGUID[i] = gem->GetGUID();
}
@@ -725,7 +725,7 @@ void hyjalAI::UpdateAI(uint32 diff)
HideNearPos(me->GetPositionX(), me->GetPositionY());
HideNearPos(5037.76f, -1889.71f);
for (uint8 i = 0; i < 92; ++i)//summon fires
- me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7], 0);
+ me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], G3D::Quat(AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7]), 0);
}
else me->SetVisible(true);
@@ -738,7 +738,7 @@ void hyjalAI::UpdateAI(uint32 diff)
HideNearPos(5563, -2763.19f);
HideNearPos(5542.2f, -2629.36f);
for (uint8 i = 0; i < 65; ++i)//summon fires
- me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7], 0);
+ me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], G3D::Quat(HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7]), 0);
}
else me->SetVisible(true);
@@ -1042,7 +1042,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
{
case 0://alliance
for (uint8 i = 0; i < 92; ++i)//summon fires
- me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7], 0);
+ me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], G3D::Quat(AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7]), 0);
for (uint8 i = 0; i < 25; ++i)//summon 25 ghouls
{
@@ -1083,7 +1083,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
break;
case 1://horde
for (uint8 i = 0; i < 65; ++i)//summon fires
- me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7], 0);
+ me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], G3D::Quat(HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7]), 0);
for (uint8 i = 0; i < 26; ++i)//summon infernals
{
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
index 074ea781838..f76e65c3423 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
@@ -1256,7 +1256,7 @@ class npc_crate_helper : public CreatureScript
instance->SetData(DATA_CRATE_COUNT, instance->GetData(DATA_CRATE_COUNT) + 1);
if (GameObject* crate = me->FindNearestGameObject(GO_SUSPICIOUS_CRATE, 5.0f))
{
- crate->SummonGameObject(GO_PLAGUED_CRATE, crate->GetPositionX(), crate->GetPositionY(), crate->GetPositionZ(), crate->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, DAY);
+ crate->SummonGameObject(GO_PLAGUED_CRATE, *crate, G3D::Quat(), DAY);
crate->Delete();
}
}
diff --git a/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp b/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp
index 00a622f1319..558b6c10a61 100644
--- a/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp
+++ b/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp
@@ -25,20 +25,49 @@ gets instead the deserter debuff.
#include "ScriptMgr.h"
#include "InstanceScript.h"
+// Bosses (East)
+// 0 - Pusillin
+// 1 - Lethtendris
+// 2 - Hydrospawn
+// 3 - Zevrim Thornhoof
+// 4 - Alzzin the Wildshaper
+
+// West
+// 5 - Tendris Warpwood
+// 6 - Magister Kalendris
+// 7 - Tsu'zee
+// 8 - Illyanna Ravenoak
+// 9 - Immol'thar
+// 10 - Prince Tortheldrin
+
+// North
+// 11 - Guard Mol'dar
+// 12 - Stomper Kreeg
+// 13 - Guard Fengus
+// 14 - Guard Slip'kik
+// 15 - Captain Kromcrush
+// 16 - King Gordok
+
+uint8 const EncounterCount = 17;
+
class instance_dire_maul : public InstanceMapScript
{
public:
instance_dire_maul() : InstanceMapScript("instance_dire_maul", 429) { }
+ struct instance_dire_maul_InstanceMapScript : public InstanceScript
+ {
+ instance_dire_maul_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ SetBossNumber(EncounterCount);
+ }
+ };
+
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_dire_maul_InstanceMapScript(map);
}
- struct instance_dire_maul_InstanceMapScript : public InstanceScript
- {
- instance_dire_maul_InstanceMapScript(Map* map) : InstanceScript(map) { }
- };
};
void AddSC_instance_dire_maul()
diff --git a/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp b/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp
index b579f9fc608..e8b101b5057 100644
--- a/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp
+++ b/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp
@@ -204,7 +204,7 @@ public:
case EVENT_COMPLETE:
{
DoCast(me, SPELL_IDOM_ROOM_CAMERA_SHAKE);
- me->SummonGameObject(GO_BELNISTRASZS_BRAZIER, 2577.196f, 947.0781f, 53.16757f, 2.356195f, 0, 0, 0.9238796f, 0.3826832f, 3600);
+ me->SummonGameObject(GO_BELNISTRASZS_BRAZIER, 2577.196f, 947.0781f, 53.16757f, 2.356195f, G3D::Quat(0.f, 0.f, 0.9238796f, 0.3826832f), 3600);
std::list<WorldObject*> ClusterList;
Trinity::AllWorldObjectsInRange objects(me, 50.0f);
Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(me, ClusterList, objects);
diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp
index b192ff8ef4d..a220468e513 100644
--- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp
+++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp
@@ -190,11 +190,7 @@ class boss_ossirian : public CreatureScript
if (Creature* Trigger = me->GetMap()->SummonCreature(NPC_OSSIRIAN_TRIGGER, CrystalCoordinates[CrystalIterator]))
{
TriggerGUID = Trigger->GetGUID();
- if (GameObject* Crystal = Trigger->SummonGameObject(GO_OSSIRIAN_CRYSTAL,
- CrystalCoordinates[CrystalIterator].GetPositionX(),
- CrystalCoordinates[CrystalIterator].GetPositionY(),
- CrystalCoordinates[CrystalIterator].GetPositionZ(),
- 0, 0, 0, 0, 0, uint32(-1)))
+ if (GameObject* Crystal = Trigger->SummonGameObject(GO_OSSIRIAN_CRYSTAL, CrystalCoordinates[CrystalIterator], G3D::Quat(), uint32(-1)))
{
CrystalGUID = Crystal->GetGUID();
++CrystalIterator;
diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp
index 6063b9fe5c6..a7b2b156128 100644
--- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp
+++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp
@@ -508,7 +508,7 @@ public:
_explosivesGuids.clear();
for (uint8 i = 0; i != MAX_EXPLOSIVES; ++i)
{
- if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_1, ExplosivesPos[0][i].m_positionX, ExplosivesPos[0][i].m_positionY, ExplosivesPos[0][i].m_positionZ, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0))
+ if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_1, ExplosivesPos[0][i], G3D::Quat(), 0))
_explosivesGuids.push_back(explosive->GetGUID());
}
me->HandleEmoteCommand(EMOTE_ONESHOT_NONE); // reset anim state
@@ -604,7 +604,7 @@ public:
_explosivesGuids.clear();
for (uint8 i = 0; i != MAX_EXPLOSIVES; ++i)
{
- if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_2, ExplosivesPos[1][i].m_positionX, ExplosivesPos[1][i].m_positionY, ExplosivesPos[1][i].m_positionZ, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0))
+ if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_2, ExplosivesPos[1][i], G3D::Quat(), 0))
_explosivesGuids.push_back(explosive->GetGUID());
}
Talk(SAY_LEGOSO_15);
diff --git a/src/server/scripts/Kalimdor/zone_felwood.cpp b/src/server/scripts/Kalimdor/zone_felwood.cpp
index bce9d62105c..2b42db4b697 100644
--- a/src/server/scripts/Kalimdor/zone_felwood.cpp
+++ b/src/server/scripts/Kalimdor/zone_felwood.cpp
@@ -19,12 +19,12 @@
/* ScriptData
SDName: Felwood
SD%Complete: 95
-SDComment: Quest support: 4101, 4102
+SDComment: Quest support: 7632
SDCategory: Felwood
EndScriptData */
/* ContentData
-npcs_riverbreeze_and_silversky
+at_ancient_leaf
EndContentData */
#include "ScriptMgr.h"
@@ -33,74 +33,6 @@ EndContentData */
#include "Player.h"
/*######
-## npcs_riverbreeze_and_silversky
-######*/
-
-#define GOSSIP_ITEM_BEACON "Please make me a Cenarion Beacon"
-
-enum RiverbreezeAndSilversky
-{
- SPELL_CENARION_BEACON = 15120,
-
- NPC_ARATHANDRIS_SILVERSKY = 9528,
- NPC_MAYBESS_RIVERBREEZE = 9529,
-
- QUEST_CLEASING_FELWOOD_A = 4101,
- QUEST_CLEASING_FELWOOD_H = 4102
-};
-
-class npcs_riverbreeze_and_silversky : public CreatureScript
-{
-public:
- npcs_riverbreeze_and_silversky() : CreatureScript("npcs_riverbreeze_and_silversky") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF+1)
- {
- player->CLOSE_GOSSIP_MENU();
- creature->CastSpell(player, SPELL_CENARION_BEACON, false);
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- uint32 creatureId = creature->GetEntry();
-
- if (creatureId == NPC_ARATHANDRIS_SILVERSKY)
- {
- if (player->GetQuestRewardStatus(QUEST_CLEASING_FELWOOD_A))
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- player->SEND_GOSSIP_MENU(2848, creature->GetGUID());
- } else if (player->GetTeam() == HORDE)
- player->SEND_GOSSIP_MENU(2845, creature->GetGUID());
- else
- player->SEND_GOSSIP_MENU(2844, creature->GetGUID());
- }
-
- if (creatureId == NPC_MAYBESS_RIVERBREEZE)
- {
- if (player->GetQuestRewardStatus(QUEST_CLEASING_FELWOOD_H))
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- player->SEND_GOSSIP_MENU(2849, creature->GetGUID());
- } else if (player->GetTeam() == ALLIANCE)
- player->SEND_GOSSIP_MENU(2843, creature->GetGUID());
- else
- player->SEND_GOSSIP_MENU(2842, creature->GetGUID());
- }
-
- return true;
- }
-};
-
-/*######
## at_ancient_leaf
######*/
@@ -138,6 +70,5 @@ class at_ancient_leaf : public AreaTriggerScript
void AddSC_felwood()
{
- new npcs_riverbreeze_and_silversky();
new at_ancient_leaf();
}
diff --git a/src/server/scripts/Kalimdor/zone_feralas.cpp b/src/server/scripts/Kalimdor/zone_feralas.cpp
index 3e67a95b503..3c6ab633f66 100644
--- a/src/server/scripts/Kalimdor/zone_feralas.cpp
+++ b/src/server/scripts/Kalimdor/zone_feralas.cpp
@@ -19,10 +19,15 @@
/* ScriptData
SDName: Feralas
SD%Complete: 100
-SDComment: Quest support: 2767, Special vendor Gregan Brewspewer
+SDComment: Quest support: 2767, 2987
SDCategory: Feralas
EndScriptData */
+/* ContentData
+npc_oox22fe
+spell_gordunni_trap
+EndContentData */
+
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
@@ -32,44 +37,6 @@ EndScriptData */
#include "WorldSession.h"
/*######
-## npc_gregan_brewspewer
-######*/
-
-#define GOSSIP_HELLO "Buy somethin', will ya?"
-
-class npc_gregan_brewspewer : public CreatureScript
-{
-public:
- npc_gregan_brewspewer() : CreatureScript("npc_gregan_brewspewer") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF+1)
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
- player->SEND_GOSSIP_MENU(2434, creature->GetGUID());
- }
- if (action == GOSSIP_ACTION_TRADE)
- player->GetSession()->SendListInventory(creature->GetGUID());
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (creature->IsVendor() && player->GetQuestStatus(3909) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
-
- player->SEND_GOSSIP_MENU(2433, creature->GetGUID());
- return true;
- }
-
-};
-
-/*######
## npc_oox22fe
######*/
@@ -183,6 +150,10 @@ public:
};
+/*######
+## spell_gordunni_trap
+######*/
+
enum GordunniTrap
{
GO_GORDUNNI_DIRT_MOUND = 144064,
@@ -199,12 +170,12 @@ class spell_gordunni_trap : public SpellScriptLoader
void HandleDummy()
{
- if (Unit* caster = GetCaster())
- if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0))
- {
- chest->SetSpellId(GetSpellInfo()->Id);
- caster->RemoveGameObject(chest, false);
- }
+ Unit* caster = GetCaster();
+ if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, *caster, G3D::Quat(), 0))
+ {
+ chest->SetSpellId(GetSpellInfo()->Id);
+ caster->RemoveGameObject(chest, false);
+ }
}
void Register() override
@@ -225,7 +196,6 @@ class spell_gordunni_trap : public SpellScriptLoader
void AddSC_feralas()
{
- new npc_gregan_brewspewer();
new npc_oox22fe();
new spell_gordunni_trap();
}
diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp
index 99a209b5f0a..531c657e1cb 100644
--- a/src/server/scripts/Kalimdor/zone_moonglade.cpp
+++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp
@@ -19,15 +19,14 @@
/* ScriptData
SDName: Moonglade
SD%Complete: 100
-SDComment: Quest support: 30, 272, 5929, 5930, 10965. Special Flight Paths for Druid class.
+SDComment: Quest support: 10965
SDCategory: Moonglade
EndScriptData */
/* ContentData
-npc_bunthen_plainswind
-npc_silva_filnaveth
npc_clintar_spirit
-npc_clintar_dreamwalker
+npc_omen
+npc_giant_spotlight
EndContentData */
#include "ScriptMgr.h"
@@ -42,128 +41,6 @@ EndContentData */
#include "CellImpl.h"
/*######
-## npc_bunthen_plainswind
-######*/
-
-enum Bunthen
-{
- QUEST_SEA_LION_HORDE = 30,
- QUEST_SEA_LION_ALLY = 272,
- TAXI_PATH_ID_ALLY = 315,
- TAXI_PATH_ID_HORDE = 316
-};
-
-#define GOSSIP_ITEM_THUNDER "I'd like to fly to Thunder Bluff."
-#define GOSSIP_ITEM_AQ_END "Do you know where I can find Half Pendant of Aquatic Endurance?"
-
-class npc_bunthen_plainswind : public CreatureScript
-{
-public:
- npc_bunthen_plainswind() : CreatureScript("npc_bunthen_plainswind") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF + 1:
- player->CLOSE_GOSSIP_MENU();
- if (player->getClass() == CLASS_DRUID && player->GetTeam() == HORDE)
- player->ActivateTaxiPathTo(TAXI_PATH_ID_HORDE);
- break;
- case GOSSIP_ACTION_INFO_DEF + 2:
- player->SEND_GOSSIP_MENU(5373, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 3:
- player->SEND_GOSSIP_MENU(5376, creature->GetGUID());
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (player->getClass() != CLASS_DRUID)
- player->SEND_GOSSIP_MENU(4916, creature->GetGUID());
- else if (player->GetTeam() != HORDE)
- {
- if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_END, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
-
- player->SEND_GOSSIP_MENU(4917, creature->GetGUID());
- }
- else if (player->getClass() == CLASS_DRUID && player->GetTeam() == HORDE)
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_THUNDER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
-
- if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_END, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
-
- player->SEND_GOSSIP_MENU(4918, creature->GetGUID());
- }
- return true;
- }
-
-};
-
-/*######
-## npc_silva_filnaveth
-######*/
-
-#define GOSSIP_ITEM_RUTHERAN "I'd like to fly to Rut'theran Village."
-#define GOSSIP_ITEM_AQ_AGI "Do you know where I can find Half Pendant of Aquatic Agility?"
-
-class npc_silva_filnaveth : public CreatureScript
-{
-public:
- npc_silva_filnaveth() : CreatureScript("npc_silva_filnaveth") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF + 1:
- player->CLOSE_GOSSIP_MENU();
- if (player->getClass() == CLASS_DRUID && player->GetTeam() == ALLIANCE)
- player->ActivateTaxiPathTo(TAXI_PATH_ID_ALLY);
- break;
- case GOSSIP_ACTION_INFO_DEF + 2:
- player->SEND_GOSSIP_MENU(5374, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 3:
- player->SEND_GOSSIP_MENU(5375, creature->GetGUID());
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (player->getClass() != CLASS_DRUID)
- player->SEND_GOSSIP_MENU(4913, creature->GetGUID());
- else if (player->GetTeam() != ALLIANCE)
- {
- if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_AGI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
-
- player->SEND_GOSSIP_MENU(4915, creature->GetGUID());
- }
- else if (player->getClass() == CLASS_DRUID && player->GetTeam() == ALLIANCE)
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RUTHERAN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
-
- if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_AGI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
-
- player->SEND_GOSSIP_MENU(4914, creature->GetGUID());
- }
- return true;
- }
-
-};
-
-/*######
## npc_clintar_spirit
######*/
@@ -652,8 +529,6 @@ public:
void AddSC_moonglade()
{
- new npc_bunthen_plainswind();
- new npc_silva_filnaveth();
new npc_clintar_spirit();
new npc_omen();
new npc_giant_spotlight();
diff --git a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp
index adb6439272a..9c531c1f1be 100644
--- a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp
+++ b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp
@@ -32,6 +32,9 @@ EndContentData */
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "Player.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "GridNotifiers.h"
/*######
## npc_shenthul
@@ -273,8 +276,555 @@ public:
};
+/* --------- Herald of War ------------- */
+enum CitizenEntries
+{
+ NPC_GRYSHKA = 31433,
+ NPC_OLVIA = 31425,
+ NPC_SANA = 31429,
+ NPC_FELIKA = 31427,
+ NPC_THATHUNG = 31430,
+ NPC_KAJA = 31423
+};
+
+enum SceneEvents
+{
+ EVENT_SCENE_1 = 1,
+ EVENT_SCENE_2 = 2,
+ EVENT_SCENE_3 = 3,
+ EVENT_SCENE_4 = 4,
+ EVENT_SCENE_5 = 5,
+ EVENT_SCENE_6 = 6,
+ EVENT_SCENE_7 = 7,
+ EVENT_SCENE_8 = 8,
+ EVENT_SCENE_9 = 9,
+ EVENT_SCENE_10 = 10,
+ EVENT_SCENE_11 = 11,
+ EVENT_SCENE_12 = 12,
+ EVENT_SCENE_13 = 13,
+ EVENT_RESET = 14
+};
+
+enum CitizenTalk
+{
+ SAY_GRYSHKA_1 = 0, // When can we reopen our shops? I'm losing gold here!
+ SAY_GRYSHKA_2 = 1, // "This is an outrage!"
+ SAY_OLVIA_1 = 0, // Where is the Warchief?
+ SAY_OLVIA_2 = 1, // What are all these Forsaken doing here?
+ SAY_FELIKA_1 = 0, // What is going on?
+ SAY_FELIKA_2 = 1, // This is an outrage!
+ SAY_THATHUNG = 0, // What is going on?
+ SAY_KAJA = 0, // Why is Thrall allowing this to happen to our city?
+ SAY_SANA = 0, // We demand answers!
+ SAY_RUNTHAK_1 = 0, // SILENCE!
+ SAY_RUNTHAK_2 = 1, // We are on the brink of all out war with the Alliance!
+ SAY_RUNTHAK_3 = 2, // Tragic events have unfolded in Northrend. The Warchief is doing all that he can to keep us safe.
+ SAY_RUNTHAK_4 = 3 // All services and shops are to remain closed until further notice! That is all!
+};
+
+class npc_overlord_runthak_orgrimmar : public CreatureScript
+{
+public:
+ npc_overlord_runthak_orgrimmar() : CreatureScript("npc_overlord_runthak_orgrimmar") { }
+
+ struct npc_overlord_runthak_orgrimmarAI : public ScriptedAI
+ {
+ npc_overlord_runthak_orgrimmarAI(Creature* creature) : ScriptedAI(creature)
+ {
+ inProgress = false;
+ }
+
+ void Reset() override
+ {
+ inProgress = false;
+ me->GetMotionMaster()->MovePath(me->GetSpawnId() * 10, true);
+ events.Reset();
+ }
+
+ void MoveInLineOfSight(Unit* who) override
+ {
+ if (who->GetTypeId() == TYPEID_PLAYER && who->IsWithinDist(me, 20.0f) && !inProgress)
+ {
+ inProgress = true;
+ events.ScheduleEvent(EVENT_SCENE_1, 2000);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ events.Update(diff);
+
+ while (uint32 eventID = events.ExecuteEvent())
+ {
+ switch (eventID)
+ {
+ case EVENT_SCENE_1:
+ GetCitizenGuids();
+ me->GetMotionMaster()->MoveIdle();
+ if (Creature* gryshka = ObjectAccessor::GetCreature(*me, gryshkaGUID))
+ {
+ me->SetFacingTo(me->GetAngle(gryshka->GetPositionX(), gryshka->GetPositionY()));
+ gryshka->AI()->Talk(SAY_GRYSHKA_1);
+ }
+ events.ScheduleEvent(EVENT_SCENE_2, 4500);
+ break;
+ case EVENT_SCENE_2:
+ if (Creature* olvia = ObjectAccessor::GetCreature(*me, olviaGUID))
+ olvia->AI()->Talk(SAY_OLVIA_1);
+ events.ScheduleEvent(EVENT_SCENE_3, 4500);
+ break;
+ case EVENT_SCENE_3:
+ if (Creature* felika = ObjectAccessor::GetCreature(*me, felikaGUID))
+ felika->AI()->Talk(SAY_FELIKA_1);
+ events.ScheduleEvent(EVENT_SCENE_4, 4500);
+ break;
+ case EVENT_SCENE_4:
+ if (Creature* thathung = ObjectAccessor::GetCreature(*me, thungGUID))
+ thathung->AI()->Talk(SAY_THATHUNG);
+ events.ScheduleEvent(EVENT_SCENE_5, 4500);
+ break;
+ case EVENT_SCENE_5:
+ if (Creature* sana = ObjectAccessor::GetCreature(*me, sanaGUID))
+ sana->AI()->Talk(SAY_SANA);
+ events.ScheduleEvent(EVENT_SCENE_6, 4500);
+ break;
+ case EVENT_SCENE_6:
+ if (Creature* gryshka = ObjectAccessor::GetCreature(*me, gryshkaGUID))
+ gryshka->AI()->Talk(SAY_GRYSHKA_2);
+ events.ScheduleEvent(EVENT_SCENE_7, 4500);
+ break;
+ case EVENT_SCENE_7:
+ if (Creature* kaja = ObjectAccessor::GetCreature(*me, kajaGUID))
+ kaja->AI()->Talk(SAY_KAJA);
+ events.ScheduleEvent(EVENT_SCENE_8, 4500);
+ break;
+ case EVENT_SCENE_8:
+ if (Creature* felika = ObjectAccessor::GetCreature(*me, felikaGUID))
+ felika->AI()->Talk(SAY_FELIKA_2);
+ events.ScheduleEvent(EVENT_SCENE_9, 4500);
+ break;
+ case EVENT_SCENE_9:
+ if (Creature* olvia = ObjectAccessor::GetCreature(*me, olviaGUID))
+ olvia->AI()->Talk(SAY_OLVIA_2);
+ events.ScheduleEvent(EVENT_SCENE_10, 4500);
+ break;
+ case EVENT_SCENE_10:
+ Talk(SAY_RUNTHAK_1);
+ events.ScheduleEvent(EVENT_SCENE_11, 1500);
+ break;
+ case EVENT_SCENE_11:
+ Talk(SAY_RUNTHAK_2);
+ events.ScheduleEvent(EVENT_SCENE_12, 4500);
+ break;
+ case EVENT_SCENE_12:
+ Talk(SAY_RUNTHAK_3);
+ events.ScheduleEvent(EVENT_SCENE_13, 4500);
+ break;
+ case EVENT_SCENE_13:
+ Talk(SAY_RUNTHAK_4);
+ events.ScheduleEvent(EVENT_RESET, 25000);
+ break;
+ case EVENT_RESET:
+ Reset();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void GetCitizenGuids()
+ {
+ // if one GUID is empty it means all the others are empty as well so we should store them
+ // otherwise do not call for grid search since someone else already activated event once before and guids are stored
+ if (gryshkaGUID.IsEmpty())
+ {
+ std::list<Unit*> citizenList;
+ Trinity::AnyFriendlyUnitInObjectRangeCheck checker(me, me, 25.0f);
+ Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(me, citizenList, checker);
+ me->VisitNearbyObject(20.0f, searcher);
+ for (Unit* target : citizenList)
+ {
+ switch (target->GetEntry())
+ {
+ case NPC_GRYSHKA:
+ gryshkaGUID = target->GetGUID();
+ break;
+ case NPC_OLVIA:
+ olviaGUID = target->GetGUID();
+ break;
+ case NPC_SANA:
+ sanaGUID = target->GetGUID();
+ break;
+ case NPC_FELIKA:
+ felikaGUID = target->GetGUID();
+ break;
+ case NPC_THATHUNG:
+ thungGUID = target->GetGUID();
+ break;
+ case NPC_KAJA:
+ kajaGUID = target->GetGUID();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ private:
+ EventMap events;
+ bool inProgress;
+ ObjectGuid gryshkaGUID;
+ ObjectGuid olviaGUID;
+ ObjectGuid sanaGUID;
+ ObjectGuid felikaGUID;
+ ObjectGuid thungGUID;
+ ObjectGuid kajaGUID;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_overlord_runthak_orgrimmarAI(creature);
+ }
+};
+
+// Phased out thrall during herald of war chain quest
+enum HeraldEntries
+{
+ NPC_PORTAL_STORMWIND = 31640,
+ NPC_JAINA_PROUDMOORE = 31418,
+ NPC_BANSHEE_SYLVANAS = 31419,
+ NPC_KORKRON_GUARD = 31417,
+ NPC_THRALL_HERALD = 31412,
+
+ GO_PORTAL_UNDERCITY = 193425
+};
+
+enum HeraldMisc
+{
+ QUEST_HERALD_OF_WAR = 13257,
+ GUARDS_SIZE = 4
+};
+
+enum HeraldActions
+{
+ ACTION_START_SCENE = 0
+};
+
+enum HeraldSpell
+{
+ SPELL_JAINA_SPAWNIN = 55761
+};
+
+enum HeraldTalk
+{
+ SAY_THRALL_0 = 0, // Kor'kron, stand down!
+ SAY_THRALL_1 = 1, // Jaina...
+ SAY_THRALL_2 = 2, // Jaina, what happened at the Wrathgate. It was a betrayal from within...
+ SAY_THRALL_3 = 3, // The Horde has lost the Undercity.
+ SAY_THRALL_4 = 4, // We now prepare to lay siege to the city and bring the perpetrators of this unforgivable crime to justice.
+ SAY_THRALL_5 = 5, // If we are forced into a conflict, the Lich King will destroy our divided forces in Northrend.
+ SAY_THRALL_6 = 6, // We will make this right, Jaina. Tell your king all that you have learned here.
+ SAY_THRALL_7 = 7, // Kor'kron, prepare transport to the Undercity.
+
+ SAY_SYLVANAS_0 = 0, // Lady Proudmoore, the Warchief speaks the truth. This subterfuge was set in motion by Varimathras and Grand Apothecary Putress. It was not the Horde's doing.
+ SAY_SYLVANAS_1 = 1, // As the combined Horde and Alliance forces began their assault upon the Wrath Gate, an uprising broke out in the Undercity. Varimathras and hordes of his demonic brethren attacked. Hundreds of my people were slain in the coup. I barely managed to escape with my life.
+
+ SAY_JAINA_0 = 0, // Thrall, what has happened? The King is preparing for war...
+ SAY_JAINA_1 = 1, // I will deliver this information to King Wrynn, Thrall, but...
+ SAY_JAINA_2 = 2, // Bolvar was like a brother to him. In the King's absence, Bolvar kept the Alliance united. He found strength for our people in our darkest hours. He watched over Anduin, raising him as his own.
+ SAY_JAINA_3 = 3, // I fear that the rage will consume him, Thrall. I remain hopeful that reason will prevail, but we must prepare for the worst... for war.
+ SAY_JAINA_4 = 4 // Farewell, Warchief. I pray that the next time we meet it will be as allies.
+};
+
+enum HeraldEvents
+{
+ EVENT_HERALD_SCENE1 = 1,
+ EVENT_HERALD_SCENE2 = 2,
+ EVENT_HERALD_SCENE3 = 3,
+ EVENT_HERALD_SCENE4 = 4,
+ EVENT_HERALD_SCENE5 = 5,
+ EVENT_HERALD_SCENE6 = 6,
+ EVENT_HERALD_SCENE7 = 7,
+ EVENT_HERALD_SCENE8 = 8,
+ EVENT_HERALD_SCENE9 = 9,
+ EVENT_HERALD_SCENE10 = 10,
+ EVENT_HERALD_SCENE11 = 11,
+ EVENT_HERALD_SCENE12 = 12,
+ EVENT_HERALD_SCENE13 = 13,
+ EVENT_HERALD_SCENE14 = 14,
+ EVENT_HERALD_SCENE15 = 15,
+ EVENT_HERALD_RESET = 16
+};
+
+Position const GuardsSpawnPosition[GUARDS_SIZE] =
+{
+ { 1909.39f, -4144.21f, 40.6368f, 0.042239f },
+ { 1910.73f, -4155.26f, 40.6316f, 0.615577f },
+ { 1934.01f, -4141.40f, 40.6375f, 3.61109f },
+ { 1931.11f, -4156.38f, 40.6130f, 2.19737f }
+};
+
+Position const GuardsMovePosition[GUARDS_SIZE] =
+{
+ { 1917.461670f, -4147.514160f, 40.636799f, 5.89346f },
+ { 1916.181274f, -4152.295898f, 40.629120f, 0.497757f },
+ { 1926.435425f, -4146.397461f, 40.618534f, 3.846709f },
+ { 1926.519165f, -4153.216797f, 40.614975f, 2.570434f }
+};
+
+Position const MiscMovePositions[3] =
+{
+ { 1921.719604f, -4143.051270f, 40.623356f, 1.657789f }, // jaina move
+ { 1921.151855f, -4139.343750f, 40.583084f, 4.732627f }, // thrall move
+ { 1918.732422f, -4139.619629f, 40.607685f, 4.803311f } // sylvanas move
+};
+
+Position const PortalSpawnPosition = { 1921.752441f, -4151.148438f, 40.623848f, 1.714324f };
+
+class npc_thrall_herald_of_war : public CreatureScript
+{
+public:
+ npc_thrall_herald_of_war() : CreatureScript("npc_thrall_herald_of_war") { }
+
+ struct npc_thrall_herald_of_warAI : public ScriptedAI
+ {
+ npc_thrall_herald_of_warAI(Creature* creature) : ScriptedAI(creature)
+ {
+ spawnedGuards = false;
+ sceneInProgress = false;
+ }
+
+ void Reset() override
+ {
+ events.Reset();
+ me->GetMotionMaster()->MovePath(me->GetSpawnId() * 10, true);
+ sceneInProgress = false;
+
+ if (!spawnedGuards)
+ {
+ spawnedGuards = true;
+ for (uint8 i = 0; i < GUARDS_SIZE; i++)
+ {
+ if (Creature* korkronGuard = me->SummonCreature(NPC_KORKRON_GUARD, GuardsSpawnPosition[i]))
+ guardsGUIDs[i] = korkronGuard->GetGUID();
+ }
+ }
+ }
+
+ void MovementInform(uint32 type, uint32 pointId) override
+ {
+ if (type != POINT_MOTION_TYPE)
+ return;
+
+ if (pointId == 2)
+ me->GetMotionMaster()->MovePath(me->GetSpawnId() * 10, true);
+ }
+
+ void DoAction(int32 actionId) override
+ {
+ if (actionId == ACTION_START_SCENE && !sceneInProgress)
+ {
+ me->GetMotionMaster()->MoveIdle();
+ sceneInProgress = true;
+
+ if (Creature* jaina = me->SummonCreature(NPC_JAINA_PROUDMOORE, PortalSpawnPosition))
+ {
+ me->SetFacingTo(me->GetAngle(jaina->GetPositionX(), jaina->GetPositionY()));
+ jainaGUID = jaina->GetGUID();
+ jaina->CastSpell(jaina, SPELL_JAINA_SPAWNIN);
+ }
+
+ if (Creature* stormwindPortal = me->SummonCreature(NPC_PORTAL_STORMWIND, PortalSpawnPosition))
+ {
+ stormwindPortal->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ stormwindPortalGUID = stormwindPortal->GetGUID();
+ }
+
+ for (uint8 i = 0; i < GUARDS_SIZE; i++)
+ {
+ if (Creature* guards = ObjectAccessor::GetCreature(*me, guardsGUIDs[i]))
+ {
+ guards->SetWalk(false);
+ guards->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
+ guards->GetMotionMaster()->MovePoint(1, GuardsMovePosition[i]);
+ }
+ }
+
+ if (Creature* sylvanas = me->FindNearestCreature(NPC_BANSHEE_SYLVANAS, 25.0f))
+ sylvanasGUID = sylvanas->GetGUID();
+
+ events.ScheduleEvent(EVENT_HERALD_SCENE1, 4000);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ events.Update(diff);
+
+ while (uint32 eventID = events.ExecuteEvent())
+ {
+ switch (eventID)
+ {
+ case EVENT_HERALD_SCENE1:
+ Talk(SAY_THRALL_0);
+ me->GetMotionMaster()->MovePoint(1, MiscMovePositions[1]);
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ {
+ jaina->SetWalk(true);
+ jaina->GetMotionMaster()->MovePoint(1, MiscMovePositions[0]);
+ }
+
+ for (uint8 i = 0; i < GUARDS_SIZE; i++)
+ {
+ if (Creature* guard = ObjectAccessor::GetCreature(*me, guardsGUIDs[i]))
+ {
+ guard->GetMotionMaster()->MoveTargetedHome();
+ guard->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
+ }
+ }
+ events.ScheduleEvent(EVENT_HERALD_SCENE2, 3000);
+ break;
+ case EVENT_HERALD_SCENE2:
+ Talk(SAY_THRALL_1);
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ me->SetFacingTo(me->GetAngle(jaina->GetPositionX(), jaina->GetPositionY()));
+ events.ScheduleEvent(EVENT_HERALD_SCENE3, 3500);
+ break;
+ case EVENT_HERALD_SCENE3:
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ jaina->AI()->Talk(SAY_JAINA_0);
+ events.ScheduleEvent(EVENT_HERALD_SCENE4, 5500);
+ break;
+ case EVENT_HERALD_SCENE4:
+ Talk(SAY_THRALL_2);
+ if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID))
+ {
+ sylvanas->SetStandState(UNIT_STAND_STATE_STAND);
+ sylvanas->SetWalk(true);
+ sylvanas->GetMotionMaster()->MovePoint(1, MiscMovePositions[2]);
+ }
+ events.ScheduleEvent(EVENT_HERALD_SCENE5, 6500);
+ break;
+ case EVENT_HERALD_SCENE5:
+ if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID))
+ sylvanas->AI()->Talk(SAY_SYLVANAS_0);
+ events.ScheduleEvent(EVENT_HERALD_SCENE6, 10000);
+ break;
+ case EVENT_HERALD_SCENE6:
+ if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID))
+ sylvanas->AI()->Talk(SAY_SYLVANAS_1);
+ events.ScheduleEvent(EVENT_HERALD_SCENE7, 20000);
+ break;
+ case EVENT_HERALD_SCENE7:
+ Talk(SAY_THRALL_3);
+ events.ScheduleEvent(EVENT_HERALD_SCENE8, 4500);
+ break;
+ case EVENT_HERALD_SCENE8:
+ Talk(SAY_THRALL_4);
+ events.ScheduleEvent(EVENT_HERALD_SCENE9, 10000);
+ break;
+ case EVENT_HERALD_SCENE9:
+ Talk(SAY_THRALL_5);
+ events.ScheduleEvent(EVENT_HERALD_SCENE10, 8000);
+ break;
+ case EVENT_HERALD_SCENE10:
+ Talk(SAY_THRALL_6);
+ events.ScheduleEvent(EVENT_HERALD_SCENE11, 9000);
+ break;
+ case EVENT_HERALD_SCENE11:
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ jaina->AI()->Talk(SAY_JAINA_1);
+ events.ScheduleEvent(EVENT_HERALD_SCENE12, 6000);
+ break;
+ case EVENT_HERALD_SCENE12:
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ jaina->AI()->Talk(SAY_JAINA_2);
+ events.ScheduleEvent(EVENT_HERALD_SCENE13, 14000);
+ break;
+ case EVENT_HERALD_SCENE13:
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ jaina->AI()->Talk(SAY_JAINA_3);
+ events.ScheduleEvent(EVENT_HERALD_SCENE14, 10000);
+ break;
+ case EVENT_HERALD_SCENE14:
+ if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
+ {
+ jaina->AI()->Talk(SAY_JAINA_4);
+ jaina->SetWalk(true);
+ jaina->GetMotionMaster()->MovePoint(2, jaina->GetHomePosition());
+ jaina->DespawnOrUnsummon(5000);
+ }
+ events.ScheduleEvent(EVENT_HERALD_SCENE15, 7000);
+ break;
+ case EVENT_HERALD_SCENE15:
+ {
+ me->SetWalk(true);
+ me->GetMotionMaster()->MovePoint(2, me->GetHomePosition());
+ Talk(SAY_THRALL_7);
+
+ if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID))
+ {
+ sylvanas->SetWalk(true);
+ sylvanas->GetMotionMaster()->MovePoint(2, sylvanas->GetHomePosition());
+ }
+
+ if (Creature* portal = ObjectAccessor::GetCreature(*me, stormwindPortalGUID))
+ portal->DespawnOrUnsummon();
+
+ events.ScheduleEvent(EVENT_HERALD_RESET, 60000);
+ break;
+ }
+ case EVENT_HERALD_RESET:
+ Reset();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ EventMap events;
+ ObjectGuid guardsGUIDs[GUARDS_SIZE];
+ ObjectGuid jainaGUID;
+ ObjectGuid sylvanasGUID;
+ ObjectGuid stormwindPortalGUID;
+ bool spawnedGuards;
+ bool sceneInProgress;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_thrall_herald_of_warAI(creature);
+ }
+};
+
+class areatrigger_orgrimmar_herald_of_war : public AreaTriggerScript
+{
+public:
+ areatrigger_orgrimmar_herald_of_war() : AreaTriggerScript("areatrigger_orgrimmar_herald_of_war") { }
+
+ bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override
+ {
+ if (player->IsAlive() && player->GetQuestStatus(QUEST_HERALD_OF_WAR) == QUEST_STATUS_COMPLETE)
+ {
+ if (Creature* thrall = player->FindNearestCreature(NPC_THRALL_HERALD, 50.0f))
+ {
+ thrall->AI()->DoAction(ACTION_START_SCENE);
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
void AddSC_orgrimmar()
{
new npc_shenthul();
new npc_thrall_warchief();
+ new npc_overlord_runthak_orgrimmar();
+ new npc_thrall_herald_of_war();
+ new areatrigger_orgrimmar_herald_of_war();
}
diff --git a/src/server/scripts/Kalimdor/zone_silithus.cpp b/src/server/scripts/Kalimdor/zone_silithus.cpp
index cc285ab991e..db7bd62c5d1 100644
--- a/src/server/scripts/Kalimdor/zone_silithus.cpp
+++ b/src/server/scripts/Kalimdor/zone_silithus.cpp
@@ -19,13 +19,20 @@
/* ScriptData
SDName: Silithus
SD%Complete: 100
-SDComment: Quest support: 8304, 8507.
+SDComment: Quest support: 8348,8352,8361,8519
SDCategory: Silithus
EndScriptData */
/* ContentData
-npcs_rutgar_and_frankal
-quest_a_pawn_on_the_eternal_pawn
+A Pawn on the Eternal Board - creatures, gameobjects and defines
+quest_a_pawn_on_the_eternal_board
+npc_qiraj_war_spawn : Adds that are summoned in the Qiraj gates battle.
+npc_anachronos_the_ancient : Creature that controls the event.
+npc_anachronos_quest_trigger: controls the spawning of the BG War mobs.
+go_crystalline_tear : GameObject that begins the event and hands out quest
+TO DO: get correct spell IDs and timings for spells cast upon dragon transformations
+TO DO: Dragons should use the HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF) after transformation,
+ but for some unknown reason it doesn't work.
EndContentData */
#include "ScriptMgr.h"
@@ -34,195 +41,66 @@ EndContentData */
#include "Group.h"
#include "Player.h"
-/*###
-## npcs_rutgar_and_frankal
-###*/
-
-//gossip item text best guess
-#define GOSSIP_ITEM1 "I seek information about Natalia"
-
-#define GOSSIP_ITEM2 "That sounds dangerous!"
-#define GOSSIP_ITEM3 "What did you do?"
-#define GOSSIP_ITEM4 "Who?"
-#define GOSSIP_ITEM5 "Women do that. What did she demand?"
-#define GOSSIP_ITEM6 "What do you mean?"
-#define GOSSIP_ITEM7 "What happened next?"
-
-#define GOSSIP_ITEM11 "Yes, please continue"
-#define GOSSIP_ITEM12 "What language?"
-#define GOSSIP_ITEM13 "The Priestess attacked you?!"
-#define GOSSIP_ITEM14 "I should ask the monkey about this"
-#define GOSSIP_ITEM15 "Then what..."
-
-enum RutgarAndFrankal //trigger creatures to kill
-{
- TRIGGER_FRANKAL = 15221,
- TRIGGER_RUTGAR = 15222
-};
-
-class npcs_rutgar_and_frankal : public CreatureScript
-{
-public:
- npcs_rutgar_and_frankal() : CreatureScript("npcs_rutgar_and_frankal") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
- player->SEND_GOSSIP_MENU(7755, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 1:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
- player->SEND_GOSSIP_MENU(7756, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 2:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
- player->SEND_GOSSIP_MENU(7757, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 3:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
- player->SEND_GOSSIP_MENU(7758, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 4:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
- player->SEND_GOSSIP_MENU(7759, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 5:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
- player->SEND_GOSSIP_MENU(7760, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 6:
- player->SEND_GOSSIP_MENU(7761, creature->GetGUID());
- //'kill' our trigger to update quest status
- player->KilledMonsterCredit(TRIGGER_RUTGAR);
- break;
-
- case GOSSIP_ACTION_INFO_DEF + 9:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM11, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
- player->SEND_GOSSIP_MENU(7762, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 10:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM12, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
- player->SEND_GOSSIP_MENU(7763, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 11:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM13, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12);
- player->SEND_GOSSIP_MENU(7764, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 12:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM14, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13);
- player->SEND_GOSSIP_MENU(7765, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 13:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM15, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14);
- player->SEND_GOSSIP_MENU(7766, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 14:
- player->SEND_GOSSIP_MENU(7767, creature->GetGUID());
- //'kill' our trigger to update quest status
- player->KilledMonsterCredit(TRIGGER_FRANKAL);
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (player->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE &&
- creature->GetEntry() == 15170 &&
- !player->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
-
- if (player->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE &&
- creature->GetEntry() == 15171 &&
- player->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9);
-
- player->SEND_GOSSIP_MENU(7754, creature->GetGUID());
-
- return true;
- }
-
-};
-
-/*####
-# quest_a_pawn_on_the_eternal_board (Defines)
-####*/
-enum EternalBoard
-{
- QUEST_A_PAWN_ON_THE_ETERNAL_BOARD = 8519,
-
- FACTION_HOSTILE = 14,
- FACTION_FRIENDLY = 35,
-
- C_ANACHRONOS = 15381,
- C_FANDRAL_STAGHELM = 15382,
- C_ARYGOS = 15380,
- C_MERITHRA = 15378,
- C_CAELESTRASZ = 15379,
-
- ANACHRONOS_SAY_1 = 0,
- ANACHRONOS_SAY_2 = 1,
- ANACHRONOS_SAY_3 = 2,
- ANACHRONOS_SAY_4 = 3,
- ANACHRONOS_SAY_5 = 4,
- ANACHRONOS_SAY_6 = 5,
- ANACHRONOS_SAY_7 = 6,
- ANACHRONOS_SAY_8 = 7,
- ANACHRONOS_SAY_9 = 8,
- ANACHRONOS_SAY_10 = 9,
- ANACHRONOS_EMOTE_1 = 10,
- ANACHRONOS_EMOTE_2 = 11,
- ANACHRONOS_EMOTE_3 = 12,
-
- FANDRAL_SAY_1 = 0,
- FANDRAL_SAY_2 = 1,
- FANDRAL_SAY_3 = 2,
- FANDRAL_SAY_4 = 3,
- FANDRAL_SAY_5 = 4,
- FANDRAL_SAY_6 = 5,
- FANDRAL_EMOTE_1 = 6,
- FANDRAL_EMOTE_2 = 7,
-
- CAELESTRASZ_SAY_1 = 0,
- CAELESTRASZ_SAY_2 = 1,
- CAELESTRASZ_YELL_1 = 2,
-
- ARYGOS_SAY_1 = 0,
- ARYGOS_YELL_1 = 1,
- ARYGOS_EMOTE_1 = 2,
-
- MERITHRA_SAY_1 = 0,
- MERITHRA_SAY_2 = 1,
- MERITHRA_YELL_1 = 2,
- MERITHRA_EMOTE_1 = 3,
-
- GO_GATE_OF_AHN_QIRAJ = 176146,
- GO_GLYPH_OF_AHN_QIRAJ = 176148,
- GO_ROOTS_OF_AHN_QIRAJ = 176147
-};
/*#####
# Quest: A Pawn on the Eternal Board
#####*/
-/* ContentData
-A Pawn on the Eternal Board - creatures, gameobjects and defines
-npc_qiraj_war_spawn : Adds that are summoned in the Qiraj gates battle.
-npc_anachronos_the_ancient : Creature that controls the event.
-npc_anachronos_quest_trigger: controls the spawning of the BG War mobs.
-go_crystalline_tear : GameObject that begins the event and hands out quest
-TO DO: get correct spell IDs and timings for spells cast upon dragon transformations
-TO DO: Dragons should use the HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF) after transformation, but for some unknown reason it doesnt work.
-EndContentData */
-
-#define EVENT_AREA_RADIUS 65 //65yds
-#define EVENT_COOLDOWN 500000 //in ms. appear after event completed or failed (should be = Adds despawn time)
+enum EternalBoard
+{
+ QUEST_A_PAWN_ON_THE_ETERNAL_BOARD = 8519,
+
+ FACTION_HOSTILE = 14,
+ FACTION_FRIENDLY = 35,
+
+ EVENT_AREA_RADIUS = 65, // 65yds
+ EVENT_COOLDOWN = 500000, // in ms. appears after event completed or failed (should be = Adds despawn time)
+
+ NPC_ANACHRONOS = 15381,
+ NPC_FANDRAL_STAGHELM = 15382,
+ NPC_ARYGOS = 15380,
+ NPC_MERITHRA_OF_THE_DREAM = 15378,
+ NPC_CAELESTRASZ = 15379,
+
+ ANACHRONOS_SAY_1 = 0,
+ ANACHRONOS_SAY_2 = 1,
+ ANACHRONOS_SAY_3 = 2,
+ ANACHRONOS_SAY_4 = 3,
+ ANACHRONOS_SAY_5 = 4,
+ ANACHRONOS_SAY_6 = 5,
+ ANACHRONOS_SAY_7 = 6,
+ ANACHRONOS_SAY_8 = 7,
+ ANACHRONOS_SAY_9 = 8,
+ ANACHRONOS_SAY_10 = 9,
+ ANACHRONOS_EMOTE_1 = 10,
+ ANACHRONOS_EMOTE_2 = 11,
+ ANACHRONOS_EMOTE_3 = 12,
+
+ FANDRAL_SAY_1 = 0,
+ FANDRAL_SAY_2 = 1,
+ FANDRAL_SAY_3 = 2,
+ FANDRAL_SAY_4 = 3,
+ FANDRAL_SAY_5 = 4,
+ FANDRAL_SAY_6 = 5,
+ FANDRAL_EMOTE_1 = 6,
+ FANDRAL_EMOTE_2 = 7,
+
+ CAELESTRASZ_SAY_1 = 0,
+ CAELESTRASZ_SAY_2 = 1,
+ CAELESTRASZ_YELL_1 = 2,
+
+ ARYGOS_SAY_1 = 0,
+ ARYGOS_YELL_1 = 1,
+ ARYGOS_EMOTE_1 = 2,
+
+ MERITHRA_SAY_1 = 0,
+ MERITHRA_SAY_2 = 1,
+ MERITHRA_YELL_1 = 2,
+ MERITHRA_EMOTE_1 = 3,
+
+ GO_GATE_OF_AHN_QIRAJ = 176146,
+ GO_GLYPH_OF_AHN_QIRAJ = 176148,
+ GO_ROOTS_OF_AHN_QIRAJ = 176147
+};
struct QuestCinematic
{
@@ -233,59 +111,59 @@ struct QuestCinematic
// Creature 0 - Anachronos, 1 - Fandral, 2 - Arygos, 3 - Merithra, 4 - Caelestrasz
static QuestCinematic EventAnim[]=
{
- {ANACHRONOS_SAY_1, 0, 2000},
- {FANDRAL_SAY_1, 1, 4000},
- {MERITHRA_EMOTE_1, 3, 500},
- {MERITHRA_SAY_1, 3, 500},
- {ARYGOS_EMOTE_1, 2, 2000},
+ {ANACHRONOS_SAY_1, 0, 2000},
+ {FANDRAL_SAY_1, 1, 4000},
+ {MERITHRA_EMOTE_1, 3, 500},
+ {MERITHRA_SAY_1, 3, 500},
+ {ARYGOS_EMOTE_1, 2, 2000},
{CAELESTRASZ_SAY_1, 4, 8000},
- {MERITHRA_SAY_2, 3, 6000},
+ {MERITHRA_SAY_2, 3, 6000},
{0, 3, 2000},
- {MERITHRA_YELL_1, 3, 2500},
- {0, 3, 3000}, //Morph
- {0, 3, 4000}, //EmoteLiftoff
- {0, 3, 4000}, // spell
- {0, 3, 1250}, //fly
- {0, 3, 250}, //remove flags
+ {MERITHRA_YELL_1, 3, 2500},
+ {0, 3, 3000}, //Morph
+ {0, 3, 4000}, //EmoteLiftoff
+ {0, 3, 4000}, // spell
+ {0, 3, 1250}, //fly
+ {0, 3, 250}, //remove flags
{ARYGOS_SAY_1, 2, 3000},
{0, 3, 2000},
{ARYGOS_YELL_1, 2, 3000},
- {0, 3, 3000}, //Morph
- {0, 3, 4000}, //EmoteLiftoff
- {0, 3, 4000}, // spell
- {0, 3, 1000}, //fly
- {0, 3, 1000}, //remove flags
+ {0, 3, 3000}, //Morph
+ {0, 3, 4000}, //EmoteLiftoff
+ {0, 3, 4000}, // spell
+ {0, 3, 1000}, //fly
+ {0, 3, 1000}, //remove flags
{CAELESTRASZ_SAY_2, 4, 5000},
{0, 3, 3000},
{CAELESTRASZ_YELL_1, 4, 3000},
- {0, 3, 3000}, //Morph
- {0, 3, 4000}, //EmoteLiftoff
- {0, 3, 2500}, // spell
+ {0, 3, 3000}, //Morph
+ {0, 3, 4000}, //EmoteLiftoff
+ {0, 3, 2500}, // spell
{ANACHRONOS_SAY_2, 0, 2000},
- {0, 3, 250}, //fly
- {0, 3, 25}, //remove flags
+ {0, 3, 250}, //fly
+ {0, 3, 25}, //remove flags
{FANDRAL_SAY_2, 1, 3000},
- {ANACHRONOS_SAY_3, 0, 10000}, //Both run through the armies
- {0, 3, 2000}, // Sands will stop
- {0, 3, 8000}, // Summon Gate
+ {ANACHRONOS_SAY_3, 0, 10000}, //Both run through the armies
+ {0, 3, 2000}, // Sands will stop
+ {0, 3, 8000}, // Summon Gate
{ANACHRONOS_SAY_4, 0, 4000},
- {0, 0, 2000}, //spell 1-> Arcane cosmetic (Mobs freeze)
- {0, 0, 5000}, //Spell 2-> Arcane long cosmetic (barrier appears) (Barrier -> Glyphs)
- {0, 0, 7000}, //BarrieR
- {0, 0, 4000}, //Glyphs
+ {0, 0, 2000}, //spell 1-> Arcane cosmetic (Mobs freeze)
+ {0, 0, 5000}, //Spell 2-> Arcane long cosmetic (barrier appears) (Barrier -> Glyphs)
+ {0, 0, 7000}, //BarrieR
+ {0, 0, 4000}, //Glyphs
{ANACHRONOS_SAY_5, 0, 2000},
- {0, 0, 4000}, // Roots
- {FANDRAL_SAY_3, 1, 3000}, //Root Text
- {FANDRAL_EMOTE_1, 1, 3000}, //falls knee
+ {0, 0, 4000}, // Roots
+ {FANDRAL_SAY_3, 1, 3000}, //Root Text
+ {FANDRAL_EMOTE_1, 1, 3000}, //falls knee
{ANACHRONOS_SAY_6, 0, 3000},
{ANACHRONOS_SAY_7, 0, 3000},
{ANACHRONOS_SAY_8, 0, 8000},
- {ANACHRONOS_EMOTE_1, 0, 1000}, //Give Scepter
+ {ANACHRONOS_EMOTE_1, 0, 1000}, //Give Scepter
{FANDRAL_SAY_4, 1, 3000},
- {FANDRAL_SAY_5, 1, 3000}, //->Equip hammer~Scepter, throw it at door
- {FANDRAL_EMOTE_2, 1, 3000}, //Throw hammer at door.
+ {FANDRAL_SAY_5, 1, 3000}, //->Equip hammer~Scepter, throw it at door
+ {FANDRAL_EMOTE_2, 1, 3000}, //Throw hammer at door.
{ANACHRONOS_SAY_9, 0, 3000},
- {FANDRAL_SAY_6, 1, 3000}, //fandral goes away
+ {FANDRAL_SAY_6, 1, 3000}, //fandral goes away
{ANACHRONOS_EMOTE_2, 0, 3000},
{ANACHRONOS_EMOTE_3, 0, 3000},
{0, 0, 2000},
@@ -348,30 +226,30 @@ Position const SpawnLocation[] =
{-8078.0f, 1518.0f, 2.61f, 3.141592f}, //Kaldorei Infantry
{-8082.0f, 1516.0f, 2.61f, 3.141592f}, //Kaldorei Infantry
- {-8088.0f, 1510.0f, 2.61f, 0.0f}, //Anubisath Conqueror
- {-8084.0f, 1520.0f, 2.61f, 0.0f}, //Anubisath Conqueror
- {-8088.0f, 1530.0f, 2.61f, 0.0f}, //Anubisath Conqueror
-
- {-8080.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8082.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8085.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8082.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8085.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8080.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Wasp
-
- {-8082.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8079.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8080.0f, 1531.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8079.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp
- {-8080.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp
-
- {-8081.0f, 1514.0f, 2.61f, 0.0f}, //Qiraj Tank
- {-8081.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank
- {-8081.0f, 1526.0f, 2.61f, 0.0f}, //Qiraj Tank
- {-8081.0f, 1512.0f, 2.61f, 0.0f}, //Qiraj Tank
- {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank
- {-8081.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Tank
+ {-8088.0f, 1510.0f, 2.61f, 0.0f}, //Anubisath Conqueror
+ {-8084.0f, 1520.0f, 2.61f, 0.0f}, //Anubisath Conqueror
+ {-8088.0f, 1530.0f, 2.61f, 0.0f}, //Anubisath Conqueror
+
+ {-8080.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8082.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8085.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8082.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8085.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8080.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Wasp
+
+ {-8082.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8079.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8080.0f, 1531.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8079.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp
+ {-8080.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp
+
+ {-8081.0f, 1514.0f, 2.61f, 0.0f}, //Qiraj Tank
+ {-8081.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank
+ {-8081.0f, 1526.0f, 2.61f, 0.0f}, //Qiraj Tank
+ {-8081.0f, 1512.0f, 2.61f, 0.0f}, //Qiraj Tank
+ {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank
+ {-8081.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Tank
{-8082.0f, 1513.0f, 2.61f, 3.141592f}, //Anubisath Conqueror
{-8082.0f, 1520.0f, 2.61f, 3.141592f}, //Anubisath Conqueror
@@ -402,14 +280,36 @@ struct SpawnSpells
static SpawnSpells SpawnCast[4] =
{
- {100000, 2000, 33652}, // Stop Time
+ {100000, 2000, 33652}, // Stoned
{38500, 300000, 28528}, // Poison Cloud
{58000, 300000, 35871}, // Frost Debuff (need correct spell)
{80950, 300000, 42075}, // Fire Explosion (need correct spell however this one looks cool)
};
+
/*#####
# npc_anachronos_the_ancient
######*/
+
+enum AnachronosTheAncient
+{
+ NPC_QIRAJI_WASP = 15414,
+ NPC_QIRAJI_TANK = 15422,
+ NPC_KALDOREI_INFANTRY = 15423,
+ NPC_ANUBISATH_CONQUEROR = 15424,
+ SPELL_ARCANE_CHANNELING = 23017,
+ SPELL_NOXIOUS_BREATH = 24818,
+ SPELL_GREEN_DRAGON_TRANSFORM_DND = 25105,
+ SPELL_RED_DRAGON_TRANSFORM_DND = 25106,
+ SPELL_BLUE_DRAGON_TRANSFORM_DND = 25107,
+ SPELL_TIME_STOP = 25158,
+ SPELL_CALL_PRISMATIC_BARRIER = 25159,
+ SPELL_CALL_GLYPHS_OF_WARDING = 25166,
+ SPELL_CALL_ANCIENTS = 25167,
+ SPELL_THROW_HAMMER = 33806,
+ SPELL_FROST_BREATH = 50505,
+ SPELL_FLAME_BREATH = 54293
+};
+
class npc_anachronos_the_ancient : public CreatureScript
{
public:
@@ -464,10 +364,10 @@ public:
if (!player)
return;
- Creature* Fandral = player->FindNearestCreature(C_FANDRAL_STAGHELM, 100.0f);
- Creature* Arygos = player->FindNearestCreature(C_ARYGOS, 100.0f);
- Creature* Caelestrasz = player->FindNearestCreature(C_CAELESTRASZ, 100.0f);
- Creature* Merithra = player->FindNearestCreature(C_MERITHRA, 100.0f);
+ Creature* Fandral = player->FindNearestCreature(NPC_FANDRAL_STAGHELM, 100.0f);
+ Creature* Arygos = player->FindNearestCreature(NPC_ARYGOS, 100.0f);
+ Creature* Caelestrasz = player->FindNearestCreature(NPC_CAELESTRASZ, 100.0f);
+ Creature* Merithra = player->FindNearestCreature(NPC_MERITHRA_OF_THE_DREAM, 100.0f);
if (!Fandral || !Arygos || !Caelestrasz || !Merithra)
return;
@@ -509,7 +409,7 @@ public:
Merithra->AI()->Talk(MERITHRA_YELL_1);
break;
case 9:
- Merithra->CastSpell(Merithra, 25105, true);
+ Merithra->CastSpell(Merithra, SPELL_GREEN_DRAGON_TRANSFORM_DND, true);
break;
case 10:
Merithra->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
@@ -517,7 +417,7 @@ public:
Merithra->GetMotionMaster()->MoveCharge(-8065, 1530, 6.61f, 3);
break;
case 11:
- Merithra->CastSpell(Merithra, 24818, false);
+ Merithra->CastSpell(Merithra, SPELL_NOXIOUS_BREATH, false);
break;
case 12:
Merithra->GetMotionMaster()->MoveCharge(-8100, 1530, 50, 42);
@@ -536,7 +436,7 @@ public:
Arygos->AI()->Talk(ARYGOS_YELL_1);
break;
case 17:
- Arygos->CastSpell(Arygos, 25107, true);
+ Arygos->CastSpell(Arygos, SPELL_BLUE_DRAGON_TRANSFORM_DND, true);
break;
case 18:
Arygos->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
@@ -544,7 +444,7 @@ public:
Arygos->GetMotionMaster()->MoveCharge(-8065, 1530, 6.61f, 42);
break;
case 19:
- Arygos->CastSpell(Arygos, 50505, false);
+ Arygos->CastSpell(Arygos, SPELL_FROST_BREATH, false);
break;
case 20:
Arygos->GetMotionMaster()->MoveCharge(-8095, 1530, 50, 42);
@@ -563,15 +463,15 @@ public:
Caelestrasz->AI()->Talk(CAELESTRASZ_YELL_1);
break;
case 25:
- Caelestrasz->CastSpell(Caelestrasz, 25106, true);
+ Caelestrasz->CastSpell(Caelestrasz, SPELL_RED_DRAGON_TRANSFORM_DND, true);
break;
case 26:
- Caelestrasz->HandleEmoteCommand(254);
+ Caelestrasz->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
Caelestrasz->SetDisableGravity(true);
Caelestrasz->GetMotionMaster()->MoveCharge(-8065, 1530, 7.61f, 4);
break;
case 27:
- Caelestrasz->CastSpell(Caelestrasz, 54293, false);
+ Caelestrasz->CastSpell(Caelestrasz, SPELL_FLAME_BREATH, false);
break;
case 28:
Talk(ANACHRONOS_SAY_2, Fandral);
@@ -596,29 +496,29 @@ public:
Caelestrasz->GetMotionMaster()->MoveCharge(-8050, 1473, 65, 15);
break; //Text: sands will stop
case 34:
- DoCast(player, 23017, true);//Arcane Channeling
+ DoCast(player, SPELL_ARCANE_CHANNELING, true);//Arcane Channeling
break;
case 35:
- me->CastSpell(-8088, 1520.43f, 2.67f, 25158, true);
+ me->CastSpell(-8088, 1520.43f, 2.67f, SPELL_TIME_STOP, true);
break;
case 36:
- DoCast(player, 25159, true);
+ DoCast(player, SPELL_CALL_PRISMATIC_BARRIER, true);
break;
case 37:
- me->SummonGameObject(GO_GATE_OF_AHN_QIRAJ, -8130, 1525, 17.5f, 0, 0, 0, 0, 0, 0);
+ me->SummonGameObject(GO_GATE_OF_AHN_QIRAJ, Position(-8130.f, 1525.f, 17.5f, 0.f), G3D::Quat(), 0);
break;
case 38:
- DoCast(player, 25166, true);
- me->SummonGameObject(GO_GLYPH_OF_AHN_QIRAJ, -8130, 1525, 17.5f, 0, 0, 0, 0, 0, 0);
+ DoCast(player, SPELL_CALL_GLYPHS_OF_WARDING, true);
+ me->SummonGameObject(GO_GLYPH_OF_AHN_QIRAJ, Position(-8130.f, 1525.f, 17.5f, 0.f), G3D::Quat(), 0);
break;
case 39:
Talk(ANACHRONOS_SAY_5, Fandral);
break;
case 40:
- Fandral->CastSpell(me, 25167, true);
+ Fandral->CastSpell(me, SPELL_CALL_ANCIENTS, true);
break;
case 41:
- Fandral->SummonGameObject(GO_ROOTS_OF_AHN_QIRAJ, -8130, 1525, 17.5f, 0, 0, 0, 0, 0, 0);
+ Fandral->SummonGameObject(GO_ROOTS_OF_AHN_QIRAJ, Position(-8130.f, 1525.f, 17.5f, 0.f), G3D::Quat(), 0);
Fandral->AI()->Talk(FANDRAL_SAY_3);
break;
case 42:
@@ -649,11 +549,11 @@ public:
break;
case 50:
Fandral->AI()->Talk(FANDRAL_EMOTE_2);
- Fandral->CastSpell(-8127, 1525, 17.5f, 33806, true);
+ Fandral->CastSpell(-8127, 1525, 17.5f, SPELL_THROW_HAMMER, true);
break;
case 51:
{
- uint32 entries[4] = { 15423, 15424, 15414, 15422 };
+ uint32 entries[4] = { NPC_KALDOREI_INFANTRY, NPC_ANUBISATH_CONQUEROR, NPC_QIRAJI_WASP, NPC_QIRAJI_TANK };
Unit* mob = NULL;
for (uint8 i = 0; i < 4; ++i)
{
@@ -661,7 +561,7 @@ public:
while (mob)
{
mob->RemoveFromWorld();
- mob = player->FindNearestCreature(15423, 50);
+ mob = player->FindNearestCreature(NPC_KALDOREI_INFANTRY, 50);
}
}
break;
@@ -706,7 +606,7 @@ public:
me->SetDisplayId(15500);
break;
case 63:
- me->HandleEmoteCommand(254);
+ me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
break;
case 64:
@@ -746,6 +646,13 @@ public:
# npc_qiraj_war_spawn
######*/
+enum QirajWarSpawn
+{
+ SPELL_STONED_CHANNEL_CAST_VISUAL = 15533,
+ SPELL_SUMMON_POISON_CLOUD = 24319,
+ SPELL_STONED = 33652
+};
+
class npc_qiraj_war_spawn : public CreatureScript
{
public:
@@ -793,22 +700,22 @@ public:
{
if (!Timers)
{
- if (me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414) //all but Kaldorei Soldiers
+ if (me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP) //all but Kaldorei Soldiers
{
SpellTimer1 = SpawnCast[1].Timer1;
SpellTimer2 = SpawnCast[2].Timer1;
SpellTimer3 = SpawnCast[3].Timer1;
}
- if (me->GetEntry() == 15423 || me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414)
+ if (me->GetEntry() == NPC_KALDOREI_INFANTRY || me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP)
SpellTimer4 = SpawnCast[0].Timer1;
Timers = true;
}
- if (me->GetEntry() == 15424 || me->GetEntry() == 15422|| me->GetEntry() == 15414)
+ if (me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP)
{
if (SpellTimer1 <= diff)
{
DoCast(me, SpawnCast[1].SpellId);
- DoCast(me, 24319);
+ DoCast(me, SPELL_SUMMON_POISON_CLOUD);
SpellTimer1 = SpawnCast[1].Timer2;
} else SpellTimer1 -= diff;
if (SpellTimer2 <= diff)
@@ -822,38 +729,38 @@ public:
SpellTimer3 = SpawnCast[3].Timer2;
} else SpellTimer3 -= diff;
}
- if (me->GetEntry() == 15423 || me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414)
+ if (me->GetEntry() == NPC_KALDOREI_INFANTRY || me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP)
{
if (SpellTimer4 <= diff)
{
me->RemoveAllAttackers();
me->AttackStop();
- DoCast(me, 15533);
+ DoCast(me, SPELL_STONED_CHANNEL_CAST_VISUAL);
SpellTimer4 = SpawnCast[0].Timer2;
} else SpellTimer4 -= diff;
}
if (!hasTarget)
{
Unit* target = NULL;
- if (me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414)
- target = me->FindNearestCreature(15423, 20, true);
- if (me->GetEntry() == 15423)
+ if (me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP)
+ target = me->FindNearestCreature(NPC_KALDOREI_INFANTRY, 20, true);
+ if (me->GetEntry() == NPC_KALDOREI_INFANTRY)
{
uint8 tar = urand(0, 2);
if (tar == 0)
- target = me->FindNearestCreature(15422, 20, true);
+ target = me->FindNearestCreature(NPC_QIRAJI_TANK, 20, true);
else if (tar == 1)
- target = me->FindNearestCreature(15424, 20, true);
+ target = me->FindNearestCreature(NPC_ANUBISATH_CONQUEROR, 20, true);
else if (tar == 2)
- target = me->FindNearestCreature(15414, 20, true);
+ target = me->FindNearestCreature(NPC_QIRAJI_WASP, 20, true);
}
hasTarget = true;
if (target)
AttackStart(target);
}
- if (!(me->FindNearestCreature(15379, 60)))
- DoCast(me, 33652);
+ if (!(me->FindNearestCreature(NPC_CAELESTRASZ, 60)))
+ DoCast(me, SPELL_STONED);
if (!UpdateVictim())
{
@@ -932,7 +839,7 @@ public:
if (Creature* spawn = me->SummonCreature(WavesInfo[WaveCount].CreatureId, SpawnLocation[i], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, desptimer))
{
- if (spawn->GetEntry() == 15423)
+ if (spawn->GetEntry() == NPC_KALDOREI_INFANTRY)
spawn->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15427 + rand32() % 4);
if (i >= 30) WaveCount = 1;
if (i >= 33) WaveCount = 2;
@@ -1040,6 +947,14 @@ void npc_qiraj_war_spawn::npc_qiraj_war_spawnAI::JustDied(Unit* /*slayer*/)
# go_crystalline_tear
######*/
+enum CrystallineTear
+{
+ ARYGOS_GNOME_FORM = 15418,
+ CAELESTRASZ_NIGHT_ELF_FORM = 15419,
+ MERITHRA_NIGHT_ELF_FORM = 15420,
+ ANACHRONOS_QUEST_TRIGGER_INVISIBLE = 15454
+};
+
class go_crystalline_tear : public GameObjectScript
{
public:
@@ -1049,35 +964,35 @@ public:
{
if (quest->GetQuestId() == QUEST_A_PAWN_ON_THE_ETERNAL_BOARD)
{
- if (Creature* trigger = go->FindNearestCreature(15454, 100))
+ if (Creature* trigger = go->FindNearestCreature(ANACHRONOS_QUEST_TRIGGER_INVISIBLE, 100))
{
- Unit* Merithra = trigger->SummonCreature(15378, -8034.535f, 1535.14f, 2.61f, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
- Unit* Caelestrasz = trigger->SummonCreature(15379, -8032.767f, 1533.148f, 2.61f, 1.5f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
- Unit* Arygos = trigger->SummonCreature(15380, -8034.52f, 1537.843f, 2.61f, 5.7f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
- /* Unit* Fandral = */ trigger->SummonCreature(15382, -8028.462f, 1535.843f, 2.61f, 3.141592f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
- Creature* Anachronos = trigger->SummonCreature(15381, -8028.75f, 1538.795f, 2.61f, 4, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
+ Unit* Merithra = trigger->SummonCreature(NPC_MERITHRA_OF_THE_DREAM, -8034.535f, 1535.14f, 2.61f, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
+ Unit* Caelestrasz = trigger->SummonCreature(NPC_CAELESTRASZ, -8032.767f, 1533.148f, 2.61f, 1.5f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
+ Unit* Arygos = trigger->SummonCreature(NPC_ARYGOS, -8034.52f, 1537.843f, 2.61f, 5.7f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
+ /* Unit* Fandral = */ trigger->SummonCreature(NPC_FANDRAL_STAGHELM, -8028.462f, 1535.843f, 2.61f, 3.141592f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
+ Creature* Anachronos = trigger->SummonCreature(NPC_ANACHRONOS, -8028.75f, 1538.795f, 2.61f, 4, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000);
if (Merithra)
{
- Merithra->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ Merithra->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
Merithra->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- Merithra->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15420);
+ Merithra->SetUInt32Value(UNIT_FIELD_DISPLAYID, MERITHRA_NIGHT_ELF_FORM);
Merithra->setFaction(35);
}
if (Caelestrasz)
{
- Caelestrasz->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ Caelestrasz->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
Caelestrasz->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- Caelestrasz->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15419);
+ Caelestrasz->SetUInt32Value(UNIT_FIELD_DISPLAYID, CAELESTRASZ_NIGHT_ELF_FORM);
Caelestrasz->setFaction(35);
}
if (Arygos)
{
- Arygos->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ Arygos->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
Arygos->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- Arygos->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15418);
+ Arygos->SetUInt32Value(UNIT_FIELD_DISPLAYID, ARYGOS_GNOME_FORM);
Arygos->setFaction(35);
}
@@ -1107,112 +1022,107 @@ public:
enum WSSpells
{
- SPELL_PUNISHMENT = 24803,
- SPELL_SPAWN_IN = 25035,
-
- AURA_TWILIGHT_SET = 24746,
- AURA_MEDALLION = 24748,
- AURA_RING = 24782,
-
- SPELL_TEMPLAR_RANDOM = 24745,
- SPELL_TEMPLAR_FIRE = 24747,
- SPELL_TEMPLAR_AIR = 24757,
- SPELL_TEMPLAR_EARTH = 24759,
- SPELL_TEMPLAR_WATER = 24761,
-
- SPELL_DUKE_RANDOM = 24762,
- SPELL_DUKE_FIRE = 24766,
- SPELL_DUKE_AIR = 24769,
- SPELL_DUKE_EARTH = 24771,
- SPELL_DUKE_WATER = 24773,
-
- SPELL_ROYAL_RANDOM = 24785,
- SPELL_ROYAL_FIRE = 24787,
- SPELL_ROYAL_AIR = 24791,
- SPELL_ROYAL_EARTH = 24792,
- SPELL_ROYAL_WATER = 24793
+ AURA_TWILIGHT_SET = 24746,
+ AURA_MEDALLION = 24748,
+ AURA_RING = 24782,
+
+ SPELL_TEMPLAR_RANDOM = 24745,
+ SPELL_TEMPLAR_FIRE = 24747,
+ SPELL_TEMPLAR_AIR = 24757,
+ SPELL_TEMPLAR_EARTH = 24759,
+ SPELL_TEMPLAR_WATER = 24761,
+
+ SPELL_DUKE_RANDOM = 24762,
+ SPELL_DUKE_FIRE = 24766,
+ SPELL_DUKE_AIR = 24769,
+ SPELL_DUKE_EARTH = 24771,
+ SPELL_DUKE_WATER = 24773,
+
+ SPELL_ROYAL_RANDOM = 24785,
+ SPELL_ROYAL_FIRE = 24787,
+ SPELL_ROYAL_AIR = 24791,
+ SPELL_ROYAL_EARTH = 24792,
+ SPELL_ROYAL_WATER = 24793,
+
+ SPELL_PUNISHMENT = 24803,
+ SPELL_SPAWN_IN = 25035
};
enum WSGossip
{
- GOSSIPID_LESSER_WS = 6540,
- GOSSIPID_WS = 6542,
- GOSSIPID_GREATER_WS = 6543
+ OPTION_ID_WS_RANDOM = 0,
+ OPTION_ID_1_CRIMSON = 1,
+ OPTION_ID_2_AZURE = 2,
+ OPTION_ID_3_EARTHEN = 3,
+ OPTION_ID_4_HOARY = 4,
+ OPTION_ID_1_CYNDERS = 1,
+ OPTION_ID_2_FATHOMS = 2,
+ OPTION_ID_3_SHARDS = 3,
+ OPTION_ID_4_ZEPHYRS = 4,
+ OPTION_ID_1_SKALDRENOX = 1,
+ OPTION_ID_2_SKWOL = 2,
+ OPTION_ID_3_KAZUM = 3,
+ OPTION_ID_4_WHIRLAXIS = 4,
+ GOSSIP_ID_LESSER_WS = 6540,
+ GOSSIP_ID_WIND_STONE = 6542,
+ GOSSIP_ID_GREATER_WS = 6543
};
enum WSCreatures
{
- NPC_TEMPLAR_FIRE = 15209,
- NPC_TEMPLAR_WATER = 15211,
- NPC_TEMPLAR_AIR = 15212,
- NPC_TEMPLAR_EARTH = 15307,
-
- NPC_DUKE_FIRE = 15206,
- NPC_DUKE_WATER = 15207,
- NPC_DUKE_EARTH = 15208,
- NPC_DUKE_AIR = 15220,
-
- NPC_ROYAL_FIRE = 15203,
- NPC_ROYAL_AIR = 15204,
- NPC_ROYAL_EARTH = 15205,
- NPC_ROYAL_WATER = 15305
+ NPC_TEMPLAR_FIRE = 15209,
+ NPC_TEMPLAR_WATER = 15211,
+ NPC_TEMPLAR_AIR = 15212,
+ NPC_TEMPLAR_EARTH = 15307,
+
+ NPC_DUKE_FIRE = 15206,
+ NPC_DUKE_WATER = 15207,
+ NPC_DUKE_EARTH = 15208,
+ NPC_DUKE_AIR = 15220,
+
+ NPC_ROYAL_FIRE = 15203,
+ NPC_ROYAL_AIR = 15204,
+ NPC_ROYAL_EARTH = 15205,
+ NPC_ROYAL_WATER = 15305
};
enum WSItems
{
- ITEM_TEMPLAR_FIRE = 20416,
- ITEM_TEMPLAR_EARTH = 20419,
- ITEM_TEMPLAR_WATER = 20420,
- ITEM_TEMPLAR_AIR = 20418,
-
- ITEM_DUKE_FIRE = 20432,
- ITEM_DUKE_EARTH = 20435,
- ITEM_DUKE_WATER = 20436,
- ITEM_DUKE_AIR = 20433,
-
- ITEM_ROYAL_FIRE = 20447,
- ITEM_ROYAL_EARTH = 20449,
- ITEM_ROYAL_WATER = 20450,
- ITEM_ROYAL_AIR = 20448,
+ ITEM_TEMPLAR_FIRE = 20416,
+ ITEM_TEMPLAR_AIR = 20418,
+ ITEM_TEMPLAR_EARTH = 20419,
+ ITEM_TEMPLAR_WATER = 20420,
+
+ ITEM_DUKE_FIRE = 20432,
+ ITEM_DUKE_AIR = 20433,
+ ITEM_DUKE_EARTH = 20435,
+ ITEM_DUKE_WATER = 20436,
+
+ ITEM_ROYAL_FIRE = 20447,
+ ITEM_ROYAL_AIR = 20448,
+ ITEM_ROYAL_EARTH = 20449,
+ ITEM_ROYAL_WATER = 20450
};
enum WS
{
- TEMPLAR = 0,
- DUKE = 1,
- ROYAL = 2,
-
- FIRE = 0x1,
- WATER = 0x2,
- EARTH = 0x4,
- AIR = 0x8
-};
+ TEMPLAR = 0,
+ DUKE = 1,
+ ROYAL = 2,
+
+ FIRE = 0x1,
+ WATER = 0x2,
+ EARTH = 0x4,
+ AIR = 0x8
+};
enum WSTexts
{
- SAY_TEMPLAR_AGGRO = 0,
- SAY_DUKE_AGGRO = 0,
- YELL_ROYAL_AGGRO = 0
+ SAY_TEMPLAR_AGGRO = 0,
+ SAY_DUKE_AGGRO = 0,
+ YELL_ROYAL_AGGRO = 0
};
-#define GOSSIP_TEMPLAR_RANDOM "I am no cultist, you monster! Come to me and face your destruction!"
-#define GOSSIP_TEMPLAR_FIRE "Crimson Templar! I hold your signet! Heed my call!"
-#define GOSSIP_TEMPLAR_EARTH "Earthen Templar! I hold your signet! Heed my call!"
-#define GOSSIP_TEMPLAR_AIR "Hoary Templar! I hold your signet! Heed my call!"
-#define GOSSIP_TEMPLAR_WATER "Azure Templar! I hold your signet! Heed my call!"
-
-#define GOSSIP_DUKE_RANDOM "You will listen to this, vile duke! I am not your Twilight's Hammer lapdog! I am here to challenge you! Come! Come, and meet your death..."
-#define GOSSIP_DUKE_FIRE "Duke of Cynders! I hold your signet! Heed my call!"
-#define GOSSIP_DUKE_EARTH "The Duke of Shards! I hold your signet! Heed my call!"
-#define GOSSIP_DUKE_AIR "The Duke of Zephyrs! I hold your signet! Heed my call!"
-#define GOSSIP_DUKE_WATER "The Duke of Fathoms! I hold your signet! Heed my call!"
-
-#define GOSSIP_ROYAL_RANDOM "The day of the judgement has come, fiend! I challenge you to battle!"
-#define GOSSIP_ROYAL_FIRE "Prince Skaldrenox! I hold your signet! Heed my call!"
-#define GOSSIP_ROYAL_EARTH "Baron Kazum! I hold your signet! Heed my call!"
-#define GOSSIP_ROYAL_AIR "High Marshal Whirlaxis! I hold your signet! Heed my call!"
-#define GOSSIP_ROYAL_WATER "Lord Skwol! I hold your signet! Heed my call!"
-
class go_wind_stone : public GameObjectScript
{
public:
@@ -1294,7 +1204,7 @@ class go_wind_stone : public GameObjectScript
case NPC_TEMPLAR_WATER:
case NPC_TEMPLAR_AIR:
case NPC_TEMPLAR_EARTH:
- summons->AI()->Talk(SAY_TEMPLAR_AGGRO);
+ summons->AI()->Talk(SAY_TEMPLAR_AGGRO, player);
break;
case NPC_DUKE_FIRE:
@@ -1323,10 +1233,10 @@ class go_wind_stone : public GameObjectScript
uint32 gossipId = go->GetGOInfo()->GetGossipMenuId();
switch (gossipId)
{
- case GOSSIPID_LESSER_WS:
+ case GOSSIP_ID_LESSER_WS:
{
if (rank >= 1) // 1 or 2 or 3
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_WS_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
else
{
go->CastSpell(player, SPELL_PUNISHMENT);
@@ -1335,19 +1245,19 @@ class go_wind_stone : public GameObjectScript
uint8 item = GetItems(player, TEMPLAR);
if (item & FIRE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_FIRE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_1_CRIMSON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
if (item & WATER)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_WATER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_2_AZURE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
if (item & EARTH)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_EARTH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_3_EARTHEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
if (item & AIR)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_AIR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_4_HOARY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
break;
}
- case GOSSIPID_WS:
+ case GOSSIP_ID_WIND_STONE:
{
if (rank >= 2) // 2 or 3
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_WS_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
else
{
go->CastSpell(player, SPELL_PUNISHMENT);
@@ -1356,19 +1266,19 @@ class go_wind_stone : public GameObjectScript
uint8 item = GetItems(player, DUKE);
if (item & FIRE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_FIRE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_1_CYNDERS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
if (item & WATER)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_WATER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_2_FATHOMS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
if (item & EARTH)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_EARTH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_3_SHARDS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9);
if (item & AIR)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_AIR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_4_ZEPHYRS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10);
break;
}
- case GOSSIPID_GREATER_WS:
+ case GOSSIP_ID_GREATER_WS:
{
if (rank == 3) // 3
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_WS_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
else
{
go->CastSpell(player, SPELL_PUNISHMENT);
@@ -1377,13 +1287,13 @@ class go_wind_stone : public GameObjectScript
uint8 item = GetItems(player, ROYAL);
if (item & FIRE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_FIRE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_1_SKALDRENOX, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12);
if (item & WATER)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_WATER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_2_SKWOL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13);
if (item & EARTH)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_EARTH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_3_KAZUM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14);
if (item & AIR)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_AIR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 15);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_4_WHIRLAXIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 15);
break;
}
default:
@@ -1462,6 +1372,5 @@ void AddSC_silithus()
new npc_anachronos_quest_trigger();
new npc_anachronos_the_ancient();
new npc_qiraj_war_spawn();
- new npcs_rutgar_and_frankal();
new go_wind_stone();
}
diff --git a/src/server/scripts/Kalimdor/zone_tanaris.cpp b/src/server/scripts/Kalimdor/zone_tanaris.cpp
index 22b4c86cbcd..966bbaec1da 100644
--- a/src/server/scripts/Kalimdor/zone_tanaris.cpp
+++ b/src/server/scripts/Kalimdor/zone_tanaris.cpp
@@ -19,15 +19,13 @@
/* ScriptData
SDName: Tanaris
SD%Complete: 80
-SDComment: Quest support: 648, 1560, 2954, 4005, 10277, 10279(Special flight path).
+SDComment: Quest support: 648, 1560, 4005, 10277
SDCategory: Tanaris
EndScriptData */
/* ContentData
npc_aquementas
npc_custodian_of_time
-npc_steward_of_time
-npc_stone_watcher_of_norgannon
npc_OOX17
npc_tooga
EndContentData */
@@ -298,116 +296,6 @@ public:
};
/*######
-## npc_steward_of_time
-######*/
-
-#define GOSSIP_ITEM_FLIGHT "Please take me to the master's lair."
-
-class npc_steward_of_time : public CreatureScript
-{
-public:
- npc_steward_of_time() : CreatureScript("npc_steward_of_time") { }
-
- bool OnQuestAccept(Player* player, Creature* /*creature*/, Quest const* quest) override
- {
- if (quest->GetQuestId() == 10279) //Quest: To The Master's Lair
- player->CastSpell(player, 34891, true); //(Flight through Caverns)
-
- return false;
- }
-
- bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF + 1)
- player->CastSpell(player, 34891, true); //(Flight through Caverns)
-
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (player->GetQuestStatus(10279) == QUEST_STATUS_INCOMPLETE || player->GetQuestRewardStatus(10279))
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
- player->SEND_GOSSIP_MENU(9978, creature->GetGUID());
- }
- else
- player->SEND_GOSSIP_MENU(9977, creature->GetGUID());
-
- return true;
- }
-
-};
-
-/*######
-## npc_stone_watcher_of_norgannon
-######*/
-
-#define GOSSIP_ITEM_NORGANNON_1 "What function do you serve?"
-#define GOSSIP_ITEM_NORGANNON_2 "What are the Plates of Uldum?"
-#define GOSSIP_ITEM_NORGANNON_3 "Where are the Plates of Uldum?"
-#define GOSSIP_ITEM_NORGANNON_4 "Excuse me? We've been \"reschedueled for visitations\"? What does that mean?!"
-#define GOSSIP_ITEM_NORGANNON_5 "So, what's inside Uldum?"
-#define GOSSIP_ITEM_NORGANNON_6 "I will return when i have the Plates of Uldum."
-
-class npc_stone_watcher_of_norgannon : public CreatureScript
-{
-public:
- npc_stone_watcher_of_norgannon() : CreatureScript("npc_stone_watcher_of_norgannon") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- player->SEND_GOSSIP_MENU(1675, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+1:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
- player->SEND_GOSSIP_MENU(1676, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+2:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
- player->SEND_GOSSIP_MENU(1677, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+3:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4);
- player->SEND_GOSSIP_MENU(1678, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+4:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5);
- player->SEND_GOSSIP_MENU(1679, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+5:
- player->CLOSE_GOSSIP_MENU();
- player->AreaExploredOrEventHappens(2954);
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (player->GetQuestStatus(2954) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
-
- player->SEND_GOSSIP_MENU(1674, creature->GetGUID());
-
- return true;
- }
-
-};
-
-/*######
## npc_OOX17
######*/
@@ -672,8 +560,6 @@ void AddSC_tanaris()
{
new npc_aquementas();
new npc_custodian_of_time();
- new npc_steward_of_time();
- new npc_stone_watcher_of_norgannon();
new npc_OOX17();
new npc_tooga();
}
diff --git a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp
index 85c3a621239..2a606856af6 100644
--- a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp
+++ b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp
@@ -19,7 +19,7 @@
/* ScriptData
SDName: Thousand Needles
SD%Complete: 100
-SDComment: Support for Quest: 1950, 4770, 4904, 4966, 5151.
+SDComment: Support for Quest: 4770, 4904, 4966, 5151.
SDCategory: Thousand Needles
EndScriptData */
@@ -27,7 +27,6 @@ EndScriptData */
npc_kanati
npc_lakota_windsong
npc_swiftmountain
-npc_plucky
npc_enraged_panther
go_panther_cage
EndContentData */
@@ -44,10 +43,9 @@ EndContentData */
enum Kanati
{
- SAY_KAN_START = 0,
-
- QUEST_PROTECT_KANATI = 4966,
- NPC_GALAK_ASS = 10720
+ SAY_KAN_START = 0,
+ QUEST_PROTECT_KANATI = 4966,
+ NPC_GALAK_ASS = 10720
};
Position const GalakLoc = {-4867.387695f, -1357.353760f, -48.226f, 0.0f};
@@ -112,19 +110,19 @@ public:
enum Lakota
{
- SAY_LAKO_START = 0,
- SAY_LAKO_LOOK_OUT = 1,
- SAY_LAKO_HERE_COME = 2,
- SAY_LAKO_MORE = 3,
- SAY_LAKO_END = 4,
-
- QUEST_FREE_AT_LAST = 4904,
- NPC_GRIM_BANDIT = 10758,
- FACTION_ESCORTEE_LAKO = 232, //guessed
-
- ID_AMBUSH_1 = 0,
- ID_AMBUSH_2 = 2,
- ID_AMBUSH_3 = 4
+ SAY_LAKO_START = 0,
+ SAY_LAKO_LOOK_OUT = 1,
+ SAY_LAKO_HERE_COME = 2,
+ SAY_LAKO_MORE = 3,
+ SAY_LAKO_END = 4,
+
+ QUEST_FREE_AT_LAST = 4904,
+ NPC_GRIM_BANDIT = 10758,
+ FACTION_ESCORTEE_LAKO = 232, //guessed
+
+ ID_AMBUSH_1 = 0,
+ ID_AMBUSH_2 = 2,
+ ID_AMBUSH_3 = 4
};
Position const BanditLoc[6] =
@@ -183,6 +181,7 @@ public:
DoSpawnBandits(ID_AMBUSH_3);
break;
case 45:
+ Talk(SAY_LAKO_END);
if (Player* player = GetPlayerForEscort())
player->GroupEventHappens(QUEST_FREE_AT_LAST, me);
break;
@@ -195,7 +194,6 @@ public:
me->SummonCreature(NPC_GRIM_BANDIT, BanditLoc[i+AmbushId], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000);
}
};
-
};
/*######
@@ -204,13 +202,13 @@ public:
enum Packa
{
- SAY_START = 0,
- SAY_WYVERN = 1,
- SAY_COMPLETE = 2,
+ SAY_START = 0,
+ SAY_WYVERN = 1,
+ SAY_COMPLETE = 2,
- QUEST_HOMEWARD = 4770,
- NPC_WYVERN = 4107,
- FACTION_ESCORTEE = 232 //guessed
+ QUEST_HOMEWARD = 4770,
+ NPC_WYVERN = 4107,
+ FACTION_ESCORTEE = 232 //guessed
};
Position const WyvernLoc[3] =
@@ -275,137 +273,10 @@ public:
};
};
-/*#####
-# npc_plucky
-######*/
-
-#define GOSSIP_P "Please tell me the Phrase.."
-
-enum Plucky
-{
- FACTION_FRIENDLY = 35,
- QUEST_SCOOP = 1950,
- SPELL_PLUCKY_HUMAN = 9192,
- SPELL_PLUCKY_CHICKEN = 9220
-};
-
-class npc_plucky : public CreatureScript
-{
-public:
- npc_plucky() : CreatureScript("npc_plucky") { }
-
- bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF+1:
- player->CLOSE_GOSSIP_MENU();
- player->CompleteQuest(QUEST_SCOOP);
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (player->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_P, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
-
- player->SEND_GOSSIP_MENU(738, creature->GetGUID());
-
- return true;
- }
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_pluckyAI(creature);
- }
-
- struct npc_pluckyAI : public ScriptedAI
- {
- npc_pluckyAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- NormFaction = creature->getFaction();
- }
-
- void Initialize()
- {
- ResetTimer = 120000;
- }
-
- uint32 NormFaction;
- uint32 ResetTimer;
-
- void Reset() override
- {
- Initialize();
-
- if (me->getFaction() != NormFaction)
- me->setFaction(NormFaction);
-
- if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
- me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
-
- DoCast(me, SPELL_PLUCKY_CHICKEN, false);
- }
-
- void ReceiveEmote(Player* player, uint32 TextEmote) override
- {
- if (player->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE)
- {
- if (TextEmote == TEXT_EMOTE_BECKON)
- {
- me->setFaction(FACTION_FRIENDLY);
- me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- DoCast(me, SPELL_PLUCKY_HUMAN, false);
- }
- }
-
- if (TextEmote == TEXT_EMOTE_CHICKEN)
- {
- if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
- return;
- else
- {
- me->setFaction(FACTION_FRIENDLY);
- me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- DoCast(me, SPELL_PLUCKY_HUMAN, false);
- me->HandleEmoteCommand(EMOTE_ONESHOT_WAVE);
- }
- }
- }
-
- void UpdateAI(uint32 Diff) override
- {
- if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
- {
- if (ResetTimer <= Diff)
- {
- if (!me->GetVictim())
- EnterEvadeMode();
- else
- me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
-
- return;
- }
- else
- ResetTimer -= Diff;
- }
-
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
- }
- };
-
-};
-
enum PantherCage
{
- ENRAGED_PANTHER = 10992
+ QUEST_HYPERCAPACITOR_GIZMO = 5151,
+ ENRAGED_PANTHER = 10992
};
class go_panther_cage : public GameObjectScript
@@ -416,7 +287,7 @@ public:
bool OnGossipHello(Player* player, GameObject* go) override
{
go->UseDoorOrButton();
- if (player->GetQuestStatus(5151) == QUEST_STATUS_INCOMPLETE)
+ if (player->GetQuestStatus(QUEST_HYPERCAPACITOR_GIZMO) == QUEST_STATUS_INCOMPLETE)
{
if (Creature* panther = go->FindNearestCreature(ENRAGED_PANTHER, 5, true))
{
@@ -466,7 +337,6 @@ void AddSC_thousand_needles()
new npc_kanati();
new npc_lakota_windsong();
new npc_paoka_swiftmountain();
- new npc_plucky();
new npc_enraged_panther();
new go_panther_cage();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
index 3c11fd5d9a6..2f97b5a8055 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
@@ -16,7 +16,8 @@
*/
/*
- * Comment: Complete - BUT THE TRIGGER NEEDS DATA WHETHER THE PRISON OF TALDARAM IS OFFLINE !
+ * Comment: Visuals missing, de-germanize code, wow 3.3.5a-ize
+ * Patch 3.3.2 (2010-01-02): Jedoga Shadowseeker now only ascends once during the encounter.
*/
#include "ScriptMgr.h"
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
index 16cfb30e2dc..2df0fceab9c 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
@@ -156,7 +156,7 @@ public:
ImpaleTarget = impaleTarget->GetGUID();
impaleTarget->SetReactState(REACT_PASSIVE);
impaleTarget->SetDisplayId(DISPLAY_INVISIBLE);
- impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
+ impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
return impaleTarget;
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 7b8ca41772f..d6682e272fd 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -137,7 +137,8 @@ enum Events
EVENT_CHECK_CORPOREALITY = 13,
EVENT_SHADOW_PULSARS_SHOOT = 14,
EVENT_TRIGGER_BERSERK = 15,
- EVENT_TWILIGHT_MENDING = 16
+ EVENT_TWILIGHT_MENDING = 16,
+ EVENT_ACTIVATE_EMBERS = 17
};
enum Actions
@@ -150,7 +151,9 @@ enum Actions
ACTION_MONITOR_CORPOREALITY = 3,
// Orb Carrier
- ACTION_SHOOT = 4
+ ACTION_WARNING_SHOOT = 4,
+ ACTION_SHOOT = 5,
+ ACTION_ACTIVATE_EMBERS = 6
};
enum Phases
@@ -168,8 +171,7 @@ enum Misc
DATA_MATERIAL_DAMAGE_TAKEN = 2,
DATA_STACKS_DISPELLED = 3,
DATA_FIGHT_PHASE = 4,
- DATA_EVADE_METHOD = 5,
- DATA_SPAWNED_FLAMES = 6,
+ DATA_SPAWNED_FLAMES = 5,
};
enum OrbCarrierSeats
@@ -189,6 +191,7 @@ enum CorporealityEvent
};
Position const HalionSpawnPos = {3156.67f, 533.8108f, 72.98822f, 3.159046f};
+Position const HalionRespawnPos = {3156.625f, 533.2674f, 72.97205f, 0.0f};
uint8 const MAX_CORPOREALITY_STATE = 11;
@@ -212,132 +215,39 @@ CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] = {
{74831, 74836}
};
-struct generic_halionAI : public BossAI
-{
- generic_halionAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _canEvade(false) { }
-
- void EnterCombat(Unit* /*who*/) override
- {
- _EnterCombat();
- me->AddAura(SPELL_TWILIGHT_PRECISION, me);
- _canEvade = false;
- events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
- events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
- events.ScheduleEvent(EVENT_BREATH, urand(10000, 15000));
- }
-
- void Reset() override
- {
- _canEvade = false;
- _Reset();
- }
-
- void JustReachedHome() override
- {
- instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
- _JustReachedHome();
- }
-
- void ExecuteEvent(uint32 eventId) override
- {
- switch (eventId)
- {
- case EVENT_CLEAVE:
- DoCastVictim(SPELL_CLEAVE);
- events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
- break;
- case EVENT_TAIL_LASH:
- DoCastAOE(SPELL_TAIL_LASH);
- events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
- break;
- case EVENT_BREATH:
- DoCast(me, me->GetEntry() == NPC_HALION ? SPELL_FLAME_BREATH : SPELL_DARK_BREATH);
- events.ScheduleEvent(EVENT_BREATH, urand(10000, 12000));
- break;
- }
- }
-
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- events.Update(diff);
-
- while (uint32 eventId = events.ExecuteEvent())
- ExecuteEvent(eventId);
-
- DoMeleeAttackIfReady();
- }
-
- void SetData(uint32 index, uint32 dataValue) override
- {
- switch (index)
- {
- case DATA_EVADE_METHOD:
- _canEvade = (dataValue == 1);
- break;
- default:
- break;
- }
- }
-
- void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) override
- {
- if (spellInfo->Id == SPELL_TWILIGHT_MENDING)
- Talk(SAY_REGENERATE);
- }
-
-protected:
- bool _canEvade;
-};
-
class boss_halion : public CreatureScript
{
public:
boss_halion() : CreatureScript("boss_halion") { }
- struct boss_halionAI : public generic_halionAI
+ struct boss_halionAI : public BossAI
{
- boss_halionAI(Creature* creature) : generic_halionAI(creature, DATA_HALION)
- {
- me->SetHomePosition(HalionSpawnPos);
- }
-
- void Reset() override
- {
- generic_halionAI::Reset();
- me->SetReactState(REACT_DEFENSIVE);
- me->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- }
+ boss_halionAI(Creature* creature) : BossAI(creature, DATA_HALION) { }
void EnterEvadeMode(EvadeReason why) override
{
- if (why == EVADE_REASON_BOUNDARY)
+ if (why == EVADE_REASON_BOUNDARY || events.IsInPhase(PHASE_ONE))
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
- controller->AI()->EnterEvadeMode();
-
- // Phase 1: We always can evade. Phase 2 & 3: We can evade if and only if the controller tells us to.
- if (events.IsInPhase(PHASE_ONE) || _canEvade)
- generic_halionAI::EnterEvadeMode(why);
+ controller->AI()->EnterEvadeMode(why);
}
- void EnterCombat(Unit* who) override
+ void EnterCombat(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
events.Reset();
events.SetPhase(PHASE_ONE);
- generic_halionAI::EnterCombat(who);
+ _EnterCombat();
+ me->AddAura(SPELL_TWILIGHT_PRECISION, me);
+ events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, Seconds(5));
+ events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(5), Seconds(15)));
+ events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(6), Seconds(10)));
+ events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(7), Seconds(12)));
+ events.ScheduleEvent(EVENT_FIERY_COMBUSTION, randtime(Seconds(15), Seconds(18)));
+ events.ScheduleEvent(EVENT_METEOR_STRIKE, Seconds(18));
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
- instance->SetBossState(DATA_HALION, IN_PROGRESS);
-
- events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, 5000);
- events.ScheduleEvent(EVENT_METEOR_STRIKE, urand(20000, 25000));
- events.ScheduleEvent(EVENT_FIERY_COMBUSTION, urand(15000, 18000));
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_ONE);
@@ -388,58 +298,76 @@ class boss_halion : public CreatureScript
}
}
+ void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_TWILIGHT_MENDING)
+ Talk(SAY_REGENERATE);
+ }
+
void UpdateAI(uint32 diff) override
{
if (events.IsInPhase(PHASE_TWO))
return;
- generic_halionAI::UpdateAI(diff);
- }
+ if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- void ExecuteEvent(uint32 eventId) override
- {
- switch (eventId)
+ events.Update(diff);
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- case EVENT_ACTIVATE_FIREWALL:
- // Flame ring is activated 5 seconds after starting encounter, DOOR_TYPE_ROOM is only instant.
- for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i)
- if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(i)))
- instance->HandleGameObject(instance->GetGuidData(DATA_FLAME_RING), false, flameRing);
- break;
- case EVENT_METEOR_STRIKE:
+ switch (eventId)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM))
+ case EVENT_CLEAVE:
+ DoCastVictim(SPELL_CLEAVE);
+ events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(8), Seconds(10)));
+ break;
+ case EVENT_TAIL_LASH:
+ DoCastAOE(SPELL_TAIL_LASH);
+ events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(11), Seconds(16)));
+ break;
+ case EVENT_BREATH:
+ DoCast(me, SPELL_FLAME_BREATH);
+ events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(16), Seconds(25)));
+ break;
+ case EVENT_ACTIVATE_FIREWALL:
+ // Flame ring is activated 5 seconds after starting encounter, DOOR_TYPE_ROOM is only instant.
+ for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i)
+ if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(i)))
+ instance->HandleGameObject(instance->GetGuidData(DATA_FLAME_RING), false, flameRing);
+ break;
+ case EVENT_METEOR_STRIKE:
{
- _meteorStrikePos = target->GetPosition();
- me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, NULL, NULL, me->GetGUID());
- Talk(SAY_METEOR_STRIKE);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM))
+ {
+ _meteorStrikePos = target->GetPosition();
+ me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, nullptr, nullptr, me->GetGUID());
+ Talk(SAY_METEOR_STRIKE);
+ }
+ events.ScheduleEvent(EVENT_METEOR_STRIKE, Seconds(38));
+ break;
}
- events.ScheduleEvent(EVENT_METEOR_STRIKE, 40000);
- break;
- }
- case EVENT_FIERY_COMBUSTION:
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM))
- DoCast(target, SPELL_FIERY_COMBUSTION);
- events.ScheduleEvent(EVENT_FIERY_COMBUSTION, 25000);
- break;
+ case EVENT_FIERY_COMBUSTION:
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM))
+ me->CastSpell(target, SPELL_FIERY_COMBUSTION, TRIGGERED_IGNORE_SET_FACING);
+ events.ScheduleEvent(EVENT_FIERY_COMBUSTION, Seconds(25));
+ break;
+ }
+ default:
+ break;
}
- default:
- generic_halionAI::ExecuteEvent(eventId);
- break;
}
+
+ DoMeleeAttackIfReady();
}
void SetData(uint32 index, uint32 value) override
{
- switch (index)
- {
- case DATA_FIGHT_PHASE:
- events.SetPhase(value);
- break;
- default:
- generic_halionAI::SetData(index, value);
- }
+ if (index != DATA_FIGHT_PHASE)
+ return;
+
+ events.SetPhase(value);
}
private:
@@ -459,9 +387,9 @@ class boss_twilight_halion : public CreatureScript
public:
boss_twilight_halion() : CreatureScript("boss_twilight_halion") { }
- struct boss_twilight_halionAI : public generic_halionAI
+ struct boss_twilight_halionAI : public BossAI
{
- boss_twilight_halionAI(Creature* creature) : generic_halionAI(creature, DATA_TWILIGHT_HALION)
+ boss_twilight_halionAI(Creature* creature) : BossAI(creature, DATA_TWILIGHT_HALION)
{
Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION));
if (!halion)
@@ -470,26 +398,29 @@ class boss_twilight_halion : public CreatureScript
// Using AddAura because no spell cast packet in sniffs.
halion->AddAura(SPELL_COPY_DAMAGE, me); // We use explicit targeting here to avoid conditions + SPELL_ATTR6_CANT_TARGET_SELF.
me->AddAura(SPELL_COPY_DAMAGE, halion);
- me->AddAura(SPELL_DUSK_SHROUD, me);
+ DoCast(me, SPELL_DUSK_SHROUD, true);
me->SetHealth(halion->GetHealth());
me->SetPhaseMask(0x20, true);
- me->SetReactState(REACT_AGGRESSIVE);
+ me->SetReactState(REACT_DEFENSIVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+ events.ScheduleEvent(EVENT_TAIL_LASH, Seconds(12));
+ events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, Seconds(15));
}
- void EnterCombat(Unit* who) override
+ void EnterCombat(Unit* /*who*/) override
{
- events.Reset();
events.SetPhase(PHASE_TWO);
- generic_halionAI::EnterCombat(who);
-
- events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000);
+ _EnterCombat();
+ me->AddAura(SPELL_TWILIGHT_PRECISION, me);
+ events.ScheduleEvent(EVENT_CLEAVE, Seconds(3));
+ events.ScheduleEvent(EVENT_BREATH, Seconds(12));
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 2);
}
- // Never evade
+ void Reset() override { }
void EnterEvadeMode(EvadeReason /*why*/) override { }
void KilledUnit(Unit* victim) override
@@ -522,6 +453,10 @@ class boss_twilight_halion : public CreatureScript
void DamageTaken(Unit* attacker, uint32& damage) override
{
+ //Needed because we already have UNIT_FLAG_IN_COMBAT, otherwise EnterCombat won't ever be called
+ if (!events.IsInPhase(PHASE_TWO) && !events.IsInPhase(PHASE_THREE))
+ EnterCombat(attacker);
+
if (me->HealthBelowPctDamaged(50, damage) && events.IsInPhase(PHASE_TWO))
{
events.SetPhase(PHASE_THREE);
@@ -542,7 +477,7 @@ class boss_twilight_halion : public CreatureScript
}
}
- void SpellHit(Unit* who, SpellInfo const* spell) override
+ void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
{
switch (spell->Id)
{
@@ -550,25 +485,51 @@ class boss_twilight_halion : public CreatureScript
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
controller->AI()->DoAction(ACTION_MONITOR_CORPOREALITY);
break;
+ case SPELL_TWILIGHT_MENDING:
+ Talk(SAY_REGENERATE);
+ break;
default:
- generic_halionAI::SpellHit(who, spell);
break;
}
}
- void ExecuteEvent(uint32 eventId) override
+ void UpdateAI(uint32 diff) override
{
- switch (eventId)
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- case EVENT_SOUL_CONSUMPTION:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM))
- DoCast(target, SPELL_SOUL_CONSUMPTION);
- events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000);
- break;
- default:
- generic_halionAI::ExecuteEvent(eventId);
- break;
+ switch (eventId)
+ {
+ case EVENT_CLEAVE:
+ DoCastVictim(SPELL_CLEAVE);
+ events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(7), Seconds(10)));
+ break;
+ case EVENT_TAIL_LASH:
+ DoCastAOE(SPELL_TAIL_LASH);
+ events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(12), Seconds(16)));
+ break;
+ case EVENT_BREATH:
+ DoCast(me, SPELL_DARK_BREATH);
+ events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(10), Seconds(14)));
+ break;
+ case EVENT_SOUL_CONSUMPTION:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM))
+ me->CastSpell(target, SPELL_SOUL_CONSUMPTION, TRIGGERED_IGNORE_SET_FACING);
+ events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, Seconds(20));
+ break;
+ default:
+ break;
+ }
}
+
+ DoMeleeAttackIfReady();
}
};
@@ -589,7 +550,6 @@ class npc_halion_controller : public CreatureScript
_instance(creature->GetInstanceScript()), _summons(me)
{
Initialize();
- me->SetPhaseMask(me->GetPhaseMask() | 0x20, true);
}
void Initialize()
@@ -599,6 +559,15 @@ class npc_halion_controller : public CreatureScript
_twilightDamageTaken = 0;
}
+ void JustRespawned() override
+ {
+ if (_instance->GetGuidData(DATA_HALION))
+ return;
+
+ Reset();
+ me->GetMap()->SummonCreature(NPC_HALION, HalionRespawnPos);
+ }
+
void Reset() override
{
_summons.DespawnAll();
@@ -626,21 +595,36 @@ class npc_halion_controller : public CreatureScript
_twilightDamageTaken = 0;
_materialDamageTaken = 0;
- _events.ScheduleEvent(EVENT_TRIGGER_BERSERK, 8 * MINUTE * IN_MILLISECONDS);
+ _events.ScheduleEvent(EVENT_TRIGGER_BERSERK, Minutes(8));
}
- void JustReachedHome() override
+ void EnterEvadeMode(EvadeReason /*why*/) override
{
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
+ {
twilightHalion->DespawnOrUnsummon();
+ _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, twilightHalion);
+ }
if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION)))
{
- halion->AI()->SetData(DATA_EVADE_METHOD, 1);
- halion->AI()->EnterEvadeMode();
+ halion->DespawnOrUnsummon();
+ _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, halion);
}
_instance->SetBossState(DATA_HALION, FAIL);
+ _summons.DespawnAll();
+
+ uint32 corpseDelay = me->GetCorpseDelay();
+ uint32 respawnDelay = me->GetRespawnDelay();
+
+ me->SetCorpseDelay(1);
+ me->SetRespawnDelay(30);
+
+ me->DespawnOrUnsummon();
+
+ me->SetCorpseDelay(corpseDelay);
+ me->SetRespawnDelay(respawnDelay);
}
void DoAction(int32 action) override
@@ -650,7 +634,16 @@ class npc_halion_controller : public CreatureScript
case ACTION_INTRO_HALION:
_events.Reset();
_events.SetPhase(PHASE_INTRO);
- _events.ScheduleEvent(EVENT_START_INTRO, 2000);
+ _events.ScheduleEvent(EVENT_START_INTRO, Seconds(2));
+ break;
+ case ACTION_INTRO_HALION_2:
+ if (_instance->GetGuidData(DATA_HALION))
+ return;
+
+ for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_4; ++i)
+ if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i)))
+ _instance->HandleGameObject(_instance->GetGuidData(i), true, tree);
+ me->GetMap()->SummonCreature(NPC_HALION, HalionRespawnPos);
break;
case ACTION_MONITOR_CORPOREALITY:
{
@@ -678,8 +671,12 @@ class npc_halion_controller : public CreatureScript
_instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 50);
_instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 50);
- _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 7500);
+ _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, Seconds(7));
+ break;
}
+ case ACTION_ACTIVATE_EMBERS:
+ _events.ScheduleEvent(EVENT_ACTIVATE_EMBERS, Seconds(6));
+ break;
default:
break;
}
@@ -692,7 +689,7 @@ class npc_halion_controller : public CreatureScript
// combat state.
if (!_events.IsInPhase(PHASE_INTRO) && me->IsInCombat() && !UpdateVictim())
{
- EnterEvadeMode();
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return;
}
@@ -704,29 +701,31 @@ class npc_halion_controller : public CreatureScript
{
case EVENT_START_INTRO:
DoCast(me, SPELL_COSMETIC_FIRE_PILLAR, true);
- _events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, 4000);
+ _events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, Seconds(4));
break;
case EVENT_INTRO_PROGRESS_1:
for (uint8 i = DATA_BURNING_TREE_3; i <= DATA_BURNING_TREE_4; ++i)
if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i)))
_instance->HandleGameObject(_instance->GetGuidData(i), true, tree);
- _events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, 4000);
+ _events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, Seconds(4));
break;
case EVENT_INTRO_PROGRESS_2:
for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_2; ++i)
if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i)))
_instance->HandleGameObject(_instance->GetGuidData(i), true, tree);
- _events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, 4000);
+ _events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, Seconds(4));
break;
case EVENT_INTRO_PROGRESS_3:
DoCast(me, SPELL_FIERY_EXPLOSION);
+ if (_instance->GetGuidData(DATA_HALION))
+ return;
if (Creature* halion = me->GetMap()->SummonCreature(NPC_HALION, HalionSpawnPos))
halion->AI()->Talk(SAY_INTRO);
break;
case EVENT_TWILIGHT_MENDING:
if (ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION))) // Just check if physical Halion is spawned
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
- twilightHalion->CastSpell((Unit*)NULL, SPELL_TWILIGHT_MENDING, true);
+ twilightHalion->CastSpell((Unit*)nullptr, SPELL_TWILIGHT_MENDING, true);
break;
case EVENT_TRIGGER_BERSERK:
for (uint8 i = DATA_HALION; i <= DATA_TWILIGHT_HALION; i++)
@@ -734,17 +733,16 @@ class npc_halion_controller : public CreatureScript
halion->CastSpell(halion, SPELL_BERSERK, true);
break;
case EVENT_SHADOW_PULSARS_SHOOT:
- if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
- twilightHalion->AI()->Talk(SAY_SPHERE_PULSE);
-
if (Creature* orbCarrier = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ORB_CARRIER)))
- orbCarrier->AI()->DoAction(ACTION_SHOOT);
-
- _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000);
+ orbCarrier->AI()->DoAction(ACTION_WARNING_SHOOT);
+ _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, Seconds(30));
break;
case EVENT_CHECK_CORPOREALITY:
UpdateCorporeality();
- _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000);
+ _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, Seconds(5));
+ break;
+ case EVENT_ACTIVATE_EMBERS:
+ _summons.DoZoneInCombat(NPC_LIVING_EMBER);
break;
default:
break;
@@ -770,7 +768,7 @@ class npc_halion_controller : public CreatureScript
DoZoneInCombat();
break;
case PHASE_TWO:
- _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000);
+ _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, Seconds(35));
break;
default:
break;
@@ -793,7 +791,7 @@ class npc_halion_controller : public CreatureScript
uint8 oldValue = _materialCorporealityValue;
if (_twilightDamageTaken == 0 || _materialDamageTaken == 0)
{
- _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100);
+ _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, Milliseconds(100));
_twilightDamageTaken = 0;
_materialDamageTaken = 0;
return;
@@ -833,7 +831,7 @@ class npc_halion_controller : public CreatureScript
}
case CORPOREALITY_TWILIGHT_MENDING:
{
- _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100);
+ _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, Milliseconds(100));
_materialDamageTaken = 0;
_twilightDamageTaken = 0;
return;
@@ -889,52 +887,73 @@ class npc_orb_carrier : public CreatureScript
struct npc_orb_carrierAI : public ScriptedAI
{
npc_orb_carrierAI(Creature* creature) : ScriptedAI(creature),
- instance(creature->GetInstanceScript())
+ _instance(creature->GetInstanceScript())
{
ASSERT(creature->GetVehicleKit());
}
- void UpdateAI(uint32 /*diff*/) override
+ void UpdateAI(uint32 diff) override
{
/// According to sniffs this spell is cast every 1 or 2 seconds.
/// However, refreshing it looks bad, so just cast the spell if
/// we are not channeling it.
if (!me->HasUnitState(UNIT_STATE_CASTING))
- me->CastSpell((Unit*)NULL, SPELL_TRACK_ROTATION, false);
+ me->CastSpell((Unit*)nullptr, SPELL_TRACK_ROTATION, false);
+
+ scheduler.Update(diff);
/// Workaround: This is here because even though the above spell has SPELL_ATTR1_CHANNEL_TRACK_TARGET,
/// we are having two creatures involded here. This attribute is handled clientside, meaning the client
/// sends orientation update itself. Here, no packet is sent, and the creature does not rotate. By
/// forcing the carrier to always be facing the rotation focus, we ensure everything works as it should.
- if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ORB_ROTATION_FOCUS)))
+ if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ORB_ROTATION_FOCUS)))
me->SetFacingToObject(rotationFocus); // setInFront
}
void DoAction(int32 action) override
{
- if (action == ACTION_SHOOT)
+ switch (action)
{
- Vehicle* vehicle = me->GetVehicleKit();
- Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH);
- Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
- if (southOrb && northOrb)
+ case ACTION_WARNING_SHOOT:
{
- if (northOrb->GetTypeId() == TYPEID_UNIT)
+ Vehicle* vehicle = me->GetVehicleKit();
+ Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
+ if (northOrb && northOrb->GetTypeId() == TYPEID_UNIT)
northOrb->ToCreature()->AI()->Talk(EMOTE_WARN_LASER);
- TriggerCutter(northOrb, southOrb);
+
+ scheduler.Schedule(Seconds(5), [this](TaskContext /*context*/)
+ {
+ DoAction(ACTION_SHOOT);
+ });
+ break;
}
+ case ACTION_SHOOT:
+ {
+ Vehicle* vehicle = me->GetVehicleKit();
+ Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH);
+ Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
+ if (southOrb && northOrb)
+ TriggerCutter(northOrb, southOrb);
- if (!IsHeroic())
- return;
+ if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION)))
+ twilightHalion->AI()->Talk(SAY_SPHERE_PULSE);
+
+ if (!IsHeroic())
+ return;
- Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST);
- Unit* westOrb = vehicle->GetPassenger(SEAT_WEST);
- if (eastOrb && westOrb)
- TriggerCutter(eastOrb, westOrb);
+ Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST);
+ Unit* westOrb = vehicle->GetPassenger(SEAT_WEST);
+ if (eastOrb && westOrb)
+ TriggerCutter(eastOrb, westOrb);
+ break;
+ }
+ default:
+ break;
}
}
private:
- InstanceScript* instance;
+ InstanceScript* _instance;
+ TaskScheduler scheduler;
void TriggerCutter(Unit* caster, Unit* target)
{
@@ -987,12 +1006,12 @@ class npc_meteor_strike_initial : public CreatureScript
controller->AI()->JustSummoned(me);
DoCast(me, SPELL_METEOR_STRIKE_COUNTDOWN);
- DoCast(me, SPELL_BIRTH_NO_VISUAL); // Unknown purpose
+ DoCast(me, SPELL_BIRTH_NO_VISUAL);
if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI()))
{
Position const* ownerPos = halionAI->GetMeteorStrikePosition();
- float randomAdjustment = frand(0.0f, static_cast<float>(M_PI / 5.0f));
+ float randomAdjustment = frand(static_cast<float>(M_PI / 5.0f), static_cast<float>(M_PI / 2.0f));
float angle[4];
angle[0] = me->GetAngle(ownerPos);
angle[1] = angle[0] + randomAdjustment;
@@ -1006,10 +1025,7 @@ class npc_meteor_strike_initial : public CreatureScript
me->SetOrientation(angle[i]);
Position newPos = me->GetNearPosition(10.0f, 0.0f); // Exact distance
if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000))
- {
- meteor->SetOrientation(angle[i]);
_meteorList.push_back(meteor);
- }
}
}
}
@@ -1046,7 +1062,7 @@ class npc_meteor_strike : public CreatureScript
{
DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true);
me->setActive(true);
- _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 500);
+ _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, Milliseconds(500));
}
}
@@ -1110,7 +1126,7 @@ class npc_meteor_strike_flame : public CreatureScript
void SetGUID(ObjectGuid guid, int32 /*id = 0 */) override
{
_rootOwnerGuid = guid;
- _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800);
+ _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, Milliseconds(800));
}
void IsSummonedBy(Unit* /*summoner*/) override
@@ -1202,7 +1218,7 @@ class npc_combustion_consumption : public CreatureScript
if (type != DATA_STACKS_DISPELLED || !_damageSpell || !_explosionSpell || !summoner)
return;
- me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me);
+ me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount + 1, me);
DoCast(me, _damageSpell);
int32 damage = 1200 + (stackAmount * 1290); // Needs more research.
@@ -1240,17 +1256,32 @@ class npc_living_inferno : public CreatureScript
// SMSG_SPELL_GO for the living ember stuff isn't even sent to the client - Blizzard on drugs.
if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC)
- me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true);
+ scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/)
+ {
+ me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true);
+ });
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
+ {
+ controller->AI()->DoAction(ACTION_ACTIVATE_EMBERS);
controller->AI()->JustSummoned(me);
+ }
}
void JustDied(Unit* /*killer*/) override
{
me->DespawnOrUnsummon(1);
}
+
+ void UpdateAI(uint32 diff) override
+ {
+ scheduler.Update(diff);
+ ScriptedAI::UpdateAI(diff);
+ }
+
+ private:
+ TaskScheduler scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1266,27 +1297,7 @@ class npc_living_ember : public CreatureScript
struct npc_living_emberAI : public ScriptedAI
{
- npc_living_emberAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- _enrageTimer = 0;
- }
-
- void Initialize()
- {
- _hasEnraged = false;
- }
-
- void Reset() override
- {
- Initialize();
- }
-
- void EnterCombat(Unit* /*who*/) override
- {
- _enrageTimer = 20000;
- _hasEnraged = false;
- }
+ npc_living_emberAI(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(Unit* /*summoner*/) override
{
@@ -1299,25 +1310,6 @@ class npc_living_ember : public CreatureScript
{
me->DespawnOrUnsummon(1);
}
-
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- if (!_hasEnraged && _enrageTimer <= diff)
- {
- _hasEnraged = true;
- DoCast(me, SPELL_BERSERK);
- }
- else _enrageTimer -= diff;
-
- DoMeleeAttackIfReady();
- }
-
- private:
- uint32 _enrageTimer;
- bool _hasEnraged;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1473,6 +1465,47 @@ class spell_halion_combustion_consumption : public SpellScriptLoader
uint32 _spellID;
};
+class spell_halion_combustion_consumption_periodic : public SpellScriptLoader
+{
+ public:
+ spell_halion_combustion_consumption_periodic() : SpellScriptLoader("spell_halion_combustion_consumption_periodic") { }
+
+ class spell_halion_combustion_consumption_periodic_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_halion_combustion_consumption_periodic_AuraScript);
+
+ bool Validate(SpellInfo const* spellInfo) override
+ {
+ if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell))
+ return false;
+ return true;
+ }
+
+ void HandleTick(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
+ Unit* caster = GetCaster();
+ if (!caster)
+ return;
+
+ uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell;
+ int32 radius = caster->GetObjectScale() * M_PI * 10000 / 3;
+
+ caster->CastCustomSpell(triggerSpell, SPELLVALUE_RADIUS_MOD, radius, (Unit*)nullptr, TRIGGERED_FULL_MASK, nullptr, aurEff, caster->GetGUID());
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_halion_combustion_consumption_periodic_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_halion_combustion_consumption_periodic_AuraScript();
+ }
+};
+
class spell_halion_marks : public SpellScriptLoader
{
public:
@@ -1511,7 +1544,7 @@ class spell_halion_marks : public SpellScriptLoader
return;
// Stacks marker
- GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, NULL, NULL, GetCasterGUID());
+ GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, nullptr, nullptr, GetCasterGUID());
}
void Register() override
@@ -1819,6 +1852,35 @@ class spell_halion_spawn_living_embers : public SpellScriptLoader
}
};
+class spell_halion_blazing_aura : public SpellScriptLoader
+{
+ public:
+ spell_halion_blazing_aura() : SpellScriptLoader("spell_halion_blazing_aura") { }
+
+ class spell_halion_blazing_aura_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_halion_blazing_aura_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetHitUnit()->CastSpell(GetHitUnit(), GetSpellInfo()->Effects[EFFECT_1].TriggerSpell);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_halion_blazing_aura_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_FORCE_CAST);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_halion_blazing_aura_SpellScript();
+ }
+};
+
+
+
void AddSC_boss_halion()
{
new boss_halion();
@@ -1840,6 +1902,7 @@ void AddSC_boss_halion()
new spell_halion_combustion_consumption("spell_halion_fiery_combustion", SPELL_MARK_OF_COMBUSTION);
new spell_halion_marks("spell_halion_mark_of_combustion", SPELL_FIERY_COMBUSTION_SUMMON, SPELL_FIERY_COMBUSTION);
new spell_halion_marks("spell_halion_mark_of_consumption", SPELL_SOUL_CONSUMPTION_SUMMON, SPELL_SOUL_CONSUMPTION);
+ new spell_halion_combustion_consumption_periodic();
new spell_halion_damage_aoe_summon();
new spell_halion_twilight_realm_handlers("spell_halion_leave_twilight_realm", SPELL_SOUL_CONSUMPTION, false);
new spell_halion_twilight_realm_handlers("spell_halion_enter_twilight_realm", SPELL_FIERY_COMBUSTION, true);
@@ -1848,4 +1911,5 @@ void AddSC_boss_halion()
new spell_halion_twilight_cutter();
new spell_halion_clear_debuffs();
new spell_halion_spawn_living_embers();
+ new spell_halion_blazing_aura();
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
index 739c3602f44..be85d3dc99c 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
@@ -53,11 +53,11 @@ class instance_ruby_sanctum : public InstanceMapScript
void OnPlayerEnter(Player* /*player*/) override
{
- if (!GetGuidData(DATA_HALION_CONTROLLER) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE)
+ if (!GetGuidData(DATA_HALION) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE)
{
instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY());
- if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
- halionController->AI()->DoAction(ACTION_INTRO_HALION);
+ if (Creature* halionController = instance->GetCreature(GetGuidData(DATA_HALION_CONTROLLER)))
+ halionController->AI()->DoAction(ACTION_INTRO_HALION_2);
}
}
@@ -161,6 +161,12 @@ class instance_ruby_sanctum : public InstanceMapScript
}
}
+ void OnCreatureRemove(Creature* creature) override
+ {
+ if (creature->GetEntry() == NPC_HALION)
+ HalionGUID = ObjectGuid::Empty;
+ }
+
void OnUnitDeath(Unit* unit) override
{
Creature* creature = unit->ToCreature();
@@ -170,7 +176,7 @@ class instance_ruby_sanctum : public InstanceMapScript
if (creature->GetEntry() == NPC_GENERAL_ZARITHRIAN && GetBossState(DATA_HALION) != DONE)
{
instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY());
- if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
+ if (Creature* halionController = instance->GetCreature(GetGuidData(DATA_HALION_CONTROLLER)))
halionController->AI()->DoAction(ACTION_INTRO_HALION);
}
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h
index 69c27a22ea0..333fef1dc85 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h
@@ -56,6 +56,7 @@ enum SharedActions
ACTION_INTRO_BALTHARUS = -3975101,
ACTION_BALTHARUS_DEATH = -3975102,
ACTION_INTRO_HALION = -4014601,
+ ACTION_INTRO_HALION_2 = -4014602,
};
enum CreaturesIds
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp
index 00900d440c9..f6726d60db3 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp
@@ -180,7 +180,7 @@ public:
{
pAnnouncer->GetMotionMaster()->MovePoint(0, 748.309f, 619.487f, 411.171f);
pAnnouncer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_CHAMPIONS_LOOT_H : GO_CHAMPIONS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, 0, 0, 0, 0, 90000);
+ pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_CHAMPIONS_LOOT_H : GO_CHAMPIONS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, G3D::Quat(), 90000);
}
}
}
@@ -203,7 +203,7 @@ public:
{
pAnnouncer->GetMotionMaster()->MovePoint(0, 748.309f, 619.487f, 411.171f);
pAnnouncer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_EADRIC_LOOT_H : GO_EADRIC_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, 0, 0, 0, 0, 90000);
+ pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_EADRIC_LOOT_H : GO_EADRIC_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, G3D::Quat(), 90000);
}
break;
case BOSS_ARGENT_CHALLENGE_P:
@@ -212,7 +212,7 @@ public:
{
pAnnouncer->GetMotionMaster()->MovePoint(0, 748.309f, 619.487f, 411.171f);
pAnnouncer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_PALETRESS_LOOT_H : GO_PALETRESS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, 0, 0, 0, 0, 90000);
+ pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_PALETRESS_LOOT_H : GO_PALETRESS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, G3D::Quat(), 90000);
}
break;
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
index 7440984d7c5..dfe0cacdef7 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
@@ -222,10 +222,10 @@ class boss_anubarak_trial : public CreatureScript
instance->SetBossState(BOSS_ANUBARAK, FAIL);
//Summon Scarab Swarms neutral at random places
for (int i = 0; i < 10; i++)
- if (Creature* temp = me->SummonCreature(NPC_SCARAB, AnubarakLoc[1].GetPositionX()+urand(0, 50)-25, AnubarakLoc[1].GetPositionY()+urand(0, 50)-25, AnubarakLoc[1].GetPositionZ()))
+ if (Creature* scarab = me->SummonCreature(NPC_SCARAB, AnubarakLoc[1].GetPositionX()+urand(0, 50)-25, AnubarakLoc[1].GetPositionY()+urand(0, 50)-25, AnubarakLoc[1].GetPositionZ()))
{
- temp->setFaction(31);
- temp->GetMotionMaster()->MoveRandom(10);
+ scarab->setFaction(31);
+ scarab->GetMotionMaster()->MoveRandom(10);
}
}
@@ -881,9 +881,9 @@ class spell_anubarak_leeching_swarm : public SpellScriptLoader
if (lifeLeeched < 250)
lifeLeeched = 250;
// Damage
- caster->CastCustomSpell(target, SPELL_LEECHING_SWARM_DMG, &lifeLeeched, 0, 0, false);
+ caster->CastCustomSpell(target, SPELL_LEECHING_SWARM_DMG, &lifeLeeched, 0, 0, true);
// Heal
- caster->CastCustomSpell(caster, SPELL_LEECHING_SWARM_HEAL, &lifeLeeched, 0, 0, false);
+ caster->CastCustomSpell(caster, SPELL_LEECHING_SWARM_HEAL, &lifeLeeched, 0, 0, true);
}
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp
index 6c1b516c7de..ee44e1391b4 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp
@@ -453,22 +453,22 @@ class boss_toc_champion_controller : public CreatureScript
for (uint8 i = 0; i < vChampionEntries.size(); ++i)
{
uint8 pos = urand(0, vChampionJumpTarget.size()-1);
- if (Creature* temp = me->SummonCreature(vChampionEntries[i], vChampionJumpOrigin[urand(0, vChampionJumpOrigin.size()-1)], TEMPSUMMON_MANUAL_DESPAWN))
+ if (Creature* champion = me->SummonCreature(vChampionEntries[i], vChampionJumpOrigin[urand(0, vChampionJumpOrigin.size()-1)], TEMPSUMMON_MANUAL_DESPAWN))
{
- _summons.Summon(temp);
- temp->SetReactState(REACT_PASSIVE);
- temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ _summons.Summon(champion);
+ champion->SetReactState(REACT_PASSIVE);
+ champion->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
if (playerTeam == ALLIANCE)
{
- temp->SetHomePosition(vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 0);
- temp->GetMotionMaster()->MoveJump(vChampionJumpTarget[pos], 20.0f, 20.0f);
- temp->SetOrientation(0);
+ champion->SetHomePosition(vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 0);
+ champion->GetMotionMaster()->MoveJump(vChampionJumpTarget[pos], 20.0f, 20.0f);
+ champion->SetOrientation(0);
}
else
{
- temp->SetHomePosition((ToCCommonLoc[1].GetPositionX()*2)-vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 3);
- temp->GetMotionMaster()->MoveJump((ToCCommonLoc[1].GetPositionX() * 2) - vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), vChampionJumpTarget[pos].GetOrientation(), 20.0f, 20.0f);
- temp->SetOrientation(3);
+ champion->SetHomePosition((ToCCommonLoc[1].GetPositionX()*2)-vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 3);
+ champion->GetMotionMaster()->MoveJump((ToCCommonLoc[1].GetPositionX() * 2) - vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), vChampionJumpTarget[pos].GetOrientation(), 20.0f, 20.0f);
+ champion->SetOrientation(3);
}
}
vChampionJumpTarget.erase(vChampionJumpTarget.begin()+pos);
@@ -485,10 +485,10 @@ class boss_toc_champion_controller : public CreatureScript
case 1:
for (SummonList::iterator i = _summons.begin(); i != _summons.end(); ++i)
{
- if (Creature* temp = ObjectAccessor::GetCreature(*me, *i))
+ if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
{
- temp->SetReactState(REACT_AGGRESSIVE);
- temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ summon->SetReactState(REACT_AGGRESSIVE);
+ summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
}
}
break;
@@ -640,12 +640,12 @@ struct boss_faction_championsAI : public BossAI
if (TeamInInstance == ALLIANCE)
{
- if (Creature* temp = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_VARIAN)))
- temp->AI()->Talk(SAY_KILL_PLAYER);
+ if (Creature* varian = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_VARIAN)))
+ varian->AI()->Talk(SAY_KILL_PLAYER);
}
else
- if (Creature* temp = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GARROSH)))
- temp->AI()->Talk(SAY_KILL_PLAYER);
+ if (Creature* garrosh = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GARROSH)))
+ garrosh->AI()->Talk(SAY_KILL_PLAYER);
}
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
index 2fba0c2af42..93823987d78 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
@@ -67,10 +67,12 @@ enum BossSpells
SPELL_SHIVAN_SLASH = 67098,
SPELL_SPINNING_STRIKE = 66283,
SPELL_MISTRESS_KISS = 66336,
- SPELL_FEL_INFERNO = 67047,
- SPELL_FEL_STREAK = 66494,
SPELL_LORD_HITTIN = 66326, // special effect preventing more specific spells be cast on the same player within 10 seconds
- SPELL_MISTRESS_KISS_DAMAGE_SILENCE = 66359
+ SPELL_MISTRESS_KISS_DAMAGE_SILENCE = 66359,
+
+ // Felflame Infernal
+ SPELL_FEL_STREAK_VISUAL = 66493,
+ SPELL_FEL_STREAK = 66494,
};
enum Events
@@ -116,7 +118,7 @@ class boss_jaraxxus : public CreatureScript
_JustReachedHome();
instance->SetBossState(BOSS_JARAXXUS, FAIL);
DoCast(me, SPELL_JARAXXUS_CHAINS);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
}
void KilledUnit(Unit* who) override
@@ -305,18 +307,17 @@ class npc_fel_infernal : public CreatureScript
{
npc_fel_infernalAI(Creature* creature) : ScriptedAI(creature)
{
- Initialize();
_instance = creature->GetInstanceScript();
}
- void Initialize()
- {
- _felStreakTimer = 30 * IN_MILLISECONDS;
- }
-
void Reset() override
{
- Initialize();
+ _scheduler.Schedule(Seconds(2), [this](TaskContext context)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
+ DoCast(target, SPELL_FEL_STREAK_VISUAL);
+ context.Repeat(Seconds(15));
+ });
me->SetInCombatWithZone();
}
@@ -328,23 +329,20 @@ class npc_fel_infernal : public CreatureScript
return;
}
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
if (!UpdateVictim())
return;
- if (_felStreakTimer <= diff)
+ _scheduler.Update(diff, [this]
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
- DoCast(target, SPELL_FEL_STREAK);
- _felStreakTimer = 30*IN_MILLISECONDS;
- }
- else
- _felStreakTimer -= diff;
-
- DoMeleeAttackIfReady();
+ DoMeleeAttackIfReady();
+ });
}
private:
- uint32 _felStreakTimer;
InstanceScript* _instance;
+ TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -579,6 +577,39 @@ class spell_mistress_kiss_area : public SpellScriptLoader
}
};
+class spell_fel_streak_visual : public SpellScriptLoader
+{
+public:
+ spell_fel_streak_visual() : SpellScriptLoader("spell_fel_streak_visual") { }
+
+ class spell_fel_streak_visual_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_fel_streak_visual_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_FEL_STREAK))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_fel_streak_visual_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_fel_streak_visual_SpellScript();
+ }
+};
+
void AddSC_boss_jaraxxus()
{
new boss_jaraxxus();
@@ -590,4 +621,5 @@ void AddSC_boss_jaraxxus()
new spell_mistress_kiss();
new spell_mistress_kiss_area();
+ new spell_fel_streak_visual();
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
index e3720503d0a..b2283ec6fde 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
@@ -25,6 +25,7 @@
#include "Vehicle.h"
#include "Player.h"
#include "SpellScript.h"
+#include "SpellAuraEffects.h"
enum Yells
{
@@ -33,6 +34,7 @@ enum Yells
// Acidmaw & Dreadscale
EMOTE_ENRAGE = 0,
+ SAY_SPECIAL = 1,
// Icehowl
EMOTE_TRAMPLE_START = 0,
@@ -78,22 +80,29 @@ enum BossSpells
SPELL_FIRE_BOMB_DOT = 66318,
SPELL_HEAD_CRACK = 66407,
- //Acidmaw & Dreadscale
+ //Acidmaw & Dreadscale Generic
+ SPELL_SWEEP = 66794,
+ SUMMON_SLIME_POOL = 66883,
+ SPELL_EMERGE = 66947,
+ SPELL_SUBMERGE = 66948,
+ SPELL_ENRAGE = 68335,
+ SPELL_SLIME_POOL_EFFECT = 66882, //In 60s it diameter grows from 10y to 40y (r=r+0.25 per second)
+ SPELL_GROUND_VISUAL_0 = 66969,
+ SPELL_GROUND_VISUAL_1 = 68302,
+ SPELL_HATE_TO_ZERO = 63984,
+ //Acidmaw
SPELL_ACID_SPIT = 66880,
SPELL_PARALYTIC_SPRAY = 66901,
- SPELL_ACID_SPEW = 66819,
- SPELL_PARALYTIC_BITE = 66824,
- SPELL_SWEEP_0 = 66794,
- SUMMON_SLIME_POOL = 66883,
- SPELL_FIRE_SPIT = 66796,
+ SPELL_PARALYTIC_BITE = 66824, //Paralytic Toxin
+ SPELL_ACID_SPEW = 66818,
+ SPELL_PARALYSIS = 66830,
+ SPELL_PARALYTIC_TOXIN = 66823,
+ //Dreadscale
+ SPELL_BURNING_BITE = 66879, // Burning Bile
SPELL_MOLTEN_SPEW = 66821,
- SPELL_BURNING_BITE = 66879,
+ SPELL_FIRE_SPIT = 66796,
SPELL_BURNING_SPRAY = 66902,
- SPELL_SWEEP_1 = 67646,
- SPELL_EMERGE_0 = 66947,
- SPELL_SUBMERGE_0 = 66948,
- SPELL_ENRAGE = 68335,
- SPELL_SLIME_POOL_EFFECT = 66882, //In 60s it diameter grows from 10y to 40y (r=r+0.25 per second)
+ SPELL_BURNING_BILE = 66869,
//Icehowl
SPELL_FEROCIOUS_BUTT = 66770,
@@ -182,7 +191,7 @@ class boss_gormok : public CreatureScript
{
case 0:
instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR));
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
break;
@@ -549,7 +558,7 @@ struct boss_jormungarAI : public BossAI
if (!Enraged && instance->GetData(TYPE_NORTHREND_BEASTS) == SNAKES_SPECIAL)
{
- me->RemoveAurasDueToSpell(SPELL_SUBMERGE_0);
+ me->RemoveAurasDueToSpell(SPELL_SUBMERGE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
DoCast(SPELL_ENRAGE);
Enraged = true;
@@ -586,10 +595,11 @@ struct boss_jormungarAI : public BossAI
case EVENT_SUMMON_ACIDMAW:
if (Creature* acidmaw = me->SummonCreature(NPC_ACIDMAW, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN))
{
- acidmaw->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ acidmaw->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
acidmaw->SetReactState(REACT_AGGRESSIVE);
acidmaw->SetInCombatWithZone();
- acidmaw->CastSpell(acidmaw, SPELL_EMERGE_0);
+ acidmaw->CastSpell(acidmaw, SPELL_EMERGE);
+ acidmaw->CastSpell(acidmaw, SPELL_GROUND_VISUAL_1, true);
}
return;
case EVENT_SPRAY:
@@ -598,7 +608,7 @@ struct boss_jormungarAI : public BossAI
events.ScheduleEvent(EVENT_SPRAY, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_STATIONARY);
return;
case EVENT_SWEEP:
- DoCastAOE(SPELL_SWEEP_0);
+ DoCastAOE(SPELL_SWEEP);
events.ScheduleEvent(EVENT_SWEEP, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_STATIONARY);
return;
default:
@@ -608,13 +618,14 @@ struct boss_jormungarAI : public BossAI
if (events.IsInPhase(PHASE_MOBILE))
DoMeleeAttackIfReady();
if (events.IsInPhase(PHASE_STATIONARY))
- DoSpellAttackIfReady(SpitSpell);
+ DoCastVictim(SpitSpell);
}
void Submerge()
{
- DoCast(me, SPELL_SUBMERGE_0);
- me->RemoveAurasDueToSpell(SPELL_EMERGE_0);
+ DoCast(me, SPELL_SUBMERGE);
+ DoCast(me, SPELL_GROUND_VISUAL_0, true);
+ me->RemoveAurasDueToSpell(SPELL_EMERGE);
me->SetInCombatWithZone();
events.SetPhase(PHASE_SUBMERGED);
events.ScheduleEvent(EVENT_EMERGE, 5*IN_MILLISECONDS, 0, PHASE_SUBMERGED);
@@ -625,17 +636,20 @@ struct boss_jormungarAI : public BossAI
void Emerge()
{
- DoCast(me, SPELL_EMERGE_0);
+ DoCast(me, SPELL_EMERGE);
+ DoCastAOE(SPELL_HATE_TO_ZERO, true);
me->SetDisplayId(ModelMobile);
- me->RemoveAurasDueToSpell(SPELL_SUBMERGE_0);
+ me->RemoveAurasDueToSpell(SPELL_SUBMERGE);
+ me->RemoveAurasDueToSpell(SPELL_GROUND_VISUAL_0);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
// if the worm was mobile before submerging, make him stationary now
if (WasMobile)
{
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
SetCombatMovement(false);
me->SetDisplayId(ModelStationary);
+ me->CastSpell(me, SPELL_GROUND_VISUAL_1, true);
events.SetPhase(PHASE_STATIONARY);
events.ScheduleEvent(EVENT_SUBMERGE, 45*IN_MILLISECONDS, 0, PHASE_STATIONARY);
events.ScheduleEvent(EVENT_SPIT, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_STATIONARY);
@@ -644,10 +658,11 @@ struct boss_jormungarAI : public BossAI
}
else
{
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
SetCombatMovement(true);
me->GetMotionMaster()->MoveChase(me->GetVictim());
me->SetDisplayId(ModelMobile);
+ me->RemoveAurasDueToSpell(SPELL_GROUND_VISUAL_1);
events.SetPhase(PHASE_MOBILE);
events.ScheduleEvent(EVENT_SUBMERGE, 45*IN_MILLISECONDS, 0, PHASE_MOBILE);
events.ScheduleEvent(EVENT_BITE, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_MOBILE);
@@ -737,7 +752,7 @@ class boss_dreadscale : public CreatureScript
{
case 0:
instance->DoCloseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR));
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
break;
@@ -910,7 +925,7 @@ class boss_icehowl : public CreatureScript
break;
case 2:
instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR));
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
break;
@@ -1008,7 +1023,7 @@ class boss_icehowl : public CreatureScript
me->SetTarget(_trampleTargetGUID);
_trampleCast = false;
SetCombatMovement(false);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
events.ScheduleEvent(EVENT_TRAMPLE, 4*IN_MILLISECONDS);
@@ -1092,7 +1107,7 @@ class boss_icehowl : public CreatureScript
Talk(EMOTE_TRAMPLE_FAIL);
}
_movementStarted = false;
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL);
SetCombatMovement(true);
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->Clear();
@@ -1122,6 +1137,148 @@ class boss_icehowl : public CreatureScript
}
};
+class spell_jormungars_paralytic_toxin : public SpellScriptLoader
+{
+public:
+ spell_jormungars_paralytic_toxin() : SpellScriptLoader("spell_jormungars_paralytic_toxin") { }
+
+ class spell_jormungars_paralytic_toxin_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_jormungars_paralytic_toxin_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PARALYSIS))
+ return false;
+ return true;
+ }
+
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* caster = GetCaster();
+ if (caster && caster->GetEntry() == NPC_ACIDMAW)
+ {
+ if (Creature* acidMaw = caster->ToCreature())
+ acidMaw->AI()->Talk(SAY_SPECIAL, GetTarget());
+ }
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ GetTarget()->RemoveAurasDueToSpell(SPELL_PARALYSIS);
+ }
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)
+ {
+ if (!canBeRecalculated)
+ amount = aurEff->GetAmount();
+
+ canBeRecalculated = false;
+ }
+
+ void HandleDummy(AuraEffect const* /*aurEff*/)
+ {
+ if (AuraEffect* slowEff = GetEffect(EFFECT_0))
+ {
+ int32 newAmount = slowEff->GetAmount() - 10;
+ if (newAmount < -100)
+ newAmount = -100;
+ slowEff->ChangeAmount(newAmount);
+
+ if (newAmount <= -100 && !GetTarget()->HasAura(SPELL_PARALYSIS))
+ GetTarget()->CastSpell(GetTarget(), SPELL_PARALYSIS, true, NULL, slowEff, GetCasterGUID());
+ }
+ }
+
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_jormungars_paralytic_toxin_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_jormungars_paralytic_toxin_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_jormungars_paralytic_toxin_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED);
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_jormungars_paralytic_toxin_AuraScript::HandleDummy, EFFECT_2, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_jormungars_paralytic_toxin_AuraScript();
+ }
+};
+
+class spell_jormungars_snakes_spray : public SpellScriptLoader
+{
+public:
+ spell_jormungars_snakes_spray(const char* name, uint32 spellId) : SpellScriptLoader(name), _spellId(spellId) { }
+
+ class spell_jormungars_snakes_spray_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_jormungars_snakes_spray_SpellScript);
+
+ public:
+ spell_jormungars_snakes_spray_SpellScript(uint32 spellId) : SpellScript(), _spellId(spellId) { }
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(_spellId))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Player* target = GetHitPlayer())
+ GetCaster()->CastSpell(target, _spellId, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_jormungars_snakes_spray_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
+ }
+
+ private:
+ uint32 _spellId;
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_jormungars_snakes_spray_SpellScript(_spellId);
+ }
+
+private:
+ uint32 _spellId;
+};
+
+class spell_jormungars_paralysis : public SpellScriptLoader
+{
+public:
+ spell_jormungars_paralysis() : SpellScriptLoader("spell_jormungars_paralysis") { }
+
+ class spell_jormungars_paralysis_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_jormungars_paralysis_AuraScript);
+
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Unit* caster = GetCaster())
+ if (InstanceScript* instance = caster->GetInstanceScript())
+ if (instance->GetData(TYPE_NORTHREND_BEASTS) == SNAKES_IN_PROGRESS || instance->GetData(TYPE_NORTHREND_BEASTS) == SNAKES_SPECIAL)
+ return;
+
+ Remove();
+ }
+
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_jormungars_paralysis_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_jormungars_paralysis_AuraScript();
+ }
+};
+
void AddSC_boss_northrend_beasts()
{
new boss_gormok();
@@ -1132,6 +1289,10 @@ void AddSC_boss_northrend_beasts()
new boss_acidmaw();
new boss_dreadscale();
new npc_slime_pool();
+ new spell_jormungars_paralytic_toxin();
+ new spell_jormungars_snakes_spray("spell_jormungars_burning_spray", SPELL_BURNING_BILE);
+ new spell_jormungars_snakes_spray("spell_jormungars_paralytic_spray", SPELL_PARALYTIC_TOXIN);
+ new spell_jormungars_paralysis();
new boss_icehowl();
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp
index 278f6a7ab4f..c6ac59218ea 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp
@@ -440,10 +440,8 @@ class boss_fjola : public CreatureScript
// Allocate an unique random stage to each position in the array.
for (int i = 0; i < MAX_STAGES - 1; ++i)
{
- int random = i + (rand32() % (MAX_STAGES - i));
- int temp = Stage[i];
- Stage[i] = Stage[random];
- Stage[random] = temp;
+ int random = i + urand(0, MAX_STAGES - i);
+ std::swap(Stage[i], Stage[random]);
}
}
private:
@@ -804,8 +802,8 @@ class spell_valkyr_essences : public SpellScriptLoader
else
{
owner->CastSpell(owner, poweringUp, true);
- if (Aura* pTemp = owner->GetAura(poweringUp))
- pTemp->ModStackAmount(stacksCount);
+ if ((pAura = owner->GetAura(poweringUp)))
+ pAura->ModStackAmount(stacksCount);
}
}
}
@@ -829,8 +827,8 @@ class spell_valkyr_essences : public SpellScriptLoader
else
{
owner->CastSpell(owner, poweringUp, true);
- if (Aura* pTemp = owner->GetAura(poweringUp))
- pTemp->ModStackAmount(stacksCount);
+ if ((pAura = owner->GetAura(poweringUp)))
+ pAura->ModStackAmount(stacksCount);
}
}
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp
index b1a0f0217c4..eb4840f3b8b 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp
@@ -314,7 +314,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript
if (tributeChest)
if (Creature* tirion = instance->GetCreature(TirionGUID))
- if (GameObject* chest = tirion->SummonGameObject(tributeChest, 805.62f, 134.87f, 142.16f, 3.27f, 0, 0, 0, 0, WEEK))
+ if (GameObject* chest = tirion->SummonGameObject(tributeChest, 805.62f, 134.87f, 142.16f, 3.27f, G3D::Quat(), WEEK))
chest->SetRespawnTime(chest->GetRespawnDelay());
break;
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp
index 0ffe74932e0..8ea292d1de5 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp
@@ -174,7 +174,7 @@ class npc_announcer_toc10 : public CreatureScript
if (Creature* jaraxxus = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_JARAXXUS)))
{
jaraxxus->RemoveAurasDueToSpell(SPELL_JARAXXUS_CHAINS);
- jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
jaraxxus->SetReactState(REACT_DEFENSIVE);
jaraxxus->SetInCombatWithZone();
}
@@ -360,11 +360,11 @@ class npc_fizzlebang_toc : public CreatureScript
{
Talk(SAY_STAGE_1_06, killer);
_instance->SetData(TYPE_EVENT, 1180);
- if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
+ if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
{
- temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- temp->SetReactState(REACT_AGGRESSIVE);
- temp->SetInCombatWithZone();
+ jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ jaraxxus->SetReactState(REACT_AGGRESSIVE);
+ jaraxxus->SetInCombatWithZone();
}
}
@@ -457,18 +457,18 @@ class npc_fizzlebang_toc : public CreatureScript
break;
case 1140:
Talk(SAY_STAGE_1_04);
- if (Creature* temp = me->SummonCreature(NPC_JARAXXUS, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY(), ToCCommonLoc[1].GetPositionZ(), 5.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME))
+ if (Creature* jaraxxus = me->SummonCreature(NPC_JARAXXUS, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY(), ToCCommonLoc[1].GetPositionZ(), 5.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME))
{
- temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- temp->SetReactState(REACT_PASSIVE);
- temp->GetMotionMaster()->MovePoint(0, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY()-10, ToCCommonLoc[1].GetPositionZ());
+ jaraxxus->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ jaraxxus->SetReactState(REACT_PASSIVE);
+ jaraxxus->GetMotionMaster()->MovePoint(0, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY()-10, ToCCommonLoc[1].GetPositionZ());
}
_instance->SetData(TYPE_EVENT, 1142);
_updateTimer = 5*IN_MILLISECONDS;
break;
case 1142:
- if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
- temp->SetTarget(me->GetGUID());
+ if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
+ jaraxxus->SetTarget(me->GetGUID());
if (Creature* pTrigger = ObjectAccessor::GetCreature(*me, _triggerGUID))
pTrigger->DespawnOrUnsummon();
if (Creature* pPortal = ObjectAccessor::GetCreature(*me, _portalGUID))
@@ -477,19 +477,19 @@ class npc_fizzlebang_toc : public CreatureScript
_updateTimer = 10*IN_MILLISECONDS;
break;
case 1144:
- if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
- temp->AI()->Talk(SAY_STAGE_1_05);
+ if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
+ jaraxxus->AI()->Talk(SAY_STAGE_1_05);
_instance->SetData(TYPE_EVENT, 1150);
_updateTimer = 5*IN_MILLISECONDS;
break;
case 1150:
- if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
+ if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS)))
{
//1-shot Fizzlebang
- temp->CastSpell(me, 67888, false);
- me->SetInCombatWith(temp);
- temp->AddThreat(me, 1000.0f);
- temp->AI()->AttackStart(me);
+ jaraxxus->CastSpell(me, 67888, false); // 67888 - Fel Lightning
+ me->SetInCombatWith(jaraxxus);
+ jaraxxus->AddThreat(me, 1000.0f);
+ jaraxxus->AI()->AttackStart(me);
}
_instance->SetData(TYPE_EVENT, 1160);
_updateTimer = 3*IN_MILLISECONDS;
@@ -561,11 +561,11 @@ class npc_tirion_toc : public CreatureScript
{
_instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR));
- if (Creature* temp = me->SummonCreature(NPC_GORMOK, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30*IN_MILLISECONDS))
+ if (Creature* gormok = me->SummonCreature(NPC_GORMOK, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30*IN_MILLISECONDS))
{
- temp->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ());
- temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- temp->SetReactState(REACT_PASSIVE);
+ gormok->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ());
+ gormok->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ gormok->SetReactState(REACT_PASSIVE);
}
}
_updateTimer = 3*IN_MILLISECONDS;
@@ -582,11 +582,11 @@ class npc_tirion_toc : public CreatureScript
if (_instance->GetBossState(BOSS_BEASTS) != DONE)
{
_instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR));
- if (Creature* temp = me->SummonCreature(NPC_DREADSCALE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN))
+ if (Creature* dreadscale = me->SummonCreature(NPC_DREADSCALE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN))
{
- temp->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ());
- temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- temp->SetReactState(REACT_PASSIVE);
+ dreadscale->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ());
+ dreadscale->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ dreadscale->SetReactState(REACT_PASSIVE);
}
}
_updateTimer = 5*IN_MILLISECONDS;
@@ -600,9 +600,9 @@ class npc_tirion_toc : public CreatureScript
if (_instance->GetBossState(BOSS_BEASTS) != DONE)
{
_instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR));
- if (Creature* temp = me->SummonCreature(NPC_ICEHOWL, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_DEAD_DESPAWN))
+ if (Creature* icehowl = me->SummonCreature(NPC_ICEHOWL, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_DEAD_DESPAWN))
{
- temp->GetMotionMaster()->MovePoint(2, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ());
+ icehowl->GetMotionMaster()->MovePoint(2, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ());
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
}
@@ -698,34 +698,34 @@ class npc_tirion_toc : public CreatureScript
break;
case 4010:
Talk(SAY_STAGE_3_02);
- if (Creature* temp = me->SummonCreature(NPC_LIGHTBANE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME))
+ if (Creature* lightbane = me->SummonCreature(NPC_LIGHTBANE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME))
{
- temp->SetVisible(false);
- temp->SetReactState(REACT_PASSIVE);
- temp->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[0].GetPositionX(), TwinValkyrsLoc[0].GetPositionY(), TwinValkyrsLoc[0].GetPositionZ());
- temp->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[1].GetPositionX(), TwinValkyrsLoc[1].GetPositionY(), TwinValkyrsLoc[1].GetPositionZ());
+ lightbane->SetVisible(false);
+ lightbane->SetReactState(REACT_PASSIVE);
+ lightbane->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[0].GetPositionX(), TwinValkyrsLoc[0].GetPositionY(), TwinValkyrsLoc[0].GetPositionZ());
+ lightbane->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[1].GetPositionX(), TwinValkyrsLoc[1].GetPositionY(), TwinValkyrsLoc[1].GetPositionZ());
}
- if (Creature* temp = me->SummonCreature(NPC_DARKBANE, ToCSpawnLoc[2].GetPositionX(), ToCSpawnLoc[2].GetPositionY(), ToCSpawnLoc[2].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME))
+ if (Creature* darkbane = me->SummonCreature(NPC_DARKBANE, ToCSpawnLoc[2].GetPositionX(), ToCSpawnLoc[2].GetPositionY(), ToCSpawnLoc[2].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME))
{
- temp->SetVisible(false);
- temp->SetReactState(REACT_PASSIVE);
- temp->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[2].GetPositionX(), TwinValkyrsLoc[2].GetPositionY(), TwinValkyrsLoc[2].GetPositionZ());
- temp->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[3].GetPositionX(), TwinValkyrsLoc[3].GetPositionY(), TwinValkyrsLoc[3].GetPositionZ());
+ darkbane->SetVisible(false);
+ darkbane->SetReactState(REACT_PASSIVE);
+ darkbane->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[2].GetPositionX(), TwinValkyrsLoc[2].GetPositionY(), TwinValkyrsLoc[2].GetPositionZ());
+ darkbane->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[3].GetPositionX(), TwinValkyrsLoc[3].GetPositionY(), TwinValkyrsLoc[3].GetPositionZ());
}
_updateTimer = 3*IN_MILLISECONDS;
_instance->SetData(TYPE_EVENT, 4015);
break;
case 4015:
_instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR));
- if (Creature* temp = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_LIGHTBANE)))
+ if (Creature* lightbane = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_LIGHTBANE)))
{
- temp->GetMotionMaster()->MovePoint(1, ToCCommonLoc[8].GetPositionX(), ToCCommonLoc[8].GetPositionY(), ToCCommonLoc[8].GetPositionZ());
- temp->SetVisible(true);
+ lightbane->GetMotionMaster()->MovePoint(1, ToCCommonLoc[8].GetPositionX(), ToCCommonLoc[8].GetPositionY(), ToCCommonLoc[8].GetPositionZ());
+ lightbane->SetVisible(true);
}
- if (Creature* temp = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_DARKBANE)))
+ if (Creature* darkbane = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_DARKBANE)))
{
- temp->GetMotionMaster()->MovePoint(1, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ());
- temp->SetVisible(true);
+ darkbane->GetMotionMaster()->MovePoint(1, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ());
+ darkbane->SetVisible(true);
}
_updateTimer = 10*IN_MILLISECONDS;
_instance->SetData(TYPE_EVENT, 4016);
@@ -754,9 +754,9 @@ class npc_tirion_toc : public CreatureScript
_instance->SetData(TYPE_EVENT, 0);
break;
case 6000:
- me->SummonCreature(NPC_TIRION_FORDRING, EndSpawnLoc[0].GetPositionX(), EndSpawnLoc[0].GetPositionY(), EndSpawnLoc[0].GetPositionZ());
- me->SummonCreature(NPC_ARGENT_MAGE, EndSpawnLoc[1].GetPositionX(), EndSpawnLoc[1].GetPositionY(), EndSpawnLoc[1].GetPositionZ());
- me->SummonGameObject(GO_PORTAL_TO_DALARAN, EndSpawnLoc[2].GetPositionX(), EndSpawnLoc[2].GetPositionY(), EndSpawnLoc[2].GetPositionZ(), 5, 0, 0, 0, 0, 0);
+ me->SummonCreature(NPC_TIRION_FORDRING, EndSpawnLoc[0]);
+ me->SummonCreature(NPC_ARGENT_MAGE, EndSpawnLoc[1]);
+ me->SummonGameObject(GO_PORTAL_TO_DALARAN, EndSpawnLoc[2], G3D::Quat(), 0);
_updateTimer = 20*IN_MILLISECONDS;
_instance->SetData(TYPE_EVENT, 6005);
break;
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h
index 90b9781954f..d53d6705400 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h
@@ -140,9 +140,9 @@ const Position AnubarakLoc[]=
const Position EndSpawnLoc[]=
{
- {648.9167f, 131.0208f, 141.6161f, 0}, // 0 - Highlord Tirion Fordring
- {649.1614f, 142.0399f, 141.3057f, 0}, // 1 - Argent Mage
- {644.6250f, 149.2743f, 140.6015f, 0} // 2 - Portal to Dalaran
+ {648.9167f, 131.0208f, 141.6161f, 0.f}, // 0 - Highlord Tirion Fordring
+ {649.1614f, 142.0399f, 141.3057f, 0.f}, // 1 - Argent Mage
+ {644.6250f, 149.2743f, 140.6015f, 5.f} // 2 - Portal to Dalaran
};
enum WorldStateIds
diff --git a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp
index ce722e391d1..292f1d7074f 100644
--- a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp
+++ b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp
@@ -198,8 +198,6 @@ public:
{
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
if (me->HasUnitState(UNIT_STATE_CASTING))
me->CastStop();
}
@@ -207,8 +205,6 @@ public:
{
if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
DoCast(SPELL_ARCANE_FIELD);
}
}
diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp
index 055d0a07f41..94d1d93225c 100644
--- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp
@@ -144,7 +144,7 @@ class boss_devourer_of_souls : public CreatureScript
void Reset() override
{
_Reset();
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->SetDisplayId(DISPLAY_ANGER);
me->SetReactState(REACT_AGGRESSIVE);
@@ -297,7 +297,7 @@ class boss_devourer_of_souls : public CreatureScript
me->SetTarget(ObjectGuid::Empty);
me->GetMotionMaster()->Clear();
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
wailingSoulTick = 15;
events.DelayEvents(18000); // no other events during wailing souls
@@ -317,7 +317,7 @@ class boss_devourer_of_souls : public CreatureScript
{
me->SetReactState(REACT_AGGRESSIVE);
me->SetDisplayId(DISPLAY_ANGER);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->GetMotionMaster()->MoveChase(me->GetVictim());
events.ScheduleEvent(EVENT_WAILING_SOULS, urand(60000, 70000));
}
diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
index 3ac85809fa2..6b20ae6a373 100644
--- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
@@ -1921,6 +1921,7 @@ class npc_frostsworn_general : public CreatureScript
{
if (Creature* reflection = me->SummonCreature(NPC_REFLECTION, *target, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 3000))
{
+ reflection->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
target->CastSpell(reflection, SPELL_CLONE, true);
target->CastSpell(reflection, SPELL_GHOST_VISUAL, true);
reflection->AI()->AttackStart(target);
@@ -2155,6 +2156,7 @@ struct npc_escape_event_trash : public ScriptedAI
DoZoneInCombat(me, 0.0f);
if (Creature* leader = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ESCAPE_LEADER)))
{
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->SetInCombatWith(leader);
leader->SetInCombatWith(me);
me->AddThreat(leader, 0.0f);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
index d77842fff0a..73a7de36580 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
@@ -2461,6 +2461,33 @@ class spell_igb_teleport_players_on_victory : public SpellScriptLoader
}
};
+// 71201 - Battle Experience - proc should never happen, handled in script
+class spell_igb_battle_experience_check : public SpellScriptLoader
+{
+public:
+ spell_igb_battle_experience_check() : SpellScriptLoader("spell_igb_battle_experience_check") { }
+
+ class spell_igb_battle_experience_check_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_igb_battle_experience_check_AuraScript);
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ return false;
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_igb_battle_experience_check_AuraScript::CheckProc);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_igb_battle_experience_check_AuraScript();
+ }
+};
+
class achievement_im_on_a_boat : public AchievementCriteriaScript
{
public:
@@ -2500,5 +2527,6 @@ void AddSC_boss_icecrown_gunship_battle()
new spell_igb_gunship_fall_teleport();
new spell_igb_check_for_players();
new spell_igb_teleport_players_on_victory();
+ new spell_igb_battle_experience_check();
new achievement_im_on_a_boat();
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
index 1a37d5238d2..aa5060e83b4 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
@@ -1312,7 +1312,7 @@ class spell_sindragosa_ice_tomb : public SpellScriptLoader
{
summon->AI()->SetGUID(GetTarget()->GetGUID(), DATA_TRAPPED_PLAYER);
GetTarget()->CastSpell(GetTarget(), SPELL_ICE_TOMB_UNTARGETABLE);
- if (GameObject* go = summon->SummonGameObject(GO_ICE_BLOCK, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0))
+ if (GameObject* go = summon->SummonGameObject(GO_ICE_BLOCK, pos, G3D::Quat(), 0))
{
go->SetSpellId(SPELL_ICE_TOMB_DAMAGE);
summon->AddGameObject(go);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
index 4a0a8217af8..de8d65693b9 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -528,6 +528,7 @@ class boss_the_lich_king : public CreatureScript
void SetupEncounter()
{
_Reset();
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_PASSIVE);
events.SetPhase(PHASE_INTRO);
Initialize();
diff --git a/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp
index 11cc645f0cb..f7e83ff1f42 100644
--- a/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp
+++ b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp
@@ -259,6 +259,58 @@ class spell_ioc_launch : public SpellScriptLoader
}
};
+enum SeaforiumBombSpells
+{
+ SPELL_SEAFORIUM_BLAST = 66676,
+ SPELL_HUGE_SEAFORIUM_BLAST = 66672,
+ SPELL_A_BOMB_INABLE_CREDIT = 68366,
+ SPELL_A_BOMB_INATION_CREDIT = 68367
+};
+
+class spell_ioc_seaforium_blast_credit : public SpellScriptLoader
+{
+ public:
+ spell_ioc_seaforium_blast_credit() : SpellScriptLoader("spell_ioc_seaforium_blast_credit") { }
+
+ class spell_ioc_seaforium_blast_credit_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ioc_seaforium_blast_credit_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_A_BOMB_INABLE_CREDIT) || !sSpellMgr->GetSpellInfo(SPELL_A_BOMB_INATION_CREDIT))
+ return false;
+ return true;
+ }
+
+ void HandleAchievementCredit(SpellEffIndex /*effIndex*/)
+ {
+ uint32 _creditSpell = 0;
+ Unit* caster = GetOriginalCaster();
+ if (!caster)
+ return;
+
+ if (GetSpellInfo()->Id == SPELL_SEAFORIUM_BLAST)
+ _creditSpell = SPELL_A_BOMB_INABLE_CREDIT;
+ else if (GetSpellInfo()->Id == SPELL_HUGE_SEAFORIUM_BLAST)
+ _creditSpell = SPELL_A_BOMB_INATION_CREDIT;
+
+ if (GetHitGObj() && GetHitGObj()->IsDestructibleBuilding())
+ caster->CastSpell(caster, _creditSpell, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ioc_seaforium_blast_credit_SpellScript::HandleAchievementCredit, EFFECT_1, SPELL_EFFECT_GAMEOBJECT_DAMAGE);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_ioc_seaforium_blast_credit_SpellScript();
+ }
+};
+
void AddSC_isle_of_conquest()
{
new npc_four_car_garage();
@@ -266,4 +318,5 @@ void AddSC_isle_of_conquest()
new spell_ioc_gunship_portal();
new spell_ioc_parachute_ic();
new spell_ioc_launch();
+ new spell_ioc_seaforium_blast_credit();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
index ef6ccf5bf4b..6ec9af68723 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
@@ -405,17 +405,19 @@ public:
if (state == STATE_ZOMBIE_DECIMATED)
{
timer += diff;
- Creature* gluth = ObjectAccessor::GetCreature(*me, GluthGUID);
- // Putting this in the UpdateAI loop fixes an issue where death gripping a decimated zombie would make the zombie stand still until the rest of the fight.
- // Also fix the issue where if one or more zombie is rooted when decimates hits (and MovePoint() is called), the zombie teleport to the boss. pretty weird behavior.
- if (gluth && timer>1600 && me->GetExactDist2d(gluth) > 10.0 && me->CanFreeMove()) // it takes about 1600 ms for the animation to cycle. This way, the animation looks relatively smooth.
+ if (Creature* gluth = ObjectAccessor::GetCreature(*me, GluthGUID))
{
- me->GetMotionMaster()->MovePoint(0, gluth->GetPosition()); // isn't dynamic. So, to take into account Gluth's movement, it must be called periodicly.
- timer = 0;
- }
+ // Putting this in the UpdateAI loop fixes an issue where death gripping a decimated zombie would make the zombie stand still until the rest of the fight.
+ // Also fix the issue where if one or more zombie is rooted when decimates hits (and MovePoint() is called), the zombie teleport to the boss. pretty weird behavior.
+ if (timer > 1600 && me->GetExactDist2d(gluth) > 10.0f && me->CanFreeMove()) // it takes about 1600 ms for the animation to cycle. This way, the animation looks relatively smooth.
+ {
+ me->GetMotionMaster()->MovePoint(0, gluth->GetPosition()); // isn't dynamic. So, to take into account Gluth's movement, it must be called periodicly.
+ timer = 0;
+ }
- if (me->GetExactDist2d(gluth) <= 10.0)
- me->StopMoving();
+ if (me->GetExactDist2d(gluth) <= 10.0f)
+ me->StopMoving();
+ }
}
else if (state == STATE_ZOMBIE_NORMAL)
DoMeleeAttackIfReady();
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
index 47807ec28cb..42efdfbfe67 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
@@ -309,7 +309,7 @@ public:
_Reset();
me->setFaction(35);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE);
ResetPlayerScale();
spawns.DespawnAll();
@@ -356,7 +356,7 @@ public:
}
DoCast(me, SPELL_KELTHUZAD_CHANNEL, false);
Talk(SAY_SUMMON_MINIONS);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE);
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 4);
me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 4);
events.SetPhase(PHASE_ONE);
@@ -426,7 +426,7 @@ public:
Talk(SAY_AGGRO);
Talk(EMOTE_PHASE_TWO);
spawns.DespawnAll();
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE);
me->CastStop();
DoStartMovement(me->GetVictim());
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp
index 4596b17bc23..0c9a236e196 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp
@@ -485,7 +485,7 @@ class spell_sapphiron_icebolt : public SpellScriptLoader
return;
float x, y, z;
GetTarget()->GetPosition(x, y, z);
- if (GameObject* block = GetTarget()->SummonGameObject(GO_ICEBLOCK, x, y, z, 0, 0, 0, 0, 0, 25))
+ if (GameObject* block = GetTarget()->SummonGameObject(GO_ICEBLOCK, x, y, z, 0.f, G3D::Quat(), 25))
_block = block->GetGUID();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
index e3971248513..720549de0b2 100644
--- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
@@ -123,6 +123,9 @@ class instance_naxxramas : public InstanceMapScript
CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT;
playerDied = 0;
+
+ nextFroggerWave = 0;
+ events.ScheduleEvent(EVENT_SUMMON_FROGGER_WAVE, Seconds(1));
}
void OnCreatureCreate(Creature* creature) override
@@ -477,6 +480,16 @@ class instance_naxxramas : public InstanceMapScript
kelthuzad->AI()->Talk(CurrentWingTaunt);
++CurrentWingTaunt;
break;
+ case EVENT_SUMMON_FROGGER_WAVE:
+ {
+ std::list<TempSummon*> spawns;
+ instance->SummonCreatureGroup(nextFroggerWave, &spawns);
+ if (!spawns.empty())
+ (*spawns.begin())->GetMotionMaster()->MovePath(10 * NPC_FROGGER + nextFroggerWave, false);
+ events.Repeat(Seconds(1) + Milliseconds(666));
+ nextFroggerWave = (nextFroggerWave+1) % 3;
+ break;
+ }
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD);
@@ -613,6 +626,8 @@ class instance_naxxramas : public InstanceMapScript
/* The Immortal / The Undying */
uint32 playerDied;
+ int8 nextFroggerWave;
+
EventMap events;
};
diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h
index a3fedf5aa40..75e7314c5d0 100644
--- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h
+++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h
@@ -107,7 +107,8 @@ enum CreaturesIds
NPC_DK_UNDERSTUDY = 16803,
NPC_BIGGLESWORTH = 16998,
NPC_LICH_KING = 16980,
- NPC_OLD_WORLD_TRIGGER = 15384
+ NPC_OLD_WORLD_TRIGGER = 15384,
+ NPC_FROGGER = 16027
};
enum GameObjectsIds
@@ -175,6 +176,9 @@ enum InstanceEvents
// Dialogue that happens after each wing.
EVENT_KELTHUZAD_WING_TAUNT,
+ // Periodic Frogger summon
+ EVENT_SUMMON_FROGGER_WAVE,
+
// Dialogue that happens after Sapphiron's death.
EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD,
EVENT_DIALOGUE_SAPPHIRON_LICHKING,
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
index 3d5a6ee8dfb..914a1a8bbb0 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
@@ -376,6 +376,8 @@ public:
me->SetDisableGravity(true);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
// TO DO: find what in core is making boss slower than in retail (when correct speed data) or find missing movement flag update or forced spline change
me->SetSpeedRate(MOVE_FLIGHT, _flySpeed * 0.25f);
if (_despawned)
@@ -464,7 +466,7 @@ public:
pos.m_positionZ = alexstraszaBunny->GetPositionZ();
alexstraszaBunny->GetNearPoint2D(pos.m_positionX, pos.m_positionY, 30.0f, alexstraszaBunny->GetAngle(me));
me->GetMotionMaster()->MoveLand(POINT_LAND_P_ONE, pos);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_LAND_START_ENCOUNTER, 7*IN_MILLISECONDS, 1, PHASE_NOT_STARTED);
@@ -604,8 +606,6 @@ public:
// Set speed to normal value
me->SetSpeedRate(MOVE_FLIGHT, _flySpeed);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAuras();
me->CombatStop(); // Sometimes threat can remain, so it's a safety measure
@@ -1001,14 +1001,7 @@ public:
_JustDied();
Talk(SAY_DEATH);
if (Creature* alexstraszaGiftBoxBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_GIFT_BOX_BUNNY_GUID)))
- {
- if (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)
- alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_10, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(),
- HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0);
- else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
- alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_25, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(),
- HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0);
- }
+ alexstraszaGiftBoxBunny->SummonGameObject(RAID_MODE(GO_HEART_OF_MAGIC_10, GO_HEART_OF_MAGIC_25), HeartOfMagicSpawnPos, G3D::Quat(), 0);
me->SummonCreature(NPC_ALEXSTRASZA, AlexstraszaSpawnPos, TEMPSUMMON_MANUAL_DESPAWN);
me->DespawnOrUnsummon(5*IN_MILLISECONDS);
@@ -2439,9 +2432,9 @@ class spell_alexstrasza_gift_beam_visual : public SpellScriptLoader
if (Creature* target = GetTarget()->ToCreature())
{
if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)
- _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_10, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0);
+ _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_10, *target, G3D::Quat(), 0);
else if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
- _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_25, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0);
+ _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_25, *target, G3D::Quat(), 0);
}
}
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp
index e87115dd8e2..eb8a92f7b28 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp
@@ -86,9 +86,7 @@ public:
void SpawnGameObject(uint32 entry, Position const& pos)
{
GameObject* go = new GameObject();
- if (!go->Create(instance->GenerateLowGuid<HighGuid::GameObject>(), entry, instance,
- PHASEMASK_NORMAL, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(),
- 0, 0, 0, 0, 120, GO_STATE_READY))
+ if (!go->Create(instance->GenerateLowGuid<HighGuid::GameObject>(), entry, instance, PHASEMASK_NORMAL, pos, G3D::Quat(), 255, GO_STATE_READY))
{
delete go;
return;
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
index 8f7687d0fca..55295a534e1 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
@@ -113,7 +113,7 @@ public:
Initialize();
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL);
if (!me->IsVisible())
me->SetVisible(true);
@@ -152,7 +152,7 @@ public:
me->AttackStop();
me->SetVisible(false);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
@@ -236,7 +236,7 @@ public:
else if (lSparkList.empty())
{
me->SetVisible(true);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL);
DoCast(me, SPELL_SPARK_DESPAWN, false);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
index 05beacca638..fa59c021cad 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
@@ -171,8 +171,7 @@ enum FreyaNpcs
enum FreyaActions
{
- ACTION_ELDER_DEATH = 1,
- ACTION_ELDER_FREYA_KILLED = 2
+ ACTION_ELDER_FREYA_KILLED = 1
};
enum FreyaEvents
@@ -619,7 +618,7 @@ class boss_freya : public CreatureScript
Elder->AttackStop();
Elder->CombatStop(true);
Elder->DeleteThreatList();
- Elder->GetAI()->DoAction(ACTION_ELDER_FREYA_KILLED);
+ Elder->AI()->DoAction(ACTION_ELDER_FREYA_KILLED);
}
}
}
@@ -705,19 +704,10 @@ class boss_elder_brightleaf : public CreatureScript
Talk(SAY_ELDER_SLAY);
}
- void JustDied(Unit* killer) override
+ void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_ELDER_DEATH);
-
- if (killer->GetTypeId() == TYPEID_PLAYER)
- {
- if (Creature* Ironbranch = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_IRONBRANCH)))
- Ironbranch->AI()->DoAction(ACTION_ELDER_DEATH);
-
- if (Creature* Stonebark = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_STONEBARK)))
- Stonebark->AI()->DoAction(ACTION_ELDER_DEATH);
- }
}
void EnterCombat(Unit* /*who*/) override
@@ -812,19 +802,10 @@ class boss_elder_stonebark : public CreatureScript
Talk(SAY_ELDER_SLAY);
}
- void JustDied(Unit* killer) override
+ void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_ELDER_DEATH);
-
- if (killer->GetTypeId() == TYPEID_PLAYER)
- {
- if (Creature* Ironbranch = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_IRONBRANCH)))
- Ironbranch->AI()->DoAction(ACTION_ELDER_DEATH);
-
- if (Creature* Brightleaf = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_BRIGHTLEAF)))
- Brightleaf->AI()->DoAction(ACTION_ELDER_DEATH);
- }
}
void EnterCombat(Unit* /*who*/) override
@@ -925,19 +906,10 @@ class boss_elder_ironbranch : public CreatureScript
Talk(SAY_ELDER_SLAY);
}
- void JustDied(Unit* killer) override
+ void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_ELDER_DEATH);
-
- if (killer->GetTypeId() == TYPEID_PLAYER)
- {
- if (Creature* Brightleaf = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_BRIGHTLEAF)))
- Brightleaf->AI()->DoAction(ACTION_ELDER_DEATH);
-
- if (Creature* Stonebark = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_STONEBARK)))
- Stonebark->AI()->DoAction(ACTION_ELDER_DEATH);
- }
}
void EnterCombat(Unit* /*who*/) override
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp
index 09d95b34521..cbd24141bdf 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp
@@ -415,7 +415,7 @@ class npc_saronite_vapors : public CreatureScript
if (damage >= me->GetHealth())
{
damage = 0;
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->SetStandState(UNIT_STAND_STATE_DEAD);
me->SetHealth(me->GetMaxHealth());
me->RemoveAllAuras();
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp
index c35a5936822..c03a1c6fbc1 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp
@@ -30,6 +30,8 @@
Destroying of Toasty Fires
*/
+/* @todo Hodir aggro behavior is wonky. He gets set to _PASSIVE, but never to _AGGRESSIVE unless you kill an ice block which doesn't spawn unless you have*/
+
enum HodirYells
{
SAY_AGGRO = 0,
@@ -184,7 +186,7 @@ class npc_flash_freeze : public CreatureScript
Initialize();
instance = me->GetInstanceScript();
me->SetDisplayId(me->GetCreatureTemplate()->Modelid2);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
}
void Initialize()
@@ -259,7 +261,7 @@ class npc_ice_block : public CreatureScript
{
instance = me->GetInstanceScript();
me->SetDisplayId(me->GetCreatureTemplate()->Modelid2);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
}
InstanceScript* instance;
@@ -269,7 +271,7 @@ class npc_ice_block : public CreatureScript
void IsSummonedBy(Unit* summoner) override
{
targetGUID = summoner->GetGUID();
- summoner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
+ summoner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
me->SetInCombatWith(summoner);
me->AddThreat(summoner, 250.0f);
summoner->AddThreat(me, 250.0f);
@@ -285,7 +287,7 @@ class npc_ice_block : public CreatureScript
{
if (Creature* Helper = ObjectAccessor::GetCreature(*me, targetGUID))
{
- Helper->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
+ Helper->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
if (Creature* Hodir = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_HODIR)))
{
@@ -375,7 +377,7 @@ class boss_hodir : public CreatureScript
Talk(SAY_SLAY);
}
- void DamageTaken(Unit* /*who*/, uint32& damage) override
+ void DamageTaken(Unit* who, uint32& damage) override
{
if (damage >= me->GetHealth())
{
@@ -388,7 +390,7 @@ class boss_hodir : public CreatureScript
me->RemoveAllAttackers();
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL);
me->InterruptNonMeleeSpells(true);
me->StopMoving();
me->GetMotionMaster()->Clear();
@@ -403,6 +405,12 @@ class boss_hodir : public CreatureScript
_JustDied();
}
+ else if (!me->IsInCombat())
+ {
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->AI()->DoZoneInCombat();
+ me->AI()->AttackStart(who);
+ }
}
void UpdateAI(uint32 diff) override
@@ -539,7 +547,7 @@ class npc_icicle : public CreatureScript
{
Initialize();
me->SetDisplayId(me->GetCreatureTemplate()->Modelid1);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
}
@@ -593,7 +601,7 @@ class npc_snowpacked_icicle : public CreatureScript
{
Initialize();
me->SetDisplayId(me->GetCreatureTemplate()->Modelid2);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED);
me->SetReactState(REACT_PASSIVE);
}
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp
index cd214a0114f..a50643c5deb 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp
@@ -178,7 +178,7 @@ class boss_ignis : public CreatureScript
{
summon->setFaction(16);
summon->SetReactState(REACT_AGGRESSIVE);
- summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_DISABLE_MOVE);
+ summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_IMMUNE_TO_PC);
}
summon->AI()->AttackStart(me->GetVictim());
@@ -375,7 +375,7 @@ class npc_scorch_ground : public CreatureScript
npc_scorch_groundAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE |UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL |UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED);
creature->SetDisplayId(16925); //model 2 in db cannot overwrite wdb fields
}
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp
index 470ad388fff..45d4fa58a03 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp
@@ -104,7 +104,7 @@ class boss_kologarn : public CreatureScript
left(false), right(false)
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
DoCast(SPELL_KOLOGARN_REDUCE_PARRY);
SetCombatMovement(false);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
index 8aa443cba3f..a965f8b39ff 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
@@ -644,10 +644,10 @@ class boss_mimiron : public CreatureScript
{
if (Creature* computer = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_COMPUTER)))
computer->AI()->DoAction(DO_DEACTIVATE_COMPUTER);
- me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION_FIREFIGHTER, GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, 0.f, 0.f, 0.9999619f, 0.008734641f, 604800);
+ me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION_FIREFIGHTER, GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, G3D::Quat(0.f, 0.f, 0.9999619f, 0.008734641f), 604800);
}
else
- me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION, GO_CACHE_OF_INNOVATION_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, 0.f, 0.f, 0.9999619f, 0.008734641f, 604800);
+ me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION, GO_CACHE_OF_INNOVATION_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, G3D::Quat(0.f, 0.f, 0.9999619f, 0.008734641f), 604800);
events.ScheduleEvent(EVENT_OUTTRO_3, 11000);
break;
case EVENT_OUTTRO_3:
@@ -972,7 +972,7 @@ class boss_vx_001 : public CreatureScript
events.ScheduleEvent(EVENT_FLAME_SUPPRESSANT_VX, 6000);
// Missing break intended.
case DO_START_VX001:
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); // Remove emotestate.
//me->SetUInt32Value(UNIT_FIELD_BYTES_1, 33554432); Blizzard handles hover animation like this it seems.
@@ -1145,7 +1145,7 @@ class boss_aerial_command_unit : public CreatureScript
events.ScheduleEvent(EVENT_SUMMON_FIRE_BOTS, 1000, 0, PHASE_AERIAL_COMMAND_UNIT);
// Missing break intended.
case DO_START_AERIAL:
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
events.SetPhase(PHASE_AERIAL_COMMAND_UNIT);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
index 363f5907048..a0ea2bd3d57 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
@@ -242,7 +242,7 @@ class boss_razorscale_controller : public CreatureScript
break;
case ACTION_PLACE_BROKEN_HARPOON:
for (uint8 n = 0; n < RAID_MODE(2, 4); n++)
- me->SummonGameObject(GO_RAZOR_BROKEN_HARPOON, PosHarpoon[n].GetPositionX(), PosHarpoon[n].GetPositionY(), PosHarpoon[n].GetPositionZ(), 2.286f, 0, 0, 0, 0, 180);
+ me->SummonGameObject(GO_RAZOR_BROKEN_HARPOON, PosHarpoon[n].GetPositionX(), PosHarpoon[n].GetPositionY(), PosHarpoon[n].GetPositionZ(), 2.286f, G3D::Quat(), 180);
break;
}
}
@@ -257,7 +257,7 @@ class boss_razorscale_controller : public CreatureScript
{
case EVENT_BUILD_HARPOON_1:
Talk(EMOTE_HARPOON);
- if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[0].GetPositionX(), PosHarpoon[0].GetPositionY(), PosHarpoon[0].GetPositionZ(), 4.790f, 0.0f, 0.0f, 0.0f, 0.0f, uint32(me->GetRespawnTime())))
+ if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[0].GetPositionX(), PosHarpoon[0].GetPositionY(), PosHarpoon[0].GetPositionZ(), 4.790f, G3D::Quat(), uint32(me->GetRespawnTime())))
{
if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) //only nearest broken harpoon
BrokenHarpoon->RemoveFromWorld();
@@ -267,7 +267,7 @@ class boss_razorscale_controller : public CreatureScript
return;
case EVENT_BUILD_HARPOON_2:
Talk(EMOTE_HARPOON);
- if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[1].GetPositionX(), PosHarpoon[1].GetPositionY(), PosHarpoon[1].GetPositionZ(), 4.659f, 0, 0, 0, 0, uint32(me->GetRespawnTime())))
+ if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[1].GetPositionX(), PosHarpoon[1].GetPositionY(), PosHarpoon[1].GetPositionZ(), 4.659f, G3D::Quat(), uint32(me->GetRespawnTime())))
{
if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f))
BrokenHarpoon->RemoveFromWorld();
@@ -276,7 +276,7 @@ class boss_razorscale_controller : public CreatureScript
return;
case EVENT_BUILD_HARPOON_3:
Talk(EMOTE_HARPOON);
- if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_3, PosHarpoon[2].GetPositionX(), PosHarpoon[2].GetPositionY(), PosHarpoon[2].GetPositionZ(), 5.382f, 0, 0, 0, 0, uint32(me->GetRespawnTime())))
+ if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_3, PosHarpoon[2].GetPositionX(), PosHarpoon[2].GetPositionY(), PosHarpoon[2].GetPositionZ(), 5.382f, G3D::Quat(), uint32(me->GetRespawnTime())))
{
if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f))
BrokenHarpoon->RemoveFromWorld();
@@ -286,7 +286,7 @@ class boss_razorscale_controller : public CreatureScript
return;
case EVENT_BUILD_HARPOON_4:
Talk(EMOTE_HARPOON);
- if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_4, PosHarpoon[3].GetPositionX(), PosHarpoon[3].GetPositionY(), PosHarpoon[3].GetPositionZ(), 4.266f, 0, 0, 0, 0, uint32(me->GetRespawnTime())))
+ if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_4, PosHarpoon[3].GetPositionX(), PosHarpoon[3].GetPositionY(), PosHarpoon[3].GetPositionZ(), 4.266f, G3D::Quat(), uint32(me->GetRespawnTime())))
{
if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f))
BrokenHarpoon->RemoveFromWorld();
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp
index 547dc681ac6..28ca7f4ef3b 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp
@@ -21,7 +21,7 @@
BossBoundaryData const boundaries =
{
- { DATA_KING_YMIRON, new RectangleBoundary(340.0f, 450.0f, -412.0f, -275.0f) }
+ { DATA_KING_YMIRON, new RectangleBoundary(340.0f, 443.0f, -412.0f, -275.0f) }
};
DoorData const doorData[] =
diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp
index b89c11147b6..df3fa266191 100644
--- a/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp
+++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp
@@ -115,6 +115,7 @@ class boss_archavon : public CreatureScript
DoCastVictim(SPELL_STOMP);
events.ScheduleEvent(EVENT_IMPALE, 3000);
events.ScheduleEvent(EVENT_STOMP, 45000);
+ Talk(EMOTE_LEAP, me->GetVictim());
break;
case EVENT_IMPALE:
DoCastVictim(SPELL_IMPALE);
diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp
index 06ebcbdc2fb..e7305e53808 100644
--- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp
+++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp
@@ -247,7 +247,7 @@ class npc_frozen_orb_stalker : public CreatureScript
npc_frozen_orb_stalkerAI(Creature* creature) : ScriptedAI(creature)
{
creature->SetVisible(false);
- creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL);
creature->SetReactState(REACT_PASSIVE);
instance = creature->GetInstanceScript();
diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp
index 698fd510411..d9315315c58 100644
--- a/src/server/scripts/Northrend/zone_borean_tundra.cpp
+++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp
@@ -2274,7 +2274,8 @@ public:
void DoAction(int32 /*iParam*/) override
{
me->StopMoving();
- me->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
+ me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
if (Player* player = ObjectAccessor::GetPlayer(*me, uiPlayerGUID))
me->SetFacingToObject(player);
uiEventTimer = 3000;
@@ -2303,7 +2304,6 @@ public:
switch (me->GetEntry())
{
case NPC_SALTY_JOHN_THORPE:
- me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
Talk(SAY_HIDDEN_CULTIST_1);
uiEventTimer = 5000;
uiEventPhase = 2;
@@ -2314,7 +2314,8 @@ public:
uiEventPhase = 2;
break;
case NPC_TOM_HEGGER:
- Talk(SAY_HIDDEN_CULTIST_3);
+ if (Player* player = ObjectAccessor::GetPlayer(*me, uiPlayerGUID))
+ Talk(SAY_HIDDEN_CULTIST_3, player);
uiEventTimer = 5000;
uiEventPhase = 2;
break;
diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp
index 4eafc1cd94e..adade245c2b 100644
--- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp
+++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp
@@ -1044,6 +1044,12 @@ public:
{
PrepareAuraScript(spell_z_check_AuraScript);
+ public:
+ spell_z_check_AuraScript()
+ {
+ _posZ = 0.0f;
+ }
+
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
_posZ = GetTarget()->GetPositionZ();
diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp
index f2edccd99b5..41785f76d9a 100644
--- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp
+++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp
@@ -253,7 +253,7 @@ public:
case 1:
Talk(SAY_WP_3);
me->CastSpell(5918.33f, 5372.91f, -98.770f, SPELL_EXPLODE_CRYSTAL, true);
- me->SummonGameObject(184743, 5918.33f, 5372.91f, -98.770f, 0, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds
+ me->SummonGameObject(184743, 5918.33f, 5372.91f, -98.770f, 0, G3D::Quat(), TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds
me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH);
break;
case 2:
@@ -264,7 +264,7 @@ public:
break;
case 8:
me->CastSpell(5887.37f, 5379.39f, -91.289f, SPELL_EXPLODE_CRYSTAL, true);
- me->SummonGameObject(184743, 5887.37f, 5379.39f, -91.289f, 0, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds
+ me->SummonGameObject(184743, 5887.37f, 5379.39f, -91.289f, 0, G3D::Quat(), TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds
me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH);
break;
case 9:
@@ -780,7 +780,7 @@ public:
bird->KillSelf();
crunchy->GetMotionMaster()->MovePoint(0, bird->GetPositionX(), bird->GetPositionY(),
- bird->GetMap()->GetWaterOrGroundLevel(bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ()));
+ bird->GetMap()->GetWaterOrGroundLevel(bird->GetPhaseMask(), bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ()));
/// @todo Make crunchy perform emote eat when he reaches the bird
break;
diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp
index e5263a8630a..490c72c5cba 100644
--- a/src/server/scripts/Northrend/zone_storm_peaks.cpp
+++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp
@@ -516,7 +516,7 @@ public:
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
voice->AI()->Talk(SAY_VOICE_1, player);
}
- if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_1, 7860.273f, -1383.622f, 1538.302f, -1.658062f, 0, 0, -0.737277f, 0.6755905f, 0))
+ if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_1, 7860.273f, -1383.622f, 1538.302f, -1.658062f, G3D::Quat(0.f, 0.f, -0.737277f, 0.6755905f), 0))
objectGUID[objectCounter++] = go->GetGUID();
events.ScheduleEvent(EVENT_SCRIPT_5, 6000);
break;
@@ -524,7 +524,7 @@ public:
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID))
voice->AI()->Talk(SAY_VOICE_2, player);
- if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_2, 7875.67f, -1387.266f, 1538.323f, -2.373644f, 0, 0, -0.9271832f, 0.3746083f, 0))
+ if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_2, 7875.67f, -1387.266f, 1538.323f, -2.373644f, G3D::Quat(0.f, 0.f, -0.9271832f, 0.3746083f), 0))
objectGUID[objectCounter++] = go->GetGUID();
events.ScheduleEvent(EVENT_SCRIPT_6, 6000);
break;
@@ -532,7 +532,7 @@ public:
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID))
voice->AI()->Talk(SAY_VOICE_3, player);
- if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_3, 7879.212f, -1401.175f, 1538.279f, 2.967041f, 0, 0, 0.9961939f, 0.08716504f, 0))
+ if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_3, 7879.212f, -1401.175f, 1538.279f, 2.967041f, G3D::Quat(0.f, 0.f, 0.9961939f, 0.08716504f), 0))
objectGUID[objectCounter++] = go->GetGUID();
events.ScheduleEvent(EVENT_SCRIPT_7, 6000);
break;
@@ -540,7 +540,7 @@ public:
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID))
voice->AI()->Talk(SAY_VOICE_4, player);
- if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_4, 7868.944f, -1411.18f, 1538.213f, 2.111848f, 0, 0, 0.8703556f, 0.4924237f, 0))
+ if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_4, 7868.944f, -1411.18f, 1538.213f, 2.111848f, G3D::Quat(0.f, 0.f, 0.8703556f, 0.4924237f), 0))
objectGUID[objectCounter++] = go->GetGUID();
events.ScheduleEvent(EVENT_SCRIPT_8, 6000);
break;
@@ -548,7 +548,7 @@ public:
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID))
voice->AI()->Talk(SAY_VOICE_5, player);
- if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_5, 7855.11f, -1406.839f, 1538.42f, 1.151916f, 0, 0, 0.5446386f, 0.8386708f, 0))
+ if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_5, 7855.11f, -1406.839f, 1538.42f, 1.151916f, G3D::Quat(0.f, 0.f, 0.5446386f, 0.8386708f), 0))
objectGUID[objectCounter] = go->GetGUID();
events.ScheduleEvent(EVENT_SCRIPT_9, 6000);
break;
diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp
index a0b9351fb75..05ed405cc6e 100644
--- a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp
+++ b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp
@@ -163,7 +163,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player* player, uint32 spellId)
GameObject* go = new GameObject;
Map* map = player->GetMap();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY))
+ if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), *player, G3D::Quat(), 255, GO_STATE_READY))
{
delete go;
return true;
@@ -192,7 +192,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player* player, uint32 spellId)
GameObject* go = new GameObject;
Map* map = player->GetMap();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY))
+ if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), *player, G3D::Quat(), 255, GO_STATE_READY))
{
delete go;
return true;
diff --git a/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp
index 9c16379c9f1..236f5bac403 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp
@@ -64,12 +64,11 @@ public:
return GetInstanceAI<boss_gurtogg_bloodboilAI>(creature);
}
- struct boss_gurtogg_bloodboilAI : public ScriptedAI
+ struct boss_gurtogg_bloodboilAI : public BossAI
{
- boss_gurtogg_bloodboilAI(Creature* creature) : ScriptedAI(creature)
+ boss_gurtogg_bloodboilAI(Creature* creature) : BossAI(creature, DATA_GURTOGG_BLOODBOIL)
{
Initialize();
- instance = creature->GetInstanceScript();
}
void Initialize()
@@ -91,8 +90,6 @@ public:
Phase1 = true;
}
- InstanceScript* instance;
-
ObjectGuid TargetGUID;
float TargetThreat;
@@ -112,8 +109,7 @@ public:
void Reset() override
{
- instance->SetBossState(DATA_GURTOGG_BLOODBOIL, NOT_STARTED);
-
+ _Reset();
Initialize();
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
@@ -122,9 +118,8 @@ public:
void EnterCombat(Unit* /*who*/) override
{
- DoZoneInCombat();
Talk(SAY_AGGRO);
- instance->SetBossState(DATA_GURTOGG_BLOODBOIL, IN_PROGRESS);
+ _EnterCombat();
}
void KilledUnit(Unit* /*victim*/) override
@@ -134,9 +129,8 @@ public:
void JustDied(Unit* /*killer*/) override
{
- instance->SetBossState(DATA_GURTOGG_BLOODBOIL, DONE);
-
Talk(SAY_DEATH);
+ _JustDied();
}
void RevertThreatOnTarget(ObjectGuid guid)
diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
index 14aeda04a7e..a63d984d849 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
@@ -473,12 +473,11 @@ class boss_illidan_stormrage : public CreatureScript
public:
boss_illidan_stormrage() : CreatureScript("boss_illidan_stormrage") { }
- struct boss_illidan_stormrageAI : public ScriptedAI
+ struct boss_illidan_stormrageAI : public BossAI
{
- boss_illidan_stormrageAI(Creature* creature) : ScriptedAI(creature), Summons(me)
+ boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE)
{
Initialize();
- instance = creature->GetInstanceScript();
DoCast(me, SPELL_DUAL_WIELD, true);
}
@@ -519,7 +518,7 @@ public:
EnterPhase(PHASE_FLIGHT_SEQUENCE);
}
}
- Summons.Despawn(summon);
+ summons.Despawn(summon);
}
void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) override
@@ -539,6 +538,7 @@ public:
void EnterCombat(Unit* /*who*/) override
{
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->setActive(true);
DoZoneInCombat();
}
@@ -561,10 +561,10 @@ public:
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE);
-
for (uint8 i = DATA_GO_ILLIDAN_DOOR_R; i < DATA_GO_ILLIDAN_DOOR_L + 1; ++i)
instance->HandleGameObject(instance->GetGuidData(i), true);
+
+ _JustDied();
}
void KilledUnit(Unit* victim) override
@@ -585,7 +585,7 @@ public:
void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override
{
- if (spell->Id == SPELL_GLAIVE_RETURNS) // Re-equip our warblades!
+ if (spell->Id == SPELL_GLAIVE_RETURNS) // Re-equip our warglaives!
{
if (!me->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID))
SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
@@ -673,7 +673,7 @@ public:
Timer[EVENT_TALK_SEQUENCE] = 100;
me->RemoveAllAuras();
me->InterruptNonMeleeSpells(false);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->GetMotionMaster()->Clear(false);
me->AttackStop();
break;
@@ -791,99 +791,99 @@ public:
{
switch (FlightCount)
{
- case 1: // lift off
- me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
- me->SetDisableGravity(true);
- me->StopMoving();
- Talk(SAY_ILLIDAN_TAKEOFF);
- Timer[EVENT_FLIGHT_SEQUENCE] = 3000;
- break;
- case 2: // move to center
- me->GetMotionMaster()->MovePoint(0, CENTER_X + 5, CENTER_Y, CENTER_Z); // +5, for SPELL_THROW_GLAIVE bug
- Timer[EVENT_FLIGHT_SEQUENCE] = 0;
- break;
- case 3: // throw one glaive
- {
- uint8 i=1;
- Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
- if (Glaive)
+ case 1: // lift off
+ me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
+ me->SetDisableGravity(true);
+ me->StopMoving();
+ Talk(SAY_ILLIDAN_TAKEOFF);
+ Timer[EVENT_FLIGHT_SEQUENCE] = 3000;
+ break;
+ case 2: // move to center
+ me->GetMotionMaster()->MovePoint(0, CENTER_X + 5, CENTER_Y, CENTER_Z); // +5, for SPELL_THROW_GLAIVE bug
+ Timer[EVENT_FLIGHT_SEQUENCE] = 0;
+ break;
+ case 3: // throw one glaive
{
- GlaiveGUID[i] = Glaive->GetGUID();
- Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- Glaive->SetDisplayId(MODEL_INVISIBLE);
- Glaive->setFaction(me->getFaction());
- DoCast(Glaive, SPELL_THROW_GLAIVE2);
+ uint8 i=1;
+ Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
+ if (Glaive)
+ {
+ GlaiveGUID[i] = Glaive->GetGUID();
+ Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ Glaive->SetDisplayId(MODEL_INVISIBLE);
+ Glaive->setFaction(me->getFaction());
+ DoCast(Glaive, SPELL_THROW_GLAIVE2);
+ }
}
- }
- Timer[EVENT_FLIGHT_SEQUENCE] = 700;
- break;
- case 4: // throw another
- SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
- {
- uint8 i=0;
- Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
- if (Glaive)
+ Timer[EVENT_FLIGHT_SEQUENCE] = 700;
+ break;
+ case 4: // throw another
+ SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
{
- GlaiveGUID[i] = Glaive->GetGUID();
- Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- Glaive->SetDisplayId(MODEL_INVISIBLE);
- Glaive->setFaction(me->getFaction());
- DoCast(Glaive, SPELL_THROW_GLAIVE, true);
+ uint8 i=0;
+ Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
+ if (Glaive)
+ {
+ GlaiveGUID[i] = Glaive->GetGUID();
+ Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ Glaive->SetDisplayId(MODEL_INVISIBLE);
+ Glaive->setFaction(me->getFaction());
+ DoCast(Glaive, SPELL_THROW_GLAIVE, true);
+ }
}
- }
- Timer[EVENT_FLIGHT_SEQUENCE] = 5000;
- break;
- case 5: // summon flames
- SummonFlamesOfAzzinoth();
- Timer[EVENT_FLIGHT_SEQUENCE] = 3000;
- break;
- case 6: // fly to hover point
- me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z);
- Timer[EVENT_FLIGHT_SEQUENCE] = 0;
- break;
- case 7: // return to center
- me->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z);
- Timer[EVENT_FLIGHT_SEQUENCE] = 0;
- break;
- case 8: // glaive return
- for (uint8 i = 0; i < 2; ++i)
- {
- if (GlaiveGUID[i])
+ Timer[EVENT_FLIGHT_SEQUENCE] = 5000;
+ break;
+ case 5: // summon flames
+ SummonFlamesOfAzzinoth();
+ Timer[EVENT_FLIGHT_SEQUENCE] = 3000;
+ break;
+ case 6: // fly to hover point
+ me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z);
+ Timer[EVENT_FLIGHT_SEQUENCE] = 0;
+ break;
+ case 7: // return to center
+ me->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z);
+ Timer[EVENT_FLIGHT_SEQUENCE] = 0;
+ break;
+ case 8: // glaive return
+ for (uint8 i = 0; i < 2; ++i)
{
- Unit* Glaive = ObjectAccessor::GetUnit(*me, GlaiveGUID[i]);
- if (Glaive)
+ if (GlaiveGUID[i])
{
- Glaive->CastSpell(me, SPELL_GLAIVE_RETURNS, false); // Make it look like the Glaive flies back up to us
- Glaive->SetDisplayId(MODEL_INVISIBLE); // disappear but not die for now
+ Unit* Glaive = ObjectAccessor::GetUnit(*me, GlaiveGUID[i]);
+ if (Glaive)
+ {
+ Glaive->CastSpell(me, SPELL_GLAIVE_RETURNS, false); // Make it look like the Glaive flies back up to us
+ Glaive->SetDisplayId(MODEL_INVISIBLE); // disappear but not die for now
+ }
}
}
- }
- Timer[EVENT_FLIGHT_SEQUENCE] = 2000;
- break;
- case 9: // land
- me->SetDisableGravity(false);
- me->StopMoving();
- me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
- for (uint8 i = 0; i < 2; ++i)
- {
- if (GlaiveGUID[i])
+ Timer[EVENT_FLIGHT_SEQUENCE] = 2000;
+ break;
+ case 9: // land
+ me->SetDisableGravity(false);
+ me->StopMoving();
+ me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
+ for (uint8 i = 0; i < 2; ++i)
{
- if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID[i]))
- glaive->DespawnOrUnsummon();
+ if (GlaiveGUID[i])
+ {
+ if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID[i]))
+ glaive->DespawnOrUnsummon();
- GlaiveGUID[i].Clear();
+ GlaiveGUID[i].Clear();
+ }
}
- }
- Timer[EVENT_FLIGHT_SEQUENCE] = 2000;
- break;
- case 10: // attack
- DoResetThreat();
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE);
- me->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
- EnterPhase(PHASE_NORMAL_2);
- break;
- default:
- break;
+ Timer[EVENT_FLIGHT_SEQUENCE] = 2000;
+ break;
+ case 10: // attack
+ DoResetThreat();
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE);
+ me->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
+ EnterPhase(PHASE_NORMAL_2);
+ break;
+ default:
+ break;
}
++FlightCount;
}
@@ -913,23 +913,23 @@ public:
switch (TransformCount)
{
- case 2:
- DoResetThreat();
- break;
- case 4:
- EnterPhase(PHASE_DEMON);
- break;
- case 7:
- DoResetThreat();
- break;
- case 9:
- if (MaievGUID)
- EnterPhase(PHASE_NORMAL_MAIEV); // Depending on whether we summoned Maiev, we switch to either phase 5 or 3
- else
- EnterPhase(PHASE_NORMAL_2);
- break;
- default:
- break;
+ case 2:
+ DoResetThreat();
+ break;
+ case 4:
+ EnterPhase(PHASE_DEMON);
+ break;
+ case 7:
+ DoResetThreat();
+ break;
+ case 9:
+ if (MaievGUID)
+ EnterPhase(PHASE_NORMAL_MAIEV); // Depending on whether we summoned Maiev, we switch to either phase 5 or 3
+ else
+ EnterPhase(PHASE_NORMAL_2);
+ break;
+ default:
+ break;
}
if (Phase == PHASE_TRANSFORM_SEQUENCE)
Timer[EVENT_TRANSFORM_SEQUENCE] = DemonTransformation[TransformCount].timer;
@@ -957,37 +957,37 @@ public:
switch (Phase)
{
- case PHASE_NORMAL:
- if (HealthBelowPct(65))
- EnterPhase(PHASE_FLIGHT_SEQUENCE);
- break;
+ case PHASE_NORMAL:
+ if (HealthBelowPct(65))
+ EnterPhase(PHASE_FLIGHT_SEQUENCE);
+ break;
- case PHASE_NORMAL_2:
- if (HealthBelowPct(30))
- EnterPhase(PHASE_TALK_SEQUENCE);
- break;
+ case PHASE_NORMAL_2:
+ if (HealthBelowPct(30))
+ EnterPhase(PHASE_TALK_SEQUENCE);
+ break;
- case PHASE_NORMAL_MAIEV:
- if (HealthBelowPct(1))
- EnterPhase(PHASE_TALK_SEQUENCE);
- break;
+ case PHASE_NORMAL_MAIEV:
+ if (HealthBelowPct(1))
+ EnterPhase(PHASE_TALK_SEQUENCE);
+ break;
- case PHASE_TALK_SEQUENCE:
- if (Event == EVENT_TALK_SEQUENCE)
- HandleTalkSequence();
- break;
+ case PHASE_TALK_SEQUENCE:
+ if (Event == EVENT_TALK_SEQUENCE)
+ HandleTalkSequence();
+ break;
- case PHASE_FLIGHT_SEQUENCE:
- if (Event == EVENT_FLIGHT_SEQUENCE)
- HandleFlightSequence();
- break;
+ case PHASE_FLIGHT_SEQUENCE:
+ if (Event == EVENT_FLIGHT_SEQUENCE)
+ HandleFlightSequence();
+ break;
- case PHASE_TRANSFORM_SEQUENCE:
- if (Event == EVENT_TRANSFORM_SEQUENCE)
- HandleTransformSequence();
- break;
- default:
- break;
+ case PHASE_TRANSFORM_SEQUENCE:
+ if (Event == EVENT_TRANSFORM_SEQUENCE)
+ HandleTransformSequence();
+ break;
+ default:
+ break;
}
if (me->IsNonMeleeSpellCast(false))
@@ -998,63 +998,63 @@ public:
switch (Event)
{
// PHASE_NORMAL
- case EVENT_BERSERK:
- Talk(SAY_ILLIDAN_ENRAGE);
- DoCast(me, SPELL_BERSERK, true);
- Timer[EVENT_BERSERK] = 5000; // The buff actually lasts forever.
- break;
-
- case EVENT_TAUNT:
- Talk(SAY_ILLIDAN_TAUNT);
- Timer[EVENT_TAUNT] = urand(25000, 35000);
- break;
-
- case EVENT_SHEAR:
- // no longer exists in 3.0f.2
- // DoCastVictim(SPELL_SHEAR);
- Timer[EVENT_SHEAR] = 25000 + (rand32() % 16 * 1000);
- break;
-
- case EVENT_FLAME_CRASH:
- DoCastVictim(SPELL_FLAME_CRASH);
- Timer[EVENT_FLAME_CRASH] = urand(30000, 40000);
- break;
-
- case EVENT_PARASITIC_SHADOWFIEND:
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200, true))
- DoCast(target, SPELL_PARASITIC_SHADOWFIEND, true);
- Timer[EVENT_PARASITIC_SHADOWFIEND] = urand(35000, 45000);
- }
- break;
-
- case EVENT_PARASITE_CHECK:
- Timer[EVENT_PARASITE_CHECK] = 0;
- break;
-
- case EVENT_DRAW_SOUL:
- DoCastVictim(SPELL_DRAW_SOUL);
- Timer[EVENT_DRAW_SOUL] = urand(50000, 60000);
- break;
-
- // PHASE_NORMAL_2
- case EVENT_AGONIZING_FLAMES:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_AGONIZING_FLAMES);
- Timer[EVENT_AGONIZING_FLAMES] = 0;
- break;
-
- case EVENT_TRANSFORM_NORMAL:
- EnterPhase(PHASE_TRANSFORM_SEQUENCE);
- break;
-
- // PHASE_NORMAL_MAIEV
- case EVENT_ENRAGE:
- DoCast(me, SPELL_ENRAGE);
- Timer[EVENT_ENRAGE] = 0;
- break;
-
- default:
- break;
+ case EVENT_BERSERK:
+ Talk(SAY_ILLIDAN_ENRAGE);
+ DoCast(me, SPELL_BERSERK, true);
+ Timer[EVENT_BERSERK] = 5000; // The buff actually lasts forever.
+ break;
+
+ case EVENT_TAUNT:
+ Talk(SAY_ILLIDAN_TAUNT);
+ Timer[EVENT_TAUNT] = urand(25000, 35000);
+ break;
+
+ case EVENT_SHEAR:
+ // no longer exists in 3.0f.2
+ // DoCastVictim(SPELL_SHEAR);
+ Timer[EVENT_SHEAR] = 25000 + (rand32() % 16 * 1000);
+ break;
+
+ case EVENT_FLAME_CRASH:
+ DoCastVictim(SPELL_FLAME_CRASH);
+ Timer[EVENT_FLAME_CRASH] = urand(30000, 40000);
+ break;
+
+ case EVENT_PARASITIC_SHADOWFIEND:
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200, true))
+ DoCast(target, SPELL_PARASITIC_SHADOWFIEND, true);
+ Timer[EVENT_PARASITIC_SHADOWFIEND] = urand(35000, 45000);
+ }
+ break;
+
+ case EVENT_PARASITE_CHECK:
+ Timer[EVENT_PARASITE_CHECK] = 0;
+ break;
+
+ case EVENT_DRAW_SOUL:
+ DoCastVictim(SPELL_DRAW_SOUL);
+ Timer[EVENT_DRAW_SOUL] = urand(50000, 60000);
+ break;
+
+ // PHASE_NORMAL_2
+ case EVENT_AGONIZING_FLAMES:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_AGONIZING_FLAMES);
+ Timer[EVENT_AGONIZING_FLAMES] = 0;
+ break;
+
+ case EVENT_TRANSFORM_NORMAL:
+ EnterPhase(PHASE_TRANSFORM_SEQUENCE);
+ break;
+
+ // PHASE_NORMAL_MAIEV
+ case EVENT_ENRAGE:
+ DoCast(me, SPELL_ENRAGE);
+ Timer[EVENT_ENRAGE] = 0;
+ break;
+
+ default:
+ break;
}
DoMeleeAttackIfReady();
}
@@ -1063,32 +1063,32 @@ public:
{
switch (Event)
{
- case EVENT_FIREBALL:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
- Timer[EVENT_FIREBALL] = 3000;
- break;
-
- case EVENT_DARK_BARRAGE:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE);
- Timer[EVENT_DARK_BARRAGE] = 0;
- break;
-
- case EVENT_EYE_BLAST:
- CastEyeBlast();
- Timer[EVENT_EYE_BLAST] = 0;
- break;
-
- case EVENT_MOVE_POINT:
- Phase = PHASE_FLIGHT_SEQUENCE;
- Timer[EVENT_FLIGHT_SEQUENCE] = 0; // do not start Event when changing hover point
- HoverPoint += (rand32() % 3 + 1);
- if (HoverPoint > 3)
- HoverPoint -= 4;
- me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z);
- break;
-
- default:
- break;
+ case EVENT_FIREBALL:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
+ Timer[EVENT_FIREBALL] = 3000;
+ break;
+
+ case EVENT_DARK_BARRAGE:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE);
+ Timer[EVENT_DARK_BARRAGE] = 0;
+ break;
+
+ case EVENT_EYE_BLAST:
+ CastEyeBlast();
+ Timer[EVENT_EYE_BLAST] = 0;
+ break;
+
+ case EVENT_MOVE_POINT:
+ Phase = PHASE_FLIGHT_SEQUENCE;
+ Timer[EVENT_FLIGHT_SEQUENCE] = 0; // do not start Event when changing hover point
+ HoverPoint += (rand32() % 3 + 1);
+ if (HoverPoint > 3)
+ HoverPoint -= 4;
+ me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z);
+ break;
+
+ default:
+ break;
}
}
@@ -1096,29 +1096,29 @@ public:
{
switch (Event)
{
- case EVENT_SHADOW_BLAST:
- me->GetMotionMaster()->Clear(false);
- if (me->GetVictim() && (!me->IsWithinDistInMap(me->GetVictim(), 50) || !me->IsWithinLOSInMap(me->GetVictim())))
- me->GetMotionMaster()->MoveChase(me->GetVictim(), 30);
- else
- me->GetMotionMaster()->MoveIdle();
- DoCastVictim(SPELL_SHADOW_BLAST);
- Timer[EVENT_SHADOW_BLAST] = 4000;
- break;
- case EVENT_SHADOWDEMON:
- DoCast(me, SPELL_SUMMON_SHADOWDEMON);
- Timer[EVENT_SHADOWDEMON] = 0;
- Timer[EVENT_FLAME_BURST] += 10000;
- break;
- case EVENT_FLAME_BURST:
- DoCast(me, SPELL_FLAME_BURST);
- Timer[EVENT_FLAME_BURST] = 15000;
- break;
- case EVENT_TRANSFORM_DEMON:
- EnterPhase(PHASE_TRANSFORM_SEQUENCE);
- break;
- default:
- break;
+ case EVENT_SHADOW_BLAST:
+ me->GetMotionMaster()->Clear(false);
+ if (me->GetVictim() && (!me->IsWithinDistInMap(me->GetVictim(), 50) || !me->IsWithinLOSInMap(me->GetVictim())))
+ me->GetMotionMaster()->MoveChase(me->GetVictim(), 30);
+ else
+ me->GetMotionMaster()->MoveIdle();
+ DoCastVictim(SPELL_SHADOW_BLAST);
+ Timer[EVENT_SHADOW_BLAST] = 4000;
+ break;
+ case EVENT_SHADOWDEMON:
+ DoCast(me, SPELL_SUMMON_SHADOWDEMON);
+ Timer[EVENT_SHADOWDEMON] = 0;
+ Timer[EVENT_FLAME_BURST] += 10000;
+ break;
+ case EVENT_FLAME_BURST:
+ DoCast(me, SPELL_FLAME_BURST);
+ Timer[EVENT_FLAME_BURST] = 15000;
+ break;
+ case EVENT_TRANSFORM_DEMON:
+ EnterPhase(PHASE_TRANSFORM_SEQUENCE);
+ break;
+ default:
+ break;
}
}
}
@@ -1128,7 +1128,6 @@ public:
uint32 Timer[EVENT_ENRAGE + 1];
PhaseIllidan Phase;
private:
- InstanceScript* instance;
EventIllidan Event;
uint32 TalkCount;
uint32 TransformCount;
@@ -1137,7 +1136,6 @@ public:
ObjectGuid MaievGUID;
ObjectGuid FlameGUID[2];
ObjectGuid GlaiveGUID[2];
- SummonList Summons;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1148,7 +1146,7 @@ public:
/********************************** End of Illidan AI* *****************************************/
-/******* Functions and vars for Akama's AI* *****/
+/******* Functions and vars for Maiev's AI* *****/
class boss_maiev_shadowsong : public CreatureScript
{
public:
@@ -1377,6 +1375,7 @@ public:
}
};
+/******* Functions and vars for Akama's AI* *****/
class npc_akama_illidan : public CreatureScript
{
public:
@@ -1434,7 +1433,7 @@ public:
KillAllElites();
- me->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has strange values..
+ me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); // Database sometimes has strange values..
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
me->setActive(false);
me->SetVisible(false);
@@ -1811,7 +1810,7 @@ public:
void boss_illidan_stormrage::boss_illidan_stormrageAI::Reset()
{
- instance->SetBossState(DATA_ILLIDAN_STORMRAGE, NOT_STARTED);
+ _Reset();
if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID))
{
@@ -1830,12 +1829,11 @@ void boss_illidan_stormrage::boss_illidan_stormrageAI::Reset()
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
me->SetDisableGravity(false);
me->setActive(false);
- Summons.DespawnAll();
}
void boss_illidan_stormrage::boss_illidan_stormrageAI::JustSummoned(Creature* summon)
{
- Summons.Summon(summon);
+ summons.Summon(summon);
switch (summon->GetEntry())
{
case PARASITIC_SHADOWFIEND:
@@ -1928,7 +1926,7 @@ void boss_illidan_stormrage::boss_illidan_stormrageAI::HandleTalkSequence()
break;
case 15:
DoCast(me, SPELL_DEATH); // Animate his kneeling + stun him
- Summons.DespawnAll();
+ summons.DespawnAll();
break;
case 17:
if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID))
diff --git a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp
index aaf72512ae9..9412067f77e 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp
@@ -149,7 +149,7 @@ public:
void TeleportPlayers()
{
- uint32 random = urand(0, 7);
+ uint32 random = urand(0, 6);
float X = TeleportPoint[random].x;
float Y = TeleportPoint[random].y;
float Z = TeleportPoint[random].z;
@@ -210,7 +210,7 @@ public:
break;
case EVENT_PRISMATIC_SHIELD:
// Random Prismatic Shield every 15 seconds.
- DoCast(me, PrismaticAuras[urand(0, 6)]);
+ DoCast(me, PrismaticAuras[urand(0, 5)]);
events.ScheduleEvent(EVENT_PRISMATIC_SHIELD, 15000);
break;
case EVENT_FATAL_ATTRACTION:
diff --git a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp
index afc7d3d0fd7..d89437c2816 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp
@@ -135,12 +135,11 @@ public:
return GetInstanceAI<boss_reliquary_of_soulsAI>(creature);
}
- struct boss_reliquary_of_soulsAI : public ScriptedAI
+ struct boss_reliquary_of_soulsAI : public BossAI
{
- boss_reliquary_of_soulsAI(Creature* creature) : ScriptedAI(creature)
+ boss_reliquary_of_soulsAI(Creature* creature) : BossAI(creature, DATA_RELIQUARY_OF_SOULS)
{
Initialize();
- instance = creature->GetInstanceScript();
Counter = 0;
Timer = 0;
SoulCount = 0;
@@ -152,8 +151,6 @@ public:
Phase = 0;
}
- InstanceScript* instance;
-
ObjectGuid EssenceGUID;
uint32 Phase;
@@ -165,7 +162,7 @@ public:
void Reset() override
{
- instance->SetBossState(DATA_RELIQUARY_OF_SOULS, NOT_STARTED);
+ _Reset();
if (EssenceGUID)
{
@@ -202,8 +199,7 @@ public:
void EnterCombat(Unit* who) override
{
me->AddThreat(who, 10000.0f);
- DoZoneInCombat();
- instance->SetBossState(DATA_RELIQUARY_OF_SOULS, IN_PROGRESS);
+ _EnterCombat();
Phase = 1;
Counter = 0;
@@ -246,11 +242,6 @@ public:
}
}
- void JustDied(Unit* /*killer*/) override
- {
- instance->SetBossState(DATA_RELIQUARY_OF_SOULS, DONE);
- }
-
void UpdateAI(uint32 diff) override
{
if (!Phase)
diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
index 1a9074c2464..c9e08176ad6 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
@@ -15,45 +15,45 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
-Name: Boss_Shade_of_Akama
-%Complete: 80
-Comment: WIP A few more adds to script, ending script, and bugs.
-Category: Black Temple
-*/
-
#include "ObjectMgr.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "PassiveAI.h"
#include "ScriptedGossip.h"
#include "GridNotifiers.h"
#include "black_temple.h"
+#include "SpellScript.h"
+#include "SpellAuraEffects.h"
enum Says
{
- // Akama Ending cinematic text
+ // Akama
SAY_BROKEN_FREE_0 = 0,
SAY_BROKEN_FREE_1 = 1,
- SAY_BROKEN_FREE_2 = 2
+ SAY_BROKEN_FREE_2 = 2,
+ SAY_LOW_HEALTH = 3,
+ SAY_DEAD = 4,
+ // Ashtongue Broken
+ SAY_BROKEN_SPECIAL = 0,
+ SAY_BROKEN_HAIL = 1
};
enum Spells
{
// Akama
- SPELL_STEALTH = 34189, // On Spawn
- SPELL_AKAMA_SOUL_CHANNEL = 40447, // Cast on self hits Shade
- SPELL_FIXATE = 40607, // Cast on self hits Shade
- SPELL_CHAIN_LIGHTNING = 39945, // Combat
- SPELL_DESTRUCTIVE_POISON = 40874, // Combat
+ SPELL_STEALTH = 34189,
+ SPELL_AKAMA_SOUL_CHANNEL = 40447,
+ SPELL_FIXATE = 40607,
+ SPELL_CHAIN_LIGHTNING = 39945,
+ SPELL_DESTRUCTIVE_POISON = 40874,
+ SPELL_AKAMA_SOUL_EXPEL = 40902,
// Shade
- SPELL_THREAT = 41602, // self cast hits Akama
- SPELL_SHADE_OF_AKAMA_TRIGGER = 40955, // Cast on death
- SPELL_AKAMA_SOUL_EXPEL_CHANNEL = 40927, // must hit shade
- SPELL_AKAMA_SOUL_EXPEL = 40902, // the one he cast
+ SPELL_THREAT = 41602,
+ SPELL_SHADE_OF_AKAMA_TRIGGER = 40955,
+ SPELL_AKAMA_SOUL_EXPEL_CHANNEL = 40927,
// Ashtongue Channeler
SPELL_SHADE_SOUL_CHANNEL = 40401,
SPELL_SHADE_SOUL_CHANNEL_2 = 40520,
- SPELL_SHADOWFORM = 40973, // Cast on Shade
// Creature Spawner
SPELL_ASHTONGUE_WAVE_B = 42035,
SPELL_SUMMON_ASHTONGUE_SORCERER = 40476,
@@ -78,11 +78,6 @@ enum Spells
enum Creatures
{
NPC_ASHTONGUE_CHANNELER = 23421,
- NPC_ASHTONGUE_SORCERER = 23215,
- NPC_ASHTONGUE_DEFENDER = 23216,
- NPC_ASHTONGUE_ELEMENTALIST = 23523,
- NPC_ASHTONGUE_ROGUE = 23318,
- NPC_ASHTONGUE_SPIRITBINDER = 23524,
NPC_ASHTONGUE_BROKEN = 23319,
NPC_CREATURE_SPAWNER_AKAMA = 23210
};
@@ -95,60 +90,114 @@ enum Factions
enum Actions
{
- ACTION_CHANNELER_DIED = 1,
- ACTION_START_SPAWNING = 2,
- ACTION_STOP_SPAWNING = 3,
- ACTION_DESPAWN_ALL_SPAWNS = 4,
+ ACTION_START_SPAWNING = 0,
+ ACTION_STOP_SPAWNING = 1,
+ ACTION_DESPAWN_ALL_SPAWNS = 2,
+ ACTION_SHADE_OF_AKAMA_DEAD = 3,
+ ACTION_BROKEN_SPECIAL = 4,
+ ACTION_BROKEN_EMOTE = 5,
+ ACTION_BROKEN_HAIL = 6
};
enum Events
{
// Akama
- EVENT_SHADE_START = 1,
- EVENT_SHADE_CHANNEL = 2,
- EVENT_FIXATE = 3,
- EVENT_CHAIN_LIGHTNING = 4,
- EVENT_DESTRUCTIVE_POISON = 5,
- // Shade
- EVENT_RESET_ENCOUNTER = 6,
- EVENT_FIND_CHANNELERS_SPAWNERS = 7,
- EVENT_SET_CHANNELERS_SPAWNERS = 8,
- EVENT_START_ATTACK_AKAMA = 9,
- EVENT_ADD_THREAT = 10,
+ EVENT_SHADE_START = 1,
+ EVENT_SHADE_CHANNEL = 2,
+ EVENT_FIXATE = 3,
+ EVENT_CHAIN_LIGHTNING = 4,
+ EVENT_DESTRUCTIVE_POISON = 5,
+ EVENT_START_BROKEN_FREE = 6,
+ EVENT_START_SOUL_EXPEL = 7,
+ EVENT_EVADE_CHECK = 8,
+ EVENT_BROKEN_FREE_1 = 9,
+ EVENT_BROKEN_FREE_2 = 10,
+ EVENT_BROKEN_FREE_3 = 11,
+ EVENT_BROKEN_FREE_4 = 12,
+ // Shade of Akama
+ EVENT_INITIALIZE_SPAWNERS = 13,
+ EVENT_START_CHANNELERS_AND_SPAWNERS = 14,
+ EVENT_ADD_THREAT = 15,
// Creature spawner
- EVENT_SPAWN_WAVE_B = 11,
- EVENT_SUMMON_ASHTONGUE_SORCERER = 12,
- EVENT_SUMMON_ASHTONGUE_DEFENDER = 13,
- // Channeler
- EVENT_CHANNEL = 14,
- // Ashtongue Sorcerer
- EVENT_SORCERER_CHANNEL = 15,
+ EVENT_SPAWN_WAVE_B = 16,
+ EVENT_SUMMON_ASHTONGUE_SORCERER = 17,
+ EVENT_SUMMON_ASHTONGUE_DEFENDER = 18,
// Ashtongue Defender
- EVENT_DEBILITATING_STRIKE = 16,
- EVENT_HEROIC_STRIKE = 17,
- EVENT_SHIELD_BASH = 18,
- EVENT_WINDFURY = 29,
+ EVENT_DEBILITATING_STRIKE = 19,
+ EVENT_HEROIC_STRIKE = 20,
+ EVENT_SHIELD_BASH = 21,
+ EVENT_WINDFURY = 22,
// Ashtongue Rogue
- EVENT_DEBILITATING_POISON = 20,
- EVENT_EVISCERATE = 21,
+ EVENT_DEBILITATING_POISON = 23,
+ EVENT_EVISCERATE = 24,
// Ashtongue Elementalist
- EVENT_RAIN_OF_FIRE = 22,
- EVENT_LIGHTNING_BOLT = 23,
+ EVENT_RAIN_OF_FIRE = 25,
+ EVENT_LIGHTNING_BOLT = 26,
// Ashtongue Spiritbinder
- EVENT_SPIRIT_HEAL = 24
+ EVENT_SPIRIT_HEAL = 27,
+ EVENT_SPIRIT_MEND_RESET = 28,
+ EVENT_CHAIN_HEAL_RESET = 29
};
-G3D::Vector3 const ShadeWP = { 512.4877f, 400.7993f, 112.7837f };
+enum Misc
+{
+ AKAMA_CHANNEL_WAYPOINT = 0,
+ AKAMA_INTRO_WAYPOINT = 1,
+
+ SUMMON_GROUP_RESET = 1
+};
-G3D::Vector3 const AkamaWP[] =
+Position const AkamaWP[2] =
{
{ 517.4877f, 400.7993f, 112.7837f },
{ 468.4435f, 401.1062f, 118.5379f }
};
-// ########################################################
-// Shade of Akama
-// ########################################################
+Position const BrokenPos[18] =
+{
+ { 495.5628f, 462.7089f, 112.8169f, 4.1808090f },
+ { 498.3421f, 463.8384f, 112.8673f, 4.5634810f },
+ { 501.6708f, 463.8806f, 112.8673f, 3.7157850f },
+ { 532.4264f, 448.4718f, 112.8563f, 3.9813020f },
+ { 532.9113f, 451.6227f, 112.8671f, 4.6479530f },
+ { 532.8243f, 453.9475f, 112.8671f, 4.7032810f },
+ { 521.5317f, 402.3790f, 112.8671f, 3.1138120f },
+ { 521.9184f, 404.6848f, 112.8671f, 4.0787760f },
+ { 522.4290f, 406.5160f, 112.8671f, 3.3869470f },
+ { 521.0833f, 393.1852f, 112.8611f, 3.0750830f },
+ { 521.9014f, 395.6381f, 112.8671f, 4.0157140f },
+ { 522.2610f, 397.7423f, 112.8671f, 3.4417790f },
+ { 532.4565f, 345.3987f, 112.8585f, 1.7232640f },
+ { 532.5565f, 346.8792f, 112.8671f, 1.8325960f },
+ { 532.5491f, 348.6840f, 112.8671f, 0.2054047f },
+ { 501.4669f, 338.5967f, 112.8504f, 1.7038430f },
+ { 499.0937f, 337.9894f, 112.8673f, 1.8586250f },
+ { 496.8722f, 338.0152f, 112.8673f, 0.5428222f }
+};
+
+Position const BrokenWP[18] =
+{
+ { 479.1884f, 434.8635f, 112.7838f },
+ { 479.7349f, 435.9843f, 112.7838f },
+ { 480.5328f, 436.8310f, 112.7838f },
+ { 493.1714f, 420.1136f, 112.7838f },
+ { 494.7830f, 417.4830f, 112.7838f },
+ { 492.9280f, 423.1891f, 112.7838f },
+ { 491.8618f, 403.2035f, 112.7838f },
+ { 491.7784f, 400.2046f, 112.7838f },
+ { 491.9451f, 406.2023f, 112.7838f },
+ { 488.3535f, 395.3652f, 112.7838f },
+ { 488.8324f, 392.3267f, 112.7838f },
+ { 489.2300f, 398.3135f, 112.7838f },
+ { 491.9286f, 383.0433f, 112.7838f },
+ { 491.1526f, 380.0966f, 112.7839f },
+ { 493.6747f, 385.5407f, 112.7838f },
+ { 476.2499f, 369.0865f, 112.7839f },
+ { 473.7637f, 367.8766f, 112.7839f },
+ { 478.8986f, 370.1895f, 112.7839f }
+};
+
+static float const MIDDLE_OF_ROOM = 400.0f;
class boss_shade_of_akama : public CreatureScript
{
@@ -160,233 +209,144 @@ public:
boss_shade_of_akamaAI(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA)
{
Initialize();
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
void Initialize()
{
- combatStarted = false;
- akamaReached = false;
- HasKilledAkama = false;
- HasKilledAkamaAndReseting = false;
+ _spawners.clear();
+ _isInPhaseOne = true;
}
void Reset() override
{
- _Reset();
- if (!HasKilledAkamaAndReseting)
- {
- for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
- if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr))
- Channeler->DespawnOrUnsummon();
-
- for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr)
- if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr))
- Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS);
-
- events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 3000);
- events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 5000);
- }
-
+ Initialize();
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
-
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN);
me->SetWalk(true);
- Initialize();
+ events.ScheduleEvent(EVENT_INITIALIZE_SPAWNERS, Seconds(1));
+ me->SummonCreatureGroup(SUMMON_GROUP_RESET);
}
- void EnterCombat(Unit* /*who*/) override { }
-
- void AttackStart(Unit* who) override
+ void EnterEvadeMode(EvadeReason /*why*/) override
{
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
- {
- if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- if (Akama->IsAlive())
- ScriptedAI::AttackStart(Akama);
- }
- else
- ScriptedAI::AttackStart(who);
- }
+ _Reset();
- void DoAction(int32 actionId) override
- {
- if (actionId == ACTION_CHANNELER_DIED)
- me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2);
+ for (ObjectGuid const& spawnerGuid : _spawners)
+ if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid))
+ spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS);
- UpdateSpeed();
+ _DespawnAtEvade();
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_AKAMA_SOUL_CHANNEL)
{
- combatStarted = true;
- events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 500);
- events.ScheduleEvent(EVENT_SET_CHANNELERS_SPAWNERS, 1000);
+ events.ScheduleEvent(EVENT_START_CHANNELERS_AND_SPAWNERS, Seconds(1));
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
- if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- me->AddThreat(Akama, 10000000.0f);
+ events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10));
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
}
- else if (spell->Id == SPELL_SHADE_SOUL_CHANNEL_2)
- UpdateSpeed();
+
+ if (spell->Id == SPELL_AKAMA_SOUL_EXPEL)
+ DoCastSelf(SPELL_AKAMA_SOUL_EXPEL_CHANNEL);
}
- void UpdateSpeed()
+ void MovementInform(uint32 motionType, uint32 /*pointId*/) override
{
- float moveSpeed = 0.2f;
-
- if (me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2) <= 3)
+ if (_isInPhaseOne && motionType == CHASE_MOTION_TYPE)
{
- moveSpeed = (2.0f - (0.6f * me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2)));
- me->SetSpeedRate(MOVE_WALK, moveSpeed / 2.5f);
- me->SetSpeedRate(MOVE_RUN, (moveSpeed * 2) / 7);
- me->ClearUnitState(UNIT_STATE_ROOT);
+ _isInPhaseOne = false;
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetWalk(false);
+ events.ScheduleEvent(EVENT_ADD_THREAT, Milliseconds(100));
+
+ for (ObjectGuid const& spawnerGuid : _spawners)
+ if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid))
+ spawner->AI()->DoAction(ACTION_STOP_SPAWNING);
}
- else
- me->AddUnitState(UNIT_STATE_ROOT);
}
- void UpdateAI(uint32 diff) override
+ void JustDied(Unit* /*killer*/) override
{
- if (HasKilledAkamaAndReseting)
- return;
+ DoCastSelf(SPELL_SHADE_OF_AKAMA_TRIGGER);
+
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
+ akama->AI()->DoAction(ACTION_SHADE_OF_AKAMA_DEAD);
+ for (ObjectGuid const& spawnerGuid : _spawners)
+ if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid))
+ spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS);
+
+ events.Reset();
+ summons.DespawnEntry(NPC_ASHTONGUE_CHANNELER);
+ instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE);
+ }
+
+ void EnterEvadeModeIfNeeded()
+ {
+ Map::PlayerList const &players = me->GetMap()->GetPlayers();
+ for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
+ if (Player* player = i->GetSource())
+ if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player))
+ return;
+
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
events.Update(diff);
- if (!combatStarted)
- {
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_RESET_ENCOUNTER:
- if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- if (!Akama->IsAlive())
- Akama->Respawn();
- break;
- case EVENT_FIND_CHANNELERS_SPAWNERS:
- {
- std::list<Creature*> ChannelerList;
- me->GetCreatureListWithEntryInGrid(ChannelerList, NPC_ASHTONGUE_CHANNELER, 15.0f);
-
- if (!ChannelerList.empty())
- for (std::list<Creature*>::const_iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr)
- {
- Channelers.push_back((*itr)->GetGUID());
- if ((*itr)->isDead())
- (*itr)->Respawn();
- }
-
- std::list<Creature*> SpawnerList;
- me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA, 90.0f);
-
- if (!SpawnerList.empty())
- for (std::list<Creature*>::const_iterator itr = SpawnerList.begin(); itr != SpawnerList.end(); ++itr)
- Spawners.push_back((*itr)->GetGUID());
-
- me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN);
- break;
- }
- default:
- break;
- }
- }
- }
- else
+ if (!UpdateVictim())
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- while (uint32 eventId = events.ExecuteEvent())
+ switch (eventId)
{
- switch (eventId)
+ case EVENT_INITIALIZE_SPAWNERS:
{
- case EVENT_SET_CHANNELERS_SPAWNERS:
- {
- for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
- if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr))
- Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
-
- for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr)
- if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr))
- Spawner->AI()->DoAction(ACTION_START_SPAWNING);
- break;
- }
- case EVENT_START_ATTACK_AKAMA:
- me->GetMotionMaster()->MovePoint(0, ShadeWP.x, ShadeWP.y, ShadeWP.z, false);
- events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 1000);
- break;
- case EVENT_ADD_THREAT:
- DoCast(SPELL_THREAT);
- events.ScheduleEvent(EVENT_ADD_THREAT, 3500);
- break;
- default:
- break;
- }
- }
+ std::list<Creature*> SpawnerList;
+ me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA);
+ for (Creature* spawner : SpawnerList)
+ _spawners.push_back(spawner->GetGUID());
- if (HasKilledAkama)
- {
- if (!HasKilledAkamaAndReseting)
- {
- HasKilledAkamaAndReseting = true;
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- instance->SetBossState(DATA_SHADE_OF_AKAMA, NOT_STARTED);
- me->RemoveAllAurasExceptType(SPELL_AURA_DUMMY);
- me->DeleteThreatList();
- me->CombatStop();
- me->GetMotionMaster()->MoveTargetedHome();
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
- combatStarted = false;
-
- if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- Akama->DespawnOrUnsummon();
-
- for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
- if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr))
- Channeler->DespawnOrUnsummon();
-
- for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr)
- if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr))
- Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS);
-
- events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 10000);
- events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 20000);
+ break;
}
- }
-
- if (!akamaReached)
- {
- if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
+ case EVENT_START_CHANNELERS_AND_SPAWNERS:
{
- if (me->IsWithinDist(Akama, 2.0f, false))
- {
- akamaReached = true;
- me->GetMotionMaster()->Clear(true);
- me->GetMotionMaster()->MoveIdle();
- me->SetWalk(false);
+ for (ObjectGuid const& summonGuid : summons)
+ if (Creature* channeler = ObjectAccessor::GetCreature(*me, summonGuid))
+ channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ for (ObjectGuid const& spawnerGuid : _spawners)
+ if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid))
+ spawner->AI()->DoAction(ACTION_START_SPAWNING);
- events.CancelEvent(EVENT_START_ATTACK_AKAMA);
- events.ScheduleEvent(EVENT_ADD_THREAT, 100);
-
- for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr)
- if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr))
- Spawner->AI()->DoAction(ACTION_STOP_SPAWNING);
- }
+ break;
}
+ case EVENT_ADD_THREAT:
+ DoCast(SPELL_THREAT);
+ events.Repeat(Seconds(3) + Milliseconds(500));
+ break;
+ case EVENT_EVADE_CHECK:
+ EnterEvadeModeIfNeeded();
+ events.Repeat(Seconds(10));
+ break;
+ default:
+ break;
}
- else
- DoMeleeAttackIfReady();
}
+
+ DoMeleeAttackIfReady();
}
- public:
- bool HasKilledAkama;
private:
- GuidList Channelers;
- GuidList Spawners;
- bool akamaReached;
- bool combatStarted;
- bool HasKilledAkamaAndReseting;
+ GuidVector _spawners;
+ bool _isInPhaseOne;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -395,10 +355,6 @@ public:
}
};
-// ########################################################
-// Akama
-// ########################################################
-
class npc_akama_shade : public CreatureScript
{
public:
@@ -406,117 +362,188 @@ public:
struct npc_akamaAI : public ScriptedAI
{
- npc_akamaAI(Creature* creature) : ScriptedAI(creature)
+ npc_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me)
{
Initialize();
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Initialize()
{
- StartChannel = false;
- StartCombat = false;
- HasYelledOnce = false;
- ShadeHasDied = false;
+ _isInCombat = false;
+ _hasYelledOnce = false;
+ _chosen.Clear();
+ _summons.DespawnAll();
+ _events.Reset();
}
void Reset() override
{
- me->setFaction(FACTION_FRIENDLY);
- DoCast(me, SPELL_STEALTH);
Initialize();
+ me->setFaction(FACTION_FRIENDLY);
+ DoCastSelf(SPELL_STEALTH);
- if (instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE)
+ if (_instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE)
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
- void JustDied(Unit* /*killer*/) override
+ void JustSummoned(Creature* summon) override
{
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
- if (Shade->IsAlive())
- ENSURE_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->HasKilledAkama = true;
- me->GetMotionMaster()->Clear(true);
- me->GetMotionMaster()->MoveIdle();
+ _summons.Summon(summon);
}
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
+
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
- if (spell->Id == SPELL_THREAT && !StartCombat)
+ if (spell->Id == SPELL_THREAT && !_isInCombat)
{
- me->ClearUnitState(UNIT_STATE_ROOT);
- me->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
- Shade->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL);
- StartCombat = true;
+ _isInCombat = true;
+ me->SetWalk(false);
+ me->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL);
+ if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
+ {
+ shade->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL);
+ AttackStart(shade);
+ _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2));
+ _events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, Seconds(5));
+ }
}
}
- void EnterCombat(Unit* /*who*/) override
+ void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override
{
- events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 2000);
- events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, 5000);
+ if (me->HealthBelowPct(20) && !_hasYelledOnce)
+ {
+ _hasYelledOnce = true;
+ Talk(SAY_LOW_HEALTH);
+ }
}
- void UpdateAI(uint32 diff) override
+ void DoAction(int32 actionId) override
{
- if (StartChannel)
+ if (actionId == ACTION_SHADE_OF_AKAMA_DEAD)
{
- events.Update(diff);
+ _isInCombat = false;
+ me->CombatStop(true);
+ me->setFaction(FACTION_FRIENDLY);
+ me->SetWalk(true);
+ _events.Reset();
+ me->GetMotionMaster()->MovePoint(AKAMA_INTRO_WAYPOINT, AkamaWP[1]);
+ }
+ }
- while (uint32 eventId = events.ExecuteEvent())
+ void MovementInform(uint32 motionType, uint32 pointId) override
+ {
+ if (motionType != POINT_MOTION_TYPE)
+ return;
+
+ if (pointId == AKAMA_CHANNEL_WAYPOINT)
+ _events.ScheduleEvent(EVENT_SHADE_CHANNEL, Seconds(1));
+
+ else if (pointId == AKAMA_INTRO_WAYPOINT)
+ {
+ me->SetWalk(false);
+ me->SetFacingTo(0.08726646f);
+ _events.ScheduleEvent(EVENT_START_SOUL_EXPEL, Seconds(1));
+ }
+ }
+
+ void SummonBrokens()
+ {
+ for (uint8 i = 0; i < 18; i++)
+ {
+ if (TempSummon* summoned = me->SummonCreature(NPC_ASHTONGUE_BROKEN, BrokenPos[i]))
{
- switch (eventId)
- {
- case EVENT_SHADE_START:
- instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS);
- me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- me->RemoveAura(SPELL_STEALTH);
- me->SetWalk(true);
- me->GetMotionMaster()->MovePoint(0, AkamaWP[0].x, AkamaWP[0].y, AkamaWP[0].z, false);
- events.ScheduleEvent(EVENT_SHADE_CHANNEL, 10000);
- break;
- case EVENT_SHADE_CHANNEL:
- me->AddUnitState(UNIT_STATE_ROOT);
- me->SetFacingTo(3.118662f);
- DoCast(me, SPELL_AKAMA_SOUL_CHANNEL);
- me->setFaction(FACTION_COMBAT);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
- events.ScheduleEvent(EVENT_FIXATE, 5000);
- break;
- case EVENT_FIXATE:
- DoCast(SPELL_FIXATE);
- StartChannel = false;
- break;
- default:
- break;
- }
+ summoned->SetWalk(true);
+ summoned->GetMotionMaster()->MovePoint(0, BrokenWP[i]);
+ if (i == 9) //On Sniffs, npc that Yell "Special" is the tenth to be created
+ _chosen = summoned->GetGUID();
}
}
+ }
- if (!UpdateVictim())
- return;
-
- events.Update(diff);
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
+ case EVENT_SHADE_START:
+ _instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS);
+ me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ me->RemoveAurasDueToSpell(SPELL_STEALTH);
+ me->SetWalk(true);
+ me->GetMotionMaster()->MovePoint(AKAMA_CHANNEL_WAYPOINT, AkamaWP[0], false);
+ break;
+ case EVENT_SHADE_CHANNEL:
+ me->SetFacingTo(3.118662f);
+ DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL);
+ me->setFaction(FACTION_COMBAT);
+ _events.ScheduleEvent(EVENT_FIXATE, Seconds(5));
+ break;
+ case EVENT_FIXATE:
+ DoCast(SPELL_FIXATE);
+ break;
case EVENT_CHAIN_LIGHTNING:
DoCastVictim(SPELL_CHAIN_LIGHTNING);
- events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, urand(10000, 15000));
+ _events.Repeat(randtime(Seconds(8), Seconds(15)));
break;
case EVENT_DESTRUCTIVE_POISON:
- DoCast(me, SPELL_DESTRUCTIVE_POISON);
- events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, urand(4000, 5000));
+ DoCastSelf(SPELL_DESTRUCTIVE_POISON);
+ _events.Repeat(randtime(Seconds(3), Seconds(7)));
+ break;
+ case EVENT_START_SOUL_EXPEL:
+ DoCast(SPELL_AKAMA_SOUL_EXPEL);
+ _events.ScheduleEvent(EVENT_START_BROKEN_FREE, Seconds(15));
+ break;
+ case EVENT_START_BROKEN_FREE:
+ me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
+ Talk(SAY_BROKEN_FREE_0);
+ SummonBrokens();
+ _events.ScheduleEvent(EVENT_BROKEN_FREE_1, Seconds(10));
+ break;
+ case EVENT_BROKEN_FREE_1:
+ Talk(SAY_BROKEN_FREE_1);
+ _events.ScheduleEvent(EVENT_BROKEN_FREE_2, Seconds(12));
+ break;
+ case EVENT_BROKEN_FREE_2:
+ Talk(SAY_BROKEN_FREE_2);
+ _events.ScheduleEvent(EVENT_BROKEN_FREE_3, Seconds(15));
+ break;
+ case EVENT_BROKEN_FREE_3:
+ if (Creature* special = ObjectAccessor::GetCreature(*me, _chosen))
+ special->AI()->Talk(SAY_BROKEN_SPECIAL);
+
+ _summons.DoAction(ACTION_BROKEN_EMOTE, _pred);
+ _events.ScheduleEvent(EVENT_BROKEN_FREE_4, Seconds(5));
+ break;
+ case EVENT_BROKEN_FREE_4:
+ _summons.DoAction(ACTION_BROKEN_HAIL, _pred);
break;
default:
break;
}
}
- DoMeleeAttackIfReady();
+ if (me->getFaction() == FACTION_COMBAT)
+ {
+ if (!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _summons.DespawnAll();
+ Talk(SAY_DEAD);
+ if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
+ if (shade->IsAlive())
+ shade->AI()->EnterEvadeMode(EVADE_REASON_OTHER);
}
void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override
@@ -524,19 +551,18 @@ public:
if (gossipListId == 0)
{
player->CLOSE_GOSSIP_MENU();
- StartChannel = true;
- events.ScheduleEvent(EVENT_SHADE_START, 500);
+ _events.ScheduleEvent(EVENT_SHADE_START, Milliseconds(500));
}
}
private:
- InstanceScript* instance;
- EventMap events;
- bool StartChannel;
- bool ShadeHasDied;
- bool StartCombat;
- bool HasYelledOnce;
-
+ InstanceScript* _instance;
+ EventMap _events;
+ SummonList _summons;
+ DummyEntryCheckPredicate _pred;
+ ObjectGuid _chosen; //Creature that should yell the speech special.
+ bool _isInCombat;
+ bool _hasYelledOnce;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -545,70 +571,44 @@ public:
}
};
-// ########################################################
-// Ashtongue Channeler
-// ########################################################
-
class npc_ashtongue_channeler : public CreatureScript
{
public:
npc_ashtongue_channeler() : CreatureScript("npc_ashtongue_channeler") { }
- struct npc_ashtongue_channelerAI : public ScriptedAI
+ struct npc_ashtongue_channelerAI : public PassiveAI
{
- npc_ashtongue_channelerAI(Creature* creature) : ScriptedAI(creature)
+ npc_ashtongue_channelerAI(Creature* creature) : PassiveAI(creature)
{
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Reset() override
{
- me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
- me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+ _scheduler.Schedule(Seconds(2), [this](TaskContext channel)
+ {
+ if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
+ {
+ if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
+ DoCastSelf(SPELL_SHADE_SOUL_CHANNEL);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_CHANNEL, 2000);
- }
+ else
+ me->DespawnOrUnsummon(Seconds(3));
+ }
- void JustDied(Unit* /*killer*/) override
- {
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
- Shade->AI()->DoAction(ACTION_CHANNELER_DIED);
+ channel.Repeat(Seconds(2));
+ });
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
- void EnterCombat(Unit* /*who*/) override { }
- void AttackStart(Unit* /*who*/) override { }
-
void UpdateAI(uint32 diff) override
{
- events.Update(diff);
-
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_CHANNEL:
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
- {
- if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
- DoCast(me, SPELL_SHADE_SOUL_CHANNEL);
- else
- {
- me->InterruptSpell(CURRENT_CHANNELED_SPELL);
- Shade->AI()->DoAction(ACTION_CHANNELER_DIED);
- }
- }
- events.ScheduleEvent(EVENT_CHANNEL, 2000);
- break;
- default:
- break;
- }
- }
+ _scheduler.Update(diff);
}
private:
- InstanceScript* instance;
- EventMap events;
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -617,10 +617,6 @@ public:
}
};
-// ########################################################
-// Creature Generator Akama
-// ########################################################
-
class npc_creature_generator_akama : public CreatureScript
{
public:
@@ -628,57 +624,53 @@ public:
struct npc_creature_generator_akamaAI : public ScriptedAI
{
- npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), Summons(me)
+ npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me)
{
Initialize();
- instance = creature->GetInstanceScript();
}
void Initialize()
{
- doSpawning = false;
- leftSide = false;
+ _leftSide = false;
+ _events.Reset();
+ _summons.DespawnAll();
}
void Reset() override
{
- Summons.DespawnAll();
-
Initialize();
- if (me->GetPositionY() < 400.0f)
- leftSide = true;
+ if (me->GetPositionY() < MIDDLE_OF_ROOM)
+ _leftSide = true;
}
void JustSummoned(Creature* summon) override
{
- Summons.Summon(summon);
+ _summons.Summon(summon);
}
void DoAction(int32 actionId) override
{
- doSpawning = true;
-
switch (actionId)
{
case ACTION_START_SPAWNING:
- if (leftSide)
+ if (_leftSide)
{
- events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100);
- events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000));
+ _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Milliseconds(100));
+ _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, randtime(Seconds(2), Seconds(5)));
}
else
{
- events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000);
- events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000));
+ _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Seconds(10));
+ _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, randtime(Seconds(2), Seconds(5)));
}
break;
case ACTION_STOP_SPAWNING:
- doSpawning = false;
+ _events.Reset();
break;
case ACTION_DESPAWN_ALL_SPAWNS:
- doSpawning = false;
- Summons.DespawnAll();
+ _events.Reset();
+ _summons.DespawnAll();
break;
default:
break;
@@ -687,39 +679,34 @@ public:
void UpdateAI(uint32 diff) override
{
- if (doSpawning)
- {
- events.Update(diff);
+ _events.Update(diff);
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
{
- switch (eventId)
- {
- case EVENT_SPAWN_WAVE_B:
- DoCast(me, SPELL_ASHTONGUE_WAVE_B);
- events.ScheduleEvent(EVENT_SPAWN_WAVE_B, urand(45000, 50000));
- break;
- case EVENT_SUMMON_ASHTONGUE_SORCERER: // left
- DoCast(me, SPELL_SUMMON_ASHTONGUE_SORCERER);
- events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(30000, 35000));
- break;
- case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right
- DoCast(me, SPELL_SUMMON_ASHTONGUE_DEFENDER);
- events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(30000, 35000));
- break;
- default:
- break;
- }
+ case EVENT_SPAWN_WAVE_B:
+ DoCastSelf(SPELL_ASHTONGUE_WAVE_B);
+ _events.Repeat(randtime(Seconds(50), Seconds(60)));
+ break;
+ case EVENT_SUMMON_ASHTONGUE_SORCERER: // left
+ DoCastSelf(SPELL_SUMMON_ASHTONGUE_SORCERER);
+ _events.Repeat(randtime(Seconds(30), Seconds(35)));
+ break;
+ case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right
+ DoCastSelf(SPELL_SUMMON_ASHTONGUE_DEFENDER);
+ _events.Repeat(randtime(Seconds(30), Seconds(40)));
+ break;
+ default:
+ break;
}
}
}
private:
- InstanceScript* instance;
- EventMap events;
- SummonList Summons;
- bool leftSide;
- bool doSpawning;
+ EventMap _events;
+ SummonList _summons;
+ bool _leftSide;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -728,10 +715,6 @@ public:
}
};
-// ########################################################
-// Ashtongue Sorcerer
-// ########################################################
-
class npc_ashtongue_sorcerer : public CreatureScript
{
public:
@@ -742,103 +725,96 @@ public:
npc_ashtongue_sorcererAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Initialize()
{
- startedBanishing = false;
- switchToCombat = false;
+ _switchToCombat = false;
+ _inBanish = false;
}
void Reset() override
{
- if (!startedBanishing)
+ if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
{
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
+ if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
+ me->GetMotionMaster()->MovePoint(0, shade->GetPosition());
+
+ else
{
- if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
- me->GetMotionMaster()->MovePoint(0, Shade->GetPositionX(), Shade->GetPositionY(), Shade->GetPositionZ(), false);
- else
- {
- if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- AttackStart(target);
- }
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
}
}
-
Initialize();
}
void JustDied(Unit* /*killer*/) override
{
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
- Shade->AI()->DoAction(ACTION_CHANNELER_DIED);
- me->DespawnOrUnsummon(5000);
+ me->DespawnOrUnsummon(Seconds(5));
}
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
void EnterCombat(Unit* /*who*/) override { }
void AttackStart(Unit* who) override
{
- if (!switchToCombat)
+ if (!_switchToCombat)
return;
+
ScriptedAI::AttackStart(who);
}
- void UpdateAI(uint32 diff) override
+ void MoveInLineOfSight(Unit* who) override
{
- events.Update(diff);
-
- while (uint32 eventId = events.ExecuteEvent())
+ if (!_inBanish && who->GetGUID() == _instance->GetGuidData(DATA_SHADE_OF_AKAMA) && me->IsWithinDist(who, 20.0f, false))
{
- switch (eventId)
+ _inBanish = true;
+ me->StopMoving();
+ me->GetMotionMaster()->Clear(false);
+ me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand(-8.0f, 8.0f), me->GetPositionY() + frand(-8.0f, 8.0f), me->GetPositionZ());
+
+ _scheduler.Schedule(Seconds(1) + Milliseconds(500), [this](TaskContext sorcer_channel)
{
- case EVENT_SORCERER_CHANNEL:
- if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
+ if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA)))
+ {
+ if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
{
- if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
- {
- me->SetFacingToObject(Shade);
- DoCast(me, SPELL_SHADE_SOUL_CHANNEL);
- events.ScheduleEvent(EVENT_SORCERER_CHANNEL, 2000);
- }
- else
- {
- me->InterruptSpell(CURRENT_CHANNELED_SPELL);
- Shade->AI()->DoAction(ACTION_CHANNELER_DIED);
- switchToCombat = true;
- if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- AttackStart(target);
- }
+ me->SetFacingToObject(shade);
+ DoCastSelf(SPELL_SHADE_SOUL_CHANNEL);
+ sorcer_channel.Repeat(Seconds(2));
}
- break;
- default:
- break;
- }
+ else
+ {
+ me->InterruptSpell(CURRENT_CHANNELED_SPELL);
+ _switchToCombat = true;
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
+ }
+ }
+ });
}
+ }
- if (!startedBanishing)
- {
- Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA));
- if (me->IsWithinDist(Shade, 20.0f, false))
- {
- me->StopMoving();
- me->GetMotionMaster()->Clear(false);
- me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand (-8.0f, 8.0f), me->GetPositionY() + frand (-8.0f, 8.0f), me->GetPositionZ(), false);
- events.ScheduleEvent(EVENT_SORCERER_CHANNEL, 1500);
- startedBanishing = true;
- }
- }
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ if (!UpdateVictim())
+ return;
DoMeleeAttackIfReady();
}
private:
- InstanceScript* instance;
- EventMap events;
- bool startedBanishing;
- bool switchToCombat;
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
+ bool _switchToCombat;
+ bool _inBanish;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -847,10 +823,6 @@ public:
}
};
-// ########################################################
-// Ashtongue Defender
-// ########################################################
-
class npc_ashtongue_defender : public CreatureScript
{
public:
@@ -860,54 +832,55 @@ public:
{
npc_ashtongue_defenderAI(Creature* creature) : ScriptedAI(creature)
{
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Reset() override
{
- if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- AttackStart(target);
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
}
void JustDied(Unit* /*killer*/) override
{
- me->DespawnOrUnsummon(5000);
+ me->DespawnOrUnsummon(Seconds(5));
}
void EnterCombat(Unit* /*who*/) override
{
- events.ScheduleEvent(EVENT_HEROIC_STRIKE, 5000);
- events.ScheduleEvent(EVENT_SHIELD_BASH, urand(10000, 16000));
- events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand(10000, 16000));
- events.ScheduleEvent(EVENT_WINDFURY, urand(8000, 12000));
+ _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(5));
+ _events.ScheduleEvent(EVENT_SHIELD_BASH, randtime(Seconds(10), Seconds(16)));
+ _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, randtime(Seconds(10), Seconds(16)));
+ _events.ScheduleEvent(EVENT_WINDFURY, randtime(Seconds(8), Seconds(12)));
}
+
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
- events.Update(diff);
+ _events.Update(diff);
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_DEBILITATING_STRIKE:
DoCastVictim(SPELL_DEBILITATING_STRIKE);
- events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand(8000, 16000));
+ _events.Repeat(randtime(Seconds(20), Seconds(25)));
break;
case EVENT_HEROIC_STRIKE:
- DoCast(me, SPELL_HEROIC_STRIKE);
- events.ScheduleEvent(EVENT_HEROIC_STRIKE, urand(50000, 60000));
+ DoCastSelf(SPELL_HEROIC_STRIKE);
+ _events.Repeat(randtime(Seconds(5), Seconds(15)));
break;
case EVENT_SHIELD_BASH:
DoCastVictim(SPELL_SHIELD_BASH);
- events.ScheduleEvent(EVENT_SHIELD_BASH, urand(8000, 16000));
+ _events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_WINDFURY:
DoCastVictim(SPELL_WINDFURY);
- events.ScheduleEvent(EVENT_WINDFURY, urand(6000 , 8000));
+ _events.Repeat(randtime(Seconds(6), Seconds(8)));
break;
default:
break;
@@ -918,8 +891,8 @@ public:
}
private:
- InstanceScript* instance;
- EventMap events;
+ InstanceScript* _instance;
+ EventMap _events;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -928,10 +901,6 @@ public:
}
};
-// ########################################################
-// Ashtongue Rogue
-// ########################################################
-
class npc_ashtongue_rogue : public CreatureScript
{
public:
@@ -941,44 +910,46 @@ public:
{
npc_ashtongue_rogueAI(Creature* creature) : ScriptedAI(creature)
{
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Reset() override
{
- if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- AttackStart(target);
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
}
void JustDied(Unit* /*killer*/) override
{
- me->DespawnOrUnsummon(5000);
+ me->DespawnOrUnsummon(Seconds(5));
}
void EnterCombat(Unit* /*who*/) override
{
- events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(500, 2000));
- events.ScheduleEvent(EVENT_EVISCERATE, urand(2000, 5000));
+ _events.ScheduleEvent(EVENT_DEBILITATING_POISON, randtime(Milliseconds(500), Seconds(2)));
+ _events.ScheduleEvent(EVENT_EVISCERATE, randtime(Seconds(2), Seconds(5)));
}
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
+
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
- events.Update(diff);
+ _events.Update(diff);
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_DEBILITATING_POISON:
DoCastVictim(SPELL_DEBILITATING_POISON);
- events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(14000, 18000));
+ _events.Repeat(randtime(Seconds(15), Seconds(20)));
break;
case EVENT_EVISCERATE:
DoCastVictim(SPELL_EVISCERATE);
- events.ScheduleEvent(EVENT_EVISCERATE, urand(12000, 16000));
+ _events.Repeat(randtime(Seconds(12), Seconds(20)));
break;
default:
break;
@@ -989,8 +960,8 @@ public:
}
private:
- InstanceScript* instance;
- EventMap events;
+ InstanceScript* _instance;
+ EventMap _events;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -999,10 +970,6 @@ public:
}
};
-// ########################################################
-// Ashtongue Elementalist
-// ########################################################
-
class npc_ashtongue_elementalist : public CreatureScript
{
public:
@@ -1012,44 +979,46 @@ public:
{
npc_ashtongue_elementalistAI(Creature* creature) : ScriptedAI(creature)
{
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Reset() override
{
- if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- AttackStart(target);
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
}
void JustDied(Unit* /*killer*/) override
{
- me->DespawnOrUnsummon(5000);
+ me->DespawnOrUnsummon(Seconds(5));
}
void EnterCombat(Unit* /*who*/) override
{
- events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 18000);
- events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 6000);
+ _events.ScheduleEvent(EVENT_RAIN_OF_FIRE, Seconds(18));
+ _events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(6));
}
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
+
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
- events.Update(diff);
+ _events.Update(diff);
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_RAIN_OF_FIRE:
DoCastVictim(SPELL_RAIN_OF_FIRE);
- events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 20000);
+ _events.Repeat(randtime(Seconds(15), Seconds(20)));
break;
case EVENT_LIGHTNING_BOLT:
DoCastVictim(SPELL_LIGHTNING_BOLT);
- events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 15000);
+ _events.Repeat(randtime(Seconds(8), Seconds(15)));
break;
default:
break;
@@ -1060,8 +1029,8 @@ public:
}
private:
- InstanceScript* instance;
- EventMap events;
+ InstanceScript* _instance;
+ EventMap _events;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1070,10 +1039,6 @@ public:
}
};
-// ########################################################
-// Ashtongue Spiritbinder
-// ########################################################
-
class npc_ashtongue_spiritbinder : public CreatureScript
{
public:
@@ -1084,44 +1049,72 @@ public:
npc_ashtongue_spiritbinderAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
void Initialize()
{
- spiritMend = false;
- chainHeal = false;
+ _spiritMend = false;
+ _chainHeal = false;
}
void Reset() override
{
Initialize();
- if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE)))
- AttackStart(target);
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ AttackStart(akama);
}
void JustDied(Unit* /*killer*/) override
{
- me->DespawnOrUnsummon(5000);
+ me->DespawnOrUnsummon(Seconds(5));
}
void EnterCombat(Unit* /*who*/) override
{
- events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (5000, 6000));
+ _events.ScheduleEvent(EVENT_SPIRIT_HEAL, randtime(Seconds(5), Seconds(6)));
}
+ void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override
+ {
+ if (!_spiritMend)
+ if (HealthBelowPct(30))
+ {
+ DoCastSelf(SPELL_SPIRIT_MEND);
+ _spiritMend = true;
+ _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, randtime(Seconds(10),Seconds(15)));
+ }
+
+ if (!_chainHeal)
+ if (HealthBelowPct(50))
+ {
+ DoCastSelf(SPELL_CHAIN_HEAL);
+ _chainHeal = true;
+ _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, randtime(Seconds(10), Seconds(15)));
+ }
+
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
+
void UpdateAI(uint32 diff) override
{
- events.Update(diff);
+ _events.Update(diff);
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SPIRIT_HEAL:
- DoCast(me, SPELL_SPIRITBINDER_SPIRIT_HEAL);
- events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (13000, 16000));
+ DoCastSelf(SPELL_SPIRITBINDER_SPIRIT_HEAL);
+ _events.Repeat(randtime(Seconds(13), Seconds(16)));
+ break;
+ case EVENT_SPIRIT_MEND_RESET:
+ _spiritMend = false;
+ break;
+ case EVENT_CHAIN_HEAL_RESET:
+ _chainHeal = false;
break;
default:
break;
@@ -1131,32 +1124,14 @@ public:
if (!UpdateVictim())
return;
- if (!spiritMend)
- {
- if (HealthBelowPct(25))
- {
- DoCast(me, SPELL_SPIRIT_MEND);
- spiritMend = true;
- }
- }
-
- if (!chainHeal)
- {
- if (HealthBelowPct(40))
- {
- DoCast(me, SPELL_CHAIN_HEAL);
- chainHeal = true;
- }
- }
-
DoMeleeAttackIfReady();
}
private:
- InstanceScript* instance;
- EventMap events;
- bool spiritMend;
- bool chainHeal;
+ InstanceScript* _instance;
+ EventMap _events;
+ bool _spiritMend;
+ bool _chainHeal;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -1165,6 +1140,119 @@ public:
}
};
+class npc_ashtongue_broken : public CreatureScript
+{
+public:
+ npc_ashtongue_broken() : CreatureScript("npc_ashtongue_broken") { }
+
+ struct npc_ashtongue_brokenAI : public ScriptedAI
+ {
+ npc_ashtongue_brokenAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _instance = me->GetInstanceScript();
+ }
+
+ void MovementInform(uint32 motionType, uint32 /*pointId*/) override
+ {
+ if (motionType != POINT_MOTION_TYPE)
+ return;
+
+ if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE)))
+ me->SetFacingToObject(akama);
+ }
+
+ void DoAction(int32 actionId) override
+ {
+ switch (actionId)
+ {
+ case ACTION_BROKEN_SPECIAL:
+ Talk(SAY_BROKEN_SPECIAL);
+ break;
+ case ACTION_BROKEN_HAIL:
+ me->setFaction(FACTION_FRIENDLY);
+ Talk(SAY_BROKEN_HAIL);
+ break;
+ case ACTION_BROKEN_EMOTE:
+ me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_KNEEL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private:
+ InstanceScript* _instance;
+ };
+
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_ashtongue_brokenAI>(creature);
+ }
+};
+
+class spell_shade_soul_channel_serverside : public SpellScriptLoader
+{
+public:
+ spell_shade_soul_channel_serverside() : SpellScriptLoader("spell_shade_soul_channel_serverside") { }
+
+ class spell_shade_soul_channel_serverside_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_shade_soul_channel_serverside_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHADE_SOUL_CHANNEL_2))
+ return false;
+ return true;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ GetTarget()->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2);
+ }
+
+ void Register() override
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_shade_soul_channel_serverside_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_shade_soul_channel_serverside_AuraScript();
+ }
+};
+
+class spell_shade_soul_channel : public SpellScriptLoader
+{
+public:
+ spell_shade_soul_channel() : SpellScriptLoader("spell_shade_soul_channel") { }
+
+ class spell_shade_soul_channel_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_shade_soul_channel_AuraScript);
+
+ void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ int32 const maxSlowEff = -99;
+ if (aurEff->GetAmount() < maxSlowEff)
+ if (AuraEffect* slowEff = GetEffect(EFFECT_0))
+ slowEff->ChangeAmount(maxSlowEff);
+ }
+
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_shade_soul_channel_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_shade_soul_channel_AuraScript();
+ }
+};
+
void AddSC_boss_shade_of_akama()
{
new boss_shade_of_akama();
@@ -1176,4 +1264,7 @@ void AddSC_boss_shade_of_akama()
new npc_ashtongue_rogue();
new npc_ashtongue_elementalist();
new npc_ashtongue_spiritbinder();
+ new npc_ashtongue_broken();
+ new spell_shade_soul_channel_serverside();
+ new spell_shade_soul_channel();
}
diff --git a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp
index 72eb99bd7dc..56d333b2dda 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp
@@ -91,7 +91,7 @@ public:
void Despawn()
{
- me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
me->RemoveCorpse();
}
@@ -218,7 +218,7 @@ public:
{
Creature* Teron = (ObjectAccessor::GetCreature((*me), TeronGUID));
if (!Teron || !Teron->IsAlive() || Teron->IsInEvadeMode())
- me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
CheckTeronTimer = 5000;
} else CheckTeronTimer -= diff;
@@ -236,9 +236,9 @@ public:
return GetInstanceAI<boss_teron_gorefiendAI>(creature);
}
- struct boss_teron_gorefiendAI : public ScriptedAI
+ struct boss_teron_gorefiendAI : public BossAI
{
- boss_teron_gorefiendAI(Creature* creature) : ScriptedAI(creature)
+ boss_teron_gorefiendAI(Creature* creature) : BossAI(creature, DATA_TERON_GOREFIEND)
{
Initialize();
instance = creature->GetInstanceScript();
@@ -277,8 +277,7 @@ public:
void Reset() override
{
- instance->SetBossState(DATA_TERON_GOREFIEND, NOT_STARTED);
-
+ _Reset();
Initialize();
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -316,9 +315,8 @@ public:
void JustDied(Unit* /*killer*/) override
{
- instance->SetBossState(DATA_TERON_GOREFIEND, DONE);
-
Talk(SAY_DEATH);
+ _JustDied();
}
float CalculateRandomLocation(float Loc, uint32 radius)
@@ -363,7 +361,7 @@ public:
/** WHAT IS FULLY NECESSARY FOR GOREFIEND TO BE 100% COMPLETE *****/
/************************************************************************/
- Unit* ghost = NULL;
+ Unit* ghost = nullptr;
if (GhostGUID)
ghost = ObjectAccessor::GetUnit(*me, GhostGUID);
if (ghost && ghost->IsAlive() && ghost->HasAura(SPELL_SHADOW_OF_DEATH))
@@ -379,7 +377,7 @@ public:
}*/
for (uint8 i = 0; i < 4; ++i)
{
- Creature* Construct = NULL;
+ Creature* Construct = nullptr;
float X = CalculateRandomLocation(ghost->GetPositionX(), 10);
float Y = CalculateRandomLocation(ghost->GetPositionY(), 10);
Construct = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, Y, ghost->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000);
@@ -435,17 +433,13 @@ public:
for (uint8 i = 0; i < 2; ++i)
{
- Creature* Shadow = NULL;
float X = CalculateRandomLocation(me->GetPositionX(), 10);
- Shadow = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 0);
- if (Shadow)
+ if (Creature* shadow = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 0))
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1);
- if (!target)
- target = me->GetVictim();
-
- if (target)
- Shadow->AI()->AttackStart(target);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1))
+ shadow->AI()->AttackStart(target);
+ else if (Unit* victim = me->GetVictim())
+ shadow->AI()->AttackStart(victim);
}
}
SummonShadowsTimer = 60000;
diff --git a/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp b/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp
index 9b33ea88ca7..b9627856ae6 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp
@@ -150,7 +150,7 @@ public:
DoCast(target, SPELL_IMPALING_SPINE, true);
SpineTargetGUID = target->GetGUID();
//must let target summon, otherwise you cannot click the spine
- target->SummonGameObject(GO_NAJENTUS_SPINE, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), me->GetOrientation(), 0, 0, 0, 0, 30);
+ target->SummonGameObject(GO_NAJENTUS_SPINE, *target, G3D::Quat(), 30);
Talk(SAY_NEEDLE);
events.DelayEvents(1500, GCD_CAST);
events.DelayEvents(15000, GCD_YELL);
diff --git a/src/server/scripts/Outland/BlackTemple/illidari_council.cpp b/src/server/scripts/Outland/BlackTemple/illidari_council.cpp
index d883a48b630..06b492e32d7 100644
--- a/src/server/scripts/Outland/BlackTemple/illidari_council.cpp
+++ b/src/server/scripts/Outland/BlackTemple/illidari_council.cpp
@@ -90,8 +90,6 @@ enum IllidariCouncil
SPELL_BERSERK = 45078
};
-#define ERROR_INST_DATA "SD2 ERROR: Instance Data for Black Temple not set properly; Illidari Council event will not function properly."
-
struct CouncilYells
{
int32 entry;
@@ -165,7 +163,7 @@ public:
Council[1] = instance->GetGuidData(DATA_VERAS_DARKSHADOW);
Council[2] = instance->GetGuidData(DATA_LADY_MALANDE);
Council[3] = instance->GetGuidData(DATA_HIGH_NETHERMANCER_ZEREVOR);
- } else TC_LOG_ERROR("scripts", ERROR_INST_DATA);
+ }
}
void EnterCombat(Unit* /*who*/) override { }
@@ -231,6 +229,7 @@ public:
{
Initialize();
instance = creature->GetInstanceScript();
+ SetBoundary(instance->GetBossBoundary(DATA_ILLIDARI_COUNCIL));
}
void Initialize()
@@ -257,7 +256,7 @@ public:
{
Initialize();
- Creature* pMember = NULL;
+ Creature* pMember = nullptr;
for (uint8 i = 0; i < 4; ++i)
{
pMember = ObjectAccessor::GetCreature((*me), Council[i]);
@@ -330,16 +329,16 @@ public:
if (DeathCount > 3)
{
if (Creature* VoiceTrigger = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BLOOD_ELF_COUNCIL_VOICE)))
- VoiceTrigger->DealDamage(VoiceTrigger, VoiceTrigger->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ VoiceTrigger->DealDamage(VoiceTrigger, VoiceTrigger->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
instance->SetBossState(DATA_ILLIDARI_COUNCIL, DONE);
//me->SummonCreature(AKAMAID, 746.466980f, 304.394989f, 311.90208f, 6.272870f, TEMPSUMMON_DEAD_DESPAWN, 0);
- me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
return;
}
Creature* pMember = (ObjectAccessor::GetCreature(*me, Council[DeathCount]));
if (pMember && pMember->IsAlive())
- pMember->DealDamage(pMember, pMember->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ pMember->DealDamage(pMember, pMember->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
++DeathCount;
EndEventTimer = 1500;
} else EndEventTimer -= diff;
@@ -922,7 +921,7 @@ public:
if (dmgInfo.GetAttacker() == target)
return;
int32 bp = absorbAmount / 2;
- target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_T, &bp, NULL, NULL, true, NULL, aurEff);
+ target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_T, &bp, nullptr, nullptr, true, nullptr, aurEff);
}
void Register() override
diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp
index 86ad7958957..bac996918ac 100644
--- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp
+++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp
@@ -35,6 +35,20 @@ DoorData const doorData[] =
{ 0, 0, DOOR_TYPE_ROOM } // END
};
+BossBoundaryData const boundaries =
+{
+ { DATA_HIGH_WARLORD_NAJENTUS, new RectangleBoundary(394.0f, 479.4f, 707.8f, 859.1f) },
+ { DATA_SUPREMUS, new RectangleBoundary(556.1f, 850.2f, 542.0f, 1001.0f) },
+ { DATA_SHADE_OF_AKAMA, new RectangleBoundary(406.8f, 564.0f, 327.9f, 473.5f) },
+ { DATA_TERON_GOREFIEND, new RectangleBoundary(512.5f, 613.3f, 373.2f, 432.0f) },
+ { DATA_TERON_GOREFIEND, new ZRangeBoundary(179.5f, 223.6f) },
+ { DATA_GURTOGG_BLOODBOIL, new RectangleBoundary(720.5f, 864.5f, 159.3f, 316.0f) },
+ { DATA_RELIQUARY_OF_SOULS, new RectangleBoundary(435.9f, 558.8f, 113.3f, 229.6f) },
+ { DATA_MOTHER_SHAHRAZ, new RectangleBoundary(903.4f, 982.1f, 92.4f, 476.7f) },
+ { DATA_ILLIDARI_COUNCIL, new EllipseBoundary(Position(696.6f, 305.0f), 70.0 , 85.0) },
+ { DATA_ILLIDAN_STORMRAGE, new EllipseBoundary(Position(694.8f, 309.0f), 70.0 , 85.0) }
+};
+
class instance_black_temple : public InstanceMapScript
{
public:
@@ -47,6 +61,7 @@ class instance_black_temple : public InstanceMapScript
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
+ LoadBossBoundaries(boundaries);
}
void OnCreatureCreate(Creature* creature) override
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
index 89b7a8cbd46..62552a3bf61 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
@@ -73,6 +73,7 @@ enum Spells
SPELL_SLIPPERY_FLOOR_AMBIENT = 46314,
SPELL_SLIPPERY_FLOOR_PERIODIC = 46320,
SPELL_SLIPPERY_FLOOR_SLIP = 45947,
+ SPELL_SLIPPERY_FLOOR_YOU_SLIPPED = 45946,
// Frozen Core
SPELL_SUICIDE = 45254,
@@ -101,19 +102,18 @@ enum Events
EVENT_EMERGE = 1,
EVENT_INITIAL_EMERGE = 2,
EVENT_SYNCH_HEALTH = 3,
- EVENT_FOUND_OPENING = 4,
- EVENT_LOOKFOROPENING_0 = 5,
- EVENT_LOOKFOROPENING_1 = 6,
- EVENT_LOOKFOROPENING_2 = 7,
- EVENT_SUMMON_HAILSTONE = 8,
- EVENT_SUMMON_COLDWEAVE = 9,
- EVENT_SUMMON_FROSTWIND = 10,
- EVENT_SUMMON_AHUNE = 11,
- EVENT_CLOSE_OPENING = 12,
- EVENT_AHUNE_PHASE_ONE = 13,
- EVENT_AHUNE_PHASE_TWO = 14,
- EVENT_START_LOOKING_FOR_OPENING = 15,
- EVENT_STOP_LOOKING_FOR_OPENING = 16
+ EVENT_LOOKFOROPENING_0 = 4,
+ EVENT_LOOKFOROPENING_1 = 5,
+ EVENT_LOOKFOROPENING_2 = 6,
+ EVENT_SUMMON_HAILSTONE = 7,
+ EVENT_SUMMON_COLDWEAVE = 8,
+ EVENT_SUMMON_FROSTWIND = 9,
+ EVENT_SUMMON_AHUNE = 10,
+ EVENT_CLOSE_OPENING = 11,
+ EVENT_AHUNE_PHASE_ONE = 12,
+ EVENT_AHUNE_PHASE_TWO = 13,
+ EVENT_START_LOOKING_FOR_OPENING = 14,
+ EVENT_STOP_LOOKING_FOR_OPENING = 15
};
enum Actions
@@ -169,19 +169,14 @@ public:
{
boss_ahuneAI(Creature* creature) : BossAI(creature, DATA_AHUNE)
{
- Initialize();
- }
-
- void Initialize()
- {
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ me->SetControlled(true, UNIT_STATE_ROOT);
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.ScheduleEvent(EVENT_INITIAL_EMERGE, 4);
- events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ events.ScheduleEvent(EVENT_INITIAL_EMERGE, Milliseconds(4));
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, Seconds(3));
}
void EnterEvadeMode(EvadeReason /*why*/) override
@@ -194,7 +189,6 @@ public:
void JustDied(Unit* /*killer*/) override
{
- _JustDied();
instance->DoCastSpellOnPlayers(SPELL_AHUNE_ACHIEVEMENT);
if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AHUNE_BUNNY)))
@@ -209,11 +203,8 @@ public:
if (group->isLFGGroup())
sLFGMgr->FinishDungeon(group->GetGUID(), 286);
}
- }
- void JustSummoned(Creature* summon) override
- {
- BossAI::JustSummoned(summon);
+ _JustDied();
}
void DoAction(int32 action) override
@@ -221,7 +212,7 @@ public:
if (action == ACTION_AHUNE_RETREAT)
{
Submerge();
- events.ScheduleEvent(EVENT_EMERGE, 35000);
+ events.ScheduleEvent(EVENT_EMERGE, Seconds(35));
}
}
@@ -246,10 +237,10 @@ public:
break;
case EVENT_SYNCH_HEALTH:
if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
- DoCast(frozenCore, SPELL_SYNCH_HEALTH);
+ DoCast(frozenCore, SPELL_SYNCH_HEALTH, true);
else
DoCast(me, SPELL_SUICIDE);
- events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ events.Repeat(Seconds(3));
break;
default:
break;
@@ -268,8 +259,8 @@ public:
me->RemoveAurasDueToSpell(SPELL_STAY_SUBMERGED);
DoCast(me, SPELL_STAND);
DoCast(me, SPELL_RESURFACE, true);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, Seconds(3));
}
void Submerge()
@@ -277,7 +268,7 @@ public:
if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
frozenCore->AI()->DoAction(ACTION_AHUNE_RETREAT);
me->RemoveAurasDueToSpell(SPELL_AHUNES_SHIELD);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_31);
DoCast(me, SPELL_SUBMERGED, true);
DoCast(me, SPELL_AHUNE_SELF_STUN, true);
DoCast(me, SPELL_STAY_SUBMERGED, true);
@@ -308,18 +299,11 @@ public:
void Initialize()
{
me->SetReactState(REACT_PASSIVE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ me->setRegeneratingHealth(false);
DoCast(me, SPELL_FROZEN_CORE_GETS_HIT);
DoCast(me, SPELL_ICE_SPEAR_AURA);
}
- void EnterEvadeMode(EvadeReason /*why*/) override
- {
- DoCast(SPELL_MINION_DESPAWNER);
- if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
- ahune->AI()->EnterEvadeMode();
- }
-
void JustDied(Unit* /*killer*/) override
{
if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
@@ -333,23 +317,20 @@ public:
{
if (action == ACTION_AHUNE_RETREAT)
{
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->RemoveAurasDueToSpell(SPELL_ICE_SPEAR_AURA);
- _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000, 0, PHASE_TWO);
+ _events.ScheduleEvent(EVENT_SYNCH_HEALTH, Seconds(3), 0, PHASE_TWO);
}
else if (action == ACTION_AHUNE_RESURFACE)
{
_events.Reset();
DoCast(me, SPELL_ICE_SPEAR_AURA);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_DISABLE_MOVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
}
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
- return;
-
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
@@ -358,10 +339,10 @@ public:
{
case EVENT_SYNCH_HEALTH:
if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
- DoCast(ahune, SPELL_SYNCH_HEALTH);
+ DoCast(ahune, SPELL_SYNCH_HEALTH, true);
else
DoCast(me, SPELL_SUICIDE);
- _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ _events.Repeat(Seconds(3));
break;
default:
break;
@@ -414,7 +395,7 @@ public:
_summons.DespawnAll();
ResetFlameCallers();
- me->SummonGameObject(GO_ICE_STONE, -69.90455f, -162.2449f, -2.366563f, 2.426008f, 0.0f, 0.0f, 0.9366722f, 0.3502074f, 0);
+ me->SummonGameObject(GO_ICE_STONE, -69.90455f, -162.2449f, -2.366563f, 2.426008f, G3D::Quat(0.0f, 0.0f, 0.9366722f, 0.3502074f), 0);
}
void DoAction(int32 action) override
@@ -435,11 +416,11 @@ public:
_submerged = false;
_events.Reset();
_events.SetPhase(PHASE_ONE);
- _events.ScheduleEvent(EVENT_SUMMON_AHUNE, 10000);
- _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, 14000, 0, PHASE_ONE);
- _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 22000, 0, PHASE_ONE);
- _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, 14000, 0, PHASE_ONE);
- _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, 108000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_AHUNE, Seconds(10));
+ _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, Seconds(14), 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, Seconds(22), 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, Seconds(14), 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, Seconds(108), 0, PHASE_ONE);
}
}
@@ -466,9 +447,9 @@ public:
case EVENT_SUMMON_COLDWEAVE:
DoCast(SPELL_SUMMON_COLDWEAVE);
DoCast(SPELL_SUMMON_COLDWEAVE);
- _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE);
+ _events.Repeat(Seconds(8));
if (_submerged)
- _events.ScheduleEvent(EVENT_SUMMON_FROSTWIND, 4000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_FROSTWIND, Seconds(4), 0, PHASE_ONE);
break;
case EVENT_SUMMON_FROSTWIND:
DoCast(SPELL_SUMMON_FROSTWIND);
@@ -494,17 +475,17 @@ public:
ahune->AI()->DoAction(ACTION_AHUNE_RETREAT);
_events.Reset();
_events.SetPhase(PHASE_TWO);
- _events.ScheduleEvent(EVENT_CLOSE_OPENING, 25000, 0, PHASE_TWO);
- _events.ScheduleEvent(EVENT_AHUNE_PHASE_ONE, 35000, 0, PHASE_TWO);
+ _events.ScheduleEvent(EVENT_CLOSE_OPENING, Seconds(25), 0, PHASE_TWO);
+ _events.ScheduleEvent(EVENT_AHUNE_PHASE_ONE, Seconds(35), 0, PHASE_TWO);
break;
case EVENT_AHUNE_PHASE_ONE:
_submerged = true;
_events.Reset();
_events.SetPhase(PHASE_ONE);
- _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE);
- _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, 5000, 0, PHASE_ONE);
- _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, 5000, 0, PHASE_ONE);
- _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, 100000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, Seconds(8), 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, Seconds(5), 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, Seconds(5), 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, Seconds(100), 0, PHASE_ONE);
break;
default:
break;
@@ -581,10 +562,10 @@ public:
switch (spellInfo->Id)
{
case SPELL_SHAMANS_LOOK_FOR_OPENING:
- _events.ScheduleEvent(EVENT_LOOKFOROPENING_0, 17000);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_0, Seconds(17));
break;
case SPELL_FOUND_OPENING:
- _events.ScheduleEvent(EVENT_FOUND_OPENING, 0);
+ Talk(EMOTE_RETREAT);
break;
default:
break;
@@ -607,21 +588,19 @@ public:
{
case EVENT_LOOKFOROPENING_0:
LookOpening(true, 0);
- _events.ScheduleEvent(EVENT_LOOKFOROPENING_1, 26000);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_1, Seconds(26));
break;
case EVENT_LOOKFOROPENING_1:
LookOpening(true, 1);
- _events.ScheduleEvent(EVENT_LOOKFOROPENING_2, 25000);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_2, Seconds(25));
break;
case EVENT_LOOKFOROPENING_2:
LookOpening(true, 2);
- _events.ScheduleEvent(EVENT_STOP_LOOKING_FOR_OPENING, 27000);
+ _events.ScheduleEvent(EVENT_STOP_LOOKING_FOR_OPENING, Seconds(27));
break;
case EVENT_STOP_LOOKING_FOR_OPENING:
LookOpening(false, _mySpot);
break;
- case EVENT_FOUND_OPENING:
- Talk(EMOTE_RETREAT);
default:
break;
}
@@ -799,25 +778,26 @@ public:
void PeriodicTick(AuraEffect const* aurEff)
{
- if (Creature* caster = GetCaster()->ToCreature())
- switch (aurEff->GetTickNumber())
- {
- case 1:
- caster->CastSpell(caster, SPELL_SUMMON_ICE_SPEAR_GO);
- break;
- case 3:
- if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f))
- spike->UseDoorOrButton();
- caster->AI()->DoCastAOE(SPELL_ICE_SPEAR_KNOCKBACK, true);
- break;
- case 5:
- if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f))
- spike->Delete();
- caster->DespawnOrUnsummon();
- break;
- default:
- break;
- }
+ if (Unit* tmpCaster = GetCaster())
+ if (Creature* caster = tmpCaster->ToCreature())
+ switch (aurEff->GetTickNumber())
+ {
+ case 1:
+ caster->CastSpell(caster, SPELL_SUMMON_ICE_SPEAR_GO);
+ break;
+ case 3:
+ if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f))
+ spike->UseDoorOrButton();
+ caster->AI()->DoCastAOE(SPELL_ICE_SPEAR_KNOCKBACK, true);
+ break;
+ case 5:
+ if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f))
+ spike->Delete();
+ caster->DespawnOrUnsummon();
+ break;
+ default:
+ break;
+ }
}
void Register() override
@@ -884,6 +864,16 @@ public:
return true;
}
+ void FilterTargets(std::list<WorldObject*>& targets)
+ {
+ if (targets.empty())
+ return;
+
+ WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
+ targets.clear();
+ targets.push_back(target);
+ }
+
void HandleDummy(SpellEffIndex /*effIndex*/)
{
GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ICE_SPEAR_BUNNY, true);
@@ -891,6 +881,7 @@ public:
void Register() override
{
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_ice_spear_target_picker_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
OnEffectHitTarget += SpellEffectFn(spell_ice_spear_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
@@ -922,7 +913,10 @@ public:
{
if (Unit* target = GetHitUnit())
if (target->isMoving())
+ {
target->CastSpell(target, SPELL_SLIPPERY_FLOOR_SLIP, true);
+ target->CastSpell(target, SPELL_SLIPPERY_FLOOR_YOU_SLIPPED, true);
+ }
}
void Register() override
diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp
index cab5b9efbc3..f4ff9f7084c 100644
--- a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp
+++ b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp
@@ -537,7 +537,7 @@ class npc_zerekethvoidzone : public CreatureScript
void Reset() override
{
- me->SetUInt32Value(UNIT_NPC_FLAGS, 0);
+ me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
me->setFaction(16);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
diff --git a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
index e64c0fe9f5e..b9e7fb0034d 100644
--- a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
+++ b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
@@ -103,7 +103,7 @@ class npc_warp_splinter_treant : public CreatureScript
{
if (Unit* Warp = ObjectAccessor::GetUnit(*me, WarpGuid))
{
- if (me->IsWithinMeleeRange(Warp, 2.5f))
+ if (me->IsWithinMeleeRange(Warp))
{
int32 CurrentHP_Treant = (int32)me->GetHealth();
Warp->CastCustomSpell(Warp, SPELL_HEAL_FATHER, &CurrentHP_Treant, 0, 0, true, 0, 0, me->GetGUID());
diff --git a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp
index 30bf085be43..9074f1ea373 100644
--- a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp
+++ b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp
@@ -959,7 +959,7 @@ public:
{
// Spell 37392 does not exist in dbc, manually spawning
me->SummonCreature(NPC_OSCILLATING_FREQUENCY_SCANNER_TOP_BUNNY, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 0.5f, me->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 50000);
- me->SummonGameObject(GO_OSCILLATING_FREQUENCY_SCANNER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), 0, 0, 0, 0, 50);
+ me->SummonGameObject(GO_OSCILLATING_FREQUENCY_SCANNER, *me, G3D::Quat(), 50);
me->DespawnOrUnsummon(50000);
}
diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp
index d35dedb5d27..8803ce9d9ed 100644
--- a/src/server/scripts/Outland/zone_netherstorm.cpp
+++ b/src/server/scripts/Outland/zone_netherstorm.cpp
@@ -19,7 +19,7 @@
/* ScriptData
SDName: Netherstorm
SD%Complete: 80
-SDComment: Quest support: 10337, 10438, 10652 (special flight paths), 10198, 10191
+SDComment: Quest support: 10337, 10652 (special flight paths), 10198, 10191
SDCategory: Netherstorm
EndScriptData */
@@ -335,56 +335,6 @@ public:
};
/*######
-## npc_professor_dabiri
-######*/
-enum ProfessorDabiriData
-{
- SPELL_PHASE_DISTRUPTOR = 35780,
-
- //WHISPER_DABIRI = 0, not existing in database
-
- QUEST_DIMENSIUS = 10439,
- QUEST_ON_NETHERY_WINGS = 10438,
-};
-
-#define GOSSIP_ITEM "I need a new phase distruptor, Professor"
-
-class npc_professor_dabiri : public CreatureScript
-{
-public:
- npc_professor_dabiri() : CreatureScript("npc_professor_dabiri") { }
-
- //OnQuestAccept:
- //if (quest->GetQuestId() == QUEST_DIMENSIUS)
- //creature->AI()->Talk(WHISPER_DABIRI, player);
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF+1)
- {
- creature->CastSpell(player, SPELL_PHASE_DISTRUPTOR, false);
- player->CLOSE_GOSSIP_MENU();
- }
-
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- if (player->GetQuestStatus(QUEST_ON_NETHERY_WINGS) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(29778))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-};
-
-/*######
## npc_phase_hunter
######*/
@@ -752,7 +702,6 @@ void AddSC_netherstorm()
{
new npc_commander_dawnforge();
new at_commander_dawnforge();
- new npc_professor_dabiri();
new npc_phase_hunter();
new npc_bessy();
new npc_maxx_a_million_escort();
diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp
index 7ea87a3c0c4..c91e5c94ca0 100644
--- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp
+++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp
@@ -19,7 +19,7 @@
/* ScriptData
SDName: Shadowmoon_Valley
SD%Complete: 100
-SDComment: Quest support: 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781, 10451. Vendor Drake Dealer Hurlunk.
+SDComment: Quest support: 10804, 10854, 10458, 10481, 10480, 10781, 10451.
SDCategory: Shadowmoon Valley
EndScriptData */
@@ -28,9 +28,6 @@ npc_invis_infernal_caster
npc_infernal_attacker
npc_mature_netherwing_drake
npc_enslaved_netherwing_drake
-npc_drake_dealer_hurlunk
-npcs_flanis_swiftwing_and_kagrosh
-npc_karynaku
npc_earthmender_wilda
npc_torloth_the_magnificent
npc_illidari_spawn
@@ -90,8 +87,10 @@ public:
void SummonInfernal()
{
- Creature* infernal = me->SummonCreature(NPC_INFERNAL_ATTACKER, me->GetPositionX(), me->GetPositionY(), ground + 0.05f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000);
- infernalGUID = infernal->GetGUID();
+ if (Creature* infernal = me->SummonCreature(NPC_INFERNAL_ATTACKER, me->GetPositionX(), me->GetPositionY(), ground + 0.05f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000))
+ infernalGUID = infernal->GetGUID();
+ else
+ infernalGUID = ObjectGuid::Empty;
}
void UpdateAI(uint32 diff) override
@@ -568,117 +567,6 @@ public:
};
};
-/*######
-## npc_drake_dealer_hurlunk
-######*/
-
-class npc_drake_dealer_hurlunk : public CreatureScript
-{
-public:
- npc_drake_dealer_hurlunk() : CreatureScript("npc_drake_dealer_hurlunk") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_TRADE)
- player->GetSession()->SendListInventory(creature->GetGUID());
-
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsVendor() && player->GetReputationRank(1015) == REP_EXALTED)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-};
-
-/*######
-## npc_flanis_swiftwing_and_kagrosh
-######*/
-
-#define GOSSIP_HSK1 "Take Flanis's Pack"
-#define GOSSIP_HSK2 "Take Kagrosh's Pack"
-
-class npcs_flanis_swiftwing_and_kagrosh : public CreatureScript
-{
-public:
- npcs_flanis_swiftwing_and_kagrosh() : CreatureScript("npcs_flanis_swiftwing_and_kagrosh") { }
-
- bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF+1)
- {
- ItemPosCountVec dest;
- uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 30658, 1, NULL);
- if (msg == EQUIP_ERR_OK)
- {
- player->StoreNewItem(dest, 30658, true);
- player->PlayerTalkClass->ClearMenus();
- }
- }
- if (action == GOSSIP_ACTION_INFO_DEF+2)
- {
- ItemPosCountVec dest;
- uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 30659, 1, NULL);
- if (msg == EQUIP_ERR_OK)
- {
- player->StoreNewItem(dest, 30659, true);
- player->PlayerTalkClass->ClearMenus();
- }
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (player->GetQuestStatus(10583) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30658, 1, true))
- player->ADD_GOSSIP_ITEM(0, GOSSIP_HSK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- if (player->GetQuestStatus(10601) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30659, 1, true))
- player->ADD_GOSSIP_ITEM(0, GOSSIP_HSK2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-};
-
-/*######
-# npc_karynaku
-####*/
-
-enum Karynaku
-{
- QUEST_ALLY_OF_NETHER = 10870,
- QUEST_ZUHULED_THE_WACK = 10866,
-
- NPC_ZUHULED_THE_WACKED = 11980,
-
- TAXI_PATH_ID = 649,
-};
-
-class npc_karynaku : public CreatureScript
-{
- public:
- npc_karynaku() : CreatureScript("npc_karynaku") { }
-
- bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override
- {
- if (quest->GetQuestId() == QUEST_ALLY_OF_NETHER)
- player->ActivateTaxiPathTo(TAXI_PATH_ID);
-
- if (quest->GetQuestId() == QUEST_ZUHULED_THE_WACK)
- creature->SummonCreature(NPC_ZUHULED_THE_WACKED, -4204.94f, 316.397f, 122.508f, 1.309f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300000);
-
- return true;
- }
-};
-
/*####
# npc_earthmender_wilda
####*/
@@ -1701,9 +1589,6 @@ void AddSC_shadowmoon_valley()
new npc_mature_netherwing_drake();
new npc_enslaved_netherwing_drake();
new npc_dragonmaw_peon();
- new npc_drake_dealer_hurlunk();
- new npcs_flanis_swiftwing_and_kagrosh();
- new npc_karynaku();
new npc_earthmender_wilda();
new npc_lord_illidan_stormrage();
new go_crystal_prison();
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index 724019a1b19..980c0db19cc 100644
--- a/src/server/scripts/Spells/spell_dk.cpp
+++ b/src/server/scripts/Spells/spell_dk.cpp
@@ -30,6 +30,13 @@
enum DeathKnightSpells
{
+ SPELL_DK_ACCLIMATION_HOLY = 50490,
+ SPELL_DK_ACCLIMATION_FIRE = 50362,
+ SPELL_DK_ACCLIMATION_FROST = 50485,
+ SPELL_DK_ACCLIMATION_ARCANE = 50486,
+ SPELL_DK_ACCLIMATION_SHADOW = 50489,
+ SPELL_DK_ACCLIMATION_NATURE = 50488,
+ SPELL_DK_ADVANTAGE_T10_4P_MELEE = 70657,
SPELL_DK_ANTI_MAGIC_SHELL_TALENT = 51052,
SPELL_DK_BLACK_ICE_R1 = 49140,
SPELL_DK_BLOOD_BOIL_TRIGGERED = 65658,
@@ -51,6 +58,7 @@ enum DeathKnightSpells
SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1 = 50365,
SPELL_DK_IMPROVED_FROST_PRESENCE_R1 = 50384,
SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1 = 50391,
+ SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL = 50475,
SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED = 63611,
SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED = 63622,
SPELL_DK_ITEM_SIGIL_VENGEFUL_HEART = 64962,
@@ -78,6 +86,141 @@ enum Misc
NPC_DK_GHOUL = 26125
};
+// -49200 - Acclimation
+class spell_dk_acclimation : public SpellScriptLoader
+{
+public:
+ spell_dk_acclimation() : SpellScriptLoader("spell_dk_acclimation") { }
+
+ class spell_dk_acclimation_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_acclimation_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_HOLY) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_FIRE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_FROST) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_NATURE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_SHADOW) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_ARCANE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetDamageInfo())
+ {
+ switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_HOLY:
+ case SPELL_SCHOOL_FIRE:
+ case SPELL_SCHOOL_NATURE:
+ case SPELL_SCHOOL_FROST:
+ case SPELL_SCHOOL_SHADOW:
+ case SPELL_SCHOOL_ARCANE:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ uint32 triggerspell = 0;
+
+ switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_HOLY:
+ triggerspell = SPELL_DK_ACCLIMATION_HOLY;
+ break;
+ case SPELL_SCHOOL_FIRE:
+ triggerspell = SPELL_DK_ACCLIMATION_FIRE;
+ break;
+ case SPELL_SCHOOL_NATURE:
+ triggerspell = SPELL_DK_ACCLIMATION_NATURE;
+ break;
+ case SPELL_SCHOOL_FROST:
+ triggerspell = SPELL_DK_ACCLIMATION_FROST;
+ break;
+ case SPELL_SCHOOL_SHADOW:
+ triggerspell = SPELL_DK_ACCLIMATION_SHADOW;
+ break;
+ case SPELL_SCHOOL_ARCANE:
+ triggerspell = SPELL_DK_ACCLIMATION_ARCANE;
+ break;
+ default:
+ return;
+ }
+
+ if (Unit* target = eventInfo.GetActionTarget())
+ target->CastSpell(target, triggerspell, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dk_acclimation_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_dk_acclimation_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dk_acclimation_AuraScript();
+ }
+};
+
+// 70656 - Advantage (T10 4P Melee Bonus)
+class spell_dk_advantage_t10_4p : public SpellScriptLoader
+{
+public:
+ spell_dk_advantage_t10_4p() : SpellScriptLoader("spell_dk_advantage_t10_4p") { }
+
+ class spell_dk_advantage_t10_4p_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_advantage_t10_4p_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DK_ADVANTAGE_T10_4P_MELEE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (Unit* caster = eventInfo.GetActor())
+ {
+ if (caster->GetTypeId() != TYPEID_PLAYER || caster->getClass() != CLASS_DEATH_KNIGHT)
+ return false;
+
+ for (uint8 i = 0; i < MAX_RUNES; ++i)
+ if (caster->ToPlayer()->GetRuneCooldown(i) == 0)
+ return false;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dk_advantage_t10_4p_AuraScript::CheckProc);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dk_advantage_t10_4p_AuraScript();
+ }
+};
+
// 50462 - Anti-Magic Shell (on raid member)
class spell_dk_anti_magic_shell_raid : public SpellScriptLoader
{
@@ -919,6 +1062,52 @@ class spell_dk_improved_blood_presence : public SpellScriptLoader
}
};
+// 63611 - Improved Blood Presence Triggered
+class spell_dk_improved_blood_presence_triggered : public SpellScriptLoader
+{
+public:
+ spell_dk_improved_blood_presence_triggered() : SpellScriptLoader("spell_dk_improved_blood_presence_triggered") { }
+
+ class spell_dk_improved_blood_presence_triggered_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_improved_blood_presence_triggered_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER)
+ return true;
+
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo())
+ eventInfo.GetActor()->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL, SPELLVALUE_BASE_POINT0, CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()),
+ eventInfo.GetActor(), true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dk_improved_blood_presence_triggered_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_dk_improved_blood_presence_triggered_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dk_improved_blood_presence_triggered_AuraScript();
+ }
+};
+
// -50384 - Improved Frost Presence
class spell_dk_improved_frost_presence : public SpellScriptLoader
{
@@ -2006,6 +2195,8 @@ public:
void AddSC_deathknight_spell_scripts()
{
+ new spell_dk_acclimation();
+ new spell_dk_advantage_t10_4p();
new spell_dk_anti_magic_shell_raid();
new spell_dk_anti_magic_shell_self();
new spell_dk_anti_magic_zone();
@@ -2022,6 +2213,7 @@ void AddSC_deathknight_spell_scripts()
new spell_dk_ghoul_explode();
new spell_dk_icebound_fortitude();
new spell_dk_improved_blood_presence();
+ new spell_dk_improved_blood_presence_triggered();
new spell_dk_improved_frost_presence();
new spell_dk_improved_unholy_presence();
new spell_dk_pestilence();
diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp
index c088ae07e66..0bf5ab01f45 100644
--- a/src/server/scripts/Spells/spell_druid.cpp
+++ b/src/server/scripts/Spells/spell_druid.cpp
@@ -31,6 +31,13 @@ enum DruidSpells
{
SPELL_DRUID_BEAR_FORM_PASSIVE = 1178,
SPELL_DRUID_DIRE_BEAR_FORM_PASSIVE = 9635,
+ SPELL_DRUID_ECLIPSE_LUNAR_PROC = 48518,
+ SPELL_DRUID_ECLIPSE_SOLAR_PROC = 48517,
+ SPELL_DRUID_FORMS_TRINKET_BEAR = 37340,
+ SPELL_DRUID_FORMS_TRINKET_CAT = 37341,
+ SPELL_DRUID_FORMS_TRINKET_MOONKIN = 37343,
+ SPELL_DRUID_FORMS_TRINKET_NONE = 37344,
+ SPELL_DRUID_FORMS_TRINKET_TREE = 37342,
SPELL_DRUID_ENRAGE = 5229,
SPELL_DRUID_ENRAGE_MOD_DAMAGE = 51185,
SPELL_DRUID_ENRAGED_DEFENSE = 70725,
@@ -48,6 +55,8 @@ enum DruidSpells
SPELL_DRUID_NATURES_SPLENDOR = 57865,
SPELL_DRUID_SURVIVAL_INSTINCTS = 50322,
SPELL_DRUID_SAVAGE_ROAR = 62071,
+ SPELL_DRUID_T9_FERAL_RELIC_BEAR = 67354,
+ SPELL_DRUID_T9_FERAL_RELIC_CAT = 67355,
SPELL_DRUID_TIGER_S_FURY_ENERGIZE = 51178
};
@@ -131,6 +140,69 @@ class spell_dru_dash : public SpellScriptLoader
}
};
+class spell_dru_eclipse : public SpellScriptLoader
+{
+public:
+ spell_dru_eclipse() : SpellScriptLoader("spell_dru_eclipse") { }
+
+ class spell_dru_eclipse_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_eclipse_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_LUNAR_PROC))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_SOLAR_PROC))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (!eventInfo.GetSpellInfo())
+ return false;
+
+ if (eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_LUNAR_PROC) || eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_SOLAR_PROC))
+ return false;
+
+ // Triggered by Wrath?
+ if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1)
+ return roll_chance_f(GetSpellInfo()->ProcChance * 0.6f) && _lunarProcCooldownEnd <= std::chrono::steady_clock::now();
+
+ return _solarProcCooldownEnd <= std::chrono::steady_clock::now();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1)
+ {
+ _lunarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount());
+ eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_LUNAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff);
+ }
+ else
+ {
+ _solarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount());
+ eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_SOLAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff);
+ }
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dru_eclipse_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_dru_eclipse_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+
+ std::chrono::steady_clock::time_point _lunarProcCooldownEnd = std::chrono::steady_clock::time_point::min();
+ std::chrono::steady_clock::time_point _solarProcCooldownEnd = std::chrono::steady_clock::time_point::min();
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dru_eclipse_AuraScript();
+ }
+};
+
// 5229 - Enrage
class spell_dru_enrage : public SpellScriptLoader
{
@@ -197,6 +269,91 @@ class spell_dru_enrage : public SpellScriptLoader
}
};
+// 37336 - Druid Forms Trinket
+class spell_dru_forms_trinket : public SpellScriptLoader
+{
+public:
+ spell_dru_forms_trinket() : SpellScriptLoader("spell_dru_forms_trinket") { }
+
+ class spell_dru_forms_trinket_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_forms_trinket_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_BEAR) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_CAT) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_MOONKIN) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_NONE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_TREE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ Unit* target = eventInfo.GetActor();
+
+ switch (target->GetShapeshiftForm())
+ {
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_CAT:
+ case FORM_MOONKIN:
+ case FORM_NONE:
+ case FORM_TREE:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ Unit* target = eventInfo.GetActor();
+ uint32 triggerspell = 0;
+
+ switch (target->GetShapeshiftForm())
+ {
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ triggerspell = SPELL_DRUID_FORMS_TRINKET_BEAR;
+ break;
+ case FORM_CAT:
+ triggerspell = SPELL_DRUID_FORMS_TRINKET_CAT;
+ break;
+ case FORM_MOONKIN:
+ triggerspell = SPELL_DRUID_FORMS_TRINKET_MOONKIN;
+ break;
+ case FORM_NONE:
+ triggerspell = SPELL_DRUID_FORMS_TRINKET_NONE;
+ break;
+ case FORM_TREE:
+ triggerspell = SPELL_DRUID_FORMS_TRINKET_TREE;
+ break;
+ default:
+ return;
+ }
+
+ target->CastSpell(target, triggerspell, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dru_forms_trinket_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_dru_forms_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dru_forms_trinket_AuraScript();
+ }
+};
+
// 54846 - Glyph of Starfire
class spell_dru_glyph_of_starfire : public SpellScriptLoader
{
@@ -1079,6 +1236,77 @@ class spell_dru_typhoon : public SpellScriptLoader
}
};
+// 67353 - T9 Feral Relic (Idol of Mutilation)
+class spell_dru_t9_feral_relic : public SpellScriptLoader
+{
+public:
+ spell_dru_t9_feral_relic() : SpellScriptLoader("spell_dru_t9_feral_relic") { }
+
+ class spell_dru_t9_feral_relic_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_t9_feral_relic_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_T9_FERAL_RELIC_BEAR) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DRUID_T9_FERAL_RELIC_CAT))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ Unit* target = eventInfo.GetActor();
+
+ switch (target->GetShapeshiftForm())
+ {
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_CAT:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ uint32 triggerspell = 0;
+
+ Unit* target = eventInfo.GetActor();
+
+ switch (target->GetShapeshiftForm())
+ {
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ triggerspell = SPELL_DRUID_T9_FERAL_RELIC_BEAR;
+ break;
+ case FORM_CAT:
+ triggerspell = SPELL_DRUID_T9_FERAL_RELIC_CAT;
+ break;
+ default:
+ return;
+ }
+
+ target->CastSpell(target, triggerspell, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dru_t9_feral_relic_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_dru_t9_feral_relic_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dru_t9_feral_relic_AuraScript();
+ }
+};
+
// 70691 - Item T10 Restoration 4P Bonus
class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader
{
@@ -1208,7 +1436,9 @@ void AddSC_druid_spell_scripts()
{
new spell_dru_bear_form_passive();
new spell_dru_dash();
+ new spell_dru_eclipse();
new spell_dru_enrage();
+ new spell_dru_forms_trinket();
new spell_dru_glyph_of_starfire();
new spell_dru_idol_lifebloom();
new spell_dru_innervate();
@@ -1230,6 +1460,7 @@ void AddSC_druid_spell_scripts()
new spell_dru_flight_form();
new spell_dru_tiger_s_fury();
new spell_dru_typhoon();
+ new spell_dru_t9_feral_relic();
new spell_dru_t10_restoration_4p_bonus();
new spell_dru_wild_growth();
}
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 8b8c5300a9e..e8ad73ceadb 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -430,6 +430,61 @@ class spell_gen_bandage : public SpellScriptLoader
}
};
+// Blood Reserve - 64568
+enum BloodReserve
+{
+ SPELL_GEN_BLOOD_RESERVE_AURA = 64568,
+ SPELL_GEN_BLOOD_RESERVE_HEAL = 64569
+};
+
+class spell_gen_blood_reserve : public SpellScriptLoader
+{
+ public:
+ spell_gen_blood_reserve() : SpellScriptLoader("spell_gen_blood_reserve") { }
+
+ class spell_gen_blood_reserve_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_gen_blood_reserve_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_GEN_BLOOD_RESERVE_HEAL))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo())
+ if (Unit* caster = eventInfo.GetActionTarget())
+ if (caster->HealthBelowPctDamaged(35, dmgInfo->GetDamage()))
+ return true;
+
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActionTarget();
+ caster->CastCustomSpell(SPELL_GEN_BLOOD_RESERVE_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), caster, TRIGGERED_FULL_MASK, nullptr, aurEff);
+ caster->RemoveAura(SPELL_GEN_BLOOD_RESERVE_AURA);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_gen_blood_reserve_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_gen_blood_reserve_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_gen_blood_reserve_AuraScript();
+ }
+};
+
enum Bonked
{
SPELL_BONKED = 62991,
@@ -2085,7 +2140,7 @@ class spell_gen_mounted_charge: public SpellScriptLoader
}
// If target isn't a training dummy there's a chance of failing the charge
- if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE) && roll_chance_f(12.5f))
+ if (!target->IsCharmedOwnedByPlayerOrPlayer() && roll_chance_f(12.5f))
spellId = SPELL_CHARGE_MISS_EFFECT;
if (Unit* vehicle = GetCaster()->GetVehicleBase())
@@ -4199,6 +4254,7 @@ void AddSC_generic_spell_scripts()
new spell_gen_aura_service_uniform();
new spell_gen_av_drekthar_presence();
new spell_gen_bandage();
+ new spell_gen_blood_reserve();
new spell_gen_bonked();
new spell_gen_break_shield("spell_gen_break_shield");
new spell_gen_break_shield("spell_gen_tournament_counterattack");
diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp
index 6442eb8acca..bd36e3cb765 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
{
@@ -1183,11 +1282,79 @@ class spell_midsummer_braziers_hit : public SpellScriptLoader
}
};
+enum RibbonPoleData
+{
+ SPELL_HAS_FULL_MIDSUMMER_SET = 58933,
+ SPELL_BURNING_HOT_POLE_DANCE = 58934,
+ SPELL_RIBBON_DANCE_COSMETIC = 29726,
+ SPELL_RIBBON_DANCE = 29175,
+ GO_RIBBON_POLE = 181605,
+};
+
+class spell_gen_ribbon_pole_dancer_check : public SpellScriptLoader
+{
+ public:
+ spell_gen_ribbon_pole_dancer_check() : SpellScriptLoader("spell_gen_ribbon_pole_dancer_check") { }
+
+ class spell_gen_ribbon_pole_dancer_check_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_gen_ribbon_pole_dancer_check_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_HAS_FULL_MIDSUMMER_SET)
+ || !sSpellMgr->GetSpellInfo(SPELL_RIBBON_DANCE)
+ || !sSpellMgr->GetSpellInfo(SPELL_BURNING_HOT_POLE_DANCE))
+ return false;
+ return true;
+ }
+
+ void PeriodicTick(AuraEffect const* /*aurEff*/)
+ {
+ Unit* target = GetTarget();
+
+ // check if aura needs to be removed
+ if (!target->FindNearestGameObject(GO_RIBBON_POLE, 8.0f) || !target->HasUnitState(UNIT_STATE_CASTING))
+ {
+ target->InterruptNonMeleeSpells(false);
+ target->RemoveAurasDueToSpell(GetId());
+ target->RemoveAura(SPELL_RIBBON_DANCE_COSMETIC);
+ return;
+ }
+
+ // set xp buff duration
+ if (Aura* aur = target->GetAura(SPELL_RIBBON_DANCE))
+ {
+ aur->SetMaxDuration(std::min(3600000, aur->GetMaxDuration() + 180000));
+ aur->RefreshDuration();
+
+ // reward achievement criteria
+ if (aur->GetMaxDuration() == 3600000 && target->HasAura(SPELL_HAS_FULL_MIDSUMMER_SET))
+ target->CastSpell(target, SPELL_BURNING_HOT_POLE_DANCE, true);
+ }
+ else
+ target->AddAura(SPELL_RIBBON_DANCE, target);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_ribbon_pole_dancer_check_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_gen_ribbon_pole_dancer_check_AuraScript();
+ }
+};
+
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();
@@ -1219,4 +1386,5 @@ void AddSC_holiday_spell_scripts()
new spell_brewfest_barker_bunny();
// Midsummer Fire Festival
new spell_midsummer_braziers_hit();
+ new spell_gen_ribbon_pole_dancer_check();
}
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index 82d9d134445..a75294ad6e0 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -51,12 +51,15 @@ enum HunterSpells
SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED = 54114,
SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF = 55711,
SPELL_HUNTER_PET_CARRION_FEEDER_TRIGGERED = 54045,
+ SPELL_HUNTER_PIERCING_SHOTS = 63468,
SPELL_HUNTER_READINESS = 23989,
SPELL_HUNTER_SNIPER_TRAINING_R1 = 53302,
SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 = 64418,
+ SPELL_HUNTER_T9_4P_GREATNESS = 68130,
SPELL_HUNTER_VICIOUS_VIPER = 61609,
SPELL_HUNTER_VIPER_ATTACK_SPEED = 60144,
- SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543
+ SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543,
+ SPELL_ROAR_OF_SACRIFICE_TRIGGERED = 67481
};
// 13161 - Aspect of the Beast
@@ -704,6 +707,63 @@ class spell_hun_pet_heart_of_the_phoenix : public SpellScriptLoader
}
};
+// -53234 - Piercing Shots
+class spell_hun_piercing_shots : public SpellScriptLoader
+{
+public:
+ spell_hun_piercing_shots() : SpellScriptLoader("spell_hun_piercing_shots") { }
+
+ class spell_hun_piercing_shots_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_hun_piercing_shots_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_PIERCING_SHOTS))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetActionTarget())
+ return true;
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ Unit* caster = eventInfo.GetActor();
+ Unit* target = eventInfo.GetActionTarget();
+
+ if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo())
+ {
+ SpellInfo const* piercingShots = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_PIERCING_SHOTS);
+ int32 duration = piercingShots->GetMaxDuration();
+ uint32 amplitude = piercingShots->Effects[EFFECT_0].Amplitude;
+ uint32 dmg = dmgInfo->GetDamage();
+
+ uint32 bp = CalculatePct(int32(dmg), aurEff->GetAmount()) / (duration / int32(amplitude));
+ bp += target->GetRemainingPeriodicAmount(target->GetGUID(), SPELL_HUNTER_PIERCING_SHOTS, SPELL_AURA_PERIODIC_DAMAGE);
+
+ caster->CastCustomSpell(SPELL_HUNTER_PIERCING_SHOTS, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff);
+ }
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_hun_piercing_shots_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_hun_piercing_shots_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hun_piercing_shots_AuraScript();
+ }
+};
+
// 56654, 58882 - Rapid Recuperation
class spell_hun_rapid_recuperation : public SpellScriptLoader
{
@@ -788,6 +848,49 @@ class spell_hun_readiness : public SpellScriptLoader
}
};
+// 53480 - Roar of Sacrifice
+class spell_hun_roar_of_sacrifice : public SpellScriptLoader
+{
+ public:
+ spell_hun_roar_of_sacrifice() : SpellScriptLoader("spell_hun_roar_of_sacrifice") { }
+
+ class spell_hun_roar_of_sacrifice_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_hun_roar_of_sacrifice_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ROAR_OF_SACRIFICE_TRIGGERED))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return GetCaster() && (eventInfo.GetDamageInfo()->GetSchoolMask() & GetEffect(EFFECT_1)->GetMiscValue()) != 0;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ uint32 damage = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount());
+ eventInfo.GetActor()->CastCustomSpell(SPELL_ROAR_OF_SACRIFICE_TRIGGERED, SPELLVALUE_BASE_POINT0, damage, GetCaster(), TRIGGERED_FULL_MASK, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_hun_roar_of_sacrifice_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_hun_roar_of_sacrifice_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hun_roar_of_sacrifice_AuraScript();
+ }
+};
+
// 37506 - Scatter Shot
class spell_hun_scatter_shot : public SpellScriptLoader
{
@@ -967,6 +1070,51 @@ class spell_hun_target_only_pet_and_owner : public SpellScriptLoader
}
};
+// 67151 - T9 4P Bonus
+class spell_hun_t9_4p_bonus : public SpellScriptLoader
+{
+public:
+ spell_hun_t9_4p_bonus() : SpellScriptLoader("spell_hun_t9_4p_bonus") { }
+
+ class spell_hun_t9_4p_bonus_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_hun_t9_4p_bonus_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_T9_4P_GREATNESS))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER && eventInfo.GetActor()->ToPlayer()->GetPet())
+ return true;
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ Unit* caster = eventInfo.GetActor();
+
+ caster->CastSpell(caster->ToPlayer()->GetPet(), SPELL_HUNTER_T9_4P_GREATNESS, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_hun_t9_4p_bonus_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_hun_t9_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hun_t9_4p_bonus_AuraScript();
+ }
+};
+
// 60144 - Viper Attack Speed
class spell_hun_viper_attack_speed : public SpellScriptLoader
{
@@ -1025,11 +1173,14 @@ void AddSC_hunter_spell_scripts()
new spell_hun_misdirection_proc();
new spell_hun_pet_carrion_feeder();
new spell_hun_pet_heart_of_the_phoenix();
+ new spell_hun_piercing_shots();
new spell_hun_rapid_recuperation();
new spell_hun_readiness();
+ new spell_hun_roar_of_sacrifice();
new spell_hun_scatter_shot();
new spell_hun_sniper_training();
new spell_hun_tame_beast();
new spell_hun_target_only_pet_and_owner();
+ new spell_hun_t9_4p_bonus();
new spell_hun_viper_attack_speed();
}
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index 84c67bad63b..5a6bb78eb10 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -1903,7 +1903,7 @@ class spell_item_crystal_prison_dummy_dnd : public SpellScriptLoader
if (Creature* target = GetHitCreature())
if (target->isDead() && !target->IsPet())
{
- GetCaster()->SummonGameObject(OBJECT_IMPRISONED_DOOMGUARD, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0, 0, 0, 0, uint32(target->GetRespawnTime()-time(NULL)));
+ GetCaster()->SummonGameObject(OBJECT_IMPRISONED_DOOMGUARD, *target, G3D::Quat(), uint32(target->GetRespawnTime()-time(NULL)));
target->DespawnOrUnsummon();
}
}
@@ -2731,6 +2731,70 @@ public:
}
};
+enum SoulPreserver
+{
+ SPELL_SOUL_PRESERVER_DRUID = 60512,
+ SPELL_SOUL_PRESERVER_PALADIN = 60513,
+ SPELL_SOUL_PRESERVER_PRIEST = 60514,
+ SPELL_SOUL_PRESERVER_SHAMAN = 60515,
+};
+
+class spell_item_soul_preserver : public SpellScriptLoader
+{
+public:
+ spell_item_soul_preserver() : SpellScriptLoader("spell_item_soul_preserver") { }
+
+ class spell_item_soul_preserver_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_soul_preserver_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_DRUID) ||
+ !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_PALADIN) ||
+ !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_PRIEST) ||
+ !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_SHAMAN))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActor();
+
+ switch (caster->getClass())
+ {
+ case CLASS_DRUID:
+ caster->CastSpell(caster, SPELL_SOUL_PRESERVER_DRUID, true, nullptr, aurEff);
+ break;
+ case CLASS_PALADIN:
+ caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PALADIN, true, nullptr, aurEff);
+ break;
+ case CLASS_PRIEST:
+ caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PRIEST, true, nullptr, aurEff);
+ break;
+ case CLASS_SHAMAN:
+ caster->CastSpell(caster, SPELL_SOUL_PRESERVER_SHAMAN, true, nullptr, aurEff);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_soul_preserver_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_item_soul_preserver_AuraScript();
+ }
+};
+
class spell_item_toy_train_set_pulse : public SpellScriptLoader
{
public:
@@ -2768,6 +2832,336 @@ public:
}
};
+enum DeathChoiceSpells
+{
+ SPELL_DEATH_CHOICE_NORMAL_AURA = 67702,
+ SPELL_DEATH_CHOICE_NORMAL_AGILITY = 67703,
+ SPELL_DEATH_CHOICE_NORMAL_STRENGTH = 67708,
+ SPELL_DEATH_CHOICE_HEROIC_AURA = 67771,
+ SPELL_DEATH_CHOICE_HEROIC_AGILITY = 67772,
+ SPELL_DEATH_CHOICE_HEROIC_STRENGTH = 67773
+};
+
+class spell_item_death_choice : public SpellScriptLoader
+{
+public:
+ spell_item_death_choice() : SpellScriptLoader("spell_item_death_choice") { }
+
+ class spell_item_death_choice_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_death_choice_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_NORMAL_STRENGTH) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_NORMAL_AGILITY) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_HEROIC_STRENGTH) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_HEROIC_AGILITY))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActor();
+ float str = caster->GetStat(STAT_STRENGTH);
+ float agi = caster->GetStat(STAT_AGILITY);
+
+ switch (aurEff->GetId())
+ {
+ case SPELL_DEATH_CHOICE_NORMAL_AURA:
+ {
+ if (str > agi)
+ caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_STRENGTH, true, nullptr, aurEff);
+ else
+ caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_AGILITY, true, nullptr, aurEff);
+ break;
+ }
+ case SPELL_DEATH_CHOICE_HEROIC_AURA:
+ {
+ if (str > agi)
+ caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_STRENGTH, true, nullptr, aurEff);
+ else
+ caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_AGILITY, true, nullptr, aurEff);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_death_choice_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_item_death_choice_AuraScript();
+ }
+};
+
+enum TrinketStackSpells
+{
+ SPELL_LIGHTNING_CAPACITOR_AURA = 37657, // Lightning Capacitor
+ SPELL_LIGHTNING_CAPACITOR_STACK = 37658,
+ SPELL_LIGHTNING_CAPACITOR_TRIGGER = 37661,
+ SPELL_THUNDER_CAPACITOR_AURA = 54841, // Thunder Capacitor
+ SPELL_THUNDER_CAPACITOR_STACK = 54842,
+ SPELL_THUNDER_CAPACITOR_TRIGGER = 54843,
+ SPELL_TOC25_CASTER_TRINKET_NORMAL_AURA = 67712, // Item - Coliseum 25 Normal Caster Trinket
+ SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK = 67713,
+ SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER = 67714,
+ SPELL_TOC25_CASTER_TRINKET_HEROIC_AURA = 67758, // Item - Coliseum 25 Heroic Caster Trinket
+ SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK = 67759,
+ SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER = 67760,
+};
+
+class spell_item_trinket_stack : public SpellScriptLoader
+{
+public:
+ spell_item_trinket_stack(char const* scriptName, uint32 stackSpell, uint32 triggerSpell) : SpellScriptLoader(scriptName),
+ _stackSpell(stackSpell), _triggerSpell(triggerSpell)
+ {
+ }
+
+ class spell_item_trinket_stack_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_trinket_stack_AuraScript);
+
+ public:
+ spell_item_trinket_stack_AuraScript(uint32 stackSpell, uint32 triggerSpell) : _stackSpell(stackSpell), _triggerSpell(triggerSpell)
+ {
+ }
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(_stackSpell) || !sSpellMgr->GetSpellInfo(_triggerSpell))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActor();
+
+ caster->CastSpell(caster, _stackSpell, true, nullptr, aurEff); // cast the stack
+
+ Aura* dummy = caster->GetAura(_stackSpell); // retrieve aura
+
+ //dont do anything if it's not the right amount of stacks;
+ if (!dummy || dummy->GetStackAmount() < aurEff->GetAmount())
+ return;
+
+ // if right amount, remove the aura and cast real trigger
+ caster->RemoveAurasDueToSpell(_stackSpell);
+ if (Unit* target = eventInfo.GetActionTarget())
+ caster->CastSpell(target, _triggerSpell, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_trinket_stack_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+
+ private:
+ uint32 _stackSpell;
+ uint32 _triggerSpell;
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_item_trinket_stack_AuraScript(_stackSpell, _triggerSpell);
+ }
+
+private:
+ uint32 _stackSpell;
+ uint32 _triggerSpell;
+};
+
+// 57345 - Darkmoon Card: Greatness
+enum DarkmoonCardSpells
+{
+ SPELL_DARKMOON_CARD_STRENGHT = 60229,
+ SPELL_DARKMOON_CARD_AGILITY = 60233,
+ SPELL_DARKMOON_CARD_INTELLECT = 60234,
+ SPELL_DARKMOON_CARD_SPIRIT = 60235,
+};
+
+class spell_item_darkmoon_card_greatness : public SpellScriptLoader
+{
+public:
+ spell_item_darkmoon_card_greatness() : SpellScriptLoader("spell_item_darkmoon_card_greatness") { }
+
+ class spell_item_darkmoon_card_greatness_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_darkmoon_card_greatness_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_STRENGHT) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_AGILITY) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_INTELLECT) ||
+ !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_SPIRIT))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActor();
+ float str = caster->GetStat(STAT_STRENGTH);
+ float agi = caster->GetStat(STAT_AGILITY);
+ float intl = caster->GetStat(STAT_INTELLECT);
+ float spi = caster->GetStat(STAT_SPIRIT);
+ float stat = 0.0f;
+
+ uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGHT;
+
+ if (str > stat)
+ {
+ spellTrigger = SPELL_DARKMOON_CARD_STRENGHT;
+ stat = str;
+ }
+
+ if (agi > stat)
+ {
+ spellTrigger = SPELL_DARKMOON_CARD_AGILITY;
+ stat = agi;
+ }
+
+ if (intl > stat)
+ {
+ spellTrigger = SPELL_DARKMOON_CARD_INTELLECT;
+ stat = intl;
+ }
+
+ if (spi > stat)
+ {
+ spellTrigger = SPELL_DARKMOON_CARD_SPIRIT;
+ stat = spi;
+ }
+
+ caster->CastSpell(caster, spellTrigger, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_darkmoon_card_greatness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_item_darkmoon_card_greatness_AuraScript();
+ }
+};
+
+// 43820 - Amani Charm of the Witch Doctor
+enum CharmWitchDoctor
+{
+ SPELL_CHARM_WITCH_DOCTOR_PROC = 43821
+};
+
+class spell_item_charm_witch_doctor : public SpellScriptLoader
+{
+public:
+ spell_item_charm_witch_doctor() : SpellScriptLoader("spell_item_charm_witch_doctor") { }
+
+ class spell_item_charm_witch_doctor_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_charm_witch_doctor_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_CHARM_WITCH_DOCTOR_PROC))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActor();
+ Unit* target = eventInfo.GetActionTarget();
+
+ if (target)
+ {
+ int32 bp = CalculatePct(target->GetCreateHealth(),aurEff->GetSpellInfo()->Effects[1].CalcValue());
+ caster->CastCustomSpell(target, SPELL_CHARM_WITCH_DOCTOR_PROC, &bp, nullptr, nullptr, true, nullptr, aurEff);
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_charm_witch_doctor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_item_charm_witch_doctor_AuraScript();
+ }
+};
+
+// 27522,40336 - Mana Drain
+enum ManaDrainSpells
+{
+ SPELL_MANA_DRAIN_ENERGIZE = 29471,
+ SPELL_MANA_DRAIN_LEECH = 27526
+};
+
+class spell_item_mana_drain : public SpellScriptLoader
+{
+public:
+ spell_item_mana_drain() : SpellScriptLoader("spell_item_mana_drain") { }
+
+ class spell_item_mana_drain_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_mana_drain_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MANA_DRAIN_ENERGIZE)
+ || !sSpellMgr->GetSpellInfo(SPELL_MANA_DRAIN_LEECH))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ Unit* caster = eventInfo.GetActor();
+ Unit* target = eventInfo.GetActionTarget();
+
+ if (caster->IsAlive())
+ caster->CastSpell(caster, SPELL_MANA_DRAIN_ENERGIZE, true, nullptr, aurEff);
+
+ if (target && target->IsAlive())
+ caster->CastSpell(target, SPELL_MANA_DRAIN_LEECH, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_mana_drain_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_item_mana_drain_AuraScript();
+ }
+};
+
void AddSC_item_spell_scripts()
{
// 23074 Arcanite Dragonling
@@ -2838,5 +3232,14 @@ void AddSC_item_spell_scripts()
new spell_item_chicken_cover();
new spell_item_muisek_vessel();
new spell_item_greatmothers_soulcatcher();
+ new spell_item_soul_preserver();
new spell_item_toy_train_set_pulse();
+ new spell_item_death_choice();
+ new spell_item_trinket_stack("spell_item_lightning_capacitor", SPELL_LIGHTNING_CAPACITOR_STACK, SPELL_LIGHTNING_CAPACITOR_TRIGGER);
+ new spell_item_trinket_stack("spell_item_thunder_capacitor", SPELL_THUNDER_CAPACITOR_STACK, SPELL_THUNDER_CAPACITOR_TRIGGER);
+ new spell_item_trinket_stack("spell_item_toc25_normal_caster_trinket", SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK, SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER);
+ new spell_item_trinket_stack("spell_item_toc25_heroic_caster_trinket", SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK, SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER);
+ new spell_item_darkmoon_card_greatness();
+ new spell_item_charm_witch_doctor();
+ new spell_item_mana_drain();
}
diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp
index 2f4e4fa6f44..bacbe31630c 100644
--- a/src/server/scripts/Spells/spell_mage.cpp
+++ b/src/server/scripts/Spells/spell_mage.cpp
@@ -29,6 +29,7 @@
enum MageSpells
{
+ SPELL_MAGE_BLAZING_SPEED = 31643,
SPELL_MAGE_BURNOUT = 29077,
SPELL_MAGE_COLD_SNAP = 11958,
SPELL_MAGE_FOCUS_MAGIC_PROC = 54648,
@@ -116,6 +117,42 @@ class spell_mage_blast_wave : public SpellScriptLoader
}
};
+// -31641 - Blazing Speed
+class spell_mage_blazing_speed : public SpellScriptLoader
+{
+public:
+ spell_mage_blazing_speed() : SpellScriptLoader("spell_mage_blazing_speed") { }
+
+ class spell_mage_blazing_speed_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_mage_blazing_speed_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_BLAZING_SPEED))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ if (Unit* target = eventInfo.GetActionTarget())
+ target->CastSpell(target, SPELL_MAGE_BLAZING_SPEED, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_mage_blazing_speed_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_mage_blazing_speed_AuraScript();
+ }
+};
+
// -44449 - Burnout
class spell_mage_burnout : public SpellScriptLoader
{
@@ -647,6 +684,7 @@ class spell_mage_summon_water_elemental : public SpellScriptLoader
void AddSC_mage_spell_scripts()
{
new spell_mage_blast_wave();
+ new spell_mage_blazing_speed();
new spell_mage_burnout();
new spell_mage_cold_snap();
new spell_mage_fire_frost_ward();
diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp
index d9fd36f5fd4..6de95af8d8f 100644
--- a/src/server/scripts/Spells/spell_paladin.cpp
+++ b/src/server/scripts/Spells/spell_paladin.cpp
@@ -37,6 +37,7 @@ enum PaladinSpells
SPELL_PALADIN_HOLY_SHOCK_R1 = 20473,
SPELL_PALADIN_HOLY_SHOCK_R1_DAMAGE = 25912,
SPELL_PALADIN_HOLY_SHOCK_R1_HEALING = 25914,
+ SPELL_PALADIN_ILLUMINATION_ENERGIZE = 20272,
SPELL_PALADIN_BLESSING_OF_LOWER_CITY_DRUID = 37878,
SPELL_PALADIN_BLESSING_OF_LOWER_CITY_PALADIN = 37879,
@@ -871,6 +872,61 @@ class spell_pal_holy_shock : public SpellScriptLoader
}
};
+// -20210 - Illumination
+class spell_pal_illumination : public SpellScriptLoader
+{
+public:
+ spell_pal_illumination() : SpellScriptLoader("spell_pal_illumination") { }
+
+ class spell_pal_illumination_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pal_illumination_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_SHOCK_R1_HEALING) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PALADIN_ILLUMINATION_ENERGIZE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_SHOCK_R1))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ // this script is valid only for the Holy Shock procs of illumination
+ if (eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetSpellInfo())
+ {
+ SpellInfo const* originalSpell = nullptr;
+
+ // if proc comes from the Holy Shock heal, need to get mana cost of original spell - else it's the original heal itself
+ if (eventInfo.GetHealInfo()->GetSpellInfo()->SpellFamilyFlags[1] & 0x00010000)
+ originalSpell = sSpellMgr->GetSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_PALADIN_HOLY_SHOCK_R1, eventInfo.GetHealInfo()->GetSpellInfo()->GetRank()));
+ else
+ originalSpell = eventInfo.GetHealInfo()->GetSpellInfo();
+
+ if (originalSpell && aurEff->GetSpellInfo())
+ {
+ Unit* target = eventInfo.GetActor(); // Paladin is the target of the energize
+ uint32 bp = CalculatePct(originalSpell->CalcPowerCost(target, originalSpell->GetSchoolMask()), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue());
+ target->CastCustomSpell(SPELL_PALADIN_ILLUMINATION_ENERGIZE, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff);
+ }
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_pal_illumination_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_pal_illumination_AuraScript();
+ }
+};
+
// Maybe this is incorrect
// These spells should always be cast on login, regardless of whether the player has the talent or not
@@ -1393,6 +1449,7 @@ void AddSC_paladin_spell_scripts()
new spell_pal_hand_of_sacrifice();
new spell_pal_hand_of_salvation();
new spell_pal_holy_shock();
+ new spell_pal_illumination();
new spell_pal_improved_aura("spell_pal_improved_concentraction_aura", SPELL_PALADIN_IMPROVED_CONCENTRACTION_AURA);
new spell_pal_improved_aura("spell_pal_improved_devotion_aura", SPELL_PALADIN_IMPROVED_DEVOTION_AURA);
new spell_pal_improved_aura("spell_pal_sanctified_retribution", SPELL_PALADIN_SANCTIFIED_RETRIBUTION_AURA);
diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp
index 51f03346df1..9e2d265aa9c 100644
--- a/src/server/scripts/Spells/spell_priest.cpp
+++ b/src/server/scripts/Spells/spell_priest.cpp
@@ -29,6 +29,7 @@
enum PriestSpells
{
+ SPELL_PRIEST_BLESSED_RECOVERY_R1 = 27813,
SPELL_PRIEST_DIVINE_AEGIS = 47753,
SPELL_PRIEST_EMPOWERED_RENEW = 63544,
SPELL_PRIEST_GLYPH_OF_CIRCLE_OF_HEALING = 55675,
@@ -88,6 +89,50 @@ class RaidCheck
Unit const* _caster;
};
+// -27811 - Blessed Recovery
+class spell_pri_blessed_recovery : public SpellScriptLoader
+{
+public:
+ spell_pri_blessed_recovery() : SpellScriptLoader("spell_pri_blessed_recovery") { }
+
+ class spell_pri_blessed_recovery_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_blessed_recovery_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_BLESSED_RECOVERY_R1))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo())
+ {
+ if (Unit* target = eventInfo.GetActionTarget())
+ {
+ uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_PRIEST_BLESSED_RECOVERY_R1, aurEff->GetSpellInfo()->GetRank());
+ uint32 bp = CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()) / 3;
+ bp += target->GetRemainingPeriodicAmount(target->GetGUID(), triggerSpell, SPELL_AURA_PERIODIC_HEAL);
+ target->CastCustomSpell(triggerSpell, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff);
+ }
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_pri_blessed_recovery_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_pri_blessed_recovery_AuraScript();
+ }
+};
+
// -34861 - Circle of Healing
class spell_pri_circle_of_healing : public SpellScriptLoader
{
@@ -869,6 +914,7 @@ class spell_pri_vampiric_touch : public SpellScriptLoader
void AddSC_priest_spell_scripts()
{
+ new spell_pri_blessed_recovery();
new spell_pri_circle_of_healing();
new spell_pri_divine_aegis();
new spell_pri_divine_hymn();
diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp
index ae761413ca5..715e4d4ed2d 100644
--- a/src/server/scripts/Spells/spell_quest.cpp
+++ b/src/server/scripts/Spells/spell_quest.cpp
@@ -1080,9 +1080,7 @@ enum RedSnapperVeryTasty
ITEM_RED_SNAPPER = 23614,
SPELL_CAST_NET = 29866,
- SPELL_NEW_SUMMON_TEST = 49214,
-
- GO_SCHOOL_OF_RED_SNAPPER = 181616
+ SPELL_NEW_SUMMON_TEST = 49214
};
class spell_q9452_cast_net: public SpellScriptLoader
@@ -1099,15 +1097,6 @@ class spell_q9452_cast_net: public SpellScriptLoader
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
- SpellCastResult CheckCast()
- {
- GameObject* go = GetCaster()->FindNearestGameObject(GO_SCHOOL_OF_RED_SNAPPER, 3.0f);
- if (!go || go->GetRespawnTime())
- return SPELL_FAILED_REQUIRES_SPELL_FOCUS;
-
- return SPELL_CAST_OK;
- }
-
void HandleDummy(SpellEffIndex /*effIndex*/)
{
Player* caster = GetCaster()->ToPlayer();
@@ -1127,7 +1116,6 @@ class spell_q9452_cast_net: public SpellScriptLoader
void Register() override
{
- OnCheckCast += SpellCheckCastFn(spell_q9452_cast_net_SpellScript::CheckCast);
OnEffectHit += SpellEffectFn(spell_q9452_cast_net_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
OnEffectHitTarget += SpellEffectFn(spell_q9452_cast_net_SpellScript::HandleActiveObject, EFFECT_1, SPELL_EFFECT_ACTIVATE_OBJECT);
}
@@ -2482,6 +2470,33 @@ class spell_q13665_q13790_bested_trigger : public SpellScriptLoader
}
};
+// herald of war and life without regret portal spells
+class spell_59064_59439_portals : public SpellScriptLoader
+{
+public:
+ spell_59064_59439_portals() : SpellScriptLoader("spell_59064_59439_portals") { }
+
+ class spell_59064_59439_portals_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_59064_59439_portals_SpellScript);
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetHitUnit()->CastSpell(GetHitUnit(), uint32(GetEffectValue()));
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_59064_59439_portals_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_59064_59439_portals_SpellScript();
+ }
+};
+
void AddSC_quest_spell_scripts()
{
new spell_q55_sacred_cleansing();
@@ -2542,4 +2557,5 @@ void AddSC_quest_spell_scripts()
new spell_q10929_fumping();
new spell_q12414_hand_over_reins();
new spell_q13665_q13790_bested_trigger();
+ new spell_59064_59439_portals();
}
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index affc4d1c26c..1abb6741e0d 100644
--- a/src/server/scripts/Spells/spell_rogue.cpp
+++ b/src/server/scripts/Spells/spell_rogue.cpp
@@ -43,7 +43,8 @@ enum RogueSpells
SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC = 59628,
SPELL_ROGUE_HONOR_AMONG_THIEVES = 51698,
SPELL_ROGUE_HONOR_AMONG_THIEVES_PROC = 52916,
- SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699
+ SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699,
+ SPELL_ROGUE_T10_2P_BONUS = 70804,
};
// 13877, 33735, (check 51211, 65956) - Blade Flurry
@@ -843,6 +844,40 @@ public:
}
};
+// 70805 - Rogue T10 2P Bonus -- THIS SHOULD BE REMOVED WITH NEW PROC SYSTEM.
+class spell_rog_t10_2p_bonus : public SpellScriptLoader
+{
+public:
+ spell_rog_t10_2p_bonus() : SpellScriptLoader("spell_rog_t10_2p_bonus") { }
+
+ class spell_rog_t10_2p_bonus_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_rog_t10_2p_bonus_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_T10_2P_BONUS))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetActor() == eventInfo.GetActionTarget();
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_rog_t10_2p_bonus_AuraScript::CheckProc);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_rog_t10_2p_bonus_AuraScript();
+ }
+};
+
void AddSC_rogue_spell_scripts()
{
new spell_rog_blade_flurry();
@@ -858,4 +893,5 @@ void AddSC_rogue_spell_scripts()
new spell_rog_tricks_of_the_trade_proc();
new spell_rog_honor_among_thieves();
new spell_rog_honor_among_thieves_proc();
+ new spell_rog_t10_2p_bonus();
}
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index 41e72b1388b..ad65c7c6ec7 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -48,8 +48,10 @@ enum ShamanSpells
SPELL_SHAMAN_ITEM_MANA_SURGE = 23571,
SPELL_SHAMAN_LAVA_FLOWS_R1 = 51480,
SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 64694,
+ SPELL_SHAMAN_LIGHTNING_SHIELD_R1 = 26364,
SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE = 52032,
SPELL_SHAMAN_MANA_TIDE_TOTEM = 39609,
+ SPELL_SHAMAN_NATURE_GUARDIAN = 31616,
SPELL_SHAMAN_SATED = 57724,
SPELL_SHAMAN_STORM_EARTH_AND_FIRE = 51483,
SPELL_SHAMAN_TOTEM_EARTHBIND_EARTHGRAB = 64695,
@@ -671,7 +673,7 @@ class spell_sha_heroism : public SpellScriptLoader
}
};
-// 23551 - Lightning Shield
+// 23551 - Lightning Shield T2 Bonus
class spell_sha_item_lightning_shield : public SpellScriptLoader
{
public:
@@ -706,7 +708,7 @@ class spell_sha_item_lightning_shield : public SpellScriptLoader
}
};
-// 23552 - Lightning Shield
+// 23552 - Lightning Shield T2 Bonus
class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader
{
public:
@@ -718,7 +720,7 @@ class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader
bool Validate(SpellInfo const* /*spellInfo*/) override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_MANA_SURGE))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE))
return false;
return true;
}
@@ -753,7 +755,7 @@ class spell_sha_item_mana_surge : public SpellScriptLoader
bool Validate(SpellInfo const* /*spellInfo*/) override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_MANA_SURGE))
return false;
return true;
}
@@ -865,6 +867,51 @@ class spell_sha_lava_lash : public SpellScriptLoader
}
};
+// -324 - Lightning Shield
+class spell_sha_lightning_shield : public SpellScriptLoader
+{
+public:
+ spell_sha_lightning_shield() : SpellScriptLoader("spell_sha_lightning_shield") { }
+
+ class spell_sha_lightning_shield_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_sha_lightning_shield_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LIGHTNING_SHIELD_R1))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetActionTarget())
+ return true;
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_LIGHTNING_SHIELD_R1, aurEff->GetSpellInfo()->GetRank());
+
+ eventInfo.GetActionTarget()->CastSpell(eventInfo.GetActor(), triggerSpell, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_sha_lightning_shield_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_sha_lightning_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_sha_lightning_shield_AuraScript();
+ }
+};
+
// 52031, 52033, 52034, 52035, 52036, 58778, 58779, 58780 - Mana Spring Totem
class spell_sha_mana_spring_totem : public SpellScriptLoader
{
@@ -924,6 +971,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader
void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (Unit* caster = GetCaster())
+ {
if (Unit* unitTarget = GetHitUnit())
{
if (unitTarget->getPowerType() == POWER_MANA)
@@ -938,6 +986,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader
caster->CastCustomSpell(unitTarget, SPELL_SHAMAN_MANA_TIDE_TOTEM, &effBasePoints0, NULL, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID());
}
}
+ }
}
void Register() override
@@ -952,6 +1001,56 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader
}
};
+// -30881 - Nature's Guardian
+class spell_sha_nature_guardian : public SpellScriptLoader
+{
+public:
+ spell_sha_nature_guardian() : SpellScriptLoader("spell_sha_nature_guardian") { }
+
+ class spell_sha_nature_guardian_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_sha_nature_guardian_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_NATURE_GUARDIAN))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ int32 healthpct = aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // %s2 - the 30% threshold for health
+
+ if (Unit* target = eventInfo.GetActionTarget())
+ {
+ if (target->HealthBelowPctDamaged(healthpct, eventInfo.GetDamageInfo()->GetDamage()))
+ {
+
+ uint32 bp = CalculatePct(target->GetMaxHealth(), aurEff->GetAmount());
+ target->CastCustomSpell(SPELL_SHAMAN_NATURE_GUARDIAN, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff);
+
+ // Threat reduction is around 10% confirmed in retail and from wiki
+ Unit* attacker = eventInfo.GetActor();
+ if (attacker->IsAlive())
+ attacker->getThreatManager().modifyThreatPercent(target, -10);
+ }
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_sha_nature_guardian_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_sha_nature_guardian_AuraScript();
+ }
+};
+
// 6495 - Sentry Totem
class spell_sha_sentry_totem : public SpellScriptLoader
{
@@ -1085,8 +1184,10 @@ void AddSC_shaman_spell_scripts()
new spell_sha_item_mana_surge();
new spell_sha_item_t10_elemental_2p_bonus();
new spell_sha_lava_lash();
+ new spell_sha_lightning_shield();
new spell_sha_mana_spring_totem();
new spell_sha_mana_tide_totem();
+ new spell_sha_nature_guardian();
new spell_sha_sentry_totem();
new spell_sha_thunderstorm();
new spell_sha_totemic_mastery();
diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp
index a0a6189cbe2..5e0074bf9f7 100644
--- a/src/server/scripts/Spells/spell_warlock.cpp
+++ b/src/server/scripts/Spells/spell_warlock.cpp
@@ -50,6 +50,12 @@ enum WarlockSpells
SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2 = 60956,
SPELL_WARLOCK_LIFE_TAP_ENERGIZE = 31818,
SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2 = 32553,
+ SPELL_WARLOCK_NETHER_PROTECTION_HOLY = 54370,
+ SPELL_WARLOCK_NETHER_PROTECTION_FIRE = 54371,
+ SPELL_WARLOCK_NETHER_PROTECTION_FROST = 54372,
+ SPELL_WARLOCK_NETHER_PROTECTION_ARCANE = 54373,
+ SPELL_WARLOCK_NETHER_PROTECTION_SHADOW = 54374,
+ SPELL_WARLOCK_NETHER_PROTECTION_NATURE = 54375,
SPELL_WARLOCK_SOULSHATTER = 32835,
SPELL_WARLOCK_SIPHON_LIFE_HEAL = 63106,
SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117
@@ -377,6 +383,8 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader
case CREATURE_FAMILY_IMP:
targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP, true);
break;
+ default:
+ break;
}
}
}
@@ -682,6 +690,95 @@ class spell_warl_life_tap : public SpellScriptLoader
}
};
+// -30299 - Nether Protection
+class spell_warl_nether_protection : public SpellScriptLoader
+{
+public:
+ spell_warl_nether_protection() : SpellScriptLoader("spell_warl_nether_protection") { }
+
+ class spell_warl_nether_protection_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_nether_protection_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_HOLY) ||
+ !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_FIRE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_FROST) ||
+ !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_ARCANE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_SHADOW) ||
+ !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_NATURE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetDamageInfo())
+ {
+ switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_HOLY:
+ case SPELL_SCHOOL_FIRE:
+ case SPELL_SCHOOL_NATURE:
+ case SPELL_SCHOOL_FROST:
+ case SPELL_SCHOOL_SHADOW:
+ case SPELL_SCHOOL_ARCANE:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ uint32 triggerspell = 0;
+
+ switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_HOLY:
+ triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_HOLY;
+ break;
+ case SPELL_SCHOOL_FIRE:
+ triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_FIRE;
+ break;
+ case SPELL_SCHOOL_NATURE:
+ triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_NATURE;
+ break;
+ case SPELL_SCHOOL_FROST:
+ triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_FROST;
+ break;
+ case SPELL_SCHOOL_SHADOW:
+ triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_SHADOW;
+ break;
+ case SPELL_SCHOOL_ARCANE:
+ triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_ARCANE;
+ break;
+ default:
+ return;
+ }
+
+ if (Unit* target = eventInfo.GetActionTarget())
+ target->CastSpell(target, triggerspell, true, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_warl_nether_protection_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_warl_nether_protection_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_warl_nether_protection_AuraScript();
+ }
+};
+
// 18541 - Ritual of Doom Effect
class spell_warl_ritual_of_doom_effect : public SpellScriptLoader
{
@@ -917,6 +1014,7 @@ void AddSC_warlock_spell_scripts()
new spell_warl_haunt();
new spell_warl_health_funnel();
new spell_warl_life_tap();
+ new spell_warl_nether_protection();
new spell_warl_ritual_of_doom_effect();
new spell_warl_seed_of_corruption();
new spell_warl_shadow_ward();
diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp
index 8f2ea5887d2..3db4f41706d 100644
--- a/src/server/scripts/World/go_scripts.cpp
+++ b/src/server/scripts/World/go_scripts.cpp
@@ -1197,6 +1197,29 @@ public:
}
};
+enum MidsummerPoleRibbon
+{
+ SPELL_POLE_DANCE = 29726,
+ SPELL_BLUE_FIRE_RING = 46842,
+ NPC_POLE_RIBBON_BUNNY = 17066,
+ ACTION_COSMETIC_FIRES = 0
+};
+
+class go_midsummer_ribbon_pole : public GameObjectScript
+{
+public:
+ go_midsummer_ribbon_pole() : GameObjectScript("go_midsummer_ribbon_pole") { }
+
+ bool OnGossipHello(Player* player, GameObject* go) override
+ {
+ if (Creature* creature = go->FindNearestCreature(NPC_POLE_RIBBON_BUNNY, 10.0f))
+ {
+ creature->GetAI()->DoAction(ACTION_COSMETIC_FIRES);
+ player->CastSpell(creature, SPELL_POLE_DANCE, true);
+ }
+ return true;
+ }
+};
enum ToyTrainSpells
{
@@ -1274,5 +1297,6 @@ void AddSC_go_scripts()
new go_veil_skith_cage();
new go_frostblade_shrine();
new go_midsummer_bonfire();
+ new go_midsummer_ribbon_pole();
new go_toy_train_set();
}
diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp
index 52174e1b012..ff15698e579 100644
--- a/src/server/scripts/World/item_scripts.cpp
+++ b/src/server/scripts/World/item_scripts.cpp
@@ -241,7 +241,7 @@ public:
float x, y, z;
go->GetClosePoint(x, y, z, go->GetObjectSize() / 3, 7.0f);
- go->SummonGameObject(GO_HIGH_QUALITY_FUR, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ(), 0, 0, 0, 0, 0, 1);
+ go->SummonGameObject(GO_HIGH_QUALITY_FUR, *go, G3D::Quat(), 1);
if (TempSummon* summon = player->SummonCreature(NPC_NESINGWARY_TRAPPER, x, y, z, go->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 1000))
{
summon->SetVisible(false);
diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp
index 159faa38c62..277bc9ac804 100644
--- a/src/server/scripts/World/npcs_special.cpp
+++ b/src/server/scripts/World/npcs_special.cpp
@@ -36,7 +36,6 @@ npc_doctor 100% Gustaf Vanhowzen and Gregory Victor, quest 6622
npc_sayge 100% Darkmoon event fortune teller, buff player based on answers given
npc_snake_trap_serpents 80% AI for snakes that summoned by Snake Trap
npc_shadowfiend 100% restore 5% of owner's mana when shadowfiend die from damage
-npc_locksmith 75% list of keys needs to be confirmed
npc_firework 100% NPC's summoned by rockets and rocket clusters, for making them cast visual
npc_train_wrecker 100% Wind-Up Train Wrecker that kills train set
EndContentData */
@@ -581,6 +580,115 @@ public:
};
/*######
+## npc_midsummer_bunny_pole
+######*/
+
+enum RibbonPoleData
+{
+ GO_RIBBON_POLE = 181605,
+ SPELL_RIBBON_DANCE_COSMETIC = 29726,
+ SPELL_RED_FIRE_RING = 46836,
+ SPELL_BLUE_FIRE_RING = 46842,
+ EVENT_CAST_RED_FIRE_RING = 1,
+ EVENT_CAST_BLUE_FIRE_RING = 2
+};
+
+class npc_midsummer_bunny_pole : public CreatureScript
+{
+public:
+ npc_midsummer_bunny_pole() : CreatureScript("npc_midsummer_bunny_pole") { }
+
+ struct npc_midsummer_bunny_poleAI : public ScriptedAI
+ {
+ npc_midsummer_bunny_poleAI(Creature* creature) : ScriptedAI(creature)
+ {
+ Initialize();
+ }
+
+ void Initialize()
+ {
+ events.Reset();
+ running = false;
+ }
+
+ void Reset() override
+ {
+ Initialize();
+ }
+
+ void DoAction(int32 /*action*/) override
+ {
+ // Don't start event if it's already running.
+ if (running)
+ return;
+
+ running = true;
+ events.ScheduleEvent(EVENT_CAST_RED_FIRE_RING, 1);
+ }
+
+ bool checkNearbyPlayers()
+ {
+ // Returns true if no nearby player has aura "Test Ribbon Pole Channel".
+ std::list<Player*> players;
+ Trinity::UnitAuraCheck check(true, SPELL_RIBBON_DANCE_COSMETIC);
+ Trinity::PlayerListSearcher<Trinity::UnitAuraCheck> searcher(me, players, check);
+ me->VisitNearbyWorldObject(10.0f, searcher);
+
+ return players.empty();
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!running)
+ return;
+
+ events.Update(diff);
+
+ switch (events.ExecuteEvent())
+ {
+ case EVENT_CAST_RED_FIRE_RING:
+ {
+ if (checkNearbyPlayers())
+ {
+ Reset();
+ return;
+ }
+
+ if (GameObject* go = me->FindNearestGameObject(GO_RIBBON_POLE, 10.0f))
+ me->CastSpell(go, SPELL_RED_FIRE_RING, true);
+
+ events.ScheduleEvent(EVENT_CAST_BLUE_FIRE_RING, Seconds(5));
+ }
+ break;
+ case EVENT_CAST_BLUE_FIRE_RING:
+ {
+ if (checkNearbyPlayers())
+ {
+ Reset();
+ return;
+ }
+
+ if (GameObject* go = me->FindNearestGameObject(GO_RIBBON_POLE, 10.0f))
+ me->CastSpell(go, SPELL_BLUE_FIRE_RING, true);
+
+ events.ScheduleEvent(EVENT_CAST_RED_FIRE_RING, Seconds(5));
+ }
+ break;
+ }
+ }
+
+ private:
+ EventMap events;
+ bool running;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_midsummer_bunny_poleAI(creature);
+ }
+};
+
+/*######
## Triage quest
######*/
@@ -1188,36 +1296,49 @@ public:
enum Sayge
{
- SPELL_DMG = 23768, // dmg
- SPELL_RES = 23769, // res
- SPELL_ARM = 23767, // arm
- SPELL_SPI = 23738, // spi
- SPELL_INT = 23766, // int
- SPELL_STM = 23737, // stm
- SPELL_STR = 23735, // str
- SPELL_AGI = 23736, // agi
- SPELL_FORTUNE = 23765 // faire fortune
+ GOSSIP_MENU_OPTION_ID_ANSWER_1 = 0,
+ GOSSIP_MENU_OPTION_ID_ANSWER_2 = 1,
+ GOSSIP_MENU_OPTION_ID_ANSWER_3 = 2,
+ GOSSIP_MENU_OPTION_ID_ANSWER_4 = 3,
+ GOSSIP_I_AM_READY_TO_DISCOVER = 6186,
+ GOSSIP_MENU_OPTION_SAYGE1 = 6185,
+ GOSSIP_MENU_OPTION_SAYGE2 = 6185,
+ GOSSIP_MENU_OPTION_SAYGE3 = 6185,
+ GOSSIP_MENU_OPTION_SAYGE4 = 6185,
+ GOSSIP_MENU_OPTION_SAYGE5 = 6187,
+ GOSSIP_MENU_OPTION_SAYGE6 = 6187,
+ GOSSIP_MENU_OPTION_SAYGE7 = 6187,
+ GOSSIP_MENU_OPTION_SAYGE8 = 6208,
+ GOSSIP_MENU_OPTION_SAYGE9 = 6208,
+ GOSSIP_MENU_OPTION_SAYGE10 = 6208,
+ GOSSIP_MENU_OPTION_SAYGE11 = 6209,
+ GOSSIP_MENU_OPTION_SAYGE12 = 6209,
+ GOSSIP_MENU_OPTION_SAYGE13 = 6209,
+ GOSSIP_MENU_OPTION_SAYGE14 = 6210,
+ GOSSIP_MENU_OPTION_SAYGE15 = 6210,
+ GOSSIP_MENU_OPTION_SAYGE16 = 6210,
+ GOSSIP_MENU_OPTION_SAYGE17 = 6211,
+ GOSSIP_MENU_I_HAVE_LONG_KNOWN = 7339,
+ GOSSIP_MENU_YOU_HAVE_BEEN_TASKED = 7340,
+ GOSSIP_MENU_SWORN_EXECUTIONER = 7341,
+ GOSSIP_MENU_DIPLOMATIC_MISSION = 7361,
+ GOSSIP_MENU_YOUR_BROTHER_SEEKS = 7362,
+ GOSSIP_MENU_A_TERRIBLE_BEAST = 7363,
+ GOSSIP_MENU_YOUR_FORTUNE_IS_CAST = 7364,
+ GOSSIP_MENU_HERE_IS_YOUR_FORTUNE = 7365,
+ GOSSIP_MENU_CANT_GIVE_YOU_YOUR = 7393,
+
+ SPELL_STRENGTH = 23735, // +10% Strength
+ SPELL_AGILITY = 23736, // +10% Agility
+ SPELL_STAMINA = 23737, // +10% Stamina
+ SPELL_SPIRIT = 23738, // +10% Spirit
+ SPELL_INTELLECT = 23766, // +10% Intellect
+ SPELL_ARMOR = 23767, // +10% Armor
+ SPELL_DAMAGE = 23768, // +10% Damage
+ SPELL_RESISTANCE = 23769, // +25 Magic Resistance (All)
+ SPELL_FORTUNE = 23765 // Darkmoon Faire Fortune
};
-#define GOSSIP_HELLO_SAYGE "Yes"
-#define GOSSIP_SENDACTION_SAYGE1 "Slay the Man"
-#define GOSSIP_SENDACTION_SAYGE2 "Turn him over to liege"
-#define GOSSIP_SENDACTION_SAYGE3 "Confiscate the corn"
-#define GOSSIP_SENDACTION_SAYGE4 "Let him go and have the corn"
-#define GOSSIP_SENDACTION_SAYGE5 "Execute your friend painfully"
-#define GOSSIP_SENDACTION_SAYGE6 "Execute your friend painlessly"
-#define GOSSIP_SENDACTION_SAYGE7 "Let your friend go"
-#define GOSSIP_SENDACTION_SAYGE8 "Confront the diplomat"
-#define GOSSIP_SENDACTION_SAYGE9 "Show not so quiet defiance"
-#define GOSSIP_SENDACTION_SAYGE10 "Remain quiet"
-#define GOSSIP_SENDACTION_SAYGE11 "Speak against your brother openly"
-#define GOSSIP_SENDACTION_SAYGE12 "Help your brother in"
-#define GOSSIP_SENDACTION_SAYGE13 "Keep your brother out without letting him know"
-#define GOSSIP_SENDACTION_SAYGE14 "Take credit, keep gold"
-#define GOSSIP_SENDACTION_SAYGE15 "Take credit, share the gold"
-#define GOSSIP_SENDACTION_SAYGE16 "Let the knight take credit"
-#define GOSSIP_SENDACTION_SAYGE17 "Thanks"
-
class npc_sayge : public CreatureScript
{
public:
@@ -1228,19 +1349,19 @@ public:
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
- if (player->GetSpellHistory()->HasCooldown(SPELL_INT) ||
- player->GetSpellHistory()->HasCooldown(SPELL_ARM) ||
- player->GetSpellHistory()->HasCooldown(SPELL_DMG) ||
- player->GetSpellHistory()->HasCooldown(SPELL_RES) ||
- player->GetSpellHistory()->HasCooldown(SPELL_STR) ||
- player->GetSpellHistory()->HasCooldown(SPELL_AGI) ||
- player->GetSpellHistory()->HasCooldown(SPELL_STM) ||
- player->GetSpellHistory()->HasCooldown(SPELL_SPI))
- player->SEND_GOSSIP_MENU(7393, creature->GetGUID());
+ if (player->GetSpellHistory()->HasCooldown(SPELL_STRENGTH) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_AGILITY) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_STAMINA) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_SPIRIT) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_INTELLECT) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_ARMOR) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_DAMAGE) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_RESISTANCE))
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_CANT_GIVE_YOU_YOUR, creature->GetGUID());
else
{
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_SAYGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
- player->SEND_GOSSIP_MENU(7339, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_I_AM_READY_TO_DISCOVER, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_I_HAVE_LONG_KNOWN, creature->GetGUID());
}
return true;
@@ -1251,43 +1372,43 @@ public:
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
- player->SEND_GOSSIP_MENU(7340, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE1, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE2, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE3, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE4, GOSSIP_MENU_OPTION_ID_ANSWER_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_YOU_HAVE_BEEN_TASKED, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 2:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE5, GOSSIP_SENDER_MAIN + 1, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE6, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE7, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF);
- player->SEND_GOSSIP_MENU(7341, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE5, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 1, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE6, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE7, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_SWORN_EXECUTIONER, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 3:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE8, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE9, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE10, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF);
- player->SEND_GOSSIP_MENU(7361, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE8, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE9, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE10,GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_DIPLOMATIC_MISSION, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 4:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE11, GOSSIP_SENDER_MAIN + 6, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE12, GOSSIP_SENDER_MAIN + 7, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE13, GOSSIP_SENDER_MAIN + 8, GOSSIP_ACTION_INFO_DEF);
- player->SEND_GOSSIP_MENU(7362, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE11, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 6, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE12, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 7, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE13, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 8, GOSSIP_ACTION_INFO_DEF);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_YOUR_BROTHER_SEEKS, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 5:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE14, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE15, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE16, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF);
- player->SEND_GOSSIP_MENU(7363, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE14, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE15, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE16, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_A_TERRIBLE_BEAST, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE17, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
- player->SEND_GOSSIP_MENU(7364, creature->GetGUID());
+ player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE17, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_YOUR_FORTUNE_IS_CAST, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 6:
creature->CastSpell(player, SPELL_FORTUNE, false);
- player->SEND_GOSSIP_MENU(7365, creature->GetGUID());
+ player->SEND_GOSSIP_MENU(GOSSIP_MENU_HERE_IS_YOUR_FORTUNE, creature->GetGUID());
break;
}
}
@@ -1302,28 +1423,28 @@ public:
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 1:
- spellId = SPELL_DMG;
+ spellId = SPELL_DAMAGE;
break;
case GOSSIP_SENDER_MAIN + 2:
- spellId = SPELL_RES;
+ spellId = SPELL_RESISTANCE;
break;
case GOSSIP_SENDER_MAIN + 3:
- spellId = SPELL_ARM;
+ spellId = SPELL_ARMOR;
break;
case GOSSIP_SENDER_MAIN + 4:
- spellId = SPELL_SPI;
+ spellId = SPELL_SPIRIT;
break;
case GOSSIP_SENDER_MAIN + 5:
- spellId = SPELL_INT;
+ spellId = SPELL_INTELLECT;
break;
case GOSSIP_SENDER_MAIN + 6:
- spellId = SPELL_STM;
+ spellId = SPELL_STAMINA;
break;
case GOSSIP_SENDER_MAIN + 7:
- spellId = SPELL_STR;
+ spellId = SPELL_STRENGTH;
break;
case GOSSIP_SENDER_MAIN + 8:
- spellId = SPELL_AGI;
+ spellId = SPELL_AGILITY;
break;
}
@@ -1743,149 +1864,6 @@ public:
};
/*######
-## npc_locksmith
-######*/
-
-enum LockSmith
-{
- QUEST_HOW_TO_BRAKE_IN_TO_THE_ARCATRAZ = 10704,
- QUEST_DARK_IRON_LEGACY = 3802,
- QUEST_THE_KEY_TO_SCHOLOMANCE_A = 5505,
- QUEST_THE_KEY_TO_SCHOLOMANCE_H = 5511,
- QUEST_HOTTER_THAN_HELL_A = 10758,
- QUEST_HOTTER_THAN_HELL_H = 10764,
- QUEST_RETURN_TO_KHAGDAR = 9837,
- QUEST_CONTAINMENT = 13159,
- QUEST_ETERNAL_VIGILANCE = 11011,
- QUEST_KEY_TO_THE_FOCUSING_IRIS = 13372,
- QUEST_HC_KEY_TO_THE_FOCUSING_IRIS = 13375,
-
- ITEM_ARCATRAZ_KEY = 31084,
- ITEM_SHADOWFORGE_KEY = 11000,
- ITEM_SKELETON_KEY = 13704,
- ITEM_SHATTERED_HALLS_KEY = 28395,
- ITEM_THE_MASTERS_KEY = 24490,
- ITEM_VIOLET_HOLD_KEY = 42482,
- ITEM_ESSENCE_INFUSED_MOONSTONE = 32449,
- ITEM_KEY_TO_THE_FOCUSING_IRIS = 44582,
- ITEM_HC_KEY_TO_THE_FOCUSING_IRIS = 44581,
-
- SPELL_ARCATRAZ_KEY = 54881,
- SPELL_SHADOWFORGE_KEY = 54882,
- SPELL_SKELETON_KEY = 54883,
- SPELL_SHATTERED_HALLS_KEY = 54884,
- SPELL_THE_MASTERS_KEY = 54885,
- SPELL_VIOLET_HOLD_KEY = 67253,
- SPELL_ESSENCE_INFUSED_MOONSTONE = 40173,
-};
-
-#define GOSSIP_LOST_ARCATRAZ_KEY "I've lost my key to the Arcatraz."
-#define GOSSIP_LOST_SHADOWFORGE_KEY "I've lost my key to the Blackrock Depths."
-#define GOSSIP_LOST_SKELETON_KEY "I've lost my key to the Scholomance."
-#define GOSSIP_LOST_SHATTERED_HALLS_KEY "I've lost my key to the Shattered Halls."
-#define GOSSIP_LOST_THE_MASTERS_KEY "I've lost my key to the Karazhan."
-#define GOSSIP_LOST_VIOLET_HOLD_KEY "I've lost my key to the Violet Hold."
-#define GOSSIP_LOST_ESSENCE_INFUSED_MOONSTONE "I've lost my Essence-Infused Moonstone."
-#define GOSSIP_LOST_KEY_TO_THE_FOCUSING_IRIS "I've lost my Key to the Focusing Iris."
-#define GOSSIP_LOST_HC_KEY_TO_THE_FOCUSING_IRIS "I've lost my Heroic Key to the Focusing Iris."
-
-class npc_locksmith : public CreatureScript
-{
-public:
- npc_locksmith() : CreatureScript("npc_locksmith") { }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- // Arcatraz Key
- if (player->GetQuestRewardStatus(QUEST_HOW_TO_BRAKE_IN_TO_THE_ARCATRAZ) && !player->HasItemCount(ITEM_ARCATRAZ_KEY, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_ARCATRAZ_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
-
- // Shadowforge Key
- if (player->GetQuestRewardStatus(QUEST_DARK_IRON_LEGACY) && !player->HasItemCount(ITEM_SHADOWFORGE_KEY, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_SHADOWFORGE_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
-
- // Skeleton Key
- if ((player->GetQuestRewardStatus(QUEST_THE_KEY_TO_SCHOLOMANCE_A) || player->GetQuestRewardStatus(QUEST_THE_KEY_TO_SCHOLOMANCE_H)) &&
- !player->HasItemCount(ITEM_SKELETON_KEY, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_SKELETON_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
-
- // Shatered Halls Key
- if ((player->GetQuestRewardStatus(QUEST_HOTTER_THAN_HELL_A) || player->GetQuestRewardStatus(QUEST_HOTTER_THAN_HELL_H)) &&
- !player->HasItemCount(ITEM_SHATTERED_HALLS_KEY, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_SHATTERED_HALLS_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
-
- // Master's Key
- if (player->GetQuestRewardStatus(QUEST_RETURN_TO_KHAGDAR) && !player->HasItemCount(ITEM_THE_MASTERS_KEY, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_THE_MASTERS_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
-
- // Violet Hold Key
- if (player->GetQuestRewardStatus(QUEST_CONTAINMENT) && !player->HasItemCount(ITEM_VIOLET_HOLD_KEY, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_VIOLET_HOLD_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
-
- // Essence-Infused Moonstone
- if (player->GetQuestRewardStatus(QUEST_ETERNAL_VIGILANCE) && !player->HasItemCount(ITEM_ESSENCE_INFUSED_MOONSTONE, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_ESSENCE_INFUSED_MOONSTONE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
-
- // Key to the Focusing Iris
- if (player->GetQuestRewardStatus(QUEST_KEY_TO_THE_FOCUSING_IRIS) && !player->HasItemCount(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_KEY_TO_THE_FOCUSING_IRIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
-
- // Heroic Key to the Focusing Iris
- if (player->GetQuestRewardStatus(QUEST_HC_KEY_TO_THE_FOCUSING_IRIS) && !player->HasItemCount(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1, true))
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_HC_KEY_TO_THE_FOCUSING_IRIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-
- bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF + 1:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_ARCATRAZ_KEY, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 2:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_SHADOWFORGE_KEY, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 3:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_SKELETON_KEY, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 4:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_SHATTERED_HALLS_KEY, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 5:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_THE_MASTERS_KEY, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 6:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_VIOLET_HOLD_KEY, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 7:
- player->CLOSE_GOSSIP_MENU();
- player->CastSpell(player, SPELL_ESSENCE_INFUSED_MOONSTONE, false);
- break;
- case GOSSIP_ACTION_INFO_DEF + 8:
- player->CLOSE_GOSSIP_MENU();
- player->AddItem(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1);
- break;
- case GOSSIP_ACTION_INFO_DEF + 9:
- player->CLOSE_GOSSIP_MENU();
- player->AddItem(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1);
- break;
- }
- return true;
- }
-};
-
-/*######
## npc_experience
######*/
@@ -2213,7 +2191,7 @@ public:
float displacement = 0.7f;
for (uint8 i = 0; i < 4; i++)
- me->SummonGameObject(GetFireworkGameObjectId(), me->GetPositionX() + (i%2 == 0 ? displacement : -displacement), me->GetPositionY() + (i > 1 ? displacement : -displacement), me->GetPositionZ() + 4.0f, me->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 1);
+ me->SummonGameObject(GetFireworkGameObjectId(), me->GetPositionX() + (i % 2 == 0 ? displacement : -displacement), me->GetPositionY() + (i > 1 ? displacement : -displacement), me->GetPositionZ() + 4.0f, me->GetOrientation(), G3D::Quat(), 1);
}
else
//me->CastSpell(me, GetFireworkSpell(me->GetEntry()), true);
@@ -2578,6 +2556,7 @@ void AddSC_npcs_special()
new npc_chicken_cluck();
new npc_dancing_flames();
new npc_torch_tossing_target_bunny_controller();
+ new npc_midsummer_bunny_pole();
new npc_doctor();
new npc_injured_patient();
new npc_garments_of_quests();
@@ -2589,7 +2568,6 @@ void AddSC_npcs_special()
new npc_training_dummy();
new npc_wormhole();
new npc_pet_trainer();
- new npc_locksmith();
new npc_experience();
new npc_firework();
new npc_spring_rabbit();
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index f4d736ac675..0241221a2ac 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -52,6 +52,7 @@
#include "Realm/Realm.h"
#include "DatabaseLoader.h"
#include "AppenderDB.h"
+#include "Metric.h"
using namespace boost::program_options;
namespace fs = boost::filesystem;
@@ -196,6 +197,13 @@ extern int main(int argc, char** argv)
LoadRealmInfo();
+ sMetric->Initialize(realm.Name, _ioService, []()
+ {
+ TC_METRIC_VALUE("online_players", sWorld->GetPlayerCount());
+ });
+
+ TC_METRIC_EVENT("events", "Worldserver started", "");
+
// Initialize the World
sScriptMgr->SetScriptLoader(AddScripts);
sWorld->SetInitialWorldSettings();
@@ -295,6 +303,9 @@ extern int main(int argc, char** argv)
StopDB();
+ TC_METRIC_EVENT("events", "Worldserver shutdown", "");
+ sMetric->ForceSend();
+
TC_LOG_INFO("server.worldserver", "Halting process...");
ShutdownCLIThread(cliThread);
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index ccb784f8c2b..0513f2b0ab4 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -34,6 +34,7 @@
# AUCTION HOUSE BOT BUYER CONFIG
# LOGGING SYSTEM SETTINGS
# PACKET SPOOF PROTECTION SETTINGS
+# METRIC SETTINGS
#
###################################################################################################
@@ -94,6 +95,12 @@ LogsDir = ""
# Default: "127.0.0.1;3306;trinity;trinity;auth" - (LoginDatabaseInfo)
# "127.0.0.1;3306;trinity;trinity;world" - (WorldDatabaseInfo)
# "127.0.0.1;3306;trinity;trinity;characters" - (CharacterDatabaseInfo)
+#
+# Don't change hostname unless you are hosting mysql on a different machine, if you need help
+# with configuration allowing to connect from diferent machine than the one running server
+# search for TCE00016 on forum.
+# Don't open port on firewall to external connections (it belongs to mysql, not to wow server).
+# The username you choose must have permisions to create/alter/rename tables.
LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth"
WorldDatabaseInfo = "127.0.0.1;3306;trinity;trinity;world"
@@ -1734,24 +1741,27 @@ Channel.RestrictedLfg = 1
#
# ChatLevelReq.Channel
-# Description: Level requirement for characters to be able to write in chat channels.
-# Default: 1
-
-ChatLevelReq.Channel = 1
-
-#
# ChatLevelReq.Whisper
-# Description: Level requirement for characters to be able to whisper other characters.
+# ChatLevelReq.Emote
+# ChatLevelReq.Say
+# ChatLevelReq.Yell
+# Description: Level requirement for characters to be able to use chats.
# Default: 1
+ChatLevelReq.Channel = 1
ChatLevelReq.Whisper = 1
+ChatLevelReq.Emote = 1
+ChatLevelReq.Say = 1
+ChatLevelReq.Yell = 1
#
-# ChatLevelReq.Say
-# Description: Level requirement for characters to be able to use say/yell/emote.
+# PartyLevelReq
+# Description: Minimum level at which players can invite to group, even if they aren't on
+# the invitee friends list. (Players who are on that friend list can always
+# invite despite having lower level)
# Default: 1
-ChatLevelReq.Say = 1
+PartyLevelReq = 1
#
# PreserveCustomChannels
@@ -2444,6 +2454,14 @@ Battleground.RewardLoserHonorFirst = 5
Battleground.RewardLoserHonorLast = 5
#
+# Battleground.ReportAFK
+# Description: Number of reports needed to kick someone AFK from Battleground.
+# Range: 1-9
+# Default: 3
+
+Battleground.ReportAFK = 3
+
+#
###################################################################################################
###################################################################################################
@@ -3028,6 +3046,16 @@ NoGrayAggro.Above = 0
NoGrayAggro.Below = 0
#
+# PreventRenameCharacterOnCustomization
+# Description: If option is set to 1, player can not rename the character in character customization.
+# Applies to all character customization commands.
+# Default: 0 - (Disabled, character can be renamed in Character Customization)
+# 1 - (Enabled, character can not be renamed in Character Customization)
+#
+
+PreventRenameCharacterOnCustomization = 0
+
+#
###################################################################################################
###################################################################################################
@@ -3283,7 +3311,7 @@ AuctionHouseBot.Items.Amount.Yellow = 0
# Armor: 8
# Reagent: 1
# Projectile: 2
-# TradeGod: 10
+# TradeGood: 10
# Generic: 1
# Recipe: 6
# Quiver: 1
@@ -3608,6 +3636,7 @@ Logger.mmaps=3,Server
#Logger.bg.arena=3,Console Server
#Logger.bg.battlefield=3,Console Server
#Logger.bg.battleground=3,Console Server
+#Logger.bg.reportpvpafk=3,Console Server
#Logger.chat.log=3,Console Server
#Logger.calendar=3,Console Server
#Logger.chat.system=3,Console Server
@@ -3700,3 +3729,42 @@ PacketSpoof.BanDuration = 86400
#
###################################################################################################
+
+###################################################################################################
+# METRIC SETTINGS
+#
+# These settings control the statistics sent to the metric database (currently InfluxDB)
+#
+# Metric.Enable
+# Description: Enables statistics sent to the metric database.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Metric.Enable = 0
+
+#
+# Metric.Interval
+# Description: Interval between every batch of data sent in seconds
+# Default: 10 seconds
+#
+
+Metric.Interval = 10
+
+#
+# Metric.ConnectionInfo
+# Description: Connection settings for metric database (currently InfluxDB).
+# Example: "hostname;port;database"
+# Default: "127.0.0.1;8086;worldserver"
+
+Metric.ConnectionInfo = "127.0.0.1;8086;worldserver"
+
+#
+# Metric.OverallStatusInterval
+# Description: Interval between every gathering of overall worldserver status data in seconds
+# Default: 1 second
+#
+
+Metric.OverallStatusInterval = 1
+
+#
+###################################################################################################