aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt20
-rw-r--r--src/common/Collision/BoundingIntervalHierarchy.h2
-rw-r--r--src/common/Collision/DynamicTree.h2
-rw-r--r--src/common/Collision/Management/IVMapManager.h2
-rw-r--r--src/common/Collision/Management/MMapFactory.h2
-rw-r--r--src/common/Collision/Management/MMapManager.h4
-rw-r--r--src/common/Collision/Management/VMapFactory.h2
-rw-r--r--src/common/Collision/Management/VMapManager2.h4
-rw-r--r--src/common/Collision/Maps/MapTree.h6
-rw-r--r--src/common/Collision/Maps/TileAssembler.h10
-rw-r--r--src/common/Collision/Models/GameObjectModel.h6
-rw-r--r--src/common/Collision/Models/ModelInstance.h4
-rw-r--r--src/common/Collision/Models/WorldModel.h8
-rw-r--r--src/common/Collision/RegularGrid.h2
-rw-r--r--src/common/Common.h43
-rw-r--r--src/common/Configuration/BuiltInConfig.h9
-rw-r--r--src/common/Configuration/Config.cpp66
-rw-r--r--src/common/Configuration/Config.h20
-rw-r--r--src/common/Cryptography/ARC4.h2
-rw-r--r--src/common/Cryptography/Authentication/AuthCrypt.h2
-rw-r--r--src/common/Cryptography/BigNumber.h2
-rw-r--r--src/common/Cryptography/HMACSHA1.h2
-rw-r--r--src/common/Cryptography/OpenSSLCrypto.h8
-rw-r--r--src/common/Cryptography/SHA1.cpp8
-rw-r--r--src/common/Cryptography/SHA1.h7
-rw-r--r--src/common/Debugging/Errors.h14
-rw-r--r--src/common/Define.h39
-rw-r--r--src/common/GitRevision.cpp1
-rw-r--r--src/common/GitRevision.h27
-rw-r--r--src/common/Logging/Appender.h8
-rw-r--r--src/common/Logging/AppenderConsole.h2
-rw-r--r--src/common/Logging/AppenderFile.h2
-rw-r--r--src/common/Logging/Log.h2
-rw-r--r--src/common/Logging/LogOperation.h3
-rw-r--r--src/common/Logging/Logger.h2
-rw-r--r--src/common/Utilities/Containers.h23
-rw-r--r--src/common/Utilities/EventMap.h14
-rw-r--r--src/common/Utilities/EventProcessor.h4
-rw-r--r--src/common/Utilities/MessageBuffer.h (renamed from src/server/shared/Networking/MessageBuffer.h)1
-rw-r--r--src/common/Utilities/Random.h18
-rw-r--r--src/common/Utilities/StartProcess.cpp269
-rw-r--r--src/common/Utilities/StartProcess.h67
-rw-r--r--src/common/Utilities/TaskScheduler.h8
-rw-r--r--src/common/Utilities/Timer.h4
-rw-r--r--src/common/Utilities/Util.cpp65
-rw-r--r--src/common/Utilities/Util.h50
-rw-r--r--src/server/authserver/CMakeLists.txt2
-rw-r--r--src/server/authserver/Main.cpp17
-rw-r--r--src/server/authserver/Server/AuthSession.cpp37
-rw-r--r--src/server/authserver/Server/AuthSession.h2
-rw-r--r--src/server/authserver/Server/AuthSocketMgr.h4
-rw-r--r--src/server/authserver/authserver.conf.dist8
-rw-r--r--src/server/database/CMakeLists.txt17
-rw-r--r--src/server/database/Database/AdhocStatement.h2
-rw-r--r--src/server/database/Database/DatabaseEnv.h6
-rw-r--r--src/server/database/Database/DatabaseLoader.cpp12
-rw-r--r--src/server/database/Database/DatabaseLoader.h2
-rw-r--r--src/server/database/Database/DatabaseWorker.h2
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.cpp8
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h6
-rw-r--r--src/server/database/Database/Field.h4
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h2
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h2
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.h2
-rw-r--r--src/server/database/Database/MySQLConnection.h4
-rw-r--r--src/server/database/Database/MySQLThreading.h2
-rw-r--r--src/server/database/Database/PreparedStatement.cpp2
-rw-r--r--src/server/database/Database/PreparedStatement.h6
-rw-r--r--src/server/database/Database/QueryCallback.h (renamed from src/common/Threading/Callback.h)6
-rw-r--r--src/server/database/Database/QueryHolder.h4
-rw-r--r--src/server/database/Database/QueryResult.cpp2
-rw-r--r--src/server/database/Database/QueryResult.h4
-rw-r--r--src/server/database/Database/SQLOperation.h2
-rw-r--r--src/server/database/Database/Transaction.h4
-rw-r--r--src/server/database/Logging/AppenderDB.h2
-rw-r--r--src/server/database/Updater/DBUpdater.cpp78
-rw-r--r--src/server/database/Updater/DBUpdater.h6
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp18
-rw-r--r--src/server/database/Updater/UpdateFetcher.h3
-rw-r--r--src/server/game/AI/CoreAI/CombatAI.cpp2
-rw-r--r--src/server/game/AI/CoreAI/CombatAI.h12
-rw-r--r--src/server/game/AI/CoreAI/GameObjectAI.h4
-rw-r--r--src/server/game/AI/CoreAI/GuardAI.h2
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.cpp6
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.h12
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp31
-rw-r--r--src/server/game/AI/CoreAI/PetAI.h4
-rw-r--r--src/server/game/AI/CoreAI/ReactorAI.h2
-rw-r--r--src/server/game/AI/CoreAI/TotemAI.h2
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h9
-rw-r--r--src/server/game/AI/CreatureAI.cpp37
-rw-r--r--src/server/game/AI/CreatureAI.h11
-rw-r--r--src/server/game/AI/CreatureAISelector.h6
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp2
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.h2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp50
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h26
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp15
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h4
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp20
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h4
-rw-r--r--src/server/game/Accounts/AccountMgr.h2
-rw-r--r--src/server/game/Accounts/RBAC.h10
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp13
-rw-r--r--src/server/game/Achievements/AchievementMgr.h6
-rw-r--r--src/server/game/AuctionHouse/AuctionHouseMgr.h6
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBot.h4
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h2
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp41
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h2
-rw-r--r--src/server/game/Battlefield/Battlefield.h6
-rw-r--r--src/server/game/Battlefield/BattlefieldMgr.h2
-rw-r--r--src/server/game/Battlefield/Zones/BattlefieldWG.h6
-rw-r--r--src/server/game/Battlegrounds/Arena.h2
-rw-r--r--src/server/game/Battlegrounds/ArenaScore.h4
-rw-r--r--src/server/game/Battlegrounds/ArenaTeam.h4
-rw-r--r--src/server/game/Battlegrounds/ArenaTeamMgr.h2
-rw-r--r--src/server/game/Battlegrounds/Battleground.h2
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.h2
-rw-r--r--src/server/game/Battlegrounds/BattlegroundQueue.cpp10
-rw-r--r--src/server/game/Battlegrounds/BattlegroundQueue.h6
-rw-r--r--src/server/game/CMakeLists.txt41
-rw-r--r--src/server/game/Calendar/CalendarMgr.h6
-rw-r--r--src/server/game/Chat/Channels/Channel.h2
-rw-r--r--src/server/game/Chat/Channels/ChannelMgr.h2
-rw-r--r--src/server/game/Chat/Chat.cpp26
-rw-r--r--src/server/game/Chat/Chat.h10
-rw-r--r--src/server/game/Chat/ChatLink.h20
-rw-r--r--src/server/game/Combat/HostileRefManager.h2
-rw-r--r--src/server/game/Combat/ThreatManager.h8
-rw-r--r--src/server/game/Combat/UnitEvents.h2
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp6
-rw-r--r--src/server/game/Conditions/ConditionMgr.h6
-rw-r--r--src/server/game/Conditions/DisableMgr.h10
-rw-r--r--src/server/game/DataStores/DBCEnums.h16
-rw-r--r--src/server/game/DataStores/DBCStores.cpp5
-rw-r--r--src/server/game/DataStores/DBCStores.h289
-rw-r--r--src/server/game/DataStores/DBCStructure.h16
-rw-r--r--src/server/game/DataStores/DBCfmt.h3
-rw-r--r--src/server/game/DataStores/M2Stores.cpp266
-rw-r--r--src/server/game/DataStores/M2Stores.h36
-rw-r--r--src/server/game/DataStores/M2Structure.h133
-rw-r--r--src/server/game/DungeonFinding/LFG.h6
-rw-r--r--src/server/game/DungeonFinding/LFGGroupData.h2
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp4
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.h2
-rw-r--r--src/server/game/DungeonFinding/LFGPlayerData.h2
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.cpp12
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.h5
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.cpp6
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.h6
-rw-r--r--src/server/game/Entities/Corpse/Corpse.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp202
-rw-r--r--src/server/game/Entities/Creature/Creature.h56
-rw-r--r--src/server/game/Entities/Creature/CreatureGroups.h4
-rw-r--r--src/server/game/Entities/Creature/GossipDef.h6
-rw-r--r--src/server/game/Entities/Creature/TemporarySummon.h10
-rw-r--r--src/server/game/Entities/DynamicObject/DynamicObject.h2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp54
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h6
-rw-r--r--src/server/game/Entities/Item/Container/Bag.h2
-rw-r--r--src/server/game/Entities/Item/Item.cpp34
-rw-r--r--src/server/game/Entities/Item/Item.h2
-rw-r--r--src/server/game/Entities/Item/ItemEnchantmentMgr.h7
-rw-r--r--src/server/game/Entities/Item/ItemPrototype.cpp121
-rw-r--r--src/server/game/Entities/Item/ItemPrototype.h68
-rw-r--r--src/server/game/Entities/Object/Object.cpp31
-rw-r--r--src/server/game/Entities/Object/Object.h19
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.cpp17
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h19
-rw-r--r--src/server/game/Entities/Object/ObjectPosSelector.h2
-rw-r--r--src/server/game/Entities/Object/Position.h14
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFieldFlags.h10
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp11
-rw-r--r--src/server/game/Entities/Pet/Pet.h2
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.cpp174
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.h60
-rw-r--r--src/server/game/Entities/Player/KillRewarder.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp403
-rw-r--r--src/server/game/Entities/Player/Player.h213
-rw-r--r--src/server/game/Entities/Player/SocialMgr.h2
-rw-r--r--src/server/game/Entities/Player/TradeData.h2
-rw-r--r--src/server/game/Entities/Totem/Totem.h2
-rw-r--r--src/server/game/Entities/Transport/Transport.h2
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp4
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp900
-rw-r--r--src/server/game/Entities/Unit/Unit.h87
-rw-r--r--src/server/game/Entities/Vehicle/Vehicle.h4
-rw-r--r--src/server/game/Events/GameEventMgr.h6
-rw-r--r--src/server/game/Globals/ObjectAccessor.cpp63
-rw-r--r--src/server/game/Globals/ObjectAccessor.h83
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp130
-rw-r--r--src/server/game/Globals/ObjectMgr.h17
-rw-r--r--src/server/game/Grids/GridStates.h10
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h20
-rw-r--r--src/server/game/Grids/ObjectGridLoader.h6
-rw-r--r--src/server/game/Groups/Group.h2
-rw-r--r--src/server/game/Groups/GroupMgr.h2
-rw-r--r--src/server/game/Groups/GroupReference.h2
-rw-r--r--src/server/game/Guilds/Guild.h4
-rw-r--r--src/server/game/Guilds/GuildMgr.h2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp25
-rw-r--r--src/server/game/Handlers/GroupHandler.cpp19
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp36
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp2
-rw-r--r--src/server/game/Handlers/QueryHandler.cpp4
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp10
-rw-r--r--src/server/game/Instances/InstanceSaveMgr.h4
-rw-r--r--src/server/game/Instances/InstanceScript.cpp14
-rw-r--r--src/server/game/Instances/InstanceScript.h12
-rw-r--r--src/server/game/Loot/LootMgr.cpp17
-rw-r--r--src/server/game/Loot/LootMgr.h85
-rw-r--r--src/server/game/Mails/Mail.h8
-rw-r--r--src/server/game/Maps/AreaBoundary.h16
-rw-r--r--src/server/game/Maps/Map.cpp36
-rw-r--r--src/server/game/Maps/Map.h12
-rw-r--r--src/server/game/Maps/MapInstanced.h2
-rw-r--r--src/server/game/Maps/MapManager.cpp2
-rw-r--r--src/server/game/Maps/MapManager.h10
-rw-r--r--src/server/game/Maps/MapScripts.cpp (renamed from src/server/game/Scripting/MapScripts.cpp)7
-rw-r--r--src/server/game/Maps/MapUpdater.h2
-rw-r--r--src/server/game/Maps/TransportMgr.h6
-rw-r--r--src/server/game/Miscellaneous/Language.h10
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h70
-rw-r--r--src/server/game/Movement/MotionMaster.cpp21
-rw-r--r--src/server/game/Movement/MotionMaster.h8
-rwxr-xr-xsrc/server/game/Movement/MovementGenerator.h2
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp5
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/IdleMovementGenerator.h2
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp4
-rw-r--r--src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp25
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp2
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h2
-rw-r--r--src/server/game/Movement/PathGenerator.h2
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.h2
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.h4
-rw-r--r--src/server/game/Movement/Spline/MovementTypedefs.h4
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.h2
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.h4
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp6
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvPMgr.h2
-rw-r--r--src/server/game/Pools/PoolMgr.cpp5
-rw-r--r--src/server/game/Pools/PoolMgr.h6
-rw-r--r--src/server/game/Quests/QuestDef.h4
-rw-r--r--src/server/game/Reputation/ReputationMgr.h2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp1178
-rw-r--r--src/server/game/Scripting/ScriptMgr.h126
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.cpp1609
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.h81
-rw-r--r--src/server/game/Scripting/ScriptSystem.cpp2
-rw-r--r--src/server/game/Scripting/ScriptSystem.h2
-rw-r--r--src/server/game/Server/Protocol/Opcodes.h2
-rw-r--r--src/server/game/Server/Protocol/PacketLog.cpp4
-rw-r--r--src/server/game/Server/Protocol/PacketLog.h2
-rw-r--r--src/server/game/Server/WorldSession.h2
-rw-r--r--src/server/game/Server/WorldSocket.cpp3
-rw-r--r--src/server/game/Server/WorldSocket.h2
-rw-r--r--src/server/game/Server/WorldSocketMgr.cpp4
-rw-r--r--src/server/game/Server/WorldSocketMgr.h4
-rw-r--r--src/server/game/Skills/SkillDiscovery.h9
-rw-r--r--src/server/game/Skills/SkillExtraItems.h10
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h2
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp53
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.h2
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp32
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h20
-rw-r--r--src/server/game/Spells/Spell.cpp128
-rw-r--r--src/server/game/Spells/Spell.h26
-rw-r--r--src/server/game/Spells/SpellEffects.cpp66
-rw-r--r--src/server/game/Spells/SpellHistory.cpp10
-rw-r--r--src/server/game/Spells/SpellHistory.h2
-rw-r--r--src/server/game/Spells/SpellInfo.cpp11
-rw-r--r--src/server/game/Spells/SpellInfo.h7
-rw-r--r--src/server/game/Spells/SpellMgr.cpp133
-rw-r--r--src/server/game/Spells/SpellMgr.h44
-rw-r--r--src/server/game/Spells/SpellScript.cpp7
-rw-r--r--src/server/game/Spells/SpellScript.h71
-rw-r--r--src/server/game/Texts/CreatureTextMgr.h2
-rw-r--r--src/server/game/Tickets/TicketMgr.h4
-rw-r--r--src/server/game/Tools/CharacterDatabaseCleaner.h22
-rw-r--r--src/server/game/Tools/PlayerDump.cpp10
-rw-r--r--src/server/game/Tools/PlayerDump.h6
-rw-r--r--src/server/game/Warden/Warden.h2
-rw-r--r--src/server/game/Warden/WardenCheckMgr.h2
-rw-r--r--src/server/game/Warden/WardenMac.h2
-rw-r--r--src/server/game/Warden/WardenWin.h2
-rw-r--r--src/server/game/Weather/Weather.h2
-rw-r--r--src/server/game/Weather/WeatherMgr.h12
-rw-r--r--src/server/game/World/World.cpp43
-rw-r--r--src/server/game/World/World.h19
-rw-r--r--src/server/scripts/CMakeLists.txt255
-rw-r--r--src/server/scripts/Commands/cs_account.cpp10
-rw-r--r--src/server/scripts/Commands/cs_character.cpp2
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp20
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp2
-rw-r--r--src/server/scripts/Commands/cs_modify.cpp460
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp117
-rw-r--r--src/server/scripts/Commands/cs_pet.cpp59
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp12
-rw-r--r--src/server/scripts/Commands/cs_reset.cpp2
-rw-r--r--src/server/scripts/Commands/cs_server.cpp78
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp34
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp11
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp32
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp14
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp833
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h3
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp9
-rw-r--r--src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp2
-rw-r--r--src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp16
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp46
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp52
-rw-r--r--src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp6
-rw-r--r--src/server/scripts/Kalimdor/kalimdor_script_loader.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_azshara.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_desolace.cpp30
-rw-r--r--src/server/scripts/Kalimdor/zone_durotar.cpp4
-rw-r--r--src/server/scripts/Kalimdor/zone_the_barrens.cpp45
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp6
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp4
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp51
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp2
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp2
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp30
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp6
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp14
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp57
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp4
-rw-r--r--src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp132
-rw-r--r--src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp (renamed from src/server/scripts/Northrend/isle_of_conquest.cpp)0
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp29
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp18
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp42
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp429
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp28
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp14
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp104
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp94
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp22
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_noth.cpp40
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp12
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp22
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp501
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp114
-rw-r--r--src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp156
-rw-r--r--src/server/scripts/Northrend/Naxxramas/naxxramas.h16
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp14
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp38
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp8
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp6
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp6
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp2
-rw-r--r--src/server/scripts/Northrend/northrend_script_loader.cpp6
-rw-r--r--src/server/scripts/Northrend/zone_grizzly_hills.cpp260
-rw-r--r--src/server/scripts/Northrend/zone_icecrown.cpp2
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_illidan.cpp36
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp4
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_supremus.cpp4
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp4
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp2
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp1052
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp120
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h38
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp2
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp19
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp6
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp6
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp151
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp146
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp2
-rw-r--r--src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp2
-rw-r--r--src/server/scripts/Outland/outland_script_loader.cpp2
-rw-r--r--src/server/scripts/Outland/zone_hellfire_peninsula.cpp8
-rw-r--r--src/server/scripts/Outland/zone_shattrath_city.cpp201
-rw-r--r--src/server/scripts/Pet/pet_dk.cpp4
-rw-r--r--src/server/scripts/Pet/pet_generic.cpp65
-rw-r--r--src/server/scripts/ScriptLoader.cpp51
-rw-r--r--src/server/scripts/ScriptLoader.cpp.in.cmake64
-rw-r--r--src/server/scripts/ScriptPCH.cpp (renamed from src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp)0
-rw-r--r--src/server/scripts/ScriptPCH.h (renamed from src/server/scripts/PrecompiledHeaders/ScriptPCH.h)0
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp288
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp231
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp95
-rw-r--r--src/server/scripts/Spells/spell_holiday.cpp4
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp108
-rw-r--r--src/server/scripts/Spells/spell_item.cpp505
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp42
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp59
-rw-r--r--src/server/scripts/Spells/spell_pet.cpp6
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp48
-rw-r--r--src/server/scripts/Spells/spell_quest.cpp2
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp40
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp109
-rw-r--r--src/server/scripts/Spells/spell_warlock.cpp100
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp2
-rw-r--r--src/server/scripts/World/boss_emerald_dragons.cpp2
-rw-r--r--src/server/scripts/World/duel_reset.cpp4
-rw-r--r--src/server/scripts/World/go_scripts.cpp2
-rw-r--r--src/server/scripts/World/item_scripts.cpp14
-rw-r--r--src/server/shared/CMakeLists.txt16
-rw-r--r--src/server/shared/DataStores/DBCFileLoader.h2
-rw-r--r--src/server/shared/Networking/SocketMgr.h12
-rw-r--r--src/server/shared/Packets/ByteBuffer.h17
-rw-r--r--src/server/shared/Realm/Realm.cpp35
-rw-r--r--src/server/shared/Realm/Realm.h24
-rw-r--r--src/server/shared/Realm/RealmList.cpp (renamed from src/server/authserver/Realms/RealmList.cpp)52
-rw-r--r--src/server/shared/Realm/RealmList.h (renamed from src/server/authserver/Realms/RealmList.h)12
-rw-r--r--src/server/worldserver/CMakeLists.txt8
-rw-r--r--src/server/worldserver/Main.cpp29
-rw-r--r--src/server/worldserver/worldserver.conf.dist128
-rw-r--r--src/tools/map_extractor/System.cpp74
434 files changed, 12986 insertions, 5289 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 6f13dc821a9..0428738f2dd 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -30,9 +30,11 @@ endif (USE_COREPCH)
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+add_definitions(-DTRINITY_API_EXPORT_COMMON)
+
add_library(common
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
# Do NOT add any extra include directory here, as we don't want the common
@@ -69,7 +71,9 @@ target_link_libraries(common
openssl
valgrind
threads
- jemalloc)
+ jemalloc
+ PRIVATE
+ process)
add_dependencies(common revision_data.h)
@@ -78,6 +82,18 @@ set_target_properties(common
FOLDER
"server")
+if( BUILD_SHARED_LIBS )
+ if( UNIX )
+ install(TARGETS common
+ LIBRARY
+ DESTINATION lib)
+ elseif( WIN32 )
+ install(TARGETS common
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+endif()
+
# Generate precompiled header
if (USE_COREPCH)
add_cxx_pch(common ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
diff --git a/src/common/Collision/BoundingIntervalHierarchy.h b/src/common/Collision/BoundingIntervalHierarchy.h
index 0b15bd2d92f..eb0a102806f 100644
--- a/src/common/Collision/BoundingIntervalHierarchy.h
+++ b/src/common/Collision/BoundingIntervalHierarchy.h
@@ -67,7 +67,7 @@ struct AABound
Copyright (c) 2003-2007 Christopher Kulla
*/
-class BIH
+class TC_COMMON_API BIH
{
private:
void init_empty()
diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h
index 0f18bb265f8..85707efebd2 100644
--- a/src/common/Collision/DynamicTree.h
+++ b/src/common/Collision/DynamicTree.h
@@ -31,7 +31,7 @@ namespace G3D
class GameObjectModel;
struct DynTreeImpl;
-class DynamicMapTree
+class TC_COMMON_API DynamicMapTree
{
DynTreeImpl *impl;
diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h
index 1e64551956c..35437b7b816 100644
--- a/src/common/Collision/Management/IVMapManager.h
+++ b/src/common/Collision/Management/IVMapManager.h
@@ -42,7 +42,7 @@ namespace VMAP
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
//===========================================================
- class IVMapManager
+ class TC_COMMON_API IVMapManager
{
private:
bool iEnableLineOfSightCalc;
diff --git a/src/common/Collision/Management/MMapFactory.h b/src/common/Collision/Management/MMapFactory.h
index edd074fc93d..6dda7a40a22 100644
--- a/src/common/Collision/Management/MMapFactory.h
+++ b/src/common/Collision/Management/MMapFactory.h
@@ -38,7 +38,7 @@ namespace MMAP
// static class
// holds all mmap global data
// access point to MMapManager singleton
- class MMapFactory
+ class TC_COMMON_API MMapFactory
{
public:
static MMapManager* createOrGetMMapManager();
diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/Collision/Management/MMapManager.h
index 1a502a916dd..a8d792296bb 100644
--- a/src/common/Collision/Management/MMapManager.h
+++ b/src/common/Collision/Management/MMapManager.h
@@ -58,7 +58,7 @@ namespace MMAP
// singleton class
// holds all all access to mmap loading unloading and meshes
- class MMapManager
+ class TC_COMMON_API MMapManager
{
public:
MMapManager() : loadedTiles(0), thread_safe_environment(true) {}
@@ -75,7 +75,7 @@ namespace MMAP
dtNavMesh const* GetNavMesh(uint32 mapId);
uint32 getLoadedTilesCount() const { return loadedTiles; }
- uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ uint32 getLoadedMapsCount() const { return uint32(loadedMMaps.size()); }
private:
bool loadMapData(uint32 mapId);
uint32 packTileID(int32 x, int32 y);
diff --git a/src/common/Collision/Management/VMapFactory.h b/src/common/Collision/Management/VMapFactory.h
index 1a45bd5094b..a730fa12ef2 100644
--- a/src/common/Collision/Management/VMapFactory.h
+++ b/src/common/Collision/Management/VMapFactory.h
@@ -29,7 +29,7 @@ namespace VMAP
{
//===========================================================
- class VMapFactory
+ class TC_COMMON_API VMapFactory
{
public:
static IVMapManager* createOrGetVMapManager();
diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h
index 722ac4935c6..61c5025e41f 100644
--- a/src/common/Collision/Management/VMapManager2.h
+++ b/src/common/Collision/Management/VMapManager2.h
@@ -51,7 +51,7 @@ namespace VMAP
class StaticMapTree;
class WorldModel;
- class ManagedModel
+ class TC_COMMON_API ManagedModel
{
public:
ManagedModel() : iModel(nullptr), iRefCount(0) { }
@@ -75,7 +75,7 @@ namespace VMAP
VMAP_DISABLE_LIQUIDSTATUS = 0x8
};
- class VMapManager2 : public IVMapManager
+ class TC_COMMON_API VMapManager2 : public IVMapManager
{
protected:
// Tree to check collision
diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h
index 63d542754c1..c6a283cac4d 100644
--- a/src/common/Collision/Maps/MapTree.h
+++ b/src/common/Collision/Maps/MapTree.h
@@ -29,7 +29,7 @@ namespace VMAP
class GroupModel;
class VMapManager2;
- struct LocationInfo
+ struct TC_COMMON_API LocationInfo
{
LocationInfo(): hitInstance(nullptr), hitModel(nullptr), ground_Z(-G3D::finf()) { }
const ModelInstance* hitInstance;
@@ -37,7 +37,7 @@ namespace VMAP
float ground_Z;
};
- class StaticMapTree
+ class TC_COMMON_API StaticMapTree
{
typedef std::unordered_map<uint32, bool> loadedTileMap;
typedef std::unordered_map<uint32, uint32> loadedSpawnMap;
@@ -87,7 +87,7 @@ namespace VMAP
StaticMapTree& operator=(StaticMapTree const& right) = delete;
};
- struct AreaInfo
+ struct TC_COMMON_API AreaInfo
{
AreaInfo(): result(false), ground_Z(-G3D::finf()), flags(0), adtId(0),
rootId(0), groupId(0) { }
diff --git a/src/common/Collision/Maps/TileAssembler.h b/src/common/Collision/Maps/TileAssembler.h
index 1e2dc1924f1..74111f69910 100644
--- a/src/common/Collision/Maps/TileAssembler.h
+++ b/src/common/Collision/Maps/TileAssembler.h
@@ -35,7 +35,7 @@ namespace VMAP
*/
//===============================================
- class ModelPosition
+ class TC_COMMON_API ModelPosition
{
private:
G3D::Matrix3 iRotation;
@@ -55,7 +55,7 @@ namespace VMAP
typedef std::map<uint32, ModelSpawn> UniqueEntryMap;
typedef std::multimap<uint32, uint32> TileMap;
- struct MapSpawns
+ struct TC_COMMON_API MapSpawns
{
UniqueEntryMap UniqueEntries;
TileMap TileEntries;
@@ -64,7 +64,7 @@ namespace VMAP
typedef std::map<uint32, MapSpawns*> MapData;
//===============================================
- struct GroupModel_Raw
+ struct TC_COMMON_API GroupModel_Raw
{
uint32 mogpflags;
uint32 GroupWMOID;
@@ -82,7 +82,7 @@ namespace VMAP
bool Read(FILE* f);
};
- struct WorldModel_Raw
+ struct TC_COMMON_API WorldModel_Raw
{
uint32 RootWMOID;
std::vector<GroupModel_Raw> groupsArray;
@@ -90,7 +90,7 @@ namespace VMAP
bool Read(const char * path);
};
- class TileAssembler
+ class TC_COMMON_API TileAssembler
{
private:
std::string iDestDir;
diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h
index 7834f53c63a..37f11af20ac 100644
--- a/src/common/Collision/Models/GameObjectModel.h
+++ b/src/common/Collision/Models/GameObjectModel.h
@@ -35,7 +35,7 @@ namespace VMAP
class GameObject;
struct GameObjectDisplayInfoEntry;
-class GameObjectModelOwnerBase
+class TC_COMMON_API GameObjectModelOwnerBase
{
public:
virtual bool IsSpawned() const { return false; }
@@ -47,7 +47,7 @@ public:
virtual void DebugVisualizeCorner(G3D::Vector3 const& /*corner*/) const { }
};
-class GameObjectModel /*, public Intersectable*/
+class TC_COMMON_API GameObjectModel /*, public Intersectable*/
{
GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL) { }
public:
@@ -84,6 +84,6 @@ private:
std::unique_ptr<GameObjectModelOwnerBase> owner;
};
-void LoadGameObjectModelList(std::string const& dataPath);
+TC_COMMON_API void LoadGameObjectModelList(std::string const& dataPath);
#endif // _GAMEOBJECT_MODEL_H
diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h
index d101630d1e9..84a41b15ce6 100644
--- a/src/common/Collision/Models/ModelInstance.h
+++ b/src/common/Collision/Models/ModelInstance.h
@@ -39,7 +39,7 @@ namespace VMAP
MOD_HAS_BOUND = 1<<2
};
- class ModelSpawn
+ class TC_COMMON_API ModelSpawn
{
public:
//mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
@@ -60,7 +60,7 @@ namespace VMAP
static bool writeToFile(FILE* rw, const ModelSpawn &spawn);
};
- class ModelInstance: public ModelSpawn
+ class TC_COMMON_API ModelInstance: public ModelSpawn
{
public:
ModelInstance(): iInvScale(0.0f), iModel(nullptr) { }
diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h
index 39787f6c664..bfc0367627f 100644
--- a/src/common/Collision/Models/WorldModel.h
+++ b/src/common/Collision/Models/WorldModel.h
@@ -33,7 +33,7 @@ namespace VMAP
struct AreaInfo;
struct LocationInfo;
- class MeshTriangle
+ class TC_COMMON_API MeshTriangle
{
public:
MeshTriangle() : idx0(0), idx1(0), idx2(0) { }
@@ -44,7 +44,7 @@ namespace VMAP
uint32 idx2;
};
- class WmoLiquid
+ class TC_COMMON_API WmoLiquid
{
public:
WmoLiquid(uint32 width, uint32 height, const G3D::Vector3 &corner, uint32 type);
@@ -70,7 +70,7 @@ namespace VMAP
};
/*! holding additional info for WMO group files */
- class GroupModel
+ class TC_COMMON_API GroupModel
{
public:
GroupModel() : iBound(), iMogpFlags(0), iGroupWMOID(0), iLiquid(NULL) { }
@@ -103,7 +103,7 @@ namespace VMAP
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
- class WorldModel
+ class TC_COMMON_API WorldModel
{
public:
WorldModel(): RootWMOID(0) { }
diff --git a/src/common/Collision/RegularGrid.h b/src/common/Collision/RegularGrid.h
index 6a2a07968ad..563cf516406 100644
--- a/src/common/Collision/RegularGrid.h
+++ b/src/common/Collision/RegularGrid.h
@@ -20,7 +20,7 @@ class NodeCreatorFunc = NodeCreator<Node>,
/*class BoundsFunc = BoundsTrait<T>,*/
class PositionFunc = PositionTrait<T>
>
-class RegularGrid2D
+class TC_COMMON_API RegularGrid2D
{
public:
diff --git a/src/common/Common.h b/src/common/Common.h
index e8adc55d20d..aa04abacd30 100644
--- a/src/common/Common.h
+++ b/src/common/Common.h
@@ -21,28 +21,31 @@
#include "Define.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <cmath>
-#include <errno.h>
-#include <signal.h>
-#include <assert.h>
-
-#include <set>
-#include <unordered_set>
+#include <algorithm>
+#include <array>
+#include <exception>
#include <list>
-#include <string>
#include <map>
-#include <unordered_map>
+#include <memory>
#include <queue>
+#include <set>
#include <sstream>
-#include <algorithm>
-#include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
-#include <array>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <cerrno>
+#include <csignal>
+
+#include <boost/optional.hpp>
+#include <boost/utility/in_place_factory.hpp>
#include <boost/functional/hash.hpp>
#include "Debugging/Errors.h"
@@ -130,9 +133,9 @@ enum LocaleConstant
#define MAX_LOCALES 8
#define MAX_ACCOUNT_TUTORIAL_VALUES 8
-extern char const* localeNames[TOTAL_LOCALES];
+TC_COMMON_API extern char const* localeNames[TOTAL_LOCALES];
-LocaleConstant GetLocaleByName(const std::string& name);
+TC_COMMON_API LocaleConstant GetLocaleByName(const std::string& name);
typedef std::vector<std::string> StringVector;
@@ -151,6 +154,10 @@ typedef std::vector<std::string> StringVector;
#define MAX_QUERY_LEN 32*1024
+//! Optional helper class to wrap optional values within.
+template <typename T>
+using Optional = boost::optional<T>;
+
namespace Trinity
{
//! std::make_unique implementation (TODO: remove this once C++14 is supported)
diff --git a/src/common/Configuration/BuiltInConfig.h b/src/common/Configuration/BuiltInConfig.h
index 4ae4ed40189..0ffa059bc41 100644
--- a/src/common/Configuration/BuiltInConfig.h
+++ b/src/common/Configuration/BuiltInConfig.h
@@ -19,6 +19,7 @@
#define BUILT_IN_CONFIG_H
#include <string>
+#include "Define.h"
/// Provides helper functions to access built-in values
/// which can be overwritten in config
@@ -26,16 +27,16 @@ namespace BuiltInConfig
{
/// Returns the CMake command when any is specified in the config,
/// returns the built-in path otherwise
- std::string GetCMakeCommand();
+ TC_COMMON_API std::string GetCMakeCommand();
/// Returns the build directory path when any is specified in the config,
/// returns the built-in one otherwise
- std::string GetBuildDirectory();
+ TC_COMMON_API std::string GetBuildDirectory();
/// Returns the source directory path when any is specified in the config,
/// returns the built-in one otherwise
- std::string GetSourceDirectory();
+ TC_COMMON_API std::string GetSourceDirectory();
/// Returns the path to the mysql executable (`mysql`) when any is specified
/// in the config, returns the built-in one otherwise
- std::string GetMySQLExecutable();
+ TC_COMMON_API std::string GetMySQLExecutable();
} // namespace BuiltInConfig
diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp
index fba438dbd33..888aa6ecef3 100644
--- a/src/common/Configuration/Config.cpp
+++ b/src/common/Configuration/Config.cpp
@@ -21,14 +21,17 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "Config.h"
+#include "Log.h"
using namespace boost::property_tree;
-bool ConfigMgr::LoadInitial(std::string const& file, std::string& error)
+bool ConfigMgr::LoadInitial(std::string const& file, std::vector<std::string> args,
+ std::string& error)
{
std::lock_guard<std::mutex> lock(_configLock);
_filename = file;
+ _args = args;
try
{
@@ -64,40 +67,73 @@ ConfigMgr* ConfigMgr::instance()
bool ConfigMgr::Reload(std::string& error)
{
- return LoadInitial(_filename, error);
+ return LoadInitial(_filename, std::move(_args), error);
}
-std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def) const
+template<class T>
+T ConfigMgr::GetValueDefault(std::string const& name, T def) const
{
- std::string value = _config.get<std::string>(ptree::path_type(name, '/'), def);
-
- value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+ try
+ {
+ return _config.get<T>(ptree::path_type(name, '/'));
+ }
+ catch (boost::property_tree::ptree_bad_path)
+ {
+ TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file",
+ name.c_str(), _filename.c_str(), name.c_str(), std::to_string(def).c_str());
+ }
+ catch (boost::property_tree::ptree_bad_data)
+ {
+ TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use %s instead",
+ name.c_str(), _filename.c_str(), std::to_string(def).c_str());
+ }
- return value;
+ return def;
}
-bool ConfigMgr::GetBoolDefault(std::string const& name, bool def) const
+template<>
+std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string def) const
{
try
{
- std::string val = _config.get<std::string>(ptree::path_type(name, '/'));
- val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
- return (val == "true" || val == "TRUE" || val == "yes" || val == "YES" || val == "1");
+ return _config.get<std::string>(ptree::path_type(name, '/'));
}
- catch (std::exception const& /*ex*/)
+ catch (boost::property_tree::ptree_bad_path)
{
- return def;
+ TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file",
+ name.c_str(), _filename.c_str(), name.c_str(), def.c_str());
}
+ catch (boost::property_tree::ptree_bad_data)
+ {
+ TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use %s instead",
+ name.c_str(), _filename.c_str(), def.c_str());
+ }
+
+ return def;
+}
+
+std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def) const
+{
+ std::string val = GetValueDefault(name, def);
+ val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
+ return val;
+}
+
+bool ConfigMgr::GetBoolDefault(std::string const& name, bool def) const
+{
+ std::string val = GetValueDefault(name, std::string(def ? "1" : "0"));
+ val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
+ return (val == "1" || val == "true" || val == "TRUE" || val == "yes" || val == "YES");
}
int ConfigMgr::GetIntDefault(std::string const& name, int def) const
{
- return _config.get<int>(ptree::path_type(name, '/'), def);
+ return GetValueDefault(name, def);
}
float ConfigMgr::GetFloatDefault(std::string const& name, float def) const
{
- return _config.get<float>(ptree::path_type(name, '/'), def);
+ return GetValueDefault(name, def);
}
std::string const& ConfigMgr::GetFilename()
diff --git a/src/common/Configuration/Config.h b/src/common/Configuration/Config.h
index 6882517b509..573bc7b4a15 100644
--- a/src/common/Configuration/Config.h
+++ b/src/common/Configuration/Config.h
@@ -19,19 +19,25 @@
#ifndef CONFIG_H
#define CONFIG_H
+#include "Define.h"
+
#include <string>
#include <list>
+#include <vector>
#include <mutex>
#include <boost/property_tree/ptree.hpp>
-class ConfigMgr
+class TC_COMMON_API ConfigMgr
{
- ConfigMgr() { }
- ~ConfigMgr() { }
+ ConfigMgr() = default;
+ ConfigMgr(ConfigMgr const&) = delete;
+ ConfigMgr& operator=(ConfigMgr const&) = delete;
+ ~ConfigMgr() = default;
public:
/// Method used only for loading main configuration files (authserver.conf and worldserver.conf)
- bool LoadInitial(std::string const& file, std::string& error);
+ bool LoadInitial(std::string const& file, std::vector<std::string> args,
+ std::string& error);
static ConfigMgr* instance();
@@ -43,15 +49,17 @@ public:
float GetFloatDefault(std::string const& name, float def) const;
std::string const& GetFilename();
+ std::vector<std::string> const& GetArguments() const { return _args; }
std::list<std::string> GetKeysByString(std::string const& name);
private:
std::string _filename;
+ std::vector<std::string> _args;
boost::property_tree::ptree _config;
std::mutex _configLock;
- ConfigMgr(ConfigMgr const&);
- ConfigMgr& operator=(ConfigMgr const&);
+ template<class T>
+ T GetValueDefault(std::string const& name, T def) const;
};
#define sConfigMgr ConfigMgr::instance()
diff --git a/src/common/Cryptography/ARC4.h b/src/common/Cryptography/ARC4.h
index f39e662e295..7e680176836 100644
--- a/src/common/Cryptography/ARC4.h
+++ b/src/common/Cryptography/ARC4.h
@@ -22,7 +22,7 @@
#include <openssl/evp.h>
#include "Define.h"
-class ARC4
+class TC_COMMON_API ARC4
{
public:
ARC4(uint8 len);
diff --git a/src/common/Cryptography/Authentication/AuthCrypt.h b/src/common/Cryptography/Authentication/AuthCrypt.h
index 878391e3ce8..db4de8a7bd1 100644
--- a/src/common/Cryptography/Authentication/AuthCrypt.h
+++ b/src/common/Cryptography/Authentication/AuthCrypt.h
@@ -23,7 +23,7 @@
class BigNumber;
-class AuthCrypt
+class TC_COMMON_API AuthCrypt
{
public:
AuthCrypt();
diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h
index a5bda50dc72..1d21be1b431 100644
--- a/src/common/Cryptography/BigNumber.h
+++ b/src/common/Cryptography/BigNumber.h
@@ -24,7 +24,7 @@
struct bignum_st;
-class BigNumber
+class TC_COMMON_API BigNumber
{
public:
BigNumber();
diff --git a/src/common/Cryptography/HMACSHA1.h b/src/common/Cryptography/HMACSHA1.h
index 29a926d5b16..049847489a6 100644
--- a/src/common/Cryptography/HMACSHA1.h
+++ b/src/common/Cryptography/HMACSHA1.h
@@ -28,7 +28,7 @@ class BigNumber;
#define SEED_KEY_SIZE 16
-class HmacHash
+class TC_COMMON_API HmacHash
{
public:
HmacHash(uint32 len, uint8 *seed);
diff --git a/src/common/Cryptography/OpenSSLCrypto.h b/src/common/Cryptography/OpenSSLCrypto.h
index df1b14b5eda..65155df9af8 100644
--- a/src/common/Cryptography/OpenSSLCrypto.h
+++ b/src/common/Cryptography/OpenSSLCrypto.h
@@ -18,6 +18,8 @@
#ifndef OPENSSL_CRYPTO_H
#define OPENSSL_CRYPTO_H
+#include "Define.h"
+
/**
* A group of functions which setup openssl crypto module to work properly in multithreaded enviroment
* If not setup properly - it will crash
@@ -25,9 +27,9 @@
namespace OpenSSLCrypto
{
/// Needs to be called before threads using openssl are spawned
- void threadsSetup();
+ TC_COMMON_API void threadsSetup();
/// Needs to be called after threads using openssl are despawned
- void threadsCleanup();
+ TC_COMMON_API void threadsCleanup();
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/common/Cryptography/SHA1.cpp b/src/common/Cryptography/SHA1.cpp
index a01bd7844ee..aed4a069827 100644
--- a/src/common/Cryptography/SHA1.cpp
+++ b/src/common/Cryptography/SHA1.cpp
@@ -18,6 +18,7 @@
#include "SHA1.h"
#include "BigNumber.h"
+#include "Util.h"
#include <cstring>
#include <stdarg.h>
@@ -67,3 +68,10 @@ void SHA1Hash::Finalize(void)
SHA1_Final(mDigest, &mC);
}
+std::string CalculateSHA1Hash(std::string const& content)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SHA1((unsigned char*)content.c_str(), content.length(), (unsigned char*)&digest);
+
+ return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
+}
diff --git a/src/common/Cryptography/SHA1.h b/src/common/Cryptography/SHA1.h
index ffa02176a2d..37ac2cc0166 100644
--- a/src/common/Cryptography/SHA1.h
+++ b/src/common/Cryptography/SHA1.h
@@ -25,7 +25,7 @@
class BigNumber;
-class SHA1Hash
+class TC_COMMON_API SHA1Hash
{
public:
SHA1Hash();
@@ -46,5 +46,8 @@ class SHA1Hash
SHA_CTX mC;
uint8 mDigest[SHA_DIGEST_LENGTH];
};
-#endif
+/// Returns the SHA1 hash of the given content as hex string.
+TC_COMMON_API std::string CalculateSHA1Hash(std::string const& content);
+
+#endif
diff --git a/src/common/Debugging/Errors.h b/src/common/Debugging/Errors.h
index 37d247ada82..e4b3563ca96 100644
--- a/src/common/Debugging/Errors.h
+++ b/src/common/Debugging/Errors.h
@@ -23,18 +23,18 @@
namespace Trinity
{
- DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
- DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...) ATTR_NORETURN ATTR_PRINTF(5, 6);
+ DECLSPEC_NORETURN TC_COMMON_API void Assert(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
+ DECLSPEC_NORETURN TC_COMMON_API void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...) ATTR_NORETURN ATTR_PRINTF(5, 6);
- DECLSPEC_NORETURN void Fatal(char const* file, int line, char const* function, char const* message, ...) ATTR_NORETURN ATTR_PRINTF(4, 5);
+ DECLSPEC_NORETURN TC_COMMON_API void Fatal(char const* file, int line, char const* function, char const* message, ...) ATTR_NORETURN ATTR_PRINTF(4, 5);
- DECLSPEC_NORETURN void Error(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
+ DECLSPEC_NORETURN TC_COMMON_API void Error(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
- DECLSPEC_NORETURN void Abort(char const* file, int line, char const* function) ATTR_NORETURN;
+ DECLSPEC_NORETURN TC_COMMON_API void Abort(char const* file, int line, char const* function) ATTR_NORETURN;
- void Warning(char const* file, int line, char const* function, char const* message);
+ TC_COMMON_API void Warning(char const* file, int line, char const* function, char const* message);
- DECLSPEC_NORETURN void AbortHandler(int sigval) ATTR_NORETURN;
+ DECLSPEC_NORETURN TC_COMMON_API void AbortHandler(int sigval) ATTR_NORETURN;
} // namespace Trinity
diff --git a/src/common/Define.h b/src/common/Define.h
index b34edb6a549..d03d26ad780 100644
--- a/src/common/Define.h
+++ b/src/common/Define.h
@@ -95,6 +95,45 @@
#endif
#endif //COMPILER == COMPILER_GNU
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+# if COMPILER == COMPILER_MICROSOFT
+# define TC_API_EXPORT __declspec(dllexport)
+# define TC_API_IMPORT __declspec(dllimport)
+# elif COMPILER == COMPILER_GNU
+# define TC_API_EXPORT __attribute__((visibility("default")))
+# define TC_API_IMPORT
+# else
+# error compiler not supported!
+# endif
+#else
+# define TC_API_EXPORT
+# define TC_API_IMPORT
+#endif
+
+#ifdef TRINITY_API_EXPORT_COMMON
+# define TC_COMMON_API TC_API_EXPORT
+#else
+# define TC_COMMON_API TC_API_IMPORT
+#endif
+
+#ifdef TRINITY_API_EXPORT_DATABASE
+# define TC_DATABASE_API TC_API_EXPORT
+#else
+# define TC_DATABASE_API TC_API_IMPORT
+#endif
+
+#ifdef TRINITY_API_EXPORT_SHARED
+# define TC_SHARED_API TC_API_EXPORT
+#else
+# define TC_SHARED_API TC_API_IMPORT
+#endif
+
+#ifdef TRINITY_API_EXPORT_GAME
+# define TC_GAME_API TC_API_EXPORT
+#else
+# define TC_GAME_API TC_API_IMPORT
+#endif
+
#define UI64FMTD "%" PRIu64
#define UI64LIT(N) UINT64_C(N)
diff --git a/src/common/GitRevision.cpp b/src/common/GitRevision.cpp
index 5343fbd6531..2162e36847f 100644
--- a/src/common/GitRevision.cpp
+++ b/src/common/GitRevision.cpp
@@ -1,5 +1,4 @@
#include "GitRevision.h"
-#include "CompilerDefs.h"
#include "revision_data.h"
char const* GitRevision::GetHash()
diff --git a/src/common/GitRevision.h b/src/common/GitRevision.h
index 7fddcb7605a..aace8ad2076 100644
--- a/src/common/GitRevision.h
+++ b/src/common/GitRevision.h
@@ -19,22 +19,23 @@
#define __GITREVISION_H__
#include <string>
+#include "Define.h"
namespace GitRevision
{
- char const* GetHash();
- char const* GetDate();
- char const* GetBranch();
- char const* GetCMakeCommand();
- char const* GetBuildDirectory();
- char const* GetSourceDirectory();
- char const* GetMySQLExecutable();
- char const* GetFullDatabase();
- char const* GetFullVersion();
- char const* GetCompanyNameStr();
- char const* GetLegalCopyrightStr();
- char const* GetFileVersionStr();
- char const* GetProductVersionStr();
+ TC_COMMON_API char const* GetHash();
+ TC_COMMON_API char const* GetDate();
+ TC_COMMON_API char const* GetBranch();
+ TC_COMMON_API char const* GetCMakeCommand();
+ TC_COMMON_API char const* GetBuildDirectory();
+ TC_COMMON_API char const* GetSourceDirectory();
+ TC_COMMON_API char const* GetMySQLExecutable();
+ TC_COMMON_API char const* GetFullDatabase();
+ TC_COMMON_API char const* GetFullVersion();
+ TC_COMMON_API char const* GetCompanyNameStr();
+ TC_COMMON_API char const* GetLegalCopyrightStr();
+ TC_COMMON_API char const* GetFileVersionStr();
+ TC_COMMON_API char const* GetProductVersionStr();
}
#endif
diff --git a/src/common/Logging/Appender.h b/src/common/Logging/Appender.h
index f0bfe423a66..d24daa2b60d 100644
--- a/src/common/Logging/Appender.h
+++ b/src/common/Logging/Appender.h
@@ -41,7 +41,7 @@ enum LogLevel
const uint8 MaxLogLevels = 6;
-enum AppenderType
+enum AppenderType : uint8
{
APPENDER_NONE,
APPENDER_CONSOLE,
@@ -59,7 +59,7 @@ enum AppenderFlags
APPENDER_FLAGS_MAKE_FILE_BACKUP = 0x10 // only used by FileAppender
};
-struct LogMessage
+struct TC_COMMON_API LogMessage
{
LogMessage(LogLevel _level, std::string const& _type, std::string&& _text)
: level(_level), type(_type), text(std::forward<std::string>(_text)), mtime(time(NULL))
@@ -85,7 +85,7 @@ struct LogMessage
}
};
-class Appender
+class TC_COMMON_API Appender
{
public:
Appender(uint8 _id, std::string const& name, LogLevel level = LOG_LEVEL_DISABLED, AppenderFlags flags = APPENDER_FLAGS_NONE);
@@ -123,7 +123,7 @@ Appender* CreateAppender(uint8 id, std::string const& name, LogLevel level, Appe
return new AppenderImpl(id, name, level, flags, std::forward<ExtraAppenderArgs>(extraArgs));
}
-class InvalidAppenderArgsException : public std::length_error
+class TC_COMMON_API InvalidAppenderArgsException : public std::length_error
{
public:
explicit InvalidAppenderArgsException(std::string const& message) : std::length_error(message) { }
diff --git a/src/common/Logging/AppenderConsole.h b/src/common/Logging/AppenderConsole.h
index 5d7eae36b40..96d17207158 100644
--- a/src/common/Logging/AppenderConsole.h
+++ b/src/common/Logging/AppenderConsole.h
@@ -42,7 +42,7 @@ enum ColorTypes
const uint8 MaxColors = uint8(WHITE) + 1;
-class AppenderConsole : public Appender
+class TC_COMMON_API AppenderConsole : public Appender
{
public:
typedef std::integral_constant<AppenderType, APPENDER_CONSOLE>::type TypeIndex;
diff --git a/src/common/Logging/AppenderFile.h b/src/common/Logging/AppenderFile.h
index 9ba5d59259c..956b7a70b93 100644
--- a/src/common/Logging/AppenderFile.h
+++ b/src/common/Logging/AppenderFile.h
@@ -21,7 +21,7 @@
#include <atomic>
#include "Appender.h"
-class AppenderFile : public Appender
+class TC_COMMON_API AppenderFile : public Appender
{
public:
typedef std::integral_constant<AppenderType, APPENDER_FILE>::type TypeIndex;
diff --git a/src/common/Logging/Log.h b/src/common/Logging/Log.h
index 062f14d525c..6460e404c90 100644
--- a/src/common/Logging/Log.h
+++ b/src/common/Logging/Log.h
@@ -34,7 +34,7 @@
#define LOGGER_ROOT "root"
-class Log
+class TC_COMMON_API Log
{
typedef std::unordered_map<std::string, Logger> LoggerMap;
diff --git a/src/common/Logging/LogOperation.h b/src/common/Logging/LogOperation.h
index 618629b5423..56e2307d492 100644
--- a/src/common/Logging/LogOperation.h
+++ b/src/common/Logging/LogOperation.h
@@ -19,11 +19,12 @@
#define LOGOPERATION_H
#include <memory>
+#include "Define.h"
class Logger;
struct LogMessage;
-class LogOperation
+class TC_COMMON_API LogOperation
{
public:
LogOperation(Logger const* _logger, std::unique_ptr<LogMessage>&& _msg)
diff --git a/src/common/Logging/Logger.h b/src/common/Logging/Logger.h
index 67eab4295a4..4ac2e4494cc 100644
--- a/src/common/Logging/Logger.h
+++ b/src/common/Logging/Logger.h
@@ -20,7 +20,7 @@
#include "Appender.h"
-class Logger
+class TC_COMMON_API Logger
{
public:
Logger();
diff --git a/src/common/Utilities/Containers.h b/src/common/Utilities/Containers.h
index f3e9432ca4c..5edb245fd87 100644
--- a/src/common/Utilities/Containers.h
+++ b/src/common/Utilities/Containers.h
@@ -20,6 +20,7 @@
#include "Define.h"
#include "Random.h"
+#include "Util.h"
#include <algorithm>
#include <functional>
#include <list>
@@ -30,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)
{
@@ -55,7 +56,7 @@ namespace Trinity
if (size)
RandomResizeList(listCopy, size);
- list = listCopy;
+ list = std::move(listCopy);
}
/*
@@ -67,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;
}
@@ -117,6 +118,19 @@ namespace Trinity
}
/**
+ * @fn void Trinity::Containers::RandomShuffle(C& container)
+ *
+ * @brief Reorder the elements of the container randomly.
+ *
+ * @param container Container to reorder
+ */
+ template <class C>
+ void RandomShuffle(C& container)
+ {
+ std::shuffle(container.begin(), container.end(), SFMTEngine::Instance());
+ }
+
+ /**
* @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2)
*
* @brief Checks if two SORTED containers have a common element
@@ -156,7 +170,6 @@ namespace Trinity
++itr;
}
}
-
}
//! namespace Containers
}
diff --git a/src/common/Utilities/EventMap.h b/src/common/Utilities/EventMap.h
index a1aaa9af269..6a314a9e633 100644
--- a/src/common/Utilities/EventMap.h
+++ b/src/common/Utilities/EventMap.h
@@ -22,7 +22,7 @@
#include "Duration.h"
#include "Util.h"
-class EventMap
+class TC_COMMON_API EventMap
{
/**
* Internal storage type.
@@ -122,7 +122,7 @@ public:
*/
void ScheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0)
{
- ScheduleEvent(eventId, time.count(), group, phase);
+ ScheduleEvent(eventId, uint32(time.count()), group, phase);
}
/**
@@ -145,7 +145,7 @@ public:
*/
void RescheduleEvent(uint32 eventId, Milliseconds const& time, uint32 group = 0, uint8 phase = 0)
{
- RescheduleEvent(eventId, time.count(), group, phase);
+ RescheduleEvent(eventId, uint32(time.count()), group, phase);
}
/**
@@ -169,7 +169,7 @@ public:
*/
void Repeat(Milliseconds const& time)
{
- Repeat(time.count());
+ Repeat(uint32(time.count()));
}
/**
@@ -190,7 +190,7 @@ public:
*/
void Repeat(Milliseconds const& minTime, Milliseconds const& maxTime)
{
- Repeat(minTime.count(), maxTime.count());
+ Repeat(uint32(minTime.count()), uint32(maxTime.count()));
}
/**
@@ -218,7 +218,7 @@ public:
*/
void DelayEvents(Milliseconds const& delay)
{
- DelayEvents(delay.count());
+ DelayEvents(uint32(delay.count()));
}
/**
@@ -239,7 +239,7 @@ public:
*/
void DelayEvents(Milliseconds const& delay, uint32 group)
{
- DelayEvents(delay.count(), group);
+ DelayEvents(uint32(delay.count()), group);
}
/**
diff --git a/src/common/Utilities/EventProcessor.h b/src/common/Utilities/EventProcessor.h
index e5eafed79b9..e10558e6a21 100644
--- a/src/common/Utilities/EventProcessor.h
+++ b/src/common/Utilities/EventProcessor.h
@@ -25,7 +25,7 @@
// Note. All times are in milliseconds here.
-class BasicEvent
+class TC_COMMON_API BasicEvent
{
public:
BasicEvent()
@@ -55,7 +55,7 @@ class BasicEvent
typedef std::multimap<uint64, BasicEvent*> EventList;
-class EventProcessor
+class TC_COMMON_API EventProcessor
{
public:
EventProcessor();
diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/common/Utilities/MessageBuffer.h
index d68bee181b1..d08c4b25bab 100644
--- a/src/server/shared/Networking/MessageBuffer.h
+++ b/src/common/Utilities/MessageBuffer.h
@@ -20,6 +20,7 @@
#include "Define.h"
#include <vector>
+#include <cstring>
class MessageBuffer
{
diff --git a/src/common/Utilities/Random.h b/src/common/Utilities/Random.h
index 5dea6117f97..b3ca00219ef 100644
--- a/src/common/Utilities/Random.h
+++ b/src/common/Utilities/Random.h
@@ -24,28 +24,28 @@
#include <random>
/* Return a random number in the range min..max. */
-int32 irand(int32 min, int32 max);
+TC_COMMON_API int32 irand(int32 min, int32 max);
/* Return a random number in the range min..max (inclusive). */
-uint32 urand(uint32 min, uint32 max);
+TC_COMMON_API uint32 urand(uint32 min, uint32 max);
/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
-uint32 urandms(uint32 min, uint32 max);
+TC_COMMON_API uint32 urandms(uint32 min, uint32 max);
/* Return a random number in the range 0 .. UINT32_MAX. */
-uint32 rand32();
+TC_COMMON_API uint32 rand32();
/* Return a random time in the range min..max (up to millisecond precision). Only works for values where millisecond difference is a valid uint32. */
-Milliseconds randtime(Milliseconds const& min, Milliseconds const& max);
+TC_COMMON_API Milliseconds randtime(Milliseconds const& min, Milliseconds const& max);
/* Return a random number in the range min..max */
-float frand(float min, float max);
+TC_COMMON_API float frand(float min, float max);
/* Return a random double from 0.0 to 1.0 (exclusive). */
-double rand_norm();
+TC_COMMON_API double rand_norm();
/* Return a random double from 0.0 to 100.0 (exclusive). */
-double rand_chance();
+TC_COMMON_API double rand_chance();
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_f(float chance)
@@ -62,7 +62,7 @@ inline bool roll_chance_i(int chance)
/*
* SFMT wrapper satisfying UniformRandomNumberGenerator concept for use in <random> algorithms
*/
-class SFMTEngine
+class TC_COMMON_API SFMTEngine
{
public:
typedef uint32 result_type;
diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp
new file mode 100644
index 00000000000..f35c6de3b5c
--- /dev/null
+++ b/src/common/Utilities/StartProcess.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "StartProcess.h"
+
+#include <atomic>
+#include <thread>
+#include <functional>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/copy.hpp>
+#include <boost/iostreams/concepts.hpp>
+#include <boost/iostreams/device/file_descriptor.hpp>
+#include <boost/process.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "Common.h"
+#include "Log.h"
+
+using namespace boost::process;
+using namespace boost::process::initializers;
+using namespace boost::iostreams;
+
+namespace Trinity {
+
+template<typename T>
+class TCLogSink
+{
+ T callback_;
+
+public:
+ typedef char char_type;
+ typedef sink_tag category;
+
+ // Requires a callback type which has a void(std::string) signature
+ TCLogSink(T callback)
+ : callback_(std::move(callback)) { }
+
+ std::streamsize write(const char* str, std::streamsize size)
+ {
+ callback_(std::string(str, size));
+ return size;
+ }
+};
+
+template<typename T>
+auto MakeTCLogSink(T&& callback)
+ -> TCLogSink<typename std::decay<T>::type>
+{
+ return { std::forward<T>(callback) };
+}
+
+template<typename T>
+static int CreateChildProcess(T waiter, std::string const& executable,
+ std::vector<std::string> const& args,
+ std::string const& logger, std::string const& input,
+ bool secure)
+{
+ auto outPipe = create_pipe();
+ auto errPipe = create_pipe();
+
+ Optional<file_descriptor_source> inputSource;
+
+ if (!secure)
+ {
+ TC_LOG_TRACE(logger, "Starting process \"%s\" with arguments: \"%s\".",
+ executable.c_str(), boost::algorithm::join(args, " ").c_str());
+ }
+
+ // Start the child process
+ child c = [&]
+ {
+ if (!input.empty())
+ {
+ inputSource = file_descriptor_source(input);
+
+ // With binding stdin
+ return execute(run_exe(boost::filesystem::absolute(executable)),
+ set_args(args),
+ inherit_env(),
+ bind_stdin(*inputSource),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+ }
+ else
+ {
+ // Without binding stdin
+ return execute(run_exe(boost::filesystem::absolute(executable)),
+ set_args(args),
+ inherit_env(),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+ }
+ }();
+
+ file_descriptor_source outFd(outPipe.source, close_handle);
+ file_descriptor_source errFd(errPipe.source, close_handle);
+
+ auto outInfo = MakeTCLogSink([&](std::string msg)
+ {
+ TC_LOG_INFO(logger, "%s", msg.c_str());
+ });
+
+ auto outError = MakeTCLogSink([&](std::string msg)
+ {
+ TC_LOG_ERROR(logger, "%s", msg.c_str());
+ });
+
+ copy(outFd, outInfo);
+ copy(errFd, outError);
+
+ // Call the waiter in the current scope to prevent
+ // the streams from closing too early on leaving the scope.
+ int const result = waiter(c);
+
+ if (!secure)
+ {
+ TC_LOG_TRACE(logger, ">> Process \"%s\" finished with return value %i.",
+ executable.c_str(), result);
+ }
+
+ if (inputSource)
+ inputSource->close();
+
+ return result;
+}
+
+int StartProcess(std::string const& executable, std::vector<std::string> const& args,
+ std::string const& logger, std::string input_file, bool secure)
+{
+ return CreateChildProcess([](child& c) -> int
+ {
+ try
+ {
+ return wait_for_exit(c);
+ }
+ catch (...)
+ {
+ return EXIT_FAILURE;
+ }
+ }, executable, args, logger, input_file, secure);
+}
+
+class AsyncProcessResultImplementation
+ : public AsyncProcessResult
+{
+ std::string const executable;
+ std::vector<std::string> const args;
+ std::string const logger;
+ std::string const input_file;
+ bool const is_secure;
+
+ std::atomic<bool> was_terminated;
+
+ // Workaround for missing move support in boost < 1.57
+ Optional<std::shared_ptr<std::future<int>>> result;
+ Optional<std::reference_wrapper<child>> my_child;
+
+public:
+ explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_,
+ std::string logger_, std::string input_file_,
+ bool secure)
+ : executable(std::move(executable_)), args(std::move(args_)),
+ logger(std::move(logger_)), input_file(input_file_),
+ is_secure(secure), was_terminated(false) { }
+
+ AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete;
+ AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete;
+ AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete;
+ AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete;
+
+ int StartProcess()
+ {
+ ASSERT(!my_child, "Process started already!");
+
+ return CreateChildProcess([&](child& c) -> int
+ {
+ int result;
+ my_child = std::reference_wrapper<child>(c);
+
+ try
+ {
+ result = wait_for_exit(c);
+ }
+ catch (...)
+ {
+ result = EXIT_FAILURE;
+ }
+
+ my_child.reset();
+ return was_terminated ? EXIT_FAILURE : result;
+
+ }, executable, args, logger, input_file, is_secure);
+ }
+
+ void SetFuture(std::future<int> result_)
+ {
+ result = std::make_shared<std::future<int>>(std::move(result_));
+ }
+
+ /// Returns the future which contains the result of the process
+ /// as soon it is finished.
+ std::future<int>& GetFutureResult() override
+ {
+ ASSERT(*result, "The process wasn't started!");
+ return **result;
+ }
+
+ /// Tries to terminate the process
+ void Terminate() override
+ {
+ if (!my_child)
+ {
+ was_terminated = true;
+ try
+ {
+ terminate(my_child->get());
+ }
+ catch(...)
+ {
+ // Do nothing
+ }
+ }
+ }
+};
+
+std::shared_ptr<AsyncProcessResult>
+ StartAsyncProcess(std::string executable, std::vector<std::string> args,
+ std::string logger, std::string input_file, bool secure)
+{
+ auto handle = std::make_shared<AsyncProcessResultImplementation>(
+ std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
+
+ handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); }));
+ return handle;
+}
+
+Optional<std::string> SearchExecutableInPath(std::string const& filename)
+{
+ try
+ {
+ auto result = search_path(filename);
+ if (result.empty())
+ return boost::none;
+ else
+ return result;
+ }
+ catch (...)
+ {
+ return boost::none;
+ }
+}
+
+} // namespace Trinity
diff --git a/src/common/Utilities/StartProcess.h b/src/common/Utilities/StartProcess.h
new file mode 100644
index 00000000000..3b380bd4f4e
--- /dev/null
+++ b/src/common/Utilities/StartProcess.h
@@ -0,0 +1,67 @@
+/*
+ * 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 Process_h__
+#define Process_h__
+
+#include <future>
+#include <memory>
+#include "Common.h"
+
+namespace Trinity {
+
+/// Starts a process with the given arguments and parameters and will block
+/// until the process is finished.
+/// When an input path is given, the file will be routed to the processes stdin.
+/// When the process is marked as secure no arguments are leaked to logs.
+/// Note that most executables expect it's name as the first argument.
+TC_COMMON_API int StartProcess(std::string const& executable, std::vector<std::string> const& args,
+ std::string const& logger, std::string input_file = "",
+ bool secure = false);
+
+/// Platform and library independent representation
+/// of asynchronous process results
+class AsyncProcessResult
+{
+public:
+ virtual ~AsyncProcessResult() { }
+
+ /// Returns the future which contains the result of the process
+ /// as soon it is finished.
+ virtual std::future<int>& GetFutureResult() = 0;
+
+ /// Tries to terminate the process
+ virtual void Terminate() = 0;
+};
+
+/// Starts a process asynchronously with the given arguments and parameters and
+/// returns an AsyncProcessResult immediately which is set, when the process exits.
+/// When an input path is given, the file will be routed to the processes stdin.
+/// When the process is marked as secure no arguments are leaked to logs.
+/// Note that most executables expect it's name as the first argument.
+TC_COMMON_API std::shared_ptr<AsyncProcessResult>
+ StartAsyncProcess(std::string executable, std::vector<std::string> args,
+ std::string logger, std::string input_file = "",
+ bool secure = false);
+
+/// Searches for the given executable in the PATH variable
+/// and returns a present optional when it was found.
+TC_COMMON_API Optional<std::string> SearchExecutableInPath(std::string const& filename);
+
+} // namespace Trinity
+
+#endif // Process_h__
diff --git a/src/common/Utilities/TaskScheduler.h b/src/common/Utilities/TaskScheduler.h
index 8cf5d914128..6784c968683 100644
--- a/src/common/Utilities/TaskScheduler.h
+++ b/src/common/Utilities/TaskScheduler.h
@@ -46,7 +46,7 @@ class TaskContext;
/// with the same duration or a new one.
/// It also provides access to the repeat counter which is useful for task that repeat itself often
/// but behave different every time (spoken event dialogs for example).
-class TaskScheduler
+class TC_COMMON_API TaskScheduler
{
friend class TaskContext;
@@ -131,7 +131,7 @@ class TaskScheduler
};
};
- class TaskQueue
+ class TC_COMMON_API TaskQueue
{
std::multiset<TaskContainer, Compare> container;
@@ -401,14 +401,14 @@ private:
auto const milli_max = std::chrono::duration_cast<std::chrono::milliseconds>(max);
// TC specific: use SFMT URandom
- return std::chrono::milliseconds(urand(milli_min.count(), milli_max.count()));
+ return std::chrono::milliseconds(urand(uint32(milli_min.count()), uint32(milli_max.count())));
}
/// Dispatch remaining tasks
void Dispatch(success_t const& callback);
};
-class TaskContext
+class TC_COMMON_API TaskContext
{
friend class TaskScheduler;
diff --git a/src/common/Utilities/Timer.h b/src/common/Utilities/Timer.h
index cdce08caaf0..f66bb90c98e 100644
--- a/src/common/Utilities/Timer.h
+++ b/src/common/Utilities/Timer.h
@@ -25,9 +25,9 @@ inline uint32 getMSTime()
{
using namespace std::chrono;
- static const system_clock::time_point ApplicationStartTime = system_clock::now();
+ static const steady_clock::time_point ApplicationStartTime = steady_clock::now();
- return uint32(duration_cast<milliseconds>(system_clock::now() - ApplicationStartTime).count());
+ return uint32(duration_cast<milliseconds>(steady_clock::now() - ApplicationStartTime).count());
}
inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index ddc07505942..3d8cda66d48 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -22,6 +22,7 @@
#include "utf8.h"
#include "Errors.h" // for ASSERT
#include <stdarg.h>
+#include <boost/algorithm/string/case_conv.hpp>
#if COMPILER == COMPILER_GNU
#include <sys/socket.h>
@@ -218,22 +219,29 @@ bool IsIPAddress(char const* ipaddress)
}
/// create PID file
-uint32 CreatePIDFile(const std::string& filename)
+uint32 CreatePIDFile(std::string const& filename)
{
- FILE* pid_file = fopen (filename.c_str(), "w" );
+ FILE* pid_file = fopen(filename.c_str(), "w");
if (pid_file == NULL)
return 0;
+ uint32 pid = GetPID();
+
+ fprintf(pid_file, "%u", pid);
+ fclose(pid_file);
+
+ return pid;
+}
+
+uint32 GetPID()
+{
#ifdef _WIN32
DWORD pid = GetCurrentProcessId();
#else
pid_t pid = getpid();
#endif
- fprintf(pid_file, "%u", pid );
- fclose(pid_file);
-
- return (uint32)pid;
+ return uint32(pid);
}
size_t utf8length(std::string& utf8str)
@@ -418,7 +426,7 @@ bool utf8ToConsole(const std::string& utf8str, std::string& conStr)
return false;
conStr.resize(wstr.size());
- CharToOemBuffW(&wstr[0], &conStr[0], wstr.size());
+ CharToOemBuffW(&wstr[0], &conStr[0], uint32(wstr.size()));
#else
// not implemented yet
conStr = utf8str;
@@ -432,7 +440,7 @@ bool consoleToUtf8(const std::string& conStr, std::string& utf8str)
#if PLATFORM == PLATFORM_WINDOWS
std::wstring wstr;
wstr.resize(conStr.size());
- OemToCharBuffW(&conStr[0], &wstr[0], conStr.size());
+ OemToCharBuffW(&conStr[0], &wstr[0], uint32(conStr.size()));
return WStrToUtf8(wstr, utf8str);
#else
@@ -450,7 +458,7 @@ bool Utf8FitTo(const std::string& str, std::wstring const& search)
return false;
// converting to lower case
- wstrToLower( temp );
+ wstrToLower(temp);
if (temp.find(search) == std::wstring::npos)
return false;
@@ -469,10 +477,10 @@ void utf8printf(FILE* out, const char *str, ...)
void vutf8printf(FILE* out, const char *str, va_list* ap)
{
#if PLATFORM == PLATFORM_WINDOWS
- char temp_buf[32*1024];
- wchar_t wtemp_buf[32*1024];
+ char temp_buf[32 * 1024];
+ wchar_t wtemp_buf[32 * 1024];
- size_t temp_len = vsnprintf(temp_buf, 32*1024, str, *ap);
+ size_t temp_len = vsnprintf(temp_buf, 32 * 1024, str, *ap);
//vsnprintf returns -1 if the buffer is too small
if (temp_len == size_t(-1))
temp_len = 32*1024-1;
@@ -480,7 +488,7 @@ void vutf8printf(FILE* out, const char *str, va_list* ap)
size_t wtemp_len = 32*1024-1;
Utf8toWStr(temp_buf, temp_len, wtemp_buf, wtemp_len);
- CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len+1);
+ CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], uint32(wtemp_len + 1));
fprintf(out, "%s", temp_buf);
#else
vfprintf(out, str, *ap);
@@ -521,3 +529,34 @@ std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse
return ss.str();
}
+
+void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/)
+{
+ // string must have even number of characters
+ if (str.length() & 1)
+ return;
+
+ int32 init = 0;
+ int32 end = int32(str.length());
+ int8 op = 1;
+
+ if (reverse)
+ {
+ init = int32(str.length() - 2);
+ end = -2;
+ op = -1;
+ }
+
+ uint32 j = 0;
+ for (int32 i = init; i != end; i += 2 * op)
+ {
+ char buffer[3] = { str[i], str[i + 1], '\0' };
+ out[j++] = uint8(strtoul(buffer, NULL, 16));
+ }
+}
+
+bool StringToBool(std::string const& str)
+{
+ std::string lowerStr = boost::algorithm::to_lower_copy(str);
+ return lowerStr == "1" || lowerStr == "true" || lowerStr == "yes";
+}
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index a3e3f21466e..cc68f3b2237 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -40,7 +40,7 @@ template<typename T, class S> struct Finder
bool operator()(const std::pair<int, S> &obj) { return obj.second.*idMember_ == val_; }
};
-class Tokenizer
+class TC_COMMON_API Tokenizer
{
public:
typedef std::vector<char const*> StorageType;
@@ -68,15 +68,15 @@ private:
StorageType m_storage;
};
-void stripLineInvisibleChars(std::string &src);
+TC_COMMON_API void stripLineInvisibleChars(std::string &src);
-int32 MoneyStringToMoney(const std::string& moneyString);
+TC_COMMON_API int32 MoneyStringToMoney(const std::string& moneyString);
-struct tm* localtime_r(const time_t* time, struct tm *result);
+TC_COMMON_API struct tm* localtime_r(const time_t* time, struct tm *result);
-std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hoursOnly = false);
-uint32 TimeStringToSecs(const std::string& timestring);
-std::string TimeToTimestampStr(time_t t);
+TC_COMMON_API std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hoursOnly = false);
+TC_COMMON_API uint32 TimeStringToSecs(const std::string& timestring);
+TC_COMMON_API std::string TimeToTimestampStr(time_t t);
inline void ApplyPercentModFloatVar(float& var, float val, bool apply)
{
@@ -111,20 +111,20 @@ inline T RoundToInterval(T& num, T floor, T ceil)
}
// UTF8 handling
-bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr);
+TC_COMMON_API bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr);
// in wsize==max size of buffer, out wsize==real string size
-bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize);
+TC_COMMON_API bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize);
inline bool Utf8toWStr(const std::string& utf8str, wchar_t* wstr, size_t& wsize)
{
return Utf8toWStr(utf8str.c_str(), utf8str.size(), wstr, wsize);
}
-bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str);
+TC_COMMON_API bool WStrToUtf8(std::wstring const& wstr, std::string& utf8str);
// size==real string size
-bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str);
+TC_COMMON_API bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str);
-size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence
-void utf8truncate(std::string& utf8str, size_t len);
+TC_COMMON_API size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence
+TC_COMMON_API void utf8truncate(std::string& utf8str, size_t len);
inline bool isBasicLatinCharacter(wchar_t wchar)
{
@@ -303,20 +303,24 @@ inline void wstrToLower(std::wstring& str)
std::transform( str.begin(), str.end(), str.begin(), wcharToLower );
}
-std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension);
+TC_COMMON_API std::wstring GetMainPartOfName(std::wstring const& wname, uint32 declension);
-bool utf8ToConsole(const std::string& utf8str, std::string& conStr);
-bool consoleToUtf8(const std::string& conStr, std::string& utf8str);
-bool Utf8FitTo(const std::string& str, std::wstring const& search);
-void utf8printf(FILE* out, const char *str, ...);
-void vutf8printf(FILE* out, const char *str, va_list* ap);
-bool Utf8ToUpperOnlyLatin(std::string& utf8String);
+TC_COMMON_API bool utf8ToConsole(const std::string& utf8str, std::string& conStr);
+TC_COMMON_API bool consoleToUtf8(const std::string& conStr, std::string& utf8str);
+TC_COMMON_API bool Utf8FitTo(const std::string& str, std::wstring const& search);
+TC_COMMON_API void utf8printf(FILE* out, const char *str, ...);
+TC_COMMON_API void vutf8printf(FILE* out, const char *str, va_list* ap);
+TC_COMMON_API bool Utf8ToUpperOnlyLatin(std::string& utf8String);
-bool IsIPAddress(char const* ipaddress);
+TC_COMMON_API bool IsIPAddress(char const* ipaddress);
-uint32 CreatePIDFile(const std::string& filename);
+TC_COMMON_API uint32 CreatePIDFile(std::string const& filename);
+TC_COMMON_API uint32 GetPID();
-std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false);
+TC_COMMON_API std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false);
+TC_COMMON_API void HexStrToByteArray(std::string const& str, uint8* out, bool reverse = false);
+
+TC_COMMON_API bool StringToBool(std::string const& str);
// simple class for not-modifyable list
template <typename T>
diff --git a/src/server/authserver/CMakeLists.txt b/src/server/authserver/CMakeLists.txt
index 1d40129ec1b..d1f0e4460e9 100644
--- a/src/server/authserver/CMakeLists.txt
+++ b/src/server/authserver/CMakeLists.txt
@@ -31,8 +31,8 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(authserver
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
if( NOT WIN32 )
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp
index a53187ad737..1392900fb9a 100644
--- a/src/server/authserver/Main.cpp
+++ b/src/server/authserver/Main.cpp
@@ -36,12 +36,14 @@
#include "GitRevision.h"
#include "Util.h"
#include <iostream>
+#include <boost/filesystem/path.hpp>
#include <boost/program_options.hpp>
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
using boost::asio::ip::tcp;
using namespace boost::program_options;
+namespace fs = boost::filesystem;
#ifndef _TRINITY_REALM_CONFIG
# define _TRINITY_REALM_CONFIG "authserver.conf"
@@ -69,7 +71,7 @@ void StopDB();
void SignalHandler(const boost::system::error_code& error, int signalNumber);
void KeepDatabaseAliveHandler(const boost::system::error_code& error);
void BanExpiryHandler(boost::system::error_code const& error);
-variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService);
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService);
boost::asio::io_service* _ioService;
boost::asio::deadline_timer* _dbPingTimer;
@@ -81,7 +83,7 @@ int main(int argc, char** argv)
{
signal(SIGABRT, &Trinity::AbortHandler);
- std::string configFile = _TRINITY_REALM_CONFIG;
+ auto configFile = fs::absolute(_TRINITY_REALM_CONFIG);
std::string configService;
auto vm = GetConsoleArguments(argc, argv, configFile, configService);
// exit if help or version is enabled
@@ -98,7 +100,9 @@ int main(int argc, char** argv)
#endif
std::string configError;
- if (!sConfigMgr->LoadInitial(configFile, configError))
+ if (!sConfigMgr->LoadInitial(configFile.generic_string(),
+ std::vector<std::string>(argv, argv + argc),
+ configError))
{
printf("Error in config file: %s\n", configError.c_str());
return 1;
@@ -109,7 +113,7 @@ int main(int argc, char** argv)
TC_LOG_INFO("server.authserver", "%s (authserver)", GitRevision::GetFullVersion());
TC_LOG_INFO("server.authserver", "<Ctrl-C> to stop.\n");
- TC_LOG_INFO("server.authserver", "Using configuration file %s.", configFile.c_str());
+ TC_LOG_INFO("server.authserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str());
TC_LOG_INFO("server.authserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
TC_LOG_INFO("server.authserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
@@ -286,13 +290,14 @@ void ServiceStatusWatcher(boost::system::error_code const& error)
}
#endif
-variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService)
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService)
{
options_description all("Allowed options");
all.add_options()
("help,h", "print usage message")
("version,v", "print version build info")
- ("config,c", value<std::string>(&configFile)->default_value(_TRINITY_REALM_CONFIG), "use <arg> as configuration file")
+ ("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_REALM_CONFIG)),
+ "use <arg> as configuration file")
;
#if PLATFORM == PLATFORM_WINDOWS
options_description win("Windows platform specific options");
diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp
index 982aca58eee..43c327ffda1 100644
--- a/src/server/authserver/Server/AuthSession.cpp
+++ b/src/server/authserver/Server/AuthSession.cpp
@@ -820,41 +820,6 @@ bool AuthSession::HandleReconnectProof()
}
}
-tcp::endpoint const GetAddressForClient(Realm const& realm, ip::address const& clientAddr)
-{
- ip::address realmIp;
-
- // Attempt to send best address for client
- if (clientAddr.is_loopback())
- {
- // Try guessing if realm is also connected locally
- if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback())
- realmIp = clientAddr;
- else
- {
- // Assume that user connecting from the machine that authserver is located on
- // has all realms available in his local network
- realmIp = realm.LocalAddress;
- }
- }
- else
- {
- if (clientAddr.is_v4() &&
- (clientAddr.to_v4().to_ulong() & realm.LocalSubnetMask.to_v4().to_ulong()) ==
- (realm.LocalAddress.to_v4().to_ulong() & realm.LocalSubnetMask.to_v4().to_ulong()))
- {
- realmIp = realm.LocalAddress;
- }
- else
- realmIp = realm.ExternalAddress;
- }
-
- tcp::endpoint endpoint(realmIp, realm.Port);
-
- // Return external IP
- return endpoint;
-}
-
bool AuthSession::HandleRealmList()
{
TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList");
@@ -924,7 +889,7 @@ void AuthSession::RealmListCallback(PreparedQueryResult result)
pkt << uint8(lock); // if 1, then realm locked
pkt << uint8(flag); // RealmFlags
pkt << name;
- pkt << boost::lexical_cast<std::string>(GetAddressForClient(realm, GetRemoteIpAddress()));
+ pkt << boost::lexical_cast<std::string>(realm.GetAddressForClient(GetRemoteIpAddress()));
pkt << float(realm.PopulationLevel);
pkt << uint8(characterCounts[realm.Id.Realm]);
pkt << uint8(realm.Timezone); // realm category
diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h
index 1babb7407a9..027011629eb 100644
--- a/src/server/authserver/Server/AuthSession.h
+++ b/src/server/authserver/Server/AuthSession.h
@@ -23,7 +23,7 @@
#include "ByteBuffer.h"
#include "Socket.h"
#include "BigNumber.h"
-#include "Callback.h"
+#include "QueryCallback.h"
#include <memory>
#include <boost/asio/ip/tcp.hpp>
diff --git a/src/server/authserver/Server/AuthSocketMgr.h b/src/server/authserver/Server/AuthSocketMgr.h
index a16b7d405b9..9923f2b2f11 100644
--- a/src/server/authserver/Server/AuthSocketMgr.h
+++ b/src/server/authserver/Server/AuthSocketMgr.h
@@ -32,9 +32,9 @@ public:
return instance;
}
- bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) override
+ bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port, int threadCount = 1) override
{
- if (!BaseSocketMgr::StartNetwork(service, bindIp, port))
+ if (!BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount))
return false;
_acceptor->AsyncAcceptWithCallback<&AuthSocketMgr::OnSocketAccept>();
diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist
index a97d75b3f57..1d3b48839a8 100644
--- a/src/server/authserver/authserver.conf.dist
+++ b/src/server/authserver/authserver.conf.dist
@@ -179,11 +179,19 @@ LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth"
# LoginDatabase.WorkerThreads
# Description: The amount of worker threads spawned to handle asynchronous (delayed) MySQL
# statements. Each worker thread is mirrored with its own connection to the
+# MySQL server and their own thread on the MySQL server.
# Default: 1
LoginDatabase.WorkerThreads = 1
#
+# LoginDatabase.SynchThreads
+# Description: The amount of MySQL connections spawned to handle.
+# Default: 1 - (LoginDatabase.WorkerThreads)
+
+LoginDatabase.SynchThreads = 1
+
+#
###################################################################################################
###################################################################################################
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
index 6d75ba963e7..bd2fa280ad6 100644
--- a/src/server/database/CMakeLists.txt
+++ b/src/server/database/CMakeLists.txt
@@ -22,8 +22,8 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_library(database
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
# Do NOT add any extra include directory unless it does not create unneeded extra dependencies,
@@ -47,10 +47,11 @@ target_include_directories(database
PRIVATE
${CMAKE_CURRENT_BINARY_DIR})
+add_definitions(-DTRINITY_API_EXPORT_DATABASE)
+
target_link_libraries(database
PUBLIC
common
- process
mysql)
set_target_properties(database
@@ -58,6 +59,18 @@ set_target_properties(database
FOLDER
"server")
+if( BUILD_SHARED_LIBS )
+ if( UNIX )
+ install(TARGETS database
+ LIBRARY
+ DESTINATION lib)
+ elseif( WIN32 )
+ install(TARGETS database
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+endif()
+
# Generate precompiled header
if (USE_COREPCH)
add_cxx_pch(database ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
diff --git a/src/server/database/Database/AdhocStatement.h b/src/server/database/Database/AdhocStatement.h
index ab85493a14e..9315038bce1 100644
--- a/src/server/database/Database/AdhocStatement.h
+++ b/src/server/database/Database/AdhocStatement.h
@@ -25,7 +25,7 @@ typedef std::future<QueryResult> QueryResultFuture;
typedef std::promise<QueryResult> QueryResultPromise;
/*! Raw, ad-hoc query. */
-class BasicStatementTask : public SQLOperation
+class TC_DATABASE_API BasicStatementTask : public SQLOperation
{
public:
BasicStatementTask(const char* sql, bool async = false);
diff --git a/src/server/database/Database/DatabaseEnv.h b/src/server/database/Database/DatabaseEnv.h
index c9a7672ac57..05a4d5aad75 100644
--- a/src/server/database/Database/DatabaseEnv.h
+++ b/src/server/database/Database/DatabaseEnv.h
@@ -39,10 +39,10 @@
#include "Implementation/WorldDatabase.h"
/// Accessor to the world database
-extern WorldDatabaseWorkerPool WorldDatabase;
+TC_DATABASE_API extern WorldDatabaseWorkerPool WorldDatabase;
/// Accessor to the character database
-extern CharacterDatabaseWorkerPool CharacterDatabase;
+TC_DATABASE_API extern CharacterDatabaseWorkerPool CharacterDatabase;
/// Accessor to the realm/login database
-extern LoginDatabaseWorkerPool LoginDatabase;
+TC_DATABASE_API extern LoginDatabaseWorkerPool LoginDatabase;
#endif
diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp
index 6a8e86ffca9..f358bdd54b4 100644
--- a/src/server/database/Database/DatabaseLoader.cpp
+++ b/src/server/database/Database/DatabaseLoader.cpp
@@ -177,9 +177,9 @@ bool DatabaseLoader::Process(std::queue<Predicate>& queue)
return true;
}
-template
-DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>& pool, std::string const& name);
-template
-DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>& pool, std::string const& name);
-template
-DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>& pool, std::string const& name);
+template TC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(LoginDatabaseWorkerPool&, std::string const&);
+template TC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(CharacterDatabaseWorkerPool&, std::string const&);
+template TC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(WorldDatabaseWorkerPool&, std::string const&);
diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h
index d3812eb060d..647c1e113e3 100644
--- a/src/server/database/Database/DatabaseLoader.h
+++ b/src/server/database/Database/DatabaseLoader.h
@@ -27,7 +27,7 @@
// A helper class to initiate all database worker pools,
// handles updating, delays preparing of statements and cleans up on failure.
-class DatabaseLoader
+class TC_DATABASE_API DatabaseLoader
{
public:
DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask);
diff --git a/src/server/database/Database/DatabaseWorker.h b/src/server/database/Database/DatabaseWorker.h
index c21a3d2a343..d6b43943f7d 100644
--- a/src/server/database/Database/DatabaseWorker.h
+++ b/src/server/database/Database/DatabaseWorker.h
@@ -24,7 +24,7 @@
class MySQLConnection;
class SQLOperation;
-class DatabaseWorker
+class TC_DATABASE_API DatabaseWorker
{
public:
DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection);
diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp
index 5d914b6e0e8..ba2a4256919 100644
--- a/src/server/database/Database/DatabaseWorkerPool.cpp
+++ b/src/server/database/Database/DatabaseWorkerPool.cpp
@@ -234,7 +234,7 @@ void DatabaseWorkerPool<T>::EscapeString(std::string& str)
return;
char* buf = new char[str.size() * 2 + 1];
- EscapeString(buf, str.c_str(), str.size());
+ EscapeString(buf, str.c_str(), uint32(str.size()));
str = buf;
delete[] buf;
}
@@ -317,6 +317,6 @@ T* DatabaseWorkerPool<T>::GetFreeConnection()
return connection;
}
-template class DatabaseWorkerPool<LoginDatabaseConnection>;
-template class DatabaseWorkerPool<WorldDatabaseConnection>;
-template class DatabaseWorkerPool<CharacterDatabaseConnection>;
+template class TC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>;
+template class TC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>;
+template class TC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>;
diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h
index d883366237f..ffdde91c0a6 100644
--- a/src/server/database/Database/DatabaseWorkerPool.h
+++ b/src/server/database/Database/DatabaseWorkerPool.h
@@ -19,7 +19,7 @@
#define _DATABASEWORKERPOOL_H
#include "Common.h"
-#include "Callback.h"
+#include "QueryCallback.h"
#include "MySQLConnection.h"
#include "Transaction.h"
#include "DatabaseWorker.h"
@@ -120,7 +120,7 @@ class DatabaseWorkerPool
//! This method should only be used for queries that are only executed once, e.g during startup.
void DirectExecute(const char* sql)
{
- if (!sql)
+ if (Trinity::IsFormatEmptyOrNull(sql))
return;
T* connection = GetFreeConnection();
@@ -175,7 +175,7 @@ class DatabaseWorkerPool
template<typename Format, typename... Args>
QueryResult PQuery(Format&& sql, Args&&... args)
{
- if (!sql)
+ if (Trinity::IsFormatEmptyOrNull(sql))
return QueryResult(nullptr);
return Query(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h
index ec9e626ee1b..123e25dbbf3 100644
--- a/src/server/database/Database/Field.h
+++ b/src/server/database/Database/Field.h
@@ -53,7 +53,7 @@
| SUM, AVG | DECIMAL |
| COUNT | BIGINT |
*/
-class Field
+class TC_DATABASE_API Field
{
friend class ResultSet;
friend class PreparedResultSet;
@@ -323,7 +323,7 @@ class Field
data.value = NULL;
}
- static size_t SizeForType(MYSQL_FIELD* field)
+ static uint32 SizeForType(MYSQL_FIELD* field)
{
switch (field->type)
{
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index e1c0238efe6..430243a8f1e 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -537,7 +537,7 @@ enum CharacterDatabaseStatements
MAX_CHARACTERDATABASE_STATEMENTS
};
-class CharacterDatabaseConnection : public MySQLConnection
+class TC_DATABASE_API CharacterDatabaseConnection : public MySQLConnection
{
public:
typedef CharacterDatabaseStatements Statements;
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index a3789fa2557..e206be16d73 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -116,7 +116,7 @@ enum LoginDatabaseStatements
MAX_LOGINDATABASE_STATEMENTS
};
-class LoginDatabaseConnection : public MySQLConnection
+class TC_DATABASE_API LoginDatabaseConnection : public MySQLConnection
{
public:
typedef LoginDatabaseStatements Statements;
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/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h
index 6ac4ce589e3..e0a02423446 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.h
+++ b/src/server/database/Database/Implementation/WorldDatabase.h
@@ -103,7 +103,7 @@ enum WorldDatabaseStatements
MAX_WORLDDATABASE_STATEMENTS
};
-class WorldDatabaseConnection : public MySQLConnection
+class TC_DATABASE_API WorldDatabaseConnection : public MySQLConnection
{
public:
typedef WorldDatabaseStatements Statements;
diff --git a/src/server/database/Database/MySQLConnection.h b/src/server/database/Database/MySQLConnection.h
index a0b908593df..566995988c0 100644
--- a/src/server/database/Database/MySQLConnection.h
+++ b/src/server/database/Database/MySQLConnection.h
@@ -35,7 +35,7 @@ enum ConnectionFlags
CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH
};
-struct MySQLConnectionInfo
+struct TC_DATABASE_API MySQLConnectionInfo
{
explicit MySQLConnectionInfo(std::string const& infoString)
{
@@ -62,7 +62,7 @@ struct MySQLConnectionInfo
typedef std::map<uint32 /*index*/, std::pair<std::string /*query*/, ConnectionFlags /*sync/async*/> > PreparedStatementMap;
-class MySQLConnection
+class TC_DATABASE_API MySQLConnection
{
template <class T> friend class DatabaseWorkerPool;
friend class PingOperation;
diff --git a/src/server/database/Database/MySQLThreading.h b/src/server/database/Database/MySQLThreading.h
index 1cfa11d7e5b..b6083500989 100644
--- a/src/server/database/Database/MySQLThreading.h
+++ b/src/server/database/Database/MySQLThreading.h
@@ -20,7 +20,7 @@
#include "Log.h"
-class MySQL
+class TC_DATABASE_API MySQL
{
public:
static void Library_Init()
diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp
index 848a923c75d..119f1d4c93b 100644
--- a/src/server/database/Database/PreparedStatement.cpp
+++ b/src/server/database/Database/PreparedStatement.cpp
@@ -344,7 +344,7 @@ void MySQLPreparedStatement::setString(const uint8 index, const char* value)
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
- size_t len = strlen(value) + 1;
+ uint32 len = uint32(strlen(value) + 1);
param->buffer_type = MYSQL_TYPE_VAR_STRING;
delete [] static_cast<char *>(param->buffer);
param->buffer = new char[len];
diff --git a/src/server/database/Database/PreparedStatement.h b/src/server/database/Database/PreparedStatement.h
index 7d6c98463d0..faaec27014f 100644
--- a/src/server/database/Database/PreparedStatement.h
+++ b/src/server/database/Database/PreparedStatement.h
@@ -70,7 +70,7 @@ struct PreparedStatementData
class MySQLPreparedStatement;
//- Upper-level class that is used in code
-class PreparedStatement
+class TC_DATABASE_API PreparedStatement
{
friend class PreparedStatementTask;
friend class MySQLPreparedStatement;
@@ -109,7 +109,7 @@ class PreparedStatement
//- Class of which the instances are unique per MySQLConnection
//- access to these class objects is only done when a prepared statement task
//- is executed.
-class MySQLPreparedStatement
+class TC_DATABASE_API MySQLPreparedStatement
{
friend class MySQLConnection;
friend class PreparedStatement;
@@ -157,7 +157,7 @@ typedef std::future<PreparedQueryResult> PreparedQueryResultFuture;
typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise;
//- Lower-level class, enqueuable operation
-class PreparedStatementTask : public SQLOperation
+class TC_DATABASE_API PreparedStatementTask : public SQLOperation
{
public:
PreparedStatementTask(PreparedStatement* stmt, bool async = false);
diff --git a/src/common/Threading/Callback.h b/src/server/database/Database/QueryCallback.h
index f7eab57ddda..5f6ae74da4f 100644
--- a/src/common/Threading/Callback.h
+++ b/src/server/database/Database/QueryCallback.h
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _CALLBACK_H
-#define _CALLBACK_H
+#ifndef _QUERY_CALLBACK_H
+#define _QUERY_CALLBACK_H
#include <future>
#include "QueryResult.h"
@@ -206,4 +206,4 @@ class QueryCallback_2
QueryCallback_2& operator=(QueryCallback_2 const& right) = delete;
};
-#endif
+#endif // _QUERY_CALLBACK_H
diff --git a/src/server/database/Database/QueryHolder.h b/src/server/database/Database/QueryHolder.h
index 9a5a03fda42..2446a4db2bd 100644
--- a/src/server/database/Database/QueryHolder.h
+++ b/src/server/database/Database/QueryHolder.h
@@ -20,7 +20,7 @@
#include <future>
-class SQLQueryHolder
+class TC_DATABASE_API SQLQueryHolder
{
friend class SQLQueryHolderTask;
private:
@@ -46,7 +46,7 @@ class SQLQueryHolder
typedef std::future<SQLQueryHolder*> QueryResultHolderFuture;
typedef std::promise<SQLQueryHolder*> QueryResultHolderPromise;
-class SQLQueryHolderTask : public SQLOperation
+class TC_DATABASE_API SQLQueryHolderTask : public SQLOperation
{
private:
SQLQueryHolder* m_holder;
diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp
index f02457f67ca..db9e737830c 100644
--- a/src/server/database/Database/QueryResult.cpp
+++ b/src/server/database/Database/QueryResult.cpp
@@ -76,7 +76,7 @@ m_length(NULL)
std::size_t rowSize = 0;
for (uint32 i = 0; i < m_fieldCount; ++i)
{
- size_t size = Field::SizeForType(&field[i]);
+ uint32 size = Field::SizeForType(&field[i]);
rowSize += size;
m_rBind[i].buffer_type = field[i].type;
diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h
index d4d63b5ec85..3b1691db1a6 100644
--- a/src/server/database/Database/QueryResult.h
+++ b/src/server/database/Database/QueryResult.h
@@ -27,7 +27,7 @@
#endif
#include <mysql.h>
-class ResultSet
+class TC_DATABASE_API ResultSet
{
public:
ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount);
@@ -60,7 +60,7 @@ class ResultSet
typedef std::shared_ptr<ResultSet> QueryResult;
-class PreparedResultSet
+class TC_DATABASE_API PreparedResultSet
{
public:
PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount);
diff --git a/src/server/database/Database/SQLOperation.h b/src/server/database/Database/SQLOperation.h
index f0500d1f232..5b3032eab87 100644
--- a/src/server/database/Database/SQLOperation.h
+++ b/src/server/database/Database/SQLOperation.h
@@ -53,7 +53,7 @@ union SQLResultSetUnion
class MySQLConnection;
-class SQLOperation
+class TC_DATABASE_API SQLOperation
{
public:
SQLOperation(): m_conn(NULL) { }
diff --git a/src/server/database/Database/Transaction.h b/src/server/database/Database/Transaction.h
index 5780c0363d9..7b5b6addfe4 100644
--- a/src/server/database/Database/Transaction.h
+++ b/src/server/database/Database/Transaction.h
@@ -25,7 +25,7 @@
class PreparedStatement;
/*! Transactions, high level class. */
-class Transaction
+class TC_DATABASE_API Transaction
{
friend class TransactionTask;
friend class MySQLConnection;
@@ -58,7 +58,7 @@ class Transaction
typedef std::shared_ptr<Transaction> SQLTransaction;
/*! Low level class*/
-class TransactionTask : public SQLOperation
+class TC_DATABASE_API TransactionTask : public SQLOperation
{
template <class T> friend class DatabaseWorkerPool;
friend class DatabaseWorker;
diff --git a/src/server/database/Logging/AppenderDB.h b/src/server/database/Logging/AppenderDB.h
index a6acc66b48c..225ae969802 100644
--- a/src/server/database/Logging/AppenderDB.h
+++ b/src/server/database/Logging/AppenderDB.h
@@ -20,7 +20,7 @@
#include "Appender.h"
-class AppenderDB: public Appender
+class TC_DATABASE_API AppenderDB: public Appender
{
public:
typedef std::integral_constant<AppenderType, APPENDER_DB>::type TypeIndex;
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index b18d6c67612..0d51f8ed060 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -22,19 +22,11 @@
#include "DatabaseLoader.h"
#include "Config.h"
#include "BuiltInConfig.h"
+#include "StartProcess.h"
#include <fstream>
#include <iostream>
#include <unordered_map>
-#include <boost/process.hpp>
-#include <boost/iostreams/stream.hpp>
-#include <boost/iostreams/copy.hpp>
-#include <boost/iostreams/device/file_descriptor.hpp>
-#include <boost/system/system_error.hpp>
-
-using namespace boost::process;
-using namespace boost::process::initializers;
-using namespace boost::iostreams;
std::string DBUpdaterUtil::GetCorrectedMySQLExecutable()
{
@@ -51,22 +43,19 @@ bool DBUpdaterUtil::CheckExecutable()
{
exe.clear();
- try
- {
- exe = search_path("mysql");
- }
- catch (std::runtime_error&)
+ if (auto path = Trinity::SearchExecutableInPath("mysql"))
{
- }
+ exe = std::move(*path);
- if (!exe.empty() && exists(exe))
- {
- // Correct the path to the cli
- corrected_path() = absolute(exe).generic_string();
- return true;
+ if (!exe.empty() && exists(exe))
+ {
+ // Correct the path to the cli
+ corrected_path() = absolute(exe).generic_string();
+ return true;
+ }
}
- TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
+ TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
absolute(exe).generic_string().c_str());
return false;
@@ -387,44 +376,9 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!database.empty())
args.push_back(database);
- // ToDo: use the existing query in memory as virtual file if possible
- file_descriptor_source source(path);
-
- uint32 ret;
- try
- {
- boost::process::pipe outPipe = create_pipe();
- boost::process::pipe errPipe = create_pipe();
-
- child c = execute(run_exe(
- boost::filesystem::absolute(DBUpdaterUtil::GetCorrectedMySQLExecutable()).generic_string()),
- set_args(args), bind_stdin(source), throw_on_error(),
- bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
- bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
-
- file_descriptor_source mysqlOutfd(outPipe.source, close_handle);
- file_descriptor_source mysqlErrfd(errPipe.source, close_handle);
-
- stream<file_descriptor_source> mysqlOutStream(mysqlOutfd);
- stream<file_descriptor_source> mysqlErrStream(mysqlErrfd);
-
- std::stringstream out;
- std::stringstream err;
-
- copy(mysqlOutStream, out);
- copy(mysqlErrStream, err);
-
- TC_LOG_INFO("sql.updates", "%s", out.str().c_str());
- TC_LOG_ERROR("sql.updates", "%s", err.str().c_str());
-
- ret = wait_for_exit(c);
- }
- catch (boost::system::system_error&)
- {
- ret = EXIT_FAILURE;
- }
-
- source.close();
+ // Invokes a mysql process which doesn't leak credentials to logs
+ int const ret = Trinity::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args,
+ "sql.updates", path.generic_string(), true);
if (ret != EXIT_SUCCESS)
{
@@ -436,6 +390,6 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
}
}
-template class DBUpdater<LoginDatabaseConnection>;
-template class DBUpdater<WorldDatabaseConnection>;
-template class DBUpdater<CharacterDatabaseConnection>;
+template class TC_DATABASE_API DBUpdater<LoginDatabaseConnection>;
+template class TC_DATABASE_API DBUpdater<WorldDatabaseConnection>;
+template class TC_DATABASE_API DBUpdater<CharacterDatabaseConnection>;
diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h
index dbb897d2527..cc5d3aad68b 100644
--- a/src/server/database/Updater/DBUpdater.h
+++ b/src/server/database/Updater/DBUpdater.h
@@ -23,7 +23,7 @@
#include <string>
#include <boost/filesystem.hpp>
-class UpdateException : public std::exception
+class TC_DATABASE_API UpdateException : public std::exception
{
public:
UpdateException(std::string const& msg) : _msg(msg) { }
@@ -41,7 +41,7 @@ enum BaseLocation
LOCATION_DOWNLOAD
};
-struct UpdateResult
+struct TC_DATABASE_API UpdateResult
{
UpdateResult()
: updated(0), recent(0), archived(0) { }
@@ -66,7 +66,7 @@ private:
};
template <class T>
-class DBUpdater
+class TC_DATABASE_API DBUpdater
{
public:
using Path = boost::filesystem::path;
diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp
index 2d60cdb92ef..7dc0a307ca2 100644
--- a/src/server/database/Updater/UpdateFetcher.cpp
+++ b/src/server/database/Updater/UpdateFetcher.cpp
@@ -18,6 +18,7 @@
#include "UpdateFetcher.h"
#include "Log.h"
#include "Util.h"
+#include "SHA1.h"
#include <fstream>
#include <chrono>
@@ -25,7 +26,6 @@
#include <sstream>
#include <exception>
#include <unordered_map>
-#include <openssl/sha.h>
using namespace boost::filesystem;
@@ -209,9 +209,8 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
}
}
- // Calculate hash
- std::string const hash =
- CalculateHash(ReadSQLUpdate(availableQuery.first));
+ // Calculate a Sha1 hash based on query content.
+ std::string const hash = CalculateSHA1Hash(ReadSQLUpdate(availableQuery.first));
UpdateMode mode = MODE_APPLY;
@@ -334,15 +333,6 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
}
-std::string UpdateFetcher::CalculateHash(std::string const& query) const
-{
- // Calculate a Sha1 hash based on query content.
- unsigned char digest[SHA_DIGEST_LENGTH];
- SHA1((unsigned char*)query.c_str(), query.length(), (unsigned char*)&digest);
-
- return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
-}
-
uint32 UpdateFetcher::Apply(Path const& path) const
{
using Time = std::chrono::high_resolution_clock;
@@ -354,7 +344,7 @@ uint32 UpdateFetcher::Apply(Path const& path) const
_applyFile(path);
// Return time the query took to apply
- return std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count();
+ return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count());
}
void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h
index c87efea2b02..cabc3c2fce3 100644
--- a/src/server/database/Updater/UpdateFetcher.h
+++ b/src/server/database/Updater/UpdateFetcher.h
@@ -25,7 +25,7 @@
#include <memory>
#include <vector>
-class UpdateFetcher
+class TC_DATABASE_API UpdateFetcher
{
typedef boost::filesystem::path Path;
@@ -112,7 +112,6 @@ private:
AppliedFileStorage ReceiveAppliedFiles() const;
std::string ReadSQLUpdate(Path const& file) const;
- std::string CalculateHash(std::string const& query) const;
uint32 Apply(Path const& path) const;
diff --git a/src/server/game/AI/CoreAI/CombatAI.cpp b/src/server/game/AI/CoreAI/CombatAI.cpp
index 716ac13c666..4d0247d9fb5 100644
--- a/src/server/game/AI/CoreAI/CombatAI.cpp
+++ b/src/server/game/AI/CoreAI/CombatAI.cpp
@@ -50,7 +50,7 @@ void AggressorAI::UpdateAI(uint32 /*diff*/)
void CombatAI::InitializeAI()
{
- for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i)
if (me->m_spells[i] && sSpellMgr->GetSpellInfo(me->m_spells[i]))
spells.push_back(me->m_spells[i]);
diff --git a/src/server/game/AI/CoreAI/CombatAI.h b/src/server/game/AI/CoreAI/CombatAI.h
index 55b91b6969e..f1718fbdb6a 100644
--- a/src/server/game/AI/CoreAI/CombatAI.h
+++ b/src/server/game/AI/CoreAI/CombatAI.h
@@ -25,7 +25,7 @@
class Creature;
-class AggressorAI : public CreatureAI
+class TC_GAME_API AggressorAI : public CreatureAI
{
public:
explicit AggressorAI(Creature* c) : CreatureAI(c) { }
@@ -36,7 +36,7 @@ class AggressorAI : public CreatureAI
typedef std::vector<uint32> SpellVct;
-class CombatAI : public CreatureAI
+class TC_GAME_API CombatAI : public CreatureAI
{
public:
explicit CombatAI(Creature* c) : CreatureAI(c) { }
@@ -55,7 +55,7 @@ class CombatAI : public CreatureAI
SpellVct spells;
};
-class CasterAI : public CombatAI
+class TC_GAME_API CasterAI : public CombatAI
{
public:
explicit CasterAI(Creature* c) : CombatAI(c) { m_attackDist = MELEE_RANGE; }
@@ -67,7 +67,7 @@ class CasterAI : public CombatAI
float m_attackDist;
};
-struct ArcherAI : public CreatureAI
+struct TC_GAME_API ArcherAI : public CreatureAI
{
public:
explicit ArcherAI(Creature* c);
@@ -80,7 +80,7 @@ struct ArcherAI : public CreatureAI
float m_minRange;
};
-struct TurretAI : public CreatureAI
+struct TC_GAME_API TurretAI : public CreatureAI
{
public:
explicit TurretAI(Creature* c);
@@ -97,7 +97,7 @@ struct TurretAI : public CreatureAI
#define VEHICLE_CONDITION_CHECK_TIME 1000
#define VEHICLE_DISMISS_TIME 5000
-struct VehicleAI : public CreatureAI
+struct TC_GAME_API VehicleAI : public CreatureAI
{
public:
explicit VehicleAI(Creature* creature);
diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h
index 4f256a5de31..7a2f23ac804 100644
--- a/src/server/game/AI/CoreAI/GameObjectAI.h
+++ b/src/server/game/AI/CoreAI/GameObjectAI.h
@@ -26,7 +26,7 @@
#include "GameObject.h"
#include "CreatureAI.h"
-class GameObjectAI
+class TC_GAME_API GameObjectAI
{
protected:
GameObject* const go;
@@ -63,7 +63,7 @@ class GameObjectAI
virtual void EventInform(uint32 /*eventId*/) { }
};
-class NullGameObjectAI : public GameObjectAI
+class TC_GAME_API NullGameObjectAI : public GameObjectAI
{
public:
explicit NullGameObjectAI(GameObject* g);
diff --git a/src/server/game/AI/CoreAI/GuardAI.h b/src/server/game/AI/CoreAI/GuardAI.h
index 63f2750a5d4..a6aa4b6624a 100644
--- a/src/server/game/AI/CoreAI/GuardAI.h
+++ b/src/server/game/AI/CoreAI/GuardAI.h
@@ -23,7 +23,7 @@
class Creature;
-class GuardAI : public ScriptedAI
+class TC_GAME_API GuardAI : public ScriptedAI
{
public:
explicit GuardAI(Creature* creature);
diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp
index aafde3c1d9a..3ed2f927645 100644
--- a/src/server/game/AI/CoreAI/PassiveAI.cpp
+++ b/src/server/game/AI/CoreAI/PassiveAI.cpp
@@ -58,6 +58,12 @@ void PossessedAI::KilledUnit(Unit* victim)
victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
+void PossessedAI::OnCharmed(bool /*apply*/)
+{
+ me->NeedChangeAI = true;
+ me->IsAIEnabled = false;
+}
+
void CritterAI::DamageTaken(Unit* /*done_by*/, uint32&)
{
if (!me->HasUnitState(UNIT_STATE_FLEEING))
diff --git a/src/server/game/AI/CoreAI/PassiveAI.h b/src/server/game/AI/CoreAI/PassiveAI.h
index bd72cd7fbe7..9ca9e75bd9f 100644
--- a/src/server/game/AI/CoreAI/PassiveAI.h
+++ b/src/server/game/AI/CoreAI/PassiveAI.h
@@ -21,7 +21,7 @@
#include "CreatureAI.h"
-class PassiveAI : public CreatureAI
+class TC_GAME_API PassiveAI : public CreatureAI
{
public:
explicit PassiveAI(Creature* c);
@@ -33,7 +33,7 @@ class PassiveAI : public CreatureAI
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
-class PossessedAI : public CreatureAI
+class TC_GAME_API PossessedAI : public CreatureAI
{
public:
explicit PossessedAI(Creature* c);
@@ -46,10 +46,12 @@ class PossessedAI : public CreatureAI
void JustDied(Unit*) override;
void KilledUnit(Unit* victim) override;
+ void OnCharmed(bool /*apply*/) override;
+
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
-class NullCreatureAI : public CreatureAI
+class TC_GAME_API NullCreatureAI : public CreatureAI
{
public:
explicit NullCreatureAI(Creature* c);
@@ -63,7 +65,7 @@ class NullCreatureAI : public CreatureAI
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
-class CritterAI : public PassiveAI
+class TC_GAME_API CritterAI : public PassiveAI
{
public:
explicit CritterAI(Creature* c) : PassiveAI(c) { }
@@ -72,7 +74,7 @@ class CritterAI : public PassiveAI
void EnterEvadeMode(EvadeReason why) override;
};
-class TriggerAI : public NullCreatureAI
+class TC_GAME_API TriggerAI : public NullCreatureAI
{
public:
explicit TriggerAI(Creature* c) : NullCreatureAI(c) { }
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index 66b1253069c..2abe20f0ae6 100644
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -225,26 +225,17 @@ void PetAI::UpdateAI(uint32 diff)
//found units to cast on to
if (!targetSpellStore.empty())
{
- uint32 index = urand(0, targetSpellStore.size() - 1);
+ TargetSpellList::iterator it = targetSpellStore.begin();
+ std::advance(it, urand(0, targetSpellStore.size() - 1));
- Spell* spell = targetSpellStore[index].second;
- Unit* target = targetSpellStore[index].first;
+ Spell* spell = (*it).second;
+ Unit* target = (*it).first;
- targetSpellStore.erase(targetSpellStore.begin() + index);
+ targetSpellStore.erase(it);
SpellCastTargets targets;
targets.SetUnitTarget(target);
- if (!me->HasInArc(float(M_PI), target))
- {
- me->SetInFront(target);
- if (target && target->GetTypeId() == TYPEID_PLAYER)
- me->SendUpdateToPlayer(target->ToPlayer());
-
- if (owner && owner->GetTypeId() == TYPEID_PLAYER)
- me->SendUpdateToPlayer(owner->ToPlayer());
- }
-
spell->prepare(&targets);
}
@@ -254,9 +245,9 @@ void PetAI::UpdateAI(uint32 diff)
}
// Update speed as needed to prevent dropping too far behind and despawning
- me->UpdateSpeed(MOVE_RUN, true);
- me->UpdateSpeed(MOVE_WALK, true);
- me->UpdateSpeed(MOVE_FLIGHT, true);
+ me->UpdateSpeed(MOVE_RUN);
+ me->UpdateSpeed(MOVE_WALK);
+ me->UpdateSpeed(MOVE_FLIGHT);
}
@@ -597,6 +588,12 @@ void PetAI::ReceiveEmote(Player* player, uint32 emote)
}
}
+void PetAI::OnCharmed(bool /*apply*/)
+{
+ me->NeedChangeAI = true;
+ me->IsAIEnabled = false;
+}
+
void PetAI::ClearCharmInfoFlags()
{
// Quick access to set all flags to FALSE
diff --git a/src/server/game/AI/CoreAI/PetAI.h b/src/server/game/AI/CoreAI/PetAI.h
index 9c33baa9a9f..93ee6c41ece 100644
--- a/src/server/game/AI/CoreAI/PetAI.h
+++ b/src/server/game/AI/CoreAI/PetAI.h
@@ -25,7 +25,7 @@
class Creature;
class Spell;
-class PetAI : public CreatureAI
+class TC_GAME_API PetAI : public CreatureAI
{
public:
@@ -49,6 +49,8 @@ class PetAI : public CreatureAI
void MoveInLineOfSight_Safe(Unit* /*who*/) { } // CreatureAI interferes with returning pets
void EnterEvadeMode(EvadeReason /*why*/) override { } // For fleeing, pets don't use this type of Evade mechanic
+ void OnCharmed(bool /*apply*/) override;
+
private:
bool _isVisible(Unit*) const;
bool _needToStop(void);
diff --git a/src/server/game/AI/CoreAI/ReactorAI.h b/src/server/game/AI/CoreAI/ReactorAI.h
index e5abaac2f00..d281ca11fdf 100644
--- a/src/server/game/AI/CoreAI/ReactorAI.h
+++ b/src/server/game/AI/CoreAI/ReactorAI.h
@@ -23,7 +23,7 @@
class Unit;
-class ReactorAI : public CreatureAI
+class TC_GAME_API ReactorAI : public CreatureAI
{
public:
diff --git a/src/server/game/AI/CoreAI/TotemAI.h b/src/server/game/AI/CoreAI/TotemAI.h
index e1d1618037f..a0e796ed7e0 100644
--- a/src/server/game/AI/CoreAI/TotemAI.h
+++ b/src/server/game/AI/CoreAI/TotemAI.h
@@ -25,7 +25,7 @@
class Creature;
class Totem;
-class TotemAI : public CreatureAI
+class TC_GAME_API TotemAI : public CreatureAI
{
public:
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index f5a0066a5e3..20a986c0483 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -40,7 +40,7 @@ enum SelectAggroTarget
};
// default predicate function to select target based on distance, player and/or aura criteria
-struct DefaultTargetSelector : public std::unary_function<Unit*, bool>
+struct TC_GAME_API DefaultTargetSelector : public std::unary_function<Unit*, bool>
{
const Unit* me;
float m_dist;
@@ -90,7 +90,7 @@ struct DefaultTargetSelector : public std::unary_function<Unit*, bool>
// Target selector for spell casts checking range, auras and attributes
/// @todo Add more checks from Spell::CheckCast
-struct SpellTargetSelector : public std::unary_function<Unit*, bool>
+struct TC_GAME_API SpellTargetSelector : public std::unary_function<Unit*, bool>
{
public:
SpellTargetSelector(Unit* caster, uint32 spellId);
@@ -104,7 +104,7 @@ struct SpellTargetSelector : public std::unary_function<Unit*, bool>
// Very simple target selector, will just skip main target
// NOTE: When passing to UnitAI::SelectTarget remember to use 0 as position for random selection
// because tank will not be in the temporary list
-struct NonTankTargetSelector : public std::unary_function<Unit*, bool>
+struct TC_GAME_API NonTankTargetSelector : public std::unary_function<Unit*, bool>
{
public:
NonTankTargetSelector(Unit* source, bool playerOnly = true) : _source(source), _playerOnly(playerOnly) { }
@@ -115,7 +115,7 @@ struct NonTankTargetSelector : public std::unary_function<Unit*, bool>
bool _playerOnly;
};
-class UnitAI
+class TC_GAME_API UnitAI
{
protected:
Unit* const me;
@@ -242,6 +242,7 @@ class 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 5b76b583d24..15bbff2793f 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -29,11 +29,13 @@
#include "Language.h"
//Disable CreatureAI when charmed
-void CreatureAI::OnCharmed(bool /*apply*/)
+void CreatureAI::OnCharmed(bool apply)
{
- //me->IsAIEnabled = !apply;*/
- me->NeedChangeAI = true;
- me->IsAIEnabled = false;
+ if (apply)
+ {
+ me->NeedChangeAI = true;
+ me->IsAIEnabled = false;
+ }
}
AISpellInfoType* UnitAI::AISpellInfo;
@@ -44,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;
@@ -257,17 +259,16 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
if (!me->IsAlive())
return false;
- // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle
- // don't remove clone caster on evade (to be verified)
- me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER);
+ me->RemoveAurasOnEvade();
// sometimes bosses stuck in combat?
me->DeleteThreatList();
me->CombatStop(true);
me->LoadCreaturesAddon();
- me->SetLootRecipient(NULL);
+ me->SetLootRecipient(nullptr);
me->ResetPlayerDamageReq();
me->SetLastDamagedTime(0);
+ me->SetCannotReachTarget(false);
if (me->IsInEvadeMode())
return false;
@@ -275,11 +276,11 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
return true;
}
-#define BOUNDARY_VISUALIZE_CREATURE 15425
-#define BOUNDARY_VISUALIZE_CREATURE_SCALE 0.25f
-#define BOUNDARY_VISUALIZE_STEP_SIZE 1
-#define BOUNDARY_VISUALIZE_FAILSAFE_LIMIT 750
-#define BOUNDARY_VISUALIZE_SPAWN_HEIGHT 5
+static const uint32 BOUNDARY_VISUALIZE_CREATURE = 15425;
+static const float BOUNDARY_VISUALIZE_CREATURE_SCALE = 0.25f;
+static const int8 BOUNDARY_VISUALIZE_STEP_SIZE = 1;
+static const int32 BOUNDARY_VISUALIZE_FAILSAFE_LIMIT = 750;
+static const float BOUNDARY_VISUALIZE_SPAWN_HEIGHT = 5.0f;
int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) const
{
typedef std::pair<int32, int32> coordinate;
@@ -295,13 +296,13 @@ int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) con
std::unordered_set<coordinate> outOfBounds;
Position startPosition = owner->GetPosition();
- if (!CheckBoundary(&startPosition)) // fall back to creature position
- {
+ if (!CheckBoundary(&startPosition))
+ { // fall back to creature position
startPosition = me->GetPosition();
if (!CheckBoundary(&startPosition))
- {
+ { // fall back to creature home position
startPosition = me->GetHomePosition();
- if (!CheckBoundary(&startPosition)) // fall back to creature home position
+ if (!CheckBoundary(&startPosition))
return LANG_CREATURE_NO_INTERIOR_POINT_FOUND;
}
}
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index e009e2ed0b6..f8fa6ba532b 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -66,7 +66,7 @@ enum SCEquip
};
typedef std::set<AreaBoundary const*> CreatureBoundary;
-class CreatureAI : public UnitAI
+class TC_GAME_API CreatureAI : public UnitAI
{
protected:
Creature* const me;
@@ -87,6 +87,7 @@ class CreatureAI : public UnitAI
{
EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty
EVADE_REASON_BOUNDARY, // the creature has moved outside its evade boundary
+ EVADE_REASON_NO_PATH, // the creature was unable to reach its target for over 5 seconds
EVADE_REASON_SEQUENCE_BREAK, // this is a boss and the pre-requisite encounters for engaging it are not defeated yet
EVADE_REASON_OTHER
};
@@ -111,7 +112,7 @@ class 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
@@ -137,8 +138,8 @@ class CreatureAI : public UnitAI
virtual void AttackedBy(Unit* /*attacker*/) { }
virtual bool IsEscorted() const { return false; }
- // Called when creature is spawned or respawned (for reseting variables)
- virtual void JustRespawned() { Reset(); }
+ // Called when creature is spawned or respawned
+ virtual void JustRespawned() { }
// Called at waypoint reached or point movement finished
virtual void MovementInform(uint32 /*type*/, uint32 /*id*/) { }
@@ -148,7 +149,7 @@ class 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/CreatureAISelector.h b/src/server/game/AI/CreatureAISelector.h
index 7c7bc705ade..0e3be1a8604 100644
--- a/src/server/game/AI/CreatureAISelector.h
+++ b/src/server/game/AI/CreatureAISelector.h
@@ -27,9 +27,9 @@ class GameObject;
namespace FactorySelector
{
- CreatureAI* selectAI(Creature*);
- MovementGenerator* selectMovementGenerator(Creature*);
- GameObjectAI* SelectGameObjectAI(GameObject*);
+ TC_GAME_API CreatureAI* selectAI(Creature*);
+ TC_GAME_API MovementGenerator* selectMovementGenerator(Creature*);
+ TC_GAME_API GameObjectAI* SelectGameObjectAI(GameObject*);
}
#endif
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 680ecfb9414..12bfb86251b 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -177,7 +177,7 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
for (Player::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
{
- me->Kill(me);
+ me->KillSelf();
return;
}
}
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h
index 429bf1d856c..b717816f9a3 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.h
+++ b/src/server/game/AI/PlayerAI/PlayerAI.h
@@ -22,7 +22,7 @@
#include "Player.h"
#include "Creature.h"
-class PlayerAI : public UnitAI
+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)) { }
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 2010ae45ba5..6475cc8117e 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,22 +183,22 @@ 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[CREATURE_MAX_SPELLS];
- memset(apSpell, 0, CREATURE_MAX_SPELLS * sizeof(SpellInfo*));
+ SpellInfo const* apSpell[MAX_CREATURE_SPELLS];
+ memset(apSpell, 0, MAX_CREATURE_SPELLS * sizeof(SpellInfo*));
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 < CREATURE_MAX_SPELLS; i++)
+ for (uint32 i = 0; i < MAX_CREATURE_SPELLS; i++)
{
tempSpell = sSpellMgr->GetSpellInfo(me->m_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);
@@ -547,7 +547,7 @@ void BossAI::UpdateAI(uint32 diff)
DoMeleeAttackIfReady();
}
-void BossAI::_DespawnAtEvade(uint32 delayToRespawn)
+void BossAI::_DespawnAtEvade(uint32 delayToRespawn, Creature* who)
{
if (delayToRespawn < 2)
{
@@ -555,18 +555,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 5452a033a17..6144a4e5203 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -38,7 +38,7 @@ T* EnsureAI(U* ai)
class InstanceScript;
-class SummonList
+class TC_GAME_API SummonList
{
public:
typedef GuidList StorageType;
@@ -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;
@@ -123,7 +123,7 @@ private:
StorageType storage_;
};
-class EntryCheckPredicate
+class TC_GAME_API EntryCheckPredicate
{
public:
EntryCheckPredicate(uint32 entry) : _entry(entry) { }
@@ -133,13 +133,13 @@ class EntryCheckPredicate
uint32 _entry;
};
-class DummyEntryCheckPredicate
+class TC_GAME_API DummyEntryCheckPredicate
{
public:
bool operator()(ObjectGuid) { return true; }
};
-struct ScriptedAI : public CreatureAI
+struct TC_GAME_API ScriptedAI : public CreatureAI
{
explicit ScriptedAI(Creature* creature);
virtual ~ScriptedAI() { }
@@ -334,7 +334,7 @@ struct ScriptedAI : public CreatureAI
bool _isHeroic;
};
-class BossAI : public ScriptedAI
+class TC_GAME_API BossAI : public ScriptedAI
{
public:
BossAI(Creature* creature, uint32 bossId);
@@ -367,7 +367,7 @@ class BossAI : public ScriptedAI
void _EnterCombat();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
- void _DespawnAtEvade(uint32 delayToRespawn = 30);
+ void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr);
void TeleportCheaters();
@@ -379,7 +379,7 @@ class BossAI : public ScriptedAI
uint32 const _bossId;
};
-class WorldBossAI : public ScriptedAI
+class TC_GAME_API WorldBossAI : public ScriptedAI
{
public:
WorldBossAI(Creature* creature);
@@ -410,10 +410,10 @@ class WorldBossAI : public ScriptedAI
};
// SD2 grid searchers.
-Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true);
-GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange);
-void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
-void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
-void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange);
+TC_GAME_API Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true);
+TC_GAME_API GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange);
+TC_GAME_API void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
+TC_GAME_API void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
+TC_GAME_API void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange);
#endif // SCRIPTEDCREATURE_H_
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/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index 673f3e671a0..37a1464d812 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -49,7 +49,7 @@ enum eEscortState
STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
-struct npc_escortAI : public ScriptedAI
+struct TC_GAME_API npc_escortAI : public ScriptedAI
{
public:
explicit npc_escortAI(Creature* creature);
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/ScriptedAI/ScriptedFollowerAI.h b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
index d1c976b45c8..e17fb7c8507 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
@@ -32,7 +32,7 @@ enum eFollowState
STATE_FOLLOW_POSTEVENT = 0x020 //can be set at complete and allow post event to run
};
-class FollowerAI : public ScriptedAI
+class TC_GAME_API FollowerAI : public ScriptedAI
{
public:
explicit FollowerAI(Creature* creature);
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 361bb1a5b1d..e21f59fe582 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -413,15 +413,10 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
if (!me->IsAlive() || me->IsInEvadeMode())
return;
- me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER);
+ me->RemoveAurasOnEvade();
me->AddUnitState(UNIT_STATE_EVADE);
- me->DeleteThreatList();
- me->CombatStop(true);
- me->LoadCreaturesAddon();
- me->SetLootRecipient(NULL);
- me->ResetPlayerDamageReq();
- me->SetLastDamagedTime(0);
+ _EnterEvadeMode();
GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db
@@ -473,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
@@ -653,8 +648,8 @@ void SmartAI::OnCharmed(bool apply)
{
GetScript()->ProcessEventsFor(SMART_EVENT_CHARMED, NULL, 0, 0, apply);
- if (!apply && !me->IsInEvadeMode() && me->GetCharmerGUID())
- if (Unit* charmer = ObjectAccessor::GetUnit(*me, me->GetCharmerGUID()))
+ if (!apply && !me->IsInEvadeMode())
+ if (Unit* charmer = me->GetCharmer())
AttackStart(charmer);
}
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index 3abe1e5774c..aa7c9ace0b3 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -42,7 +42,7 @@ enum SmartEscortVars
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2
};
-class SmartAI : public CreatureAI
+class TC_GAME_API SmartAI : public CreatureAI
{
public:
~SmartAI(){ }
@@ -230,7 +230,7 @@ class SmartAI : public CreatureAI
bool mJustReset;
};
-class SmartGameObjectAI : public GameObjectAI
+class TC_GAME_API SmartGameObjectAI : public GameObjectAI
{
public:
SmartGameObjectAI(GameObject* g) : GameObjectAI(g) { }
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 859282891fb..a18f0a6574b 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -489,7 +489,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
// unless target is outside spell range, out of mana, or LOS.
bool _allowMove = false;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(e.action.cast.spell);
int32 mana = me->GetPower(POWER_MANA);
if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) ||
@@ -3239,29 +3239,28 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (!me)
return;
- WorldObject* creature = NULL;
+ Creature* creature = nullptr;
if (e.event.distance.guid != 0)
{
creature = FindCreatureNear(me, e.event.distance.guid);
-
if (!creature)
return;
- if (!me->IsInRange(creature, 0, (float)e.event.distance.dist))
+ if (!me->IsInRange(creature, 0, static_cast<float>(e.event.distance.dist)))
return;
}
else if (e.event.distance.entry != 0)
{
std::list<Creature*> list;
- me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist);
+ me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist));
if (!list.empty())
creature = list.front();
}
if (creature)
- ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat);
+ ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat, creature);
break;
}
@@ -3270,29 +3269,28 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (!me)
return;
- WorldObject* gameobject = NULL;
+ GameObject* gameobject = nullptr;
if (e.event.distance.guid != 0)
{
gameobject = FindGameObjectNear(me, e.event.distance.guid);
-
if (!gameobject)
return;
- if (!me->IsInRange(gameobject, 0, (float)e.event.distance.dist))
+ if (!me->IsInRange(gameobject, 0, static_cast<float>(e.event.distance.dist)))
return;
}
else if (e.event.distance.entry != 0)
{
std::list<GameObject*> list;
- me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist);
+ me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist));
if (!list.empty())
gameobject = list.front();
}
if (gameobject)
- ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat);
+ ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat, nullptr, 0, 0, false, nullptr, gameobject);
break;
}
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index e8b89a813b5..a28f2234860 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -28,7 +28,7 @@
#include "SmartScriptMgr.h"
//#include "SmartAI.h"
-class SmartScript
+class TC_GAME_API SmartScript
{
public:
SmartScript();
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index ba1738a523b..86aaf45af88 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -859,7 +859,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (!IsSpellValid(e, e.action.cast.spell))
return false;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(e.action.cast.spell);
for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
if (spellInfo->Effects[j].IsEffect(SPELL_EFFECT_KILL_CREDIT) || spellInfo->Effects[j].IsEffect(SPELL_EFFECT_KILL_CREDIT2))
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 748792b9b42..3105f087bf7 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -1440,7 +1440,7 @@ public:
};
typedef std::unordered_map<uint32, ObjectGuidList*> ObjectListMap;
-class SmartWaypointMgr
+class TC_GAME_API SmartWaypointMgr
{
private:
SmartWaypointMgr() { }
@@ -1472,7 +1472,7 @@ typedef std::unordered_map<int32, SmartAIEventList> SmartAIEventMap;
typedef std::map<uint32 /*entry*/, std::pair<uint32 /*spellId*/, SpellEffIndex /*effIndex*/> > CacheSpellContainer;
typedef std::pair<CacheSpellContainer::const_iterator, CacheSpellContainer::const_iterator> CacheSpellContainerBounds;
-class SmartAIMgr
+class TC_GAME_API SmartAIMgr
{
private:
SmartAIMgr() { }
diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h
index faf28cc707f..f3bb1abe64e 100644
--- a/src/server/game/Accounts/AccountMgr.h
+++ b/src/server/game/Accounts/AccountMgr.h
@@ -49,7 +49,7 @@ typedef std::map<uint32, rbac::RBACPermission*> RBACPermissionsContainer;
typedef std::map<uint8, rbac::RBACPermissionContainer> RBACDefaultPermissionsContainer;
}
-class AccountMgr
+class TC_GAME_API AccountMgr
{
private:
AccountMgr();
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index 1dc98f8b2c1..d1d418b0931 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -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,
@@ -697,6 +697,10 @@ enum RBACPermissions
// 799 - 834 6.x only
RBAC_PERM_COMMAND_DEBUG_LOADCELLS = 835,
RBAC_PERM_COMMAND_DEBUG_BOUNDARY = 836,
+ RBAC_PERM_COMMAND_NPC_EVADE = 837,
+ RBAC_PERM_COMMAND_PET_LEVEL = 838,
+ RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE = 839,
+ RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840,
// custom permissions 1000+
RBAC_PERM_MAX
@@ -714,7 +718,7 @@ enum RBACCommandResult
typedef std::set<uint32> RBACPermissionContainer;
-class RBACPermission
+class TC_GAME_API RBACPermission
{
public:
RBACPermission(uint32 id = 0, std::string const& name = ""):
@@ -749,7 +753,7 @@ class RBACPermission
* - Granted permissions: through linked permissions and directly assigned
* - Denied permissions: through linked permissions and directly assigned
*/
-class RBACData
+class TC_GAME_API RBACData
{
public:
RBACData(uint32 id, std::string const& name, int32 realmId, uint8 secLevel = 255):
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index ebc814405bf..11c2635da33 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -646,13 +646,20 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
{
+ uint32 team = GetPlayer()->GetTeam();
+
// broadcast realm first reached
WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, GetPlayer()->GetName().size() + 1 + 8 + 4 + 4);
data << GetPlayer()->GetName();
data << uint64(GetPlayer()->GetGUID());
data << uint32(achievement->ID);
- data << uint32(0); // 1=link supplied string as player name, 0=display plain string
- sWorld->SendGlobalMessage(&data);
+
+ std::size_t linkTypePos = data.wpos();
+ data << uint32(1); // display name as clickable link in chat
+ sWorld->SendGlobalMessage(&data, nullptr, team);
+
+ data.put<uint32>(linkTypePos, 0); // display name as plain string in chat
+ sWorld->SendGlobalMessage(&data, nullptr, team == ALLIANCE ? HORDE : ALLIANCE);
}
// if player is in world he can tell his friends about new achievement
else if (GetPlayer()->IsInWorld())
@@ -1533,7 +1540,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
//! Since no common attributes were found, (not even in titleRewardFlags field)
//! we explicitly check by ID. Maybe in the future we could move the achievement_reward
//! condition fields to the condition system.
- if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, 0) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
+ if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
GetPlayer()->SetTitle(titleEntry);
diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h
index aba56bfbfc1..c4ac5f0e808 100644
--- a/src/server/game/Achievements/AchievementMgr.h
+++ b/src/server/game/Achievements/AchievementMgr.h
@@ -212,7 +212,7 @@ struct AchievementCriteriaData
bool Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscValue1 = 0) const;
};
-struct AchievementCriteriaDataSet
+struct TC_GAME_API AchievementCriteriaDataSet
{
AchievementCriteriaDataSet() : criteria_id(0) { }
typedef std::vector<AchievementCriteriaData> Storage;
@@ -262,7 +262,7 @@ enum ProgressType
PROGRESS_HIGHEST
};
-class AchievementMgr
+class TC_GAME_API AchievementMgr
{
public:
AchievementMgr(Player* player);
@@ -306,7 +306,7 @@ class AchievementMgr
TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS
};
-class AchievementGlobalMgr
+class TC_GAME_API AchievementGlobalMgr
{
AchievementGlobalMgr() { }
~AchievementGlobalMgr() { }
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h
index 7fc039114e7..42322929a30 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.h
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h
@@ -70,7 +70,7 @@ enum AuctionHouses
AUCTIONHOUSE_NEUTRAL = 7
};
-struct AuctionEntry
+struct TC_GAME_API AuctionEntry
{
uint32 Id;
uint8 houseId;
@@ -101,7 +101,7 @@ struct AuctionEntry
};
//this class is used as auctionhouse instance
-class AuctionHouseObject
+class TC_GAME_API AuctionHouseObject
{
public:
~AuctionHouseObject()
@@ -146,7 +146,7 @@ class AuctionHouseObject
};
-class AuctionHouseMgr
+class TC_GAME_API AuctionHouseMgr
{
private:
AuctionHouseMgr();
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBot.h b/src/server/game/AuctionHouseBot/AuctionHouseBot.h
index 8f90e8fa76f..1a438e01cdb 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBot.h
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBot.h
@@ -196,7 +196,7 @@ enum AuctionBotConfigFloatValues
};
// All basic config data used by other AHBot classes for self-configure.
-class AuctionBotConfig
+class TC_GAME_API AuctionBotConfig
{
private:
AuctionBotConfig(): _itemsPerCycleBoost(1000), _itemsPerCycleNormal(20) {}
@@ -270,7 +270,7 @@ typedef AuctionHouseBotStatusInfoPerType AuctionHouseBotStatusInfo[MAX_AUCTION_H
// This class handle both Selling and Buying method
// (holder of AuctionBotBuyer and AuctionBotSeller objects)
-class AuctionHouseBot
+class TC_GAME_API AuctionHouseBot
{
private:
AuctionHouseBot();
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h
index e1b6b425c48..979f73c0099 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.h
@@ -67,7 +67,7 @@ private:
// This class handle all Buyer method
// (holder of AuctionBotConfig for each auction house type)
-class AuctionBotBuyer : public AuctionBotAgent
+class TC_GAME_API AuctionBotBuyer : public AuctionBotAgent
{
public:
AuctionBotBuyer();
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
index 5acb56b5173..17b104eb388 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
@@ -582,13 +582,13 @@ void AuctionBotSeller::LoadSellerValues(SellerConfiguration& config)
break;
}
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO) / 100);
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO));
config.SetPriceRatioPerClass(ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_PRICE_RATIO));
config.SetPriceRatioPerClass(ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_PRICE_RATIO));
@@ -701,10 +701,10 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
{
uint32 classRatio = config.GetPriceRatioPerClass(ItemClass(itemProto->Class));
uint32 qualityRatio = config.GetPriceRatioPerQuality(AuctionQuality(itemProto->Quality));
- uint32 priceRatio = (classRatio * qualityRatio) / 100;
+ float priceRatio = (classRatio * qualityRatio) / 10000.0f;
- uint32 buyPrice = itemProto->BuyPrice;
- uint32 sellPrice = itemProto->SellPrice;
+ float buyPrice = itemProto->BuyPrice;
+ float sellPrice = itemProto->SellPrice;
if (buyPrice == 0)
{
@@ -712,11 +712,11 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
buyPrice = sellPrice * GetSellModifier(itemProto);
else
{
- uint32 divisor = ((itemProto->Class == 2 || itemProto->Class == 4) ? 284 : 80);
- uint32 tempLevel = (itemProto->ItemLevel == 0 ? 1 : itemProto->ItemLevel);
- uint32 tempQuality = (itemProto->Quality == 0 ? 1 : itemProto->Quality);
+ float divisor = ((itemProto->Class == ITEM_CLASS_WEAPON || itemProto->Class == ITEM_CLASS_ARMOR) ? 284.0f : 80.0f);
+ float tempLevel = (itemProto->ItemLevel == 0 ? 1.0f : itemProto->ItemLevel);
+ float tempQuality = (itemProto->Quality == 0 ? 1.0f : itemProto->Quality);
- buyPrice = tempLevel * tempQuality * GetBuyModifier(itemProto)* tempLevel / divisor;
+ buyPrice = tempLevel * tempQuality * static_cast<float>(GetBuyModifier(itemProto))* tempLevel / divisor;
}
}
@@ -726,14 +726,15 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
if (sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYPRICE_SELLER))
buyPrice = sellPrice;
- uint32 basePrice = (buyPrice * stackCount * priceRatio) / (itemProto->Class == 6 ? 200 : itemProto->BuyCount) / 100;
- uint32 range = basePrice * 0.04;
+ float basePriceFloat = (buyPrice * stackCount * priceRatio) / (itemProto->Class == 6 ? 200.0f : static_cast<float>(itemProto->BuyCount)) / 100.0f;
+ float range = basePriceFloat * 0.04f;
- buyp = urand(basePrice - range, basePrice + range) + 1;
-
- basePrice = buyp * .5;
+ buyp = static_cast<uint32>(frand(basePriceFloat - range, basePriceFloat + range) + 0.5f);
+ if (buyp == 0)
+ buyp = 1;
+ uint32 basePrice = buyp * .5;
range = buyp * .4;
- bidp = urand(basePrice - range, basePrice + range) + 1;
+ bidp = urand(static_cast<uint32>(basePrice - range + 0.5f), static_cast<uint32>(basePrice + range + 0.5f)) + 1;
}
// Determines the stack size to use for the item
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h
index dd82b0f3dda..563c2cda04a 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h
@@ -115,7 +115,7 @@ private:
// This class handle all Selling method
// (holder of AHB_Seller_Config data for each auction house type)
-class AuctionBotSeller : public AuctionBotAgent
+class TC_GAME_API AuctionBotSeller : public AuctionBotAgent
{
public:
typedef std::vector<uint32> ItemPool;
diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h
index ade3e8c9699..6968e31c7bd 100644
--- a/src/server/game/Battlefield/Battlefield.h
+++ b/src/server/game/Battlefield/Battlefield.h
@@ -67,7 +67,7 @@ class BfGraveyard;
typedef std::vector<BfGraveyard*> GraveyardVect;
typedef std::map<ObjectGuid, time_t> PlayerTimerMap;
-class BfCapturePoint
+class TC_GAME_API BfCapturePoint
{
public:
BfCapturePoint(Battlefield* bf);
@@ -135,7 +135,7 @@ class BfCapturePoint
ObjectGuid m_capturePointGUID;
};
-class BfGraveyard
+class TC_GAME_API BfGraveyard
{
public:
BfGraveyard(Battlefield* Bf);
@@ -182,7 +182,7 @@ class BfGraveyard
Battlefield* m_Bf;
};
-class Battlefield : public ZoneScript
+class TC_GAME_API Battlefield : public ZoneScript
{
friend class BattlefieldMgr;
diff --git a/src/server/game/Battlefield/BattlefieldMgr.h b/src/server/game/Battlefield/BattlefieldMgr.h
index 5d2c13538f6..2f2af12927e 100644
--- a/src/server/game/Battlefield/BattlefieldMgr.h
+++ b/src/server/game/Battlefield/BattlefieldMgr.h
@@ -24,7 +24,7 @@ class Player;
class ZoneScript;
// class to handle player enter / leave / areatrigger / GO use events
-class BattlefieldMgr
+class TC_GAME_API BattlefieldMgr
{
public:
static BattlefieldMgr* instance();
diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h
index e5cc392cef3..52789d38d8e 100644
--- a/src/server/game/Battlefield/Zones/BattlefieldWG.h
+++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h
@@ -266,7 +266,7 @@ class WintergraspCapturePoint : public BfCapturePoint
* WinterGrasp Battlefield *
* ######################### */
-class BattlefieldWG : public Battlefield
+class TC_GAME_API BattlefieldWG : public Battlefield
{
public:
~BattlefieldWG();
@@ -1098,7 +1098,7 @@ StaticWintergraspWorkshopInfo const WorkshopData[WG_MAX_WORKSHOP] =
// ********************************************************************
// Structure for different buildings that can be destroyed during battle
-struct BfWGGameObjectBuilding
+struct TC_GAME_API BfWGGameObjectBuilding
{
private:
// WG object
@@ -1150,7 +1150,7 @@ public:
};
// Structure for the 6 workshop
-struct WintergraspWorkshop
+struct TC_GAME_API WintergraspWorkshop
{
private:
BattlefieldWG* _wg; // Pointer to wintergrasp
diff --git a/src/server/game/Battlegrounds/Arena.h b/src/server/game/Battlegrounds/Arena.h
index 863f1fc8c6f..1fe99f1f414 100644
--- a/src/server/game/Battlegrounds/Arena.h
+++ b/src/server/game/Battlegrounds/Arena.h
@@ -36,7 +36,7 @@ enum ArenaWorldStates
ARENA_WORLD_STATE_ALIVE_PLAYERS_GOLD = 3601
};
-class Arena : public Battleground
+class TC_GAME_API Arena : public Battleground
{
protected:
Arena();
diff --git a/src/server/game/Battlegrounds/ArenaScore.h b/src/server/game/Battlegrounds/ArenaScore.h
index c2d9d18c00b..9427a108565 100644
--- a/src/server/game/Battlegrounds/ArenaScore.h
+++ b/src/server/game/Battlegrounds/ArenaScore.h
@@ -21,7 +21,7 @@
#include "BattlegroundScore.h"
#include "SharedDefines.h"
-struct ArenaScore : public BattlegroundScore
+struct TC_GAME_API ArenaScore : public BattlegroundScore
{
friend class Arena;
@@ -56,7 +56,7 @@ struct ArenaScore : public BattlegroundScore
uint8 TeamId; // BattlegroundTeamId
};
-struct ArenaTeamScore
+struct TC_GAME_API ArenaTeamScore
{
friend class Arena;
friend class Battleground;
diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h
index f27c3761bde..7e9f490bff9 100644
--- a/src/server/game/Battlegrounds/ArenaTeam.h
+++ b/src/server/game/Battlegrounds/ArenaTeam.h
@@ -85,7 +85,7 @@ enum ArenaTeamTypes
ARENA_TEAM_5v5 = 5
};
-struct ArenaTeamMember
+struct TC_GAME_API ArenaTeamMember
{
ObjectGuid Guid;
std::string Name;
@@ -113,7 +113,7 @@ struct ArenaTeamStats
#define MAX_ARENA_SLOT 3 // 0..2 slots
-class ArenaTeam
+class TC_GAME_API ArenaTeam
{
public:
ArenaTeam();
diff --git a/src/server/game/Battlegrounds/ArenaTeamMgr.h b/src/server/game/Battlegrounds/ArenaTeamMgr.h
index 269795092c7..7f6800bd71b 100644
--- a/src/server/game/Battlegrounds/ArenaTeamMgr.h
+++ b/src/server/game/Battlegrounds/ArenaTeamMgr.h
@@ -20,7 +20,7 @@
#include "ArenaTeam.h"
-class ArenaTeamMgr
+class TC_GAME_API ArenaTeamMgr
{
private:
ArenaTeamMgr();
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index 03f138a5a8b..bb6ae360536 100644
--- a/src/server/game/Battlegrounds/Battleground.h
+++ b/src/server/game/Battlegrounds/Battleground.h
@@ -221,7 +221,7 @@ This class is used to:
3. some certain cases, same for all battlegrounds
4. It has properties same for all battlegrounds
*/
-class Battleground
+class TC_GAME_API Battleground
{
public:
Battleground();
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h
index 50100ce47f6..dbba964d4b3 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h
@@ -55,7 +55,7 @@ struct BattlegroundTemplate
bool IsArena() const { return BattlemasterEntry->type == MAP_ARENA; }
};
-class BattlegroundMgr
+class TC_GAME_API BattlegroundMgr
{
private:
BattlegroundMgr();
diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp
index 34588c7e380..796afe472fe 100644
--- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp
@@ -161,9 +161,9 @@ GroupQueueInfo* BattlegroundQueue::AddGroup(Player* leader, Group* grp, Battlegr
//announce world (this don't need mutex)
if (isRated && sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE))
{
- ArenaTeam* Team = sArenaTeamMgr->GetArenaTeamById(arenateamid);
- if (Team)
- sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, Team->GetName().c_str(), ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating);
+ ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(arenateamid);
+ if (team)
+ sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, team->GetName().c_str(), ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating);
}
//add players from group to ginfo
@@ -354,8 +354,8 @@ void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount)
// announce to world if arena team left queue for rated match, show only once
if (group->ArenaType && group->IsRated && group->Players.empty() && sWorld->getBoolConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE))
- if (ArenaTeam* Team = sArenaTeamMgr->GetArenaTeamById(group->ArenaTeamId))
- sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, Team->GetName().c_str(), group->ArenaType, group->ArenaType, group->ArenaTeamRating);
+ if (ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(group->ArenaTeamId))
+ sWorld->SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, team->GetName().c_str(), group->ArenaType, group->ArenaType, group->ArenaTeamRating);
// if player leaves queue and he is invited to rated arena match, then he have to lose
if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount)
diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h
index 31f108053d9..f56ccb9bdbf 100644
--- a/src/server/game/Battlegrounds/BattlegroundQueue.h
+++ b/src/server/game/Battlegrounds/BattlegroundQueue.h
@@ -72,7 +72,7 @@ enum BattlegroundQueueInvitationType
};
class Battleground;
-class BattlegroundQueue
+class TC_GAME_API BattlegroundQueue
{
public:
BattlegroundQueue();
@@ -142,7 +142,7 @@ class BattlegroundQueue
This class is used to invite player to BG again, when minute lasts from his first invitation
it is capable to solve all possibilities
*/
-class BGQueueInviteEvent : public BasicEvent
+class TC_GAME_API BGQueueInviteEvent : public BasicEvent
{
public:
BGQueueInviteEvent(ObjectGuid pl_guid, uint32 BgInstanceGUID, BattlegroundTypeId BgTypeId, uint8 arenaType, uint32 removeTime) :
@@ -165,7 +165,7 @@ class BGQueueInviteEvent : public BasicEvent
We must store removeInvite time in case player left queue and joined and is invited again
We must store bgQueueTypeId, because battleground can be deleted already, when player entered it
*/
-class BGQueueRemoveEvent : public BasicEvent
+class TC_GAME_API BGQueueRemoveEvent : public BasicEvent
{
public:
BGQueueRemoveEvent(ObjectGuid pl_guid, uint32 bgInstanceGUID, BattlegroundTypeId BgTypeId, BattlegroundQueueTypeId bgQueueTypeId, uint32 removeTime)
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index bba0614d421..239e59d6dbb 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -21,10 +21,7 @@ endif ()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
-add_library(game
- ${PRIVATE_SOURCES}
- ${PRIVATE_PCH_SOURCE}
-)
+add_definitions(-DTRINITY_API_EXPORT_GAME)
CollectIncludeDirectories(
${CMAKE_CURRENT_SOURCE_DIR}
@@ -32,22 +29,50 @@ CollectIncludeDirectories(
# Exclude
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
+# Provide an interface target for the game project to allow
+# dependent projects to build meanwhile.
+add_library(game-interface INTERFACE)
+
+target_include_directories(game-interface
+ INTERFACE
+ ${PUBLIC_INCLUDES})
+
+target_link_libraries(game-interface
+ INTERFACE
+ shared
+ Detour)
+
+add_library(game
+ ${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES})
+
target_include_directories(game
- PUBLIC
- ${PUBLIC_INCLUDES}
PRIVATE
${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(game
PUBLIC
- shared
- Detour)
+ game-interface
+ PRIVATE
+ efsw)
set_target_properties(game
PROPERTIES
FOLDER
"server")
+if( BUILD_SHARED_LIBS )
+ if( UNIX )
+ install(TARGETS game
+ LIBRARY
+ DESTINATION lib)
+ elseif( WIN32 )
+ install(TARGETS game
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+endif()
+
# Generate precompiled header
if (USE_COREPCH)
add_cxx_pch(game ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
diff --git a/src/server/game/Calendar/CalendarMgr.h b/src/server/game/Calendar/CalendarMgr.h
index 084685f372c..c282e3e202c 100644
--- a/src/server/game/Calendar/CalendarMgr.h
+++ b/src/server/game/Calendar/CalendarMgr.h
@@ -126,7 +126,7 @@ enum CalendarError
#define CALENDAR_MAX_GUILD_EVENTS 100
#define CALENDAR_MAX_INVITES 100
-struct CalendarInvite
+struct TC_GAME_API CalendarInvite
{
public:
CalendarInvite(CalendarInvite const& calendarInvite, uint64 inviteId, uint64 eventId)
@@ -186,7 +186,7 @@ struct CalendarInvite
std::string _text;
};
-struct CalendarEvent
+struct TC_GAME_API CalendarEvent
{
public:
CalendarEvent(CalendarEvent const& calendarEvent, uint64 eventId)
@@ -266,7 +266,7 @@ typedef std::vector<CalendarInvite*> CalendarInviteStore;
typedef std::set<CalendarEvent*> CalendarEventStore;
typedef std::map<uint64 /* eventId */, CalendarInviteStore > CalendarEventInviteStore;
-class CalendarMgr
+class TC_GAME_API CalendarMgr
{
private:
CalendarMgr();
diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h
index 26bc6989d07..319105d2a8d 100644
--- a/src/server/game/Chat/Channels/Channel.h
+++ b/src/server/game/Chat/Channels/Channel.h
@@ -118,7 +118,7 @@ enum ChannelMemberFlags
// 0x80
};
-class Channel
+class TC_GAME_API Channel
{
struct PlayerInfo
{
diff --git a/src/server/game/Chat/Channels/ChannelMgr.h b/src/server/game/Chat/Channels/ChannelMgr.h
index 2cd4edc4fb1..abe45690997 100644
--- a/src/server/game/Chat/Channels/ChannelMgr.h
+++ b/src/server/game/Chat/Channels/ChannelMgr.h
@@ -28,7 +28,7 @@
#define MAX_CHANNEL_PASS_STR 31
-class ChannelMgr
+class TC_GAME_API ChannelMgr
{
typedef std::map<std::wstring, Channel*> ChannelMap;
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index f0e98ee8c1f..ec90a5f7efb 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -32,18 +32,19 @@
#include "ScriptMgr.h"
#include "ChatLink.h"
-bool ChatHandler::load_command_table = true;
+// Lazy loading of the command table cache from commands and the
+// ScriptMgr should be thread safe since the player commands,
+// cli commands and ScriptMgr updates are all dispatched one after
+// one inside the world update loop.
+static Optional<std::vector<ChatCommand>> commandTableCache;
std::vector<ChatCommand> const& ChatHandler::getCommandTable()
{
- static std::vector<ChatCommand> commandTableCache;
-
- if (LoadCommandTable())
+ if (!commandTableCache)
{
- SetLoadCommandTable(false);
-
- std::vector<ChatCommand> cmds = sScriptMgr->GetChatCommands();
- commandTableCache.swap(cmds);
+ // We need to initialize this at top since SetDataForCommandInTable
+ // calls getCommandTable() recursively.
+ commandTableCache = sScriptMgr->GetChatCommands();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
PreparedQueryResult result = WorldDatabase.Query(stmt);
@@ -54,13 +55,18 @@ std::vector<ChatCommand> const& ChatHandler::getCommandTable()
Field* fields = result->Fetch();
std::string name = fields[0].GetString();
- SetDataForCommandInTable(commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
+ SetDataForCommandInTable(*commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
}
while (result->NextRow());
}
}
- return commandTableCache;
+ return *commandTableCache;
+}
+
+void ChatHandler::invalidateCommandTable()
+{
+ commandTableCache.reset();
}
char const* ChatHandler::GetTrinityString(uint32 entry) const
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index 7ce0792cdf9..1c9368275ad 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -36,7 +36,7 @@ class WorldObject;
struct GameTele;
-class ChatCommand
+class TC_GAME_API ChatCommand
{
typedef bool(*pHandler)(ChatHandler*, char const*);
@@ -52,7 +52,7 @@ class ChatCommand
std::vector<ChatCommand> ChildCommands;
};
-class ChatHandler
+class TC_GAME_API ChatHandler
{
public:
WorldSession* GetSession() { return m_session; }
@@ -96,6 +96,7 @@ class ChatHandler
bool ParseCommands(const char* text);
static std::vector<ChatCommand> const& getCommandTable();
+ static void invalidateCommandTable();
bool isValidChatMessage(const char* msg);
void SendGlobalSysMessage(const char *str);
@@ -143,8 +144,6 @@ class ChatHandler
GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(ObjectGuid::LowType lowguid, uint32 entry);
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val){ sentErrorMessage = val; }
- static bool LoadCommandTable() { return load_command_table; }
- static void SetLoadCommandTable(bool val) { load_command_table = val; }
bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd);
protected:
@@ -157,11 +156,10 @@ class ChatHandler
WorldSession* m_session; // != NULL for chat command call and NULL for CLI command
// common global flag
- static bool load_command_table;
bool sentErrorMessage;
};
-class CliHandler : public ChatHandler
+class TC_GAME_API CliHandler : public ChatHandler
{
public:
typedef void Print(void*, char const*);
diff --git a/src/server/game/Chat/ChatLink.h b/src/server/game/Chat/ChatLink.h
index 4adb61bbbf1..0d413ce49df 100644
--- a/src/server/game/Chat/ChatLink.h
+++ b/src/server/game/Chat/ChatLink.h
@@ -34,7 +34,7 @@ class Quest;
///////////////////////////////////////////////////////////////////////////////////////////////////
// ChatLink - abstract base class for various links
-class ChatLink
+class TC_GAME_API ChatLink
{
public:
ChatLink() : _color(0), _startPos(0), _endPos(0) { }
@@ -54,7 +54,7 @@ protected:
};
// ItemChatLink - link to item
-class ItemChatLink : public ChatLink
+class TC_GAME_API ItemChatLink : public ChatLink
{
public:
ItemChatLink() : ChatLink(), _item(NULL), _suffix(NULL), _property(NULL)
@@ -74,7 +74,7 @@ protected:
};
// QuestChatLink - link to quest
-class QuestChatLink : public ChatLink
+class TC_GAME_API QuestChatLink : public ChatLink
{
public:
QuestChatLink() : ChatLink(), _quest(nullptr), _questLevel(0) { }
@@ -87,7 +87,7 @@ protected:
};
// SpellChatLink - link to quest
-class SpellChatLink : public ChatLink
+class TC_GAME_API SpellChatLink : public ChatLink
{
public:
SpellChatLink() : ChatLink(), _spell(nullptr) { }
@@ -99,7 +99,7 @@ protected:
};
// AchievementChatLink - link to quest
-class AchievementChatLink : public ChatLink
+class TC_GAME_API AchievementChatLink : public ChatLink
{
public:
AchievementChatLink() : ChatLink(), _guid(0), _achievement(NULL)
@@ -116,7 +116,7 @@ protected:
};
// TradeChatLink - link to trade info
-class TradeChatLink : public SpellChatLink
+class TC_GAME_API TradeChatLink : public SpellChatLink
{
public:
TradeChatLink() : SpellChatLink(), _minSkillLevel(0), _maxSkillLevel(0), _guid(0) { }
@@ -129,7 +129,7 @@ private:
};
// TalentChatLink - link to talent
-class TalentChatLink : public SpellChatLink
+class TC_GAME_API TalentChatLink : public SpellChatLink
{
public:
TalentChatLink() : SpellChatLink(), _talentId(0), _rankId(0) { }
@@ -141,7 +141,7 @@ private:
};
// EnchantmentChatLink - link to enchantment
-class EnchantmentChatLink : public SpellChatLink
+class TC_GAME_API EnchantmentChatLink : public SpellChatLink
{
public:
EnchantmentChatLink() : SpellChatLink() { }
@@ -149,7 +149,7 @@ public:
};
// GlyphChatLink - link to glyph
-class GlyphChatLink : public SpellChatLink
+class TC_GAME_API GlyphChatLink : public SpellChatLink
{
public:
GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(NULL) { }
@@ -159,7 +159,7 @@ private:
GlyphPropertiesEntry const* _glyph;
};
-class LinkExtractor
+class TC_GAME_API LinkExtractor
{
public:
explicit LinkExtractor(const char* msg);
diff --git a/src/server/game/Combat/HostileRefManager.h b/src/server/game/Combat/HostileRefManager.h
index 96152ed46f7..855f9e3d272 100644
--- a/src/server/game/Combat/HostileRefManager.h
+++ b/src/server/game/Combat/HostileRefManager.h
@@ -29,7 +29,7 @@ class SpellInfo;
//=================================================
-class HostileRefManager : public RefManager<Unit, ThreatManager>
+class TC_GAME_API HostileRefManager : public RefManager<Unit, ThreatManager>
{
private:
Unit* iOwner;
diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h
index 0f4efdbeab9..a68d803304d 100644
--- a/src/server/game/Combat/ThreatManager.h
+++ b/src/server/game/Combat/ThreatManager.h
@@ -39,14 +39,14 @@ class SpellInfo;
//==============================================================
// Class to calculate the real threat based
-struct ThreatCalcHelper
+struct TC_GAME_API ThreatCalcHelper
{
static float calcThreat(Unit* hatedUnit, Unit* hatingUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = NULL);
static bool isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell = NULL);
};
//==============================================================
-class HostileReference : public Reference<Unit, ThreatManager>
+class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager>
{
public:
HostileReference(Unit* refUnit, ThreatManager* threatManager, float threat);
@@ -141,7 +141,7 @@ class HostileReference : public Reference<Unit, ThreatManager>
//==============================================================
class ThreatManager;
-class ThreatContainer
+class TC_GAME_API ThreatContainer
{
friend class ThreatManager;
@@ -198,7 +198,7 @@ class ThreatContainer
//=================================================
-class ThreatManager
+class TC_GAME_API ThreatManager
{
public:
friend class HostileReference;
diff --git a/src/server/game/Combat/UnitEvents.h b/src/server/game/Combat/UnitEvents.h
index e501cdaa4a2..f50edcf3c7d 100644
--- a/src/server/game/Combat/UnitEvents.h
+++ b/src/server/game/Combat/UnitEvents.h
@@ -81,7 +81,7 @@ class UnitBaseEvent
//==============================================================
-class ThreatRefStatusChangeEvent : public UnitBaseEvent
+class TC_GAME_API ThreatRefStatusChangeEvent : public UnitBaseEvent
{
private:
HostileReference* iHostileReference;
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 50e3e64ea2c..74e94135b86 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -440,7 +440,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
if (Unit* unit = object->ToUnit())
{
if (ConditionValue1 == 0)
- condMeets = (unit->getStandState() == ConditionValue2);
+ condMeets = (unit->GetStandState() == ConditionValue2);
else if (ConditionValue2 == 0)
condMeets = unit->IsStandState();
else if (ConditionValue2 == 1)
@@ -738,7 +738,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
@@ -1242,7 +1242,7 @@ bool ConditionMgr::addToGossipMenuItems(Condition* cond) const
bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond) const
{
uint32 conditionEffMask = cond->SourceGroup;
- SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->EnsureSpellInfo(cond->SourceEntry));
+ SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->AssertSpellInfo(cond->SourceEntry));
std::list<uint32> sharedMasks;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h
index 0b49552acb4..83e714781c3 100644
--- a/src/server/game/Conditions/ConditionMgr.h
+++ b/src/server/game/Conditions/ConditionMgr.h
@@ -159,7 +159,7 @@ enum MaxConditionTargets
MAX_CONDITION_TARGETS = 3
};
-struct ConditionSourceInfo
+struct TC_GAME_API ConditionSourceInfo
{
WorldObject* mConditionTargets[MAX_CONDITION_TARGETS]; // an array of targets available for conditions
Condition const* mLastFailedCondition;
@@ -172,7 +172,7 @@ struct ConditionSourceInfo
}
};
-struct Condition
+struct TC_GAME_API Condition
{
ConditionSourceType SourceType; //SourceTypeOrReferenceId
uint32 SourceGroup;
@@ -224,7 +224,7 @@ typedef std::unordered_map<uint32, ConditionsByEntryMap> ConditionEntriesByCreat
typedef std::unordered_map<std::pair<int32, uint32 /*SAI source_type*/>, ConditionsByEntryMap> SmartEventConditionContainer;
typedef std::unordered_map<uint32, ConditionContainer> ConditionReferenceContainer;//only used for references
-class ConditionMgr
+class TC_GAME_API ConditionMgr
{
private:
ConditionMgr();
diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h
index e74b7a9c319..cae1e0329e8 100644
--- a/src/server/game/Conditions/DisableMgr.h
+++ b/src/server/game/Conditions/DisableMgr.h
@@ -57,11 +57,11 @@ enum MMapDisableTypes
namespace DisableMgr
{
- void LoadDisables();
- bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags = 0);
- void CheckQuestDisables();
- bool IsVMAPDisabledFor(uint32 entry, uint8 flags);
- bool IsPathfindingEnabled(uint32 mapId);
+ TC_GAME_API void LoadDisables();
+ TC_GAME_API bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags = 0);
+ TC_GAME_API void CheckQuestDisables();
+ TC_GAME_API bool IsVMAPDisabledFor(uint32 entry, uint8 flags);
+ TC_GAME_API bool IsPathfindingEnabled(uint32 mapId);
}
#endif //TRINITY_DISABLEMGR_H
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index f0ea5b4a5f1..cb2f26e567f 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -19,6 +19,22 @@
#ifndef DBCENUMS_H
#define DBCENUMS_H
+#pragma pack(push, 1)
+
+struct DBCPosition2D
+{
+ float X;
+ float Y;
+};
+
+struct DBCPosition3D
+{
+ float X;
+ float Y;
+ float Z;
+};
+
+#pragma pack(pop)
enum LevelLimit
{
// Client expected level limitation, like as used in DBC item max levels for "until max player level"
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 6c51c71fe7b..36ec418ed56 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -73,6 +73,7 @@ DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt);
DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt);
DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
+DBCStorage <CinematicCameraEntry> sCinematicCameraStore(CinematicCameraEntryfmt);
DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt);
@@ -311,6 +312,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrRacesStore, dbcPath, "ChrRaces.dbc");
+ LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicCameraStore, dbcPath, "CinematicCamera.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc");
@@ -1017,5 +1019,6 @@ ResponseCodes ValidateName(std::string const& name, LocaleConstant locale)
EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender)
{
- return sEmotesTextSoundMap[EmotesTextSoundKey(emote, race, gender)];
+ auto itr = sEmotesTextSoundMap.find(EmotesTextSoundKey(emote, race, gender));
+ return itr != sEmotesTextSoundMap.end() ? itr->second : nullptr;
}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index 56ee1f618dd..00b4141555f 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -27,18 +27,18 @@
#include <list>
typedef std::list<uint32> SimpleFactionsList;
-SimpleFactionsList const* GetFactionTeamList(uint32 faction);
+TC_GAME_API SimpleFactionsList const* GetFactionTeamList(uint32 faction);
-char* GetPetName(uint32 petfamily, uint32 dbclang);
-uint32 GetTalentSpellCost(uint32 spellId);
-TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
+TC_GAME_API char* GetPetName(uint32 petfamily, uint32 dbclang);
+TC_GAME_API uint32 GetTalentSpellCost(uint32 spellId);
+TC_GAME_API TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
-char const* GetRaceName(uint8 race, uint8 locale);
-char const* GetClassName(uint8 class_, uint8 locale);
+TC_GAME_API char const* GetRaceName(uint8 race, uint8 locale);
+TC_GAME_API char const* GetClassName(uint8 class_, uint8 locale);
-WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid);
+TC_GAME_API WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid);
-uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId);
+TC_GAME_API uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId);
enum ContentLevels
{
@@ -46,154 +46,155 @@ enum ContentLevels
CONTENT_61_70,
CONTENT_71_80
};
-ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId);
+TC_GAME_API ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId);
-bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId);
+TC_GAME_API bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId);
-void Zone2MapCoordinates(float &x, float &y, uint32 zone);
-void Map2ZoneCoordinates(float &x, float &y, uint32 zone);
+TC_GAME_API void Zone2MapCoordinates(float &x, float &y, uint32 zone);
+TC_GAME_API void Map2ZoneCoordinates(float &x, float &y, uint32 zone);
typedef std::map<uint32/*pair32(map, diff)*/, MapDifficulty> MapDifficultyMap;
-MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty);
-MapDifficulty const* GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty);
+TC_GAME_API MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty);
+TC_GAME_API MapDifficulty const* GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty);
-uint32 const* /*[MAX_TALENT_TABS]*/ GetTalentTabPages(uint8 cls);
+TC_GAME_API uint32 const* /*[MAX_TALENT_TABS]*/ GetTalentTabPages(uint8 cls);
-uint32 GetLiquidFlags(uint32 liquidType);
+TC_GAME_API uint32 GetLiquidFlags(uint32 liquidType);
-PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level);
-PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id);
+TC_GAME_API PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level);
+TC_GAME_API PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id);
-CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender);
-CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color);
+TC_GAME_API CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender);
+TC_GAME_API CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color);
-LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
+TC_GAME_API LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
-uint32 GetDefaultMapLight(uint32 mapId);
+TC_GAME_API uint32 GetDefaultMapLight(uint32 mapId);
typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRaceClassInfoMap;
typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
-SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
-
-ResponseCodes ValidateName(std::string const& name, LocaleConstant locale);
-
-EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender);
-
-extern DBCStorage <AchievementEntry> sAchievementStore;
-extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
-extern DBCStorage <AreaTableEntry> sAreaTableStore;
-extern DBCStorage <AreaGroupEntry> sAreaGroupStore;
-extern DBCStorage <AreaPOIEntry> sAreaPOIStore;
-extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore;
-extern DBCStorage <AuctionHouseEntry> sAuctionHouseStore;
-extern DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore;
-extern DBCStorage <BannedAddOnsEntry> sBannedAddOnsStore;
-extern DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore;
-extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore;
-extern DBCStorage <ChatChannelsEntry> sChatChannelsStore;
-extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore;
-extern DBCStorage <CharSectionsEntry> sCharSectionsStore;
-extern DBCStorage <CharTitlesEntry> sCharTitlesStore;
-extern DBCStorage <ChrClassesEntry> sChrClassesStore;
-extern DBCStorage <ChrRacesEntry> sChrRacesStore;
-extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore;
-extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
-extern DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
-extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore;
-extern DBCStorage <CreatureModelDataEntry> sCreatureModelDataStore;
-extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore;
-extern DBCStorage <CreatureTypeEntry> sCreatureTypeStore;
-extern DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore;
-extern DBCStorage <DestructibleModelDataEntry> sDestructibleModelDataStore;
-extern DBCStorage <DungeonEncounterEntry> sDungeonEncounterStore;
-extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore;
-extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore;
-extern DBCStorage <EmotesEntry> sEmotesStore;
-extern DBCStorage <EmotesTextEntry> sEmotesTextStore;
-extern DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore;
-extern DBCStorage <FactionEntry> sFactionStore;
-extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore;
-extern DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore;
-extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore;
-extern DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore;
-extern DBCStorage <GlyphSlotEntry> sGlyphSlotStore;
-
-extern DBCStorage <GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore;
-extern DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore;
-extern DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore;
-extern DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore;
-extern DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore;
-extern DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore;
-extern DBCStorage <GtNPCManaCostScalerEntry> sGtNPCManaCostScalerStore;
-extern DBCStorage <GtOCTClassCombatRatingScalarEntry> sGtOCTClassCombatRatingScalarStore;
-extern DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore;
-//extern DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore; -- not used currently
-extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore;
-extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore;
-extern DBCStorage <HolidaysEntry> sHolidaysStore;
-extern DBCStorage <ItemEntry> sItemStore;
-extern DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore;
-//extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore; -- not used currently
-extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore;
-extern DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore;
-extern DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore;
-extern DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore;
-extern DBCStorage <ItemSetEntry> sItemSetStore;
-extern DBCStorage <LFGDungeonEntry> sLFGDungeonStore;
-extern DBCStorage <LiquidTypeEntry> sLiquidTypeStore;
-extern DBCStorage <LockEntry> sLockStore;
-extern DBCStorage <MailTemplateEntry> sMailTemplateStore;
-extern DBCStorage <MapEntry> sMapStore;
-//extern DBCStorage <MapDifficultyEntry> sMapDifficultyStore; -- use GetMapDifficultyData insteed
-extern MapDifficultyMap sMapDifficultyMap;
-extern DBCStorage <MovieEntry> sMovieStore;
-extern DBCStorage <OverrideSpellDataEntry> sOverrideSpellDataStore;
-extern DBCStorage <PowerDisplayEntry> sPowerDisplayStore;
-extern DBCStorage <QuestSortEntry> sQuestSortStore;
-extern DBCStorage <QuestXPEntry> sQuestXPStore;
-extern DBCStorage <QuestFactionRewEntry> sQuestFactionRewardStore;
-extern DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore;
-extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore;
-extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore;
-extern DBCStorage <SkillLineEntry> sSkillLineStore;
-extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
-extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
-extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore;
-extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
-extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore;
-extern DBCStorage <SpellDifficultyEntry> sSpellDifficultyStore;
-extern DBCStorage <SpellDurationEntry> sSpellDurationStore;
-extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore;
-extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore;
-extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore;
-extern PetFamilySpellsStore sPetFamilySpellsStore;
-extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore;
-extern DBCStorage <SpellRangeEntry> sSpellRangeStore;
-extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore;
-extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore;
-extern DBCStorage <SpellEntry> sSpellStore;
-extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore;
-extern DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore;
-extern DBCStorage <TalentEntry> sTalentStore;
-extern DBCStorage <TalentTabEntry> sTalentTabStore;
-extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore;
-extern DBCStorage <TaxiPathEntry> sTaxiPathStore;
-extern TaxiMask sTaxiNodesMask;
-extern TaxiMask sOldContinentsNodesMask;
-extern TaxiMask sHordeTaxiNodesMask;
-extern TaxiMask sAllianceTaxiNodesMask;
-extern TaxiMask sDeathKnightTaxiNodesMask;
-extern TaxiPathSetBySource sTaxiPathSetBySource;
-extern TaxiPathNodesByPath sTaxiPathNodesByPath;
-extern DBCStorage <TeamContributionPointsEntry> sTeamContributionPointsStore;
-extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore;
-extern DBCStorage <VehicleEntry> sVehicleStore;
-extern DBCStorage <VehicleSeatEntry> sVehicleSeatStore;
-extern DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore;
-//extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates
-extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore;
-extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore;
-
-void LoadDBCStores(const std::string& dataPath);
+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 EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender);
+
+TC_GAME_API extern DBCStorage <AchievementEntry> sAchievementStore;
+TC_GAME_API extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
+TC_GAME_API extern DBCStorage <AreaTableEntry> sAreaTableStore;
+TC_GAME_API extern DBCStorage <AreaGroupEntry> sAreaGroupStore;
+TC_GAME_API extern DBCStorage <AreaPOIEntry> sAreaPOIStore;
+TC_GAME_API extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore;
+TC_GAME_API extern DBCStorage <AuctionHouseEntry> sAuctionHouseStore;
+TC_GAME_API extern DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore;
+TC_GAME_API extern DBCStorage <BannedAddOnsEntry> sBannedAddOnsStore;
+TC_GAME_API extern DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore;
+TC_GAME_API extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore;
+TC_GAME_API extern DBCStorage <ChatChannelsEntry> sChatChannelsStore;
+TC_GAME_API extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore;
+TC_GAME_API extern DBCStorage <CharSectionsEntry> sCharSectionsStore;
+TC_GAME_API extern DBCStorage <CharTitlesEntry> sCharTitlesStore;
+TC_GAME_API extern DBCStorage <ChrClassesEntry> sChrClassesStore;
+TC_GAME_API extern DBCStorage <ChrRacesEntry> sChrRacesStore;
+TC_GAME_API extern DBCStorage <CinematicCameraEntry> sCinematicCameraStore;
+TC_GAME_API extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore;
+TC_GAME_API extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
+TC_GAME_API extern DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
+TC_GAME_API extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore;
+TC_GAME_API extern DBCStorage <CreatureModelDataEntry> sCreatureModelDataStore;
+TC_GAME_API extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore;
+TC_GAME_API extern DBCStorage <CreatureTypeEntry> sCreatureTypeStore;
+TC_GAME_API extern DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore;
+TC_GAME_API extern DBCStorage <DestructibleModelDataEntry> sDestructibleModelDataStore;
+TC_GAME_API extern DBCStorage <DungeonEncounterEntry> sDungeonEncounterStore;
+TC_GAME_API extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore;
+TC_GAME_API extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore;
+TC_GAME_API extern DBCStorage <EmotesEntry> sEmotesStore;
+TC_GAME_API extern DBCStorage <EmotesTextEntry> sEmotesTextStore;
+TC_GAME_API extern DBCStorage <EmotesTextSoundEntry> sEmotesTextSoundStore;
+TC_GAME_API extern DBCStorage <FactionEntry> sFactionStore;
+TC_GAME_API extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore;
+TC_GAME_API extern DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore;
+TC_GAME_API extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore;
+TC_GAME_API extern DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore;
+TC_GAME_API extern DBCStorage <GlyphSlotEntry> sGlyphSlotStore;
+
+TC_GAME_API extern DBCStorage <GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore;
+TC_GAME_API extern DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore;
+TC_GAME_API extern DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore;
+TC_GAME_API extern DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore;
+TC_GAME_API extern DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore;
+TC_GAME_API extern DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore;
+TC_GAME_API extern DBCStorage <GtNPCManaCostScalerEntry> sGtNPCManaCostScalerStore;
+TC_GAME_API extern DBCStorage <GtOCTClassCombatRatingScalarEntry> sGtOCTClassCombatRatingScalarStore;
+TC_GAME_API extern DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore;
+//TC_GAME_API extern DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore; -- not used currently
+TC_GAME_API extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore;
+TC_GAME_API extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore;
+TC_GAME_API extern DBCStorage <HolidaysEntry> sHolidaysStore;
+TC_GAME_API extern DBCStorage <ItemEntry> sItemStore;
+TC_GAME_API extern DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore;
+//TC_GAME_API extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore; -- not used currently
+TC_GAME_API extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore;
+TC_GAME_API extern DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore;
+TC_GAME_API extern DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore;
+TC_GAME_API extern DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore;
+TC_GAME_API extern DBCStorage <ItemSetEntry> sItemSetStore;
+TC_GAME_API extern DBCStorage <LFGDungeonEntry> sLFGDungeonStore;
+TC_GAME_API extern DBCStorage <LiquidTypeEntry> sLiquidTypeStore;
+TC_GAME_API extern DBCStorage <LockEntry> sLockStore;
+TC_GAME_API extern DBCStorage <MailTemplateEntry> sMailTemplateStore;
+TC_GAME_API extern DBCStorage <MapEntry> sMapStore;
+//TC_GAME_API extern DBCStorage <MapDifficultyEntry> sMapDifficultyStore; -- use GetMapDifficultyData insteed
+TC_GAME_API extern MapDifficultyMap sMapDifficultyMap;
+TC_GAME_API extern DBCStorage <MovieEntry> sMovieStore;
+TC_GAME_API extern DBCStorage <OverrideSpellDataEntry> sOverrideSpellDataStore;
+TC_GAME_API extern DBCStorage <PowerDisplayEntry> sPowerDisplayStore;
+TC_GAME_API extern DBCStorage <QuestSortEntry> sQuestSortStore;
+TC_GAME_API extern DBCStorage <QuestXPEntry> sQuestXPStore;
+TC_GAME_API extern DBCStorage <QuestFactionRewEntry> sQuestFactionRewardStore;
+TC_GAME_API extern DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore;
+TC_GAME_API extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore;
+TC_GAME_API extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore;
+TC_GAME_API extern DBCStorage <SkillLineEntry> sSkillLineStore;
+TC_GAME_API extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
+TC_GAME_API extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
+TC_GAME_API extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore;
+TC_GAME_API extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
+TC_GAME_API extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore;
+TC_GAME_API extern DBCStorage <SpellDifficultyEntry> sSpellDifficultyStore;
+TC_GAME_API extern DBCStorage <SpellDurationEntry> sSpellDurationStore;
+TC_GAME_API extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore;
+TC_GAME_API extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore;
+TC_GAME_API extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore;
+TC_GAME_API extern PetFamilySpellsStore sPetFamilySpellsStore;
+TC_GAME_API extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore;
+TC_GAME_API extern DBCStorage <SpellRangeEntry> sSpellRangeStore;
+TC_GAME_API extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore;
+TC_GAME_API extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore;
+TC_GAME_API extern DBCStorage <SpellEntry> sSpellStore;
+TC_GAME_API extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore;
+TC_GAME_API extern DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore;
+TC_GAME_API extern DBCStorage <TalentEntry> sTalentStore;
+TC_GAME_API extern DBCStorage <TalentTabEntry> sTalentTabStore;
+TC_GAME_API extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore;
+TC_GAME_API extern DBCStorage <TaxiPathEntry> sTaxiPathStore;
+TC_GAME_API extern TaxiMask sTaxiNodesMask;
+TC_GAME_API extern TaxiMask sOldContinentsNodesMask;
+TC_GAME_API extern TaxiMask sHordeTaxiNodesMask;
+TC_GAME_API extern TaxiMask sAllianceTaxiNodesMask;
+TC_GAME_API extern TaxiMask sDeathKnightTaxiNodesMask;
+TC_GAME_API extern TaxiPathSetBySource sTaxiPathSetBySource;
+TC_GAME_API extern TaxiPathNodesByPath sTaxiPathNodesByPath;
+TC_GAME_API extern DBCStorage <TeamContributionPointsEntry> sTeamContributionPointsStore;
+TC_GAME_API extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore;
+TC_GAME_API extern DBCStorage <VehicleEntry> sVehicleStore;
+TC_GAME_API extern DBCStorage <VehicleSeatEntry> sVehicleSeatStore;
+TC_GAME_API extern DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore;
+//TC_GAME_API extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates
+TC_GAME_API extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore;
+TC_GAME_API extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore;
+
+TC_GAME_API void LoadDBCStores(const std::string& dataPath);
#endif
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index b5dc4489148..13f0198a5e7 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -725,24 +725,20 @@ struct ChrRacesEntry
uint32 expansion; // 68 (0 - original race, 1 - tbc addon, ...)
};
-/* not used
struct CinematicCameraEntry
{
- uint32 id; // 0 index
- char* filename; // 1
- uint32 soundid; // 2 in SoundEntries.dbc or 0
- float start_x; // 3
- float start_y; // 4
- float start_z; // 5
- float unk6; // 6 speed?
+ uint32 ID; // 0
+ char const* Model; // 1 Model filename (translate .mdx to .m2)
+ uint32 SoundID; // 2 Sound ID (voiceover for cinematic)
+ DBCPosition3D Origin; // 3-5 Position in map used for basis for M2 co-ordinates
+ float OriginFacing; // 6 Orientation in map used for basis for M2 co-ordinates
};
-*/
struct CinematicSequencesEntry
{
uint32 Id; // 0 index
//uint32 unk1; // 1 always 0
- //uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
+ uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
// 3-9 always 0
};
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index c61ec997bc2..1accc81714b 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -38,7 +38,8 @@ char const CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi";
char const ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx";
char const ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
char const ChrRacesEntryfmt[] = "niixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
-char const CinematicSequencesEntryfmt[] = "nxxxxxxxxx";
+char const CinematicCameraEntryfmt[] = "nsiffff";
+char const CinematicSequencesEntryfmt[] = "nxixxxxxxx";
char const CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxx";
char const CreatureDisplayInfoExtrafmt[] = "diixxxxxxxxxxxxxxxxxx";
char const CreatureFamilyfmt[] = "nfifiiiiixssssssssssssssssxx";
diff --git a/src/server/game/DataStores/M2Stores.cpp b/src/server/game/DataStores/M2Stores.cpp
new file mode 100644
index 00000000000..69581f16549
--- /dev/null
+++ b/src/server/game/DataStores/M2Stores.cpp
@@ -0,0 +1,266 @@
+/*
+* 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 "DBCStores.h"
+#include "M2Structure.h"
+#include "M2Stores.h"
+#include "Common.h"
+#include "Containers.h"
+#include "Log.h"
+#include "World.h"
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <boost/filesystem/path.hpp>
+
+std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore;
+
+// Convert the geomoetry from a spline value, to an actual WoW XYZ
+G3D::Vector3 TranslateLocation(G3D::Vector4 const* DBCPosition, G3D::Vector3 const* basePosition, G3D::Vector3 const* splineVector)
+{
+ G3D::Vector3 work;
+ float x = basePosition->x + splineVector->x;
+ float y = basePosition->y + splineVector->y;
+ float z = basePosition->z + splineVector->z;
+ float const distance = sqrt((x * x) + (y * y));
+ float angle = std::atan2(x, y) - DBCPosition->w;
+
+ if (angle < 0)
+ angle += 2 * float(M_PI);
+
+ work.x = DBCPosition->x + (distance * sin(angle));
+ work.y = DBCPosition->y + (distance * cos(angle));
+ work.z = DBCPosition->z + z;
+ return work;
+}
+
+// Number of cameras not used. Multiple cameras never used in 3.3.5
+bool readCamera(M2Camera const* cam, uint32 buffSize, M2Header const* header, CinematicCameraEntry const* dbcentry)
+{
+ char const* buffer = reinterpret_cast<char const*>(header);
+
+ FlyByCameraCollection cameras;
+ FlyByCameraCollection targetcam;
+
+ G3D::Vector4 DBCData;
+ DBCData.x = dbcentry->Origin.X;
+ DBCData.y = dbcentry->Origin.Y;
+ DBCData.z = dbcentry->Origin.Z;
+ DBCData.w = dbcentry->OriginFacing;
+
+ // Read target locations, only so that we can calculate orientation
+ for (uint32 k = 0; k < cam->target_positions.timestamps.number; ++k)
+ {
+ // Extract Target positions
+ if (cam->target_positions.timestamps.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ M2Array const* targTsArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.timestamps.offset_elements);
+ if (targTsArray->offset_elements + sizeof(uint32) > buffSize || cam->target_positions.values.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ uint32 const* targTimestamps = reinterpret_cast<uint32 const*>(buffer + targTsArray->offset_elements);
+ M2Array const* targArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.values.offset_elements);
+
+ if (targArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ M2SplineKey<G3D::Vector3> const* targPositions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + targArray->offset_elements);
+
+ // Read the data for this set
+ uint32 currPos = targArray->offset_elements;
+ for (uint32 i = 0; i < targTsArray->number; ++i)
+ {
+ if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ // Translate co-ordinates
+ G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->target_position_base, &targPositions->p0);
+
+ // Add to vector
+ FlyByCamera thisCam;
+ thisCam.timeStamp = targTimestamps[i];
+ thisCam.locations.x = newPos.x;
+ thisCam.locations.y = newPos.y;
+ thisCam.locations.z = newPos.z;
+ thisCam.locations.w = 0.0f;
+ targetcam.push_back(thisCam);
+ targPositions++;
+ currPos += sizeof(M2SplineKey<G3D::Vector3>);
+ }
+ }
+
+ // Read camera positions and timestamps (translating first position of 3 only, we don't need to translate the whole spline)
+ for (uint32 k = 0; k < cam->positions.timestamps.number; ++k)
+ {
+ // Extract Camera positions for this set
+ if (cam->positions.timestamps.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ M2Array const* posTsArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.timestamps.offset_elements);
+ if (posTsArray->offset_elements + sizeof(uint32) > buffSize || cam->positions.values.offset_elements + sizeof(M2Array) > buffSize)
+ return false;
+ uint32 const* posTimestamps = reinterpret_cast<uint32 const*>(buffer + posTsArray->offset_elements);
+ M2Array const* posArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.values.offset_elements);
+ if (posArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ M2SplineKey<G3D::Vector3> const* positions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + posArray->offset_elements);
+
+ // Read the data for this set
+ uint32 currPos = posArray->offset_elements;
+ for (uint32 i = 0; i < posTsArray->number; ++i)
+ {
+ if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize)
+ return false;
+ // Translate co-ordinates
+ G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->position_base, &positions->p0);
+
+ // Add to vector
+ FlyByCamera thisCam;
+ thisCam.timeStamp = posTimestamps[i];
+ thisCam.locations.x = newPos.x;
+ thisCam.locations.y = newPos.y;
+ thisCam.locations.z = newPos.z;
+
+ if (targetcam.size() > 0)
+ {
+ // Find the target camera before and after this camera
+ FlyByCamera lastTarget;
+ FlyByCamera nextTarget;
+
+ // Pre-load first item
+ lastTarget = targetcam[0];
+ nextTarget = targetcam[0];
+ for (uint32 j = 0; j < targetcam.size(); ++j)
+ {
+ nextTarget = targetcam[j];
+ if (targetcam[j].timeStamp > posTimestamps[i])
+ break;
+
+ lastTarget = targetcam[j];
+ }
+
+ float x = lastTarget.locations.x;
+ float y = lastTarget.locations.y;
+ float z = lastTarget.locations.z;
+
+ // Now, the timestamps for target cam and position can be different. So, if they differ we interpolate
+ if (lastTarget.timeStamp != posTimestamps[i])
+ {
+ uint32 timeDiffTarget = nextTarget.timeStamp - lastTarget.timeStamp;
+ uint32 timeDiffThis = posTimestamps[i] - lastTarget.timeStamp;
+ float xDiff = nextTarget.locations.x - lastTarget.locations.x;
+ float yDiff = nextTarget.locations.y - lastTarget.locations.y;
+ float zDiff = nextTarget.locations.z - lastTarget.locations.z;
+ x = lastTarget.locations.x + (xDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ y = lastTarget.locations.y + (yDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ z = lastTarget.locations.z + (zDiff * (float(timeDiffThis) / float(timeDiffTarget)));
+ }
+ float xDiff = x - thisCam.locations.x;
+ float yDiff = y - thisCam.locations.y;
+ thisCam.locations.w = std::atan2(yDiff, xDiff);
+
+ if (thisCam.locations.w < 0)
+ thisCam.locations.w += 2 * float(M_PI);
+ }
+
+ cameras.push_back(thisCam);
+ positions++;
+ currPos += sizeof(M2SplineKey<G3D::Vector3>);
+ }
+ }
+
+ sFlyByCameraStore[dbcentry->ID] = cameras;
+ return true;
+}
+
+void LoadM2Cameras(std::string const& dataPath)
+{
+ sFlyByCameraStore.clear();
+ TC_LOG_INFO("server.loading", ">> Loading Cinematic Camera files");
+
+ uint32 oldMSTime = getMSTime();
+ for (uint32 i = 0; i < sCinematicCameraStore.GetNumRows(); ++i)
+ {
+ if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i))
+ {
+ std::string filenameWork = dataPath;
+ filenameWork.append(dbcentry->Model);
+
+ // Replace slashes (always to forward slash, because boost!)
+ std::replace(filenameWork.begin(), filenameWork.end(), '\\', '/');
+
+ boost::filesystem::path filename = filenameWork;
+
+ // Convert to native format
+ filename.make_preferred();
+
+ // Replace mdx to .m2
+ filename.replace_extension("m2");
+
+ std::ifstream m2file(filename.string().c_str(), std::ios::in | std::ios::binary);
+ if (!m2file.is_open())
+ continue;
+
+ // Get file size
+ m2file.seekg(0, std::ios::end);
+ std::streamoff const fileSize = m2file.tellg();
+
+ // Reject if not at least the size of the header
+ if (static_cast<uint32 const>(fileSize) < sizeof(M2Header))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File is smaller than header size", filename.string().c_str());
+ m2file.close();
+ continue;
+ }
+
+ // Read 4 bytes (signature)
+ m2file.seekg(0, std::ios::beg);
+ char fileCheck[5];
+ m2file.read(fileCheck, 4);
+ fileCheck[4] = 0;
+
+ // Check file has correct magic (MD20)
+ if (strcmp(fileCheck, "MD20"))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File identifier not found", filename.string().c_str());
+ m2file.close();
+ continue;
+ }
+
+ // Now we have a good file, read it all into a vector of char's, then close the file.
+ std::vector<char> buffer(fileSize);
+ m2file.seekg(0, std::ios::beg);
+ if (!m2file.read(buffer.data(), fileSize))
+ {
+ m2file.close();
+ continue;
+ }
+ m2file.close();
+
+ // Read header
+ M2Header const* header = reinterpret_cast<M2Header const*>(buffer.data());
+
+ if (header->ofsCameras + sizeof(M2Camera) > static_cast<uint32 const>(fileSize))
+ {
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.string().c_str());
+ continue;
+ }
+
+ // Get camera(s) - Main header, then dump them.
+ M2Camera const* cam = reinterpret_cast<M2Camera const*>(buffer.data() + header->ofsCameras);
+ if (!readCamera(cam, fileSize, header, dbcentry))
+ TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.string().c_str());
+ }
+ }
+ TC_LOG_INFO("server.loading", ">> Loaded %u cinematic waypoint sets in %u ms", (uint32)sFlyByCameraStore.size(), GetMSTimeDiffToNow(oldMSTime));
+}
diff --git a/src/server/game/DataStores/M2Stores.h b/src/server/game/DataStores/M2Stores.h
new file mode 100644
index 00000000000..97224475e5d
--- /dev/null
+++ b/src/server/game/DataStores/M2Stores.h
@@ -0,0 +1,36 @@
+/*
+* 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 TRINITY_M2STORES_H
+#define TRINITY_M2STORES_H
+
+#include "SharedDefines.h"
+#include "Common.h"
+
+struct FlyByCamera
+{
+ uint32 timeStamp;
+ G3D::Vector4 locations;
+};
+
+typedef std::vector<FlyByCamera> FlyByCameraCollection;
+
+TC_GAME_API extern std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore;
+
+TC_GAME_API void LoadM2Cameras(std::string const& dataPath);
+
+#endif \ No newline at end of file
diff --git a/src/server/game/DataStores/M2Structure.h b/src/server/game/DataStores/M2Structure.h
new file mode 100644
index 00000000000..43e8d008b9f
--- /dev/null
+++ b/src/server/game/DataStores/M2Structure.h
@@ -0,0 +1,133 @@
+/*
+* 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 TRINITY_M2STRUCTURE_H
+#define TRINITY_M2STRUCTURE_H
+
+#include <G3D/Vector3.h>
+#include <G3D/AABox.h>
+
+// Structures for M2 file. Source: https://wowdev.wiki
+#pragma pack(push, 1)
+template<typename T>
+struct M2SplineKey
+{
+ T p0;
+ T p1;
+ T p2;
+};
+
+struct M2Header
+{
+ char Magic[4]; // "MD20"
+ uint32 Version; // The version of the format.
+ uint32 lName; // Length of the model's name including the trailing \0
+ uint32 ofsName; // Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess.
+ uint32 GlobalModelFlags; // 0x0001: tilt x, 0x0002: tilt y, 0x0008: add 2 fields in header, 0x0020: load .phys data (MoP+), 0x0080: has _lod .skin files (MoP?+), 0x0100: is camera related.
+ uint32 nGlobalSequences;
+ uint32 ofsGlobalSequences; // A list of timestamps.
+ uint32 nAnimations;
+ uint32 ofsAnimations; // Information about the animations in the model.
+ uint32 nAnimationLookup;
+ uint32 ofsAnimationLookup; // Mapping of global IDs to the entries in the Animation sequences block.
+ uint32 nBones; // MAX_BONES = 0x100
+ uint32 ofsBones; // Information about the bones in this model.
+ uint32 nKeyBoneLookup;
+ uint32 ofsKeyBoneLookup; // Lookup table for key skeletal bones.
+ uint32 nVertices;
+ uint32 ofsVertices; // Vertices of the model.
+ uint32 nViews; // Views (LOD) are now in .skins.
+ uint32 nSubmeshAnimations;
+ uint32 ofsSubmeshAnimations; // Submesh color and alpha animations definitions.
+ uint32 nTextures;
+ uint32 ofsTextures; // Textures of this model.
+ uint32 nTransparency;
+ uint32 ofsTransparency; // Transparency of textures.
+ uint32 nUVAnimation;
+ uint32 ofsUVAnimation;
+ uint32 nTexReplace;
+ uint32 ofsTexReplace; // Replaceable Textures.
+ uint32 nRenderFlags;
+ uint32 ofsRenderFlags; // Blending modes / render flags.
+ uint32 nBoneLookupTable;
+ uint32 ofsBoneLookupTable; // A bone lookup table.
+ uint32 nTexLookup;
+ uint32 ofsTexLookup; // The same for textures.
+ uint32 nTexUnits; // possibly removed with cata?!
+ uint32 ofsTexUnits; // And texture units. Somewhere they have to be too.
+ uint32 nTransLookup;
+ uint32 ofsTransLookup; // Everything needs its lookup. Here are the transparencies.
+ uint32 nUVAnimLookup;
+ uint32 ofsUVAnimLookup;
+ G3D::AABox BoundingBox; // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height
+ float BoundingSphereRadius;
+ G3D::AABox CollisionBox;
+ float CollisionSphereRadius;
+ uint32 nBoundingTriangles;
+ uint32 ofsBoundingTriangles; // Our bounding volumes. Similar structure like in the old ofsViews.
+ uint32 nBoundingVertices;
+ uint32 ofsBoundingVertices;
+ uint32 nBoundingNormals;
+ uint32 ofsBoundingNormals;
+ uint32 nAttachments;
+ uint32 ofsAttachments; // Attachments are for weapons etc.
+ uint32 nAttachLookup;
+ uint32 ofsAttachLookup; // Of course with a lookup.
+ uint32 nEvents;
+ uint32 ofsEvents; // Used for playing sounds when dying and a lot else.
+ uint32 nLights;
+ uint32 ofsLights; // Lights are mainly used in loginscreens but in wands and some doodads too.
+ uint32 nCameras; // Format of Cameras changed with version 271!
+ uint32 ofsCameras; // The cameras are present in most models for having a model in the Character-Tab.
+ uint32 nCameraLookup;
+ uint32 ofsCameraLookup; // And lookup-time again.
+ uint32 nRibbonEmitters;
+ uint32 ofsRibbonEmitters; // Things swirling around. See the CoT-entrance for light-trails.
+ uint32 nParticleEmitters;
+ uint32 ofsParticleEmitters; // Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles.
+ uint32 nBlendMaps; // This has to deal with blending. Exists IFF (flags & 0x8) != 0. When set, textures blending is overriden by the associated array. See M2/WotLK#Blend_mode_overrides
+ uint32 ofsBlendMaps; // Same as above. Points to an array of uint16 of nBlendMaps entries -- From WoD information.};
+};
+
+struct M2Array
+{
+ uint32_t number;
+ uint32 offset_elements;
+};
+struct M2Track
+{
+ uint16_t interpolation_type;
+ uint16_t global_sequence;
+ M2Array timestamps;
+ M2Array values;
+};
+
+struct M2Camera
+{
+ uint32_t type; // 0: portrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table.
+ float fov; // No radians, no degrees. Multiply by 35 to get degrees.
+ float far_clip;
+ float near_clip;
+ M2Track positions; // How the camera's position moves. Should be 3*3 floats.
+ G3D::Vector3 position_base;
+ M2Track target_positions; // How the target moves. Should be 3*3 floats.
+ G3D::Vector3 target_position_base;
+ M2Track rolldata; // The camera can have some roll-effect. Its 0 to 2*Pi.
+};
+#pragma pack(pop)
+
+#endif \ No newline at end of file
diff --git a/src/server/game/DungeonFinding/LFG.h b/src/server/game/DungeonFinding/LFG.h
index 73c123723b1..b837a0df272 100644
--- a/src/server/game/DungeonFinding/LFG.h
+++ b/src/server/game/DungeonFinding/LFG.h
@@ -101,9 +101,9 @@ typedef std::map<ObjectGuid, LfgLockMap> LfgLockPartyMap;
typedef std::map<ObjectGuid, uint8> LfgRolesMap;
typedef std::map<ObjectGuid, ObjectGuid> LfgGroupsMap;
-std::string ConcatenateDungeons(LfgDungeonSet const& dungeons);
-std::string GetRolesString(uint8 roles);
-std::string GetStateString(LfgState state);
+TC_GAME_API std::string ConcatenateDungeons(LfgDungeonSet const& dungeons);
+TC_GAME_API std::string GetRolesString(uint8 roles);
+TC_GAME_API std::string GetStateString(LfgState state);
} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGGroupData.h b/src/server/game/DungeonFinding/LFGGroupData.h
index 7ae4777cd4d..62a41b6b350 100644
--- a/src/server/game/DungeonFinding/LFGGroupData.h
+++ b/src/server/game/DungeonFinding/LFGGroupData.h
@@ -31,7 +31,7 @@ enum LfgGroupEnum
/**
Stores all lfg data needed about a group.
*/
-class LfgGroupData
+class TC_GAME_API LfgGroupData
{
public:
LfgGroupData();
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index fe997bc0343..e72859c23e7 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -40,8 +40,6 @@ namespace lfg
LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1),
m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK))
{
- new LFGPlayerScript();
- new LFGGroupScript();
}
LFGMgr::~LFGMgr()
@@ -1148,7 +1146,7 @@ void LFGMgr::RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdate
for (GuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it)
{
ObjectGuid guid = *it;
- queue.AddToQueue(guid);
+ queue.AddToQueue(guid, true);
}
ProposalsStore.erase(itProposal);
diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h
index 7be08d448db..c72c0a3cdab 100644
--- a/src/server/game/DungeonFinding/LFGMgr.h
+++ b/src/server/game/DungeonFinding/LFGMgr.h
@@ -289,7 +289,7 @@ struct LFGDungeonData
uint32 Entry() const { return id + (type << 24); }
};
-class LFGMgr
+class TC_GAME_API LFGMgr
{
private:
LFGMgr();
diff --git a/src/server/game/DungeonFinding/LFGPlayerData.h b/src/server/game/DungeonFinding/LFGPlayerData.h
index 859317ca956..91e4153a6d5 100644
--- a/src/server/game/DungeonFinding/LFGPlayerData.h
+++ b/src/server/game/DungeonFinding/LFGPlayerData.h
@@ -26,7 +26,7 @@ namespace lfg
/**
Stores all lfg data needed about the player.
*/
-class LfgPlayerData
+class TC_GAME_API LfgPlayerData
{
public:
LfgPlayerData();
diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp
index 314803d1602..d156158dee6 100644
--- a/src/server/game/DungeonFinding/LFGQueue.cpp
+++ b/src/server/game/DungeonFinding/LFGQueue.cpp
@@ -117,7 +117,7 @@ std::string LFGQueue::GetDetailedMatchRoles(GuidList const& check) const
return o.str();
}
-void LFGQueue::AddToQueue(ObjectGuid guid)
+void LFGQueue::AddToQueue(ObjectGuid guid, bool reAdd)
{
LfgQueueDataContainer::iterator itQueue = QueueDataStore.find(guid);
if (itQueue == QueueDataStore.end())
@@ -126,7 +126,10 @@ void LFGQueue::AddToQueue(ObjectGuid guid)
return;
}
- AddToNewQueue(guid);
+ if (reAdd)
+ AddToFrontCurrentQueue(guid);
+ else
+ AddToNewQueue(guid);
}
void LFGQueue::RemoveFromQueue(ObjectGuid guid)
@@ -171,6 +174,11 @@ void LFGQueue::AddToCurrentQueue(ObjectGuid guid)
currentQueueStore.push_back(guid);
}
+void LFGQueue::AddToFrontCurrentQueue(ObjectGuid guid)
+{
+ currentQueueStore.push_front(guid);
+}
+
void LFGQueue::RemoveFromCurrentQueue(ObjectGuid guid)
{
currentQueueStore.remove(guid);
diff --git a/src/server/game/DungeonFinding/LFGQueue.h b/src/server/game/DungeonFinding/LFGQueue.h
index ed8193ab605..32711e81468 100644
--- a/src/server/game/DungeonFinding/LFGQueue.h
+++ b/src/server/game/DungeonFinding/LFGQueue.h
@@ -83,13 +83,13 @@ typedef std::map<ObjectGuid, LfgQueueData> LfgQueueDataContainer;
/**
Stores all data related to queue
*/
-class LFGQueue
+class TC_GAME_API LFGQueue
{
public:
// Add/Remove from queue
std::string GetDetailedMatchRoles(GuidList const& check) const;
- void AddToQueue(ObjectGuid guid);
+ void AddToQueue(ObjectGuid guid, bool reAdd = false);
void RemoveFromQueue(ObjectGuid guid);
void AddQueueData(ObjectGuid guid, time_t joinTime, LfgDungeonSet const& dungeons, LfgRolesMap const& rolesMap);
void RemoveQueueData(ObjectGuid guid);
@@ -116,6 +116,7 @@ class LFGQueue
void AddToNewQueue(ObjectGuid guid);
void AddToCurrentQueue(ObjectGuid guid);
+ void AddToFrontCurrentQueue(ObjectGuid guid);
void RemoveFromNewQueue(ObjectGuid guid);
void RemoveFromCurrentQueue(ObjectGuid guid);
diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp
index 79d36055870..1d2b963b254 100644
--- a/src/server/game/DungeonFinding/LFGScripts.cpp
+++ b/src/server/game/DungeonFinding/LFGScripts.cpp
@@ -241,4 +241,10 @@ void LFGGroupScript::OnInviteMember(Group* group, ObjectGuid guid)
sLFGMgr->LeaveLfg(leader);
}
+void AddSC_LFGScripts()
+{
+ new LFGPlayerScript();
+ new LFGGroupScript();
+}
+
} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGScripts.h b/src/server/game/DungeonFinding/LFGScripts.h
index 377614bc55d..9f52668ea61 100644
--- a/src/server/game/DungeonFinding/LFGScripts.h
+++ b/src/server/game/DungeonFinding/LFGScripts.h
@@ -29,7 +29,7 @@ class Group;
namespace lfg
{
-class LFGPlayerScript : public PlayerScript
+class TC_GAME_API LFGPlayerScript : public PlayerScript
{
public:
LFGPlayerScript();
@@ -40,7 +40,7 @@ class LFGPlayerScript : public PlayerScript
void OnMapChanged(Player* player) override;
};
-class LFGGroupScript : public GroupScript
+class TC_GAME_API LFGGroupScript : public GroupScript
{
public:
LFGGroupScript();
@@ -53,4 +53,6 @@ class LFGGroupScript : public GroupScript
void OnInviteMember(Group* group, ObjectGuid guid) override;
};
+/*keep private*/ void AddSC_LFGScripts();
+
} // namespace lfg
diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h
index 5062645eac8..c9dd0b01509 100644
--- a/src/server/game/Entities/Corpse/Corpse.h
+++ b/src/server/game/Entities/Corpse/Corpse.h
@@ -46,7 +46,7 @@ enum CorpseFlags
CORPSE_FLAG_LOOTABLE = 0x20
};
-class Corpse : public WorldObject, public GridObject<Corpse>
+class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse>
{
public:
explicit Corpse(CorpseType type = CORPSE_BONES);
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index d4efaf62257..22e917448c3 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -54,7 +54,7 @@ TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
if (itr != spellList.end())
return &itr->second;
- return NULL;
+ return nullptr;
}
bool VendorItemData::RemoveItem(uint32 item_id)
@@ -78,7 +78,7 @@ VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extend
for (VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i)
if ((*i)->item == item_id && (*i)->ExtendedCost == extendedCost)
return *i;
- return NULL;
+ return nullptr;
}
uint32 CreatureTemplate::GetRandomValidModelId() const
@@ -182,13 +182,13 @@ m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0),
m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0),
m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
-m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
-m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(NULL), m_creatureData(NULL), m_waypointID(0), m_path_id(0), m_formation(NULL)
+m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
+m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
m_valuesCount = UNIT_END;
- for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
m_spells[i] = 0;
DisableReputationGain = false;
@@ -197,16 +197,14 @@ m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(
m_CombatDistance = 0;//MELEE_RANGE;
ResetLootMode(); // restore default loot mode
- TriggerJustRespawned = false;
+ m_TriggerJustRespawned = false;
m_isTempWorldObject = false;
- _focusSpell = NULL;
- _focusDelay = 0;
}
Creature::~Creature()
{
delete i_AI;
- i_AI = NULL;
+ i_AI = nullptr;
//if (m_uint32Values)
// TC_LOG_ERROR("entities.unit", "Deconstruct Creature Entry = %u", GetEntry());
@@ -344,10 +342,10 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
m_creatureInfo = cinfo; // map mode related always
// equal to player Race field, but creature does not have race
- SetByteValue(UNIT_FIELD_BYTES_0, 0, 0);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, 0);
// known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
- SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
// Cancel load if no model defined
if (!(cinfo->GetFirstValidModelId()))
@@ -366,7 +364,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
// Load creature equipment
if (data && data->equipmentId != 0)
@@ -381,10 +379,10 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
- SetSpeed(MOVE_WALK, cinfo->speed_walk);
- SetSpeed(MOVE_RUN, cinfo->speed_run);
- SetSpeed(MOVE_SWIM, 1.0f); // using 1.0 rate
- SetSpeed(MOVE_FLIGHT, 1.0f); // using 1.0 rate
+ SetSpeedRate(MOVE_WALK, cinfo->speed_walk);
+ SetSpeedRate(MOVE_RUN, cinfo->speed_run);
+ SetSpeedRate(MOVE_SWIM, 1.0f); // using 1.0 rate
+ SetSpeedRate(MOVE_FLIGHT, 1.0f); // using 1.0 rate
// Will set UNIT_FIELD_BOUNDINGRADIUS and UNIT_FIELD_COMBATREACH
SetObjectScale(cinfo->scale);
@@ -396,7 +394,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
if (!m_respawnradius && m_defaultMovementType == RANDOM_MOTION_TYPE)
m_defaultMovementType = IDLE_MOTION_TYPE;
- for (uint8 i=0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint8 i=0; i < MAX_CREATURE_SPELLS; ++i)
m_spells[i] = GetCreatureTemplate()->spells[i];
return true;
@@ -490,9 +488,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
void Creature::Update(uint32 diff)
{
- if (IsAIEnabled && TriggerJustRespawned)
+ if (IsAIEnabled && m_TriggerJustRespawned)
{
- TriggerJustRespawned = false;
+ m_TriggerJustRespawned = false;
AI()->JustRespawned();
if (m_vehicleKit)
m_vehicleKit->Reset();
@@ -650,33 +648,32 @@ void Creature::Update(uint32 diff)
m_regenTimer -= diff;
}
- if (m_regenTimer != 0)
- break;
-
- bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim
- !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player
- !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster
+ if (m_regenTimer == 0)
+ {
+ bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim
+ !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player
+ !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster
- /*if (m_regenTimer <= diff)
- {*/
- if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed())) // regenerate health if not in combat or if polymorphed
- RegenerateHealth();
+ if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed() || CanNotReachTarget())) // regenerate health if not in combat or if polymorphed
+ RegenerateHealth();
- if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER))
- {
- if (getPowerType() == POWER_ENERGY)
- Regenerate(POWER_ENERGY);
- else
- RegenerateMana();
+ if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER))
+ {
+ if (getPowerType() == POWER_ENERGY)
+ Regenerate(POWER_ENERGY);
+ else
+ RegenerateMana();
+ }
+ m_regenTimer = CREATURE_REGEN_INTERVAL;
}
- /*if (!bIsPolymorphed) // only increase the timer if not polymorphed
- m_regenTimer += CREATURE_REGEN_INTERVAL - diff;
+ if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid())
+ {
+ m_cannotReachTimer += diff;
+ if (m_cannotReachTimer >= CREATURE_NOPATH_EVADE_TIME)
+ if (IsAIEnabled)
+ AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_PATH);
}
- else
- if (!bIsPolymorphed) // if polymorphed, skip the timer
- m_regenTimer -= diff;*/
- m_regenTimer = CREATURE_REGEN_INTERVAL;
break;
}
default:
@@ -769,7 +766,7 @@ void Creature::DoFleeToGetAssistance()
float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS);
if (radius >0)
{
- Creature* creature = NULL;
+ Creature* creature = nullptr;
CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY()));
Cell cell(p);
@@ -782,7 +779,7 @@ void Creature::DoFleeToGetAssistance()
cell.Visit(p, grid_creature_searcher, *GetMap(), *this, radius);
SetNoSearchAssistance(true);
- UpdateSpeed(MOVE_RUN, false);
+ UpdateSpeed(MOVE_RUN);
if (!creature)
//SetFeared(true, EnsureVictim()->GetGUID(), 0, sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY));
@@ -793,6 +790,24 @@ void Creature::DoFleeToGetAssistance()
}
}
+bool Creature::AIM_Destroy()
+{
+ if (m_AI_locked)
+ {
+ TC_LOG_DEBUG("scripts", "AIM_Destroy: failed to destroy, locked.");
+ return false;
+ }
+
+ ASSERT(!i_disabledAI,
+ "The disabled AI wasn't cleared!");
+
+ delete i_AI;
+ i_AI = nullptr;
+
+ IsAIEnabled = false;
+ return true;
+}
+
bool Creature::AIM_Initialize(CreatureAI* ai)
{
// make sure nothing can change the AI during AI update
@@ -802,12 +817,12 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
return false;
}
- UnitAI* oldAI = i_AI;
+ AIM_Destroy();
Motion_Initialize();
i_AI = ai ? ai : FactorySelector::selectAI(this);
- delete oldAI;
+
IsAIEnabled = true;
i_AI->InitializeAI();
// Initialize vehicle
@@ -891,7 +906,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
LastUsedScriptID = GetCreatureTemplate()->ScriptID;
@@ -969,14 +984,14 @@ bool Creature::isCanTrainingAndResetTalentsOf(Player* player) const
Player* Creature::GetLootRecipient() const
{
if (!m_lootRecipient)
- return NULL;
+ return nullptr;
return ObjectAccessor::FindConnectedPlayer(m_lootRecipient);
}
Group* Creature::GetLootRecipientGroup() const
{
if (!m_lootRecipientGroup)
- return NULL;
+ return nullptr;
return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
}
@@ -984,7 +999,7 @@ void Creature::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)
{
@@ -1608,7 +1623,7 @@ void Creature::setDeathState(DeathState s)
if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss())
SaveRespawnTime();
- ReleaseFocus(); // remove spellcast focus (this also clears unit target)
+ ReleaseFocus(nullptr, false); // remove spellcast focus
SetTarget(ObjectGuid::Empty); // drop target - dead mobs shouldn't ever target things
SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
@@ -1620,7 +1635,7 @@ void Creature::setDeathState(DeathState s)
if (HasSearchedAssistance())
{
SetNoSearchAssistance(false);
- UpdateSpeed(MOVE_RUN, false);
+ UpdateSpeed(MOVE_RUN);
}
//Dismiss group if is leader
@@ -1637,7 +1652,7 @@ void Creature::setDeathState(DeathState s)
//if (IsPet())
// setActive(true);
SetFullHealth();
- SetLootRecipient(NULL);
+ SetLootRecipient(nullptr);
ResetPlayerDamageReq();
UpdateMovementFlags();
@@ -1707,7 +1722,7 @@ void Creature::Respawn(bool force)
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
GetMotionMaster()->InitDefault();
@@ -1717,7 +1732,7 @@ void Creature::Respawn(bool force)
{
//reset the AI to be sure no dirty or uninitialized values will be used till next tick
AI()->Reset();
- TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
+ m_TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing
}
uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0;
@@ -1808,15 +1823,15 @@ 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)
{
if (!victim)
- return NULL;
+ return nullptr;
- for (uint32 i=0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint32 i=0; i < MAX_CREATURE_SPELLS; ++i)
{
if (!m_spells[i])
continue;
@@ -1856,15 +1871,15 @@ SpellInfo const* Creature::reachWithSpellAttack(Unit* victim)
continue;
return spellInfo;
}
- return NULL;
+ return nullptr;
}
SpellInfo const* Creature::reachWithSpellCure(Unit* victim)
{
if (!victim)
- return NULL;
+ return nullptr;
- for (uint32 i=0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint32 i=0; i < MAX_CREATURE_SPELLS; ++i)
{
if (!m_spells[i])
continue;
@@ -1903,7 +1918,7 @@ SpellInfo const* Creature::reachWithSpellCure(Unit* victim)
continue;
return spellInfo;
}
- return NULL;
+ return nullptr;
}
// select nearest hostile unit within the given distance (regardless of threat list).
@@ -1913,7 +1928,7 @@ Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) c
Cell cell(p);
cell.SetNoCreate();
- Unit* target = NULL;
+ Unit* target = nullptr;
{
if (dist == 0.0f)
@@ -1939,7 +1954,7 @@ Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const
Cell cell(p);
cell.SetNoCreate();
- Unit* target = NULL;
+ Unit* target = nullptr;
if (dist > MAX_VISIBILITY_DISTANCE)
{
@@ -1963,7 +1978,7 @@ Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const
Player* Creature::SelectNearestPlayer(float distance) const
{
- Player* target = NULL;
+ Player* target = nullptr;
Trinity::NearestPlayerInObjectRangeCheck checker(this, distance);
Trinity::PlayerLastSearcher<Trinity::NearestPlayerInObjectRangeCheck> searcher(this, target, checker);
@@ -2260,7 +2275,7 @@ void Creature::SendZoneUnderAttackMessage(Player* attacker)
WorldPacket data(SMSG_ZONE_UNDER_ATTACK, 4);
data << (uint32)GetAreaId();
- sWorld->SendGlobalMessage(&data, NULL, (enemy_team == ALLIANCE ? HORDE : ALLIANCE));
+ sWorld->SendGlobalMessage(&data, nullptr, (enemy_team == ALLIANCE ? HORDE : ALLIANCE));
}
void Creature::SetInCombatWithZone()
@@ -2309,10 +2324,10 @@ uint32 Creature::GetShieldBlockValue() const //dunno mob block
bool Creature::HasSpell(uint32 spellID) const
{
uint8 i;
- for (i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (i = 0; i < MAX_CREATURE_SPELLS; ++i)
if (spellID == m_spells[i])
break;
- return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells
+ return i < MAX_CREATURE_SPELLS; //broke before end of iteration of known spells
}
time_t Creature::GetRespawnTimeEx() const
@@ -2757,13 +2772,13 @@ void Creature::SetTarget(ObjectGuid guid)
bool Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
{
// already focused
- if (_focusSpell)
+ if (m_focusSpell)
return false;
if ((!target || target == this) && !focusSpell->GetCastTime()) // instant cast, untargeted (or self-targeted) spell doesn't need any facing updates
return false;
- _focusSpell = focusSpell;
+ m_focusSpell = focusSpell;
// "instant" creature casts that require re-targeting will be delayed by a short moment to prevent facing bugs
bool shouldDelay = false;
@@ -2786,26 +2801,22 @@ bool Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
)
)
{
- const MapRefManager& mapPlayers = GetMap()->GetPlayers();
- for (MapRefManager::const_iterator it = mapPlayers.begin(); it != mapPlayers.end(); ++it)
- if (Player* player = (*it).GetSource())
+ std::list<Player*> playersNearby;
+ GetPlayerListInGrid(playersNearby, GetVisibilityRange());
+ for (Player* player : playersNearby)
+ {
+ // only update players that are known to the client (have already been created)
+ if (player->HaveAtClient(this))
{
- // only update players that can both see us, and are actually in combat with us (this is a performance tradeoff)
- if (player->CanSeeOrDetect(this, false, true) && IsInCombatWith(player))
- {
- SendUpdateToPlayer(player);
- shouldDelay = true;
- }
+ SendUpdateToPlayer(player);
+ shouldDelay = true;
}
+ }
if (shouldDelay)
shouldDelay = !(focusSpell->IsTriggered() || focusSpell->GetCastTime() || focusSpell->GetSpellInfo()->IsChanneled());
-
}
}
- // tell the creature that it should reacquire its current target after the cast is done (this is handled in ::Attack)
- MustReacquireTarget();
-
bool canTurnDuringCast = !focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST);
// Face the target - we need to do this before the unit state is modified for no-turn spells
if (target)
@@ -2828,16 +2839,16 @@ bool Creature::IsFocusing(Spell const* focusSpell, bool withDelay)
return false;
}
- if (focusSpell && (focusSpell != _focusSpell))
+ if (focusSpell && (focusSpell != m_focusSpell))
return false;
- if (!_focusSpell)
+ if (!m_focusSpell)
{
- if (!withDelay || !_focusDelay)
+ if (!withDelay || !m_focusDelay)
return false;
- if (GetMSTimeDiffToNow(_focusDelay) > 1000) // @todo figure out if we can get rid of this magic number somehow
+ if (GetMSTimeDiffToNow(m_focusDelay) > 1000) // @todo figure out if we can get rid of this magic number somehow
{
- _focusDelay = 0; // save checks in the future
+ m_focusDelay = 0; // save checks in the future
return false;
}
}
@@ -2847,20 +2858,25 @@ bool Creature::IsFocusing(Spell const* focusSpell, bool withDelay)
void Creature::ReleaseFocus(Spell const* focusSpell, bool withDelay)
{
- if (!_focusSpell)
+ if (!m_focusSpell)
return;
// focused to something else
- if (focusSpell && focusSpell != _focusSpell)
+ if (focusSpell && focusSpell != m_focusSpell)
return;
- SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty);
+ if (IsPet()) // player pets do not use delay system
+ SetGuidValue(UNIT_FIELD_TARGET, GetVictim() ? EnsureVictim()->GetGUID() : ObjectGuid::Empty);
+ else
+ // tell the creature that it should reacquire its actual target after the delay expires (this is handled in ::Attack)
+ // player pets don't need to do this, as they automatically reacquire their target on focus release
+ MustReacquireTarget();
- if (_focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
+ if (m_focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
ClearUnitState(UNIT_STATE_CANNOT_TURN);
- _focusSpell = nullptr;
- _focusDelay = withDelay ? getMSTime() : 0; // don't allow re-target right away to prevent visual bugs
+ m_focusSpell = nullptr;
+ m_focusDelay = (!IsPet() && withDelay) ? getMSTime() : 0; // don't allow re-target right away to prevent visual bugs
}
void Creature::StartPickPocketRefillTimer()
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 0803345f4f0..60cce700477 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -67,15 +67,16 @@ enum CreatureFlagsExtra
CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH | \
CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING | CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ | CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
-#define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS
+static const uint32 CREATURE_REGEN_INTERVAL = 2 * IN_MILLISECONDS;
+static const uint32 CREATURE_NOPATH_EVADE_TIME = 5 * IN_MILLISECONDS;
-#define MAX_KILL_CREDIT 2
-#define MAX_CREATURE_MODELS 4
-#define MAX_CREATURE_QUEST_ITEMS 6
-#define CREATURE_MAX_SPELLS 8
+static const uint8 MAX_KILL_CREDIT = 2;
+static const uint32 MAX_CREATURE_MODELS = 4;
+static const uint32 MAX_CREATURE_QUEST_ITEMS = 6;
+static const uint32 MAX_CREATURE_SPELLS = 8;
// from `creature_template` table
-struct CreatureTemplate
+struct TC_GAME_API CreatureTemplate
{
uint32 Entry;
uint32 DifficultyEntry[MAX_DIFFICULTY - 1];
@@ -117,7 +118,7 @@ struct CreatureTemplate
uint32 pickpocketLootId;
uint32 SkinLootId;
int32 resistance[MAX_SPELL_SCHOOL];
- uint32 spells[CREATURE_MAX_SPELLS];
+ uint32 spells[MAX_CREATURE_SPELLS];
uint32 PetSpellDataId;
uint32 VehicleId;
uint32 mingold;
@@ -145,11 +146,11 @@ struct 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
@@ -157,12 +158,12 @@ struct 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 == 0 || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_PET) == 0)
return false;
// if can tame exotic then can tame any tameable
@@ -179,7 +180,7 @@ typedef std::unordered_map<uint32, CreatureTemplate> CreatureTemplateContainer;
#pragma pack(push, 1)
// Defines base stats for creatures (used to calculate HP/mana/armor/attackpower/rangedattackpower/all damage).
-struct CreatureBaseStats
+struct TC_GAME_API CreatureBaseStats
{
uint32 BaseHealth[MAX_EXPANSIONS];
uint32 BaseMana;
@@ -401,7 +402,7 @@ struct TrainerSpell
typedef std::unordered_map<uint32 /*spellid*/, TrainerSpell> TrainerSpellMap;
-struct TrainerSpellData
+struct TC_GAME_API TrainerSpellData
{
TrainerSpellData() : trainerType(0) { }
~TrainerSpellData() { spellList.clear(); }
@@ -421,7 +422,7 @@ struct TrainerSpellData
typedef std::vector<uint8> CreatureTextRepeatIds;
typedef std::unordered_map<uint8, CreatureTextRepeatIds> CreatureTextRepeatGroup;
-class Creature : public Unit, public GridObject<Creature>, public MapObject
+class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public MapObject
{
public:
@@ -475,7 +476,9 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
uint8 getLevelForTarget(WorldObject const* target) const override; // overwrite Unit::getLevelForTarget for boss level support
bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); }
+ bool IsEvadingAttacks() const { return IsInEvadeMode() || CanNotReachTarget(); }
+ bool AIM_Destroy();
bool AIM_Initialize(CreatureAI* ai = NULL);
void Motion_Initialize();
@@ -566,7 +569,7 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
SpellInfo const* reachWithSpellAttack(Unit* victim);
SpellInfo const* reachWithSpellCure(Unit* victim);
- uint32 m_spells[CREATURE_MAX_SPELLS];
+ uint32 m_spells[MAX_CREATURE_SPELLS];
bool CanStartAttack(Unit const* u, bool force) const;
float GetAttackDistance(Unit const* player) const;
@@ -631,6 +634,15 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; }
virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const;
+ void SetCannotReachTarget(bool cannotReach)
+ {
+ if (cannotReach == m_cannotReachTarget)
+ return;
+ m_cannotReachTarget = cannotReach;
+ m_cannotReachTimer = 0;
+ }
+ bool CanNotReachTarget() const { return m_cannotReachTarget; }
+
void SetPosition(float x, float y, float z, float o);
void SetPosition(const Position &pos) { SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); }
@@ -718,6 +730,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
bool m_AlreadyCallAssistance;
bool m_AlreadySearchedAssistance;
bool m_regenHealth;
+ bool m_cannotReachTarget;
+ uint32 m_cannotReachTimer;
bool m_AI_locked;
SpellSchoolMask m_meleeDamageSchoolMask;
@@ -746,15 +760,15 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
//Formation var
CreatureGroup* m_formation;
- bool TriggerJustRespawned;
+ bool m_TriggerJustRespawned;
- Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
- uint32 _focusDelay;
+ Spell const* m_focusSpell; ///> Locks the target during spell cast for proper facing
+ uint32 m_focusDelay;
CreatureTextRepeatGroup m_textRepeat;
};
-class AssistDelayEvent : public BasicEvent
+class TC_GAME_API AssistDelayEvent : public BasicEvent
{
public:
AssistDelayEvent(ObjectGuid victim, Unit& owner) : BasicEvent(), m_victim(victim), m_owner(owner) { }
@@ -769,7 +783,7 @@ class AssistDelayEvent : public BasicEvent
Unit& m_owner;
};
-class ForcedDespawnDelayEvent : public BasicEvent
+class TC_GAME_API ForcedDespawnDelayEvent : public BasicEvent
{
public:
ForcedDespawnDelayEvent(Creature& owner) : BasicEvent(), m_owner(owner) { }
diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h
index d337d388b6e..14472a30293 100644
--- a/src/server/game/Entities/Creature/CreatureGroups.h
+++ b/src/server/game/Entities/Creature/CreatureGroups.h
@@ -38,7 +38,7 @@ struct FormationInfo
typedef std::unordered_map<uint32/*memberDBGUID*/, FormationInfo*> CreatureGroupInfoType;
-class FormationMgr
+class TC_GAME_API FormationMgr
{
private:
FormationMgr() { }
@@ -53,7 +53,7 @@ class FormationMgr
CreatureGroupInfoType CreatureGroupMap;
};
-class CreatureGroup
+class TC_GAME_API CreatureGroup
{
private:
Creature* m_leader; //Important do not forget sometimes to work with pointers instead synonims :D:D
diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h
index 1e64e83c86a..01b27032286 100644
--- a/src/server/game/Entities/Creature/GossipDef.h
+++ b/src/server/game/Entities/Creature/GossipDef.h
@@ -157,7 +157,7 @@ struct QuestMenuItem
typedef std::vector<QuestMenuItem> QuestMenuItemList;
-class GossipMenu
+class TC_GAME_API GossipMenu
{
public:
GossipMenu();
@@ -222,7 +222,7 @@ class GossipMenu
LocaleConstant _locale;
};
-class QuestMenu
+class TC_GAME_API QuestMenu
{
public:
QuestMenu();
@@ -252,7 +252,7 @@ class QuestMenu
QuestMenuItemList _questMenuItems;
};
-class PlayerMenu
+class TC_GAME_API PlayerMenu
{
public:
explicit PlayerMenu(WorldSession* session);
diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h
index b80e7d3e2bc..87cb883b261 100644
--- a/src/server/game/Entities/Creature/TemporarySummon.h
+++ b/src/server/game/Entities/Creature/TemporarySummon.h
@@ -37,7 +37,7 @@ struct TempSummonData
uint32 time; ///< Despawn time, usable only with certain temp summon types
};
-class TempSummon : public Creature
+class TC_GAME_API TempSummon : public Creature
{
public:
explicit TempSummon(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject);
@@ -64,7 +64,7 @@ class TempSummon : public Creature
ObjectGuid m_summonerGUID;
};
-class Minion : public TempSummon
+class TC_GAME_API Minion : public TempSummon
{
public:
Minion(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject);
@@ -82,7 +82,7 @@ class Minion : public TempSummon
float m_followAngle;
};
-class Guardian : public Minion
+class TC_GAME_API Guardian : public Minion
{
public:
Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject);
@@ -106,7 +106,7 @@ class Guardian : public Minion
float m_statFromOwner[MAX_STATS];
};
-class Puppet : public Minion
+class TC_GAME_API Puppet : public Minion
{
public:
Puppet(SummonPropertiesEntry const* properties, Unit* owner);
@@ -116,7 +116,7 @@ class Puppet : public Minion
void RemoveFromWorld() override;
};
-class ForcedUnsummonDelayEvent : public BasicEvent
+class TC_GAME_API ForcedUnsummonDelayEvent : public BasicEvent
{
public:
ForcedUnsummonDelayEvent(TempSummon& owner) : BasicEvent(), m_owner(owner) { }
diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h
index 9443a5b5b89..d7c1f0190fd 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.h
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.h
@@ -32,7 +32,7 @@ enum DynamicObjectType
DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2
};
-class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MapObject
+class TC_GAME_API DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MapObject
{
public:
DynamicObject(bool isWorldObject);
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 1acfeacbf83..d7e478bb4e5 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,8 +49,8 @@ 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_spawnId = 0;
m_rotation = 0;
@@ -71,9 +71,15 @@ GameObject::~GameObject()
// CleanupsBeforeDelete();
}
-bool GameObject::AIM_Initialize()
+void GameObject::AIM_Destroy()
{
delete m_AI;
+ m_AI = nullptr;
+}
+
+bool GameObject::AIM_Initialize()
+{
+ AIM_Destroy();
m_AI = FactorySelector::SelectGameObjectAI(this);
@@ -490,7 +496,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())
@@ -505,7 +511,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);
@@ -565,8 +571,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))
@@ -1087,7 +1093,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()));
@@ -1107,7 +1113,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);
@@ -1130,7 +1136,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;
@@ -1154,7 +1160,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);
@@ -1282,8 +1288,8 @@ void GameObject::Use(Unit* user)
{
if (Player* ChairUser = ObjectAccessor::FindPlayer(itr->second))
{
- if (ChairUser->IsSitState() && ChairUser->getStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f)
- continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->getStandState() != UNIT_STAND_STATE_SIT check is required.
+ if (ChairUser->IsSitState() && ChairUser->GetStandState() != UNIT_STAND_STATE_SIT && ChairUser->GetExactDist2d(x_i, y_i) < 0.1f)
+ continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required.
else
itr->second.Clear(); // This seat is unoccupied.
}
@@ -1380,7 +1386,7 @@ void GameObject::Use(Unit* user)
// cast this spell later if provided
spellId = info->goober.spellId;
- spellCaster = NULL;
+ spellCaster = nullptr;
break;
}
@@ -1499,7 +1505,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);
@@ -1863,7 +1869,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;
@@ -1923,7 +1929,7 @@ void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3
SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3);
}
-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;
@@ -1942,7 +1948,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)
@@ -1973,7 +1979,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);
@@ -2155,14 +2161,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);
}
@@ -2170,7 +2176,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)
{
@@ -2289,7 +2295,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 7be78556a50..2b092427178 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -640,7 +640,7 @@ class GameObjectModel;
// 5 sec for bobber catch
#define FISHING_BOBBER_READY_TIME 5
-class GameObject : public WorldObject, public GridObject<GameObject>, public MapObject
+class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>, public MapObject
{
public:
explicit GameObject();
@@ -849,8 +849,10 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map
void UpdateModelPosition();
- protected:
+ void AIM_Destroy();
bool AIM_Initialize();
+
+ protected:
GameObjectModel* CreateModel();
void UpdateModel(); // updates model in case displayId were changed
uint32 m_spellId;
diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h
index 5bfafa4238c..89615f94e52 100644
--- a/src/server/game/Entities/Item/Container/Bag.h
+++ b/src/server/game/Entities/Item/Container/Bag.h
@@ -25,7 +25,7 @@
#include "Item.h"
#include "ItemPrototype.h"
-class Bag : public Item
+class TC_GAME_API Bag : public Item
{
public:
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 773d5a05772..a8dad0b2fbc 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -519,41 +519,11 @@ Player* Item::GetOwner()const
return ObjectAccessor::FindPlayer(GetOwnerGUID());
}
+// Just a "legacy shortcut" for proto->GetSkill()
uint32 Item::GetSkill()
{
- const static uint32 item_weapon_skills[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_FISHING
- };
-
- const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] =
- {
- 0, SKILL_CLOTH, SKILL_LEATHER, SKILL_MAIL, SKILL_PLATE_MAIL, 0, SKILL_SHIELD, 0, 0, 0, 0
- };
-
ItemTemplate const* proto = GetTemplate();
-
- switch (proto->Class)
- {
- case ITEM_CLASS_WEAPON:
- if (proto->SubClass >= MAX_ITEM_SUBCLASS_WEAPON)
- return 0;
- else
- return item_weapon_skills[proto->SubClass];
-
- case ITEM_CLASS_ARMOR:
- if (proto->SubClass >= MAX_ITEM_SUBCLASS_ARMOR)
- return 0;
- else
- return item_armor_skills[proto->SubClass];
-
- default:
- return 0;
- }
+ return proto->GetSkill();
}
uint32 Item::GetSpell()
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index 378d7f5e2e4..8658200c28c 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -203,7 +203,7 @@ enum ItemUpdateState
bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto);
-class Item : public Object
+class TC_GAME_API Item : public Object
{
public:
static Item* CreateItem(uint32 itemEntry, uint32 count, Player const* player = NULL);
diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.h b/src/server/game/Entities/Item/ItemEnchantmentMgr.h
index 2d5c27177b1..8ce9ae780fe 100644
--- a/src/server/game/Entities/Item/ItemEnchantmentMgr.h
+++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.h
@@ -21,8 +21,9 @@
#include "Common.h"
-void LoadRandomEnchantmentsTable();
-uint32 GetItemEnchantMod(int32 entry);
-uint32 GenerateEnchSuffixFactor(uint32 item_id);
+TC_GAME_API void LoadRandomEnchantmentsTable();
+TC_GAME_API uint32 GetItemEnchantMod(int32 entry);
+TC_GAME_API uint32 GenerateEnchSuffixFactor(uint32 item_id);
+
#endif
diff --git a/src/server/game/Entities/Item/ItemPrototype.cpp b/src/server/game/Entities/Item/ItemPrototype.cpp
new file mode 100644
index 00000000000..3bb7b128b08
--- /dev/null
+++ b/src/server/game/Entities/Item/ItemPrototype.cpp
@@ -0,0 +1,121 @@
+/*
+* 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 "ItemPrototype.h"
+
+bool ItemTemplate::CanChangeEquipStateInCombat() const
+{
+ switch (InventoryType)
+ {
+ case INVTYPE_RELIC:
+ case INVTYPE_SHIELD:
+ case INVTYPE_HOLDABLE:
+ return true;
+ }
+
+ switch (Class)
+ {
+ case ITEM_CLASS_WEAPON:
+ case ITEM_CLASS_PROJECTILE:
+ return true;
+ }
+
+ return false;
+}
+
+
+float ItemTemplate::getDPS() const
+{
+ if (Delay == 0)
+ return 0;
+ float temp = 0;
+ for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
+ temp += Damage[i].DamageMin + Damage[i].DamageMax;
+ return temp * 500 / 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;
+ if (bonus < 0)
+ return 0;
+ return bonus;
+ }
+ return 0;
+}
+
+float ItemTemplate::GetItemLevelIncludingQuality() const
+{
+ float itemLevel = (float)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;
+ }
+ return std::max<float>(0.f, itemLevel);
+}
+
+uint32 ItemTemplate::GetSkill() const
+{
+ const static uint32 item_weapon_skills[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_FISHING
+ };
+
+ const static uint32 item_armor_skills[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_ARMOR:
+ if (SubClass >= MAX_ITEM_SUBCLASS_ARMOR)
+ return 0;
+ else
+ return item_armor_skills[SubClass];
+
+ default:
+ return 0;
+ }
+}
diff --git a/src/server/game/Entities/Item/ItemPrototype.h b/src/server/game/Entities/Item/ItemPrototype.h
index 48ef9e81016..9b0f4628364 100644
--- a/src/server/game/Entities/Item/ItemPrototype.h
+++ b/src/server/game/Entities/Item/ItemPrototype.h
@@ -664,25 +664,7 @@ struct ItemTemplate
uint32 FlagsCu;
// helpers
- bool CanChangeEquipStateInCombat() const
- {
- switch (InventoryType)
- {
- case INVTYPE_RELIC:
- case INVTYPE_SHIELD:
- case INVTYPE_HOLDABLE:
- return true;
- }
-
- switch (Class)
- {
- case ITEM_CLASS_WEAPON:
- case ITEM_CLASS_PROJECTILE:
- return true;
- }
-
- return false;
- }
+ bool CanChangeEquipStateInCombat() const;
bool IsCurrencyToken() const { return (BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) != 0; }
@@ -691,51 +673,13 @@ struct ItemTemplate
return (Stackable == 2147483647 || Stackable <= 0) ? uint32(0x7FFFFFFF-1) : uint32(Stackable);
}
- float getDPS() const
- {
- if (Delay == 0)
- return 0;
- float temp = 0;
- for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
- temp+=Damage[i].DamageMin + Damage[i].DamageMax;
- return temp*500/Delay;
- }
+ float getDPS() const;
- int32 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;
- if (bonus < 0)
- return 0;
- return bonus;
- }
- return 0;
- }
+ int32 getFeralBonus(int32 extraDPS = 0) const;
- float GetItemLevelIncludingQuality() const
- {
- float itemLevel = (float)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;
- }
- return std::max<float>(0.f, itemLevel);
- }
+ float GetItemLevelIncludingQuality() const;
+
+ uint32 GetSkill() const;
bool IsPotion() const { return Class == ITEM_CLASS_CONSUMABLE && SubClass == ITEM_SUBCLASS_POTION; }
bool IsWeaponVellum() const { return Class == ITEM_CLASS_TRADE_GOODS && SubClass == ITEM_SUBCLASS_WEAPON_ENCHANTMENT; }
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index f68464193ec..daad0c9d958 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -221,7 +221,10 @@ void Object::SendUpdateToPlayer(Player* player)
UpdateData upd;
WorldPacket packet;
- BuildCreateUpdateBlockForPlayer(&upd, player);
+ if (player->HaveAtClient(this))
+ BuildValuesUpdateBlockForPlayer(&upd, player);
+ else
+ BuildCreateUpdateBlockForPlayer(&upd, player);
upd.BuildPacket(&packet);
player->GetSession()->SendPacket(&packet);
}
@@ -925,6 +928,11 @@ bool Object::HasByteFlag(uint16 index, uint8 offset, uint8 flag) const
return (((uint8*)&m_uint32Values[index])[offset] & flag) != 0;
}
+void Object::ApplyModByteFlag(uint16 index, uint8 offset, uint8 flag, bool apply)
+{
+ if (apply) SetByteFlag(index, offset, flag); else RemoveByteFlag(index, offset, flag);
+}
+
void Object::SetFlag64(uint16 index, uint64 newFlag)
{
uint64 oldval = GetUInt64Value(index);
@@ -1491,9 +1499,20 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
float WorldObject::GetGridActivationRange() const
{
if (ToPlayer())
+ {
+ if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
return GetMap()->GetVisibilityRange();
+ }
else if (ToCreature())
return ToCreature()->m_SightDistance;
+ else if (ToDynObject())
+ {
+ if (isActiveObject())
+ return GetMap()->GetVisibilityRange();
+ else
+ return 0.0f;
+ }
else
return 0.0f;
}
@@ -1514,6 +1533,8 @@ float WorldObject::GetSightRange(const WorldObject* target) const
{
if (target && target->isActiveObject() && !target->ToPlayer())
return MAX_VISIBILITY_DISTANCE;
+ else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
else
return GetMap()->GetVisibilityRange();
}
@@ -1523,6 +1544,11 @@ float WorldObject::GetSightRange(const WorldObject* target) const
return SIGHT_RANGE_UNIT;
}
+ if (ToDynObject() && isActiveObject())
+ {
+ return GetMap()->GetVisibilityRange();
+ }
+
return 0.0f;
}
@@ -2041,7 +2067,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 4bdc5720acb..47b28d8ea3a 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -90,7 +90,7 @@ class ZoneScript;
typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType;
-class Object
+class TC_GAME_API Object
{
public:
virtual ~Object();
@@ -160,6 +160,7 @@ class Object
void RemoveByteFlag(uint16 index, uint8 offset, uint8 newFlag);
void ToggleByteFlag(uint16 index, uint8 offset, uint8 flag);
bool HasByteFlag(uint16 index, uint8 offset, uint8 flag) const;
+ void ApplyModByteFlag(uint16 index, uint8 offset, uint8 flag, bool apply);
void SetFlag64(uint16 index, uint64 newFlag);
void RemoveFlag64(uint16 index, uint64 oldFlag);
@@ -397,7 +398,7 @@ enum MapObjectCellMoveState
MAP_OBJECT_CELL_MOVE_INACTIVE, //in move list but should not move
};
-class MapObject
+class TC_GAME_API MapObject
{
friend class Map; //map for moving creatures
friend class ObjectGridLoader; //grid loader for loading creatures
@@ -422,7 +423,7 @@ class MapObject
}
};
-class WorldObject : public Object, public WorldLocation
+class TC_GAME_API WorldObject : public Object, public WorldLocation
{
protected:
explicit WorldObject(bool isWorldObject); //note: here it means if it is in grid object list or world object list
@@ -551,8 +552,8 @@ class 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();
@@ -585,14 +586,6 @@ class WorldObject : public Object, public WorldLocation
template<class NOTIFIER> void VisitNearbyGridObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitGrid(GetPositionX(), GetPositionY(), radius, notifier); }
template<class NOTIFIER> void VisitNearbyWorldObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitWorld(GetPositionX(), GetPositionY(), radius, notifier); }
-#ifdef MAP_BASED_RAND_GEN
- int32 irand(int32 min, int32 max) const { return int32 (GetMap()->mtRand.randInt(max - min)) + min; }
- uint32 urand(uint32 min, uint32 max) const { return GetMap()->mtRand.randInt(max - min) + min;}
- int32 rand32() const { return GetMap()->mtRand.randInt();}
- double rand_norm() const { return GetMap()->mtRand.randExc();}
- double rand_chance() const { return GetMap()->mtRand.randExc(100.0);}
-#endif
-
uint32 LastUsedScriptID;
// Transports
diff --git a/src/server/game/Entities/Object/ObjectGuid.cpp b/src/server/game/Entities/Object/ObjectGuid.cpp
index 3e2bd000b6e..2fb766c5201 100644
--- a/src/server/game/Entities/Object/ObjectGuid.cpp
+++ b/src/server/game/Entities/Object/ObjectGuid.cpp
@@ -95,4 +95,19 @@ void ObjectGuidGeneratorBase::HandleCounterOverflow(HighGuid high)
{
TC_LOG_ERROR("misc", "%s guid overflow!! Can't continue, shutting down server. ", ObjectGuid::GetTypeName(high));
World::StopNow(ERROR_EXIT_CODE);
-} \ No newline at end of file
+}
+
+#define GUID_TRAIT_INSTANTIATE_GUID( HIGH_GUID ) \
+ template class TC_GAME_API ObjectGuidGenerator< HIGH_GUID >;
+
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Container)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Player)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::GameObject)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Transport)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Unit)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Pet)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Vehicle)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::DynamicObject)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Mo_Transport)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Instance)
+GUID_TRAIT_INSTANTIATE_GUID(HighGuid::Group)
diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h
index 44644421567..71c66622790 100644
--- a/src/server/game/Entities/Object/ObjectGuid.h
+++ b/src/server/game/Entities/Object/ObjectGuid.h
@@ -111,7 +111,7 @@ struct PackedGuidReader
ObjectGuid* GuidPtr;
};
-class ObjectGuid
+class TC_GAME_API ObjectGuid
{
public:
static ObjectGuid const Empty;
@@ -252,9 +252,9 @@ typedef std::unordered_set<ObjectGuid> GuidUnorderedSet;
// minimum buffer size for packed guid is 9 bytes
#define PACKED_GUID_MIN_BUFFER_SIZE 9
-class PackedGuid
+class TC_GAME_API PackedGuid
{
- friend ByteBuffer& operator<<(ByteBuffer& buf, PackedGuid const& guid);
+ friend TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, PackedGuid const& guid);
public:
explicit PackedGuid() : _packedGuid(PACKED_GUID_MIN_BUFFER_SIZE) { _packedGuid.appendPackGUID(0); }
@@ -270,8 +270,7 @@ class PackedGuid
ByteBuffer _packedGuid;
};
-
-class ObjectGuidGeneratorBase
+class TC_GAME_API ObjectGuidGeneratorBase
{
public:
ObjectGuidGeneratorBase(ObjectGuid::LowType start = 1) : _nextGuid(start) { }
@@ -286,7 +285,7 @@ protected:
};
template<HighGuid high>
-class ObjectGuidGenerator : public ObjectGuidGeneratorBase
+class TC_GAME_API ObjectGuidGenerator : public ObjectGuidGeneratorBase
{
public:
explicit ObjectGuidGenerator(ObjectGuid::LowType start = 1) : ObjectGuidGeneratorBase(start) { }
@@ -299,11 +298,11 @@ public:
}
};
-ByteBuffer& operator<<(ByteBuffer& buf, ObjectGuid const& guid);
-ByteBuffer& operator>>(ByteBuffer& buf, ObjectGuid& guid);
+TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, ObjectGuid const& guid);
+TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, ObjectGuid& guid);
-ByteBuffer& operator<<(ByteBuffer& buf, PackedGuid const& guid);
-ByteBuffer& operator>>(ByteBuffer& buf, PackedGuidReader const& guid);
+TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, PackedGuid const& guid);
+TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, PackedGuidReader const& guid);
inline PackedGuid ObjectGuid::WriteAsPacked() const { return PackedGuid(*this); }
diff --git a/src/server/game/Entities/Object/ObjectPosSelector.h b/src/server/game/Entities/Object/ObjectPosSelector.h
index 26a23678009..84c694abf08 100644
--- a/src/server/game/Entities/Object/ObjectPosSelector.h
+++ b/src/server/game/Entities/Object/ObjectPosSelector.h
@@ -30,7 +30,7 @@ inline UsedPosType operator ~(UsedPosType uptype)
return uptype==USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS;
}
-struct ObjectPosSelector
+struct TC_GAME_API ObjectPosSelector
{
struct UsedPos
{
diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h
index 18d356c28d4..6325cc51fe2 100644
--- a/src/server/game/Entities/Object/Position.h
+++ b/src/server/game/Entities/Object/Position.h
@@ -22,7 +22,7 @@
class ByteBuffer;
-struct Position
+struct TC_GAME_API Position
{
Position(float x = 0, float y = 0, float z = 0, float o = 0)
: m_positionX(x), m_positionY(y), m_positionZ(z), m_orientation(NormalizeOrientation(o)) { }
@@ -216,11 +216,11 @@ public:
}
};
-ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYStreamer const& streamer);
-ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYStreamer const& streamer);
-ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
-ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
-ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer);
-ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer);
+TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYStreamer const& streamer);
+TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYStreamer const& streamer);
+TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
+TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer);
+TC_GAME_API ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer);
+TC_GAME_API ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer);
#endif // Trinity_game_Position_h__
diff --git a/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h b/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h
index 0b4e59f4f9b..8208a5e9894 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h
+++ b/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h
@@ -35,10 +35,10 @@ enum UpdatefieldFlags
UF_FLAG_DYNAMIC = 0x100
};
-extern uint32 ItemUpdateFieldFlags[CONTAINER_END];
-extern uint32 UnitUpdateFieldFlags[PLAYER_END];
-extern uint32 GameObjectUpdateFieldFlags[GAMEOBJECT_END];
-extern uint32 DynamicObjectUpdateFieldFlags[DYNAMICOBJECT_END];
-extern uint32 CorpseUpdateFieldFlags[CORPSE_END];
+TC_GAME_API extern uint32 ItemUpdateFieldFlags[CONTAINER_END];
+TC_GAME_API extern uint32 UnitUpdateFieldFlags[PLAYER_END];
+TC_GAME_API extern uint32 GameObjectUpdateFieldFlags[GAMEOBJECT_END];
+TC_GAME_API extern uint32 DynamicObjectUpdateFieldFlags[DYNAMICOBJECT_END];
+TC_GAME_API extern uint32 CorpseUpdateFieldFlags[CORPSE_END];
#endif // _UPDATEFIELDFLAGS_H
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 0da63c5e500..37fb23b38f5 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -213,12 +213,14 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
case SUMMON_PET:
petlevel = owner->getLevel();
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x800); // class = mage
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(CLASS_MAGE));
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet dismiss, cancel)
break;
case HUNTER_PET:
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); // class = warrior, gender = none, power = focus
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_WARRIOR);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
@@ -226,7 +228,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
// this enables popup window (pet abandon, cancel)
SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS));
SetPower(POWER_HAPPINESS, fields[12].GetUInt32());
- setPowerType(POWER_FOCUS);
break;
default:
if (!IsPetGhoul())
@@ -822,7 +823,9 @@ bool Pet::CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phas
if (cinfo->type == CREATURE_TYPE_BEAST)
{
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_WARRIOR);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
}
diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h
index b0863a371e6..3a65fb44e04 100644
--- a/src/server/game/Entities/Pet/Pet.h
+++ b/src/server/game/Entities/Pet/Pet.h
@@ -37,7 +37,7 @@ typedef std::vector<uint32> AutoSpellList;
class Player;
-class Pet : public Guardian
+class TC_GAME_API Pet : public Guardian
{
public:
explicit Pet(Player* owner, PetType type = MAX_PET_TYPE);
diff --git a/src/server/game/Entities/Player/CinematicMgr.cpp b/src/server/game/Entities/Player/CinematicMgr.cpp
new file mode 100644
index 00000000000..cc5a62300ad
--- /dev/null
+++ b/src/server/game/Entities/Player/CinematicMgr.cpp
@@ -0,0 +1,174 @@
+/*
+* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+*
+* 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 "CinematicMgr.h"
+#include "Creature.h"
+#include "Player.h"
+#include "TemporarySummon.h"
+
+CinematicMgr::CinematicMgr(Player* playerref)
+{
+ 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;
+}
+
+CinematicMgr::~CinematicMgr()
+{
+ if (m_cinematicCamera && m_activeCinematicCameraId)
+ EndCinematic();
+}
+
+void CinematicMgr::BeginCinematic()
+{
+ // Sanity check for active camera set
+ if (m_activeCinematicCameraId == 0)
+ return;
+
+ auto itr = sFlyByCameraStore.find(m_activeCinematicCameraId);
+ if (itr != sFlyByCameraStore.end())
+ {
+ // Initialize diff, and set camera
+ m_cinematicDiff = 0;
+ m_cinematicCamera = &itr->second;
+
+ auto camitr = m_cinematicCamera->begin();
+ if (camitr != m_cinematicCamera->end())
+ {
+ Position pos(camitr->locations.x, camitr->locations.y, camitr->locations.z, camitr->locations.w);
+ if (!pos.IsPositionValid())
+ return;
+
+ player->GetMap()->LoadGrid(camitr->locations.x, camitr->locations.y);
+ m_CinematicObject = player->SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 5 * MINUTE * IN_MILLISECONDS);
+ if (m_CinematicObject)
+ {
+ m_CinematicObject->setActive(true);
+ player->SetViewpoint(m_CinematicObject, true);
+ }
+
+ // Get cinematic length
+ FlyByCameraCollection::const_reverse_iterator camrevitr = m_cinematicCamera->rbegin();
+ if (camrevitr != m_cinematicCamera->rend())
+ m_cinematicLength = camrevitr->timeStamp;
+ else
+ m_cinematicLength = 0;
+ }
+ }
+}
+
+void CinematicMgr::EndCinematic()
+{
+ if (m_activeCinematicCameraId == 0)
+ return;
+
+ m_cinematicDiff = 0;
+ m_cinematicCamera = nullptr;
+ m_activeCinematicCameraId = 0;
+ if (m_CinematicObject)
+ {
+ if (WorldObject* vpObject = player->GetViewpoint())
+ if (vpObject == m_CinematicObject)
+ player->SetViewpoint(m_CinematicObject, false);
+
+ m_CinematicObject->AddObjectToRemoveList();
+ }
+}
+
+void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/)
+{
+ if (m_activeCinematicCameraId == 0 || !m_cinematicCamera || m_cinematicCamera->size() == 0)
+ return;
+
+ Position lastPosition;
+ uint32 lastTimestamp = 0;
+ Position nextPosition;
+ uint32 nextTimestamp = 0;
+
+ // Obtain direction of travel
+ for (FlyByCamera cam : *m_cinematicCamera)
+ {
+ if (cam.timeStamp > m_cinematicDiff)
+ {
+ nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ nextTimestamp = cam.timeStamp;
+ break;
+ }
+ lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ lastTimestamp = cam.timeStamp;
+ }
+ float angle = lastPosition.GetAngle(&nextPosition);
+ angle -= lastPosition.GetOrientation();
+ if (angle < 0)
+ angle += 2 * float(M_PI);
+
+ // Look for position around 2 second ahead of us.
+ int32 workDiff = m_cinematicDiff;
+
+ // Modify result based on camera direction (Humans for example, have the camera point behind)
+ workDiff += static_cast<int32>(float(CINEMATIC_LOOKAHEAD) * cos(angle));
+
+ // Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end
+ FlyByCameraCollection::const_reverse_iterator endItr = m_cinematicCamera->rbegin();
+ if (endItr != m_cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp))
+ workDiff = endItr->timeStamp;
+
+ // Never try to go back in time before the start of cinematic!
+ if (workDiff < 0)
+ workDiff = m_cinematicDiff;
+
+ // Obtain the previous and next waypoint based on timestamp
+ for (FlyByCamera cam : *m_cinematicCamera)
+ {
+ if (static_cast<int32>(cam.timeStamp) >= workDiff)
+ {
+ nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ nextTimestamp = cam.timeStamp;
+ break;
+ }
+ lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w);
+ lastTimestamp = cam.timeStamp;
+ }
+
+ // Never try to go beyond the end of the cinematic
+ if (workDiff > static_cast<int32>(nextTimestamp))
+ workDiff = static_cast<int32>(nextTimestamp);
+
+ // Interpolate the position for this moment in time (or the adjusted moment in time)
+ uint32 timeDiff = nextTimestamp - lastTimestamp;
+ uint32 interDiff = workDiff - lastTimestamp;
+ float xDiff = nextPosition.m_positionX - lastPosition.m_positionX;
+ float yDiff = nextPosition.m_positionY - lastPosition.m_positionY;
+ float zDiff = nextPosition.m_positionZ - lastPosition.m_positionZ;
+ Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionY +
+ (yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff))));
+
+ // Advance (at speed) to this position. The remote sight object is used
+ // to send update information to player in cinematic
+ if (m_CinematicObject && interPosition.IsPositionValid())
+ m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 500.0f, false, true);
+
+ // If we never received an end packet 10 seconds after the final timestamp then force an end
+ if (m_cinematicDiff > m_cinematicLength + 10 * IN_MILLISECONDS)
+ EndCinematic();
+}
diff --git a/src/server/game/Entities/Player/CinematicMgr.h b/src/server/game/Entities/Player/CinematicMgr.h
new file mode 100644
index 00000000000..ab067afa042
--- /dev/null
+++ b/src/server/game/Entities/Player/CinematicMgr.h
@@ -0,0 +1,60 @@
+/*
+* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+*
+* 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 CinematicMgr_h__
+#define CinematicMgr_h__
+
+#include "Define.h"
+#include "Object.h"
+#include "M2Stores.h"
+
+#define CINEMATIC_LOOKAHEAD (2 * IN_MILLISECONDS)
+#define CINEMATIC_UPDATEDIFF 500
+
+class Player;
+
+class TC_GAME_API CinematicMgr
+{
+ friend class Player;
+public:
+ explicit CinematicMgr(Player* playerref);
+ ~CinematicMgr();
+
+ // Cinematic camera data and remote sight functions
+ uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; }
+ void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; }
+ bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); }
+ void BeginCinematic();
+ void EndCinematic();
+ void UpdateCinematicLocation(uint32 diff);
+
+private:
+ // Remote location information
+ Player* player;
+
+protected:
+ uint32 m_cinematicDiff;
+ uint32 m_lastCinematicCheck;
+ uint32 m_activeCinematicCameraId;
+ uint32 m_cinematicLength;
+ FlyByCameraCollection* m_cinematicCamera;
+ Position m_remoteSightPosition;
+ TempSummon* m_CinematicObject;
+};
+
+#endif \ No newline at end of file
diff --git a/src/server/game/Entities/Player/KillRewarder.h b/src/server/game/Entities/Player/KillRewarder.h
index 08530de900c..210e5ff0246 100644
--- a/src/server/game/Entities/Player/KillRewarder.h
+++ b/src/server/game/Entities/Player/KillRewarder.h
@@ -24,7 +24,7 @@ class Player;
class Unit;
class Group;
-class KillRewarder
+class TC_GAME_API KillRewarder
{
public:
KillRewarder(Player* killer, Unit* victim, bool isBattleGround);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 4c3f7289b11..df8d8605036 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -332,8 +332,6 @@ Player::Player(WorldSession* session): Unit(true)
m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
- clearResurrectRequestData();
-
memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
m_social = nullptr;
@@ -476,21 +474,11 @@ Player::Player(WorldSession* session): Unit(true)
// Player summoning
m_summon_expire = 0;
- m_summon_mapid = 0;
- m_summon_x = 0.0f;
- m_summon_y = 0.0f;
- m_summon_z = 0.0f;
m_mover = this;
m_movedPlayer = this;
m_seer = this;
- m_recallMap = 0;
- m_recallX = 0;
- m_recallY = 0;
- m_recallZ = 0;
- m_recallO = 0;
-
m_homebindMapId = 0;
m_homebindAreaId = 0;
m_homebindX = 0;
@@ -537,6 +525,9 @@ Player::Player(WorldSession* session): Unit(true)
_activeCheats = CHEAT_NONE;
healthBeforeDuel = 0;
manaBeforeDuel = 0;
+
+ _cinematicMgr = new CinematicMgr(this);
+
m_achievementMgr = new AchievementMgr(this);
m_reputationMgr = new ReputationMgr(this);
}
@@ -576,6 +567,7 @@ Player::~Player()
delete m_runes;
delete m_achievementMgr;
delete m_reputationMgr;
+ delete _cinematicMgr;
sWorld->DecreasePlayerCount();
}
@@ -645,9 +637,10 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
return false;
}
- uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
-
- SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, createInfo->Race);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, createInfo->Class);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, createInfo->Gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powertype);
InitDisplayIds();
if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
{
@@ -660,13 +653,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value
- SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24)));
- SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair |
- (0x00 << 8) |
- (0x00 << 16) |
- (((GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED) << 24)));
- SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender);
- SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, createInfo->Skin);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, createInfo->Face);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, createInfo->HairStyle);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, createInfo->HairColor);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, createInfo->FacialHair);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, createInfo->Gender);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_ARENA_FACTION, 0);
SetUInt32Value(PLAYER_GUILDID, 0);
SetUInt32Value(PLAYER_GUILDRANK, 0);
@@ -1181,7 +1175,7 @@ void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK);
uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue);
- SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, newDrunkValue);
UpdateObjectVisibility();
if (!isSobering)
@@ -1224,7 +1218,15 @@ void Player::Update(uint32 p_time)
m_spellModTakingSpell = nullptr;
}
- //used to implement delayed far teleport
+ // Update cinematic location, if 500ms have passed and we're doing a cinematic now.
+ _cinematicMgr->m_cinematicDiff += p_time;
+ if (_cinematicMgr->m_cinematicCamera && _cinematicMgr->m_activeCinematicCameraId && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF)
+ {
+ _cinematicMgr->m_lastCinematicCheck = getMSTime();
+ _cinematicMgr->UpdateCinematicLocation(p_time);
+ }
+
+ //used to implement delayed far teleports
SetCanDelayTeleport(true);
Unit::Update(p_time);
SetCanDelayTeleport(false);
@@ -1565,7 +1567,7 @@ void Player::setDeathState(DeathState s)
// lost combo points at any target (targeted combo points clear in Unit::setDeathState)
ClearComboPoints();
- clearResurrectRequestData();
+ ClearResurrectRequestData();
//FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true);
@@ -2056,24 +2058,7 @@ void Player::ProcessDelayedOperations()
return;
if (m_DelayedOperations & DELAYED_RESURRECT_PLAYER)
- {
- ResurrectPlayer(0.0f, false);
-
- if (GetMaxHealth() > m_resurrectHealth)
- SetHealth(m_resurrectHealth);
- else
- SetFullHealth();
-
- if (GetMaxPower(POWER_MANA) > m_resurrectMana)
- SetPower(POWER_MANA, m_resurrectMana);
- else
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
-
- SetPower(POWER_RAGE, 0);
- SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
-
- SpawnCorpseBones();
- }
+ ResurrectUsingRequestDataImpl();
if (m_DelayedOperations & DELAYED_SAVE_PLAYER)
SaveToDB();
@@ -2434,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
@@ -2841,8 +2826,8 @@ void Player::GiveLevel(uint8 level)
{
++m_grantableLevels;
- if (!HasByteFlag(PLAYER_FIELD_BYTES, 1, 0x01))
- SetByteFlag(PLAYER_FIELD_BYTES, 1, 0x01);
+ if (!HasByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01))
+ SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
}
sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
@@ -3860,7 +3845,7 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId)
if (_spell_idx->second->skillId != SKILL_MOUNTS)
break; // We can break because mount spells belong only to one skillline (at least 310 flyers do)
- spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &&
spellInfo->Effects[i].CalcValue() == 310)
@@ -3880,7 +3865,7 @@ void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns)
// remove cooldowns on spells that have < 10 min CD
GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS;
}, true);
@@ -4758,30 +4743,9 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
}
}
-void Player::SendGhoulResurrectRequest(Player* target)
-{
- target->m_ghoulResurrectPlayerGUID = GetGUID();
-
- WorldPacket data(SMSG_RESURRECT_REQUEST, 8 + 4 + 1 + 1);
- data << uint64(GetGUID());
- data << uint32(0);
- data << uint8(0);
- data << uint8(0);
- target->GetSession()->SendPacket(&data);
-}
-
-void Player::GhoulResurrect()
-{
- CastSpell(this, 46619 /*SPELL_DK_RAISE_ALLY*/, true, nullptr, nullptr, m_ghoulResurrectPlayerGUID);
-
- m_ghoulResurrectPlayerGUID = ObjectGuid::Empty;
-}
-
void Player::RemoveGhoul()
{
- if (IsGhouled())
- if (Creature* ghoul = ObjectAccessor::GetCreature(*this, m_ghoulResurrectGhoulGUID))
- ghoul->DespawnOrUnsummon(); // Raise Ally aura will handle unauras
+ RemoveAura(SPELL_DK_RAISE_ALLY);
}
void Player::KillPlayer()
@@ -4829,7 +4793,7 @@ Corpse* Player::CreateCorpse()
// prevent the existence of 2 corpses for one player
SpawnCorpseBones();
- uint32 _pb, _pb2, _cfb1, _cfb2;
+ uint32 _cfb1, _cfb2;
Corpse* corpse = new Corpse((m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE);
SetPvPDeath(false);
@@ -4842,16 +4806,13 @@ Corpse* Player::CreateCorpse()
_corpseLocation.WorldRelocate(*this);
- _pb = GetUInt32Value(PLAYER_BYTES);
- _pb2 = GetUInt32Value(PLAYER_BYTES_2);
-
- uint8 skin = (uint8)(_pb);
- uint8 face = (uint8)(_pb >> 8);
- uint8 hairstyle = (uint8)(_pb >> 16);
- uint8 haircolor = (uint8)(_pb >> 24);
- uint8 facialhair = (uint8)(_pb2);
+ uint8 skin = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
+ uint8 face = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID);
+ uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ uint8 haircolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ uint8 facialhair = GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
- _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, 0) << 16) | (skin << 24));
+ _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) << 16) | (skin << 24));
_cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
@@ -4980,6 +4941,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;
@@ -6396,15 +6360,6 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, bool t
return true;
}
-void Player::SaveRecallPosition()
-{
- m_recallMap = GetMapId();
- m_recallX = GetPositionX();
- m_recallY = GetPositionY();
- m_recallZ = GetPositionZ();
- m_recallO = GetOrientation();
-}
-
void Player::SendMessageToSetInRange(WorldPacket* data, float dist, bool self)
{
if (self)
@@ -6444,6 +6399,8 @@ void Player::SendCinematicStart(uint32 CinematicSequenceId) const
WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(CinematicSequenceId);
SendDirectMessage(&data);
+ if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId))
+ _cinematicMgr->SetActiveCinematicCamera(sequence->cinematicCamera);
}
void Player::SendMovieStart(uint32 MovieId) const
@@ -7010,6 +6967,7 @@ void Player::SetInArenaTeam(uint32 ArenaTeamId, uint8 slot, uint8 type)
SetArenaTeamInfoField(slot, ARENA_TEAM_ID, ArenaTeamId);
SetArenaTeamInfoField(slot, ARENA_TEAM_TYPE, type);
}
+
void Player::SetArenaTeamInfoField(uint8 slot, ArenaTeamInfoType type, uint32 value)
{
SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + type, value);
@@ -10383,8 +10341,7 @@ InventoryResult Player::CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, Ite
if (pItem2)
{
// can be merged at least partly
- uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
- if (res != EQUIP_ERR_OK)
+ if (pItem2->CanBeMergedPartlyWith(pProto) != EQUIP_ERR_OK)
continue;
// descrease at current stacksize
@@ -10434,8 +10391,7 @@ InventoryResult Player::CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 sl
if (pItem2)
{
// can be merged at least partly
- uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
- if (res != EQUIP_ERR_OK)
+ if (pItem2->CanBeMergedPartlyWith(pProto) != EQUIP_ERR_OK)
continue;
// descrease at current stacksize
@@ -11113,7 +11069,7 @@ InventoryResult Player::CanStoreItems(Item** items, int count, uint32* itemLimit
continue;
// search free slot in bags
- for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
+ for (uint8 t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
{
if (Bag* bag = GetBagByPos(t))
{
@@ -12638,7 +12594,7 @@ void Player::DestroyConjuredItems(bool update)
Item* Player::GetItemByEntry(uint32 entry) const
{
// in inventory
- for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
+ for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetEntry() == entry)
return pItem;
@@ -12648,14 +12604,14 @@ Item* Player::GetItemByEntry(uint32 entry) const
if (pItem->GetEntry() == entry)
return pItem;
- for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Bag* pBag = GetBagByPos(i))
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
if (Item* pItem = pBag->GetItemByPos(j))
if (pItem->GetEntry() == entry)
return pItem;
- for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetEntry() == entry)
return pItem;
@@ -13848,8 +13804,8 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
TC_LOG_ERROR("entities.player", "Player::ApplyEnchantment: Unknown item enchantment (ID: %u, DisplayType: %u) for player '%s' (%s)",
enchant_id, enchant_display_type, GetName().c_str(), GetGUID().ToString().c_str());
break;
- } /*switch (enchant_display_type)*/
- } /*for*/
+ }
+ }
}
// visualize enchantment at player and equipped items
@@ -16900,12 +16856,9 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
return false;
}
- // overwrite some data fields
- uint32 bytes0 = 0;
- bytes0 |= fields[3].GetUInt8(); // race
- bytes0 |= fields[4].GetUInt8() << 8; // class
- bytes0 |= gender << 16; // gender
- SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, fields[3].GetUInt8());
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, fields[4].GetUInt8());
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, gender);
// check if race/class combination is valid
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
@@ -16932,25 +16885,25 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
money = MAX_MONEY_AMOUNT;
SetMoney(money);
- SetByteValue(PLAYER_BYTES, 0, fields[9].GetUInt8());
- SetByteValue(PLAYER_BYTES, 1, fields[10].GetUInt8());
- SetByteValue(PLAYER_BYTES, 2, fields[11].GetUInt8());
- SetByteValue(PLAYER_BYTES, 3, fields[12].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 0, fields[13].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 2, fields[14].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 3, fields[15].GetUInt8());
- SetByteValue(PLAYER_BYTES_3, 0, fields[5].GetUInt8());
- SetByteValue(PLAYER_BYTES_3, 1, fields[54].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, fields[9].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, fields[10].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, fields[11].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, fields[12].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, fields[13].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS, fields[14].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, fields[15].GetUInt8());
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, fields[5].GetUInt8());
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, fields[54].GetUInt8());
if (!ValidateAppearance(
fields[3].GetUInt8(), // race
fields[4].GetUInt8(), // class
gender,
- GetByteValue(PLAYER_BYTES, 2),
- GetByteValue(PLAYER_BYTES, 3),
- GetByteValue(PLAYER_BYTES, 1),
- GetByteValue(PLAYER_BYTES_2, 0),
- GetByteValue(PLAYER_BYTES, 0)))
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID),
+ GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID)))
{
TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", guid.ToString().c_str());
return false;
@@ -16964,7 +16917,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetUInt32Value(PLAYER_AMMO_ID, fields[68].GetUInt32());
// set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise)
- SetByteValue(PLAYER_FIELD_BYTES, 2, fields[70].GetUInt8());
+ SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[70].GetUInt8());
InitDisplayIds();
@@ -17547,7 +17500,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND);
if (m_grantableLevels > 0)
- SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01);
+ SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
_LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
@@ -17754,7 +17707,7 @@ void Player::LoadCorpse(PreparedQueryResult result)
{
Field* fields = result->Fetch();
_corpseLocation.WorldRelocate(fields[0].GetUInt16(), fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
- ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable());
+ ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable());
}
else
ResurrectPlayer(0.5f);
@@ -17790,7 +17743,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
ObjectGuid::LowType bagGuid = fields[11].GetUInt32();
uint8 slot = fields[12].GetUInt8();
- uint8 err = EQUIP_ERR_OK;
+ InventoryResult err = EQUIP_ERR_OK;
// Item is not in bag
if (!bagGuid)
{
@@ -17866,7 +17819,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
else
{
TC_LOG_ERROR("entities.player", "Player::_LoadInventory: Player '%s' (%s) has item (%s, entry: %u) which can't be loaded into inventory (Bag %u, slot: %u) by reason %u. Item will be sent by mail.",
- GetName().c_str(), GetGUID().ToString().c_str(), item->GetGUID().ToString().c_str(), item->GetEntry(), bagGuid, slot, err);
+ GetName().c_str(), GetGUID().ToString().c_str(), item->GetGUID().ToString().c_str(), item->GetEntry(), bagGuid, slot, uint32(err));
item->DeleteFromInventoryDB(trans);
problematicItems.push_back(item);
}
@@ -18988,17 +18941,17 @@ void Player::SaveToDB(bool create /*=false*/)
stmt->setString(index++, GetName());
stmt->setUInt8(index++, getRace());
stmt->setUInt8(index++, getClass());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, 0)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
stmt->setUInt8(index++, getLevel());
stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
stmt->setUInt32(index++, GetMoney());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 1));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 3));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 3));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE));
stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
stmt->setUInt16(index++, (uint16)GetMapId());
stmt->setUInt32(index++, (uint32)GetInstanceId());
@@ -19088,7 +19041,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
stmt->setString(index++, ss.str());
- stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES));
stmt->setUInt32(index, m_grantableLevels);
}
else
@@ -19098,17 +19051,17 @@ void Player::SaveToDB(bool create /*=false*/)
stmt->setString(index++, GetName());
stmt->setUInt8(index++, getRace());
stmt->setUInt8(index++, getClass());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, 0)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
stmt->setUInt8(index++, getLevel());
stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
stmt->setUInt32(index++, GetMoney());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 1));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 3));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 3));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE));
stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
if (!IsBeingTeleported())
@@ -19213,7 +19166,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
stmt->setString(index++, ss.str());
- stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES));
stmt->setUInt32(index++, m_grantableLevels);
stmt->setUInt8(index++, IsInWorld() && !GetSession()->PlayerLogout() ? 1 : 0);
@@ -20352,6 +20305,11 @@ void Player::Say(std::string const& text, Language language, WorldObject const*
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
}
+void Player::Say(uint32 textId, WorldObject const* target /*= nullptr*/)
+{
+ Talk(textId, CHAT_MSG_SAY, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), target);
+}
+
void Player::Yell(std::string const& text, Language language, WorldObject const* /*= nullptr*/)
{
std::string _text(text);
@@ -20362,6 +20320,11 @@ void Player::Yell(std::string const& text, Language language, WorldObject const*
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
}
+void Player::Yell(uint32 textId, WorldObject const* target /*= nullptr*/)
+{
+ Talk(textId, CHAT_MSG_YELL, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), target);
+}
+
void Player::TextEmote(std::string const& text, WorldObject const* /*= nullptr*/, bool /*= false*/)
{
std::string _text(text);
@@ -20372,6 +20335,11 @@ void Player::TextEmote(std::string const& text, WorldObject const* /*= nullptr*/
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT));
}
+void Player::TextEmote(uint32 textId, WorldObject const* target /*= nullptr*/, bool /*isBossEmote = false*/)
+{
+ Talk(textId, CHAT_MSG_EMOTE, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), target);
+}
+
void Player::Whisper(std::string const& text, Language language, Player* target, bool /*= false*/)
{
ASSERT(target);
@@ -20408,6 +20376,24 @@ void Player::Whisper(std::string const& text, Language language, Player* target,
ChatHandler(GetSession()).PSendSysMessage(LANG_PLAYER_DND, target->GetName().c_str(), target->autoReplyMsg.c_str());
}
+void Player::Whisper(uint32 textId, Player* target, bool /*isBossWhisper = false*/)
+{
+ if (!target)
+ return;
+
+ BroadcastText const* bct = sObjectMgr->GetBroadcastText(textId);
+ if (!bct)
+ {
+ TC_LOG_ERROR("entities.unit", "WorldObject::MonsterWhisper: `broadcast_text` was not %u found", textId);
+ return;
+ }
+
+ LocaleConstant locale = target->GetSession()->GetSessionDbLocaleIndex();
+ WorldPacket data;
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_UNIVERSAL, this, target, bct->GetText(locale, getGender()), 0, "", locale);
+ target->SendDirectMessage(&data);
+}
+
Item* Player::GetMItem(uint32 id)
{
ItemMap::const_iterator itr = mMitems.find(id);
@@ -20526,7 +20512,7 @@ void Player::VehicleSpellInitialize()
data << uint8(0); // Command State
data << uint16(0x800); // DisableActions (set for all vehicles)
- for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i)
{
uint32 spellId = vehicle->m_spells[i];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@@ -20550,7 +20536,7 @@ void Player::VehicleSpellInitialize()
data << uint32(MAKE_UNIT_ACTION_BUTTON(spellId, i+8));
}
- for (uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i)
+ for (uint32 i = MAX_CREATURE_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i)
data << uint32(0);
data << uint8(0); // Auras?
@@ -20933,13 +20919,13 @@ void Player::SetRestBonus(float rest_bonus_new)
// update data for client
if ((GetsRecruitAFriendBonus(true) && (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0)))
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_RAF_LINKED);
else
{
if (m_rest_bonus > 10)
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_RESTED);
else if (m_rest_bonus <= 1)
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_NOT_RAF_LINKED);
}
//RestTickUpdate
@@ -21273,7 +21259,7 @@ void Player::InitDisplayIds()
return;
}
- uint8 gender = GetByteValue(PLAYER_BYTES_3, 0);
+ uint8 gender = GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
switch (gender)
{
case GENDER_FEMALE:
@@ -21447,8 +21433,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
// honor points price
if (GetHonorPoints() < (iece->reqhonorpoints * count))
{
- TC_LOG_ERROR("entities.player", "Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
- SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL);
+ SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, nullptr, nullptr);
return false;
}
@@ -21664,24 +21649,17 @@ void Player::UpdatePotionCooldown(Spell* spell)
m_lastPotionId = 0;
}
-void Player::setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
+void Player::SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura)
{
- m_resurrectGUID = guid;
- m_resurrectMap = mapId;
- m_resurrectX = X;
- m_resurrectY = Y;
- m_resurrectZ = Z;
- m_resurrectHealth = health;
- m_resurrectMana = mana;
+ ASSERT(!IsResurrectRequested());
+ _resurrectionData.reset(new ResurrectionData());
+ _resurrectionData->GUID = caster->GetGUID();
+ _resurrectionData->Location.WorldRelocate(*caster);
+ _resurrectionData->Health = health;
+ _resurrectionData->Mana = mana;
+ _resurrectionData->Aura = appliedAura;
}
-void Player::clearResurrectRequestData()
-{
- setResurrectRequestData(ObjectGuid::Empty, 0, 0.0f, 0.0f, 0.0f, 0, 0);
-
- m_ghoulResurrectPlayerGUID = ObjectGuid::Empty;
- m_ghoulResurrectGhoulGUID = ObjectGuid::Empty;
-}
//slot to be excluded while counting
bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) const
{
@@ -21889,7 +21867,7 @@ void Player::SetBattlegroundEntryPoint()
void Player::SetBGTeam(uint32 team)
{
m_bgData.bgTeam = team;
- SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0));
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_ARENA_FACTION, uint8(team == ALLIANCE ? 1 : 0));
}
uint32 Player::GetBGTeam() const
@@ -21989,7 +21967,7 @@ WorldLocation Player::GetStartPosition() const
return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0);
}
-bool Player::HaveAtClient(WorldObject const* u) const
+bool Player::HaveAtClient(Object const* u) const
{
return u == this || m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end();
}
@@ -22590,7 +22568,7 @@ void Player::ApplyEquipCooldown(Item* pItem)
continue;
// Don't replace longer cooldowns by equip cooldown if we have any.
- if (GetSpellHistory()->GetRemainingCooldown(sSpellMgr->EnsureSpellInfo(spellData.SpellId)) > 30 * IN_MILLISECONDS)
+ if (GetSpellHistory()->GetRemainingCooldown(sSpellMgr->AssertSpellInfo(spellData.SpellId)) > 30 * IN_MILLISECONDS)
continue;
GetSpellHistory()->AddCooldown(spellData.SpellId, pItem->GetEntry(), std::chrono::seconds(30));
@@ -23208,13 +23186,32 @@ void Player::UpdateForQuestWorldObjects()
GetSession()->SendPacket(&packet);
}
-void Player::SetSummonPoint(uint32 mapid, float x, float y, float z)
+bool Player::HasSummonPending() const
+{
+ return m_summon_expire >= time(nullptr);
+}
+
+void Player::SendSummonRequestFrom(Unit* summoner)
{
+ if (!summoner)
+ return;
+
+ // Player already has active summon request
+ if (HasSummonPending())
+ return;
+
+ // Evil Twin (ignore player summon, but hide this for summoner)
+ if (HasAura(23445))
+ return;
+
m_summon_expire = time(nullptr) + MAX_PLAYER_SUMMON_DELAY;
- m_summon_mapid = mapid;
- m_summon_x = x;
- m_summon_y = y;
- m_summon_z = z;
+ m_summon_location.WorldRelocate(*summoner);
+
+ WorldPacket data(SMSG_SUMMON_REQUEST, 8 + 4 + 4);
+ data << uint64(summoner->GetGUID()); // summoner guid
+ data << uint32(summoner->GetZoneId()); // summoner zone
+ data << uint32(MAX_PLAYER_SUMMON_DELAY*IN_MILLISECONDS); // auto decline after msecs
+ GetSession()->SendPacket(&data);
}
void Player::SummonIfPossible(bool agree)
@@ -23245,7 +23242,7 @@ void Player::SummonIfPossible(bool agree)
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1);
- TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation());
+ TeleportTo(m_summon_location);
}
void Player::RemoveItemDurations(Item* item)
@@ -23284,8 +23281,7 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
return;
ItemPosCountVec off_dest;
- uint8 off_msg = CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false);
- if (off_msg == EQUIP_ERR_OK)
+ if (CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false) == EQUIP_ERR_OK)
{
RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
StoreItem(off_dest, offItem, true);
@@ -23588,8 +23584,14 @@ void Player::ResurrectUsingRequestData()
{
RemoveGhoul();
+ if (uint32 aura = _resurrectionData->Aura)
+ {
+ CastSpell(this, aura, true, nullptr, nullptr, _resurrectionData->GUID);
+ return;
+ }
+
/// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
- TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
+ TeleportTo(_resurrectionData->Location);
if (IsBeingTeleported())
{
@@ -23597,15 +23599,20 @@ void Player::ResurrectUsingRequestData()
return;
}
+ ResurrectUsingRequestDataImpl();
+}
+
+void Player::ResurrectUsingRequestDataImpl()
+{
ResurrectPlayer(0.0f, false);
- if (GetMaxHealth() > m_resurrectHealth)
- SetHealth(m_resurrectHealth);
+ if (GetMaxHealth() > _resurrectionData->Health)
+ SetHealth(_resurrectionData->Health);
else
SetFullHealth();
- if (GetMaxPower(POWER_MANA) > m_resurrectMana)
- SetPower(POWER_MANA, m_resurrectMana);
+ if (GetMaxPower(POWER_MANA) > _resurrectionData->Mana)
+ SetPower(POWER_MANA, _resurrectionData->Mana);
else
SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
@@ -24100,10 +24107,10 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n
if (level > GT_MAX_LEVEL)
level = GT_MAX_LEVEL; // max level in this dbc
- uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
- uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
- uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
- uint8 skincolor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ uint8 haircolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ uint8 facialhair = GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
+ uint8 skincolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
if ((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair) && (!newSkin || (newSkin->hair_id == skincolor)))
return 0;
@@ -26133,6 +26140,9 @@ bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
if (!packetOnly && !Unit::SetCanFly(apply))
return false;
+ if (!apply)
+ SetFallInformation(0, GetPositionZ());
+
WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
@@ -26324,7 +26334,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
case SUMMON_PET:
// this enables pet details window (Shift+P)
pet->GetCharmInfo()->SetPetNumber(pet_number, true);
- pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048);
+ pet->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_MAGE);
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
pet->SetFullHealth();
@@ -26461,3 +26471,22 @@ void Player::RemoveRestFlag(RestFlag restFlag)
RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
}
}
+
+uint32 Player::DoRandomRoll(uint32 minimum, uint32 maximum)
+{
+ ASSERT(maximum <= 10000);
+
+ uint32 roll = urand(minimum, maximum);
+
+ WorldPacket data(MSG_RANDOM_ROLL, 4 + 4 + 4 + 8);
+ data << uint32(minimum);
+ data << uint32(maximum);
+ data << uint32(roll);
+ data << GetGUID();
+ if (Group* group = GetGroup())
+ group->BroadcastPacket(&data, false);
+ else
+ SendDirectMessage(&data);
+
+ return roll;
+}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index b2cb2aed226..6a38cd3b9fe 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -30,6 +30,7 @@
#include "SpellHistory.h"
#include "Unit.h"
#include "TradeData.h"
+#include "CinematicMgr.h"
#include <limits>
#include <string>
@@ -315,7 +316,7 @@ enum RuneCooldowns
RUNE_MISS_COOLDOWN = 1500 // cooldown applied on runes when the spell misses
};
-enum RuneType
+enum RuneType : uint8
{
RUNE_BLOOD = 0,
RUNE_UNHOLY = 1,
@@ -415,50 +416,48 @@ enum PlayerFlags
PLAYER_FLAGS_UNK31 = 0x80000000
};
-// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
-// can't use enum for uint64 values
-#define PLAYER_TITLE_DISABLED UI64LIT(0x0000000000000000)
-#define PLAYER_TITLE_NONE UI64LIT(0x0000000000000001)
-#define PLAYER_TITLE_PRIVATE UI64LIT(0x0000000000000002) // 1
-#define PLAYER_TITLE_CORPORAL UI64LIT(0x0000000000000004) // 2
-#define PLAYER_TITLE_SERGEANT_A UI64LIT(0x0000000000000008) // 3
-#define PLAYER_TITLE_MASTER_SERGEANT UI64LIT(0x0000000000000010) // 4
-#define PLAYER_TITLE_SERGEANT_MAJOR UI64LIT(0x0000000000000020) // 5
-#define PLAYER_TITLE_KNIGHT UI64LIT(0x0000000000000040) // 6
-#define PLAYER_TITLE_KNIGHT_LIEUTENANT UI64LIT(0x0000000000000080) // 7
-#define PLAYER_TITLE_KNIGHT_CAPTAIN UI64LIT(0x0000000000000100) // 8
-#define PLAYER_TITLE_KNIGHT_CHAMPION UI64LIT(0x0000000000000200) // 9
-#define PLAYER_TITLE_LIEUTENANT_COMMANDER UI64LIT(0x0000000000000400) // 10
-#define PLAYER_TITLE_COMMANDER UI64LIT(0x0000000000000800) // 11
-#define PLAYER_TITLE_MARSHAL UI64LIT(0x0000000000001000) // 12
-#define PLAYER_TITLE_FIELD_MARSHAL UI64LIT(0x0000000000002000) // 13
-#define PLAYER_TITLE_GRAND_MARSHAL UI64LIT(0x0000000000004000) // 14
-#define PLAYER_TITLE_SCOUT UI64LIT(0x0000000000008000) // 15
-#define PLAYER_TITLE_GRUNT UI64LIT(0x0000000000010000) // 16
-#define PLAYER_TITLE_SERGEANT_H UI64LIT(0x0000000000020000) // 17
-#define PLAYER_TITLE_SENIOR_SERGEANT UI64LIT(0x0000000000040000) // 18
-#define PLAYER_TITLE_FIRST_SERGEANT UI64LIT(0x0000000000080000) // 19
-#define PLAYER_TITLE_STONE_GUARD UI64LIT(0x0000000000100000) // 20
-#define PLAYER_TITLE_BLOOD_GUARD UI64LIT(0x0000000000200000) // 21
-#define PLAYER_TITLE_LEGIONNAIRE UI64LIT(0x0000000000400000) // 22
-#define PLAYER_TITLE_CENTURION UI64LIT(0x0000000000800000) // 23
-#define PLAYER_TITLE_CHAMPION UI64LIT(0x0000000001000000) // 24
-#define PLAYER_TITLE_LIEUTENANT_GENERAL UI64LIT(0x0000000002000000) // 25
-#define PLAYER_TITLE_GENERAL UI64LIT(0x0000000004000000) // 26
-#define PLAYER_TITLE_WARLORD UI64LIT(0x0000000008000000) // 27
-#define PLAYER_TITLE_HIGH_WARLORD UI64LIT(0x0000000010000000) // 28
-#define PLAYER_TITLE_GLADIATOR UI64LIT(0x0000000020000000) // 29
-#define PLAYER_TITLE_DUELIST UI64LIT(0x0000000040000000) // 30
-#define PLAYER_TITLE_RIVAL UI64LIT(0x0000000080000000) // 31
-#define PLAYER_TITLE_CHALLENGER UI64LIT(0x0000000100000000) // 32
-#define PLAYER_TITLE_SCARAB_LORD UI64LIT(0x0000000200000000) // 33
-#define PLAYER_TITLE_CONQUEROR UI64LIT(0x0000000400000000) // 34
-#define PLAYER_TITLE_JUSTICAR UI64LIT(0x0000000800000000) // 35
-#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU UI64LIT(0x0000001000000000) // 36
-#define PLAYER_TITLE_MERCILESS_GLADIATOR UI64LIT(0x0000002000000000) // 37
-#define PLAYER_TITLE_OF_THE_SHATTERED_SUN UI64LIT(0x0000004000000000) // 38
-#define PLAYER_TITLE_HAND_OF_ADAL UI64LIT(0x0000008000000000) // 39
-#define PLAYER_TITLE_VENGEFUL_GLADIATOR UI64LIT(0x0000010000000000) // 40
+enum PlayerBytesOffsets
+{
+ PLAYER_BYTES_OFFSET_SKIN_ID = 0,
+ PLAYER_BYTES_OFFSET_FACE_ID = 1,
+ PLAYER_BYTES_OFFSET_HAIR_STYLE_ID = 2,
+ PLAYER_BYTES_OFFSET_HAIR_COLOR_ID = 3
+};
+
+enum PlayerBytes2Offsets
+{
+ PLAYER_BYTES_2_OFFSET_FACIAL_STYLE = 0,
+ PLAYER_BYTES_2_OFFSET_PARTY_TYPE = 1,
+ PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS = 2,
+ PLAYER_BYTES_2_OFFSET_REST_STATE = 3
+};
+
+enum PlayerBytes3Offsets
+{
+ PLAYER_BYTES_3_OFFSET_GENDER = 0,
+ PLAYER_BYTES_3_OFFSET_INEBRIATION = 1,
+ PLAYER_BYTES_3_OFFSET_PVP_TITLE = 2,
+ PLAYER_BYTES_3_OFFSET_ARENA_FACTION = 3
+};
+
+enum PlayerFieldBytesOffsets
+{
+ PLAYER_FIELD_BYTES_OFFSET_FLAGS = 0,
+ PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL = 1,
+ PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES = 2,
+ PLAYER_FIELD_BYTES_OFFSET_LIFETIME_MAX_PVP_RANK = 3
+};
+
+enum PlayerFieldBytes2Offsets
+{
+ PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID = 0, // uint16!
+ PLAYER_FIELD_BYTES_2_OFFSET_IGNORE_POWER_REGEN_PREDICTION_MASK = 2,
+ PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION = 3
+};
+
+static_assert((PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID & 1) == 0, "PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID must be aligned to 2 byte boundary");
+
+#define PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET (PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID / 2)
#define KNOWN_TITLES_SIZE 3
#define MAX_TITLE_INDEX (KNOWN_TITLES_SIZE*64) // 3 uint64 fields
@@ -584,7 +583,7 @@ enum PlayerSlots
#define INVENTORY_SLOT_BAG_0 255
-enum EquipmentSlots // 19 slots
+enum EquipmentSlots : uint8 // 19 slots
{
EQUIPMENT_SLOT_START = 0,
EQUIPMENT_SLOT_HEAD = 0,
@@ -609,13 +608,13 @@ enum EquipmentSlots // 19 slots
EQUIPMENT_SLOT_END = 19
};
-enum InventorySlots // 4 slots
+enum InventorySlots : uint8 // 4 slots
{
INVENTORY_SLOT_BAG_START = 19,
INVENTORY_SLOT_BAG_END = 23
};
-enum InventoryPackSlots // 16 slots
+enum InventoryPackSlots : uint8 // 16 slots
{
INVENTORY_SLOT_ITEM_START = 23,
INVENTORY_SLOT_ITEM_END = 39
@@ -640,7 +639,7 @@ enum BuyBackSlots // 12 slots
BUYBACK_SLOT_END = 86
};
-enum KeyRingSlots // 32 slots
+enum KeyRingSlots : uint8 // 32 slots
{
KEYRING_SLOT_START = 86,
KEYRING_SLOT_END = 118
@@ -750,7 +749,7 @@ enum TeleportToOptions
};
/// Type of environmental damages
-enum EnviromentalDamage
+enum EnviromentalDamage : uint8
{
DAMAGE_EXHAUSTED = 0,
DAMAGE_DROWNING = 1,
@@ -833,7 +832,7 @@ enum PlayerDelayedOperations
// Player summoning auto-decline time (in secs)
#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE)
// Maximum money amount : 2^31 - 1
-extern uint32 const MAX_MONEY_AMOUNT;
+TC_GAME_API extern uint32 const MAX_MONEY_AMOUNT;
enum BindExtensionState
{
@@ -902,7 +901,7 @@ enum ReferAFriendError
ERR_REFER_A_FRIEND_SUMMON_OFFLINE_S = 0x0D
};
-enum PlayerRestState
+enum PlayerRestState : uint8
{
REST_STATE_RESTED = 0x01,
REST_STATE_NOT_RAF_LINKED = 0x02,
@@ -919,7 +918,7 @@ enum PlayerCommandStates
CHEAT_WATERWALK = 0x10
};
-class PlayerTaxi
+class TC_GAME_API PlayerTaxi
{
public:
PlayerTaxi();
@@ -1014,9 +1013,21 @@ struct TradeStatusInfo
uint8 Slot;
};
-class Player : public Unit, public GridObject<Player>
+struct ResurrectionData
+{
+ ObjectGuid GUID;
+ WorldLocation Location;
+ uint32 Health;
+ uint32 Mana;
+ uint32 Aura;
+};
+
+#define SPELL_DK_RAISE_ALLY 46619
+
+class TC_GAME_API Player : public Unit, public GridObject<Player>
{
friend class WorldSession;
+ friend class CinematicMgr;
friend void Item::AddToUpdateQueueOf(Player* player);
friend void Item::RemoveFromUpdateQueueOf(Player* player);
public:
@@ -1041,7 +1052,8 @@ class Player : public Unit, public GridObject<Player>
bool TeleportTo(WorldLocation const &loc, uint32 options = 0);
bool TeleportToBGEntryPoint();
- void SetSummonPoint(uint32 mapid, float x, float y, float z);
+ bool HasSummonPending() const;
+ void SendSummonRequestFrom(Unit* summoner);
void SummonIfPossible(bool agree);
bool Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo);
@@ -1139,12 +1151,16 @@ class Player : public Unit, public GridObject<Player>
/// Handles said message in regular chat based on declared language and in config pre-defined Range.
void Say(std::string const& text, Language language, WorldObject const* = nullptr) override;
+ void Say(uint32 textId, WorldObject const* target = nullptr) override;
/// Handles yelled message in regular chat based on declared language and in config pre-defined Range.
void Yell(std::string const& text, Language language, WorldObject const* = nullptr) override;
+ void Yell(uint32 textId, WorldObject const* target = nullptr) override;
/// Outputs an universal text which is supposed to be an action.
void TextEmote(std::string const& text, WorldObject const* = nullptr, bool = false) override;
+ void TextEmote(uint32 textId, WorldObject const* target = nullptr, bool isBossEmote = false) override;
/// Handles whispers from Addons and players based on sender, receiver's guid and language.
void Whisper(std::string const& text, Language language, Player* receiver, bool = false) override;
+ void Whisper(uint32 textId, Player* target, bool isBossWhisper = false) override;
/*********************************************************/
/*** STORAGE SYSTEM ***/
@@ -1174,8 +1190,8 @@ class Player : public Unit, public GridObject<Player>
static bool IsBankPos(uint8 bag, uint8 slot);
bool IsValidPos(uint16 pos, bool explicit_pos) const { return IsValidPos(pos >> 8, pos & 255, explicit_pos); }
bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const;
- uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, 2); }
- void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, 2, count); }
+ uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS); }
+ void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS, count); }
bool HasItemCount(uint32 item, uint32 count = 1, bool inBankAlso = false) const;
bool HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item const* ignoreItem = nullptr) const;
bool CanNoReagentCast(SpellInfo const* spellInfo) const;
@@ -1260,6 +1276,8 @@ class Player : public Unit, public GridObject<Player>
TradeData* GetTradeData() const { return m_trade; }
void TradeCancel(bool sendback);
+ CinematicMgr* GetCinematicMgr() const { return _cinematicMgr; }
+
void UpdateEnchantTime(uint32 time);
void UpdateSoulboundTradeItems();
void AddTradeableItem(Item* item);
@@ -1578,7 +1596,8 @@ class 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);
@@ -1590,11 +1609,24 @@ class Player : public Unit, public GridObject<Player>
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
void UpdatePotionCooldown(Spell* spell = nullptr);
- void setResurrectRequestData(ObjectGuid guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana);
- void clearResurrectRequestData();
- bool isResurrectRequestedBy(ObjectGuid guid) const { return !m_resurrectGUID.IsEmpty() && m_resurrectGUID == guid; }
- bool isResurrectRequested() const { return !m_resurrectGUID.IsEmpty(); }
+ void SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura);
+
+ void ClearResurrectRequestData()
+ {
+ _resurrectionData.reset();
+ }
+
+ bool IsResurrectRequestedBy(ObjectGuid const& guid) const
+ {
+ if (!IsResurrectRequested())
+ return false;
+
+ return !_resurrectionData->GUID.IsEmpty() && _resurrectionData->GUID == guid;
+ }
+
+ bool IsResurrectRequested() const { return _resurrectionData.get() != nullptr; }
void ResurrectUsingRequestData();
+ void ResurrectUsingRequestDataImpl();
uint8 getCinematic() const { return m_cinematic; }
void setCinematic(uint8 cine) { m_cinematic = cine; }
@@ -1785,14 +1817,7 @@ class Player : public Unit, public GridObject<Player>
void ResurrectPlayer(float restore_percent, bool applySickness = false);
void BuildPlayerRepop();
void RepopAtGraveyard();
- void SendGhoulResurrectRequest(Player* target);
- bool IsValidGhoulResurrectRequest(ObjectGuid guid)
- {
- return !m_ghoulResurrectPlayerGUID.IsEmpty() && m_ghoulResurrectPlayerGUID == guid;
- }
- void GhoulResurrect();
- void SetGhoulResurrectGhoulGUID(ObjectGuid guid) { m_ghoulResurrectGhoulGUID = guid; }
- ObjectGuid GetGhoulResurrectGhoulGUID() { return m_ghoulResurrectGhoulGUID; }
+
void RemoveGhoul();
void DurabilityLossAll(double percent, bool inventory);
@@ -1894,7 +1919,7 @@ class Player : public Unit, public GridObject<Player>
//End of PvP System
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
- uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); }
+ uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION); }
static DrunkenState GetDrunkenstateByValue(uint8 value);
uint32 GetDeathTimer() const { return m_deathTimer; }
@@ -2058,13 +2083,8 @@ class Player : public Unit, public GridObject<Player>
uint32 GetSaveTimer() const { return m_nextSave; }
void SetSaveTimer(uint32 timer) { m_nextSave = timer; }
- // Recall position
- uint32 m_recallMap;
- float m_recallX;
- float m_recallY;
- float m_recallZ;
- float m_recallO;
- void SaveRecallPosition();
+ void SaveRecallPosition() { m_recall_location.WorldRelocate(*this); }
+ void Recall() { TeleportTo(m_recall_location); }
void SetHomebind(WorldLocation const& loc, uint32 areaId);
@@ -2080,7 +2100,7 @@ class Player : public Unit, public GridObject<Player>
// currently visible objects at player client
GuidUnorderedSet m_clientGUIDs;
- bool HaveAtClient(WorldObject const* u) const;
+ bool HaveAtClient(Object const* u) const;
bool IsNeverVisible() const override;
@@ -2117,6 +2137,8 @@ class Player : public Unit, public GridObject<Player>
void SendCinematicStart(uint32 CinematicSequenceId) const;
void SendMovieStart(uint32 MovieId) const;
+ uint32 DoRandomRoll(uint32 minimum, uint32 maximum);
+
/*********************************************************/
/*** INSTANCE SYSTEM ***/
/*********************************************************/
@@ -2422,13 +2444,7 @@ class Player : public Unit, public GridObject<Player>
void ResetTimeSync();
void SendTimeSync();
- ObjectGuid m_resurrectGUID;
- uint32 m_resurrectMap;
- float m_resurrectX, m_resurrectY, m_resurrectZ;
- uint32 m_resurrectHealth, m_resurrectMana;
-
- ObjectGuid m_ghoulResurrectPlayerGUID;
- ObjectGuid m_ghoulResurrectGhoulGUID;
+ std::unique_ptr<ResurrectionData> _resurrectionData;
WorldSession* m_session;
@@ -2490,10 +2506,10 @@ class Player : public Unit, public GridObject<Player>
// Player summoning
time_t m_summon_expire;
- uint32 m_summon_mapid;
- float m_summon_x;
- float m_summon_y;
- float m_summon_z;
+ WorldLocation m_summon_location;
+
+ // Recall position
+ WorldLocation m_recall_location;
DeclinedName *m_declinedname;
Runes *m_runes;
@@ -2515,6 +2531,8 @@ class Player : public Unit, public GridObject<Player>
Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update);
Item* _LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields);
+ CinematicMgr* _cinematicMgr;
+
GuidSet m_refundableItems;
void SendRefundInfo(Item* item);
void RefundItem(Item* item);
@@ -2583,15 +2601,17 @@ class Player : public Unit, public GridObject<Player>
WorldLocation _corpseLocation;
};
-void AddItemsSetItem(Player* player, Item* item);
-void RemoveItemsSetItem(Player* player, ItemTemplate const* proto);
+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;
@@ -2627,9 +2647,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/Player/SocialMgr.h b/src/server/game/Entities/Player/SocialMgr.h
index 0512e61033c..803301b95c7 100644
--- a/src/server/game/Entities/Player/SocialMgr.h
+++ b/src/server/game/Entities/Player/SocialMgr.h
@@ -99,7 +99,7 @@ enum FriendsResult
#define SOCIALMGR_FRIEND_LIMIT 50
#define SOCIALMGR_IGNORE_LIMIT 50
-class PlayerSocial
+class TC_GAME_API PlayerSocial
{
friend class SocialMgr;
public:
diff --git a/src/server/game/Entities/Player/TradeData.h b/src/server/game/Entities/Player/TradeData.h
index 276e6f3db1c..396d784fee7 100644
--- a/src/server/game/Entities/Player/TradeData.h
+++ b/src/server/game/Entities/Player/TradeData.h
@@ -31,7 +31,7 @@ enum TradeSlots
class Item;
class Player;
-class TradeData
+class TC_GAME_API TradeData
{
public:
TradeData(Player* player, Player* trader) :
diff --git a/src/server/game/Entities/Totem/Totem.h b/src/server/game/Entities/Totem/Totem.h
index bbe7943f4b6..42be4525a0c 100644
--- a/src/server/game/Entities/Totem/Totem.h
+++ b/src/server/game/Entities/Totem/Totem.h
@@ -32,7 +32,7 @@ enum TotemType
#define SENTRY_TOTEM_ENTRY 3968
-class Totem : public Minion
+class TC_GAME_API Totem : public Minion
{
public:
Totem(SummonPropertiesEntry const* properties, Unit* owner);
diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h
index 987fbf70739..4cd20c8b613 100644
--- a/src/server/game/Entities/Transport/Transport.h
+++ b/src/server/game/Entities/Transport/Transport.h
@@ -25,7 +25,7 @@
struct CreatureData;
-class Transport : public GameObject, public TransportBase
+class TC_GAME_API Transport : public GameObject, public TransportBase
{
friend Transport* TransportMgr::CreateTransport(uint32, ObjectGuid::LowType, Map*);
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index a8e13a9f7db..82792a49c96 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -1152,7 +1152,7 @@ bool Guardian::UpdateStats(Stats stat)
if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
AddPct(mod, spellInfo->Effects[EFFECT_0].CalcValue());
}
}
@@ -1321,7 +1321,7 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged)
if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* sProto = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* sProto = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
mod += CalculatePct(1.0f, sProto->Effects[1].CalcValue());
}
}
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 6711ed2d157..9afc2987319 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -578,7 +578,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons
void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb)
{
- if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()))
+ if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()))
{
if (absorb)
*absorb += damage;
@@ -599,11 +599,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;
@@ -1120,7 +1123,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss)
if (!victim)
return;
- if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()))
+ if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()))
return;
SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(damageInfo->SpellID);
@@ -1346,7 +1349,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
{
Unit* victim = damageInfo->target;
- if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()))
+ if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()))
return;
// Hmmmm dont like this emotes client must by self do all animations
@@ -2044,7 +2047,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackTy
MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const
{
- if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())
+ if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())
return MELEE_HIT_EVADE;
int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim);
@@ -2656,7 +2659,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo
return SPELL_MISS_NONE;
// Return evade for units in evade mode
- if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())
+ if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())
return SPELL_MISS_EVADE;
// Try victim reflect spell
@@ -3189,6 +3192,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);
@@ -4185,6 +4197,16 @@ void Unit::RemoveArenaAuras()
}
}
+void Unit::RemoveAurasOnEvade()
+{
+ if (IsCharmedOwnedByPlayerOrPlayer()) // if it is a player owned creature it should not remove the aura
+ return;
+
+ // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle
+ // don't remove clone caster on evade (to be verified)
+ RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER);
+}
+
void Unit::RemoveAllAurasOnDeath()
{
// used just after dieing to remove all visible auras
@@ -5277,7 +5299,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();
@@ -5287,8 +5309,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;
@@ -5364,24 +5384,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;
@@ -5611,11 +5627,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
@@ -5657,11 +5668,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)
@@ -6299,24 +6305,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:
@@ -6497,7 +6485,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;
@@ -6812,10 +6800,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;
@@ -6878,8 +6862,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)
@@ -7122,10 +7106,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)
@@ -7171,10 +7151,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
@@ -7476,26 +7452,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();
@@ -7707,12 +7674,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
@@ -7755,7 +7716,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();
@@ -7781,91 +7742,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;
- }
- }
- 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
@@ -7888,325 +7764,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;
@@ -8240,7 +7797,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;
@@ -8402,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
@@ -8539,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;
}
@@ -8586,29 +8137,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();
@@ -8699,14 +8244,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;
}
@@ -8715,7 +8253,7 @@ void Unit::setPowerType(Powers new_powertype)
if (getPowerType() == new_powertype)
return;
- SetByteValue(UNIT_FIELD_BYTES_0, 3, new_powertype);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, new_powertype);
if (GetTypeId() == TYPEID_PLAYER)
{
@@ -8990,7 +8528,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
}
else
{
- if (victim->ToCreature()->IsInEvadeMode())
+ if (victim->ToCreature()->IsEvadingAttacks())
return false;
}
@@ -9106,7 +8644,7 @@ bool Unit::AttackStop()
if (creature->HasSearchedAssistance())
{
creature->SetNoSearchAssistance(false);
- UpdateSpeed(MOVE_RUN, false);
+ UpdateSpeed(MOVE_RUN);
}
}
@@ -9440,7 +8978,7 @@ void Unit::SetMinion(Minion *minion, bool apply)
// FIXME: hack, speed must be set only at follow
if (GetTypeId() == TYPEID_PLAYER && minion->IsPet())
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- minion->SetSpeed(UnitMoveType(i), m_speed_rate[i], true);
+ minion->SetSpeedRate(UnitMoveType(i), m_speed_rate[i]);
// Ghoul pets have energy instead of mana (is anywhere better place for this code?)
if (minion->IsPetGhoul() || minion->IsRisenAlly())
@@ -11870,12 +11408,12 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
if (IsPet())
{
- UpdateSpeed(MOVE_RUN, true);
- UpdateSpeed(MOVE_SWIM, true);
- UpdateSpeed(MOVE_FLIGHT, true);
+ UpdateSpeed(MOVE_RUN);
+ UpdateSpeed(MOVE_SWIM);
+ UpdateSpeed(MOVE_FLIGHT);
}
- if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_MOUNTED_COMBAT))
+ if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED))
Dismount();
}
@@ -11916,7 +11454,7 @@ void Unit::ClearInCombat()
if (Unit* owner = GetOwner())
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
if (owner->GetSpeedRate(UnitMoveType(i)) > GetSpeedRate(UnitMoveType(i)))
- SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true);
+ SetSpeedRate(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)));
}
else if (!IsCharmed())
return;
@@ -11925,6 +11463,7 @@ void Unit::ClearInCombat()
ToPlayer()->UpdatePotionCooldown();
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LEAVE_COMBAT);
}
bool Unit::isTargetableForAttack(bool checkFakeDeath) const
@@ -12003,35 +11542,33 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|| target->GetReactionTo(this) > REP_NEUTRAL)
return false;
+ Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
+ Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
+
// Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
- if (GetReactionTo(target) == REP_NEUTRAL &&
- target->GetReactionTo(this) <= REP_NEUTRAL)
+ if (
+ (playerAffectingAttacker && !playerAffectingTarget) ||
+ (!playerAffectingAttacker && playerAffectingTarget)
+ )
{
- if (!(target->GetTypeId() == TYPEID_PLAYER && GetTypeId() == TYPEID_PLAYER) &&
- !(target->GetTypeId() == TYPEID_UNIT && GetTypeId() == TYPEID_UNIT))
- {
- Player const* player = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer() : ToPlayer();
- Unit const* creature = target->GetTypeId() == TYPEID_UNIT ? target : this;
+ Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
+ Unit const* creature = playerAffectingAttacker ? target : this;
- if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
- {
- if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
- if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction))
- if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
- if (!(repState->Flags & FACTION_FLAG_AT_WAR))
- return false;
+ if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
+ {
+ if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
+ if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction))
+ if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
+ if (!(repState->Flags & FACTION_FLAG_AT_WAR))
+ return false;
- }
}
}
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;
- Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : NULL;
- Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : NULL;
-
// check duel - before sanctuary checks
if (playerAffectingAttacker && playerAffectingTarget)
if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
@@ -12113,7 +11650,7 @@ 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;
// PvP case
@@ -12147,7 +11684,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;
}
@@ -12302,7 +11839,7 @@ void Unit::SetVisible(bool x)
UpdateObjectVisibility();
}
-void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
+void Unit::UpdateSpeed(UnitMoveType mtype)
{
int32 main_speed_mod = 0;
float stack_bonus = 1.0f;
@@ -12365,7 +11902,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
// Update speed for vehicle if available
if (GetTypeId() == TYPEID_PLAYER && GetVehicle())
- GetVehicleBase()->UpdateSpeed(MOVE_FLIGHT, true);
+ GetVehicleBase()->UpdateSpeed(MOVE_FLIGHT);
break;
}
default:
@@ -12447,7 +11984,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
speed = min_speed;
}
- SetSpeed(mtype, speed, forced);
+ SetSpeedRate(mtype, speed);
}
float Unit::GetSpeed(UnitMoveType mtype) const
@@ -12455,7 +11992,12 @@ float Unit::GetSpeed(UnitMoveType mtype) const
return m_speed_rate[mtype]*(IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
}
-void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
+void Unit::SetSpeed(UnitMoveType mtype, float newValue)
+{
+ SetSpeedRate(mtype, newValue / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]));
+}
+
+void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
{
if (rate < 0)
rate = 0.0f;
@@ -12468,103 +12010,67 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
propagateSpeedChange();
- WorldPacket data;
- if (!forced)
+ // Spline packets are for units controlled by AI. "Force speed change" (wrongly named opcodes) and "move set speed" packets are for units controlled by a player.
+ static Opcodes const moveTypeToOpcode[MAX_MOVE_TYPE][3] =
+ {
+ {SMSG_SPLINE_SET_WALK_SPEED, SMSG_FORCE_WALK_SPEED_CHANGE, MSG_MOVE_SET_WALK_SPEED },
+ {SMSG_SPLINE_SET_RUN_SPEED, SMSG_FORCE_RUN_SPEED_CHANGE, MSG_MOVE_SET_RUN_SPEED },
+ {SMSG_SPLINE_SET_RUN_BACK_SPEED, SMSG_FORCE_RUN_BACK_SPEED_CHANGE, MSG_MOVE_SET_RUN_BACK_SPEED },
+ {SMSG_SPLINE_SET_SWIM_SPEED, SMSG_FORCE_SWIM_SPEED_CHANGE, MSG_MOVE_SET_SWIM_SPEED },
+ {SMSG_SPLINE_SET_SWIM_BACK_SPEED, SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, MSG_MOVE_SET_SWIM_BACK_SPEED },
+ {SMSG_SPLINE_SET_TURN_RATE, SMSG_FORCE_TURN_RATE_CHANGE, MSG_MOVE_SET_TURN_RATE },
+ {SMSG_SPLINE_SET_FLIGHT_SPEED, SMSG_FORCE_FLIGHT_SPEED_CHANGE, MSG_MOVE_SET_FLIGHT_SPEED },
+ {SMSG_SPLINE_SET_FLIGHT_BACK_SPEED, SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, MSG_MOVE_SET_FLIGHT_BACK_SPEED },
+ {SMSG_SPLINE_SET_PITCH_RATE, SMSG_FORCE_PITCH_RATE_CHANGE, MSG_MOVE_SET_PITCH_RATE },
+ };
+
+ if (GetTypeId() == TYPEID_PLAYER)
{
- switch (mtype)
- {
- case MOVE_WALK:
- data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_RUN:
- data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_RUN_BACK:
- data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_SWIM:
- data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_SWIM_BACK:
- data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_TURN_RATE:
- data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_FLIGHT:
- data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_FLIGHT_BACK:
- data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_PITCH_RATE:
- data.Initialize(MSG_MOVE_SET_PITCH_RATE, 8+4+2+4+4+4+4+4+4+4);
- break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::SetSpeed: Unsupported move type (%d), data not sent to client.", mtype);
- return;
- }
+ // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
+ // and do it only for real sent packets and use run for run/mounted as client expected
+ ++ToPlayer()->m_forced_speed_changes[mtype];
+
+ if (!IsInCombat())
+ if (Pet* pet = ToPlayer()->GetPet())
+ pet->SetSpeedRate(mtype, m_speed_rate[mtype]);
+ }
+
+ if (m_movedPlayer) // 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;
+ self.Initialize(moveTypeToOpcode[mtype][1], mtype != MOVE_RUN ? 8 + 4 + 4 : 8 + 4 + 1 + 4);
+ self << GetPackGUID();
+ self << (uint32)0; // Movement counter. Unimplemented at the moment! NUM_PMOVE_EVTS = 0x39Z.
+ if (mtype == MOVE_RUN)
+ self << uint8(1); // unknown byte added in 2.1.0
+ self << float(GetSpeed(mtype));
+ m_movedPlayer->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;
+ data.Initialize(moveTypeToOpcode[mtype][2], 8 + 30 + 4);
data << GetPackGUID();
BuildMovementPacket(&data);
data << float(GetSpeed(mtype));
- SendMessageToSet(&data, true);
+ SendMessageToSet(&data, false);
}
- else
+ else // unit controlled by AI.
{
- if (GetTypeId() == TYPEID_PLAYER)
- {
- // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
- // and do it only for real sent packets and use run for run/mounted as client expected
- ++ToPlayer()->m_forced_speed_changes[mtype];
-
- if (!IsInCombat())
- if (Pet* pet = ToPlayer()->GetPet())
- pet->SetSpeed(mtype, m_speed_rate[mtype], forced);
- }
-
- switch (mtype)
- {
- case MOVE_WALK:
- data.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE, 16);
- break;
- case MOVE_RUN:
- data.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE, 17);
- break;
- case MOVE_RUN_BACK:
- data.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_SWIM:
- data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, 16);
- break;
- case MOVE_SWIM_BACK:
- data.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_TURN_RATE:
- data.Initialize(SMSG_FORCE_TURN_RATE_CHANGE, 16);
- break;
- case MOVE_FLIGHT:
- data.Initialize(SMSG_FORCE_FLIGHT_SPEED_CHANGE, 16);
- break;
- case MOVE_FLIGHT_BACK:
- data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_PITCH_RATE:
- data.Initialize(SMSG_FORCE_PITCH_RATE_CHANGE, 16);
- break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::SetSpeed: Unsupported move type (%d), data not sent to client.", mtype);
- return;
- }
+ // send notification to every clients.
+ WorldPacket data;
+ data.Initialize(moveTypeToOpcode[mtype][0], 8 + 4);
data << GetPackGUID();
- data << (uint32)0; // moveEvent, NUM_PMOVE_EVTS = 0x39
- if (mtype == MOVE_RUN)
- data << uint8(0); // new 2.1.0
data << float(GetSpeed(mtype));
- SendMessageToSet(&data, true);
+ SendMessageToSet(&data, false);
}
}
+bool Unit::IsGhouled() const
+{
+ return HasAura(SPELL_DK_RAISE_ALLY);
+}
+
void Unit::setDeathState(DeathState s)
{
// Death state needs to be updated before RemoveAllAurasOnDeath() is called, to prevent entering combat
@@ -12822,7 +12328,7 @@ Unit* Creature::SelectVictim()
if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target))
{
- if(!IsFocusing())
+ if (!IsFocusing())
SetInFront(target);
return target;
}
@@ -13735,6 +13241,9 @@ void Unit::UpdateCharmAI()
delete i_AI;
i_AI = i_disabledAI;
i_disabledAI = nullptr;
+
+ if (GetTypeId() == TYPEID_UNIT)
+ ToCreature()->AI()->OnCharmed(false);
}
}
else
@@ -13882,7 +13391,7 @@ void CharmInfo::InitPossessCreateSpells()
break;
}
- for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
{
uint32 spellId = _unit->ToCreature()->m_spells[i];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@@ -14269,6 +13778,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)
@@ -14276,6 +13786,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);
@@ -14300,6 +13814,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;
@@ -14353,9 +13871,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;
+ Milliseconds cooldown = Milliseconds::zero();
if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown)
- cooldown = i->spellProcEvent->cooldown;
+ cooldown = Seconds(i->spellProcEvent->cooldown);
// Note: must SetCantProc(false) before return
if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC))
@@ -14364,9 +13882,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;
}
@@ -14374,7 +13892,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);
@@ -14391,9 +13909,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;
}
@@ -14410,7 +13929,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;
@@ -14419,20 +13939,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;
@@ -14441,7 +13963,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;
@@ -14449,9 +13971,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;
}
@@ -14536,6 +14059,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)
{
@@ -14564,6 +14090,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)
{
@@ -14571,9 +14099,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);
}
}
@@ -14583,9 +14111,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);
}
}
@@ -14718,7 +14246,7 @@ void Unit::SendMovementFlagUpdate(bool self /* = false */)
bool Unit::IsSitState() const
{
- uint8 s = getStandState();
+ uint8 s = GetStandState();
return
s == UNIT_STAND_STATE_SIT_CHAIR || s == UNIT_STAND_STATE_SIT_LOW_CHAIR ||
s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR ||
@@ -14727,7 +14255,7 @@ bool Unit::IsSitState() const
bool Unit::IsStandState() const
{
- uint8 s = getStandState();
+ uint8 s = GetStandState();
return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL;
}
@@ -14764,7 +14292,7 @@ void Unit::SetDisplayId(uint32 modelId)
SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
// Set Gender by modelId
if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
void Unit::RestoreDisplayId()
@@ -15207,23 +14735,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;
@@ -15231,12 +14759,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)
@@ -15254,15 +14782,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)
@@ -15275,19 +14803,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;
@@ -15297,19 +14832,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);
}
@@ -15620,12 +15154,19 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
Player* creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
/// @todo do instance binding anyway if the charmer/owner is offline
- if (instanceMap->IsDungeon() && creditedPlayer)
+ if (instanceMap->IsDungeon() && (creditedPlayer || this == victim))
{
if (instanceMap->IsRaidOrHeroicDungeon())
{
if (creature->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
- ((InstanceMap*)instanceMap)->PermBindAllPlayers(creditedPlayer);
+ {
+ // if the boss killed itself we still need to bind players to the instance
+ if (!creditedPlayer && instanceMap->HavePlayers())
+ creditedPlayer = instanceMap->GetPlayers().getFirst()->GetSource();
+
+ if (creditedPlayer)
+ ((InstanceMap*)instanceMap)->PermBindAllPlayers(creditedPlayer);
+ }
}
else
{
@@ -15633,7 +15174,8 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
// until the players leave the instance
time_t resettime = creature->GetRespawnTimeEx() + 2 * HOUR;
if (InstanceSave* save = sInstanceSaveMgr->GetInstanceSave(creature->GetInstanceId()))
- if (save->GetResetTime() < resettime) save->SetResetTime(resettime);
+ if (save->GetResetTime() < resettime)
+ save->SetResetTime(resettime);
}
}
}
@@ -16075,7 +15617,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
if (cinfo && cinfo->type == CREATURE_TYPE_DEMON)
{
// to prevent client crash
- SetByteValue(UNIT_FIELD_BYTES_0, 1, (uint8)CLASS_MAGE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, (uint8)CLASS_MAGE);
// just to enable stat window
if (GetCharmInfo())
@@ -16177,7 +15719,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate();
if (cinfo && cinfo->type == CREATURE_TYPE_DEMON)
{
- SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
if (GetCharmInfo())
GetCharmInfo()->SetPetNumber(0, true);
else
@@ -16307,8 +15849,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;
@@ -16326,8 +15868,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;
@@ -16695,7 +16237,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
- uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
+ uint8 hairColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
switch (hairColor)
{
case 7: // Violet
@@ -16716,7 +16258,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
- uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 skinColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
// Male
if (getGender() == GENDER_MALE)
{
@@ -16775,7 +16317,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
- uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
+ uint8 hairColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
switch (hairColor)
{
case 0: // Green
@@ -16795,7 +16337,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
- uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 skinColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
// Male
if (getGender() == GENDER_MALE)
{
@@ -17672,7 +17214,7 @@ void Unit::SetFacingToObject(WorldObject const* object)
/// @todo figure out under what conditions creature will move towards object instead of facing it where it currently is.
Movement::MoveSplineInit init(this);
- init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
+ init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset(), false);
init.SetFacing(GetAngle(object)); // when on transport, GetAngle will still return global coordinates (and angle) that needs transforming
init.Launch();
}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 0c4ee979748..30ef6eb394f 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -76,6 +76,7 @@ enum SpellAuraInterruptFlags
AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat
AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000, // 24 removed by any direct damage
AURA_INTERRUPT_FLAG_LANDING = 0x02000000, // 25 removed by hitting the ground
+ AURA_INTERRUPT_FLAG_LEAVE_COMBAT = 0x80000000, // 31 removed by leaving combat
AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE)
};
@@ -190,6 +191,30 @@ enum UnitStandFlags
UNIT_STAND_FLAGS_ALL = 0xFF
};
+enum UnitBytes0Offsets
+{
+ UNIT_BYTES_0_OFFSET_RACE = 0,
+ UNIT_BYTES_0_OFFSET_CLASS = 1,
+ UNIT_BYTES_0_OFFSET_GENDER = 2,
+ UNIT_BYTES_0_OFFSET_POWER_TYPE = 3,
+};
+
+enum UnitBytes1Offsets
+{
+ UNIT_BYTES_1_OFFSET_STAND_STATE = 0,
+ UNIT_BYTES_1_OFFSET_PET_TALENTS = 1,
+ UNIT_BYTES_1_OFFSET_VIS_FLAG = 2,
+ UNIT_BYTES_1_OFFSET_ANIM_TIER = 3
+};
+
+enum UnitBytes2Offsets
+{
+ UNIT_BYTES_2_OFFSET_SHEATH_STATE = 0,
+ UNIT_BYTES_2_OFFSET_PVP_FLAG = 1,
+ UNIT_BYTES_2_OFFSET_PET_FLAG = 2,
+ UNIT_BYTES_2_OFFSET_SHAPESHIFT = 3,
+};
+
// byte flags value (UNIT_FIELD_BYTES_1, 3)
enum UnitBytes1_Flags
{
@@ -533,10 +558,10 @@ enum UnitMoveType
#define MAX_MOVE_TYPE 9
-extern float baseMoveSpeed[MAX_MOVE_TYPE];
-extern float playerBaseMoveSpeed[MAX_MOVE_TYPE];
+TC_GAME_API extern float baseMoveSpeed[MAX_MOVE_TYPE];
+TC_GAME_API extern float playerBaseMoveSpeed[MAX_MOVE_TYPE];
-enum WeaponAttackType
+enum WeaponAttackType : uint8
{
BASE_ATTACK = 0,
OFF_ATTACK = 1,
@@ -826,7 +851,7 @@ struct CleanDamage
struct CalcDamageInfo;
-class DamageInfo
+class TC_GAME_API DamageInfo
{
private:
Unit* const m_attacker;
@@ -889,7 +914,7 @@ public:
SpellSchoolMask GetSchoolMask() const { return _schoolMask; };
};
-class ProcEventInfo
+class TC_GAME_API ProcEventInfo
{
public:
ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask,
@@ -947,7 +972,7 @@ struct CalcDamageInfo
};
// Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode
-struct SpellNonMeleeDamage
+struct TC_GAME_API SpellNonMeleeDamage
{
SpellNonMeleeDamage(Unit* _attacker, Unit* _target, uint32 _SpellID, uint32 _schoolMask)
: target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), overkill(0), schoolMask(_schoolMask),
@@ -1043,7 +1068,7 @@ enum ReactStates
REACT_AGGRESSIVE = 2
};
-enum CommandStates
+enum CommandStates : uint8
{
COMMAND_STAY = 0,
COMMAND_FOLLOW = 1,
@@ -1108,7 +1133,7 @@ enum ActionBarIndex
#define MAX_UNIT_ACTION_BAR_INDEX (ACTION_BAR_INDEX_END-ACTION_BAR_INDEX_START)
-struct CharmInfo
+struct TC_GAME_API CharmInfo
{
public:
explicit CharmInfo(Unit* unit);
@@ -1210,7 +1235,7 @@ enum PlayerTotemType
struct SpellProcEventEntry; // used only privately
-class Unit : public WorldObject
+class TC_GAME_API Unit : public WorldObject
{
public:
typedef std::set<Unit*> AttackerSet;
@@ -1276,7 +1301,7 @@ class Unit : public WorldObject
void _removeAttacker(Unit* pAttacker); // must be called only from Unit::AttackStop()
Unit* getAttackerForHelper() const; // If someone wants to help, who to give them
bool Attack(Unit* victim, bool meleeAttack);
- void MustReacquireTarget() { m_shouldReacquireTarget = true; } // flags the Unit for forced target reacquisition in the next ::Attack call
+ void MustReacquireTarget() { m_shouldReacquireTarget = true; } // flags the Unit for forced (client displayed) target reacquisition in the next ::Attack call
void CastStop(uint32 except_spellid = 0);
bool AttackStop();
void RemoveAllAttackers();
@@ -1314,11 +1339,11 @@ class Unit : public WorldObject
uint8 getLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); }
uint8 getLevelForTarget(WorldObject const* /*target*/) const override { return getLevel(); }
void SetLevel(uint8 lvl);
- uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); }
+ uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE); }
uint32 getRaceMask() const { return 1 << (getRace()-1); }
- uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
+ uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS); }
uint32 getClassMask() const { return 1 << (getClass()-1); }
- uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
+ uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER); }
float GetStat(Stats stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0+stat)); }
void SetStat(Stats stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0+stat, val); }
@@ -1347,7 +1372,7 @@ class Unit : public WorldObject
int32 ModifyHealth(int32 val);
int32 GetHealthGain(int32 dVal);
- Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); }
+ Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE)); }
void setPowerType(Powers power);
uint32 GetPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_POWER1 +power); }
uint32 GetMaxPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_MAXPOWER1+power); }
@@ -1388,7 +1413,7 @@ class Unit : public WorldObject
uint32 GetCreatureType() const;
uint32 GetCreatureTypeMask() const;
- uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
+ uint8 GetStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
bool IsSitState() const;
bool IsStandState() const;
void SetStandState(uint8 state);
@@ -1602,7 +1627,7 @@ class Unit : public WorldObject
bool IsAlive() const { return (m_deathState == ALIVE); }
bool isDying() const { return (m_deathState == JUST_DIED); }
bool isDead() const { return (m_deathState == DEAD || m_deathState == CORPSE); }
- bool IsGhouled() const { return HasAura(46619 /*SPELL_DK_RAISE_ALLY*/); }
+ bool IsGhouled() const;
DeathState getDeathState() const { return m_deathState; }
virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet
@@ -1731,6 +1756,7 @@ class Unit : public WorldObject
void RemoveAreaAurasDueToLeaveWorld();
void RemoveAllAuras();
void RemoveArenaAuras();
+ void RemoveAurasOnEvade();
void RemoveAllAurasOnDeath();
void RemoveAllAurasRequiringDeadTarget();
void RemoveAllAurasExceptType(AuraType type);
@@ -1832,6 +1858,9 @@ class 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; }
@@ -1994,10 +2023,11 @@ class Unit : public WorldObject
void CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32* absorb, uint32* resist, SpellInfo const* spellInfo = NULL);
void CalcHealAbsorb(Unit* victim, SpellInfo const* spellInfo, uint32& healAmount, uint32& absorb);
- void UpdateSpeed(UnitMoveType mtype, bool forced);
+ void UpdateSpeed(UnitMoveType mtype);
float GetSpeed(UnitMoveType mtype) const;
float GetSpeedRate(UnitMoveType mtype) const { return m_speed_rate[mtype]; }
- void SetSpeed(UnitMoveType mtype, float rate, bool forced = false);
+ void SetSpeed(UnitMoveType mtype, float newValue);
+ void SetSpeedRate(UnitMoveType mtype, float rate);
float ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const;
int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints = NULL) const;
@@ -2141,11 +2171,11 @@ class Unit : public WorldObject
virtual void Yell(std::string const& text, Language language, WorldObject const* target = nullptr);
virtual void TextEmote(std::string const& text, WorldObject const* target = nullptr, bool isBossEmote = false);
virtual void Whisper(std::string const& text, Language language, Player* target, bool isBossWhisper = false);
- void Talk(uint32 textId, ChatMsg msgType, float textRange, WorldObject const* target);
- void Say(uint32 textId, WorldObject const* target = nullptr);
- void Yell(uint32 textId, WorldObject const* target = nullptr);
- void TextEmote(uint32 textId, WorldObject const* target = nullptr, bool isBossEmote = false);
- void Whisper(uint32 textId, Player* target, bool isBossWhisper = false);
+ virtual void Talk(uint32 textId, ChatMsg msgType, float textRange, WorldObject const* target);
+ virtual void Say(uint32 textId, WorldObject const* target = nullptr);
+ virtual void Yell(uint32 textId, WorldObject const* target = nullptr);
+ virtual void TextEmote(uint32 textId, WorldObject const* target = nullptr, bool isBossEmote = false);
+ virtual void Whisper(uint32 textId, Player* target, bool isBossWhisper = false);
protected:
explicit Unit (bool isWorldObject);
@@ -2225,11 +2255,12 @@ class 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.h b/src/server/game/Entities/Vehicle/Vehicle.h
index cd28c4082d4..f8992708a46 100644
--- a/src/server/game/Entities/Vehicle/Vehicle.h
+++ b/src/server/game/Entities/Vehicle/Vehicle.h
@@ -29,7 +29,7 @@ struct VehicleEntry;
class Unit;
class VehicleJoinEvent;
-class Vehicle : public TransportBase
+class TC_GAME_API Vehicle : public TransportBase
{
protected:
friend bool Unit::CreateVehicleKit(uint32 id, uint32 creatureEntry);
@@ -118,7 +118,7 @@ class Vehicle : public TransportBase
PendingJoinEventContainer _pendingJoinEvents; ///< Collection of delayed join events for prospective passengers
};
-class VehicleJoinEvent : public BasicEvent
+class TC_GAME_API VehicleJoinEvent : public BasicEvent
{
friend class Vehicle;
protected:
diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h
index d2982a402f2..44ed2fb1332 100644
--- a/src/server/game/Events/GameEventMgr.h
+++ b/src/server/game/Events/GameEventMgr.h
@@ -93,7 +93,7 @@ class Player;
class Creature;
class Quest;
-class GameEventMgr
+class TC_GAME_API GameEventMgr
{
private:
GameEventMgr();
@@ -182,8 +182,8 @@ class GameEventMgr
#define sGameEventMgr GameEventMgr::instance()
-bool IsHolidayActive(HolidayIds id);
-bool IsEventActive(uint16 event_id);
+TC_GAME_API bool IsHolidayActive(HolidayIds id);
+TC_GAME_API bool IsEventActive(uint16 event_id);
#endif
diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp
index 762c9b9c28e..247c4943113 100644
--- a/src/server/game/Globals/ObjectAccessor.cpp
+++ b/src/server/game/Globals/ObjectAccessor.cpp
@@ -34,6 +34,53 @@
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/locks.hpp>
+template<class T>
+void HashMapHolder<T>::Insert(T* o)
+{
+ boost::unique_lock<boost::shared_mutex> lock(*GetLock());
+
+ GetContainer()[o->GetGUID()] = o;
+}
+
+template<class T>
+void HashMapHolder<T>::Remove(T* o)
+{
+ boost::unique_lock<boost::shared_mutex> lock(*GetLock());
+
+ GetContainer().erase(o->GetGUID());
+}
+
+template<class T>
+T* HashMapHolder<T>::Find(ObjectGuid guid)
+{
+ boost::shared_lock<boost::shared_mutex> lock(*GetLock());
+
+ typename MapType::iterator itr = GetContainer().find(guid);
+ return (itr != GetContainer().end()) ? itr->second : NULL;
+}
+
+template<class T>
+auto HashMapHolder<T>::GetContainer() -> MapType&
+{
+ static MapType _objectMap;
+ return _objectMap;
+}
+
+template<class T>
+boost::shared_mutex* HashMapHolder<T>::GetLock()
+{
+ static boost::shared_mutex _lock;
+ return &_lock;
+}
+
+HashMapHolder<Player>::MapType const& ObjectAccessor::GetPlayers()
+{
+ return HashMapHolder<Player>::GetContainer();
+}
+
+template class TC_GAME_API HashMapHolder<Player>;
+template class TC_GAME_API HashMapHolder<Transport>;
+
WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, ObjectGuid const& guid)
{
switch (guid.GetHigh())
@@ -206,11 +253,6 @@ Player* ObjectAccessor::FindConnectedPlayerByName(std::string const& name)
return NULL;
}
-HashMapHolder<Player>::MapType const& ObjectAccessor::GetPlayers()
-{
- return HashMapHolder<Player>::GetContainer();
-}
-
void ObjectAccessor::SaveAllPlayers()
{
boost::shared_lock<boost::shared_mutex> lock(*HashMapHolder<Player>::GetLock());
@@ -219,14 +261,3 @@ void ObjectAccessor::SaveAllPlayers()
for (HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
itr->second->SaveToDB();
}
-
-/// Define the static members of HashMapHolder
-
-template <class T> typename HashMapHolder<T>::MapType HashMapHolder<T>::_objectMap;
-template <class T> boost::shared_mutex HashMapHolder<T>::_lock;
-
-/// Global definitions for the hashmap storage
-
-template class HashMapHolder<Player>;
-
-template class HashMapHolder<Transport>;
diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h
index d8466dbb1bc..be7066c84a9 100644
--- a/src/server/game/Globals/ObjectAccessor.h
+++ b/src/server/game/Globals/ObjectAccessor.h
@@ -42,76 +42,56 @@ class WorldRunnable;
class Transport;
template <class T>
-class HashMapHolder
+class TC_GAME_API HashMapHolder
{
- public:
- static_assert(std::is_same<Player, T>::value
- || std::is_same<Transport, T>::value,
- "Only Player and Transport can be registered in global HashMapHolder");
+ //Non instanceable only static
+ HashMapHolder() { }
- typedef std::unordered_map<ObjectGuid, T*> MapType;
+public:
+ static_assert(std::is_same<Player, T>::value
+ || std::is_same<Transport, T>::value,
+ "Only Player and Transport can be registered in global HashMapHolder");
- static void Insert(T* o)
- {
- boost::unique_lock<boost::shared_mutex> lock(_lock);
+ typedef std::unordered_map<ObjectGuid, T*> MapType;
- _objectMap[o->GetGUID()] = o;
- }
+ static void Insert(T* o);
- static void Remove(T* o)
- {
- boost::unique_lock<boost::shared_mutex> lock(_lock);
+ static void Remove(T* o);
- _objectMap.erase(o->GetGUID());
- }
+ static T* Find(ObjectGuid guid);
- static T* Find(ObjectGuid guid)
- {
- boost::shared_lock<boost::shared_mutex> lock(_lock);
+ static MapType& GetContainer();
- typename MapType::iterator itr = _objectMap.find(guid);
- return (itr != _objectMap.end()) ? itr->second : NULL;
- }
-
- static MapType& GetContainer() { return _objectMap; }
-
- static boost::shared_mutex* GetLock() { return &_lock; }
-
- private:
- //Non instanceable only static
- HashMapHolder() { }
-
- static boost::shared_mutex _lock;
- static MapType _objectMap;
+ static boost::shared_mutex* GetLock();
};
namespace ObjectAccessor
{
// these functions return objects only if in map of specified object
- WorldObject* GetWorldObject(WorldObject const&, ObjectGuid const&);
- Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask);
- Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid);
- GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid);
- Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid);
- DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid);
- Unit* GetUnit(WorldObject const&, ObjectGuid const& guid);
- Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid);
- Pet* GetPet(WorldObject const&, ObjectGuid const& guid);
- Player* GetPlayer(Map const*, ObjectGuid const& guid);
- Player* GetPlayer(WorldObject const&, ObjectGuid const& guid);
- Creature* GetCreatureOrPetOrVehicle(WorldObject const&, ObjectGuid const&);
+ TC_GAME_API WorldObject* GetWorldObject(WorldObject const&, ObjectGuid const&);
+ TC_GAME_API Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask);
+ TC_GAME_API Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API Unit* GetUnit(WorldObject const&, ObjectGuid const& guid);
+ TC_GAME_API Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API Pet* GetPet(WorldObject const&, ObjectGuid const& guid);
+ TC_GAME_API Player* GetPlayer(Map const*, ObjectGuid const& guid);
+ TC_GAME_API Player* GetPlayer(WorldObject const&, ObjectGuid const& guid);
+ TC_GAME_API Creature* GetCreatureOrPetOrVehicle(WorldObject const&, ObjectGuid const&);
// these functions return objects if found in whole world
// ACCESS LIKE THAT IS NOT THREAD SAFE
- Player* FindPlayer(ObjectGuid const&);
- Player* FindPlayerByName(std::string const& name);
+ TC_GAME_API Player* FindPlayer(ObjectGuid const&);
+ TC_GAME_API Player* FindPlayerByName(std::string const& name);
// this returns Player even if he is not in world, for example teleporting
- Player* FindConnectedPlayer(ObjectGuid const&);
- Player* FindConnectedPlayerByName(std::string const& name);
+ TC_GAME_API Player* FindConnectedPlayer(ObjectGuid const&);
+ TC_GAME_API Player* FindConnectedPlayerByName(std::string const& name);
// when using this, you must use the hashmapholder's lock
- HashMapHolder<Player>::MapType const& GetPlayers();
+ TC_GAME_API HashMapHolder<Player>::MapType const& GetPlayers();
template<class T>
void AddObject(T* object)
@@ -125,7 +105,8 @@ namespace ObjectAccessor
HashMapHolder<T>::Remove(object);
}
- void SaveAllPlayers();
+ TC_GAME_API void SaveAllPlayers();
};
#endif
+
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index fc7d668ae0b..f26a3cb4721 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -487,7 +487,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
creatureTemplate.resistance[i] = fields[42 + i - 1].GetInt16();
- for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
creatureTemplate.spells[i] = fields[48 + i].GetUInt32();
creatureTemplate.PetSpellDataId = fields[56].GetUInt32();
@@ -833,7 +833,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
if (!displayScaleEntry)
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);
- for (int k = 0; k < MAX_KILL_CREDIT; ++k)
+ for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
{
if (cInfo->KillCredit[k])
{
@@ -920,7 +920,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing PetSpellDataId (%u).", cInfo->Entry, cInfo->PetSpellDataId);
}
- for (uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j)
+ for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j)
{
if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j]))
{
@@ -4715,7 +4715,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
if (tableName.empty())
return;
- if (sScriptMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
+ if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
return;
TC_LOG_INFO("server.loading", "Loading %s...", tableName.c_str());
@@ -5162,7 +5162,7 @@ void ObjectMgr::LoadSpellScriptNames()
while (spellInfo)
{
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
spellInfo = spellInfo->GetNextRankSpell();
}
}
@@ -5171,7 +5171,7 @@ void ObjectMgr::LoadSpellScriptNames()
if (spellInfo->IsRanked())
TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName.c_str(), spellId);
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
}
++count;
@@ -5193,45 +5193,59 @@ void ObjectMgr::ValidateSpellScripts()
uint32 count = 0;
- for (SpellScriptsContainer::iterator itr = _spellScriptsStore.begin(); itr != _spellScriptsStore.end();)
+ for (auto spell : _spellScriptsStore)
{
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->first);
- std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> > SpellScriptLoaders;
- sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
- itr = _spellScriptsStore.upper_bound(itr->first);
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell.first);
- for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
+ auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
+
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScript* spellScript = sitr->first->GetSpellScript();
- AuraScript* auraScript = sitr->first->GetAuraScript();
- bool valid = true;
- if (!spellScript && !auraScript)
- {
- TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second).c_str());
- valid = false;
- }
- if (spellScript)
- {
- spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- spellScript->_Register();
- if (!spellScript->_Validate(spellEntry))
- valid = false;
- delete spellScript;
- }
- if (auraScript)
+ if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
{
- auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- auraScript->_Register();
- if (!auraScript->_Validate(spellEntry))
- valid = false;
- delete auraScript;
- }
- if (!valid)
- {
- _spellScriptsStore.erase(sitr->second);
+ ++count;
+
+ std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
+ std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
+
+ if (!spellScript && !auraScript)
+ {
+ TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(itr->second.first).c_str());
+
+ itr->second.second = false;
+ continue;
+ }
+
+ if (spellScript)
+ {
+ spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ spellScript->_Register();
+
+ if (!spellScript->_Validate(spellEntry))
+ {
+ itr->second.second = false;
+ continue;
+ }
+ }
+
+ if (auraScript)
+ {
+ auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ auraScript->_Register();
+
+ if (!auraScript->_Validate(spellEntry))
+ {
+ itr->second.second = false;
+ continue;
+ }
+ }
+
+ // Enable the script when all checks passed
+ itr->second.second = true;
}
+ else
+ itr->second.second = false;
}
- ++count;
}
TC_LOG_INFO("server.loading", ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
@@ -5932,14 +5946,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;
}
@@ -5958,31 +5972,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));
@@ -6031,7 +6045,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);
}
@@ -6057,7 +6071,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;
}
@@ -6172,7 +6186,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;
}
@@ -8571,6 +8585,8 @@ void ObjectMgr::LoadScriptNames()
{
uint32 oldMSTime = getMSTime();
+ // We insert an empty placeholder here so we can use the
+ // script id 0 as dummy for "no script found".
_scriptNamesStore.emplace_back("");
QueryResult result = WorldDatabase.Query(
@@ -8612,18 +8628,18 @@ void ObjectMgr::LoadScriptNames()
std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
-#ifdef SCRIPTS
- for (size_t i = 1; i < _scriptNamesStore.size(); ++i)
- UnusedScriptNames.push_back(_scriptNamesStore[i]);
-#endif
-
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
+ObjectMgr::ScriptNameContainer const& ObjectMgr::GetAllScriptNames() const
+{
+ return _scriptNamesStore;
+}
+
std::string const& ObjectMgr::GetScriptName(uint32 id) const
{
static std::string const empty = "";
- return id < _scriptNamesStore.size() ? _scriptNamesStore[id] : empty;
+ return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 3d6ae293edb..576a8d2ccac 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -370,17 +370,17 @@ struct ScriptInfo
typedef std::multimap<uint32, ScriptInfo> ScriptMap;
typedef std::map<uint32, ScriptMap > ScriptMapMap;
-typedef std::multimap<uint32, uint32> SpellScriptsContainer;
+typedef std::multimap<uint32 /*spell id*/, std::pair<uint32 /*script id*/, bool /*enabled*/>> SpellScriptsContainer;
typedef std::pair<SpellScriptsContainer::iterator, SpellScriptsContainer::iterator> SpellScriptsBounds;
-extern ScriptMapMap sSpellScripts;
-extern ScriptMapMap sEventScripts;
-extern ScriptMapMap sWaypointScripts;
+TC_GAME_API extern ScriptMapMap sSpellScripts;
+TC_GAME_API extern ScriptMapMap sEventScripts;
+TC_GAME_API extern ScriptMapMap sWaypointScripts;
std::string GetScriptsTableNameByType(ScriptsType type);
ScriptMapMap* GetScriptsMapByType(ScriptsType type);
std::string GetScriptCommandName(ScriptCommands command);
-struct SpellClickInfo
+struct TC_GAME_API SpellClickInfo
{
uint32 spellId;
uint8 castFlags;
@@ -637,7 +637,7 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);
#define MAX_PET_NAME 12 // max allowed by client name length
#define MAX_CHARTER_NAME 24 // max allowed by client name length
-bool normalizePlayerName(std::string& name);
+TC_GAME_API bool normalizePlayerName(std::string& name);
struct LanguageDesc
{
@@ -646,7 +646,7 @@ struct LanguageDesc
uint32 skill_id;
};
-extern LanguageDesc lang_description[LANGUAGES_COUNT];
+TC_GAME_API extern LanguageDesc lang_description[LANGUAGES_COUNT];
LanguageDesc const* GetLanguageDescByID(uint32 lang);
enum EncounterCreditType
@@ -671,7 +671,7 @@ typedef std::unordered_map<uint32, DungeonEncounterList> DungeonEncounterContain
class PlayerDumpReader;
-class ObjectMgr
+class TC_GAME_API ObjectMgr
{
friend class PlayerDumpReader;
@@ -1272,6 +1272,7 @@ class ObjectMgr
bool IsVendorItemValid(uint32 vendor_entry, uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* player = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0) const;
void LoadScriptNames();
+ ScriptNameContainer const& GetAllScriptNames() const;
std::string const& GetScriptName(uint32 id) const;
uint32 GetScriptId(std::string const& name);
diff --git a/src/server/game/Grids/GridStates.h b/src/server/game/Grids/GridStates.h
index 9420bef4b9d..b567da43b69 100644
--- a/src/server/game/Grids/GridStates.h
+++ b/src/server/game/Grids/GridStates.h
@@ -24,32 +24,32 @@
class Map;
-class GridState
+class TC_GAME_API GridState
{
public:
virtual ~GridState() { };
virtual void Update(Map &, NGridType&, GridInfo &, uint32 t_diff) const = 0;
};
-class InvalidState : public GridState
+class TC_GAME_API InvalidState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
-class ActiveState : public GridState
+class TC_GAME_API ActiveState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
-class IdleState : public GridState
+class TC_GAME_API IdleState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
};
-class RemovalState : public GridState
+class TC_GAME_API RemovalState : public GridState
{
public:
void Update(Map &, NGridType &, GridInfo &, uint32 t_diff) const override;
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 6edeadf6b94..1d2b0bd33cf 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -38,7 +38,7 @@ class Player;
namespace Trinity
{
- struct VisibleNotifier
+ struct TC_GAME_API VisibleNotifier
{
Player &i_player;
UpdateData i_data;
@@ -61,7 +61,7 @@ namespace Trinity
void Visit(DynamicObjectMapType &);
};
- struct PlayerRelocationNotifier : public VisibleNotifier
+ struct TC_GAME_API PlayerRelocationNotifier : public VisibleNotifier
{
PlayerRelocationNotifier(Player &player) : VisibleNotifier(player) { }
@@ -70,7 +70,7 @@ namespace Trinity
void Visit(PlayerMapType &);
};
- struct CreatureRelocationNotifier
+ struct TC_GAME_API CreatureRelocationNotifier
{
Creature &i_creature;
CreatureRelocationNotifier(Creature &c) : i_creature(c) { }
@@ -79,7 +79,7 @@ namespace Trinity
void Visit(PlayerMapType &);
};
- struct DelayedUnitRelocation
+ struct TC_GAME_API DelayedUnitRelocation
{
Map &i_map;
Cell &cell;
@@ -92,7 +92,7 @@ namespace Trinity
void Visit(PlayerMapType &);
};
- struct AIRelocationNotifier
+ struct TC_GAME_API AIRelocationNotifier
{
Unit &i_unit;
bool isCreature;
@@ -120,7 +120,7 @@ namespace Trinity
void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); }
};
- struct MessageDistDeliverer
+ struct TC_GAME_API MessageDistDeliverer
{
WorldObject* i_source;
WorldPacket* i_message;
@@ -566,7 +566,7 @@ namespace Trinity
// WorldObject check classes
- class AnyDeadUnitObjectInRangeCheck
+ class TC_GAME_API AnyDeadUnitObjectInRangeCheck
{
public:
AnyDeadUnitObjectInRangeCheck(Unit* searchObj, float range) : i_searchObj(searchObj), i_range(range) { }
@@ -579,7 +579,7 @@ namespace Trinity
float i_range;
};
- class AnyDeadUnitSpellTargetInRangeCheck : public AnyDeadUnitObjectInRangeCheck
+ class TC_GAME_API AnyDeadUnitSpellTargetInRangeCheck : public AnyDeadUnitObjectInRangeCheck
{
public:
AnyDeadUnitSpellTargetInRangeCheck(Unit* searchObj, float range, SpellInfo const* spellInfo, SpellTargetCheckTypes check)
@@ -1237,7 +1237,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 +1254,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/Grids/ObjectGridLoader.h b/src/server/game/Grids/ObjectGridLoader.h
index 9dc4c7e048b..f2893da609e 100644
--- a/src/server/game/Grids/ObjectGridLoader.h
+++ b/src/server/game/Grids/ObjectGridLoader.h
@@ -27,7 +27,7 @@
class ObjectWorldLoader;
-class ObjectGridLoader
+class TC_GAME_API ObjectGridLoader
{
friend class ObjectWorldLoader;
@@ -55,7 +55,7 @@ class ObjectGridLoader
};
//Stop the creatures before unloading the NGrid
-class ObjectGridStoper
+class TC_GAME_API ObjectGridStoper
{
public:
void Visit(CreatureMapType &m);
@@ -63,7 +63,7 @@ class ObjectGridStoper
};
//Move the foreign creatures back to respawn positions before unloading the NGrid
-class ObjectGridEvacuator
+class TC_GAME_API ObjectGridEvacuator
{
public:
void Visit(CreatureMapType &m);
diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h
index 42fd9b582e8..18b8119246f 100644
--- a/src/server/game/Groups/Group.h
+++ b/src/server/game/Groups/Group.h
@@ -158,7 +158,7 @@ struct InstanceGroupBind
/** request member stats checken **/
/// @todo uninvite people that not accepted invite
-class Group
+class TC_GAME_API Group
{
public:
struct MemberSlot
diff --git a/src/server/game/Groups/GroupMgr.h b/src/server/game/Groups/GroupMgr.h
index ee6ae18f3ea..14ff3519aa6 100644
--- a/src/server/game/Groups/GroupMgr.h
+++ b/src/server/game/Groups/GroupMgr.h
@@ -20,7 +20,7 @@
#include "Group.h"
-class GroupMgr
+class TC_GAME_API GroupMgr
{
private:
GroupMgr();
diff --git a/src/server/game/Groups/GroupReference.h b/src/server/game/Groups/GroupReference.h
index 4718dda1832..6e7373de7d7 100644
--- a/src/server/game/Groups/GroupReference.h
+++ b/src/server/game/Groups/GroupReference.h
@@ -24,7 +24,7 @@
class Group;
class Player;
-class GroupReference : public Reference<Group, Player>
+class TC_GAME_API GroupReference : public Reference<Group, Player>
{
protected:
uint8 iSubGroup;
diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h
index 2bc57a70671..e25a3201957 100644
--- a/src/server/game/Guilds/Guild.h
+++ b/src/server/game/Guilds/Guild.h
@@ -223,7 +223,7 @@ enum GuildMemberFlags
};
// Emblem info
-class EmblemInfo
+class TC_GAME_API EmblemInfo
{
public:
EmblemInfo() : m_style(0), m_color(0), m_borderStyle(0), m_borderColor(0), m_backgroundColor(0) { }
@@ -279,7 +279,7 @@ typedef std::vector <GuildBankRightsAndSlots> GuildBankRightsAndSlotsVec;
typedef std::set <uint8> SlotIds;
-class Guild
+class TC_GAME_API Guild
{
private:
// Class representing guild member
diff --git a/src/server/game/Guilds/GuildMgr.h b/src/server/game/Guilds/GuildMgr.h
index d8662f50f43..ad2555c2e71 100644
--- a/src/server/game/Guilds/GuildMgr.h
+++ b/src/server/game/Guilds/GuildMgr.h
@@ -20,7 +20,7 @@
#include "Guild.h"
-class GuildMgr
+class TC_GAME_API GuildMgr
{
private:
GuildMgr();
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 2b4b77dc7a9..19638ec1bf8 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -642,7 +642,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte
TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), createInfo->Name.c_str(), newChar.GetGUID().GetCounter());
sScriptMgr->OnPlayerCreate(&newChar);
- sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, 0), newChar.getRace(), newChar.getClass(), newChar.getLevel());
+ sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER), newChar.getRace(), newChar.getClass(), newChar.getLevel());
newChar.CleanupsBeforeDelete();
delete createInfo;
@@ -1242,20 +1242,25 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair);
- if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
+ if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER))
return;
BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair);
- if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
+ if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER))
return;
BarberShopStyleEntry const* bs_skinColor = sBarberShopStyleStore.LookupEntry(SkinColor);
- if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, 0)))
+ if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)))
return;
- if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, 0), bs_hair->hair_id, Color, _player->GetByteValue(PLAYER_BYTES, 1), bs_facialHair->hair_id, bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, 0)))
+ if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER),
+ bs_hair->hair_id,
+ Color,
+ _player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID),
+ bs_facialHair->hair_id,
+ bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID)))
return;
GameObject* go = _player->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_BARBER_CHAIR, 5.0f);
@@ -1265,7 +1270,7 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
return;
}
- if (_player->getStandState() != UNIT_STAND_STATE_SIT_LOW_CHAIR + go->GetGOInfo()->barberChair.chairheight)
+ if (_player->GetStandState() != UNIT_STAND_STATE_SIT_LOW_CHAIR + go->GetGOInfo()->barberChair.chairheight)
{
SendBarberShopResult(BARBER_SHOP_RESULT_NOT_ON_CHAIR);
return;
@@ -1287,11 +1292,11 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
_player->ModifyMoney(-int32(cost)); // it isn't free
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, cost);
- _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id));
- _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color));
- _player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, uint8(bs_hair->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, uint8(Color));
+ _player->SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, uint8(bs_facialHair->hair_id));
if (bs_skinColor)
- _player->SetByteValue(PLAYER_BYTES, 0, uint8(bs_skinColor->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, uint8(bs_skinColor->hair_id));
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1);
diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp
index 84cbb86345d..1c1982600ca 100644
--- a/src/server/game/Handlers/GroupHandler.cpp
+++ b/src/server/game/Handlers/GroupHandler.cpp
@@ -488,9 +488,7 @@ void WorldSession::HandleMinimapPingOpcode(WorldPacket& recvData)
void WorldSession::HandleRandomRollOpcode(WorldPacket& recvData)
{
- TC_LOG_DEBUG("network", "WORLD: Received MSG_RANDOM_ROLL");
-
- uint32 minimum, maximum, roll;
+ uint32 minimum, maximum;
recvData >> minimum;
recvData >> maximum;
@@ -499,20 +497,7 @@ void WorldSession::HandleRandomRollOpcode(WorldPacket& recvData)
return;
/********************/
- // everything's fine, do it
- roll = urand(minimum, maximum);
-
- //TC_LOG_DEBUG("ROLL: MIN: %u, MAX: %u, ROLL: %u", minimum, maximum, roll);
-
- WorldPacket data(MSG_RANDOM_ROLL, 4+4+4+8);
- data << uint32(minimum);
- data << uint32(maximum);
- data << uint32(roll);
- data << uint64(GetPlayer()->GetGUID());
- if (GetPlayer()->GetGroup())
- GetPlayer()->GetGroup()->BroadcastPacket(&data, false);
- else
- SendPacket(&data);
+ GetPlayer()->DoRandomRoll(minimum, maximum);
}
void WorldSession::HandleRaidTargetUpdateOpcode(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 4fa5e2f848d..52b36d80202 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -284,7 +284,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
continue;
uint32 pzoneid = target->GetZoneId();
- uint8 gender = target->GetByteValue(PLAYER_BYTES_3, 0);
+ uint8 gender = target->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
bool z_show = true;
for (uint32 i = 0; i < zones_count; ++i)
@@ -405,7 +405,7 @@ void WorldSession::HandleLogoutRequestOpcode(WorldPacket& /*recvData*/)
// not set flags if player can't free move to prevent lost state at logout cancel
if (GetPlayer()->CanFreeMove())
{
- if (GetPlayer()->getStandState() == UNIT_STAND_STATE_STAND)
+ if (GetPlayer()->GetStandState() == UNIT_STAND_STATE_STAND)
GetPlayer()->SetStandState(UNIT_STAND_STATE_SIT);
WorldPacket data(SMSG_FORCE_MOVE_ROOT, (8+4)); // guess size
@@ -773,17 +773,11 @@ void WorldSession::HandleResurrectResponseOpcode(WorldPacket& recvData)
if (status == 0)
{
- GetPlayer()->clearResurrectRequestData(); // reject
+ GetPlayer()->ClearResurrectRequestData(); // reject
return;
}
- if (GetPlayer()->IsValidGhoulResurrectRequest(guid))
- {
- GetPlayer()->GhoulResurrect();
- return;
- }
-
- if (!GetPlayer()->isResurrectRequestedBy(guid))
+ if (!GetPlayer()->IsResurrectRequestedBy(guid))
return;
GetPlayer()->ResurrectUsingRequestData();
@@ -890,15 +884,15 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recvData)
player->SendTransferAborted(entry->MapID, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(entry->IsRaid()));
break;
case Map::CANNOT_ENTER_NOT_IN_RAID:
- if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
- {
- char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
- TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance '%s'", player->GetName().c_str(), mapName);
- // probably there must be special opcode, because client has this string constant in GlobalStrings.lua
- player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName);
- }
+ {
+ WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4);
+ data << uint32(0);
+ data << uint32(2); // You must be in a raid group to enter this instance.
+ player->GetSession()->SendPacket(&data);
+ TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance map %d", player->GetName().c_str(), at->target_mapId);
reviveAtTrigger = true;
break;
+ }
case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE:
{
WorldPacket data(SMSG_CORPSE_NOT_IN_INSTANCE);
@@ -1062,12 +1056,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recvData)
void WorldSession::HandleCompleteCinematic(WorldPacket& /*recvData*/)
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_COMPLETE_CINEMATIC");
+ // If player has sight bound to visual waypoint NPC we should remove it
+ GetPlayer()->GetCinematicMgr()->EndCinematic();
}
void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recvData*/)
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_NEXT_CINEMATIC_CAMERA");
+ // Sent by client when cinematic actually begun. So we begin the server side process
+ GetPlayer()->GetCinematicMgr()->BeginCinematic();
}
void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData)
@@ -1163,7 +1159,7 @@ void WorldSession::HandleSetActionBarToggles(WorldPacket& recvData)
return;
}
- GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, actionBar);
+ GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, actionBar);
}
void WorldSession::HandlePlayedTime(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 02702fc5622..64f927a987f 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -473,7 +473,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData)
{
TC_LOG_ERROR("network", "%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
move_type_name[move_type], _player->GetName().c_str(), _player->GetSpeed(move_type), newspeed);
- _player->SetSpeed(move_type, _player->GetSpeedRate(move_type), true);
+ _player->SetSpeedRate(move_type, _player->GetSpeedRate(move_type));
}
else // must be lesser - cheating
{
diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp
index ebc9ebde994..1eee9e0d9fe 100644
--- a/src/server/game/Handlers/QueryHandler.cpp
+++ b/src/server/game/Handlers/QueryHandler.cpp
@@ -133,10 +133,10 @@ void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recvData)
CreatureQuestItemList const* items = sObjectMgr->GetCreatureQuestItemList(entry);
if (items)
- for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
+ for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
data << (i < items->size() ? uint32((*items)[i]) : uint32(0));
else
- for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
+ for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
data << uint32(0);
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 6be1fd30ae3..e07e10ab00c 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -594,11 +594,11 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
if (creator->GetTypeId() == TYPEID_PLAYER)
{
Player* player = creator->ToPlayer();
- data << uint8(player->GetByteValue(PLAYER_BYTES, 0)); // skin
- data << uint8(player->GetByteValue(PLAYER_BYTES, 1)); // face
- data << uint8(player->GetByteValue(PLAYER_BYTES, 2)); // hair
- data << uint8(player->GetByteValue(PLAYER_BYTES, 3)); // haircolor
- data << uint8(player->GetByteValue(PLAYER_BYTES_2, 0)); // facialhair
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
data << uint32(player->GetGuildId()); // unk
static EquipmentSlots const itemSlots[] =
diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h
index 255b68165e3..18e22602e07 100644
--- a/src/server/game/Instances/InstanceSaveMgr.h
+++ b/src/server/game/Instances/InstanceSaveMgr.h
@@ -41,7 +41,7 @@ class Group;
- player-instance binds for permanent heroic/raid saves
- group-instance binds (both solo and permanent) cache the player binds for the group leader
*/
-class InstanceSave
+class TC_GAME_API InstanceSave
{
friend class InstanceSaveManager;
public:
@@ -147,7 +147,7 @@ class InstanceSave
typedef std::unordered_map<uint32 /*PAIR32(map, difficulty)*/, time_t /*resetTime*/> ResetTimeByMapDifficultyMap;
-class InstanceSaveManager
+class TC_GAME_API InstanceSaveManager
{
friend class InstanceSave;
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 1575b50098f..0887d183a8b 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -29,6 +29,8 @@
#include "Pet.h"
#include "WorldSession.h"
#include "Opcodes.h"
+#include "ScriptReloadMgr.h"
+#include "ScriptMgr.h"
BossBoundaryData::~BossBoundaryData()
{
@@ -36,6 +38,18 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
+InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
+ auto const scriptname = sObjectMgr->GetScriptName(scriptId);
+ ASSERT(!scriptname.empty());
+ // Acquire a strong reference from the script module
+ // to keep it loaded until this object is destroyed.
+ module_reference = sScriptMgr->AcquireModuleReferenceOfScriptName(scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+}
+
void InstanceScript::SaveToDB()
{
std::string data = GetSaveData();
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index ce83061e162..3814afe2f4b 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -36,6 +36,7 @@ class Unit;
class Player;
class GameObject;
class Creature;
+class ModuleReference;
enum EncounterFrameType
{
@@ -79,7 +80,7 @@ struct BossBoundaryEntry
AreaBoundary const* Boundary;
};
-struct BossBoundaryData
+struct TC_GAME_API BossBoundaryData
{
typedef std::vector<BossBoundaryEntry> StorageType;
typedef StorageType::const_iterator const_iterator;
@@ -134,10 +135,10 @@ typedef std::map<uint32 /*entry*/, MinionInfo> MinionInfoMap;
typedef std::map<uint32 /*type*/, ObjectGuid /*guid*/> ObjectGuidMap;
typedef std::map<uint32 /*entry*/, uint32 /*type*/> ObjectInfoMap;
-class InstanceScript : public ZoneScript
+class TC_GAME_API InstanceScript : public ZoneScript
{
public:
- explicit InstanceScript(Map* map) : instance(map), completedEncounters(0) { }
+ explicit InstanceScript(Map* map);
virtual ~InstanceScript() { }
@@ -289,6 +290,11 @@ class InstanceScript : public ZoneScript
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
+
+ #ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Strong reference to the associated script module
+ std::shared_ptr<ModuleReference> module_reference;
+ #endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
};
template<class AI, class T>
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 19dc210ea5b..5cede9a32d5 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -1871,3 +1871,20 @@ void LoadLootTemplates_Reference()
TC_LOG_INFO("server.loading", ">> Loaded refence loot templates in %u ms", GetMSTimeDiffToNow(oldMSTime));
}
+
+void LoadLootTables()
+{
+ LoadLootTemplates_Creature();
+ LoadLootTemplates_Fishing();
+ LoadLootTemplates_Gameobject();
+ LoadLootTemplates_Item();
+ LoadLootTemplates_Mail();
+ LoadLootTemplates_Milling();
+ LoadLootTemplates_Pickpocketing();
+ LoadLootTemplates_Skinning();
+ LoadLootTemplates_Disenchant();
+ LoadLootTemplates_Prospecting();
+ LoadLootTemplates_Spell();
+
+ LoadLootTemplates_Reference();
+}
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index af6f3e56707..0afb327d7f0 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -54,7 +54,7 @@ enum RollMask
#define MAX_NR_QUEST_ITEMS 32
// unrelated to the number of quest items shown, just for reserve
-enum LootMethod
+enum LootMethod : uint8
{
FREE_FOR_ALL = 0,
ROUND_ROBIN = 1,
@@ -74,7 +74,7 @@ enum PermissionTypes
NONE_PERMISSION = 6
};
-enum LootType
+enum LootType : uint8
{
LOOT_NONE = 0,
@@ -122,7 +122,7 @@ enum LootSlotType
class Player;
class LootStore;
-struct LootStoreItem
+struct TC_GAME_API LootStoreItem
{
uint32 itemid; // id of the item
uint32 reference; // referenced TemplateleId
@@ -148,7 +148,7 @@ struct LootStoreItem
typedef std::set<ObjectGuid::LowType> AllowedLooterSet;
-struct LootItem
+struct TC_GAME_API LootItem
{
uint32 itemid;
uint32 randomSuffix;
@@ -203,7 +203,7 @@ typedef std::unordered_map<uint32, LootTemplate*> LootTemplateMap;
typedef std::set<uint32> LootIdSet;
-class LootStore
+class TC_GAME_API LootStore
{
public:
explicit LootStore(char const* name, char const* entryName, bool ratesAllowed)
@@ -239,7 +239,7 @@ class LootStore
bool m_ratesAllowed;
};
-class LootTemplate
+class TC_GAME_API LootTemplate
{
class LootGroup; // A set of loot definitions for items (refs are not allowed inside)
typedef std::vector<LootGroup*> LootGroups;
@@ -307,7 +307,7 @@ struct LootView;
ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li);
ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
-struct Loot
+struct TC_GAME_API Loot
{
friend ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
@@ -409,48 +409,33 @@ struct LootView
: loot(_loot), viewer(_viewer), permission(_permission) { }
};
-extern LootStore LootTemplates_Creature;
-extern LootStore LootTemplates_Fishing;
-extern LootStore LootTemplates_Gameobject;
-extern LootStore LootTemplates_Item;
-extern LootStore LootTemplates_Mail;
-extern LootStore LootTemplates_Milling;
-extern LootStore LootTemplates_Pickpocketing;
-extern LootStore LootTemplates_Reference;
-extern LootStore LootTemplates_Skinning;
-extern LootStore LootTemplates_Disenchant;
-extern LootStore LootTemplates_Prospecting;
-extern LootStore LootTemplates_Spell;
-
-void LoadLootTemplates_Creature();
-void LoadLootTemplates_Fishing();
-void LoadLootTemplates_Gameobject();
-void LoadLootTemplates_Item();
-void LoadLootTemplates_Mail();
-void LoadLootTemplates_Milling();
-void LoadLootTemplates_Pickpocketing();
-void LoadLootTemplates_Skinning();
-void LoadLootTemplates_Disenchant();
-void LoadLootTemplates_Prospecting();
-
-void LoadLootTemplates_Spell();
-void LoadLootTemplates_Reference();
-
-inline void LoadLootTables()
-{
- LoadLootTemplates_Creature();
- LoadLootTemplates_Fishing();
- LoadLootTemplates_Gameobject();
- LoadLootTemplates_Item();
- LoadLootTemplates_Mail();
- LoadLootTemplates_Milling();
- LoadLootTemplates_Pickpocketing();
- LoadLootTemplates_Skinning();
- LoadLootTemplates_Disenchant();
- LoadLootTemplates_Prospecting();
- LoadLootTemplates_Spell();
-
- LoadLootTemplates_Reference();
-}
+TC_GAME_API extern LootStore LootTemplates_Creature;
+TC_GAME_API extern LootStore LootTemplates_Fishing;
+TC_GAME_API extern LootStore LootTemplates_Gameobject;
+TC_GAME_API extern LootStore LootTemplates_Item;
+TC_GAME_API extern LootStore LootTemplates_Mail;
+TC_GAME_API extern LootStore LootTemplates_Milling;
+TC_GAME_API extern LootStore LootTemplates_Pickpocketing;
+TC_GAME_API extern LootStore LootTemplates_Reference;
+TC_GAME_API extern LootStore LootTemplates_Skinning;
+TC_GAME_API extern LootStore LootTemplates_Disenchant;
+TC_GAME_API extern LootStore LootTemplates_Prospecting;
+TC_GAME_API extern LootStore LootTemplates_Spell;
+
+TC_GAME_API void LoadLootTemplates_Creature();
+TC_GAME_API void LoadLootTemplates_Fishing();
+TC_GAME_API void LoadLootTemplates_Gameobject();
+TC_GAME_API void LoadLootTemplates_Item();
+TC_GAME_API void LoadLootTemplates_Mail();
+TC_GAME_API void LoadLootTemplates_Milling();
+TC_GAME_API void LoadLootTemplates_Pickpocketing();
+TC_GAME_API void LoadLootTemplates_Skinning();
+TC_GAME_API void LoadLootTemplates_Disenchant();
+TC_GAME_API void LoadLootTemplates_Prospecting();
+
+TC_GAME_API void LoadLootTemplates_Spell();
+TC_GAME_API void LoadLootTemplates_Reference();
+
+TC_GAME_API void LoadLootTables();
#endif
diff --git a/src/server/game/Mails/Mail.h b/src/server/game/Mails/Mail.h
index 407edf2badb..ee1a0aae2c9 100644
--- a/src/server/game/Mails/Mail.h
+++ b/src/server/game/Mails/Mail.h
@@ -79,7 +79,7 @@ enum MailShowFlags
MAIL_SHOW_RETURN = 0x0010
};
-class MailSender
+class TC_GAME_API MailSender
{
public: // Constructors
MailSender(MailMessageType messageType, ObjectGuid::LowType sender_guidlow_or_entry, MailStationery stationery = MAIL_STATIONERY_DEFAULT)
@@ -100,7 +100,7 @@ class MailSender
MailStationery m_stationery;
};
-class MailReceiver
+class TC_GAME_API MailReceiver
{
public: // Constructors
explicit MailReceiver(ObjectGuid::LowType receiver_lowguid) : m_receiver(NULL), m_receiver_lowguid(receiver_lowguid) { }
@@ -114,7 +114,7 @@ class MailReceiver
ObjectGuid::LowType m_receiver_lowguid;
};
-class MailDraft
+class TC_GAME_API MailDraft
{
typedef std::map<ObjectGuid::LowType, Item*> MailItemMap;
@@ -162,7 +162,7 @@ struct MailItemInfo
};
typedef std::vector<MailItemInfo> MailItemInfoVec;
-struct Mail
+struct TC_GAME_API Mail
{
uint32 messageID;
uint8 messageType;
diff --git a/src/server/game/Maps/AreaBoundary.h b/src/server/game/Maps/AreaBoundary.h
index a134b783ca6..0973d1a6e86 100644
--- a/src/server/game/Maps/AreaBoundary.h
+++ b/src/server/game/Maps/AreaBoundary.h
@@ -20,7 +20,7 @@
#include "Position.h"
-class AreaBoundary
+class TC_GAME_API AreaBoundary
{
public:
enum BoundaryType
@@ -40,7 +40,7 @@ class AreaBoundary
{
double d_positionX, d_positionY, d_positionZ;
DoublePosition(double x = 0.0, double y = 0.0, double z = 0.0, float o = 0.0f)
- : Position(x, y, z, o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
+ : Position(float(x), float(y), float(z), o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
DoublePosition(float x, float y = 0.0f, float z = 0.0f, float o = 0.0f)
: Position(x, y, z, o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
DoublePosition(const Position& pos)
@@ -66,7 +66,7 @@ class AreaBoundary
bool m_isInvertedBoundary;
};
-class RectangleBoundary : public AreaBoundary
+class TC_GAME_API RectangleBoundary : public AreaBoundary
{
public:
// X axis is north/south, Y axis is east/west, larger values are northwest
@@ -79,7 +79,7 @@ class RectangleBoundary : public AreaBoundary
const float _minX, _maxX, _minY, _maxY;
};
-class CircleBoundary : public AreaBoundary
+class TC_GAME_API CircleBoundary : public AreaBoundary
{
public:
CircleBoundary(Position const& center, double radius, bool isInverted = false);
@@ -95,7 +95,7 @@ class CircleBoundary : public AreaBoundary
const double _radiusSq;
};
-class EllipseBoundary : public AreaBoundary
+class TC_GAME_API EllipseBoundary : public AreaBoundary
{
public:
EllipseBoundary(Position const& center, double radiusX, double radiusY, bool isInverted = false);
@@ -109,7 +109,7 @@ class EllipseBoundary : public AreaBoundary
const double _radiusYSq, _scaleXSq;
};
-class TriangleBoundary : public AreaBoundary
+class TC_GAME_API TriangleBoundary : public AreaBoundary
{
public:
TriangleBoundary(Position const& pointA, Position const& pointB, Position const& pointC, bool isInverted = false);
@@ -123,7 +123,7 @@ class TriangleBoundary : public AreaBoundary
const double _abx, _bcx, _cax, _aby, _bcy, _cay;
};
-class ParallelogramBoundary : public AreaBoundary
+class TC_GAME_API ParallelogramBoundary : public AreaBoundary
{
public:
// Note: AB must be orthogonal to AD
@@ -138,7 +138,7 @@ class ParallelogramBoundary : public AreaBoundary
const double _abx, _dax, _aby, _day;
};
-class ZRangeBoundary : public AreaBoundary
+class TC_GAME_API ZRangeBoundary : public AreaBoundary
{
public:
ZRangeBoundary(float minZ, float maxZ, bool isInverted = false);
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 7b94895c3de..794be12ee7c 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -17,6 +17,7 @@
*/
#include "Map.h"
+#include "MapManager.h"
#include "Battleground.h"
#include "MMapFactory.h"
#include "CellImpl.h"
@@ -64,7 +65,7 @@ Map::~Map()
}
if (!m_scriptSchedule.empty())
- sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+ sMapMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
@@ -713,6 +714,15 @@ void Map::Update(const uint32 t_diff)
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
+ // If player is using far sight, visit that object too
+ if (WorldObject* viewPoint = player->GetViewpoint())
+ {
+ if (Creature* viewCreature = viewPoint->ToCreature())
+ VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update);
+ else if (DynamicObject* viewObject = viewPoint->ToDynObject())
+ VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update);
+ }
+
// Handle updates for creatures in combat with player and are more than 60 yards away
if (player->IsInCombat())
{
@@ -2335,9 +2345,9 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
{
// we have mapheight and vmapheight and must select more appropriate
- // we are already under the surface or vmap height above map heigt
+ // vmap height above map height
// or if the distance of the vmap height is less the land height distance
- if (z < mapHeight || vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z))
+ if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z))
return vmapHeight;
else
return mapHeight; // better use .map surface height
@@ -2657,8 +2667,8 @@ void Map::UpdateObjectsVisibilityFor(Player* player, Cell cell, CellCoord cellpa
cell.SetNoCreate();
TypeContainerVisitor<Trinity::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
TypeContainerVisitor<Trinity::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
- cell.Visit(cellpair, world_notifier, *this, *player, player->GetSightRange());
- cell.Visit(cellpair, grid_notifier, *this, *player, player->GetSightRange());
+ cell.Visit(cellpair, world_notifier, *this, *player->m_seer, player->GetSightRange());
+ cell.Visit(cellpair, grid_notifier, *this, *player->m_seer, player->GetSightRange());
// send data
notifier.SendToSelf();
@@ -2993,15 +3003,15 @@ void Map::RemoveFromActive(DynamicObject* obj)
RemoveFromActiveHelper(obj);
}
-template bool Map::AddToMap(Corpse*);
-template bool Map::AddToMap(Creature*);
-template bool Map::AddToMap(GameObject*);
-template bool Map::AddToMap(DynamicObject*);
+template TC_GAME_API bool Map::AddToMap(Corpse*);
+template TC_GAME_API bool Map::AddToMap(Creature*);
+template TC_GAME_API bool Map::AddToMap(GameObject*);
+template TC_GAME_API bool Map::AddToMap(DynamicObject*);
-template void Map::RemoveFromMap(Corpse*, bool);
-template void Map::RemoveFromMap(Creature*, bool);
-template void Map::RemoveFromMap(GameObject*, bool);
-template void Map::RemoveFromMap(DynamicObject*, bool);
+template TC_GAME_API void Map::RemoveFromMap(Corpse*, bool);
+template TC_GAME_API void Map::RemoveFromMap(Creature*, bool);
+template TC_GAME_API void Map::RemoveFromMap(GameObject*, bool);
+template TC_GAME_API void Map::RemoveFromMap(DynamicObject*, bool);
/* ******* Dungeon Instance Maps ******* */
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 1da3f40c1d0..5f134613e85 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -154,7 +154,7 @@ struct LiquidData
float depth_level;
};
-class GridMap
+class TC_GAME_API GridMap
{
uint32 _flags;
union{
@@ -253,7 +253,7 @@ typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHol
typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
-class Map : public GridRefManager<NGridType>
+class TC_GAME_API Map : public GridRefManager<NGridType>
{
friend class MapReference;
public:
@@ -277,6 +277,7 @@ class Map : public GridRefManager<NGridType>
virtual bool AddPlayerToMap(Player*);
virtual void RemovePlayerFromMap(Player*, bool);
+
template<class T> bool AddToMap(T *);
template<class T> void RemoveFromMap(T *, bool);
@@ -292,7 +293,8 @@ class Map : public GridRefManager<NGridType>
void GameObjectRelocation(GameObject* go, float x, float y, float z, float orientation, bool respawnRelocationOnFail = true);
void DynamicObjectRelocation(DynamicObject* go, float x, float y, float z, float orientation);
- template<class T, class CONTAINER> void Visit(const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor);
+ template<class T, class CONTAINER>
+ void Visit(const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor);
bool IsRemovalGrid(float x, float y) const
{
@@ -754,7 +756,7 @@ enum InstanceResetMethod
INSTANCE_RESET_RESPAWN_DELAY
};
-class InstanceMap : public Map
+class TC_GAME_API InstanceMap : public Map
{
public:
InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent);
@@ -786,7 +788,7 @@ class InstanceMap : public Map
uint32 i_script_id;
};
-class BattlegroundMap : public Map
+class TC_GAME_API BattlegroundMap : public Map
{
public:
BattlegroundMap(uint32 id, time_t, uint32 InstanceId, Map* _parent, uint8 spawnMode);
diff --git a/src/server/game/Maps/MapInstanced.h b/src/server/game/Maps/MapInstanced.h
index 7fa66bde06a..eea54e1f013 100644
--- a/src/server/game/Maps/MapInstanced.h
+++ b/src/server/game/Maps/MapInstanced.h
@@ -23,7 +23,7 @@
#include "InstanceSaveMgr.h"
#include "DBCEnums.h"
-class MapInstanced : public Map
+class TC_GAME_API MapInstanced : public Map
{
friend class MapManager;
public:
diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp
index c1882b3dc75..79a8b3855e8 100644
--- a/src/server/game/Maps/MapManager.cpp
+++ b/src/server/game/Maps/MapManager.cpp
@@ -38,10 +38,10 @@
#include "AchievementMgr.h"
MapManager::MapManager()
+ : _nextInstanceId(0), _scheduledScripts(0)
{
i_gridCleanUpDelay = sWorld->getIntConfig(CONFIG_INTERVAL_GRIDCLEAN);
i_timer.SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_MAPUPDATE));
- _nextInstanceId = 0;
}
MapManager::~MapManager() { }
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index b13d6552022..91a26c29150 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -28,7 +28,7 @@
class Transport;
struct TransportCreatureProto;
-class MapManager
+class TC_GAME_API MapManager
{
public:
static MapManager* instance();
@@ -126,6 +126,11 @@ class MapManager
template<typename Worker>
void DoForAllMapsWithMapId(uint32 mapId, Worker&& worker);
+ void IncreaseScheduledScriptsCount() { ++_scheduledScripts; }
+ void DecreaseScheduledScriptCount() { --_scheduledScripts; }
+ void DecreaseScheduledScriptCount(std::size_t count) { _scheduledScripts -= count; }
+ bool IsScriptScheduled() const { return _scheduledScripts > 0; }
+
private:
typedef std::unordered_map<uint32, Map*> MapMapType;
typedef std::vector<bool> InstanceIds;
@@ -150,6 +155,9 @@ class MapManager
InstanceIds _instanceIds;
uint32 _nextInstanceId;
MapUpdater m_updater;
+
+ // atomic op counter for active scripts amount
+ std::atomic<std::size_t> _scheduledScripts;
};
template<typename Worker>
diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp
index a01c109b9ca..c5d7bdc64ce 100644
--- a/src/server/game/Scripting/MapScripts.cpp
+++ b/src/server/game/Maps/MapScripts.cpp
@@ -21,6 +21,7 @@
#include "GridNotifiersImpl.h"
#include "GossipDef.h"
#include "Map.h"
+#include "MapManager.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "Item.h"
@@ -58,7 +59,7 @@ void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, O
if (iter->first == 0)
immedScript = true;
- sScriptMgr->IncreaseScheduledScriptsCount();
+ sMapMgr->IncreaseScheduledScriptsCount();
}
///- If one of the effects should be immediate, launch the script execution
if (/*start &&*/ immedScript && !i_scriptLock)
@@ -86,7 +87,7 @@ void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* sou
sa.script = &script;
m_scriptSchedule.insert(ScriptScheduleMap::value_type(time_t(sWorld->GetGameTime() + delay), sa));
- sScriptMgr->IncreaseScheduledScriptsCount();
+ sMapMgr->IncreaseScheduledScriptsCount();
///- If effects should be immediate, launch the script execution
if (delay == 0 && !i_scriptLock)
@@ -878,6 +879,6 @@ void Map::ScriptsProcess()
m_scriptSchedule.erase(iter);
iter = m_scriptSchedule.begin();
- sScriptMgr->DecreaseScheduledScriptCount();
+ sMapMgr->DecreaseScheduledScriptCount();
}
}
diff --git a/src/server/game/Maps/MapUpdater.h b/src/server/game/Maps/MapUpdater.h
index 3d0f0b9e7e8..d95011d5a42 100644
--- a/src/server/game/Maps/MapUpdater.h
+++ b/src/server/game/Maps/MapUpdater.h
@@ -28,7 +28,7 @@
class MapUpdateRequest;
class Map;
-class MapUpdater
+class TC_GAME_API MapUpdater
{
public:
diff --git a/src/server/game/Maps/TransportMgr.h b/src/server/game/Maps/TransportMgr.h
index 32fca6bd26e..229fea882a4 100644
--- a/src/server/game/Maps/TransportMgr.h
+++ b/src/server/game/Maps/TransportMgr.h
@@ -82,7 +82,7 @@ struct TransportTemplate
typedef std::map<uint32, TransportAnimationEntry const*> TransportPathContainer;
typedef std::map<uint32, TransportRotationEntry const*> TransportPathRotationContainer;
-struct TransportAnimation
+struct TC_GAME_API TransportAnimation
{
TransportAnimation() : TotalTime(0) { }
@@ -96,9 +96,9 @@ struct TransportAnimation
typedef std::map<uint32, TransportAnimation> TransportAnimationContainer;
-class TransportMgr
+class TC_GAME_API TransportMgr
{
- friend void LoadDBCStores(std::string const&);
+ friend TC_GAME_API void LoadDBCStores(std::string const&);
public:
static TransportMgr* instance();
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index dcee5ff7071..d6af5d81432 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -887,7 +887,9 @@ enum TrinityStrings
LANG_CHARACTER_DELETED_LIST_LINE_CHAT = 1026,
LANG_SQLDRIVER_QUERY_LOGGING_ENABLED = 1027,
LANG_SQLDRIVER_QUERY_LOGGING_DISABLED = 1028,
- // Room for more level 4 1029-1099 not used
+ LANG_ACCOUNT_INVALID_BNET_NAME = 1029, // 6.x ONLY
+ LANG_ACCOUNT_USE_BNET_COMMANDS = 1030, // 6.x enum value name but different text in DB
+ // Room for more level 4 1031-1099 not used
// Level 3 (continue)
LANG_ACCOUNT_SETADDON = 1100,
@@ -1061,7 +1063,7 @@ enum TrinityStrings
LANG_COMMAND_NO_FROZEN_PLAYERS = 5004,
LANG_COMMAND_LIST_FREEZE = 5005,
LANG_COMMAND_PERMA_FROZEN_PLAYER = 5006,
- LANG_INSTANCE_RAID_GROUP_ONLY = 5007,
+ // = 5007, unused
LANG_INSTANCE_CLOSED = 5008,
LANG_COMMAND_PLAYED_TO_ALL = 5009,
LANG_NPCINFO_LINKGUID = 5010,
@@ -1210,6 +1212,8 @@ enum TrinityStrings
LANG_CREATURE_NO_INTERIOR_POINT_FOUND = 11011,
LANG_CREATURE_MOVEMENT_NOT_BOUNDED = 11012,
LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED = 11013,
- LANG_INSTANCE_BIND_MISMATCH = 11014
+ LANG_INSTANCE_BIND_MISMATCH = 11014,
+ LANG_CREATURE_NOT_AI_ENABLED = 11015,
+ LANG_SELECT_PLAYER_OR_PET = 11016,
};
#endif
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 77d6f739da6..ac7edc64b90 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -23,7 +23,7 @@
#include "DetourNavMesh.h"
#include <cassert>
-enum SpellEffIndex
+enum SpellEffIndex : uint8
{
EFFECT_0 = 0,
EFFECT_1 = 1,
@@ -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
@@ -828,7 +828,7 @@ enum SpellEffects
SPELL_EFFECT_CREATE_ITEM_2 = 157,
SPELL_EFFECT_MILLING = 158,
SPELL_EFFECT_ALLOW_RENAME_PET = 159,
- SPELL_EFFECT_160 = 160,
+ SPELL_EFFECT_FORCE_CAST_2 = 160,
SPELL_EFFECT_TALENT_SPEC_COUNT = 161,
SPELL_EFFECT_TALENT_SPEC_SELECT = 162,
SPELL_EFFECT_163 = 163,
@@ -2576,38 +2576,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 2a57524cb3c..f27e47fba21 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -302,6 +302,27 @@ void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generate
}
}
+void MotionMaster::MoveCloserAndStop(uint32 id, Unit* target, float distance)
+{
+ float distanceToTravel = _owner->GetExactDist2d(target) - distance;
+ if (distanceToTravel > 0.0f)
+ {
+ float angle = _owner->GetAngle(target);
+ float destx = _owner->GetPositionX() + distanceToTravel * std::cos(angle);
+ float desty = _owner->GetPositionY() + distanceToTravel * std::sin(angle);
+ MovePoint(id, destx, desty, target->GetPositionZ());
+ }
+ else
+ {
+ // we are already close enough. We just need to turn toward the target without changing position.
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZMinusOffset());
+ init.SetFacing(target);
+ init.Launch();
+ Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
+ }
+}
+
void MotionMaster::MoveLand(uint32 id, Position const& pos)
{
float x, y, z;
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 76ae12986d5..005f10b44af 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -79,7 +79,7 @@ enum RotateDirection
// assume it is 25 yard per 0.6 second
#define SPEED_CHARGE 42.0f
-class MotionMaster //: private std::stack<MovementGenerator *>
+class TC_GAME_API MotionMaster //: private std::stack<MovementGenerator *>
{
private:
//typedef std::stack<MovementGenerator *> Impl;
@@ -173,6 +173,12 @@ class MotionMaster //: private std::stack<MovementGenerator *>
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ, generatePath); }
void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
+ /* Makes the unit move toward the target until it is at a certain distance from it. The unit then stops.
+ Only works in 2D.
+ This method doesn't account for any movement done by the target. in other words, it only works if the target is stationary.
+ */
+ void MoveCloserAndStop(uint32 id, Unit* target, float distance);
+
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index 56e5dc7058a..d9dd17fabc2 100755
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -27,7 +27,7 @@
class Unit;
-class MovementGenerator
+class TC_GAME_API MovementGenerator
{
public:
virtual ~MovementGenerator();
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
index 108276c951a..6f6a8037d47 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -23,11 +23,6 @@
#include "MoveSpline.h"
#include "Player.h"
-#ifdef MAP_BASED_RAND_GEN
-#define rand_norm() unit.rand_norm()
-#define urand(a, b) unit.urand(a, b)
-#endif
-
template<class T>
void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
{
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
index 9aa778c5651..161f9e3e970 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
@@ -32,7 +32,7 @@ class IdleMovementGenerator : public MovementGenerator
MovementGeneratorType GetMovementGeneratorType() const override { return IDLE_MOTION_TYPE; }
};
-extern IdleMovementGenerator si_idleMovement;
+TC_GAME_API extern IdleMovementGenerator si_idleMovement;
class RotateMovementGenerator : public MovementGenerator
{
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 2e013c44ae8..421678ded17 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -26,10 +26,6 @@
#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
-#ifdef MAP_BASED_RAND_GEN
-#define rand_norm() creature.rand_norm()
-#endif
-
template<>
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index b595059557a..3fd3b702ba5 100644
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -36,8 +36,14 @@ 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);
return;
+ }
if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsFocusing(nullptr, true))
return;
@@ -48,6 +54,9 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up
{
if (!i_offset)
{
+ if (i_target->IsWithinDistInMap(owner, CONTACT_DISTANCE))
+ return;
+
// to nearest contact position
i_target->GetContactPoint(owner, x, y, z);
}
@@ -99,8 +108,10 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up
bool result = i_path->CalculatePath(x, y, z, forceDest);
if (!result || (i_path->GetPathType() & PATHFIND_NOPATH))
{
- // Cant reach target
+ // can't reach target
i_recalculateTravel = true;
+ if (owner->GetTypeId() == TYPEID_UNIT)
+ owner->ToCreature()->SetCannotReachTarget(true);
return;
}
@@ -108,6 +119,8 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up
i_targetReached = false;
i_recalculateTravel = false;
owner->AddUnitState(UNIT_STATE_CHASE);
+ if (owner->GetTypeId() == TYPEID_UNIT)
+ owner->ToCreature()->SetCannotReachTarget(false);
Movement::MoveSplineInit init(owner);
init.MovebyPath(i_path->GetPath());
@@ -136,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();
@@ -198,6 +211,8 @@ void ChaseMovementGenerator<T>::_reachTarget(T* owner)
{
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
owner->Attack(this->i_target.getTarget(), true);
+ if (owner->GetTypeId() == TYPEID_UNIT)
+ owner->ToCreature()->SetCannotReachTarget(false);
}
template<>
@@ -265,9 +280,9 @@ void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
if (!owner->IsPet() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
return;
- owner->UpdateSpeed(MOVE_RUN, true);
- owner->UpdateSpeed(MOVE_WALK, true);
- owner->UpdateSpeed(MOVE_SWIM, true);
+ owner->UpdateSpeed(MOVE_RUN);
+ owner->UpdateSpeed(MOVE_WALK);
+ owner->UpdateSpeed(MOVE_SWIM);
}
template<>
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index 48d29b3259b..dd1cf494325 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -213,7 +213,7 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
creature->SetHomePosition(creature->GetPosition());
if (creature->IsStopped())
- Stop(STOP_TIME_FOR_PLAYER);
+ Stop(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER));
else if (creature->movespline->Finalized())
{
OnArrived(creature);
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
index 1dd4611d53b..72309822dab 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -28,9 +28,9 @@
#include "MovementGenerator.h"
#include "WaypointManager.h"
#include "Player.h"
+#include "World.h"
#define FLIGHT_TRAVEL_UPDATE 100
-#define STOP_TIME_FOR_PLAYER 3 * MINUTE * IN_MILLISECONDS // 3 Minutes
#define TIMEDIFF_NEXT_WP 250
template<class T, class P>
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
index 71e0e88f0b2..3cad62abf25 100644
--- a/src/server/game/Movement/PathGenerator.h
+++ b/src/server/game/Movement/PathGenerator.h
@@ -49,7 +49,7 @@ enum PathType
PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
};
-class PathGenerator
+class TC_GAME_API PathGenerator
{
public:
explicit PathGenerator(Unit const* owner);
diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h
index 209f978d658..0e62862cecc 100644
--- a/src/server/game/Movement/Spline/MoveSpline.h
+++ b/src/server/game/Movement/Spline/MoveSpline.h
@@ -37,7 +37,7 @@ namespace Movement
// MoveSpline represents smooth catmullrom or linear curve and point that moves belong it
// curve can be cyclic - in this case movement will be cyclic
// point can have vertical acceleration motion componemt(used in fall, parabolic movement)
- class MoveSpline
+ class TC_GAME_API MoveSpline
{
public:
typedef Spline<int32> MySpline;
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h
index a94c84d92f3..3f38da97916 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.h
+++ b/src/server/game/Movement/Spline/MoveSplineInit.h
@@ -35,7 +35,7 @@ namespace Movement
};
// Transforms coordinates from global to transport offsets
- class TransportPathTransform
+ class TC_GAME_API TransportPathTransform
{
public:
TransportPathTransform(Unit* owner, bool transformForTransport)
@@ -49,7 +49,7 @@ namespace Movement
/* Initializes and launches spline movement
*/
- class MoveSplineInit
+ class TC_GAME_API MoveSplineInit
{
public:
diff --git a/src/server/game/Movement/Spline/MovementTypedefs.h b/src/server/game/Movement/Spline/MovementTypedefs.h
index 031ec729949..c87fea47a82 100644
--- a/src/server/game/Movement/Spline/MovementTypedefs.h
+++ b/src/server/game/Movement/Spline/MovementTypedefs.h
@@ -69,8 +69,8 @@ namespace Movement
typedef counter<uint32, 0xFFFFFFFF> UInt32Counter;
- extern float gravity;
- extern UInt32Counter splineIdGen;
+ TC_GAME_API extern float gravity;
+ TC_GAME_API extern UInt32Counter splineIdGen;
}
#endif // TRINITYSERVER_TYPEDEFS_H
diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h
index a1f98c52b0b..63dc4184308 100644
--- a/src/server/game/Movement/Waypoints/WaypointManager.h
+++ b/src/server/game/Movement/Waypoints/WaypointManager.h
@@ -44,7 +44,7 @@ struct WaypointData
typedef std::vector<WaypointData*> WaypointPath;
typedef std::unordered_map<uint32, WaypointPath> WaypointPathContainer;
-class WaypointMgr
+class TC_GAME_API WaypointMgr
{
public:
static WaypointMgr* instance();
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.h b/src/server/game/OutdoorPvP/OutdoorPvP.h
index 79f34ff10ea..255f3cd0f99 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.h
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.h
@@ -84,7 +84,7 @@ class Unit;
struct GossipMenuItems;
class OutdoorPvP;
-class OPvPCapturePoint
+class TC_GAME_API OPvPCapturePoint
{
public:
@@ -185,7 +185,7 @@ class OPvPCapturePoint
};
// base class for specific outdoor pvp handlers
-class OutdoorPvP : public ZoneScript
+class TC_GAME_API OutdoorPvP : public ZoneScript
{
friend class OutdoorPvPMgr;
diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
index 7f0695e16b3..aa00d211d6e 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
@@ -33,8 +33,14 @@ void OutdoorPvPMgr::Die()
for (OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
delete *itr;
+ m_OutdoorPvPSet.clear();
+
for (OutdoorPvPDataMap::iterator itr = m_OutdoorPvPDatas.begin(); itr != m_OutdoorPvPDatas.end(); ++itr)
delete itr->second;
+
+ m_OutdoorPvPDatas.clear();
+
+ m_OutdoorPvPMap.clear();
}
OutdoorPvPMgr* OutdoorPvPMgr::instance()
diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.h b/src/server/game/OutdoorPvP/OutdoorPvPMgr.h
index b1158075c57..e53a78ac271 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.h
+++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.h
@@ -35,7 +35,7 @@ struct OutdoorPvPData
};
// class to handle player enter / leave / areatrigger / GO use events
-class OutdoorPvPMgr
+class TC_GAME_API OutdoorPvPMgr
{
private:
OutdoorPvPMgr();
diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp
index 81d318fb1d9..812d8ea3940 100644
--- a/src/server/game/Pools/PoolMgr.cpp
+++ b/src/server/game/Pools/PoolMgr.cpp
@@ -60,6 +60,11 @@ bool ActivePoolData::IsActiveObject<Quest>(uint32 quest_id) const
return mActiveQuests.find(quest_id) != mActiveQuests.end();
}
+template TC_GAME_API bool ActivePoolData::IsActiveObject<Creature>(uint32) const;
+template TC_GAME_API bool ActivePoolData::IsActiveObject<GameObject>(uint32) const;
+template TC_GAME_API bool ActivePoolData::IsActiveObject<Pool>(uint32) const;
+template TC_GAME_API bool ActivePoolData::IsActiveObject<Quest>(uint32) const;
+
template<>
void ActivePoolData::ActivateObject<Creature>(uint32 db_guid, uint32 pool_id)
{
diff --git a/src/server/game/Pools/PoolMgr.h b/src/server/game/Pools/PoolMgr.h
index 6c48a011b73..f1af3a3530e 100644
--- a/src/server/game/Pools/PoolMgr.h
+++ b/src/server/game/Pools/PoolMgr.h
@@ -43,7 +43,7 @@ class Pool // for Pool of Pool
typedef std::set<ObjectGuid::LowType> ActivePoolObjects;
typedef std::map<uint32, uint32> ActivePoolPools;
-class ActivePoolData
+class TC_GAME_API ActivePoolData
{
public:
template<typename T>
@@ -66,7 +66,7 @@ class ActivePoolData
};
template <class T>
-class PoolGroup
+class TC_GAME_API PoolGroup
{
typedef std::vector<PoolObject> PoolObjectList;
public:
@@ -101,7 +101,7 @@ typedef std::multimap<uint32, uint32> PooledQuestRelation;
typedef std::pair<PooledQuestRelation::const_iterator, PooledQuestRelation::const_iterator> PooledQuestRelationBounds;
typedef std::pair<PooledQuestRelation::iterator, PooledQuestRelation::iterator> PooledQuestRelationBoundsNC;
-class PoolMgr
+class TC_GAME_API PoolMgr
{
private:
PoolMgr();
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index a502fef6140..5e3bb4889ab 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -60,7 +60,7 @@ enum QuestFailedReason
INVALIDREASON_DAILY_QUEST_COMPLETED_TODAY = 29 // You have completed that daily quest today.
};
-enum QuestShareMessages
+enum QuestShareMessages : uint8
{
QUEST_PARTY_MSG_SHARING_QUEST = 0,
QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1,
@@ -190,7 +190,7 @@ struct QuestLocale
// This Quest class provides a convenient way to access a few pretotaled (cached) quest details,
// all base quest information, and any utility functions such as generating the amount of
// xp to give
-class Quest
+class TC_GAME_API Quest
{
friend class ObjectMgr;
public:
diff --git a/src/server/game/Reputation/ReputationMgr.h b/src/server/game/Reputation/ReputationMgr.h
index 6f319a39b0c..5a51c9eb383 100644
--- a/src/server/game/Reputation/ReputationMgr.h
+++ b/src/server/game/Reputation/ReputationMgr.h
@@ -61,7 +61,7 @@ typedef std::map<uint32, ReputationRank> ForcedReactions;
class Player;
-class ReputationMgr
+class TC_GAME_API ReputationMgr
{
public: // constructors and global modifiers
explicit ReputationMgr(Player* owner) : _player(owner),
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 96477e2013e..ac440da3f24 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -17,6 +17,7 @@
*/
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
@@ -34,11 +35,9 @@
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Chat.h"
-
-// namespace
-// {
- UnusedScriptNamesContainer UnusedScriptNames;
-// }
+#include "MapManager.h"
+#include "LFGScripts.h"
+#include "InstanceScript.h"
// Trait which indicates whether this script type
// must be assigned in the database.
@@ -67,6 +66,10 @@ struct is_script_database_bound<GameObjectScript>
: std::true_type { };
template<>
+struct is_script_database_bound<VehicleScript>
+ : std::true_type { };
+
+template<>
struct is_script_database_bound<AreaTriggerScript>
: std::true_type { };
@@ -94,143 +97,848 @@ template<>
struct is_script_database_bound<AchievementCriteriaScript>
: std::true_type { };
+enum Spells
+{
+ SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
+};
+
+class ScriptRegistryInterface
+{
+public:
+ ScriptRegistryInterface() { }
+ virtual ~ScriptRegistryInterface() { }
+
+ ScriptRegistryInterface(ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface(ScriptRegistryInterface&&) = delete;
+
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface&&) = delete;
+
+ /// Removes all scripts associated with the given script context.
+ /// Requires ScriptRegistryBase::SwapContext to be called after all transfers have finished.
+ virtual void ReleaseContext(std::string const& context) = 0;
+
+ /// Injects and updates the changed script objects.
+ virtual void SwapContext(bool initialize) = 0;
+
+ /// Removes the scripts used by this registry from the given container.
+ /// Used to find unused script names.
+ virtual void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) = 0;
+
+ /// Unloads the script registry.
+ virtual void Unload() = 0;
+};
+
+template<class>
+class ScriptRegistry;
+
+class ScriptRegistryCompositum
+ : public ScriptRegistryInterface
+{
+ ScriptRegistryCompositum() { }
+
+ template<class>
+ friend class ScriptRegistry;
+
+ /// Type erasure wrapper for objects
+ class DeleteableObjectBase
+ {
+ public:
+ DeleteableObjectBase() { }
+ virtual ~DeleteableObjectBase() { }
+
+ DeleteableObjectBase(DeleteableObjectBase const&) = delete;
+ DeleteableObjectBase& operator= (DeleteableObjectBase const&) = delete;
+ };
+
+ template<typename T>
+ class DeleteableObject
+ : public DeleteableObjectBase
+ {
+ public:
+ DeleteableObject(T&& object)
+ : _object(std::forward<T>(object)) { }
+
+ private:
+ T _object;
+ };
+
+public:
+ void SetScriptNameInContext(std::string const& scriptname, std::string const& context)
+ {
+ ASSERT(_scriptnames_to_context.find(scriptname) == _scriptnames_to_context.end(),
+ "Scriptname was assigned to this context already!");
+ _scriptnames_to_context.insert(std::make_pair(scriptname, context));
+ }
+
+ std::string const& GetScriptContextOfScriptName(std::string const& scriptname) const
+ {
+ auto itr = _scriptnames_to_context.find(scriptname);
+ ASSERT(itr != _scriptnames_to_context.end() &&
+ "Given scriptname doesn't exist!");
+ return itr->second;
+ }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ for (auto const registry : _registries)
+ registry->ReleaseContext(context);
+
+ // Clear the script names in context after calling the release hooks
+ // since it's possible that new references to a shared library
+ // are acquired when releasing.
+ for (auto itr = _scriptnames_to_context.begin();
+ itr != _scriptnames_to_context.end();)
+ if (itr->second == context)
+ itr = _scriptnames_to_context.erase(itr);
+ else
+ ++itr;
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ for (auto const registry : _registries)
+ registry->SwapContext(initialize);
+
+ DoDelayedDelete();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const registry : _registries)
+ registry->RemoveUsedScriptsFromContainer(scripts);
+ }
+
+ void Unload() final override
+ {
+ for (auto const registry : _registries)
+ registry->Unload();
+ }
+
+ template<typename T>
+ void QueueForDelayedDelete(T&& any)
+ {
+ _delayed_delete_queue.push_back(
+ Trinity::make_unique<
+ DeleteableObject<typename std::decay<T>::type>
+ >(std::forward<T>(any))
+ );
+ }
+
+ static ScriptRegistryCompositum* Instance()
+ {
+ static ScriptRegistryCompositum instance;
+ return &instance;
+ }
+
+private:
+ void Register(ScriptRegistryInterface* registry)
+ {
+ _registries.insert(registry);
+ }
+
+ void DoDelayedDelete()
+ {
+ _delayed_delete_queue.clear();
+ }
+
+ std::unordered_set<ScriptRegistryInterface*> _registries;
+
+ std::vector<std::unique_ptr<DeleteableObjectBase>> _delayed_delete_queue;
+
+ std::unordered_map<
+ std::string /*script name*/,
+ std::string /*context*/
+ > _scriptnames_to_context;
+};
+
+#define sScriptRegistryCompositum ScriptRegistryCompositum::Instance()
+
+template<typename /*ScriptType*/, bool /*IsDatabaseBound*/>
+class SpecializedScriptRegistry;
+
// This is the global static registry of scripts.
-template<class TScript>
-class ScriptRegistry
+template<class ScriptType>
+class ScriptRegistry final
+ : public SpecializedScriptRegistry<
+ ScriptType, is_script_database_bound<ScriptType>::value>
+{
+ ScriptRegistry()
+ {
+ sScriptRegistryCompositum->Register(this);
+ }
+
+public:
+ static ScriptRegistry* Instance()
+ {
+ static ScriptRegistry instance;
+ return &instance;
+ }
+
+ void LogDuplicatedScriptPointerError(ScriptType const* first, ScriptType const* second)
+ {
+ // See if the script is using the same memory as another script. If this happens, it means that
+ // someone forgot to allocate new memory for a script.
+ TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
+ first->GetName().c_str(), second->GetName().c_str());
+ }
+};
+
+class ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHookBase() { }
+ virtual ~ScriptRegistrySwapHookBase() { }
+
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase&&) = delete;
+
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase&&) = delete;
+
+ /// Called before the actual context release happens
+ virtual void BeforeReleaseContext(std::string const& /*context*/) { }
+
+ /// Called before SwapContext
+ virtual void BeforeSwapContext(bool /*initialize*/) { }
+
+ /// Called before Unload
+ virtual void BeforeUnload() { }
+};
+
+template<typename ScriptType, typename Base>
+class ScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+};
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class UnsupportedScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ ASSERT(bounds.first == bounds.second);
+ }
+};
+
+/// This hook is responsible for swapping Creature and GameObject AI's
+template<typename ObjectType, typename ScriptType, typename Base>
+class CreatureGameObjectScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
{
+ template<typename W>
+ class AIFunctionMapWorker
+ {
public:
+ template<typename T>
+ AIFunctionMapWorker(T&& worker)
+ : _worker(std::forward<T>(worker)) { }
+
+ void Visit(std::unordered_map<ObjectGuid, ObjectType*>& objects)
+ {
+ _worker(objects);
+ }
+
+ template<typename O>
+ void Visit(std::unordered_map<ObjectGuid, O*>&) { }
- typedef std::map<uint32, TScript*> ScriptMap;
- typedef typename ScriptMap::iterator ScriptMapIterator;
+ private:
+ W _worker;
+ };
- // The actual list of scripts. This will be accessed concurrently, so it must not be modified
- // after server startup.
- static ScriptMap ScriptPointerList;
- static std::vector<TScript*> Scripts;
+ class AsyncCastHotswapEffectEvent : public BasicEvent
+ {
+ public:
+ explicit AsyncCastHotswapEffectEvent(Unit* owner) : owner_(owner) { }
- static void AddScript(TScript* const script, bool addToDeleteContainer = true)
+ bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
{
- ASSERT(script);
+ owner_->CastSpell(owner_, SPELL_HOTSWAP_VISUAL_SPELL_EFFECT, true);
+ return true;
+ }
+
+ private:
+ Unit* owner_;
+ };
+
+ // Hook which is called before a creature is swapped
+ static void UnloadStage1(Creature* creature)
+ {
+ creature->m_Events.KillAllEvents(true);
+
+ if (creature->IsCharmed())
+ creature->RemoveCharmedBy(nullptr);
+
+ ASSERT(!creature->IsCharmed(),
+ "There is a disabled AI which is still loaded.");
+
+ creature->AI()->EnterEvadeMode();
+ }
+
+ static void UnloadStage2(Creature* creature)
+ {
+ bool const destroyed = creature->AIM_Destroy();
+ ASSERT(destroyed,
+ "Destroying the AI should never fail here!");
+ (void)destroyed;
+
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+ }
+
+ // Hook which is called before a gameobject is swapped
+ static void UnloadStage1(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
- // See if the script is using the same memory as another script. If this happens, it means that
- // someone forgot to allocate new memory for a script.
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
+ static void UnloadStage2(GameObject* gameobject)
+ {
+ gameobject->AIM_Destroy();
+
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+ }
+
+ // Hook which is called after a creature was swapped
+ static void LoadStage1(Creature* creature)
+ {
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+
+ if (creature->IsAlive())
+ creature->ClearUnitState(UNIT_STATE_EVADE);
+
+ bool const created = creature->AIM_Initialize();
+ ASSERT(created,
+ "Creating the AI should never fail here!");
+ (void)created;
+ }
+
+ static void LoadStage2(Creature* creature)
+ {
+ if (!creature->IsAlive())
+ return;
+
+ creature->AI()->EnterEvadeMode();
+
+ // Cast a dummy visual spell asynchronously here to signal
+ // that the AI was hot swapped
+ creature->m_Events.AddEvent(new AsyncCastHotswapEffectEvent(creature),
+ creature->m_Events.CalculateTime(0));
+ }
+
+ // Hook which is called after a gameobject was swapped
+ static void LoadStage1(GameObject* gameobject)
+ {
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+
+ gameobject->AIM_Initialize();
+ }
+
+ static void LoadStage2(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ template<typename T>
+ void RunOverAllEntities(T fn)
+ {
+ auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects)
+ {
+ for (auto object : objects)
+ fn(object.second);
+ };
+
+ AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator));
+ TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> visitor(worker);
+
+ sMapMgr->DoForAllMaps([&](Map* map)
+ {
+ // Run the worker over all maps
+ visitor.Visit(map->GetObjectsStore());
+ });
+ }
+
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto ids_to_remove = static_cast<Base*>(this)->GetScriptIDsToRemove(context);
+
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_to_remove.find(object->GetScriptId()) != ids_to_remove.end())
{
- if (it->second == script)
- {
- TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
- script->GetName().c_str(), it->second->GetName().c_str());
+ UnloadStage1(object);
+ stage2.push_back(object);
+ }
+ });
- return;
+ for (auto object : stage2)
+ UnloadStage2(object);
+
+ // Add the new ids which are removed to the global ids to remove set
+ ids_removed_.insert(ids_to_remove.begin(), ids_to_remove.end());
+ }
+
+ void BeforeSwapContext(bool initialize) override
+ {
+ // Never swap creature or gameobject scripts when initializing
+ if (initialize)
+ return;
+
+ // Add the recently added scripts to the deleted scripts to replace
+ // default AI's with recently added core scripts.
+ ids_removed_.insert(static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().begin(),
+ static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().end());
+
+ std::vector<ObjectType*> remove;
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_removed_.find(object->GetScriptId()) != ids_removed_.end())
+ {
+ if (object->AI())
+ {
+ // Overwrite existing (default) AI's which are replaced by a new script
+ UnloadStage1(object);
+ remove.push_back(object);
}
+
+ stage2.push_back(object);
}
+ });
- AddScript(is_script_database_bound<TScript>{}, script);
- if (addToDeleteContainer)
- Scripts.push_back(script);
+ for (auto object : remove)
+ UnloadStage2(object);
+
+ for (auto object : stage2)
+ LoadStage1(object);
+
+ for (auto object : stage2)
+ LoadStage2(object);
+
+ ids_removed_.clear();
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(ids_removed_.empty());
+ }
+
+private:
+ std::unordered_set<uint32> ids_removed_;
+};
+
+// This hook is responsible for swapping CreatureAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<CreatureScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ Creature, CreatureScript, Base
+ > { };
+
+// This hook is responsible for swapping GameObjectAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<GameObjectScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ GameObject, GameObjectScript, Base
+ > { };
+
+/// This hook is responsible for swapping BattlegroundScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<BattlegroundScript, Base>
+ : public UnsupportedScriptRegistrySwapHooks<Base> { };
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class ScriptRegistrySwapHooks<OutdoorPvPScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if ((!swapped) && (bounds.first != bounds.second))
+ {
+ swapped = true;
+ sOutdoorPvPMgr->Die();
}
+ }
- // Gets a script by its ID (assigned by ObjectMgr).
- static TScript* GetScriptById(uint32 id)
+ void BeforeSwapContext(bool initialize) override
+ {
+ // Never swap outdoor pvp scripts when initializing
+ if ((!initialize) && swapped)
{
- ScriptMapIterator it = ScriptPointerList.find(id);
- if (it != ScriptPointerList.end())
- return it->second;
+ sOutdoorPvPMgr->InitOutdoorPvP();
+ swapped = false;
+ }
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping InstanceMapScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<InstanceMapScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ swapped = false;
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
- return NULL;
+/// This hook is responsible for swapping SpellScriptLoader's
+template<typename Base>
+class ScriptRegistrySwapHooks<SpellScriptLoader, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ if (swapped)
+ {
+ sObjectMgr->ValidateSpellScripts();
+ swapped = false;
}
+ }
- private:
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+// Database bound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, true>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename>
+ friend class UnsupportedScriptRegistrySwapHooks;
+
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+ template<typename, typename, typename>
+ friend class CreatureGameObjectScriptRegistrySwapHooks;
+
+public:
+ SpecializedScriptRegistry() { }
+
+ typedef std::unordered_map<
+ uint32 /*script id*/,
+ std::unique_ptr<ScriptType>
+ > ScriptStoreType;
+
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ _scripts.erase(itr->second);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ this->BeforeSwapContext(initialize);
+
+ _recently_added_ids.clear();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ ASSERT(_recently_added_ids.empty(),
+ "Recently added script ids should be empty here!");
- // Adds a database bound script
- static void AddScript(std::true_type, TScript* const script)
+ _scripts.clear();
+ _ids_of_contexts.clear();
+ }
+
+ // Adds a database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
+
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ // Get an ID for the script. An ID only exists if it's a script that is assigned in the database
+ // through a script name (or similar).
+ if (uint32 const id = sObjectMgr->GetScriptId(script->GetName()))
{
- // Get an ID for the script. An ID only exists if it's a script that is assigned in the database
- // through a script name (or similar).
- uint32 id = sObjectMgr->GetScriptId(script->GetName());
- if (id)
+ // Try to find an existing script.
+ for (auto const& stored_script : _scripts)
{
- // Try to find an existing script.
- bool existing = false;
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
- {
- // If the script names match...
- if (it->second->GetName() == script->GetName())
- {
- // ... It exists.
- existing = true;
- break;
- }
- }
-
- // If the script isn't assigned -> assign it!
- if (!existing)
- {
- ScriptPointerList[id] = script;
- sScriptMgr->IncrementScriptCount();
-
- #ifdef SCRIPTS
- UnusedScriptNamesContainer::iterator itr = std::lower_bound(UnusedScriptNames.begin(), UnusedScriptNames.end(), script->GetName());
- if (itr != UnusedScriptNames.end() && *itr == script->GetName())
- UnusedScriptNames.erase(itr);
- #endif
- }
- else
+ // If the script names match...
+ if (stored_script.second->GetName() == script->GetName())
{
// If the script is already assigned -> delete it!
- TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, so the script can't work.",
- script->GetName().c_str());
+ TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, "
+ "so the script can't work.", script->GetName().c_str());
- ABORT(); // Error that should be fixed ASAP.
+ // Error that should be fixed ASAP.
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ ABORT();
+ return;
}
}
- else
- {
- // The script uses a script name from database, but isn't assigned to anything.
- TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str());
- }
- }
- // Adds a non database bound script
- static void AddScript(std::false_type, TScript* const script)
+ // If the script isn't assigned -> assign it!
+ _scripts.insert(std::make_pair(id, std::move(script_ptr)));
+ _ids_of_contexts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), id));
+ _recently_added_ids.insert(id);
+
+ sScriptRegistryCompositum->SetScriptNameInContext(script->GetName(),
+ sScriptMgr->GetCurrentScriptContext());
+ }
+ else
{
- // We're dealing with a code-only script; just add it.
- ScriptPointerList[_scriptIdCounter++] = script;
- sScriptMgr->IncrementScriptCount();
+ // The script uses a script name from database, but isn't assigned to anything.
+ TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.",
+ script->GetName().c_str());
+
+ // Avoid calling "delete script;" because we are currently in the script constructor
+ // In a valid scenario this will not happen because every script has a name assigned in the database
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
}
+ }
- // Counter used for code-only scripts.
- static uint32 _scriptIdCounter;
+ // Gets a script by its ID (assigned by ObjectMgr).
+ ScriptType* GetScriptById(uint32 id)
+ {
+ auto const itr = _scripts.find(id);
+ if (itr != _scripts.end())
+ return itr->second.get();
+
+ return nullptr;
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
+
+protected:
+ // Returns the script id's which are registered to a certain context
+ std::unordered_set<uint32> GetScriptIDsToRemove(std::string const& context) const
+ {
+ // Create a set of all ids which are removed
+ std::unordered_set<uint32> scripts_to_remove;
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ scripts_to_remove.insert(itr->second);
+
+ return scripts_to_remove;
+ }
+
+ std::unordered_set<uint32> const& GetRecentlyAddedScriptIDs() const
+ {
+ return _recently_added_ids;
+ }
+
+private:
+ ScriptStoreType _scripts;
+
+ // Scripts of a specific context
+ std::unordered_multimap<std::string /*context*/, uint32 /*id*/> _ids_of_contexts;
+
+ // Script id's which were registered recently
+ std::unordered_set<uint32> _recently_added_ids;
+};
+
+/// This hook is responsible for swapping CommandScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<CommandScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& /*context*/) final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeUnload() final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+};
+
+// Database unbound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, false>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+public:
+ typedef std::unordered_multimap<std::string /*context*/, std::unique_ptr<ScriptType>> ScriptStoreType;
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ SpecializedScriptRegistry() { }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ _scripts.erase(context);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ this->BeforeSwapContext(initialize);
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ _scripts.clear();
+ }
+
+ // Adds a non database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
+
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ for (auto const& entry : _scripts)
+ if (entry.second.get() == script)
+ {
+ static_cast<ScriptRegistry<ScriptType>*>(this)->
+ LogDuplicatedScriptPointerError(script, entry.second.get());
+
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
+ }
+
+ // We're dealing with a code-only script, just add it.
+ _scripts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), std::move(script_ptr)));
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
+
+private:
+ ScriptStoreType _scripts;
};
// Utility macros to refer to the script registry.
-#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptMap
-#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptMapIterator
-#define SCR_REG_LST(T) ScriptRegistry<T>::ScriptPointerList
-#define SCR_REG_VEC(T) ScriptRegistry<T>::Scripts
+#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptStoreType
+#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptStoreIteratorType
+#define SCR_REG_LST(T) ScriptRegistry<T>::Instance()->GetScripts()
// Utility macros for looping over scripts.
#define FOR_SCRIPTS(T, C, E) \
if (SCR_REG_LST(T).empty()) \
return; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOR_SCRIPTS_RET(T, C, E, R) \
if (SCR_REG_LST(T).empty()) \
return R; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOREACH_SCRIPT(T) \
FOR_SCRIPTS(T, itr, end) \
- itr->second
+ itr->second
// Utility macros for finding specific scripts.
#define GET_SCRIPT(T, I, V) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return;
+
#define GET_SCRIPT_RET(T, I, V, R) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return R;
@@ -240,8 +948,18 @@ struct TSpellSummary
uint8 Effects; // set of enum SelectEffect
} *SpellSummary;
+ScriptObject::ScriptObject(const char* name) : _name(name)
+{
+ sScriptMgr->IncreaseScriptCount();
+}
+
+ScriptObject::~ScriptObject()
+{
+ sScriptMgr->DecreaseScriptCount();
+}
+
ScriptMgr::ScriptMgr()
- : _scriptCount(0), _scheduledScripts(0), _script_loader_callback(nullptr)
+ : _scriptCount(0), _script_loader_callback(nullptr)
{
}
@@ -255,6 +973,9 @@ ScriptMgr* ScriptMgr::instance()
void ScriptMgr::Initialize()
{
+ ASSERT(sSpellMgr->GetSpellInfo(SPELL_HOTSWAP_VISUAL_SPELL_EFFECT)
+ && "Reload hotswap spell effect for creatures isn't valid!");
+
uint32 oldMSTime = getMSTime();
LoadDatabase();
@@ -263,59 +984,85 @@ void ScriptMgr::Initialize()
FillSpellSummary();
+ // Load core scripts
+ SetScriptContext("___static___");
+
+ // SmartAI
AddSC_SmartScripts();
+ // LFGScripts
+ lfg::AddSC_LFGScripts();
+
+ // Load all static linked scripts through the script loader function.
ASSERT(_script_loader_callback,
"Script loader callback wasn't registered!");
-
_script_loader_callback();
-#ifdef SCRIPTS
- for (std::string const& scriptName : UnusedScriptNames)
+ // Initialize all dynamic scripts
+ // and finishes the context switch to do
+ // bulk loading
+ sScriptReloadMgr->Initialize();
+
+ // Loads all scripts from the current context
+ sScriptMgr->SwapScriptContext(true);
+
+ // Print unused script names.
+ std::unordered_set<std::string> unusedScriptNames(
+ sObjectMgr->GetAllScriptNames().begin(),
+ sObjectMgr->GetAllScriptNames().end());
+
+ // Remove the used scripts from the given container.
+ sScriptRegistryCompositum->RemoveUsedScriptsFromContainer(unusedScriptNames);
+
+ for (std::string const& scriptName : unusedScriptNames)
{
- TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, but no core script found!", scriptName.c_str());
+ // Avoid complaining about empty script names since the
+ // script name container contains a placeholder as the 0 element.
+ if (scriptName.empty())
+ continue;
+
+ TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, "
+ "but no core script found!", scriptName.c_str());
}
-#endif
- TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms",
+ GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ScriptMgr::SetScriptContext(std::string const& context)
+{
+ _currentContext = context;
+}
+
+void ScriptMgr::SwapScriptContext(bool initialize)
+{
+ sScriptRegistryCompositum->SwapContext(initialize);
+ _currentContext.clear();
+}
+
+void ScriptMgr::ReleaseScriptContext(std::string const& context)
+{
+ sScriptRegistryCompositum->ReleaseContext(context);
+}
+
+std::shared_ptr<ModuleReference>
+ ScriptMgr::AcquireModuleReferenceOfScriptName(std::string const& scriptname) const
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Returns the reference to the module of the given scriptname
+ return ScriptReloadMgr::AcquireModuleReferenceOfContext(
+ sScriptRegistryCompositum->GetScriptContextOfScriptName(scriptname));
+#else
+ (void)scriptname;
+ // Something went wrong when this function is used in
+ // a static linked context.
+ WPAbort();
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
void ScriptMgr::Unload()
{
- #define SCR_CLEAR(T) \
- for (T* scr : SCR_REG_VEC(T)) \
- delete scr; \
- SCR_REG_VEC(T).clear();
-
- // Clear scripts for every script type.
- SCR_CLEAR(SpellScriptLoader);
- SCR_CLEAR(ServerScript);
- SCR_CLEAR(WorldScript);
- SCR_CLEAR(FormulaScript);
- SCR_CLEAR(WorldMapScript);
- SCR_CLEAR(InstanceMapScript);
- SCR_CLEAR(BattlegroundMapScript);
- SCR_CLEAR(ItemScript);
- SCR_CLEAR(CreatureScript);
- SCR_CLEAR(GameObjectScript);
- SCR_CLEAR(AreaTriggerScript);
- SCR_CLEAR(BattlegroundScript);
- SCR_CLEAR(OutdoorPvPScript);
- SCR_CLEAR(CommandScript);
- SCR_CLEAR(WeatherScript);
- SCR_CLEAR(AuctionHouseScript);
- SCR_CLEAR(ConditionScript);
- SCR_CLEAR(VehicleScript);
- SCR_CLEAR(DynamicObjectScript);
- SCR_CLEAR(TransportScript);
- SCR_CLEAR(AchievementCriteriaScript);
- SCR_CLEAR(PlayerScript);
- SCR_CLEAR(AccountScript);
- SCR_CLEAR(GuildScript);
- SCR_CLEAR(GroupScript);
- SCR_CLEAR(UnitScript);
-
- #undef SCR_CLEAR
+ sScriptRegistryCompositum->Unload();
delete[] SpellSummary;
delete[] UnitAI::AISpellInfo;
@@ -413,38 +1160,22 @@ void ScriptMgr::FillSpellSummary()
}
}
-void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
+template<typename T, typename F>
+void CreateSpellOrAuraScripts(uint32 spellId, std::list<T*>& scriptVector, F&& extractor)
{
SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
+ // When the script is disabled continue with the next one
+ if (!itr->second.second)
continue;
- SpellScript* script = tmpscript->GetSpellScript();
-
- if (!script)
- continue;
-
- script->_Init(&tmpscript->GetName(), spellId);
-
- scriptVector.push_back(script);
- }
-}
-
-void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
-{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
-
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
+ SpellScriptLoader* tmpscript = sScriptMgr->GetSpellScriptLoader(itr->second.first);
if (!tmpscript)
continue;
- AuraScript* script = tmpscript->GetAuraScript();
+ T* script = (*tmpscript.*extractor)();
if (!script)
continue;
@@ -455,19 +1186,19 @@ void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& script
}
}
-void ScriptMgr::CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, SpellScriptsContainer::iterator> >& scriptVector)
+void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
- scriptVector.reserve(std::distance(bounds.first, bounds.second));
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetSpellScript);
+}
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
- continue;
+void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
+{
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetAuraScript);
+}
- scriptVector.push_back(std::make_pair(tmpscript, itr));
- }
+SpellScriptLoader* ScriptMgr::GetSpellScriptLoader(uint32 scriptId)
+{
+ return ScriptRegistry<SpellScriptLoader>::Instance()->GetScriptById(scriptId);
}
void ScriptMgr::OnNetworkStart()
@@ -1495,56 +2226,62 @@ void ScriptMgr::OnGroupDisband(Group* group)
void ScriptMgr::OnHeal(Unit* healer, Unit* reciever, uint32& gain)
{
FOREACH_SCRIPT(UnitScript)->OnHeal(healer, reciever, gain);
+ FOREACH_SCRIPT(PlayerScript)->OnHeal(healer, reciever, gain);
}
void ScriptMgr::OnDamage(Unit* attacker, Unit* victim, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->OnDamage(attacker, victim, damage);
+ FOREACH_SCRIPT(PlayerScript)->OnDamage(attacker, victim, damage);
}
void ScriptMgr::ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
}
void ScriptMgr::ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyMeleeDamage(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyMeleeDamage(target, attacker, damage);
}
void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifySpellDamageTaken(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifySpellDamageTaken(target, attacker, damage);
}
SpellScriptLoader::SpellScriptLoader(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<SpellScriptLoader>::AddScript(this);
+ ScriptRegistry<SpellScriptLoader>::Instance()->AddScript(this);
}
ServerScript::ServerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ServerScript>::AddScript(this);
+ ScriptRegistry<ServerScript>::Instance()->AddScript(this);
}
WorldScript::WorldScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WorldScript>::AddScript(this);
+ ScriptRegistry<WorldScript>::Instance()->AddScript(this);
}
FormulaScript::FormulaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<FormulaScript>::AddScript(this);
+ ScriptRegistry<FormulaScript>::Instance()->AddScript(this);
}
UnitScript::UnitScript(const char* name, bool addToScripts)
: ScriptObject(name)
{
- ScriptRegistry<UnitScript>::AddScript(this, addToScripts);
+ if (addToScripts)
+ ScriptRegistry<UnitScript>::Instance()->AddScript(this);
}
WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
@@ -1553,7 +2290,7 @@ WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsWorldMap())
TC_LOG_ERROR("scripts", "WorldMapScript for map %u is invalid.", mapId);
- ScriptRegistry<WorldMapScript>::AddScript(this);
+ ScriptRegistry<WorldMapScript>::Instance()->AddScript(this);
}
InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
@@ -1562,7 +2299,7 @@ InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsDungeon())
TC_LOG_ERROR("scripts", "InstanceMapScript for map %u is invalid.", mapId);
- ScriptRegistry<InstanceMapScript>::AddScript(this);
+ ScriptRegistry<InstanceMapScript>::Instance()->AddScript(this);
}
BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
@@ -1571,156 +2308,141 @@ BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsBattleground())
TC_LOG_ERROR("scripts", "BattlegroundMapScript for map %u is invalid.", mapId);
- ScriptRegistry<BattlegroundMapScript>::AddScript(this);
+ ScriptRegistry<BattlegroundMapScript>::Instance()->AddScript(this);
}
ItemScript::ItemScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ItemScript>::AddScript(this);
+ ScriptRegistry<ItemScript>::Instance()->AddScript(this);
}
CreatureScript::CreatureScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<CreatureScript>::AddScript(this);
+ ScriptRegistry<CreatureScript>::Instance()->AddScript(this);
}
GameObjectScript::GameObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GameObjectScript>::AddScript(this);
+ ScriptRegistry<GameObjectScript>::Instance()->AddScript(this);
}
AreaTriggerScript::AreaTriggerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AreaTriggerScript>::AddScript(this);
+ ScriptRegistry<AreaTriggerScript>::Instance()->AddScript(this);
}
BattlegroundScript::BattlegroundScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<BattlegroundScript>::AddScript(this);
+ ScriptRegistry<BattlegroundScript>::Instance()->AddScript(this);
}
OutdoorPvPScript::OutdoorPvPScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<OutdoorPvPScript>::AddScript(this);
+ ScriptRegistry<OutdoorPvPScript>::Instance()->AddScript(this);
}
CommandScript::CommandScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<CommandScript>::AddScript(this);
+ ScriptRegistry<CommandScript>::Instance()->AddScript(this);
}
WeatherScript::WeatherScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WeatherScript>::AddScript(this);
+ ScriptRegistry<WeatherScript>::Instance()->AddScript(this);
}
AuctionHouseScript::AuctionHouseScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AuctionHouseScript>::AddScript(this);
+ ScriptRegistry<AuctionHouseScript>::Instance()->AddScript(this);
}
ConditionScript::ConditionScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ConditionScript>::AddScript(this);
+ ScriptRegistry<ConditionScript>::Instance()->AddScript(this);
}
VehicleScript::VehicleScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<VehicleScript>::AddScript(this);
+ ScriptRegistry<VehicleScript>::Instance()->AddScript(this);
}
DynamicObjectScript::DynamicObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<DynamicObjectScript>::AddScript(this);
+ ScriptRegistry<DynamicObjectScript>::Instance()->AddScript(this);
}
TransportScript::TransportScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<TransportScript>::AddScript(this);
+ ScriptRegistry<TransportScript>::Instance()->AddScript(this);
}
AchievementCriteriaScript::AchievementCriteriaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AchievementCriteriaScript>::AddScript(this);
+ ScriptRegistry<AchievementCriteriaScript>::Instance()->AddScript(this);
}
PlayerScript::PlayerScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<PlayerScript>::AddScript(this);
+ ScriptRegistry<PlayerScript>::Instance()->AddScript(this);
}
AccountScript::AccountScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AccountScript>::AddScript(this);
+ ScriptRegistry<AccountScript>::Instance()->AddScript(this);
}
GuildScript::GuildScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GuildScript>::AddScript(this);
+ ScriptRegistry<GuildScript>::Instance()->AddScript(this);
}
GroupScript::GroupScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GroupScript>::AddScript(this);
+ ScriptRegistry<GroupScript>::Instance()->AddScript(this);
}
-// Instantiate static members of ScriptRegistry.
-template<class TScript> std::map<uint32, TScript*> ScriptRegistry<TScript>::ScriptPointerList;
-template<class TScript> std::vector<TScript*> ScriptRegistry<TScript>::Scripts;
-template<class TScript> uint32 ScriptRegistry<TScript>::_scriptIdCounter = 0;
-
// Specialize for each script type class like so:
-template class ScriptRegistry<SpellScriptLoader>;
-template class ScriptRegistry<ServerScript>;
-template class ScriptRegistry<WorldScript>;
-template class ScriptRegistry<FormulaScript>;
-template class ScriptRegistry<WorldMapScript>;
-template class ScriptRegistry<InstanceMapScript>;
-template class ScriptRegistry<BattlegroundMapScript>;
-template class ScriptRegistry<ItemScript>;
-template class ScriptRegistry<CreatureScript>;
-template class ScriptRegistry<GameObjectScript>;
-template class ScriptRegistry<AreaTriggerScript>;
-template class ScriptRegistry<BattlegroundScript>;
-template class ScriptRegistry<OutdoorPvPScript>;
-template class ScriptRegistry<CommandScript>;
-template class ScriptRegistry<WeatherScript>;
-template class ScriptRegistry<AuctionHouseScript>;
-template class ScriptRegistry<ConditionScript>;
-template class ScriptRegistry<VehicleScript>;
-template class ScriptRegistry<DynamicObjectScript>;
-template class ScriptRegistry<TransportScript>;
-template class ScriptRegistry<AchievementCriteriaScript>;
-template class ScriptRegistry<PlayerScript>;
-template class ScriptRegistry<GuildScript>;
-template class ScriptRegistry<GroupScript>;
-template class ScriptRegistry<UnitScript>;
-template class ScriptRegistry<AccountScript>;
-
-// Undefine utility macros.
-#undef GET_SCRIPT_RET
-#undef GET_SCRIPT
-#undef FOREACH_SCRIPT
-#undef FOR_SCRIPTS_RET
-#undef FOR_SCRIPTS
-#undef SCR_REG_LST
-#undef SCR_REG_ITR
-#undef SCR_REG_MAP
+template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
+template class TC_GAME_API ScriptRegistry<ServerScript>;
+template class TC_GAME_API ScriptRegistry<WorldScript>;
+template class TC_GAME_API ScriptRegistry<FormulaScript>;
+template class TC_GAME_API ScriptRegistry<WorldMapScript>;
+template class TC_GAME_API ScriptRegistry<InstanceMapScript>;
+template class TC_GAME_API ScriptRegistry<BattlegroundMapScript>;
+template class TC_GAME_API ScriptRegistry<ItemScript>;
+template class TC_GAME_API ScriptRegistry<CreatureScript>;
+template class TC_GAME_API ScriptRegistry<GameObjectScript>;
+template class TC_GAME_API ScriptRegistry<AreaTriggerScript>;
+template class TC_GAME_API ScriptRegistry<BattlegroundScript>;
+template class TC_GAME_API ScriptRegistry<OutdoorPvPScript>;
+template class TC_GAME_API ScriptRegistry<CommandScript>;
+template class TC_GAME_API ScriptRegistry<WeatherScript>;
+template class TC_GAME_API ScriptRegistry<AuctionHouseScript>;
+template class TC_GAME_API ScriptRegistry<ConditionScript>;
+template class TC_GAME_API ScriptRegistry<VehicleScript>;
+template class TC_GAME_API ScriptRegistry<DynamicObjectScript>;
+template class TC_GAME_API ScriptRegistry<TransportScript>;
+template class TC_GAME_API ScriptRegistry<AchievementCriteriaScript>;
+template class TC_GAME_API ScriptRegistry<PlayerScript>;
+template class TC_GAME_API ScriptRegistry<GuildScript>;
+template class TC_GAME_API ScriptRegistry<GroupScript>;
+template class TC_GAME_API ScriptRegistry<UnitScript>;
+template class TC_GAME_API ScriptRegistry<AccountScript>;
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index c22ffaf05a4..cc1b65fa593 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -46,6 +46,7 @@ class InstanceMap;
class InstanceScript;
class Item;
class Map;
+class ModuleReference;
class OutdoorPvP;
class Player;
class Quest;
@@ -147,7 +148,7 @@ struct OutdoorPvPData;
event on all registered scripts of that type.
*/
-class ScriptObject
+class TC_GAME_API ScriptObject
{
friend class ScriptMgr;
@@ -157,14 +158,8 @@ class ScriptObject
protected:
- ScriptObject(const char* name)
- : _name(name)
- {
- }
-
- virtual ~ScriptObject()
- {
- }
+ ScriptObject(const char* name);
+ virtual ~ScriptObject();
private:
@@ -186,7 +181,7 @@ template<class TObject> class UpdatableScript
virtual void OnUpdate(TObject* /*obj*/, uint32 /*diff*/) { }
};
-class SpellScriptLoader : public ScriptObject
+class TC_GAME_API SpellScriptLoader : public ScriptObject
{
protected:
@@ -201,7 +196,7 @@ class SpellScriptLoader : public ScriptObject
virtual AuraScript* GetAuraScript() const { return NULL; }
};
-class ServerScript : public ScriptObject
+class TC_GAME_API ServerScript : public ScriptObject
{
protected:
@@ -231,7 +226,7 @@ class ServerScript : public ScriptObject
virtual void OnPacketReceive(WorldSession* /*session*/, WorldPacket& /*packet*/) { }
};
-class WorldScript : public ScriptObject
+class TC_GAME_API WorldScript : public ScriptObject
{
protected:
@@ -264,7 +259,7 @@ class WorldScript : public ScriptObject
virtual void OnShutdown() { }
};
-class FormulaScript : public ScriptObject
+class TC_GAME_API FormulaScript : public ScriptObject
{
protected:
@@ -331,14 +326,15 @@ template<class TMap> class MapScript : public UpdatableScript<TMap>
virtual void OnPlayerLeave(TMap* /*map*/, Player* /*player*/) { }
};
-class WorldMapScript : public ScriptObject, public MapScript<Map>
+class TC_GAME_API WorldMapScript : public ScriptObject, public MapScript<Map>
{
protected:
WorldMapScript(const char* name, uint32 mapId);
};
-class InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
+class TC_GAME_API InstanceMapScript
+ : public ScriptObject, public MapScript<InstanceMap>
{
protected:
@@ -350,14 +346,14 @@ class InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
virtual InstanceScript* GetInstanceScript(InstanceMap* /*map*/) const { return NULL; }
};
-class BattlegroundMapScript : public ScriptObject, public MapScript<BattlegroundMap>
+class TC_GAME_API BattlegroundMapScript : public ScriptObject, public MapScript<BattlegroundMap>
{
protected:
BattlegroundMapScript(const char* name, uint32 mapId);
};
-class ItemScript : public ScriptObject
+class TC_GAME_API ItemScript : public ScriptObject
{
protected:
@@ -381,7 +377,7 @@ class ItemScript : public ScriptObject
virtual bool OnRemove(Player* /*player*/, Item* /*item*/) { return false; }
};
-class UnitScript : public ScriptObject
+class TC_GAME_API UnitScript : public ScriptObject
{
protected:
@@ -404,7 +400,7 @@ class UnitScript : public ScriptObject
virtual void ModifySpellDamageTaken(Unit* /*target*/, Unit* /*attacker*/, int32& /*damage*/) { }
};
-class CreatureScript : public UnitScript, public UpdatableScript<Creature>
+class TC_GAME_API CreatureScript : public UnitScript, public UpdatableScript<Creature>
{
protected:
@@ -440,7 +436,7 @@ class CreatureScript : public UnitScript, public UpdatableScript<Creature>
virtual CreatureAI* GetAI(Creature* /*creature*/) const { return NULL; }
};
-class GameObjectScript : public ScriptObject, public UpdatableScript<GameObject>
+class TC_GAME_API GameObjectScript : public ScriptObject, public UpdatableScript<GameObject>
{
protected:
@@ -485,7 +481,7 @@ class GameObjectScript : public ScriptObject, public UpdatableScript<GameObject>
virtual GameObjectAI* GetAI(GameObject* /*go*/) const { return NULL; }
};
-class AreaTriggerScript : public ScriptObject
+class TC_GAME_API AreaTriggerScript : public ScriptObject
{
protected:
@@ -497,7 +493,7 @@ class AreaTriggerScript : public ScriptObject
virtual bool OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) { return false; }
};
-class BattlegroundScript : public ScriptObject
+class TC_GAME_API BattlegroundScript : public ScriptObject
{
protected:
@@ -509,7 +505,7 @@ class BattlegroundScript : public ScriptObject
virtual Battleground* GetBattleground() const = 0;
};
-class OutdoorPvPScript : public ScriptObject
+class TC_GAME_API OutdoorPvPScript : public ScriptObject
{
protected:
@@ -521,7 +517,7 @@ class OutdoorPvPScript : public ScriptObject
virtual OutdoorPvP* GetOutdoorPvP() const = 0;
};
-class CommandScript : public ScriptObject
+class TC_GAME_API CommandScript : public ScriptObject
{
protected:
@@ -533,7 +529,7 @@ class CommandScript : public ScriptObject
virtual std::vector<ChatCommand> GetCommands() const = 0;
};
-class WeatherScript : public ScriptObject, public UpdatableScript<Weather>
+class TC_GAME_API WeatherScript : public ScriptObject, public UpdatableScript<Weather>
{
protected:
@@ -545,7 +541,7 @@ class WeatherScript : public ScriptObject, public UpdatableScript<Weather>
virtual void OnChange(Weather* /*weather*/, WeatherState /*state*/, float /*grade*/) { }
};
-class AuctionHouseScript : public ScriptObject
+class TC_GAME_API AuctionHouseScript : public ScriptObject
{
protected:
@@ -566,7 +562,7 @@ class AuctionHouseScript : public ScriptObject
virtual void OnAuctionExpire(AuctionHouseObject* /*ah*/, AuctionEntry* /*entry*/) { }
};
-class ConditionScript : public ScriptObject
+class TC_GAME_API ConditionScript : public ScriptObject
{
protected:
@@ -578,7 +574,7 @@ class ConditionScript : public ScriptObject
virtual bool OnConditionCheck(Condition const* /*condition*/, ConditionSourceInfo& /*sourceInfo*/) { return true; }
};
-class VehicleScript : public ScriptObject
+class TC_GAME_API VehicleScript : public ScriptObject
{
protected:
@@ -605,14 +601,14 @@ class VehicleScript : public ScriptObject
virtual void OnRemovePassenger(Vehicle* /*veh*/, Unit* /*passenger*/) { }
};
-class DynamicObjectScript : public ScriptObject, public UpdatableScript<DynamicObject>
+class TC_GAME_API DynamicObjectScript : public ScriptObject, public UpdatableScript<DynamicObject>
{
protected:
DynamicObjectScript(const char* name);
};
-class TransportScript : public ScriptObject, public UpdatableScript<Transport>
+class TC_GAME_API TransportScript : public ScriptObject, public UpdatableScript<Transport>
{
protected:
@@ -633,7 +629,7 @@ class TransportScript : public ScriptObject, public UpdatableScript<Transport>
virtual void OnRelocate(Transport* /*transport*/, uint32 /*waypointId*/, uint32 /*mapId*/, float /*x*/, float /*y*/, float /*z*/) { }
};
-class AchievementCriteriaScript : public ScriptObject
+class TC_GAME_API AchievementCriteriaScript : public ScriptObject
{
protected:
@@ -645,7 +641,7 @@ class AchievementCriteriaScript : public ScriptObject
virtual bool OnCheck(Player* source, Unit* target) = 0;
};
-class PlayerScript : public UnitScript
+class TC_GAME_API PlayerScript : public UnitScript
{
protected:
@@ -742,7 +738,7 @@ class PlayerScript : public UnitScript
virtual void OnQuestStatusChange(Player* /*player*/, uint32 /*questId*/, QuestStatus /*status*/) { }
};
-class AccountScript : public ScriptObject
+class TC_GAME_API AccountScript : public ScriptObject
{
protected:
@@ -769,7 +765,7 @@ class AccountScript : public ScriptObject
virtual void OnFailedPasswordChange(uint32 /*accountId*/) {}
};
-class GuildScript : public ScriptObject
+class TC_GAME_API GuildScript : public ScriptObject
{
protected:
@@ -810,7 +806,7 @@ class GuildScript : public ScriptObject
virtual void OnBankEvent(Guild* /*guild*/, uint8 /*eventType*/, uint8 /*tabId*/, ObjectGuid::LowType /*playerGuid*/, uint32 /*itemOrMoney*/, uint16 /*itemStackCount*/, uint8 /*destTabId*/) { }
};
-class GroupScript : public ScriptObject
+class TC_GAME_API GroupScript : public ScriptObject
{
protected:
@@ -834,17 +830,8 @@ class GroupScript : public ScriptObject
virtual void OnDisband(Group* /*group*/) { }
};
-// Placed here due to ScriptRegistry::AddScript dependency.
-#define sScriptMgr ScriptMgr::instance()
-
-// namespace
-// {
- typedef std::list<std::string> UnusedScriptNamesContainer;
- extern UnusedScriptNamesContainer UnusedScriptNames;
-// }
-
// Manages registration, loading, and execution of scripts.
-class ScriptMgr
+class TC_GAME_API ScriptMgr
{
friend class ScriptObject;
@@ -852,16 +839,17 @@ class ScriptMgr
ScriptMgr();
virtual ~ScriptMgr();
+ void FillSpellSummary();
+ void LoadDatabase();
+
+ void IncreaseScriptCount() { ++_scriptCount; }
+ void DecreaseScriptCount() { --_scriptCount; }
+
public: /* Initialization */
static ScriptMgr* instance();
void Initialize();
- void LoadDatabase();
- void FillSpellSummary();
-
- const char* ScriptsVersion() const { return "Integrated Trinity Scripts"; }
- void IncrementScriptCount() { ++_scriptCount; }
uint32 GetScriptCount() const { return _scriptCount; }
typedef void(*ScriptLoaderCallbackType)();
@@ -873,6 +861,27 @@ class ScriptMgr
_script_loader_callback = script_loader_callback;
}
+ public: /* Script contexts */
+ /// Set the current script context, which allows the ScriptMgr
+ /// to accept new scripts in this context.
+ /// Requires a SwapScriptContext() call afterwards to load the new scripts.
+ void SetScriptContext(std::string const& context);
+ /// Returns the current script context.
+ std::string const& GetCurrentScriptContext() const { return _currentContext; }
+ /// Releases all scripts associated with the given script context immediately.
+ /// Requires a SwapScriptContext() call afterwards to finish the unloading.
+ void ReleaseScriptContext(std::string const& context);
+ /// Executes all changed introduced by SetScriptContext and ReleaseScriptContext.
+ /// It is possible to combine multiple SetScriptContext and ReleaseScriptContext
+ /// calls for better performance (bulk changes).
+ void SwapScriptContext(bool initialize = false);
+
+ /// 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.
+ std::shared_ptr<ModuleReference> AcquireModuleReferenceOfScriptName(
+ std::string const& scriptname) const;
+
public: /* Unloading */
void Unload();
@@ -881,7 +890,7 @@ class ScriptMgr
void CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector);
void CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector);
- void CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, std::multimap<uint32, uint32>::iterator> >& scriptVector);
+ SpellScriptLoader* GetSpellScriptLoader(uint32 scriptId);
public: /* ServerScript */
@@ -1094,21 +1103,14 @@ class ScriptMgr
void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage);
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage);
- public: /* Scheduled scripts */
-
- uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; }
- bool IsScriptScheduled() const { return _scheduledScripts > 0; }
-
private:
-
uint32 _scriptCount;
- //atomic op counter for active scripts amount
- std::atomic<uint32> _scheduledScripts;
-
ScriptLoaderCallbackType _script_loader_callback;
+
+ std::string _currentContext;
};
+#define sScriptMgr ScriptMgr::instance()
+
#endif
diff --git a/src/server/game/Scripting/ScriptReloadMgr.cpp b/src/server/game/Scripting/ScriptReloadMgr.cpp
new file mode 100644
index 00000000000..d13fa9c30f0
--- /dev/null
+++ b/src/server/game/Scripting/ScriptReloadMgr.cpp
@@ -0,0 +1,1609 @@
+/*
+ * 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 "ScriptReloadMgr.h"
+#include "Errors.h"
+
+#ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
+// This method should never be called
+std::shared_ptr<ModuleReference>
+ ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& /*context*/)
+{
+ WPAbort();
+}
+
+// Returns the empty implemented ScriptReloadMgr
+ScriptReloadMgr* ScriptReloadMgr::instance()
+{
+ static ScriptReloadMgr instance;
+ return &instance;
+}
+
+#else
+
+#include <algorithm>
+#include <regex>
+#include <vector>
+#include <future>
+#include <memory>
+#include <fstream>
+#include <type_traits>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "efsw/efsw.hpp"
+
+#include "Log.h"
+#include "Config.h"
+#include "BuiltInConfig.h"
+#include "ScriptMgr.h"
+#include "SHA1.h"
+#include "StartProcess.h"
+#include "MPSCQueue.h"
+#include "GitRevision.h"
+
+namespace fs = boost::filesystem;
+
+#ifdef _WIN32
+ #include <windows.h>
+ #define HOTSWAP_PLATFORM_REQUIRES_CACHING
+#else // Posix
+ #include <dlfcn.h>
+ // #define HOTSWAP_PLATFORM_REQUIRES_CACHING
+#endif
+
+// Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr
+// in this compilation unit.
+#undef sScriptReloadMgr
+#define sScriptReloadMgr static_cast<HotSwapScriptReloadMgr*>(ScriptReloadMgr::instance())
+
+// Returns "" on Windows and "lib" on posix.
+static char const* GetSharedLibraryPrefix()
+{
+#ifdef _WIN32
+ return "";
+#else // Posix
+ return "lib";
+#endif
+}
+
+// Returns "dll" on Windows and "so" on posix.
+static char const* GetSharedLibraryExtension()
+{
+#ifdef _WIN32
+ return "dll";
+#else // Posix
+ return "so";
+#endif
+}
+
+#ifdef _WIN32
+typedef HMODULE HandleType;
+#else // Posix
+typedef void* HandleType;
+#endif
+
+static fs::path GetDirectoryOfExecutable()
+{
+ ASSERT((!sConfigMgr->GetArguments().empty()),
+ "Expected the arguments to contain at least 1 element!");
+
+ fs::path path(sConfigMgr->GetArguments()[0]);
+ if (path.is_absolute())
+ return path.parent_path();
+ else
+ return fs::absolute(path).parent_path();
+}
+
+class SharedLibraryUnloader
+{
+public:
+ explicit SharedLibraryUnloader(fs::path path)
+ : path_(std::move(path)) { }
+ SharedLibraryUnloader(fs::path path, Optional<fs::path> cache_path)
+ : path_(std::move(path)), cache_path_(std::move(cache_path)) { }
+
+ void operator() (HandleType handle) const
+ {
+ // Unload the associated shared library.
+#ifdef _WIN32
+ bool success = (FreeLibrary(handle) != 0);
+#else // Posix
+ bool success = (dlclose(handle) == 0);
+#endif
+
+ if (!success)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".",
+ path_.generic_string().c_str());
+
+ return;
+ }
+
+ /// When the shared library was cached delete it's shared version
+ if (cache_path_)
+ {
+ boost::system::error_code error;
+ if (!fs::remove(*cache_path_, error))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to delete the cached shared library \"%s\" (%s).",
+ cache_path_->generic_string().c_str(), error.message().c_str());
+
+ return;
+ }
+
+ TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\" "
+ "and deleted it's cached version at \"%s\"",
+ path_.generic_string().c_str(), cache_path_->generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\".",
+ path_.generic_string().c_str());
+ }
+ }
+
+private:
+ fs::path const path_;
+ Optional<fs::path> const cache_path_;
+};
+
+typedef std::unique_ptr<typename std::remove_pointer<HandleType>::type, SharedLibraryUnloader> HandleHolder;
+
+typedef char const* (*GetScriptModuleRevisionHashType)();
+typedef void (*AddScriptsType)();
+typedef char const* (*GetScriptModuleType)();
+typedef char const* (*GetBuildDirectiveType)();
+
+class ScriptModule
+ : public ModuleReference
+{
+public:
+ explicit ScriptModule(HandleHolder handle, GetScriptModuleRevisionHashType getScriptModuleRevisionHash,
+ AddScriptsType addScripts, GetScriptModuleType getScriptModule,
+ GetBuildDirectiveType getBuildDirective, fs::path const& path)
+ : _handle(std::forward<HandleHolder>(handle)), _getScriptModuleRevisionHash(getScriptModuleRevisionHash),
+ _addScripts(addScripts), _getScriptModule(getScriptModule),
+ _getBuildDirective(getBuildDirective), _path(path) { }
+
+ ScriptModule(ScriptModule const&) = delete;
+ ScriptModule(ScriptModule&& right) = delete;
+
+ ScriptModule& operator= (ScriptModule const&) = delete;
+ ScriptModule& operator= (ScriptModule&& right) = delete;
+
+ static Optional<std::shared_ptr<ScriptModule>>
+ CreateFromPath(fs::path const& path, Optional<fs::path> cache_path);
+
+ char const* GetScriptModuleRevisionHash() const override
+ {
+ return _getScriptModuleRevisionHash();
+ }
+
+ void AddScripts() const
+ {
+ return _addScripts();
+ }
+
+ char const* GetScriptModule() const override
+ {
+ return _getScriptModule();
+ }
+
+ char const* GetBuildDirective() const
+ {
+ return _getBuildDirective();
+ }
+
+ fs::path const& GetModulePath() const override
+ {
+ return _path;
+ }
+
+private:
+ HandleHolder _handle;
+
+ GetScriptModuleRevisionHashType _getScriptModuleRevisionHash;
+ AddScriptsType _addScripts;
+ GetScriptModuleType _getScriptModule;
+ GetBuildDirectiveType _getBuildDirective;
+
+ fs::path _path;
+};
+
+template<typename Fn>
+static bool GetFunctionFromSharedLibrary(HandleType handle, std::string const& name, Fn& fn)
+{
+#ifdef _WIN32
+ fn = reinterpret_cast<Fn>(GetProcAddress(handle, name.c_str()));
+#else // Posix
+ fn = reinterpret_cast<Fn>(dlsym(handle, name.c_str()));
+#endif
+ return fn != nullptr;
+}
+
+// Load a shared library from the given path.
+Optional<std::shared_ptr<ScriptModule>>
+ ScriptModule::CreateFromPath(fs::path const& path, Optional<fs::path> cache_path)
+{
+ auto const load_path = [&] () -> fs::path {
+ if (cache_path)
+ return *cache_path;
+ else
+ return path;
+ }();
+
+#ifdef _WIN32
+ HandleType handle = LoadLibrary(load_path.generic_string().c_str());
+#else // Posix
+ HandleType handle = dlopen(load_path.generic_string().c_str(), RTLD_LAZY);
+#endif
+
+ if (!handle)
+ {
+ if (cache_path)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Could not dynamic load the shared library \"%s\" "
+ "(the library is cached at %s)",
+ path.generic_string().c_str(), cache_path->generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Could not dynamic load the shared library \"%s\".",
+ path.generic_string().c_str());
+ }
+
+ return boost::none;
+ }
+
+ // Use RAII to release the library on failure.
+ HandleHolder holder(handle, SharedLibraryUnloader(path, std::move(cache_path)));
+
+ GetScriptModuleRevisionHashType getScriptModuleRevisionHash;
+ AddScriptsType addScripts;
+ GetScriptModuleType getScriptModule;
+ GetBuildDirectiveType getBuildDirective;
+
+ if (GetFunctionFromSharedLibrary(handle, "GetScriptModuleRevisionHash", getScriptModuleRevisionHash) &&
+ GetFunctionFromSharedLibrary(handle, "AddScripts", addScripts) &&
+ GetFunctionFromSharedLibrary(handle, "GetScriptModule", getScriptModule) &&
+ GetFunctionFromSharedLibrary(handle, "GetBuildDirective", getBuildDirective))
+ return std::make_shared<ScriptModule>(std::move(holder), getScriptModuleRevisionHash,
+ addScripts, getScriptModule, getBuildDirective, path);
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Could not extract all required functions from the shared library \"%s\"!",
+ path.generic_string().c_str());
+
+ return boost::none;
+ }
+}
+
+static bool HasValidScriptModuleName(std::string const& name)
+{
+ // Detects scripts_NAME.dll's / .so's
+ static std::regex const regex(
+ Trinity::StringFormat("^%s[sS]cripts_[a-zA-Z0-9_]+\\.%s$",
+ GetSharedLibraryPrefix(),
+ GetSharedLibraryExtension()));
+
+ return std::regex_match(name, regex);
+}
+
+/// File watcher responsible for watching shared libraries
+class LibraryUpdateListener : public efsw::FileWatchListener
+{
+public:
+ LibraryUpdateListener() { }
+ virtual ~LibraryUpdateListener() { }
+
+ void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename = "") final override;
+};
+
+static LibraryUpdateListener libraryUpdateListener;
+
+/// File watcher responsible for watching source files
+class SourceUpdateListener : public efsw::FileWatchListener
+{
+ fs::path const path_;
+
+ std::string const script_module_name_;
+
+ efsw::WatchID const watcher_id_;
+
+public:
+ explicit SourceUpdateListener(fs::path path, std::string script_module_name);
+
+ virtual ~SourceUpdateListener();
+
+ void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename = "") final override;
+};
+
+namespace std
+{
+ template <>
+ struct hash<fs::path>
+ {
+ hash<string> hasher;
+
+ std::size_t operator()(fs::path const& key) const
+ {
+ return hasher(key.generic_string());
+ }
+ };
+}
+
+/// Invokes a synchronous CMake process with the given arguments
+template<typename... T>
+static int InvokeCMakeCommand(T&&... args)
+{
+ auto const executable = BuiltInConfig::GetCMakeCommand();
+ return Trinity::StartProcess(executable, {
+ executable,
+ std::forward<T>(args)...
+ }, "scripts.hotswap");
+}
+
+/// Invokes an asynchronous CMake process with the given arguments
+template<typename... T>
+static std::shared_ptr<Trinity::AsyncProcessResult> InvokeAsyncCMakeCommand(T&&... args)
+{
+ auto const executable = BuiltInConfig::GetCMakeCommand();
+ return Trinity::StartAsyncProcess(executable, {
+ executable,
+ std::forward<T>(args)...
+ }, "scripts.hotswap");
+}
+
+/// Calculates the C++ project name of the given module which is
+/// the lowercase string of scripts_${module}.
+static std::string CalculateScriptModuleProjectName(std::string const& module)
+{
+ std::string module_project = "scripts_" + module;
+ std::transform(module_project.begin(), module_project.end(),
+ module_project.begin(), ::tolower);
+
+ return module_project;
+}
+
+/// Returns false when there isn't any attached debugger to the process which
+/// could block the rebuild of new shared libraries.
+static bool IsDebuggerBlockingRebuild()
+{
+#ifdef _WIN32
+ if (IsDebuggerPresent())
+ return true;
+#endif
+ return false;
+}
+
+/// ScriptReloadMgr which is used when dynamic linking is enabled
+///
+/// This class manages shared library loading/unloading through watching
+/// the script module directory. Loaded shared libraries are mirrored
+/// into a cache subdirectory to allow lazy unloading as long as
+/// the shared library is still used which is useful for scripts
+/// which can't be instantly replaced like spells or instances.
+/// Several modules which reference different versions can be kept loaded
+/// to serve scripts of different versions to entities and spells.
+///
+/// Also this class invokes rebuilds as soon as the source of loaded
+/// scripts change and installs the modules correctly through CMake.
+class HotSwapScriptReloadMgr final
+ : public ScriptReloadMgr
+{
+ friend class ScriptReloadMgr;
+ friend class SourceUpdateListener;
+
+ /// Reflects a queued change on a shared library or shared library
+ /// which is waiting for processing
+ enum class ChangeStateRequest : uint8
+ {
+ CHANGE_REQUEST_ADDED,
+ CHANGE_REQUEST_MODIFIED,
+ CHANGE_REQUEST_REMOVED
+ };
+
+ /// Reflects a running job of an invoked asynchronous external process
+ enum class BuildJobType : uint8
+ {
+ BUILD_JOB_NONE,
+ BUILD_JOB_RERUN_CMAKE,
+ BUILD_JOB_COMPILE,
+ BUILD_JOB_INSTALL,
+ };
+
+ // Represents a job which was invoked through a source or shared library change
+ class BuildJob
+ {
+ // Script module which is processed in the current running job
+ std::string script_module_name_;
+ // The C++ project name of the module which is processed
+ std::string script_module_project_name_;
+ // The build directive of the current module which is processed
+ // like "Release" or "Debug". The build directive from the
+ // previous same module is used if there was any.
+ std::string script_module_build_directive_;
+ // The time where the build job started
+ uint32 start_time_;
+
+ // Type of the current running job
+ BuildJobType type_;
+ // The async process result of the current job
+ std::shared_ptr<Trinity::AsyncProcessResult> async_result_;
+
+ public:
+ explicit BuildJob(std::string script_module_name, std::string script_module_project_name,
+ std::string script_module_build_directive)
+ : script_module_name_(std::move(script_module_name)),
+ script_module_project_name_(std::move(script_module_project_name)),
+ script_module_build_directive_(std::move(script_module_build_directive)),
+ start_time_(getMSTime()), type_(BuildJobType::BUILD_JOB_NONE) { }
+
+ bool IsValid() const
+ {
+ return type_ != BuildJobType::BUILD_JOB_NONE;
+ }
+
+ std::string const& GetModuleName() const { return script_module_name_; }
+
+ std::string const& GetProjectName() const { return script_module_project_name_; }
+
+ std::string const& GetBuildDirective() const { return script_module_build_directive_; }
+
+ uint32 GetTimeFromStart() const { return GetMSTimeDiffToNow(start_time_); }
+
+ BuildJobType GetType() const { return type_; }
+
+ std::shared_ptr<Trinity::AsyncProcessResult> const& GetProcess() const
+ {
+ ASSERT(async_result_, "Tried to access an empty process handle!");
+ return async_result_;
+ }
+
+ /// Updates the current running job with the given type and async result
+ void UpdateCurrentJob(BuildJobType type,
+ std::shared_ptr<Trinity::AsyncProcessResult> async_result)
+ {
+ ASSERT(type != BuildJobType::BUILD_JOB_NONE, "None isn't allowed here!");
+ ASSERT(async_result, "The async result must not be empty!");
+
+ type_ = type;
+ async_result_ = std::move(async_result);
+ }
+ };
+
+ /// Base class for lockfree asynchronous messages to the script reloader
+ class ScriptReloaderMessage
+ {
+ public:
+ virtual ~ScriptReloaderMessage() { }
+
+ /// Invoke this function to run a message thread safe on the reloader
+ virtual void operator() (HotSwapScriptReloadMgr* reloader) = 0;
+ };
+
+ /// Implementation class which wraps functional types and dispatches
+ /// it in the overwritten implementation of the reloader messages.
+ template<typename T>
+ class ScriptReloaderMessageImplementation
+ : public ScriptReloaderMessage
+ {
+ T dispatcher_;
+
+ public:
+ explicit ScriptReloaderMessageImplementation(T dispatcher)
+ : dispatcher_(std::move(dispatcher)) { }
+
+ void operator() (HotSwapScriptReloadMgr* reloader) final override
+ {
+ dispatcher_(reloader);
+ }
+ };
+
+ /// Uses the given functional type and creates a asynchronous reloader
+ /// message on the heap, which requires deletion.
+ template<typename T>
+ auto MakeMessage(T&& dispatcher)
+ -> ScriptReloaderMessageImplementation<typename std::decay<T>::type>*
+ {
+ return new ScriptReloaderMessageImplementation<typename std::decay<T>::type>
+ (std::forward<T>(dispatcher));
+ }
+
+public:
+ HotSwapScriptReloadMgr()
+ : _libraryWatcher(-1), _unique_library_name_counter(0),
+ _last_time_library_changed(0), _last_time_sources_changed(0),
+ _last_time_user_informed(0), terminate_early(false) { }
+
+ virtual ~HotSwapScriptReloadMgr()
+ {
+ // Delete all messages
+ ScriptReloaderMessage* message;
+ while (_messages.Dequeue(message))
+ delete message;
+ }
+
+ /// Returns the absolute path to the script module directory
+ static fs::path GetLibraryDirectory()
+ {
+ // When an absolute path is given in the config use it,
+ // otherwise interpret paths relative to the executable.
+ fs::path path(sConfigMgr->GetStringDefault("HotSwap.ScriptDir", "scripts"));
+ if (path.is_absolute())
+ return path;
+ else
+ return fs::absolute(path, GetDirectoryOfExecutable());
+ }
+
+ /// Returns the absolute path to the scripts directory in the source tree.
+ static fs::path GetSourceDirectory()
+ {
+ fs::path dir = BuiltInConfig::GetSourceDirectory();
+ dir /= "src";
+ dir /= "server";
+ dir /= "scripts";
+ return dir;
+ }
+
+ /// Initializes the file watchers and loads all existing shared libraries
+ /// into the running server.
+ void Initialize() final override
+ {
+ if (!sWorld->getBoolConfig(CONFIG_HOTSWAP_ENABLED))
+ return;
+
+ if (BuiltInConfig::GetBuildDirectory().find(" ") != std::string::npos)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Your build directory path \"%s\" "
+ "contains spaces, which isn't allowed for compatibility reasons! "
+ "You need to create a build directory which doesn't contain any space character "
+ "in it's path!",
+ BuiltInConfig::GetBuildDirectory().c_str());
+
+ return;
+ }
+
+ {
+ auto const library_directory = GetLibraryDirectory();
+ if (!fs::exists(library_directory) || !fs::is_directory(library_directory))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Library directory \"%s\" doesn't exist!.",
+ library_directory.generic_string().c_str());
+ return;
+ }
+ }
+
+ #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
+
+ temporary_cache_path_ = CalculateTemporaryCachePath();
+
+ // We use the boost filesystem function versions which accept
+ // an error code to prevent it from throwing exceptions.
+ boost::system::error_code code;
+ if ((!fs::exists(temporary_cache_path_, code)
+ || (fs::remove_all(temporary_cache_path_, code) > 0)) &&
+ !fs::create_directory(temporary_cache_path_, code))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory at \"%s\".",
+ temporary_cache_path_.generic_string().c_str());
+
+ return;
+ }
+
+ // Used to silent compiler warnings
+ (void)code;
+
+ #endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
+
+ // Correct the CMake prefix when needed
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED))
+ DoCMakePrefixCorrectionIfNeeded();
+
+ InitializeDefaultLibraries();
+ InitializeFileWatchers();
+ }
+
+ /// Needs to be called periodically from the worldserver loop
+ /// to invoke queued actions like module loading/unloading and script
+ /// compilation.
+ /// This method should be invoked from a thread safe point to
+ /// prevent misbehavior.
+ void Update() final override
+ {
+ // Consume all messages
+ ScriptReloaderMessage* message;
+ while (_messages.Dequeue(message))
+ {
+ (*message)(this);
+ delete message;
+ }
+
+ DispatchRunningBuildJobs();
+ DispatchModuleChanges();
+ }
+
+ /// Unloads the manager and cancels all runnings jobs immediately
+ void Unload() final override
+ {
+ if (_libraryWatcher >= 0)
+ {
+ _fileWatcher.removeWatch(_libraryWatcher);
+ _libraryWatcher = -1;
+ }
+
+ // If a build is in progress cancel it
+ if (_build_job)
+ {
+ _build_job->GetProcess()->Terminate();
+ _build_job.reset();
+ }
+
+ // Release all strong references to script modules
+ // to trigger unload actions as early as possible,
+ // otherwise the worldserver will crash on exit.
+ _running_script_modules.clear();
+ }
+
+ /// Queue's a thread safe message to the reloader which is executed on
+ /// the next world server update tick.
+ template<typename T>
+ void QueueMessage(T&& message)
+ {
+ _messages.Enqueue(MakeMessage(std::forward<T>(message)));
+ }
+
+ /// Queues an action which marks the given shared library as changed
+ /// which will add, unload or reload it at the next world update tick.
+ /// This method is thread safe.
+ void QueueSharedLibraryChanged(fs::path const& path)
+ {
+ _last_time_library_changed = getMSTime();
+ _libraries_changed.insert(path);
+ }
+
+ /// Queues a notification that a source file was added
+ /// This method is thread unsafe.
+ void QueueAddSourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_ADDED);
+ }
+
+ /// Queues a notification that a source file was modified
+ /// This method is thread unsafe.
+ void QueueModifySourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_MODIFIED);
+ }
+
+ /// Queues a notification that a source file was removed
+ /// This method is thread unsafe.
+ void QueueRemoveSourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_REMOVED);
+ }
+
+private:
+ // Loads all shared libraries which are contained in the
+ // scripts directory on startup.
+ void InitializeDefaultLibraries()
+ {
+ fs::path const libraryDirectory(GetLibraryDirectory());
+ fs::directory_iterator const dir_end;
+
+ uint32 count = 0;
+
+ // Iterate through all shared libraries in the script directory and load it
+ for (fs::directory_iterator dir_itr(libraryDirectory); dir_itr != dir_end ; ++dir_itr)
+ if (fs::is_regular_file(dir_itr->path()) && HasValidScriptModuleName(dir_itr->path().filename().generic_string()))
+ {
+ TC_LOG_INFO("scripts.hotswap", "Loading script module \"%s\"...",
+ dir_itr->path().filename().generic_string().c_str());
+
+ // Don't swap the script context to do bulk loading
+ ProcessLoadScriptModule(dir_itr->path(), false);
+ ++count;
+ }
+
+ TC_LOG_INFO("scripts.hotswap", ">> Loaded %u script modules.", count);
+ }
+
+ // Initialize all enabled file watchers.
+ // Needs to be called after InitializeDefaultLibraries()!
+ void InitializeFileWatchers()
+ {
+ _libraryWatcher = _fileWatcher.addWatch(GetLibraryDirectory().generic_string(), &libraryUpdateListener, false);
+ if (_libraryWatcher >= 0)
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Library reloader is listening on \"%s\".",
+ GetLibraryDirectory().generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to initialize the library reloader on \"%s\".",
+ GetLibraryDirectory().generic_string().c_str());
+ }
+
+ _fileWatcher.watch();
+ }
+
+ static fs::path CalculateTemporaryCachePath()
+ {
+ auto path = fs::temp_directory_path();
+ path /= Trinity::StringFormat("tc_script_cache_%s_%s",
+ GitRevision::GetBranch(),
+ CalculateSHA1Hash(sConfigMgr->GetFilename()).c_str());
+
+ return path;
+ }
+
+ fs::path GenerateUniquePathForLibraryInCache(fs::path path)
+ {
+ ASSERT(!temporary_cache_path_.empty(),
+ "The temporary cache path wasn't set!");
+
+ // Create the cache path and increment the library counter to use an unique name for each library
+ auto cache_path = temporary_cache_path_;
+ cache_path /= Trinity::StringFormat("%s.%u%s",
+ path.stem().generic_string().c_str(),
+ _unique_library_name_counter++,
+ path.extension().generic_string().c_str());
+
+ return cache_path;
+ }
+
+ /// Updates the current state of the given source path
+ void UpdateSourceChangeRequest(std::string const& module_name,
+ fs::path const& path,
+ ChangeStateRequest state)
+ {
+ _last_time_sources_changed = getMSTime();
+
+ // Write when there is no module with the given name known
+ auto module_itr = _sources_changed.find(module_name);
+
+ // When the file was modified it's enough to mark the module as
+ // dirty by initializing the associated map.
+ if (module_itr == _sources_changed.end())
+ module_itr = _sources_changed.insert(std::make_pair(
+ module_name, decltype(_sources_changed)::mapped_type{})).first;
+
+ // Leave when the file was just modified as explained above
+ if (state == ChangeStateRequest::CHANGE_REQUEST_MODIFIED)
+ return;
+
+ // Insert when the given path isn't existent
+ auto const itr = module_itr->second.find(path);
+ if (itr == module_itr->second.end())
+ {
+ module_itr->second.insert(std::make_pair(path, state));
+ return;
+ }
+
+ ASSERT((itr->second == ChangeStateRequest::CHANGE_REQUEST_ADDED)
+ || (itr->second == ChangeStateRequest::CHANGE_REQUEST_REMOVED),
+ "Stored value is invalid!");
+
+ ASSERT((state == ChangeStateRequest::CHANGE_REQUEST_ADDED)
+ || (state == ChangeStateRequest::CHANGE_REQUEST_REMOVED),
+ "The given state is invalid!");
+
+ ASSERT(state != itr->second,
+ "Tried to apply a state which is stored already!");
+
+ module_itr->second.erase(itr);
+ }
+
+ /// Called periodically on the worldserver tick to process all
+ /// load/unload/reload requests of shared libraries.
+ void DispatchModuleChanges()
+ {
+ // When there are no libraries to change return
+ if (_libraries_changed.empty())
+ return;
+
+ // Wait some time after changes to catch bulk changes
+ if (GetMSTimeDiffToNow(_last_time_library_changed) < 500)
+ return;
+
+ for (auto const& path : _libraries_changed)
+ {
+ bool const is_running =
+ _running_script_module_names.find(path) != _running_script_module_names.end();
+
+ bool const exists = fs::exists(path);
+
+ if (is_running)
+ {
+ if (exists)
+ ProcessReloadScriptModule(path);
+ else
+ ProcessUnloadScriptModule(path);
+ }
+ else if (exists)
+ ProcessLoadScriptModule(path);
+ }
+
+ _libraries_changed.clear();
+ }
+
+ void ProcessLoadScriptModule(fs::path const& path, bool swap_context = true)
+ {
+ ASSERT(_running_script_module_names.find(path) == _running_script_module_names.end(),
+ "Can't load a module which is running already!");
+
+ Optional<fs::path> cache_path;
+
+ #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
+
+ // Copy the shared library into a cache on platforms which lock files on use (windows).
+ cache_path = GenerateUniquePathForLibraryInCache(path);
+
+ {
+ boost::system::error_code code;
+ fs::copy_file(path, *cache_path, fs::copy_option::fail_if_exists, code);
+ if (code)
+ {
+ TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module "
+ "\"%s\" at \"%s\" with reason (\"%s\")!",
+ path.filename().generic_string().c_str(), cache_path->generic_string().c_str(),
+ code.message().c_str());
+
+ // Find a better solution for this but it's much better
+ // to start the core without scripts
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ ABORT();
+ return;
+ }
+
+ TC_LOG_TRACE("scripts.hotswap", ">> Copied the shared library \"%s\" to \"%s\" for caching.",
+ path.filename().generic_string().c_str(), cache_path->generic_string().c_str());
+ }
+
+ #endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING
+
+ auto module = ScriptModule::CreateFromPath(path, cache_path);
+ if (!module)
+ {
+ TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!",
+ path.filename().generic_string().c_str());
+
+ // Find a better solution for this but it's much better
+ // to start the core without scripts
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ ABORT();
+ return;
+ }
+
+ // Limit the git revision hash to 7 characters.
+ std::string module_revision((*module)->GetScriptModuleRevisionHash());
+ if (module_revision.size() >= 7)
+ module_revision = module_revision.substr(0, 7);
+
+ std::string const module_name = (*module)->GetScriptModule();
+ TC_LOG_INFO("scripts.hotswap", ">> Loaded script module \"%s\" (\"%s\" - %s).",
+ path.filename().generic_string().c_str(), module_name.c_str(), module_revision.c_str());
+
+ if (module_revision.empty())
+ {
+ TC_LOG_WARN("scripts.hotswap", ">> Script module \"%s\" has an empty revision hash!",
+ path.filename().generic_string().c_str());
+ }
+ else
+ {
+ // Trim the revision hash
+ std::string my_revision_hash = GitRevision::GetHash();
+ std::size_t const trim = std::min(module_revision.size(), my_revision_hash.size());
+ my_revision_hash = my_revision_hash.substr(0, trim);
+ module_revision = module_revision.substr(0, trim);
+
+ if (my_revision_hash != module_revision)
+ {
+ TC_LOG_WARN("scripts.hotswap", ">> Script module \"%s\" has a different revision hash! "
+ "Binary incompatibility could lead to unknown behaviour!", path.filename().generic_string().c_str());
+ }
+ }
+
+ {
+ auto const itr = _running_script_modules.find(module_name);
+ if (itr != _running_script_modules.end())
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Attempt to load a module twice \"%s\" (loaded module is at %s)!",
+ path.generic_string().c_str(), itr->second.first->GetModulePath().generic_string().c_str());
+
+ return;
+ }
+ }
+
+ 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,
+ module_name);
+
+ // 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))));
+ _running_script_module_names.insert(std::make_pair(path, module_name));
+ }
+
+ void ProcessReloadScriptModule(fs::path const& path)
+ {
+ ProcessUnloadScriptModule(path, false);
+ ProcessLoadScriptModule(path);
+ }
+
+ void ProcessUnloadScriptModule(fs::path const& path, bool finish = true)
+ {
+ auto const itr = _running_script_module_names.find(path);
+
+ ASSERT(itr != _running_script_module_names.end(),
+ "Can't unload a module which isn't running!");
+
+ // Unload the script context
+ sScriptMgr->ReleaseScriptContext(itr->second);
+
+ if (finish)
+ sScriptMgr->SwapScriptContext();
+
+ TC_LOG_INFO("scripts.hotswap", "Released script module \"%s\" (\"%s\")...",
+ path.filename().generic_string().c_str(), itr->second.c_str());
+
+ // Unload the script module
+ auto ref = _running_script_modules.find(itr->second);
+ ASSERT(ref != _running_script_modules.end() &&
+ "Expected the script reference to be present!");
+
+ // Yield a message when there are other owning references to
+ // the module which prevents it from unloading.
+ // The module will be unloaded once all scripts provided from the module
+ // are destroyed.
+ if (!ref->second.first.unique())
+ {
+ TC_LOG_INFO("scripts.hotswap",
+ "Script module %s is still used by %lu spell, aura or instance scripts. "
+ "Will lazy unload the module once all scripts stopped using it, "
+ "to use the latest version of an edited script unbind yourself from "
+ "the instance or re-cast the spell.",
+ ref->second.first->GetScriptModule(), ref->second.first.use_count() - 1);
+ }
+
+ // Remove the owning reference from the reloader
+ _running_script_modules.erase(ref);
+ _running_script_module_names.erase(itr);
+ }
+
+ /// Called periodically on the worldserver tick to process all recompile
+ /// requests. This method invokes one build or install job at the time
+ void DispatchRunningBuildJobs()
+ {
+ if (_build_job)
+ {
+ // Terminate the current build job when an associated source was changed
+ // while compiling and the terminate early option is enabled.
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED))
+ {
+ if (!terminate_early && _sources_changed.find(_build_job->GetModuleName()) != _sources_changed.end())
+ {
+ /*
+ FIXME: Currently crashes the server
+ TC_LOG_INFO("scripts.hotswap", "Terminating the running build of module \"%s\"...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->GetProcess()->Terminate();
+ _build_job.reset();
+
+ // Continue with the default execution path
+ DispatchRunningBuildJobs();
+ return;
+ */
+
+ terminate_early = true;
+ return;
+ }
+ }
+
+ // Wait for the current build job to finish, if the job finishes in time
+ // evaluate it and continue with the next one.
+ if (_build_job->GetProcess()->GetFutureResult().
+ wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ ProcessReadyBuildJob();
+ else
+ return; // Return when the job didn't finish in time
+
+ // Skip this cycle when the previous job scheduled a new one
+ if (_build_job)
+ return;
+ }
+
+ // Avoid burst updates through waiting for a short time after changes
+ if ((_last_time_sources_changed != 0) &&
+ (GetMSTimeDiffToNow(_last_time_sources_changed) < 500))
+ return;
+
+ // If the changed sources are empty do nothing
+ if (_sources_changed.empty())
+ return;
+
+ // Wait until are attached debugger were detached.
+ if (IsDebuggerBlockingRebuild())
+ {
+ if ((_last_time_user_informed == 0) ||
+ (GetMSTimeDiffToNow(_last_time_user_informed) > 7500))
+ {
+ _last_time_user_informed = getMSTime();
+
+ // Informs the user that the attached debugger is blocking the automatic script rebuild.
+ TC_LOG_INFO("scripts.hotswap", "Your attached debugger is blocking the TrinityCore "
+ "automatic script rebuild, please detach it!");
+ }
+
+ return;
+ }
+
+ // Find all source files of a changed script module and removes
+ // it from the changed source list, invoke the build afterwards.
+ bool rebuild_buildfiles;
+ auto module_name = [&]
+ {
+ auto itr = _sources_changed.begin();
+ auto name = itr->first;
+ rebuild_buildfiles = !itr->second.empty();
+
+ if (sLog->ShouldLog("scripts.hotswap", LogLevel::LOG_LEVEL_TRACE))
+ for (auto const& entry : itr->second)
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s was %s.",
+ entry.first.generic_string().c_str(),
+ ((entry.second == ChangeStateRequest::CHANGE_REQUEST_ADDED) ?
+ "added" : "removed"));
+ }
+
+ _sources_changed.erase(itr);
+ return name;
+ }();
+
+ // Erase the added delete history all modules when we
+ // invoke a cmake rebuild since we add all
+ // added files of other modules to the build as well
+ if (rebuild_buildfiles)
+ {
+ for (auto& entry : _sources_changed)
+ entry.second.clear();
+ }
+
+ ASSERT(!module_name.empty(),
+ "The current module name is invalid!");
+
+ TC_LOG_INFO("scripts.hotswap", "Recompiling Module \"%s\"...",
+ module_name.c_str());
+
+ // Calculate the project name of the script module
+ auto project_name = CalculateScriptModuleProjectName(module_name);
+
+ // Find the best build directive for the module
+ auto build_directive = [&] () -> std::string
+ {
+ auto directive = sConfigMgr->GetStringDefault("HotSwap.ReCompilerBuildType", "");
+ if (!directive.empty())
+ return directive;
+
+ auto const itr = _known_modules_build_directives.find(module_name);
+ if (itr != _known_modules_build_directives.end())
+ return itr->second;
+ else // If no build directive of the module was found use the one from the game library
+ return _BUILD_DIRECTIVE;
+ }();
+
+ // Initiate the new build job
+ _build_job = BuildJob(std::move(module_name),
+ std::move(project_name), std::move(build_directive));
+
+ // Rerun CMake when we need to recreate the build files
+ if (rebuild_buildfiles
+ && sWorld->getBoolConfig(CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED))
+ DoRerunCMake();
+ else
+ DoCompileCurrentProcessedModule();
+ }
+
+ void ProcessReadyBuildJob()
+ {
+ ASSERT(_build_job->IsValid(), "Invalid build job!");
+
+ // Retrieve the result
+ auto const error = _build_job->GetProcess()->GetFutureResult().get();
+
+ if (terminate_early)
+ {
+ _build_job.reset();
+ terminate_early = false;
+ return;
+ }
+
+ switch (_build_job->GetType())
+ {
+ case BuildJobType::BUILD_JOB_RERUN_CMAKE:
+ {
+ if (!error)
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Successfully updated the build files!");
+ }
+ else
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Failed to update the build files at \"%s\", "
+ "it's possible that recently added sources are not included "
+ "in your next builds, rerun CMake manually.",
+ BuiltInConfig::GetBuildDirectory().c_str());
+ }
+ // Continue with building the changes sources
+ DoCompileCurrentProcessedModule();
+ return;
+ }
+ case BuildJobType::BUILD_JOB_COMPILE:
+ {
+ if (!error) // Build was successful
+ {
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_INSTALL_ENABLED))
+ {
+ // Continue with the installation when it's enabled
+ TC_LOG_INFO("scripts.hotswap",
+ ">> Successfully build module %s, continue with installing...",
+ _build_job->GetModuleName().c_str());
+
+ DoInstallCurrentProcessedModule();
+ return;
+ }
+
+ // Skip the installation because it's disabled in config
+ TC_LOG_INFO("scripts.hotswap",
+ ">> Successfully build module %s, skipped the installation.",
+ _build_job->GetModuleName().c_str());
+ }
+ else // Build wasn't successful
+ {
+ TC_LOG_ERROR("scripts.hotswap",
+ ">> The build of module %s failed! See the log for details.",
+ _build_job->GetModuleName().c_str());
+ }
+ break;
+ }
+ case BuildJobType::BUILD_JOB_INSTALL:
+ {
+ if (!error)
+ {
+ // Installation was successful
+ TC_LOG_INFO("scripts.hotswap", ">> Successfully installed module %s in %us",
+ _build_job->GetModuleName().c_str(),
+ _build_job->GetTimeFromStart() / IN_MILLISECONDS);
+ }
+ else
+ {
+ // Installation wasn't successful
+ TC_LOG_INFO("scripts.hotswap",
+ ">> The installation of module %s failed! See the log for details.",
+ _build_job->GetModuleName().c_str());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Clear the current job
+ _build_job.reset();
+ }
+
+ /// Reruns CMake asynchronously over the build directory
+ void DoRerunCMake()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Rerunning CMake because there were sources added or removed...");
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_RERUN_CMAKE,
+ InvokeAsyncCMakeCommand(BuiltInConfig::GetBuildDirectory()));
+ }
+
+ /// Invokes a new build of the current active module job
+ void DoCompileCurrentProcessedModule()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Starting asynchronous build job for module %s...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_COMPILE,
+ InvokeAsyncCMakeCommand(
+ "--build", BuiltInConfig::GetBuildDirectory(),
+ "--target", _build_job->GetProjectName(),
+ "--config", _build_job->GetBuildDirective()));
+ }
+
+ /// Invokes a new asynchronous install of the current active module job
+ void DoInstallCurrentProcessedModule()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Starting asynchronous install job for module %s...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_INSTALL,
+ InvokeAsyncCMakeCommand(
+ "-DCOMPONENT=" + _build_job->GetProjectName(),
+ "-DBUILD_TYPE=" + _build_job->GetBuildDirective(),
+ "-P", fs::absolute("cmake_install.cmake",
+ BuiltInConfig::GetBuildDirectory()).generic_string()));
+ }
+
+ /// Sets the CMAKE_INSTALL_PREFIX variable in the CMake cache
+ /// to point to the current worldserver position,
+ /// since most users will forget this.
+ void DoCMakePrefixCorrectionIfNeeded()
+ {
+ TC_LOG_INFO("scripts.hotswap", "Correcting your CMAKE_INSTALL_PREFIX in \"%s\"...",
+ BuiltInConfig::GetBuildDirectory().c_str());
+
+ auto const cmake_cache_path = fs::absolute("CMakeCache.txt",
+ BuiltInConfig::GetBuildDirectory());
+
+ // Stop when the CMakeCache wasn't found
+ if (![&]
+ {
+ boost::system::error_code error;
+ if (!fs::exists(cmake_cache_path, error))
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> CMake cache \"%s\" doesn't exist, "
+ "set the \"BuildDirectory\" option in your worldserver.conf to point"
+ "to your build directory!",
+ cmake_cache_path.generic_string().c_str());
+
+ return false;
+ }
+ else
+ return true;
+ }())
+ return;
+
+ TC_LOG_TRACE("scripts.hotswap", "Checking CMake cache (\"%s\") "
+ "for the correct CMAKE_INSTALL_PREFIX location...",
+ cmake_cache_path.generic_string().c_str());
+
+ std::string cmake_cache_content;
+ {
+ std::ifstream in(cmake_cache_path.generic_string());
+ if (!in.is_open())
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Failed to read the CMake cache at \"%s\"!",
+ cmake_cache_path.generic_string().c_str());
+
+ return;
+ }
+
+ std::ostringstream ss;
+ ss << in.rdbuf();
+ cmake_cache_content = ss.str();
+
+ in.close();
+ }
+
+ static std::string const prefix_key = "CMAKE_INSTALL_PREFIX:PATH=";
+
+ // Extract the value of CMAKE_INSTALL_PREFIX
+ auto begin = cmake_cache_content.find(prefix_key);
+ if (begin != std::string::npos)
+ {
+ begin += prefix_key.length();
+ auto const end = cmake_cache_content.find("\n", begin);
+ if (end != std::string::npos)
+ {
+ fs::path value = cmake_cache_content.substr(begin, end - begin);
+
+ auto current_path = fs::current_path();
+
+ #ifndef _WIN32
+ // The worldserver location is ${CMAKE_INSTALL_PREFIX}/bin
+ // on all other platforms then windows
+ current_path = current_path.parent_path();
+ #endif
+
+ if (value != current_path)
+ {
+ // Prevent correction of the install prefix
+ // when we are starting the core from inside the build tree
+ bool const is_in_path = [&]
+ {
+ fs::path base = BuiltInConfig::GetBuildDirectory();
+ fs::path branch = value;
+ while (!branch.empty())
+ {
+ if (base == branch)
+ return true;
+
+ branch = branch.parent_path();
+ }
+
+ return false;
+ }();
+
+ if (is_in_path)
+ return;
+
+ TC_LOG_INFO("scripts.hotswap", ">> Found outdated CMAKE_INSTALL_PREFIX (\"%s\"), "
+ "worldserver is currently installed at %s...",
+ value.generic_string().c_str(), current_path.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> CMAKE_INSTALL_PREFIX is equal to the current path of execution.");
+ return;
+ }
+ }
+ }
+
+ TC_LOG_INFO("scripts.hotswap", "Invoking CMake cache correction...");
+
+ auto const error = InvokeCMakeCommand(
+ "-DCMAKE_INSTALL_PREFIX:PATH=" + fs::current_path().generic_string(),
+ BuiltInConfig::GetBuildDirectory());
+
+ if (error)
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Failed to update the CMAKE_INSTALL_PREFIX! "
+ "This could lead to unexpected behaviour!");
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Successfully corrected your CMAKE_INSTALL_PREFIX variable"
+ "to point at your current path of execution.");
+ }
+ }
+
+ // File watcher instance and watcher ID's
+ efsw::FileWatcher _fileWatcher;
+ efsw::WatchID _libraryWatcher;
+
+ // Unique library name counter which is used to
+ // generate unique names for every shared library version.
+ uint32 _unique_library_name_counter;
+
+ // Queue which is used for thread safe message processing
+ MPSCQueue<ScriptReloaderMessage> _messages;
+
+ // Change requests to load or unload shared libraries
+ std::unordered_set<fs::path /*path*/> _libraries_changed;
+ // The timestamp which indicates the last time a library was changed
+ uint32 _last_time_library_changed;
+
+ // Contains all running script modules
+ // The associated shared libraries are unloaded immediately
+ // on loosing ownership through RAII.
+ std::unordered_map<std::string /*module name*/,
+ std::pair<std::shared_ptr<ScriptModule>, std::unique_ptr<SourceUpdateListener>>
+ > _running_script_modules;
+ // Container which maps the path of a shared library to it's module name
+ std::unordered_map<fs::path, std::string /*module name*/> _running_script_module_names;
+ // Container which maps the module name to it's last known build directive
+ std::unordered_map<std::string /*module name*/, std::string /*build directive*/> _known_modules_build_directives;
+
+ // Modules which were changed and are queued for recompilation
+ std::unordered_map<std::string /*module*/,
+ std::unordered_map<fs::path /*path*/, ChangeStateRequest /*state*/>> _sources_changed;
+ // Tracks the time since the last module has changed to avoid burst updates
+ uint32 _last_time_sources_changed;
+
+ // Tracks the last timestamp the user was informed about a certain repeating event.
+ uint32 _last_time_user_informed;
+
+ // Represents the current build job which is in progress
+ Optional<BuildJob> _build_job;
+
+ // Is true when the build job dispatcher should stop after
+ // the current job has finished
+ bool terminate_early;
+
+ // The path to the tc_scripts temporary cache
+ fs::path temporary_cache_path_;
+};
+
+/// Maps efsw actions to strings
+static char const* ActionToString(efsw::Action action)
+{
+ switch (action)
+ {
+ case efsw::Action::Add:
+ return "added";
+ case efsw::Action::Delete:
+ return "deleted";
+ case efsw::Action::Moved:
+ return "moved";
+ default:
+ return "modified";
+ }
+}
+
+void LibraryUpdateListener::handleFileAction(efsw::WatchID watchid, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename)
+{
+ // TC_LOG_TRACE("scripts.hotswap", "Library listener detected change on possible module \"%s\ (%s)".", filename.c_str(), ActionToString(action));
+
+ // Split moved actions into a delete and an add action
+ if (action == efsw::Action::Moved)
+ {
+ ASSERT(!oldFilename.empty(), "Old filename doesn't exist!");
+ handleFileAction(watchid, dir, oldFilename, efsw::Action::Delete);
+ handleFileAction(watchid, dir, filename, efsw::Action::Add);
+ return;
+ }
+
+ sScriptReloadMgr->QueueMessage([=](HotSwapScriptReloadMgr* reloader) mutable
+ {
+ auto const path = fs::absolute(
+ filename,
+ sScriptReloadMgr->GetLibraryDirectory());
+
+ if (!HasValidScriptModuleName(filename))
+ return;
+
+ switch (action)
+ {
+ case efsw::Actions::Add:
+ TC_LOG_TRACE("scripts.hotswap", ">> Loading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ case efsw::Actions::Delete:
+ TC_LOG_TRACE("scripts.hotswap", ">> Unloading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ case efsw::Actions::Modified:
+ TC_LOG_TRACE("scripts.hotswap", ">> Reloading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ default:
+ WPAbort();
+ break;
+ }
+ });
+}
+
+/// Returns true when the given path has a known C++ file extension
+static bool HasCXXSourceFileExtension(fs::path const& path)
+{
+ static std::regex const regex("^\\.(h|hpp|c|cc|cpp)$");
+ return std::regex_match(path.extension().generic_string(), regex);
+}
+
+SourceUpdateListener::SourceUpdateListener(fs::path path, std::string script_module_name)
+ : path_(std::move(path)), script_module_name_(std::move(script_module_name)),
+ watcher_id_(sScriptReloadMgr->_fileWatcher.addWatch(path_.generic_string(), this, true))
+{
+ if (watcher_id_ >= 0)
+ {
+ TC_LOG_TRACE("scripts.hotswap", ">> Attached the source recompiler to \"%s\".",
+ path_.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to initialize thesource recompiler on \"%s\".",
+ path_.generic_string().c_str());
+ }
+}
+
+SourceUpdateListener::~SourceUpdateListener()
+{
+ if (watcher_id_ >= 0)
+ {
+ sScriptReloadMgr->_fileWatcher.removeWatch(watcher_id_);
+
+ TC_LOG_TRACE("scripts.hotswap", ">> Detached the source recompiler from \"%s\".",
+ path_.generic_string().c_str());
+ }
+}
+
+void SourceUpdateListener::handleFileAction(efsw::WatchID watchid, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename)
+{
+ // TC_LOG_TRACE("scripts.hotswap", "Source listener detected change on possible file \"%s/%s\" (%s).", dir.c_str(), filename.c_str(), ActionToString(action));
+
+ // Skip the file change notification if the recompiler is disabled
+ if (!sWorld->getBoolConfig(CONFIG_HOTSWAP_RECOMPILER_ENABLED))
+ return;
+
+ // Split moved actions into a delete and an add action
+ if (action == efsw::Action::Moved)
+ {
+ ASSERT(!oldFilename.empty(), "Old filename doesn't exist!");
+ handleFileAction(watchid, dir, oldFilename, efsw::Action::Delete);
+ handleFileAction(watchid, dir, filename, efsw::Action::Add);
+ return;
+ }
+
+ auto const path = fs::absolute(
+ filename,
+ dir);
+
+ // Check if the file is a C/C++ source file.
+ if (!path.has_extension() || !HasCXXSourceFileExtension(path))
+ return;
+
+ /// Thread safe part
+ sScriptReloadMgr->QueueMessage([=](HotSwapScriptReloadMgr* reloader)
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Detected source change on module \"%s\", "
+ "queued for recompilation...", script_module_name_.c_str());
+
+ switch (action)
+ {
+ case efsw::Actions::Add:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was added.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueAddSourceFile(script_module_name_, path);
+ break;
+ case efsw::Actions::Delete:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was deleted.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueRemoveSourceFile(script_module_name_, path);
+ break;
+ case efsw::Actions::Modified:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was modified.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueModifySourceFile(script_module_name_, path);
+ break;
+ default:
+ WPAbort();
+ break;
+ }
+ });
+}
+
+// Returns the module reference of the given context
+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 { };
+}
+
+// Returns the full hot swap implemented ScriptReloadMgr
+ScriptReloadMgr* ScriptReloadMgr::instance()
+{
+ static HotSwapScriptReloadMgr instance;
+ return &instance;
+}
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
diff --git a/src/server/game/Scripting/ScriptReloadMgr.h b/src/server/game/Scripting/ScriptReloadMgr.h
new file mode 100644
index 00000000000..f9b388f8eb0
--- /dev/null
+++ b/src/server/game/Scripting/ScriptReloadMgr.h
@@ -0,0 +1,81 @@
+/*
+ * 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 SCRIPT_RELOADER_H
+#define SCRIPT_RELOADER_H
+
+#include <memory>
+#include <string>
+#include "Define.h"
+#include <boost/filesystem/path.hpp>
+
+/// Represents a strong reference to a dynamic library which
+/// provides C++ scripts. As long as one reference to the library exists
+/// the library is kept loaded in the server, which makes it possible to lazy
+/// unload several script types on demand (like SpellScripts), and to
+/// provide multiple versions of the same script to the script factories.
+///
+/// Acquire a new reference through using:
+/// `ScriptReloadMgr::AcquireModuleReferenceOfContext`
+class ModuleReference
+{
+public:
+ virtual ~ModuleReference() { }
+
+ /// Returns the git revision hash of the referenced script module
+ virtual char const* GetScriptModuleRevisionHash() const = 0;
+ /// Returns the name of the referenced script module
+ virtual char const* GetScriptModule() const = 0;
+ /// Returns the path to the script module
+ virtual boost::filesystem::path const& GetModulePath() const = 0;
+};
+
+/// Provides the whole physical dynamic library unloading capability.
+/// Loads, Reloads and Unloads dynamic libraries on changes and
+/// informs the ScriptMgr about changes which were made.
+/// The ScriptReloadMgr is also responsible for watching the source directory
+/// and to invoke a build on changes.
+class TC_GAME_API ScriptReloadMgr
+{
+protected:
+ ScriptReloadMgr() { }
+
+public:
+ virtual ~ScriptReloadMgr() { }
+
+ /// Initializes the ScriptReloadMgr
+ virtual void Initialize() { }
+
+ /// Needs to be called periodically to check for updates on script modules.
+ /// Expects to be invoked in a thread safe way which means it's required that
+ /// the current thread is the only one which accesses the world data.
+ virtual void Update() { }
+
+ /// Unloads the ScriptReloadMgr
+ virtual void Unload() { }
+
+ /// Returns an owning reference to the current module of the given context
+ static std::shared_ptr<ModuleReference> AcquireModuleReferenceOfContext(
+ std::string const& context);
+
+ /// Returns the unique ScriptReloadMgr singleton instance
+ static ScriptReloadMgr* instance();
+};
+
+#define sScriptReloadMgr ScriptReloadMgr::instance()
+
+#endif // SCRIPT_RELOADER_H
diff --git a/src/server/game/Scripting/ScriptSystem.cpp b/src/server/game/Scripting/ScriptSystem.cpp
index 309838a919e..52c5c1640af 100644
--- a/src/server/game/Scripting/ScriptSystem.cpp
+++ b/src/server/game/Scripting/ScriptSystem.cpp
@@ -21,7 +21,7 @@
#include "DatabaseEnv.h"
#include "ScriptMgr.h"
-ScriptPointVector const SystemMgr::_empty;
+TC_GAME_API ScriptPointVector const SystemMgr::_empty;
SystemMgr* SystemMgr::instance()
{
diff --git a/src/server/game/Scripting/ScriptSystem.h b/src/server/game/Scripting/ScriptSystem.h
index 9e6a0d2a5a7..7cf8ffc85b0 100644
--- a/src/server/game/Scripting/ScriptSystem.h
+++ b/src/server/game/Scripting/ScriptSystem.h
@@ -45,7 +45,7 @@ struct ScriptPointMove
typedef std::vector<ScriptPointMove> ScriptPointVector;
-class SystemMgr
+class TC_GAME_API SystemMgr
{
private:
SystemMgr() { }
diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h
index 0d30134aa9f..48ed03004ea 100644
--- a/src/server/game/Server/Protocol/Opcodes.h
+++ b/src/server/game/Server/Protocol/Opcodes.h
@@ -1373,7 +1373,7 @@ struct OpcodeHandler
void (WorldSession::*handler)(WorldPacket& recvPacket);
};
-extern OpcodeHandler opcodeTable[NUM_MSG_TYPES];
+TC_GAME_API extern OpcodeHandler opcodeTable[NUM_MSG_TYPES];
#pragma pack(pop)
diff --git a/src/server/game/Server/Protocol/PacketLog.cpp b/src/server/game/Server/Protocol/PacketLog.cpp
index 11a02828998..114b2ae0ecc 100644
--- a/src/server/game/Server/Protocol/PacketLog.cpp
+++ b/src/server/game/Server/Protocol/PacketLog.cpp
@@ -45,7 +45,7 @@ struct PacketHeader
uint32 SocketPort;
};
- char Direction[4];
+ uint32 Direction;
uint32 ConnectionId;
uint32 ArrivalTicks;
uint32 OptionalDataSize;
@@ -109,7 +109,7 @@ void PacketLog::LogPacket(WorldPacket const& packet, Direction direction, boost:
std::lock_guard<std::mutex> lock(_logPacketLock);
PacketHeader header;
- *reinterpret_cast<uint32*>(header.Direction) = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
+ header.Direction = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
header.ConnectionId = 0;
header.ArrivalTicks = getMSTime();
diff --git a/src/server/game/Server/Protocol/PacketLog.h b/src/server/game/Server/Protocol/PacketLog.h
index b211cfbf3ca..5e7661a884b 100644
--- a/src/server/game/Server/Protocol/PacketLog.h
+++ b/src/server/game/Server/Protocol/PacketLog.h
@@ -31,7 +31,7 @@ enum Direction
class WorldPacket;
-class PacketLog
+class TC_GAME_API PacketLog
{
private:
PacketLog();
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index d98143fcec0..584367bd375 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -246,7 +246,7 @@ struct PacketCounter
};
/// Player session in the World
-class WorldSession
+class TC_GAME_API WorldSession
{
public:
WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter);
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index e3c6179a34a..1c77283d812 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -344,11 +344,12 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
default:
{
sessionGuard.lock();
+
LogOpcodeText(opcode, sessionGuard);
+
if (!_worldSession)
{
TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
- CloseSocket();
return ReadDataHandlerResult::Error;
}
diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h
index 9b36e458511..3874b60b0bb 100644
--- a/src/server/game/Server/WorldSocket.h
+++ b/src/server/game/Server/WorldSocket.h
@@ -48,7 +48,7 @@ struct ClientPktHeader
struct AuthSession;
-class WorldSocket : public Socket<WorldSocket>
+class TC_GAME_API WorldSocket : public Socket<WorldSocket>
{
typedef Socket<WorldSocket> BaseSocket;
diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp
index cbdb6a4a70b..2faf0704d4f 100644
--- a/src/server/game/Server/WorldSocketMgr.cpp
+++ b/src/server/game/Server/WorldSocketMgr.cpp
@@ -53,7 +53,7 @@ WorldSocketMgr& WorldSocketMgr::Instance()
return instance;
}
-bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port)
+bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port, int threadCount)
{
_tcpNoDelay = sConfigMgr->GetBoolDefault("Network.TcpNodelay", true);
@@ -71,7 +71,7 @@ bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string
return false;
}
- BaseSocketMgr::StartNetwork(service, bindIp, port);
+ BaseSocketMgr::StartNetwork(service, bindIp, port, threadCount);
_acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this));
diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h
index fddf3bee4c6..a5aee344bf7 100644
--- a/src/server/game/Server/WorldSocketMgr.h
+++ b/src/server/game/Server/WorldSocketMgr.h
@@ -30,7 +30,7 @@
class WorldSocket;
/// Manages all sockets connected to peers and network threads
-class WorldSocketMgr : public SocketMgr<WorldSocket>
+class TC_GAME_API WorldSocketMgr : public SocketMgr<WorldSocket>
{
typedef SocketMgr<WorldSocket> BaseSocketMgr;
@@ -38,7 +38,7 @@ public:
static WorldSocketMgr& Instance();
/// Start network, listen at address:port .
- bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port) override;
+ bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port, int networkThreads) override;
/// Stops all network threads, It will wait for all running threads .
void StopNetwork() override;
diff --git a/src/server/game/Skills/SkillDiscovery.h b/src/server/game/Skills/SkillDiscovery.h
index b7fe1cdc8b2..c2020e5b075 100644
--- a/src/server/game/Skills/SkillDiscovery.h
+++ b/src/server/game/Skills/SkillDiscovery.h
@@ -23,8 +23,9 @@
class Player;
-void LoadSkillDiscoveryTable();
-uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player);
-bool HasDiscoveredAllSpells(uint32 spellId, Player* player);
-uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player);
+TC_GAME_API void LoadSkillDiscoveryTable();
+TC_GAME_API uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player);
+TC_GAME_API bool HasDiscoveredAllSpells(uint32 spellId, Player* player);
+TC_GAME_API uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player);
+
#endif
diff --git a/src/server/game/Skills/SkillExtraItems.h b/src/server/game/Skills/SkillExtraItems.h
index 2889b221600..5a477b65f0b 100644
--- a/src/server/game/Skills/SkillExtraItems.h
+++ b/src/server/game/Skills/SkillExtraItems.h
@@ -23,12 +23,14 @@
// predef classes used in functions
class Player;
+
// returns true and sets the appropriate info if the player can create a perfect item with the given spellId
-bool CanCreatePerfectItem(Player* player, uint32 spellId, float &perfectCreateChance, uint32 &perfectItemType);
+TC_GAME_API bool CanCreatePerfectItem(Player* player, uint32 spellId, float &perfectCreateChance, uint32 &perfectItemType);
// load perfection proc info from DB
-void LoadSkillPerfectItemTable();
+TC_GAME_API void LoadSkillPerfectItemTable();
// returns true and sets the appropriate info if the player can create extra items with the given spellId
-bool CanCreateExtraItems(Player* player, uint32 spellId, float &additionalChance, uint8 &additionalMax);
+TC_GAME_API bool CanCreateExtraItems(Player* player, uint32 spellId, float &additionalChance, uint8 &additionalMax);
// function to load the extra item creation info from DB
-void LoadSkillExtraItemTable();
+TC_GAME_API void LoadSkillExtraItemTable();
+
#endif
diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h
index e0daf59fcc2..02dd88e582a 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,
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 207908c6d51..4133d0c8d65 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
@@ -1325,7 +1325,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
{
// apply glow vision
if (target->GetTypeId() == TYPEID_PLAYER)
- target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+ target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.AddFlag(type);
target->m_invisibility.AddValue(type, GetAmount());
@@ -1337,7 +1337,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
// if not have different invisibility auras.
// remove glow vision
if (target->GetTypeId() == TYPEID_PLAYER)
- target->RemoveByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+ target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.DelFlag(type);
}
@@ -1409,7 +1409,7 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo
target->SetStandFlags(UNIT_STAND_FLAGS_CREEP);
if (target->GetTypeId() == TYPEID_PLAYER)
- target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_STEALTH);
+ target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_STEALTH);
}
else
{
@@ -1421,7 +1421,7 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo
target->RemoveStandFlags(UNIT_STAND_FLAGS_CREEP);
if (target->GetTypeId() == TYPEID_PLAYER)
- target->RemoveByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_STEALTH);
+ target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_STEALTH);
}
}
@@ -2400,7 +2400,7 @@ void AuraEffect::HandleAuraTrackStealthed(AuraApplication const* aurApp, uint8 m
if (target->HasAuraType(GetAuraType()))
return;
}
- target->ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_TRACK_STEALTHED, apply);
+ target->ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_TRACK_STEALTHED, apply);
}
void AuraEffect::HandleAuraModStalked(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2915,7 +2915,7 @@ void AuraEffect::HandleAuraModIncreaseSpeed(AuraApplication const* aurApp, uint8
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_RUN, true);
+ target->UpdateSpeed(MOVE_RUN);
}
void AuraEffect::HandleAuraModIncreaseMountedSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2930,7 +2930,7 @@ void AuraEffect::HandleAuraModIncreaseFlightSpeed(AuraApplication const* aurApp,
Unit* target = aurApp->GetTarget();
if (mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK)
- target->UpdateSpeed(MOVE_FLIGHT, true);
+ target->UpdateSpeed(MOVE_FLIGHT);
//! Update ability to fly
if (GetAuraType() == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)
@@ -2965,7 +2965,7 @@ void AuraEffect::HandleAuraModIncreaseSwimSpeed(AuraApplication const* aurApp, u
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_SWIM, true);
+ target->UpdateSpeed(MOVE_SWIM);
}
void AuraEffect::HandleAuraModDecreaseSpeed(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
@@ -2975,12 +2975,12 @@ void AuraEffect::HandleAuraModDecreaseSpeed(AuraApplication const* aurApp, uint8
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_RUN, true);
- target->UpdateSpeed(MOVE_SWIM, true);
- target->UpdateSpeed(MOVE_FLIGHT, true);
- target->UpdateSpeed(MOVE_RUN_BACK, true);
- target->UpdateSpeed(MOVE_SWIM_BACK, true);
- target->UpdateSpeed(MOVE_FLIGHT_BACK, true);
+ target->UpdateSpeed(MOVE_RUN);
+ target->UpdateSpeed(MOVE_SWIM);
+ target->UpdateSpeed(MOVE_FLIGHT);
+ target->UpdateSpeed(MOVE_RUN_BACK);
+ target->UpdateSpeed(MOVE_SWIM_BACK);
+ target->UpdateSpeed(MOVE_FLIGHT_BACK);
}
void AuraEffect::HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
@@ -2990,9 +2990,9 @@ void AuraEffect::HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_RUN, true);
- target->UpdateSpeed(MOVE_SWIM, true);
- target->UpdateSpeed(MOVE_FLIGHT, true);
+ target->UpdateSpeed(MOVE_RUN);
+ target->UpdateSpeed(MOVE_SWIM);
+ target->UpdateSpeed(MOVE_FLIGHT);
}
/*********************************************************/
@@ -4686,7 +4686,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
case 71563:
if (Aura* newAura = target->AddAura(71564, target))
newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount);
- break;
+ break;
}
}
// AT REMOVE
@@ -4808,7 +4808,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
uint32 spellId = 24659;
if (apply && caster)
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(spellId);
for (uint32 i = 0; i < spell->StackAmount; ++i)
caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID());
@@ -4823,7 +4823,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
uint32 spellId = 24662;
if (apply && caster)
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(spellId);
for (uint32 i = 0; i < spell->StackAmount; ++i)
caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID());
break;
@@ -5248,7 +5248,7 @@ void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 m
if (apply)
{
- target->SetUInt16Value(PLAYER_FIELD_BYTES2, 0, overrideId);
+ target->SetUInt16Value(PLAYER_FIELD_BYTES2, PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET, overrideId);
if (OverrideSpellDataEntry const* overrideSpells = sOverrideSpellDataStore.LookupEntry(overrideId))
for (uint8 i = 0; i < MAX_OVERRIDE_SPELL; ++i)
if (uint32 spellId = overrideSpells->spellId[i])
@@ -5256,7 +5256,7 @@ void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 m
}
else
{
- target->SetUInt16Value(PLAYER_FIELD_BYTES2, 0, 0);
+ target->SetUInt16Value(PLAYER_FIELD_BYTES2, PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET, 0);
if (OverrideSpellDataEntry const* overrideSpells = sOverrideSpellDataStore.LookupEntry(overrideId))
for (uint8 i = 0; i < MAX_OVERRIDE_SPELL; ++i)
if (uint32 spellId = overrideSpells->spellId[i])
@@ -5302,9 +5302,9 @@ void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8
return;
if (apply)
- aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
+ aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER);
else if (!aurApp->GetTarget()->GetBaseMap()->Instanceable())
- aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
+ aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER);
}
void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
@@ -5974,9 +5974,6 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, m_spellInfo);
- if (target->GetHealth() < damage)
- damage = uint32(target->GetHealth());
-
TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s health leech of %s for %u dmg inflicted by %u abs is %u",
GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb);
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h
index b400520a08a..32a7b97fff2 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.h
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.h
@@ -27,7 +27,7 @@ class Aura;
typedef void(AuraEffect::*pAuraEffectHandler)(AuraApplication const* aurApp, uint8 mode, bool apply) const;
-class AuraEffect
+class TC_GAME_API AuraEffect
{
friend void Aura::_InitEffects(uint8 effMask, Unit* caster, int32 *baseAmount);
friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount, Item* castItem, ObjectGuid casterGUID);
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 1ca5df6b327..3d35e4039ba 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;
@@ -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 da075fa25cd..a147957f258 100644
--- a/src/server/game/Spells/Auras/SpellAuras.h
+++ b/src/server/game/Spells/Auras/SpellAuras.h
@@ -39,7 +39,7 @@ class ChargeDropEvent;
// update aura target map every 500 ms instead of every update - reduce amount of grid searcher calls
#define UPDATE_TARGET_MAP_INTERVAL 500
-class AuraApplication
+class TC_GAME_API AuraApplication
{
friend void Unit::_ApplyAura(AuraApplication * aurApp, uint8 effMask);
friend void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode);
@@ -82,7 +82,7 @@ class AuraApplication
void ClientUpdate(bool remove = false);
};
-class Aura
+class TC_GAME_API Aura
{
friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID);
public:
@@ -203,12 +203,12 @@ class 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,11 +269,13 @@ class Aura
ChargeDropEvent* m_dropEvent;
+ std::chrono::steady_clock::time_point m_procCooldown;
+
private:
Unit::AuraApplicationList m_removedApplications;
};
-class UnitAura : public Aura
+class TC_GAME_API UnitAura : public Aura
{
friend Aura* Aura::Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID);
protected:
@@ -294,7 +296,7 @@ class UnitAura : public Aura
DiminishingGroup m_AuraDRGroup:8; // Diminishing
};
-class DynObjAura : public Aura
+class TC_GAME_API DynObjAura : public Aura
{
friend Aura* Aura::Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID);
protected:
@@ -305,7 +307,7 @@ class DynObjAura : public Aura
void FillTargetMap(std::map<Unit*, uint8> & targets, Unit* caster) override;
};
-class ChargeDropEvent : public BasicEvent
+class TC_GAME_API ChargeDropEvent : public BasicEvent
{
friend class Aura;
protected:
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 2561ab57ca5..02e1c07bbc0 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -979,14 +979,22 @@ void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTa
{
CallScriptObjectTargetSelectHandlers(target, effIndex, targetType);
if (target)
- m_targets.SetDst(*target);
+ {
+ SpellDestination dest(*target);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
+ m_targets.SetDst(dest);
+ }
}
else
TC_LOG_DEBUG("spells", "SPELL: cannot find channel spell destination for spell ID %u, effect %u", m_spellInfo->Id, effIndex);
break;
case TARGET_DEST_CHANNEL_CASTER:
- m_targets.SetDst(*channeledSpell->GetCaster());
+ {
+ SpellDestination dest(*channeledSpell->GetCaster());
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
+ m_targets.SetDst(dest);
break;
+ }
default:
ASSERT(false && "Spell::SelectImplicitChannelTargets: received not implemented target type");
break;
@@ -1042,7 +1050,11 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar
if (m_spellInfo->RequiresSpellFocus)
{
if (focusObject)
- m_targets.SetDst(*focusObject);
+ {
+ SpellDestination dest(*focusObject);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
+ m_targets.SetDst(dest);
+ }
return;
}
break;
@@ -1068,7 +1080,6 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar
switch (targetType.GetObjectType())
{
case TARGET_OBJECT_TYPE_UNIT:
- {
if (Unit* unit = target->ToUnit())
AddUnitTarget(unit, effMask, true, false);
else
@@ -1077,7 +1088,6 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar
return;
}
break;
- }
case TARGET_OBJECT_TYPE_GOBJ:
if (GameObject* gobjTarget = target->ToGameObject())
AddGOTarget(gobjTarget, effMask);
@@ -1088,8 +1098,12 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar
}
break;
case TARGET_OBJECT_TYPE_DEST:
- m_targets.SetDst(*target);
+ {
+ SpellDestination dest(*target);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
+ m_targets.SetDst(dest);
break;
+ }
default:
ASSERT(false && "Spell::SelectImplicitNearbyTargets: received not implemented target object type");
break;
@@ -1288,18 +1302,30 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
}
default:
{
- float dist;
+ float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
float angle = targetType.CalcDirectionAngle();
float objSize = m_caster->GetObjectSize();
- if (targetType.GetTarget() == TARGET_DEST_CASTER_SUMMON)
- dist = PET_FOLLOW_DIST;
- else
- dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
if (dist < objSize)
dist = objSize;
- else if (targetType.GetTarget() == TARGET_DEST_CASTER_RANDOM)
- dist = objSize + (dist - objSize) * float(rand_norm());
+
+ switch (targetType.GetTarget())
+ {
+ case TARGET_DEST_CASTER_SUMMON:
+ dist = PET_FOLLOW_DIST;
+ break;
+ case TARGET_DEST_CASTER_RANDOM:
+ dist = objSize + (dist - objSize) * float(rand_norm());
+ break;
+ case TARGET_DEST_CASTER_FRONT_LEFT:
+ case TARGET_DEST_CASTER_BACK_LEFT:
+ case TARGET_DEST_CASTER_FRONT_RIGHT:
+ case TARGET_DEST_CASTER_BACK_RIGHT:
+ dist = dist + objSize;
+ break;
+ default:
+ break;
+ }
Position pos = dest._position;
m_caster->MovePositionToFirstCollision(pos, dist, angle);
@@ -1538,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;
}
}
@@ -2294,8 +2320,13 @@ 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)
{
@@ -2448,8 +2479,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();
@@ -2960,12 +2990,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)
{
- 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
@@ -3502,11 +3537,11 @@ 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())
+ if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING)))
cancel();
}
@@ -3811,7 +3846,7 @@ void Spell::SendSpellStart()
if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell)
castFlags |= CAST_FLAG_PENDING;
- if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO))
+ if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
castFlags |= CAST_FLAG_AMMO;
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
(m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet()))
@@ -3864,7 +3899,7 @@ void Spell::SendSpellGo()
if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell)
castFlags |= CAST_FLAG_PENDING;
- if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO))
+ if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
@@ -4831,6 +4866,10 @@ SpellCastResult Spell::CheckCast(bool strict)
m_customError = SpellCustomErrors(condInfo.mLastFailedCondition->ErrorTextId);
return SpellCastResult(condInfo.mLastFailedCondition->ErrorType);
}
+
+ if (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR)
+ return SPELL_FAILED_DONT_REPORT;
+
if (!condInfo.mLastFailedCondition || !condInfo.mLastFailedCondition->ConditionTarget)
return SPELL_FAILED_CASTER_AURASTATE;
return SPELL_FAILED_BAD_TARGETS;
@@ -5306,6 +5345,9 @@ SpellCastResult Spell::CheckCast(bool strict)
if (!target || m_caster->ToPlayer() == target || (!target->IsInSameRaidWith(m_caster->ToPlayer()) && m_spellInfo->Id != 48955)) // refer-a-friend spell
return SPELL_FAILED_BAD_TARGETS;
+ if (target->HasSummonPending())
+ return SPELL_FAILED_SUMMON_PENDING;
+
// check if our map is dungeon
MapEntry const* map = sMapStore.LookupEntry(m_caster->GetMapId());
if (map->IsDungeon())
@@ -5702,27 +5744,37 @@ bool Spell::CanAutoCast(Unit* target)
{
ObjectGuid targetguid = target->GetGUID();
- for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
+ // check if target already has the same or a more powerful aura
+ for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
- if (m_spellInfo->Effects[j].Effect == SPELL_EFFECT_APPLY_AURA)
+ if (!GetSpellInfo()->Effects[i].IsAura())
+ continue;
+
+ AuraType const& auraType = AuraType(GetSpellInfo()->Effects[i].ApplyAuraName);
+ Unit::AuraEffectList const& auras = target->GetAuraEffectsByType(auraType);
+ for (Unit::AuraEffectList::const_iterator auraIt = auras.begin(); auraIt != auras.end(); ++auraIt)
{
- if (m_spellInfo->StackAmount <= 1)
+ if (GetSpellInfo()->Id == (*auraIt)->GetSpellInfo()->Id)
+ return false;
+
+ switch (sSpellMgr->CheckSpellGroupStackRules(GetSpellInfo(), (*auraIt)->GetSpellInfo()))
{
- if (target->HasAuraEffect(m_spellInfo->Id, j))
+ case SPELL_GROUP_STACK_RULE_EXCLUSIVE:
return false;
- }
- else
- {
- if (AuraEffect* aureff = target->GetAuraEffect(m_spellInfo->Id, j))
- if (aureff->GetBase()->GetStackAmount() >= m_spellInfo->StackAmount)
+ case SPELL_GROUP_STACK_RULE_EXCLUSIVE_FROM_SAME_CASTER:
+ if (GetCaster() == (*auraIt)->GetCaster())
+ return false;
+ break;
+ case SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT: // this one has further checks, but i don't think they're necessary for autocast logic
+ case SPELL_GROUP_STACK_RULE_EXCLUSIVE_HIGHEST:
+ if (abs(GetSpellInfo()->Effects[i].BasePoints) <= abs((*auraIt)->GetAmount()))
return false;
+ break;
+ case SPELL_GROUP_STACK_RULE_DEFAULT:
+ default:
+ break;
}
}
- else if (m_spellInfo->Effects[j].IsAreaAuraEffect())
- {
- if (target->HasAuraEffect(m_spellInfo->Id, j))
- return false;
- }
}
SpellCastResult result = CheckPetCast(target);
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 696d2801645..b7134283ccb 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -80,7 +80,7 @@ enum SpellRangeFlag
SPELL_RANGE_RANGED = 2 //hunter range and ranged weapon
};
-struct SpellDestination
+struct TC_GAME_API SpellDestination
{
SpellDestination();
SpellDestination(float x, float y, float z, float orientation = 0.0f, uint32 mapId = MAPID_INVALID);
@@ -95,7 +95,7 @@ struct SpellDestination
Position _transportOffset;
};
-class SpellCastTargets
+class TC_GAME_API SpellCastTargets
{
public:
SpellCastTargets();
@@ -218,7 +218,7 @@ enum SpellEffectHandleMode
typedef std::list<std::pair<uint32, ObjectGuid>> DispelList;
-class Spell
+class TC_GAME_API Spell
{
friend void Unit::SetCurrentCastSpell(Spell* pSpell);
friend class SpellScript;
@@ -694,21 +694,13 @@ class Spell
ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS];
-#ifdef MAP_BASED_RAND_GEN
- int32 irand(int32 min, int32 max) { return int32 (m_caster->GetMap()->mtRand.randInt(max - min)) + min; }
- uint32 urand(uint32 min, uint32 max) { return m_caster->GetMap()->mtRand.randInt(max - min) + min; }
- int32 rand32() { return m_caster->GetMap()->mtRand.randInt(); }
- double rand_norm() { return m_caster->GetMap()->mtRand.randExc(); }
- double rand_chance() { return m_caster->GetMap()->mtRand.randExc(100.0); }
-#endif
-
Spell(Spell const& right) = delete;
Spell& operator=(Spell const& right) = delete;
};
namespace Trinity
{
- struct WorldObjectSpellTargetCheck
+ struct TC_GAME_API WorldObjectSpellTargetCheck
{
Unit* _caster;
Unit* _referer;
@@ -723,7 +715,7 @@ namespace Trinity
bool operator()(WorldObject* target);
};
- struct WorldObjectSpellNearbyTargetCheck : public WorldObjectSpellTargetCheck
+ struct TC_GAME_API WorldObjectSpellNearbyTargetCheck : public WorldObjectSpellTargetCheck
{
float _range;
Position const* _position;
@@ -732,7 +724,7 @@ namespace Trinity
bool operator()(WorldObject* target);
};
- struct WorldObjectSpellAreaTargetCheck : public WorldObjectSpellTargetCheck
+ struct TC_GAME_API WorldObjectSpellAreaTargetCheck : public WorldObjectSpellTargetCheck
{
float _range;
Position const* _position;
@@ -741,7 +733,7 @@ namespace Trinity
bool operator()(WorldObject* target);
};
- struct WorldObjectSpellConeTargetCheck : public WorldObjectSpellAreaTargetCheck
+ struct TC_GAME_API WorldObjectSpellConeTargetCheck : public WorldObjectSpellAreaTargetCheck
{
float _coneAngle;
WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster,
@@ -749,7 +741,7 @@ namespace Trinity
bool operator()(WorldObject* target);
};
- struct WorldObjectSpellTrajTargetCheck : public WorldObjectSpellAreaTargetCheck
+ struct TC_GAME_API WorldObjectSpellTrajTargetCheck : public WorldObjectSpellAreaTargetCheck
{
WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, SpellInfo const* spellInfo);
bool operator()(WorldObject* target);
@@ -758,7 +750,7 @@ namespace Trinity
typedef void(Spell::*pEffect)(SpellEffIndex effIndex);
-class SpellEvent : public BasicEvent
+class TC_GAME_API SpellEvent : public BasicEvent
{
public:
SpellEvent(Spell* spell);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index f9bf33553cc..9d0370c0255 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -217,7 +217,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create item or create item template and replace by some randon spell loot item
&Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling
&Spell::EffectRenamePet, //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again
- &Spell::EffectNULL, //160 SPELL_EFFECT_160 1 spell - 45534
+ &Spell::EffectForceCast, //160 SPELL_EFFECT_FORCE_CAST_2
&Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert)
&Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec
&Spell::EffectNULL, //163 unused
@@ -250,13 +250,13 @@ void Spell::EffectResurrectNew(SpellEffIndex effIndex)
Player* target = unitTarget->ToPlayer();
- if (target->isResurrectRequested()) // already have one active request
+ if (target->IsResurrectRequested()) // already have one active request
return;
uint32 health = damage;
uint32 mana = m_spellInfo->Effects[effIndex].MiscValue;
ExecuteLogEffectResurrect(effIndex, target);
- target->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
+ target->SetResurrectRequestData(m_caster, health, mana, 0);
SendResurrectRequest(target);
}
@@ -1830,7 +1830,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
sSpellMgr->GetSetOfSpellsInSpellGroup(SPELL_GROUP_ELIXIR_BATTLE, avalibleElixirs);
for (std::set<uint32>::iterator itr = avalibleElixirs.begin(); itr != avalibleElixirs.end();)
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(*itr);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(*itr);
if (spellInfo->SpellLevel < m_spellInfo->SpellLevel || spellInfo->SpellLevel > unitTarget->getLevel())
avalibleElixirs.erase(itr++);
else if (sSpellMgr->IsSpellMemberOfSpellGroup(*itr, SPELL_GROUP_ELIXIR_SHATTRATH))
@@ -2110,8 +2110,7 @@ void Spell::EffectSummonChangeItem(SpellEffIndex effIndex)
else if (player->IsBankPos(pos))
{
ItemPosCountVec dest;
- uint8 msg = player->CanBankItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true);
- if (msg == EQUIP_ERR_OK)
+ if (player->CanBankItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true) == EQUIP_ERR_OK)
{
player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
@@ -2133,7 +2132,7 @@ void Spell::EffectSummonChangeItem(SpellEffIndex effIndex)
player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
- uint8 msg = player->CanEquipItem(m_CastItem->GetSlot(), dest, pNewItem, true);
+ InventoryResult msg = player->CanEquipItem(m_CastItem->GetSlot(), dest, pNewItem, true);
if (msg == EQUIP_ERR_OK || msg == EQUIP_ERR_CANT_DO_RIGHT_NOW)
{
@@ -3662,16 +3661,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, 22682, true);
return;
}
- // Decimate
- case 28374:
- case 54426:
- if (unitTarget)
- {
- int32 decimateDamage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5));
- if (decimateDamage > 0)
- m_caster->CastCustomSpell(28375, SPELLVALUE_BASE_POINT0, decimateDamage, unitTarget);
- }
- return;
// Mirren's Drinking Hat
case 29830:
{
@@ -3782,28 +3771,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
break;
}
- // Roll Dice - Decahedral Dwarven Dice
- case 47770:
- {
- char buf[128];
- const char *gender = "his";
- if (m_caster->getGender() > 0)
- gender = "her";
- sprintf(buf, "%s rubs %s [Decahedral Dwarven Dice] between %s hands and rolls. One %u and one %u.", m_caster->GetName().c_str(), gender, gender, urand(1, 10), urand(1, 10));
- m_caster->TextEmote(buf);
- break;
- }
- // Roll 'dem Bones - Worn Troll Dice
- case 47776:
- {
- char buf[128];
- const char *gender = "his";
- if (m_caster->getGender() > 0)
- gender = "her";
- sprintf(buf, "%s causually tosses %s [Worn Troll Dice]. One %u and one %u.", m_caster->GetName().c_str(), gender, urand(1, 6), urand(1, 6));
- m_caster->TextEmote(buf);
- break;
- }
// Death Knight Initiate Visual
case 51519:
{
@@ -4245,20 +4212,7 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/)
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
- // Evil Twin (ignore player summon, but hide this for summoner)
- if (unitTarget->HasAura(23445))
- return;
-
- float x, y, z;
- m_caster->GetPosition(x, y, z);
-
- unitTarget->ToPlayer()->SetSummonPoint(m_caster->GetMapId(), x, y, z);
-
- WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4);
- data << uint64(m_caster->GetGUID()); // summoner guid
- data << uint32(m_caster->GetZoneId()); // summoner zone
- data << uint32(MAX_PLAYER_SUMMON_DELAY*IN_MILLISECONDS); // auto decline after msecs
- unitTarget->ToPlayer()->GetSession()->SendPacket(&data);
+ unitTarget->ToPlayer()->SendSummonRequestFrom(m_caster);
}
void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/)
@@ -4536,7 +4490,7 @@ void Spell::EffectResurrect(SpellEffIndex effIndex)
Player* target = unitTarget->ToPlayer();
- if (target->isResurrectRequested()) // already have one active request
+ if (target->IsResurrectRequested()) // already have one active request
return;
uint32 health = target->CountPctFromMaxHealth(damage);
@@ -4544,7 +4498,7 @@ void Spell::EffectResurrect(SpellEffIndex effIndex)
ExecuteLogEffectResurrect(effIndex, target);
- target->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
+ target->SetResurrectRequestData(m_caster, health, mana, 0);
SendResurrectRequest(target);
}
@@ -5521,7 +5475,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/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
index adf5fc47c77..31490bea29b 100644
--- a/src/server/game/Spells/SpellHistory.cpp
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -373,7 +373,7 @@ void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /
player->SendDirectMessage(&data);
if (startCooldown)
- StartCooldown(sSpellMgr->EnsureSpellInfo(categoryItr->second->SpellId), itemId, spell);
+ StartCooldown(sSpellMgr->AssertSpellInfo(categoryItr->second->SpellId), itemId, spell);
}
WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
@@ -483,7 +483,7 @@ bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/
bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const
{
- return HasCooldown(sSpellMgr->EnsureSpellInfo(spellId), itemId, ignoreCategoryCooldown);
+ return HasCooldown(sSpellMgr->AssertSpellInfo(spellId), itemId, ignoreCategoryCooldown);
}
uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const
@@ -533,7 +533,7 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim
else
{
Creature* creatureOwner = _owner->ToCreature();
- for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
if (creatureOwner->m_spells[i])
knownSpells.insert(creatureOwner->m_spells[i]);
}
@@ -542,7 +542,7 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim
WorldPacket spellCooldowns;
for (uint32 spellId : knownSpells)
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(spellId);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId);
if (spellInfo->IsCooldownStartedOnEvent())
continue;
@@ -688,7 +688,7 @@ void SpellHistory::RestoreCooldownStateAfterDuel()
// add all profession CDs created while in duel (if any)
for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS ||
spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS)
diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h
index f0a53fe130d..ccc8a7daa96 100644
--- a/src/server/game/Spells/SpellHistory.h
+++ b/src/server/game/Spells/SpellHistory.h
@@ -31,7 +31,7 @@ class SpellInfo;
class Unit;
struct SpellCategoryEntry;
-class SpellHistory
+class TC_GAME_API SpellHistory
{
public:
typedef std::chrono::system_clock Clock;
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 069c794ca8b..ac157b48783 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -418,7 +418,8 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const
level = int32(_spellInfo->MaxLevel);
else if (level < int32(_spellInfo->BaseLevel))
level = int32(_spellInfo->BaseLevel);
- level -= int32(_spellInfo->SpellLevel);
+ if (!_spellInfo->IsPassive())
+ level -= int32(_spellInfo->SpellLevel);
basePoints += int32(level * basePointsPerLevel);
}
@@ -524,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
@@ -754,7 +755,7 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] =
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 157 SPELL_EFFECT_CREATE_ITEM_2
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 158 SPELL_EFFECT_MILLING
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 159 SPELL_EFFECT_ALLOW_RENAME_PET
- {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 160 SPELL_EFFECT_160
+ {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 160 SPELL_EFFECT_FORCE_CAST_2
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 161 SPELL_EFFECT_TALENT_SPEC_COUNT
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 162 SPELL_EFFECT_TALENT_SPEC_SELECT
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 163 SPELL_EFFECT_163
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index ba658c885fa..1530235174a 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -187,13 +187,14 @@ enum SpellCustomAttributes
SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER = 0x00010000,
SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET = 0x00020000,
SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET = 0x00040000,
+ SPELL_ATTR0_CU_NEEDS_AMMO_DATA = 0x00080000,
SPELL_ATTR0_CU_NEGATIVE = SPELL_ATTR0_CU_NEGATIVE_EFF0 | SPELL_ATTR0_CU_NEGATIVE_EFF1 | SPELL_ATTR0_CU_NEGATIVE_EFF2
};
uint32 GetTargetFlagMask(SpellTargetObjectTypes objType);
-class SpellImplicitTargetInfo
+class TC_GAME_API SpellImplicitTargetInfo
{
private:
Targets _target;
@@ -224,7 +225,7 @@ private:
static StaticData _data[TOTAL_SPELL_TARGETS];
};
-class SpellEffectInfo
+class TC_GAME_API SpellEffectInfo
{
SpellInfo const* _spellInfo;
uint8 _effIndex;
@@ -290,7 +291,7 @@ private:
static StaticData _data[TOTAL_SPELL_EFFECTS];
};
-class SpellInfo
+class TC_GAME_API SpellInfo
{
public:
uint32 Id;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 8bb0e401a6c..56042e05257 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;
@@ -3056,6 +3048,7 @@ void SpellMgr::LoadSpellInfoCorrections()
case 48246: // Ball of Flame
case 36327: // Shoot Arcane Explosion Arrow
case 55479: // Force Obedience
+ case 28560: // Summon Blizzard (Sapphiron)
spellInfo->MaxAffectedTargets = 1;
break;
case 36384: // Skartax Purple Beam
@@ -3241,6 +3234,8 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx4 = 0;
break;
case 8145: // Tremor Totem (instant pulse)
+ spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS;
+ /*no break*/
case 6474: // Earthbind Totem (instant pulse)
spellInfo->AttributesEx5 |= SPELL_ATTR5_START_PERIODIC_AT_APPLY;
break;
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index 827e3c671ab..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;
@@ -441,7 +441,7 @@ enum EffectRadiusIndex
};
// Spell pet auras
-class PetAura
+class TC_GAME_API PetAura
{
private:
typedef std::unordered_map<uint32, uint32> PetAuraMap;
@@ -488,7 +488,7 @@ class PetAura
};
typedef std::map<uint32, PetAura> SpellPetAuraMap;
-struct SpellArea
+struct TC_GAME_API SpellArea
{
uint32 spellId;
uint32 areaId; // zone/subzone/or 0 is not limited to zone
@@ -593,13 +593,13 @@ inline bool IsProfessionOrRidingSkill(uint32 skill)
bool IsPartOfSkillLine(uint32 skillId, uint32 spellId);
// spell diminishing returns
-DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered);
-DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group);
-DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group);
-int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellInfo const* spellproto);
-bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group);
+TC_GAME_API DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered);
+TC_GAME_API DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group);
+TC_GAME_API DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group);
+TC_GAME_API int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellInfo const* spellproto);
+TC_GAME_API bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group);
-class SpellMgr
+class TC_GAME_API SpellMgr
{
// Constructors
private:
@@ -692,7 +692,7 @@ class SpellMgr
// SpellInfo object management
SpellInfo const* GetSpellInfo(uint32 spellId) const { return spellId < GetSpellInfoStoreSize() ? mSpellInfoMap[spellId] : NULL; }
// Use this only with 100% valid spellIds
- SpellInfo const* EnsureSpellInfo(uint32 spellId) const
+ SpellInfo const* AssertSpellInfo(uint32 spellId) const
{
ASSERT(spellId < GetSpellInfoStoreSize());
SpellInfo const* spellInfo = mSpellInfoMap[spellId];
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 6876f8fa7ef..e2598386466 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -16,6 +16,7 @@
*/
#include "Spell.h"
+#include "ScriptMgr.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "SpellMgr.h"
@@ -50,6 +51,12 @@ void _SpellScript::_Init(std::string const* scriptname, uint32 spellId)
m_currentScriptState = SPELL_SCRIPT_STATE_NONE;
m_scriptName = scriptname;
m_scriptSpellId = spellId;
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Acquire a strong reference to the binary code
+ // to keep it loaded until all spells are destroyed.
+ m_moduleReference = sScriptMgr->AcquireModuleReferenceOfScriptName(*scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
std::string const* _SpellScript::_GetScriptName() const
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 77a993fffae..539bc54cc94 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -22,6 +22,7 @@
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "Spell.h"
+#include "ScriptReloadMgr.h"
#include <stack>
class Unit;
@@ -52,7 +53,7 @@ enum SpellScriptState
#define SPELL_SCRIPT_STATE_END SPELL_SCRIPT_STATE_UNLOADING + 1
// helper class from which SpellScript and SpellAura derive, use these classes instead
-class _SpellScript
+class TC_GAME_API _SpellScript
{
// internal use classes & functions
// DO NOT OVERRIDE THESE IN SCRIPTS
@@ -68,7 +69,7 @@ class _SpellScript
std::string const* _GetScriptName() const;
protected:
- class EffectHook
+ class TC_GAME_API EffectHook
{
public:
EffectHook(uint8 _effIndex);
@@ -82,7 +83,7 @@ class _SpellScript
uint8 effIndex;
};
- class EffectNameCheck
+ class TC_GAME_API EffectNameCheck
{
public:
EffectNameCheck(uint16 _effName) { effName = _effName; }
@@ -92,7 +93,7 @@ class _SpellScript
uint16 effName;
};
- class EffectAuraNameCheck
+ class TC_GAME_API EffectAuraNameCheck
{
public:
EffectAuraNameCheck(uint16 _effAurName) { effAurName = _effAurName; }
@@ -105,6 +106,16 @@ class _SpellScript
uint8 m_currentScriptState;
std::string const* m_scriptName;
uint32 m_scriptSpellId;
+
+ private:
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+
+ // Strong reference to keep the binary code loaded
+ std::shared_ptr<ModuleReference> m_moduleReference;
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
public:
//
// SpellScript/AuraScript interface base
@@ -149,7 +160,7 @@ enum SpellScriptHookType
#define HOOK_SPELL_END SPELL_SCRIPT_HOOK_CHECK_CAST + 1
#define HOOK_SPELL_COUNT HOOK_SPELL_END - HOOK_SPELL_START
-class SpellScript : public _SpellScript
+class TC_GAME_API SpellScript : public _SpellScript
{
// internal use classes & functions
// DO NOT OVERRIDE THESE IN SCRIPTS
@@ -165,7 +176,7 @@ class SpellScript : public _SpellScript
SPELLSCRIPT_FUNCTION_TYPE_DEFINES(SpellScript)
- class CastHandler
+ class TC_GAME_API CastHandler
{
public:
CastHandler(SpellCastFnType _pCastHandlerScript);
@@ -174,7 +185,7 @@ class SpellScript : public _SpellScript
SpellCastFnType pCastHandlerScript;
};
- class CheckCastHandler
+ class TC_GAME_API CheckCastHandler
{
public:
CheckCastHandler(SpellCheckCastFnType checkCastHandlerScript);
@@ -183,7 +194,7 @@ class SpellScript : public _SpellScript
SpellCheckCastFnType _checkCastHandlerScript;
};
- class EffectHandler : public _SpellScript::EffectNameCheck, public _SpellScript::EffectHook
+ class TC_GAME_API EffectHandler : public _SpellScript::EffectNameCheck, public _SpellScript::EffectHook
{
public:
EffectHandler(SpellEffectFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName);
@@ -194,7 +205,7 @@ class SpellScript : public _SpellScript
SpellEffectFnType pEffectHandlerScript;
};
- class HitHandler
+ class TC_GAME_API HitHandler
{
public:
HitHandler(SpellHitFnType _pHitHandlerScript);
@@ -203,7 +214,7 @@ class SpellScript : public _SpellScript
SpellHitFnType pHitHandlerScript;
};
- class TargetHook : public _SpellScript::EffectHook
+ class TC_GAME_API TargetHook : public _SpellScript::EffectHook
{
public:
TargetHook(uint8 _effectIndex, uint16 _targetType, bool _area, bool _dest);
@@ -216,7 +227,7 @@ class SpellScript : public _SpellScript
bool dest;
};
- class ObjectAreaTargetSelectHandler : public TargetHook
+ class TC_GAME_API ObjectAreaTargetSelectHandler : public TargetHook
{
public:
ObjectAreaTargetSelectHandler(SpellObjectAreaTargetSelectFnType _pObjectAreaTargetSelectHandlerScript, uint8 _effIndex, uint16 _targetType);
@@ -225,7 +236,7 @@ class SpellScript : public _SpellScript
SpellObjectAreaTargetSelectFnType pObjectAreaTargetSelectHandlerScript;
};
- class ObjectTargetSelectHandler : public TargetHook
+ class TC_GAME_API ObjectTargetSelectHandler : public TargetHook
{
public:
ObjectTargetSelectHandler(SpellObjectTargetSelectFnType _pObjectTargetSelectHandlerScript, uint8 _effIndex, uint16 _targetType);
@@ -234,7 +245,7 @@ class SpellScript : public _SpellScript
SpellObjectTargetSelectFnType pObjectTargetSelectHandlerScript;
};
- class DestinationTargetSelectHandler : public TargetHook
+ class TC_GAME_API DestinationTargetSelectHandler : public TargetHook
{
public:
DestinationTargetSelectHandler(SpellDestinationTargetSelectFnType _DestinationTargetSelectHandlerScript, uint8 _effIndex, uint16 _targetType);
@@ -470,7 +481,7 @@ enum AuraScriptHookType
#define HOOK_AURA_EFFECT_END HOOK_AURA_EFFECT_CALC_SPELLMOD + 1
#define HOOK_AURA_EFFECT_COUNT HOOK_AURA_EFFECT_END - HOOK_AURA_EFFECT_START
*/
-class AuraScript : public _SpellScript
+class TC_GAME_API AuraScript : public _SpellScript
{
// internal use classes & functions
// DO NOT OVERRIDE THESE IN SCRIPTS
@@ -493,7 +504,7 @@ class AuraScript : public _SpellScript
AURASCRIPT_FUNCTION_TYPE_DEFINES(AuraScript)
- class CheckAreaTargetHandler
+ class TC_GAME_API CheckAreaTargetHandler
{
public:
CheckAreaTargetHandler(AuraCheckAreaTargetFnType pHandlerScript);
@@ -501,7 +512,7 @@ class AuraScript : public _SpellScript
private:
AuraCheckAreaTargetFnType pHandlerScript;
};
- class AuraDispelHandler
+ class TC_GAME_API AuraDispelHandler
{
public:
AuraDispelHandler(AuraDispelFnType pHandlerScript);
@@ -509,14 +520,14 @@ class AuraScript : public _SpellScript
private:
AuraDispelFnType pHandlerScript;
};
- class EffectBase : public _SpellScript::EffectAuraNameCheck, public _SpellScript::EffectHook
+ class TC_GAME_API EffectBase : public _SpellScript::EffectAuraNameCheck, public _SpellScript::EffectHook
{
public:
EffectBase(uint8 _effIndex, uint16 _effName);
std::string ToString();
bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex) override;
};
- class EffectPeriodicHandler : public EffectBase
+ class TC_GAME_API EffectPeriodicHandler : public EffectBase
{
public:
EffectPeriodicHandler(AuraEffectPeriodicFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName);
@@ -524,7 +535,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectPeriodicFnType pEffectHandlerScript;
};
- class EffectUpdatePeriodicHandler : public EffectBase
+ class TC_GAME_API EffectUpdatePeriodicHandler : public EffectBase
{
public:
EffectUpdatePeriodicHandler(AuraEffectUpdatePeriodicFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName);
@@ -532,7 +543,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectUpdatePeriodicFnType pEffectHandlerScript;
};
- class EffectCalcAmountHandler : public EffectBase
+ class TC_GAME_API EffectCalcAmountHandler : public EffectBase
{
public:
EffectCalcAmountHandler(AuraEffectCalcAmountFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName);
@@ -540,7 +551,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectCalcAmountFnType pEffectHandlerScript;
};
- class EffectCalcPeriodicHandler : public EffectBase
+ class TC_GAME_API EffectCalcPeriodicHandler : public EffectBase
{
public:
EffectCalcPeriodicHandler(AuraEffectCalcPeriodicFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName);
@@ -548,7 +559,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectCalcPeriodicFnType pEffectHandlerScript;
};
- class EffectCalcSpellModHandler : public EffectBase
+ class TC_GAME_API EffectCalcSpellModHandler : public EffectBase
{
public:
EffectCalcSpellModHandler(AuraEffectCalcSpellModFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName);
@@ -556,7 +567,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectCalcSpellModFnType pEffectHandlerScript;
};
- class EffectApplyHandler : public EffectBase
+ class TC_GAME_API EffectApplyHandler : public EffectBase
{
public:
EffectApplyHandler(AuraEffectApplicationModeFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName, AuraEffectHandleModes _mode);
@@ -565,7 +576,7 @@ class AuraScript : public _SpellScript
AuraEffectApplicationModeFnType pEffectHandlerScript;
AuraEffectHandleModes mode;
};
- class EffectAbsorbHandler : public EffectBase
+ class TC_GAME_API EffectAbsorbHandler : public EffectBase
{
public:
EffectAbsorbHandler(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex);
@@ -573,7 +584,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectAbsorbFnType pEffectHandlerScript;
};
- class EffectManaShieldHandler : public EffectBase
+ class TC_GAME_API EffectManaShieldHandler : public EffectBase
{
public:
EffectManaShieldHandler(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex);
@@ -581,7 +592,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectAbsorbFnType pEffectHandlerScript;
};
- class EffectSplitHandler : public EffectBase
+ class TC_GAME_API EffectSplitHandler : public EffectBase
{
public:
EffectSplitHandler(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex);
@@ -589,7 +600,7 @@ class AuraScript : public _SpellScript
private:
AuraEffectSplitFnType pEffectHandlerScript;
};
- class CheckProcHandler
+ class TC_GAME_API CheckProcHandler
{
public:
CheckProcHandler(AuraCheckProcFnType handlerScript);
@@ -597,7 +608,7 @@ class AuraScript : public _SpellScript
private:
AuraCheckProcFnType _HandlerScript;
};
- class AuraProcHandler
+ class TC_GAME_API AuraProcHandler
{
public:
AuraProcHandler(AuraProcFnType handlerScript);
@@ -605,7 +616,7 @@ class AuraScript : public _SpellScript
private:
AuraProcFnType _HandlerScript;
};
- class EffectProcHandler : public EffectBase
+ class TC_GAME_API EffectProcHandler : public EffectBase
{
public:
EffectProcHandler(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName);
@@ -645,7 +656,7 @@ class AuraScript : public _SpellScript
AuraApplication const* m_auraApplication;
bool m_defaultActionPrevented;
- class ScriptStateStore
+ class TC_GAME_API ScriptStateStore
{
public:
AuraApplication const* _auraApplication;
diff --git a/src/server/game/Texts/CreatureTextMgr.h b/src/server/game/Texts/CreatureTextMgr.h
index e364eb6eefa..d7953463a97 100644
--- a/src/server/game/Texts/CreatureTextMgr.h
+++ b/src/server/game/Texts/CreatureTextMgr.h
@@ -75,7 +75,7 @@ typedef std::unordered_map<uint32, CreatureTextHolder> CreatureTextMap; // a
typedef std::map<CreatureTextId, CreatureTextLocale> LocaleCreatureTextMap;
-class CreatureTextMgr
+class TC_GAME_API CreatureTextMgr
{
private:
CreatureTextMgr() { }
diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h
index 59f38e6d23f..ff2cd656dce 100644
--- a/src/server/game/Tickets/TicketMgr.h
+++ b/src/server/game/Tickets/TicketMgr.h
@@ -84,7 +84,7 @@ enum TicketType
TICKET_TYPE_CHARACTER_DELETED = 2,
};
-class GmTicket
+class TC_GAME_API GmTicket
{
public:
GmTicket();
@@ -181,7 +181,7 @@ private:
};
typedef std::map<uint32, GmTicket*> GmTicketList;
-class TicketMgr
+class TC_GAME_API TicketMgr
{
private:
TicketMgr();
diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.h b/src/server/game/Tools/CharacterDatabaseCleaner.h
index ecbd6d0a790..f1e6900a6bb 100644
--- a/src/server/game/Tools/CharacterDatabaseCleaner.h
+++ b/src/server/game/Tools/CharacterDatabaseCleaner.h
@@ -30,20 +30,20 @@ namespace CharacterDatabaseCleaner
CLEANING_FLAG_QUESTSTATUS = 0x10
};
- void CleanDatabase();
+ TC_GAME_API void CleanDatabase();
- void CheckUnique(const char* column, const char* table, bool (*check)(uint32));
+ TC_GAME_API void CheckUnique(const char* column, const char* table, bool (*check)(uint32));
- bool AchievementProgressCheck(uint32 criteria);
- bool SkillCheck(uint32 skill);
- bool SpellCheck(uint32 spell_id);
- bool TalentCheck(uint32 talent_id);
+ TC_GAME_API bool AchievementProgressCheck(uint32 criteria);
+ TC_GAME_API bool SkillCheck(uint32 skill);
+ TC_GAME_API bool SpellCheck(uint32 spell_id);
+ TC_GAME_API bool TalentCheck(uint32 talent_id);
- void CleanCharacterAchievementProgress();
- void CleanCharacterSkills();
- void CleanCharacterSpell();
- void CleanCharacterTalent();
- void CleanCharacterQuestStatus();
+ TC_GAME_API void CleanCharacterAchievementProgress();
+ TC_GAME_API void CleanCharacterSkills();
+ TC_GAME_API void CleanCharacterSpell();
+ TC_GAME_API void CleanCharacterTalent();
+ TC_GAME_API void CleanCharacterQuestStatus();
}
#endif
diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp
index 113fea2ea35..4c5ac1dab5e 100644
--- a/src/server/game/Tools/PlayerDump.cpp
+++ b/src/server/game/Tools/PlayerDump.cpp
@@ -315,10 +315,10 @@ bool PlayerDumpWriter::DumpTable(std::string& dump, ObjectGuid::LowType guid, ch
break;
case DTT_CHARACTER:
{
- if (result->GetFieldCount() <= 68) // avoid crashes on next check
+ if (result->GetFieldCount() <= 73) // avoid crashes on next check
TC_LOG_FATAL("misc", "PlayerDumpWriter::DumpTable - Trying to access non-existing or wrong positioned field (`deleteInfos_Account`) in `characters` table.");
- if (result->Fetch()[68].GetUInt32()) // characters.deleteInfos_Account - if filled error
+ if (result->Fetch()[73].GetUInt32()) // characters.deleteInfos_Account - if filled error
return false;
break;
}
@@ -549,11 +549,11 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
ROLLBACK(DUMP_FILE_BROKEN);
const char null[5] = "NULL";
- if (!ChangeNth(line, 69, null)) // characters.deleteInfos_Account
+ if (!ChangeNth(line, 74, null)) // characters.deleteInfos_Account
ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, 70, null)) // characters.deleteInfos_Name
+ if (!ChangeNth(line, 75, null)) // characters.deleteInfos_Name
ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, 71, null)) // characters.deleteDate
+ if (!ChangeNth(line, 76, null)) // characters.deleteDate
ROLLBACK(DUMP_FILE_BROKEN);
break;
}
diff --git a/src/server/game/Tools/PlayerDump.h b/src/server/game/Tools/PlayerDump.h
index 95d6ed2d04b..12cafc6ad48 100644
--- a/src/server/game/Tools/PlayerDump.h
+++ b/src/server/game/Tools/PlayerDump.h
@@ -62,7 +62,7 @@ enum DumpReturn
DUMP_CHARACTER_DELETED
};
-class PlayerDump
+class TC_GAME_API PlayerDump
{
public:
typedef std::set<ObjectGuid::LowType> DumpGuidSet;
@@ -72,7 +72,7 @@ class PlayerDump
PlayerDump() { }
};
-class PlayerDumpWriter : public PlayerDump
+class TC_GAME_API PlayerDumpWriter : public PlayerDump
{
public:
PlayerDumpWriter() { }
@@ -90,7 +90,7 @@ class PlayerDumpWriter : public PlayerDump
DumpGuidSet items;
};
-class PlayerDumpReader : public PlayerDump
+class TC_GAME_API PlayerDumpReader : public PlayerDump
{
public:
PlayerDumpReader() { }
diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h
index 35ee18d4c02..b0aa496cbe8 100644
--- a/src/server/game/Warden/Warden.h
+++ b/src/server/game/Warden/Warden.h
@@ -92,7 +92,7 @@ struct ClientWardenModule
class WorldSession;
-class Warden
+class TC_GAME_API Warden
{
friend class WardenWin;
friend class WardenMac;
diff --git a/src/server/game/Warden/WardenCheckMgr.h b/src/server/game/Warden/WardenCheckMgr.h
index a09dc3a6dbb..7349fd5b589 100644
--- a/src/server/game/Warden/WardenCheckMgr.h
+++ b/src/server/game/Warden/WardenCheckMgr.h
@@ -48,7 +48,7 @@ struct WardenCheckResult
BigNumber Result; // MEM_CHECK
};
-class WardenCheckMgr
+class TC_GAME_API WardenCheckMgr
{
private:
WardenCheckMgr();
diff --git a/src/server/game/Warden/WardenMac.h b/src/server/game/Warden/WardenMac.h
index 26a2d86524e..c7435502f63 100644
--- a/src/server/game/Warden/WardenMac.h
+++ b/src/server/game/Warden/WardenMac.h
@@ -28,7 +28,7 @@
class WorldSession;
class Warden;
-class WardenMac : public Warden
+class TC_GAME_API WardenMac : public Warden
{
public:
WardenMac();
diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h
index 4bf1af77c47..b3e6d7c586c 100644
--- a/src/server/game/Warden/WardenWin.h
+++ b/src/server/game/Warden/WardenWin.h
@@ -62,7 +62,7 @@ struct WardenInitModuleRequest
class WorldSession;
class Warden;
-class WardenWin : public Warden
+class TC_GAME_API WardenWin : public Warden
{
public:
WardenWin();
diff --git a/src/server/game/Weather/Weather.h b/src/server/game/Weather/Weather.h
index c1f029d6264..6a770615308 100644
--- a/src/server/game/Weather/Weather.h
+++ b/src/server/game/Weather/Weather.h
@@ -62,7 +62,7 @@ enum WeatherState
};
/// Weather for one zone
-class Weather
+class TC_GAME_API Weather
{
public:
diff --git a/src/server/game/Weather/WeatherMgr.h b/src/server/game/Weather/WeatherMgr.h
index 97c541fd3c0..e3dd4ac9ec4 100644
--- a/src/server/game/Weather/WeatherMgr.h
+++ b/src/server/game/Weather/WeatherMgr.h
@@ -30,15 +30,15 @@ class Player;
namespace WeatherMgr
{
- void LoadWeatherData();
+ TC_GAME_API void LoadWeatherData();
- Weather* FindWeather(uint32 id);
- Weather* AddWeather(uint32 zone_id);
- void RemoveWeather(uint32 zone_id);
+ TC_GAME_API Weather* FindWeather(uint32 id);
+ TC_GAME_API Weather* AddWeather(uint32 zone_id);
+ TC_GAME_API void RemoveWeather(uint32 zone_id);
- void SendFineWeatherUpdateToPlayer(Player* player);
+ TC_GAME_API void SendFineWeatherUpdateToPlayer(Player* player);
- void Update(uint32 diff);
+ TC_GAME_API void Update(uint32 diff);
}
#endif
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 2263fbca49f..d84fe11383f 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -54,6 +54,7 @@
#include "PoolMgr.h"
#include "GitRevision.h"
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "SmartAI.h"
@@ -65,19 +66,20 @@
#include "WaypointMovementGenerator.h"
#include "WeatherMgr.h"
#include "WorldSession.h"
+#include "M2Stores.h"
+TC_GAME_API std::atomic<bool> World::m_stopEvent(false);
+TC_GAME_API uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
-std::atomic<bool> World::m_stopEvent(false);
-uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
-std::atomic<uint32> World::m_worldLoopCounter(0);
+TC_GAME_API std::atomic<uint32> World::m_worldLoopCounter(0);
-float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE;
-float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;
-float World::m_MaxVisibleDistanceInBGArenas = DEFAULT_VISIBILITY_BGARENAS;
+TC_GAME_API float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE;
+TC_GAME_API float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;
+TC_GAME_API float World::m_MaxVisibleDistanceInBGArenas = DEFAULT_VISIBILITY_BGARENAS;
-int32 World::m_visibility_notify_periodOnContinents = DEFAULT_VISIBILITY_NOTIFY_PERIOD;
-int32 World::m_visibility_notify_periodInInstances = DEFAULT_VISIBILITY_NOTIFY_PERIOD;
-int32 World::m_visibility_notify_periodInBGArenas = DEFAULT_VISIBILITY_NOTIFY_PERIOD;
+TC_GAME_API int32 World::m_visibility_notify_periodOnContinents = DEFAULT_VISIBILITY_NOTIFY_PERIOD;
+TC_GAME_API int32 World::m_visibility_notify_periodInInstances = DEFAULT_VISIBILITY_NOTIFY_PERIOD;
+TC_GAME_API int32 World::m_visibility_notify_periodInBGArenas = DEFAULT_VISIBILITY_NOTIFY_PERIOD;
/// World constructor
World::World()
@@ -898,6 +900,7 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_ALLOW_GM_GROUP] = sConfigMgr->GetBoolDefault("GM.AllowInvite", false);
m_bool_configs[CONFIG_GM_LOWER_SECURITY] = sConfigMgr->GetBoolDefault("GM.LowerSecurity", false);
m_float_configs[CONFIG_CHANCE_OF_GM_SURVEY] = sConfigMgr->GetFloatDefault("GM.TicketSystem.ChanceOfGMSurvey", 50.0f);
+ m_int_configs[CONFIG_FORCE_SHUTDOWN_THRESHOLD] = sConfigMgr->GetIntDefault("GM.ForceShutdownThreshold", 30);
m_int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetIntDefault("Visibility.GroupMode", 1);
@@ -1066,7 +1069,6 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfigMgr->GetBoolDefault("Arena.AutoDistributePoints", false);
m_int_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfigMgr->GetIntDefault ("Arena.AutoDistributeInterval", 7);
m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE] = sConfigMgr->GetBoolDefault("Arena.QueueAnnouncer.Enable", false);
- m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetBoolDefault("Arena.QueueAnnouncer.PlayerOnly", false);
m_int_configs[CONFIG_ARENA_SEASON_ID] = sConfigMgr->GetIntDefault ("Arena.ArenaSeason.ID", 1);
m_int_configs[CONFIG_ARENA_START_RATING] = sConfigMgr->GetIntDefault ("Arena.ArenaStartRating", 0);
m_int_configs[CONFIG_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetIntDefault ("Arena.ArenaStartPersonalRating", 1000);
@@ -1081,6 +1083,7 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetBoolDefault("OffhandCheckAtSpellUnlearn", true);
m_int_configs[CONFIG_CREATURE_PICKPOCKET_REFILL] = sConfigMgr->GetIntDefault("Creature.PickPocketRefillDelay", 10 * MINUTE);
+ m_int_configs[CONFIG_CREATURE_STOP_FOR_PLAYER] = sConfigMgr->GetIntDefault("Creature.MovingStopTimeForPlayer", 3 * MINUTE * IN_MILLISECONDS);
if (int32 clientCacheId = sConfigMgr->GetIntDefault("ClientCacheVersion", 0))
{
@@ -1319,6 +1322,14 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Creature.Zone.Area.Data", false);
m_bool_configs[CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Gameoject.Zone.Area.Data", false);
+ // HotSwap
+ m_bool_configs[CONFIG_HOTSWAP_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.Enabled", true);
+ m_bool_configs[CONFIG_HOTSWAP_RECOMPILER_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableReCompiler", true);
+ m_bool_configs[CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableEarlyTermination", true);
+ m_bool_configs[CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableBuildFileRecreation", true);
+ 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);
+
// call ScriptMgr if we're reloading the configuration
if (reload)
sScriptMgr->OnConfigLoad(reload);
@@ -1393,6 +1404,9 @@ void World::SetInitialWorldSettings()
LoadDBCStores(m_dataPath);
DetectDBCLang();
+ // Load cinematic cameras
+ LoadM2Cameras(m_dataPath);
+
std::vector<uint32> mapIds;
for (uint32 mapId = 0; mapId < sMapStore.GetNumRows(); mapId++)
if (sMapStore.LookupEntry(mapId))
@@ -1826,6 +1840,8 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_PINGDB].SetInterval(getIntConfig(CONFIG_DB_PING_INTERVAL)*MINUTE*IN_MILLISECONDS); // Mysql ping time in minutes
+ m_timers[WUPDATE_CHECK_FILECHANGES].SetInterval(500);
+
//to set mailtimer to return mails every day between 4 and 5 am
//mailtimer is increased when updating auctions
//one second is 1000 -(tested on win system)
@@ -2109,6 +2125,13 @@ void World::Update(uint32 diff)
m_timers[WUPDATE_AHBOT].Reset();
}
+ /// <li> Handle file changes
+ if (m_timers[WUPDATE_CHECK_FILECHANGES].Passed())
+ {
+ sScriptReloadMgr->Update();
+ m_timers[WUPDATE_CHECK_FILECHANGES].Reset();
+ }
+
/// <li> Handle session updates when the timer has passed
ResetTimeDiffRecord();
UpdateSessions(diff);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 6d1f6313ae5..330e78cf510 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -28,7 +28,7 @@
#include "Timer.h"
#include "SharedDefines.h"
#include "QueryResult.h"
-#include "Callback.h"
+#include "QueryCallback.h"
#include "Realm/Realm.h"
#include <atomic>
@@ -56,7 +56,8 @@ enum ServerMessageType
enum ShutdownMask
{
SHUTDOWN_MASK_RESTART = 1,
- SHUTDOWN_MASK_IDLE = 2
+ SHUTDOWN_MASK_IDLE = 2,
+ SHUTDOWN_MASK_FORCE = 4
};
enum ShutdownExitCode
@@ -81,6 +82,7 @@ enum WorldTimers
WUPDATE_DELETECHARS,
WUPDATE_AHBOT,
WUPDATE_PINGDB,
+ WUPDATE_CHECK_FILECHANGES,
WUPDATE_COUNT
};
@@ -129,7 +131,6 @@ enum WorldBoolConfigs
CONFIG_BG_XP_FOR_KILL,
CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS,
CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE,
- CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY,
CONFIG_ARENA_SEASON_IN_PROGRESS,
CONFIG_ARENA_LOG_EXTENDED_INFO,
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
@@ -168,6 +169,12 @@ enum WorldBoolConfigs
CONFIG_RESET_DUEL_HEALTH_MANA,
CONFIG_BASEMAP_LOAD_GRIDS,
CONFIG_INSTANCEMAP_LOAD_GRIDS,
+ CONFIG_HOTSWAP_ENABLED,
+ CONFIG_HOTSWAP_RECOMPILER_ENABLED,
+ CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED,
+ CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED,
+ CONFIG_HOTSWAP_INSTALL_ENABLED,
+ CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED,
BOOL_CONFIG_VALUE_COUNT
};
@@ -246,6 +253,7 @@ enum WorldIntConfigs
CONFIG_GM_LEVEL_IN_GM_LIST,
CONFIG_GM_LEVEL_IN_WHO_LIST,
CONFIG_START_GM_LEVEL,
+ CONFIG_FORCE_SHUTDOWN_THRESHOLD,
CONFIG_GROUP_VISIBILITY,
CONFIG_MAIL_DELIVERY_DELAY,
CONFIG_UPTIME_UPDATE,
@@ -351,6 +359,7 @@ enum WorldIntConfigs
CONFIG_BG_REWARD_LOSER_HONOR_LAST,
CONFIG_BIRTHDAY_TIME,
CONFIG_CREATURE_PICKPOCKET_REFILL,
+ CONFIG_CREATURE_STOP_FOR_PLAYER,
CONFIG_AHBOT_UPDATE_INTERVAL,
CONFIG_CHARTER_COST_GUILD,
CONFIG_CHARTER_COST_ARENA_2v2,
@@ -537,7 +546,7 @@ struct CharacterInfo
};
/// The World
-class World
+class TC_GAME_API World
{
public:
static World* instance();
@@ -869,7 +878,7 @@ class World
std::deque<std::future<PreparedQueryResult>> m_realmCharCallbacks;
};
-extern Realm realm;
+TC_GAME_API extern Realm realm;
#define sWorld World::instance()
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index aadfd3e616c..31ba073e77d 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -8,69 +8,232 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# Enable precompiled headers when using the GCC compiler.
-
-message(STATUS "SCRIPT PREPARATIONS")
-
-macro(PrepareScripts name out)
- file(GLOB_RECURSE found
- ${name}/*.h
- ${name}/*.cpp
- )
- list(APPEND ${out} ${found})
- message(STATUS " -> Prepared: ${name}")
-endmacro(PrepareScripts)
-
-PrepareScripts(Spells PRIVATE_SOURCES)
-PrepareScripts(Commands PRIVATE_SOURCES)
-
-if(SCRIPTS)
- PrepareScripts(Custom PRIVATE_SOURCES)
- PrepareScripts(World PRIVATE_SOURCES)
- PrepareScripts(OutdoorPvP PRIVATE_SOURCES)
- PrepareScripts(EasternKingdoms PRIVATE_SOURCES)
- PrepareScripts(Kalimdor PRIVATE_SOURCES)
- PrepareScripts(Outland PRIVATE_SOURCES)
- PrepareScripts(Northrend PRIVATE_SOURCES)
- PrepareScripts(Events PRIVATE_SOURCES)
- PrepareScripts(Pet PRIVATE_SOURCES)
+message("")
+
+# Make the script module list available in the current scope
+GetScriptModuleList(SCRIPT_MODULE_LIST)
+
+# Make the native install offset available in this scope
+GetInstallOffset(INSTALL_OFFSET)
+
+# Sets the SCRIPTS_${SCRIPT_MODULE} variables
+# when using predefined templates for script building
+# like dynamic, static, minimal-static...
+# Sets SCRIPTS_DEFAULT_LINKAGE
+if (SCRIPTS MATCHES "dynamic")
+ set(SCRIPTS_DEFAULT_LINKAGE "dynamic")
+elseif(SCRIPTS MATCHES "static")
+ set(SCRIPTS_DEFAULT_LINKAGE "static")
+else()
+ set(SCRIPTS_DEFAULT_LINKAGE "disabled")
+endif()
+# Sets SCRIPTS_USE_WHITELIST
+# Sets SCRIPTS_WHITELIST
+if (SCRIPTS MATCHES "minimal")
+ set(SCRIPTS_USE_WHITELIST ON)
+ # Whitelist which is used when minimal is selected
+ list(APPEND SCRIPTS_WHITELIST Commands Spells)
endif()
-message(STATUS "SCRIPT PREPARATION COMPLETE")
-message("")
+# Set the SCRIPTS_${SCRIPT_MODULE} variables from the
+# variables set above
+foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
+ ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
+ if (${SCRIPT_MODULE_VARIABLE} STREQUAL "default")
+ if(SCRIPTS_USE_WHITELIST)
+ list(FIND SCRIPTS_WHITELIST "${SCRIPT_MODULE}" INDEX)
+ if (${INDEX} GREATER -1)
+ set(${SCRIPT_MODULE_VARIABLE} ${SCRIPTS_DEFAULT_LINKAGE})
+ else()
+ set(${SCRIPT_MODULE_VARIABLE} "disabled")
+ endif()
+ else()
+ set(${SCRIPT_MODULE_VARIABLE} ${SCRIPTS_DEFAULT_LINKAGE})
+ endif()
+ endif()
+ # Build the Graph values
+ if (${SCRIPT_MODULE_VARIABLE} MATCHES "dynamic")
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ GetNativeSharedLibraryName(${SCRIPT_MODULE_PROJECT_NAME} SCRIPT_PROJECT_LIBRARY)
+ list(APPEND GRAPH_KEYS ${SCRIPT_MODULE_PROJECT_NAME})
+ set(GRAPH_VALUE_DISPLAY_${SCRIPT_MODULE_PROJECT_NAME} ${SCRIPT_PROJECT_LIBRARY})
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_${SCRIPT_MODULE_PROJECT_NAME} ${SCRIPT_MODULE})
+ elseif(${SCRIPT_MODULE_VARIABLE} MATCHES "static")
+ list(APPEND GRAPH_KEYS worldserver)
+ set(GRAPH_VALUE_DISPLAY_worldserver worldserver)
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_worldserver ${SCRIPT_MODULE})
+ else()
+ list(APPEND GRAPH_KEYS disabled)
+ set(GRAPH_VALUE_DISPLAY_disabled disabled)
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_disabled ${SCRIPT_MODULE})
+ endif()
+endforeach()
-list(APPEND PRIVATE_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.h
- ${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.cpp)
+list(SORT GRAPH_KEYS)
+list(REMOVE_DUPLICATES GRAPH_KEYS)
+# Display the script graph
+message("* Script configuration (${SCRIPTS}):
+ |")
+
+foreach(GRAPH_KEY ${GRAPH_KEYS})
+ if (NOT GRAPH_KEY STREQUAL "disabled")
+ message(" +- ${GRAPH_VALUE_DISPLAY_${GRAPH_KEY}}")
+ else()
+ message(" | ${GRAPH_VALUE_DISPLAY_${GRAPH_KEY}}")
+ endif()
+ foreach(GRAPH_PROJECT_ENTRY ${GRAPH_VALUE_CONTAINS_MODULES_${GRAPH_KEY}})
+ message(" | +- ${GRAPH_PROJECT_ENTRY}")
+ endforeach()
+ message(" |")
+endforeach()
+
+# Base sources which are used by every script project
if (USE_SCRIPTPCH)
- set(PRIVATE_PCH_HEADER PrecompiledHeaders/ScriptPCH.h)
- set(PRIVATE_PCH_SOURCE PrecompiledHeaders/ScriptPCH.cpp)
+ set(PRIVATE_PCH_HEADER ScriptPCH.h)
+ set(PRIVATE_PCH_SOURCE ScriptPCH.cpp)
endif ()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
-add_library(scripts
- ${PRIVATE_SOURCES}
+# Configures the scriptloader with the given name and stores the output in the LOADER_OUT variable.
+# It is possible to expose multiple subdirectories from the same scriptloader through passing
+# it to the variable arguments
+function(ConfigureScriptLoader SCRIPTLOADER_NAME LOADER_OUT IS_DYNAMIC_SCRIPTLOADER)
+ # Deduces following variables which are referenced by thge template:
+ # TRINITY_IS_DYNAMIC_SCRIPTLOADER
+ # TRINITY_SCRIPTS_FORWARD_DECL
+ # TRINITY_SCRIPTS_INVOKE
+ # TRINITY_CURRENT_SCRIPT_PROJECT
+
+ # To generate export macros
+ set(TRINITY_IS_DYNAMIC_SCRIPTLOADER ${IS_DYNAMIC_SCRIPTLOADER})
+ # To generate forward declarations of the loading functions
+ unset(TRINITY_SCRIPTS_FORWARD_DECL)
+ unset(TRINITY_SCRIPTS_INVOKE)
+ # The current script project which is built in
+ set(TRINITY_CURRENT_SCRIPT_PROJECT ${SCRIPTLOADER_NAME})
+ foreach(LOCALE_SCRIPT_MODULE ${ARGN})
+ # Determine the loader function ("Add##${NameOfDirectory}##Scripts()")
+ set(LOADER_FUNCTION
+ "Add${LOCALE_SCRIPT_MODULE}Scripts()")
+ # Generate the funciton call and the forward declarations
+ set(TRINITY_SCRIPTS_FORWARD_DECL
+ "${TRINITY_SCRIPTS_FORWARD_DECL}void ${LOADER_FUNCTION};\n")
+ set(TRINITY_SCRIPTS_INVOKE
+ "${TRINITY_SCRIPTS_INVOKE} ${LOADER_FUNCTION};\n")
+ endforeach()
+ set(GENERATED_LOADER ${CMAKE_CURRENT_BINARY_DIR}/gen_scriptloader/${SCRIPTLOADER_NAME}/ScriptLoader.cpp)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.cpp.in.cmake ${GENERATED_LOADER})
+ set(${LOADER_OUT} ${GENERATED_LOADER} PARENT_SCOPE)
+endfunction()
+
+# Generates the actual script projects
+# Fills the STATIC_SCRIPT_MODULES and DYNAMIC_SCRIPT_MODULE_PROJECTS variables
+# which contain the names which scripts are linked statically/dynamically and
+# adds the sources of the static modules to the PRIVATE_SOURCES variable.
+foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
+ GetPathToScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PATH)
+ ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
+
+ if ((${SCRIPT_MODULE_VARIABLE} STREQUAL "disabled") OR
+ (${SCRIPT_MODULE_VARIABLE} STREQUAL "static"))
+ # Uninstall disabled modules
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ GetNativeSharedLibraryName(${SCRIPT_MODULE_PROJECT_NAME} SCRIPT_MODULE_OUTPUT_NAME)
+ list(APPEND DISABLED_SCRIPT_MODULE_PROJECTS ${INSTALL_OFFSET}/${SCRIPT_MODULE_OUTPUT_NAME})
+ if (${SCRIPT_MODULE_VARIABLE} STREQUAL "static")
+ # Add the module name to STATIC_SCRIPT_MODULES
+ list(APPEND STATIC_SCRIPT_MODULES ${SCRIPT_MODULE})
+ # Add the module content to the whole static module
+ CollectSourceFiles(${SCRIPT_MODULE_PATH} PRIVATE_SOURCES)
+ endif()
+ elseif(${SCRIPT_MODULE_VARIABLE} STREQUAL "dynamic")
+ # Generate an own dynamic module which is loadable on runtime
+ # Add the module content to the whole static module
+ unset(SCRIPT_MODULE_PRIVATE_SOURCES)
+ CollectSourceFiles(${SCRIPT_MODULE_PATH} SCRIPT_MODULE_PRIVATE_SOURCES)
+ # Configure the scriptloader
+ ConfigureScriptLoader(${SCRIPT_MODULE} SCRIPT_MODULE_PRIVATE_SCRIPTLOADER ON ${SCRIPT_MODULE})
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ # Add the module name to DYNAMIC_SCRIPT_MODULES
+ list(APPEND DYNAMIC_SCRIPT_MODULE_PROJECTS ${SCRIPT_MODULE_PROJECT_NAME})
+ # Create the script module project
+ add_library(${SCRIPT_MODULE_PROJECT_NAME} SHARED
+ ${PRIVATE_PCH_SOURCE}
+ ${SCRIPT_MODULE_PRIVATE_SOURCES}
+ ${SCRIPT_MODULE_PRIVATE_SCRIPTLOADER})
+ target_link_libraries(${SCRIPT_MODULE_PROJECT_NAME}
+ PUBLIC
+ game)
+ set_target_properties(${SCRIPT_MODULE_PROJECT_NAME}
+ PROPERTIES
+ FOLDER
+ "scripts")
+
+ if(UNIX)
+ install(TARGETS ${SCRIPT_MODULE_PROJECT_NAME}
+ DESTINATION ${INSTALL_OFFSET}
+ COMPONENT ${SCRIPT_MODULE_PROJECT_NAME})
+ elseif(WIN32)
+ install(TARGETS ${SCRIPT_MODULE_PROJECT_NAME}
+ RUNTIME DESTINATION ${INSTALL_OFFSET}
+ COMPONENT ${SCRIPT_MODULE_PROJECT_NAME})
+ if(MSVC)
+ # Place the script modules in the script subdirectory
+ set_target_properties(${SCRIPT_MODULE_PROJECT_NAME} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/Debug/scripts
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/Release/scripts
+ RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/bin/RelWithDebInfo/scripts
+ RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/bin/MinSizeRel/scripts)
+ endif()
+ endif()
+ else()
+ message(FATAL_ERROR "Unknown value \"${${SCRIPT_MODULE_VARIABLE}}\"!")
+ endif()
+endforeach()
+
+# Add the dynamic script modules to the worldserver as dependency
+set(WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES ${DYNAMIC_SCRIPT_MODULE_PROJECTS} PARENT_SCOPE)
+
+ConfigureScriptLoader("static" SCRIPT_MODULE_PRIVATE_SCRIPTLOADER OFF ${STATIC_SCRIPT_MODULES})
+
+add_library(scripts STATIC
+ ScriptLoader.h
${PRIVATE_PCH_SOURCE}
-)
+ ${SCRIPT_MODULE_PRIVATE_SCRIPTLOADER}
+ ${PRIVATE_SOURCES})
-target_include_directories(scripts
+target_link_libraries(scripts
PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR}
- PRIVATE
- ${CMAKE_CURRENT_BINARY_DIR})
+ game-interface)
-target_link_libraries(scripts
+target_include_directories(scripts
PUBLIC
- game)
+ ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(scripts
- PROPERTIES
- FOLDER
- "server")
+ PROPERTIES
+ FOLDER
+ "scripts")
# Generate precompiled header
if (USE_SCRIPTPCH)
- add_cxx_pch(scripts ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
+ list(APPEND ALL_SCRIPT_PROJECTS scripts ${DYNAMIC_SCRIPT_MODULE_PROJECTS})
+ add_cxx_pch("${ALL_SCRIPT_PROJECTS}" ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
+endif()
+
+# Remove all shared libraries in the installl directory which
+# are contained in the static library already.
+if (DISABLED_SCRIPT_MODULE_PROJECTS)
+ install(CODE "
+ foreach(SCRIPT_TO_UNINSTALL ${DISABLED_SCRIPT_MODULE_PROJECTS})
+ if (EXISTS \"\${SCRIPT_TO_UNINSTALL}\")
+ message(STATUS \"Uninstalling: \${SCRIPT_TO_UNINSTALL}\")
+ file(REMOVE \"\${SCRIPT_TO_UNINSTALL}\")
+ endif()
+ endforeach()
+ ")
endif()
+
+message("")
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 6ffb92d9684..4bce2d168a9 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -121,8 +121,14 @@ public:
if (!accountName || !password)
return false;
- AccountOpResult result = sAccountMgr->CreateAccount(std::string(accountName), std::string(password), email);
- switch (result)
+ if (strchr(accountName, '@'))
+ {
+ handler->PSendSysMessage(LANG_ACCOUNT_USE_BNET_COMMANDS);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ switch (sAccountMgr->CreateAccount(std::string(accountName), std::string(password), email))
{
case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName);
diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp
index 9557d182df1..01602e2f24c 100644
--- a/src/server/scripts/Commands/cs_character.cpp
+++ b/src/server/scripts/Commands/cs_character.cpp
@@ -436,7 +436,7 @@ public:
if (isalpha(levelStr[0]))
{
nameStr = levelStr;
- levelStr = NULL; // current level will used
+ levelStr = nullptr; // current level will used
}
Player* target;
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 01a048c8795..ca4dd814e01 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -34,6 +34,7 @@ EndScriptData */
#include "Transport.h"
#include "Language.h"
#include "MapManager.h"
+#include "M2Stores.h"
#include <fstream>
@@ -124,6 +125,23 @@ public:
return false;
}
+ // Dump camera locations
+ if (CinematicSequencesEntry const* cineSeq = sCinematicSequencesStore.LookupEntry(id))
+ {
+ std::unordered_map<uint32, FlyByCameraCollection>::const_iterator itr = sFlyByCameraStore.find(cineSeq->cinematicCamera);
+ if (itr != sFlyByCameraStore.end())
+ {
+ handler->PSendSysMessage("Waypoints for sequence %u, camera %u", id, cineSeq->cinematicCamera);
+ uint32 count = 1 ;
+ for (FlyByCamera cam : itr->second)
+ {
+ handler->PSendSysMessage("%02u - %7ums [%f, %f, %f] Facing %f (%f degrees)", count, cam.timeStamp, cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w, cam.locations.w * (180 / M_PI));
+ count++;
+ }
+ handler->PSendSysMessage("%u waypoints dumped", itr->second.size());
+ }
+ }
+
handler->GetSession()->GetPlayer()->SendCinematicStart(id);
return true;
}
@@ -830,7 +848,7 @@ public:
if (Unit* unit = ref->GetSource()->GetOwner())
{
++count;
- handler->PSendSysMessage(" %u. %s (guid %u) - threat %f", count, unit->GetName().c_str(), unit->GetGUID().GetCounter(), ref->getThreat());
+ handler->PSendSysMessage(" %u. %s (current guid %u, DB guid %u) - threat %f", count, unit->GetName().c_str(), unit->GetGUID().GetCounter(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0, ref->getThreat());
}
ref = ref->next();
}
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index 3e35a721162..a98f9f4bf9c 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -796,7 +796,7 @@ public:
target->CleanupAfterTaxiFlight();
}
- target->TeleportTo(target->m_recallMap, target->m_recallX, target->m_recallY, target->m_recallZ, target->m_recallO);
+ target->Recall();
return true;
}
diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp
index 3a062b21c3e..e25018cf1bd 100644
--- a/src/server/scripts/Commands/cs_modify.cpp
+++ b/src/server/scripts/Commands/cs_modify.cpp
@@ -80,23 +80,32 @@ public:
return commandTable;
}
- //Edit Player HP
- static bool HandleModifyHPCommand(ChatHandler* handler, const char* args)
+ template<typename... Args>
+ static void NotifyModification(ChatHandler* handler, Unit* target, TrinityStrings resourceMessage, TrinityStrings resourceReportMessage, Args&&... args)
+ {
+ if (Player* player = target->ToPlayer())
+ {
+ handler->PSendSysMessage(resourceMessage, handler->GetNameLink(player).c_str(), args...);
+ if (handler->needReportToTarget(player))
+ ChatHandler(player->GetSession()).PSendSysMessage(resourceReportMessage, handler->GetNameLink().c_str(), std::forward<Args>(args)...);
+ }
+ }
+
+ static bool CheckModifyResources(ChatHandler* handler, const char* args, Player* target, int32& res, int32& resmax, int8 const multiplier = 1)
{
if (!*args)
return false;
- int32 hp = atoi((char*)args);
- int32 hpm = atoi((char*)args);
+ res = atoi((char*)args) * multiplier;
+ resmax = atoi((char*)args) * multiplier;
- if (hp < 1 || hpm < 1 || hpm < hp)
+ if (res < 1 || resmax < 1 || resmax < res)
{
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
return false;
}
- Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
@@ -107,164 +116,87 @@ public:
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
- handler->PSendSysMessage(LANG_YOU_CHANGE_HP, handler->GetNameLink(target).c_str(), hp, hpm);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_HP_CHANGED, handler->GetNameLink().c_str(), hp, hpm);
-
- target->SetMaxHealth(hpm);
- target->SetHealth(hp);
-
return true;
}
- //Edit Player Mana
- static bool HandleModifyManaCommand(ChatHandler* handler, const char* args)
+ //Edit Player HP
+ static bool HandleModifyHPCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- int32 mana = atoi((char*)args);
- int32 manam = atoi((char*)args);
-
- if (mana <= 0 || manam <= 0 || manam < mana)
+ int32 hp, hpmax;
+ Player* target = handler->getSelectedPlayerOrSelf();
+ if (CheckModifyResources(handler, args, target, hp, hpmax))
{
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_HP, LANG_YOURS_HP_CHANGED, hp, hpmax);
+ target->SetMaxHealth(hpmax);
+ target->SetHealth(hp);
+ return true;
}
+ return false;
+ }
+ //Edit Player Mana
+ static bool HandleModifyManaCommand(ChatHandler* handler, const char* args)
+ {
+ int32 mana, manamax;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+
+ if (CheckModifyResources(handler, args, target, mana, manamax))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_MANA, LANG_YOURS_MANA_CHANGED, mana, manamax);
+ target->SetMaxPower(POWER_MANA, manamax);
+ target->SetPower(POWER_MANA, mana);
+ return true;
}
-
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_MANA, handler->GetNameLink(target).c_str(), mana, manam);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_MANA_CHANGED, handler->GetNameLink().c_str(), mana, manam);
-
- target->SetMaxPower(POWER_MANA, manam);
- target->SetPower(POWER_MANA, mana);
-
- return true;
+ return false;
}
//Edit Player Energy
static bool HandleModifyEnergyCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- int32 energy = atoi((char*)args)*10;
- int32 energym = atoi((char*)args)*10;
-
- if (energy <= 0 || energym <= 0 || energym < energy)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ int32 energy, energymax;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ int8 const energyMultiplier = 10;
+ if (CheckModifyResources(handler, args, target, energy, energymax, energyMultiplier))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_ENERGY, LANG_YOURS_ENERGY_CHANGED, energy / energyMultiplier, energymax / energyMultiplier);
+ target->SetMaxPower(POWER_ENERGY, energymax);
+ target->SetPower(POWER_ENERGY, energy);
+ TC_LOG_DEBUG("misc", handler->GetTrinityString(LANG_CURRENT_ENERGY), target->GetMaxPower(POWER_ENERGY));
+ return true;
}
-
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_ENERGY, handler->GetNameLink(target).c_str(), energy/10, energym/10);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, handler->GetNameLink().c_str(), energy/10, energym/10);
-
- target->SetMaxPower(POWER_ENERGY, energym);
- target->SetPower(POWER_ENERGY, energy);
-
- TC_LOG_DEBUG("misc", handler->GetTrinityString(LANG_CURRENT_ENERGY), target->GetMaxPower(POWER_ENERGY));
-
- return true;
+ return false;
}
//Edit Player Rage
static bool HandleModifyRageCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- int32 rage = atoi((char*)args)*10;
- int32 ragem = atoi((char*)args)*10;
-
- if (rage <= 0 || ragem <= 0 || ragem < rage)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ int32 rage, ragemax;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ int8 const rageMultiplier = 10;
+ if (CheckModifyResources(handler, args, target, rage, ragemax, rageMultiplier))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_RAGE, LANG_YOURS_RAGE_CHANGED, rage / rageMultiplier, ragemax / rageMultiplier);
+ target->SetMaxPower(POWER_RAGE, ragemax);
+ target->SetPower(POWER_RAGE, rage);
+ return true;
}
-
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_RAGE, handler->GetNameLink(target).c_str(), rage/10, ragem/10);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_RAGE_CHANGED, handler->GetNameLink().c_str(), rage/10, ragem/10);
-
- target->SetMaxPower(POWER_RAGE, ragem);
- target->SetPower(POWER_RAGE, rage);
-
- return true;
+ return false;
}
// Edit Player Runic Power
static bool HandleModifyRunicPowerCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- int32 rune = atoi((char*)args)*10;
- int32 runem = atoi((char*)args)*10;
-
- if (rune <= 0 || runem <= 0 || runem < rune)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ int32 rune, runemax;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ int8 const runeMultiplier = 10;
+ if (CheckModifyResources(handler, args, target, rune, runemax, runeMultiplier))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_RUNIC_POWER, LANG_YOURS_RUNIC_POWER_CHANGED, rune / runeMultiplier, runemax / runeMultiplier);
+ target->SetMaxPower(POWER_RUNIC_POWER, runemax);
+ target->SetPower(POWER_RUNIC_POWER, rune);
+ return true;
}
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_RUNIC_POWER, handler->GetNameLink(target).c_str(), rune/10, runem/10);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_RUNIC_POWER_CHANGED, handler->GetNameLink().c_str(), rune/10, runem/10);
-
- target->SetMaxPower(POWER_RUNIC_POWER, runem);
- target->SetPower(POWER_RUNIC_POWER, rune);
-
- return true;
+ return false;
}
//Edit Player Faction
@@ -437,22 +369,20 @@ public:
return false;
}
- //Edit Player Aspeed
- static bool HandleModifyASpeedCommand(ChatHandler* handler, const char* args)
+ static bool CheckModifySpeed(ChatHandler* handler, const char* args, Unit* target, float& speed, float minimumBound, float maximumBound, bool checkInFlight = true)
{
if (!*args)
return false;
- float ASpeed = (float)atof((char*)args);
+ speed = (float)atof((char*)args);
- if (ASpeed > 50.0f || ASpeed < 0.1f)
+ if (speed > maximumBound || speed < minimumBound)
{
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
return false;
}
- Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
@@ -460,238 +390,107 @@ public:
return false;
}
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- std::string targetNameLink = handler->GetNameLink(target);
-
- if (target->IsInFlight())
+ if (Player* player = target->ToPlayer())
{
- handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str());
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_ASPEED, targetNameLink.c_str(), ASpeed);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, handler->GetNameLink().c_str(), ASpeed);
+ // check online security
+ if (handler->HasLowerSecurity(player, ObjectGuid::Empty))
+ return false;
- target->SetSpeed(MOVE_WALK, ASpeed, true);
- target->SetSpeed(MOVE_RUN, ASpeed, true);
- target->SetSpeed(MOVE_SWIM, ASpeed, true);
- //target->SetSpeed(MOVE_TURN, ASpeed, true);
- target->SetSpeed(MOVE_FLIGHT, ASpeed, true);
+ if (player->IsInFlight() && checkInFlight)
+ {
+ handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, handler->GetNameLink(player).c_str());
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+ }
return true;
}
- //Edit Player Speed
- static bool HandleModifySpeedCommand(ChatHandler* handler, const char* args)
+ //Edit Player Aspeed
+ static bool HandleModifyASpeedCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- float Speed = (float)atof((char*)args);
-
- if (Speed > 50.0f || Speed < 0.1f)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ float allSpeed;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ if (CheckModifySpeed(handler, args, target, allSpeed, 0.1f, 50.0f))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_ASPEED, LANG_YOURS_ASPEED_CHANGED, allSpeed);
+ target->SetSpeedRate(MOVE_WALK, allSpeed);
+ target->SetSpeedRate(MOVE_RUN, allSpeed);
+ target->SetSpeedRate(MOVE_SWIM, allSpeed);
+ target->SetSpeedRate(MOVE_FLIGHT, allSpeed);
+ return true;
}
+ return false;
+ }
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- std::string targetNameLink = handler->GetNameLink(target);
-
- if (target->IsInFlight())
+ //Edit Player Speed
+ static bool HandleModifySpeedCommand(ChatHandler* handler, const char* args)
+ {
+ float Speed;
+ Player* target = handler->getSelectedPlayerOrSelf();
+ if (CheckModifySpeed(handler, args, target, Speed, 0.1f, 50.0f))
{
- handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str());
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_SPEED, LANG_YOURS_SPEED_CHANGED, Speed);
+ target->SetSpeedRate(MOVE_RUN, Speed);
+ return true;
}
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_SPEED, targetNameLink.c_str(), Speed);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, handler->GetNameLink().c_str(), Speed);
-
- target->SetSpeed(MOVE_RUN, Speed, true);
-
- return true;
+ return false;
}
//Edit Player Swim Speed
static bool HandleModifySwimCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- float Swim = (float)atof((char*)args);
-
- if (Swim > 50.0f || Swim < 0.1f)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ float swimSpeed;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ if (CheckModifySpeed(handler, args, target, swimSpeed, 0.1f, 50.0f))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- std::string targetNameLink = handler->GetNameLink(target);
-
- if (target->IsInFlight())
- {
- handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str());
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_SWIM_SPEED, LANG_YOURS_SWIM_SPEED_CHANGED, swimSpeed);
+ target->SetSpeedRate(MOVE_SWIM, swimSpeed);
+ return true;
}
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, targetNameLink.c_str(), Swim);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, handler->GetNameLink().c_str(), Swim);
-
- target->SetSpeed(MOVE_SWIM, Swim, true);
-
- return true;
+ return false;
}
- //Edit Player Walk Speed
+ //Edit Player Backwards Walk Speed
static bool HandleModifyBWalkCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- float BSpeed = (float)atof((char*)args);
-
- if (BSpeed > 50.0f || BSpeed < 0.1f)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ float backSpeed;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ if (CheckModifySpeed(handler, args, target, backSpeed, 0.1f, 50.0f))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- std::string targetNameLink = handler->GetNameLink(target);
-
- if (target->IsInFlight())
- {
- handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str());
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_BACK_SPEED, LANG_YOURS_BACK_SPEED_CHANGED, backSpeed);
+ target->SetSpeedRate(MOVE_RUN_BACK, backSpeed);
+ return true;
}
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, targetNameLink.c_str(), BSpeed);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, handler->GetNameLink().c_str(), BSpeed);
-
- target->SetSpeed(MOVE_RUN_BACK, BSpeed, true);
-
- return true;
+ return false;
}
//Edit Player Fly
static bool HandleModifyFlyCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- float FSpeed = (float)atof((char*)args);
-
- if (FSpeed > 50.0f || FSpeed < 0.1f)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ float flySpeed;
Player* target = handler->getSelectedPlayerOrSelf();
- if (!target)
+ if (CheckModifySpeed(handler, args, target, flySpeed, 0.1f, 50.0f, false))
{
- handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
- handler->SetSentErrorMessage(true);
- return false;
+ NotifyModification(handler, target, LANG_YOU_CHANGE_FLY_SPEED, LANG_YOURS_FLY_SPEED_CHANGED, flySpeed);
+ target->SetSpeedRate(MOVE_FLIGHT, flySpeed);
+ return true;
}
-
- // check online security
- if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
- return false;
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, handler->GetNameLink(target).c_str(), FSpeed);
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, handler->GetNameLink().c_str(), FSpeed);
-
- target->SetSpeed(MOVE_FLIGHT, FSpeed, true);
-
- return true;
+ return false;
}
//Edit Player or Creature Scale
static bool HandleModifyScaleCommand(ChatHandler* handler, const char* args)
{
- if (!*args)
- return false;
-
- float Scale = (float)atof((char*)args);
- if (Scale > 10.0f || Scale < 0.1f)
- {
- handler->SendSysMessage(LANG_BAD_VALUE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
+ float Scale;
Unit* target = handler->getSelectedUnit();
- if (!target)
+ if (CheckModifySpeed(handler, args, target, Scale, 0.1f, 10.0f, false))
{
- handler->SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- handler->SetSentErrorMessage(true);
- return false;
- }
-
- if (Player* player = target->ToPlayer())
- {
- // check online security
- if (handler->HasLowerSecurity(player, ObjectGuid::Empty))
- return false;
-
- handler->PSendSysMessage(LANG_YOU_CHANGE_SIZE, handler->GetNameLink(player).c_str(), Scale);
- if (handler->needReportToTarget(player))
- ChatHandler(player->GetSession()).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, handler->GetNameLink().c_str(), Scale);
+ NotifyModification(handler, target, LANG_YOU_CHANGE_SIZE, LANG_YOURS_SIZE_CHANGED, Scale);
+ target->SetObjectScale(Scale);
+ return true;
}
-
- target->SetObjectScale(Scale);
-
- return true;
+ return false;
}
//Enable Player mount
@@ -932,9 +731,7 @@ public:
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
- handler->PSendSysMessage(LANG_YOU_GIVE_MOUNT, handler->GetNameLink(target).c_str());
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_MOUNT_GIVED, handler->GetNameLink().c_str());
+ NotifyModification(handler, target, LANG_YOU_GIVE_MOUNT, LANG_MOUNT_GIVED);
target->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP);
target->Mount(mId);
@@ -988,10 +785,7 @@ public:
TC_LOG_DEBUG("misc", handler->GetTrinityString(LANG_CURRENT_MONEY), targetMoney, moneyToAdd, newmoney);
if (newmoney <= 0)
{
- handler->PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, handler->GetNameLink(target).c_str());
- if (handler->needReportToTarget(target))
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, handler->GetNameLink().c_str());
-
+ NotifyModification(handler, target, LANG_YOU_TAKE_ALL_MONEY, LANG_YOURS_ALL_MONEY_GONE);
target->SetMoney(0);
}
else
@@ -1339,8 +1133,8 @@ public:
}
// Set gender
- target->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender);
- target->SetByteValue(PLAYER_BYTES_3, 0, gender);
+ target->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, gender);
+ target->SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, gender);
// Change display ID
target->InitDisplayIds();
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index fbd199b99db..438d05c5687 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -220,14 +220,15 @@ public:
{ "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" },
{ "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" },
{ "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" },
- { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, NULL, "", npcAddCommandTable },
- { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, NULL, "", npcDeleteCommandTable },
- { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, NULL, "", npcFollowCommandTable },
- { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, NULL, "", npcSetCommandTable },
+ { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable },
+ { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable },
+ { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable },
+ { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable },
+ { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" },
};
static std::vector<ChatCommand> commandTable =
{
- { "npc", rbac::RBAC_PERM_COMMAND_NPC, false, NULL, "", npcCommandTable },
+ { "npc", rbac::RBAC_PERM_COMMAND_NPC, false, nullptr, "", npcCommandTable },
};
return commandTable;
}
@@ -318,17 +319,17 @@ public:
uint32 itemId = item_int;
- char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
+ char* fmaxcount = strtok(nullptr, " "); //add maxcount, default: 0
uint32 maxcount = 0;
if (fmaxcount)
maxcount = atoul(fmaxcount);
- char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
+ char* fincrtime = strtok(nullptr, " "); //add incrtime, default: 0
uint32 incrtime = 0;
if (fincrtime)
incrtime = atoul(fincrtime);
- char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
+ char* fextendedcost = strtok(nullptr, " "); //add ExtendedCost, default: 0
uint32 extendedcost = fextendedcost ? atoul(fextendedcost) : 0;
Creature* vendor = handler->getSelectedCreature();
if (!vendor)
@@ -361,7 +362,7 @@ public:
return false;
char* guidStr = strtok((char*)args, " ");
- char* waitStr = strtok((char*)NULL, " ");
+ char* waitStr = strtok((char*)nullptr, " ");
ObjectGuid::LowType lowGuid = atoi((char*)guidStr);
@@ -446,36 +447,24 @@ public:
}
Creature* creature = handler->getSelectedCreature();
- if (!creature)
+ if (!creature || creature->IsPet())
{
handler->SendSysMessage(LANG_SELECT_CREATURE);
handler->SetSentErrorMessage(true);
return false;
}
- if (creature->IsPet())
- {
- if (((Pet*)creature)->getPetType() == HUNTER_PET)
- {
- creature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr->GetXPForLevel(lvl)/4);
- creature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
- }
- ((Pet*)creature)->GivePetLevel(lvl);
- }
- else
- {
- creature->SetMaxHealth(100 + 30*lvl);
- creature->SetHealth(100 + 30*lvl);
- creature->SetLevel(lvl);
- creature->SaveToDB();
- }
+ creature->SetMaxHealth(100 + 30*lvl);
+ creature->SetHealth(100 + 30*lvl);
+ creature->SetLevel(lvl);
+ creature->SaveToDB();
return true;
}
static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args)
{
- Creature* unit = NULL;
+ Creature* unit = nullptr;
if (*args)
{
@@ -628,7 +617,7 @@ public:
return false;
char* arg1 = strtok((char*)args, " ");
- char* arg2 = strtok((char*)NULL, "");
+ char* arg2 = strtok((char*)nullptr, "");
if (!arg1 || !arg2)
return false;
@@ -926,8 +915,8 @@ public:
// later switched on/off according to special events (like escort
// quests, etc)
char* guid_str = strtok((char*)args, " ");
- char* type_str = strtok((char*)NULL, " ");
- char* dontdel_str = strtok((char*)NULL, " ");
+ char* type_str = strtok((char*)nullptr, " ");
+ char* dontdel_str = strtok((char*)nullptr, " ");
bool doNotDelete = false;
@@ -935,7 +924,7 @@ public:
return false;
ObjectGuid::LowType lowguid = 0;
- Creature* creature = NULL;
+ Creature* creature = nullptr;
if (dontdel_str)
{
@@ -961,7 +950,7 @@ public:
{
//TC_LOG_ERROR("misc", "DEBUG: type_str, NODEL ");
doNotDelete = true;
- type_str = NULL;
+ type_str = nullptr;
}
}
}
@@ -1001,7 +990,7 @@ public:
}
// now lowguid is low guid really existed creature
- // and creature point (maybe) to this creature or NULL
+ // and creature point (maybe) to this creature or nullptr
MovementGeneratorType move_type;
@@ -1260,7 +1249,7 @@ public:
}
char* receiver_str = strtok((char*)args, " ");
- char* text = strtok(NULL, "");
+ char* text = strtok(nullptr, "");
if (!receiver_str || !text)
{
@@ -1319,7 +1308,16 @@ public:
if (!*args)
return false;
- char* charID = handler->extractKeyFromLink((char*)args, "Hcreature_entry");
+ bool loot = false;
+ char const* spawntype_str = strtok((char*)args, " ");
+ char const* entry_str = strtok(nullptr, "");
+ if (stricmp(spawntype_str, "LOOT") == 0)
+ loot = true;
+ else if (stricmp(spawntype_str, "NOLOOT") == 0)
+ loot = false;
+ else
+ entry_str = args;
+ char* charID = handler->extractKeyFromLink((char*)entry_str, "Hcreature_entry");
if (!charID)
return false;
@@ -1332,7 +1330,7 @@ public:
if (!sObjectMgr->GetCreatureTemplate(id))
return false;
- chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120);
+ chr->SummonCreature(id, *chr, loot ? TEMPSUMMON_CORPSE_TIMED_DESPAWN : TEMPSUMMON_CORPSE_DESPAWN, 30 * IN_MILLISECONDS);
return true;
}
@@ -1404,6 +1402,51 @@ public:
return true;
}
+ static bool HandleNpcEvadeCommand(ChatHandler* handler, char const* args)
+ {
+ Creature* creatureTarget = handler->getSelectedCreature();
+ if (!creatureTarget || creatureTarget->IsPet())
+ {
+ handler->PSendSysMessage(LANG_SELECT_CREATURE);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!creatureTarget->IsAIEnabled)
+ {
+ handler->PSendSysMessage(LANG_CREATURE_NOT_AI_ENABLED);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* type_str = args ? strtok((char*)args, " ") : nullptr;
+ char* force_str = args ? strtok(nullptr, " ") : nullptr;
+
+ CreatureAI::EvadeReason why = CreatureAI::EVADE_REASON_OTHER;
+ bool force = false;
+ if (type_str)
+ {
+ if (stricmp(type_str, "NO_HOSTILES") == 0 || stricmp(type_str, "EVADE_REASON_NO_HOSTILES") == 0)
+ why = CreatureAI::EVADE_REASON_NO_HOSTILES;
+ else if (stricmp(type_str, "BOUNDARY") == 0 || stricmp(type_str, "EVADE_REASON_BOUNDARY") == 0)
+ why = CreatureAI::EVADE_REASON_BOUNDARY;
+ else if (stricmp(type_str, "SEQUENCE_BREAK") == 0 || stricmp(type_str, "EVADE_REASON_SEQUENCE_BREAK") == 0)
+ why = CreatureAI::EVADE_REASON_SEQUENCE_BREAK;
+ else if (stricmp(type_str, "FORCE") == 0)
+ force = true;
+
+ if (!force && force_str)
+ if (stricmp(force_str, "FORCE") == 0)
+ force = true;
+ }
+
+ if (force)
+ creatureTarget->ClearUnitState(UNIT_STATE_EVADE);
+ creatureTarget->AI()->EnterEvadeMode(why);
+
+ return true;
+ }
+
static bool HandleNpcAddFormationCommand(ChatHandler* handler, char const* args)
{
if (!*args)
@@ -1515,7 +1558,7 @@ public:
if (!pSlotID)
return false;
- char* pItemID = strtok(NULL, " ");
+ char* pItemID = strtok(nullptr, " ");
if (!pItemID)
return false;
diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp
index 4f0a179142d..787265cc19b 100644
--- a/src/server/scripts/Commands/cs_pet.cpp
+++ b/src/server/scripts/Commands/cs_pet.cpp
@@ -22,6 +22,19 @@
#include "ObjectMgr.h"
#include "ScriptMgr.h"
+static inline Pet* GetSelectedPlayerPetOrOwn(ChatHandler* handler)
+{
+ if (Unit* target = handler->getSelectedUnit())
+ {
+ if (target->GetTypeId() == TYPEID_PLAYER)
+ return target->ToPlayer()->GetPet();
+ if (target->IsPet())
+ return target->ToPet();
+ return nullptr;
+ }
+ Player* player = handler->GetSession()->GetPlayer();
+ return player ? player->GetPet() : nullptr;
+}
class pet_commandscript : public CommandScript
{
public:
@@ -34,6 +47,7 @@ public:
{ "create", rbac::RBAC_PERM_COMMAND_PET_CREATE, false, &HandlePetCreateCommand, "" },
{ "learn", rbac::RBAC_PERM_COMMAND_PET_LEARN, false, &HandlePetLearnCommand, "" },
{ "unlearn", rbac::RBAC_PERM_COMMAND_PET_UNLEARN, false, &HandlePetUnlearnCommand, "" },
+ { "level", rbac::RBAC_PERM_COMMAND_PET_LEVEL, false, &HandlePetLevelCommand, "" },
};
static std::vector<ChatCommand> commandTable =
@@ -54,9 +68,9 @@ public:
return false;
}
- CreatureTemplate const* creatrueTemplate = creatureTarget->GetCreatureTemplate();
+ CreatureTemplate const* creatureTemplate = creatureTarget->GetCreatureTemplate();
// Creatures with family 0 crashes the server
- if (!creatrueTemplate->family)
+ if (!creatureTemplate->family)
{
handler->PSendSysMessage("This creature cannot be tamed. (family id: 0).");
handler->SetSentErrorMessage(true);
@@ -119,12 +133,11 @@ public:
if (!*args)
return false;
- Player* player = handler->GetSession()->GetPlayer();
- Pet* pet = player->GetPet();
+ Pet* pet = GetSelectedPlayerPetOrOwn(handler);
if (!pet)
{
- handler->PSendSysMessage("You have no pet");
+ handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
handler->SetSentErrorMessage(true);
return false;
}
@@ -162,11 +175,10 @@ public:
if (!*args)
return false;
- Player* player = handler->GetSession()->GetPlayer();
- Pet* pet = player->GetPet();
+ Pet* pet = GetSelectedPlayerPetOrOwn(handler);
if (!pet)
{
- handler->PSendSysMessage("You have no pet");
+ handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
handler->SetSentErrorMessage(true);
return false;
}
@@ -180,6 +192,37 @@ public:
return true;
}
+
+ static bool HandlePetLevelCommand(ChatHandler* handler, char const* args)
+ {
+ Pet* pet = GetSelectedPlayerPetOrOwn(handler);
+ Player* owner = pet ? pet->GetOwner() : nullptr;
+ if (!pet || !owner)
+ {
+ handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 level = args ? atoi(args) : 0;
+ if (level == 0)
+ level = owner->getLevel() - pet->getLevel();
+ if (level == 0 || level < -STRONG_MAX_LEVEL || level > STRONG_MAX_LEVEL)
+ {
+ handler->SendSysMessage(LANG_BAD_VALUE);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 newLevel = pet->getLevel() + level;
+ if (newLevel < 1)
+ newLevel = 1;
+ else if (newLevel > owner->getLevel())
+ newLevel = owner->getLevel();
+
+ pet->GivePetLevel(newLevel);
+ return true;
+ }
};
void AddSC_pet_commandscript()
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index 56b0dbf43d0..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, "" },
@@ -251,7 +251,7 @@ public:
static bool HandleReloadAllScriptsCommand(ChatHandler* handler, const char* /*args*/)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->PSendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -393,7 +393,7 @@ public:
static bool HandleReloadCommandCommand(ChatHandler* handler, const char* /*args*/)
{
- handler->SetLoadCommandTable(true);
+ ChatHandler::invalidateCommandTable();
handler->SendGlobalGMSysMessage("DB table `command` will be reloaded at next chat command use.");
return true;
}
@@ -893,7 +893,7 @@ public:
static bool HandleReloadEventScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -913,7 +913,7 @@ public:
static bool HandleReloadWpScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -946,7 +946,7 @@ public:
static bool HandleReloadSpellScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp
index 05941120423..ba1dab28350 100644
--- a/src/server/scripts/Commands/cs_reset.cpp
+++ b/src/server/scripts/Commands/cs_reset.cpp
@@ -102,7 +102,7 @@ public:
player->setFactionForRace(player->getRace());
- player->SetUInt32Value(UNIT_FIELD_BYTES_0, ((player->getRace()) | (player->getClass() << 8) | (player->getGender() << 16) | (powerType << 24)));
+ player->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powerType);
// reset only if player not in some form;
if (player->GetShapeshiftForm() == FORM_NONE)
diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp
index 83bc2e47674..e0e52d4e1f5 100644
--- a/src/server/scripts/Commands/cs_server.cpp
+++ b/src/server/scripts/Commands/cs_server.cpp
@@ -29,6 +29,7 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
#include "GitRevision.h"
+#include "Util.h"
class server_commandscript : public CommandScript
{
@@ -52,12 +53,14 @@ public:
static std::vector<ChatCommand> serverRestartCommandTable =
{
{ "cancel", rbac::RBAC_PERM_COMMAND_SERVER_RESTART_CANCEL, true, &HandleServerShutDownCancelCommand, "" },
+ { "force", rbac::RBAC_PERM_COMMAND_SERVER_RESTART_FORCE, true, &HandleServerForceRestartCommand, "" },
{ "" , rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, &HandleServerRestartCommand, "" },
};
static std::vector<ChatCommand> serverShutdownCommandTable =
{
{ "cancel", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_CANCEL, true, &HandleServerShutDownCancelCommand, "" },
+ { "force", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE, true, &HandleServerForceShutDownCommand, "" },
{ "" , rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, &HandleServerShutDownCommand, "" },
};
@@ -73,19 +76,19 @@ public:
{
{ "corpses", rbac::RBAC_PERM_COMMAND_SERVER_CORPSES, true, &HandleServerCorpsesCommand, "" },
{ "exit", rbac::RBAC_PERM_COMMAND_SERVER_EXIT, true, &HandleServerExitCommand, "" },
- { "idlerestart", rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, true, NULL, "", serverIdleRestartCommandTable },
- { "idleshutdown", rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, true, NULL, "", serverIdleShutdownCommandTable },
+ { "idlerestart", rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, true, nullptr, "", serverIdleRestartCommandTable },
+ { "idleshutdown", rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, true, nullptr, "", serverIdleShutdownCommandTable },
{ "info", rbac::RBAC_PERM_COMMAND_SERVER_INFO, true, &HandleServerInfoCommand, "" },
{ "motd", rbac::RBAC_PERM_COMMAND_SERVER_MOTD, true, &HandleServerMotdCommand, "" },
{ "plimit", rbac::RBAC_PERM_COMMAND_SERVER_PLIMIT, true, &HandleServerPLimitCommand, "" },
- { "restart", rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, NULL, "", serverRestartCommandTable },
- { "shutdown", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, NULL, "", serverShutdownCommandTable },
- { "set", rbac::RBAC_PERM_COMMAND_SERVER_SET, true, NULL, "", serverSetCommandTable },
+ { "restart", rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, nullptr, "", serverRestartCommandTable },
+ { "shutdown", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, nullptr, "", serverShutdownCommandTable },
+ { "set", rbac::RBAC_PERM_COMMAND_SERVER_SET, true, nullptr, "", serverSetCommandTable },
};
static std::vector<ChatCommand> commandTable =
{
- { "server", rbac::RBAC_PERM_COMMAND_SERVER, true, NULL, "", serverCommandTable },
+ { "server", rbac::RBAC_PERM_COMMAND_SERVER, true, nullptr, "", serverCommandTable },
};
return commandTable;
}
@@ -192,19 +195,34 @@ public:
return true;
}
- static bool HandleServerShutDownCommand(ChatHandler* /*handler*/, char const* args)
+ static inline bool IsOnlyUser(WorldSession* mySession)
{
- return ShutdownServer(args, 0, SHUTDOWN_EXIT_CODE);
+ // check if there is any session connected from a different address
+ std::string myAddr = mySession ? mySession->GetRemoteAddress() : "";
+ SessionMap const& sessions = sWorld->GetAllSessions();
+ for (SessionMap::value_type const& session : sessions)
+ if (session.second && myAddr != session.second->GetRemoteAddress())
+ return false;
+ return true;
+ }
+ static bool HandleServerShutDownCommand(ChatHandler* handler, char const* args)
+ {
+ return ShutdownServer(args, IsOnlyUser(handler->GetSession()) ? SHUTDOWN_MASK_FORCE : 0, SHUTDOWN_EXIT_CODE);
}
- static bool HandleServerRestartCommand(ChatHandler* /*handler*/, char const* args)
+ static bool HandleServerRestartCommand(ChatHandler* handler, char const* args)
{
- return ShutdownServer(args, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
+ return ShutdownServer(args, IsOnlyUser(handler->GetSession()) ? (SHUTDOWN_MASK_FORCE | SHUTDOWN_MASK_RESTART) : SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
}
- static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args)
+ static bool HandleServerForceShutDownCommand(ChatHandler* /*handler*/, char const* args)
{
- return ShutdownServer(args, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE);
+ return ShutdownServer(args, SHUTDOWN_MASK_FORCE, SHUTDOWN_EXIT_CODE);
+ }
+
+ static bool HandleServerForceRestartCommand(ChatHandler* /*handler*/, char const* args)
+ {
+ return ShutdownServer(args, SHUTDOWN_MASK_FORCE | SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
}
static bool HandleServerIdleShutDownCommand(ChatHandler* /*handler*/, char const* args)
@@ -212,6 +230,11 @@ public:
return ShutdownServer(args, SHUTDOWN_MASK_IDLE, SHUTDOWN_EXIT_CODE);
}
+ static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args)
+ {
+ return ShutdownServer(args, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE);
+ }
+
// Exit the realm
static bool HandleServerExitCommand(ChatHandler* handler, char const* /*args*/)
{
@@ -256,8 +279,8 @@ public:
return false;
char* type = strtok((char*)args, " ");
- char* name = strtok(NULL, " ");
- char* level = strtok(NULL, " ");
+ char* name = strtok(nullptr, " ");
+ char* level = strtok(nullptr, " ");
if (!type || !name || !level || *name == '\0' || *level == '\0' || (*type != 'a' && *type != 'l'))
return false;
@@ -313,10 +336,26 @@ private:
return false;
// #delay [#exit_code] [reason]
+ int32 delay = 0;
char* delayStr = strtok((char*)args, " ");
- if (!delayStr || !isNumeric(delayStr))
+ if (!delayStr)
return false;
+ if (isNumeric(delayStr))
+ {
+ delay = atoi(delayStr);
+ // Prevent interpret wrong arg value as 0 secs shutdown time
+ if ((delay == 0 && (delayStr[0] != '0' || delayStr[1] != '\0')) || delay < 0)
+ return false;
+ }
+ else
+ {
+ delay = TimeStringToSecs(std::string(delayStr));
+
+ if (delay == 0)
+ return false;
+ }
+
char* exitCodeStr = nullptr;
char reason[256] = { 0 };
@@ -337,17 +376,14 @@ private:
}
}
- int32 delay = atoi(delayStr);
-
- // Prevent interpret wrong arg value as 0 secs shutdown time
- if ((delay == 0 && (delayStr[0] != '0' || delayStr[1] != '\0')) || delay < 0)
- return false;
-
int32 exitCode = defaultExitCode;
if (exitCodeStr)
if (!ParseExitCode(exitCodeStr, exitCode))
return false;
+ if (delay < (int32)sWorld->getIntConfig(CONFIG_FORCE_SHUTDOWN_THRESHOLD) && !(shutdownMask & SHUTDOWN_MASK_FORCE))
+ return false;
+
sWorld->ShutdownServ(delay, shutdownMask, static_cast<uint8>(exitCode), std::string(reason));
return true;
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
index 51bb314968d..ffec32c0619 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
@@ -27,6 +27,8 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "molten_core.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
enum Emotes
{
@@ -36,9 +38,10 @@ enum Emotes
enum Spells
{
SPELL_INFERNO = 19695,
+ SPELL_INFERNO_DMG = 19698,
SPELL_IGNITE_MANA = 19659,
SPELL_LIVING_BOMB = 20475,
- SPELL_ARMAGEDDON = 20479,
+ SPELL_ARMAGEDDON = 20478,
};
enum Events
@@ -119,7 +122,36 @@ class boss_baron_geddon : public CreatureScript
}
};
+class spell_baron_geddon_inferno : public SpellScriptLoader
+{
+ public:
+ spell_baron_geddon_inferno() : SpellScriptLoader("spell_baron_geddon_inferno") { }
+
+ class spell_baron_geddon_inferno_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_baron_geddon_inferno_AuraScript);
+
+ void OnPeriodic(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
+ int32 damageForTick[8] = { 500, 500, 1000, 1000, 2000, 2000, 3000, 5000 };
+ GetTarget()->CastCustomSpell(SPELL_INFERNO_DMG, SPELLVALUE_BASE_POINT0, damageForTick[aurEff->GetTickNumber() - 1], (Unit*)nullptr, TRIGGERED_FULL_MASK, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_baron_geddon_inferno_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_baron_geddon_inferno_AuraScript();
+ }
+};
+
void AddSC_boss_baron_geddon()
{
new boss_baron_geddon();
+ new spell_baron_geddon_inferno();
}
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp
index 7d7c60ac419..6ff20e66f7f 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp
@@ -138,7 +138,7 @@ public:
{
Initialize();
- me->SetSpeed(MOVE_RUN, 2.0f);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
me->SetDisableGravity(true);
me->SetWalk(false);
me->setActive(true);
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp
index 0f6913b7ebc..3f236c060d7 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp
@@ -674,6 +674,12 @@ public:
void Initialize()
{
+ // Hello, developer from the future! It's me again!
+ // This time, you're fixing Karazhan scripts. Awesome. These are a mess of hacks. An amalgamation of hacks, so to speak. Maybe even a Patchwerk thereof.
+ // Anyway, I digress.
+ // @todo This line below is obviously a hack. Duh. I'm just coming in here to hackfix the encounter to actually be completable.
+ // It needs a rewrite. Badly. Please, take good care of it.
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE);
CycloneTimer = 30000;
ChainLightningTimer = 10000;
}
@@ -701,8 +707,6 @@ public:
void EnterCombat(Unit* /*who*/) override
{
Talk(SAY_CRONE_AGGRO);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
}
void JustDied(Unit* /*killer*/) override
@@ -716,9 +720,6 @@ public:
if (!UpdateVictim())
return;
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
-
if (CycloneTimer <= diff)
{
if (Creature* Cyclone = DoSpawnCreature(CREATURE_CYCLONE, float(urand(0, 9)), float(urand(0, 9)), 0, 0, TEMPSUMMON_TIMED_DESPAWN, 15000))
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
index 4aef3c8d4a3..ace15cc2a7e 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
@@ -562,7 +562,7 @@ public:
arca->GetMotionMaster()->MovePoint(0, -11010.82f, -1761.18f, 156.47f);
arca->setActive(true);
arca->InterruptNonMeleeSpells(true);
- arca->SetSpeed(MOVE_FLIGHT, 2.0f);
+ arca->SetSpeedRate(MOVE_FLIGHT, 2.0f);
}
return 10000;
case 13:
diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
index b809128e731..ebe406c8909 100644
--- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
+++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
@@ -405,7 +405,7 @@ public:
Creature* Orb = DoSpawnCreature(CREATURE_ARCANE_SPHERE, 5, 5, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
if (Orb && target)
{
- Orb->SetSpeed(MOVE_RUN, 0.5f);
+ Orb->SetSpeedRate(MOVE_RUN, 0.5f);
Orb->AddThreat(target, 1000000.0f);
Orb->AI()->AttackStart(target);
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
index 81044a0dbb1..3cf78b79f67 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
@@ -396,7 +396,7 @@ class npc_eye_of_acherus : public CreatureScript
if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
{
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- me->SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true);
+ me->SetSpeedRate(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)));
Talk(TALK_MOVE_START, owner);
}
me->GetMotionMaster()->MovePath(me->GetEntry() * 100, false);
@@ -419,7 +419,7 @@ class npc_eye_of_acherus : public CreatureScript
{
owner->RemoveAura(SPELL_EYE_FLIGHT_BOOST);
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- me->SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true);
+ me->SetSpeedRate(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)));
Talk(TALK_CONTROL, owner);
}
@@ -703,7 +703,7 @@ class npc_dark_rider_of_acherus : public CreatureScript
TargetGUID = who->GetGUID();
me->SetWalk(true);
- me->SetSpeed(MOVE_RUN, 0.4f);
+ me->SetSpeedRate(MOVE_RUN, 0.4f);
me->GetMotionMaster()->MoveChase(who);
me->SetTarget(TargetGUID);
Intro = true;
@@ -1050,7 +1050,7 @@ class npc_scarlet_miner_cart : public CreatureScript
// Not 100% correct, but movement is smooth. Sometimes miner walks faster
// than normal, this speed is fast enough to keep up at those times.
- me->SetSpeed(MOVE_RUN, 1.25f);
+ me->SetSpeedRate(MOVE_RUN, 1.25f);
me->GetMotionMaster()->MoveFollow(miner, 1.0f, 0);
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
index e336ff24382..d25a225717a 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
@@ -923,7 +923,7 @@ public:
if (ObjectAccessor::GetCreature(*me, uiLichKingGUID))
DoCast(me, SPELL_MOGRAINE_CHARGE); // jumping charge
// doesn't make it looks well, so workarounds, Darion charges, looks better
- me->SetSpeed(MOVE_RUN, 3.0f);
+ me->SetSpeedRate(MOVE_RUN, 3.0f);
me->SetWalk(false);
SetHoldState(false);
JumpToNextStep(0);
@@ -935,7 +935,7 @@ public:
temp->HandleEmoteCommand(EMOTE_ONESHOT_KICK);
temp->AI()->Talk(SAY_LIGHT_OF_DAWN46);
}
- me->SetSpeed(MOVE_RUN, 6.0f);
+ me->SetSpeedRate(MOVE_RUN, 6.0f);
me->SetStandState(UNIT_STAND_STATE_DEAD);
SetHoldState(false); // Darion got kicked by lich king
JumpToNextStep(0);
@@ -997,7 +997,7 @@ public:
Unit* temp = me->SummonCreature(NPC_DEFENDER_OF_THE_LIGHT, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->setFaction(me->getFaction());
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
uiDefenderGUID[0] = temp->GetGUID();
@@ -1005,7 +1005,7 @@ public:
temp = me->SummonCreature(NPC_RIMBLAT_EARTHSHATTER, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->setFaction(me->getFaction());
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
uiEarthshatterGUID[0] = temp->GetGUID();
@@ -1014,7 +1014,7 @@ public:
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
temp->AI()->Talk(SAY_LIGHT_OF_DAWN50);
}
@@ -1022,7 +1022,7 @@ public:
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->HandleEmoteCommand(EMOTE_STATE_ATTACK_UNARMED);
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
}
@@ -1030,7 +1030,7 @@ public:
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
}
}
@@ -1044,33 +1044,33 @@ public:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiMaxwellGUID))
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[14]);
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiKorfaxGUID))
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[11]);
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiEligorGUID))
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[17]);
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiDefenderGUID[0]))
{
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }));
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiEarthshatterGUID[0]))
{
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }));
}
@@ -1093,7 +1093,7 @@ public:
break;
case 46: // Darion stand up, "not today"
- me->SetSpeed(MOVE_RUN, 1.0f);
+ me->SetSpeedRate(MOVE_RUN, 1.0f);
me->SetWalk(true);
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_LIGHT_OF_DAWN53);
@@ -1153,7 +1153,7 @@ public:
temp->AI()->Talk(EMOTE_LIGHT_OF_DAWN16);
temp->CastSpell(temp, SPELL_TIRION_CHARGE, false); // jumping charge
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
- temp->SetSpeed(MOVE_RUN, 3.0f); // workarounds, make Tirion still running
+ temp->SetSpeedRate(MOVE_RUN, 3.0f); // workarounds, make Tirion still running
temp->SetWalk(false);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[2]);
if (Creature* lktemp = ObjectAccessor::GetCreature(*me, uiLichKingGUID))
@@ -1171,7 +1171,7 @@ public:
case 54:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiLichKingGUID))
{
- temp->SetSpeed(MOVE_RUN, 1.0f);
+ temp->SetSpeedRate(MOVE_RUN, 1.0f);
me->SetWalk(true);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[29]); // 26
}
@@ -1208,7 +1208,7 @@ public:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiTirionGUID)) // Tirion runs to Darion
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 1.0f);
+ temp->SetSpeedRate(MOVE_RUN, 1.0f);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[6]);
}
JumpToNextStep(2500);
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
index 53b6d2be8dd..9abd0c049d8 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
@@ -109,7 +109,7 @@ public:
FlyBackTimer = 4500;
break;
case 2:
- if (!player->isResurrectRequested())
+ if (!player->IsResurrectRequested())
{
me->HandleEmoteCommand(EMOTE_ONESHOT_CUSTOM_SPELL_01);
DoCast(player, SPELL_REVIVE, true);
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
index 74b300919d8..734b70933aa 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
@@ -450,7 +450,7 @@ public:
me->SetVisible(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->SetDisableGravity(true);
- me->SetSpeed(MOVE_WALK, 5.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 5.0f);
wp_reached = false;
count = 0;
say_timer = 3000;
@@ -894,12 +894,12 @@ public:
if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS)
return false;
- player->AreaExploredOrEventHappens(11405);
- if (Creature* horseman = go->SummonCreature(HH_MOUNTED, FlightPoint[20].x, FlightPoint[20].y, FlightPoint[20].z, 0, TEMPSUMMON_MANUAL_DESPAWN, 0))
- {
- ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->PlayerGUID = player->GetGUID();
- ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->FlyMode();
- }
+ player->AreaExploredOrEventHappens(11405);
+ if (Creature* horseman = go->SummonCreature(HH_MOUNTED, FlightPoint[20].x, FlightPoint[20].y, FlightPoint[20].z, 0, TEMPSUMMON_MANUAL_DESPAWN, 0))
+ {
+ ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->PlayerGUID = player->GetGUID();
+ ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->FlyMode();
+ }
return true;
}
};
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
index d9b481d7b6d..fded249f9ca 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
@@ -249,7 +249,7 @@ public:
me->CastStop(SPELL_FOG_BREATH);
me->RemoveAurasDueToSpell(SPELL_FOG_BREATH);
me->StopMoving();
- me->SetSpeed(MOVE_RUN, 2.0f);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
events.ScheduleEvent(EVENT_CLEAVE, urand(5000, 10000));
events.ScheduleEvent(EVENT_CORROSION, urand(10000, 20000));
@@ -530,7 +530,7 @@ public:
npc_felmyst_vaporAI(Creature* creature) : ScriptedAI(creature)
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetSpeed(MOVE_RUN, 0.8f);
+ me->SetSpeedRate(MOVE_RUN, 0.8f);
}
void Reset() override { }
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
index d86b3707606..7563a880f0a 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
@@ -143,11 +143,7 @@ enum Spells
SPELL_RING_OF_BLUE_FLAMES = 45825 //Cast this spell when the go is activated
};
-/*** Error messages ***/
-#define ERROR_KJ_NOT_SUMMONED "TSCR ERROR: Unable to summon Kil'Jaeden for some reason"
-
/*** Others ***/
-#define FLOOR_Z 28.050388f
#define SHIELD_ORB_Z 45.000f
enum Phase
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp
index 04d23cd7d4e..6c84677708e 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp
@@ -15,88 +15,119 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Boss_Muru
-SD%Complete: 80
-SDComment: all sounds, black hole effect triggers to often (46228)
-*/
-
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "sunwell_plateau.h"
-#include "Player.h"
-#include "SpellInfo.h"
+#include "SpellScript.h"
+#include "SpellAuraEffects.h"
-// Muru & Entropius's spells
enum Spells
{
- SPELL_ENRAGE = 26662,
-
// Muru's spells
- SPELL_NEGATIVE_ENERGY = 46009, //(this trigger 46008)
- SPELL_DARKNESS = 45999,
- SPELL_OPEN_ALL_PORTALS = 46177,
- SPELL_OPEN_PORTAL = 45977,
- SPELL_OPEN_PORTAL_2 = 45976,
- SPELL_SUMMON_BERSERKER = 46037,
- SPELL_SUMNON_FURY_MAGE = 46038,
- SPELL_SUMMON_VOID_SENTINEL = 45988,
- SPELL_SUMMON_ENTROPIUS = 46217,
+ SPELL_OPEN_PORTAL_PERIODIC = 45994,
+ SPELL_DARKNESS_PERIODIC = 45998,
+ SPELL_NEGATIVE_ENERGY_PERIODIC = 46009,
+ SPELL_SUMMON_VOID_SPAWN = 46071,
+ SPELL_SUMMON_BLOOD_ELVES_SCRIPT = 46050,
+ SPELL_SUMMON_BLOOD_ELVES_PERIODIC = 46041,
+ SPELL_OPEN_ALL_PORTALS = 46177,
+ SPELL_SUMMON_ENTROPIUS = 46217,
+ SPELL_ENRAGE = 26662,
+ SPELL_SUMMON_DARK_FIEND_0 = 46000,
+ SPELL_SUMMON_DARK_FIEND_1 = 46001,
+ SPELL_SUMMON_DARK_FIEND_2 = 46002,
+ SPELL_SUMMON_DARK_FIEND_3 = 46003,
+ SPELL_SUMMON_DARK_FIEND_4 = 46004,
+ SPELL_SUMMON_DARK_FIEND_5 = 46005,
+ SPELL_SUMMON_DARK_FIEND_6 = 46006,
+ SPELL_SUMMON_DARK_FIEND_7 = 46007,
+ SPELL_SUMMON_BERSERKER = 46037,
+ SPELL_SUMMON_BERSERKER_2 = 46040,
+ SPELL_SUMMON_FURY_MAGE = 46038,
+ SPELL_SUMMON_FURY_MAGE_2 = 46039,
// Entropius's spells
- SPELL_DARKNESS_E = 46269,
- SPELL_BLACKHOLE = 46282,
- SPELL_NEGATIVE_ENERGY_E = 46284,
- SPELL_ENTROPIUS_SPAWN = 46223,
-
- // Shadowsword Berserker's spells
- SPELL_FLURRY = 46160,
- SPELL_DUAL_WIELD = 29651,
+ SPELL_ENTROPIUS_COSMETIC_SPAWN = 46223,
+ SPELL_DARKNESS_E = 46269,
+ SPELL_NEGATIVE_ENERGY_PERIODIC_E = 46284,
+ SPELL_BLACKHOLE = 46282,
+ SPELL_SUMMON_DARKFIEND_E = 46263,
+
+ // Myruu's Portal Target
+ SPELL_SUMMON_VOID_SENTINEL_SUMMONER = 45978,
+ SPELL_SUMMON_VOID_SENTINEL_SUMMONER_VISUAL = 45989,
+ SPELL_SUMMON_VOID_SENTINEL = 45988,
+ SPELL_TRANSFORM_VISUAL_MISSILE = 46205,
+ TRANSFORM_VISUAL_MISSILE_1 = 46208,
+ TRANSFORM_VISUAL_MISSILE_2 = 46178,
+ SPELL_OPEN_PORTAL = 45977,
+ SPELL_OPEN_PORTAL_2 = 45976,
- // Shadowsword Fury Mage's spells
- SPELL_FEL_FIREBALL = 46101,
- SPELL_SPELL_FURY = 46102,
+ //Dark Fiend Spells
+ SPELL_DARKFIEND_DAMAGE = 45944,
+ SPELL_DARKFIEND_VISUAL = 45936,
+ SPELL_DARKFIEND_SKIN = 45934,
// Void Sentinel's spells
- SPELL_SHADOW_PULSE = 46087,
- SPELL_VOID_BLAST = 46161,
+ SPELL_SHADOW_PULSE_PERIODIC = 46086,
+ SPELL_VOID_BLAST = 46161,
- // Void Spawn's spells
- SPELL_SHADOW_BOLT_VOLLEY = 46082,
+ //Black Hole Spells
+ SPELL_BLACKHOLE_SUMMON_VISUAL = 46242,
+ SPELL_BLACKHOLE_SUMMON_VISUAL_2 = 46247,
+ SPELL_BLACKHOLE_PASSIVE = 46228,
+ SPELL_BLACK_HOLE_VISUAL_2 = 46235
+};
- //Dark Fiend Spells
- SPELL_DARKFIEND_AOE = 45944,
- SPELL_DARKFIEND_VISUAL = 45936,
- SPELL_DARKFIEND_SKIN = 45934,
+enum Phases
+{
+ PHASE_ONE = 1,
+ PHASE_TWO = 2
+};
- //Black Hole Spells
- SPELL_BLACKHOLE_SPAWN = 46242,
- SPELL_BLACKHOLE_GROW = 46228
+enum Misc
+{
+ MAX_VOID_SPAWNS = 6,
+ MAX_SUMMON_BLOOD_ELVES = 4,
+ MAX_SUMMON_DARK_FIEND = 8
};
-enum Events
+uint32 const SummonDarkFiendSpells[MAX_SUMMON_DARK_FIEND] =
{
- // M'uru
- EVENT_DARKNESS = 1,
- EVENT_SUMMON_HUMANOIDS,
- EVENT_SUMMON_SENTINEL,
- EVENT_PHASE_TRANSITION, // Delayed phase transition.
- EVENT_ENRAGE,
-
- // Entropius
- EVENT_SUMMON_BLACK_HOLE
+ SPELL_SUMMON_DARK_FIEND_0,
+ SPELL_SUMMON_DARK_FIEND_1,
+ SPELL_SUMMON_DARK_FIEND_2,
+ SPELL_SUMMON_DARK_FIEND_3,
+ SPELL_SUMMON_DARK_FIEND_4,
+ SPELL_SUMMON_DARK_FIEND_5,
+ SPELL_SUMMON_DARK_FIEND_6,
+ SPELL_SUMMON_DARK_FIEND_7
};
-enum Phases
+uint32 const SummonBloodElvesSpells[MAX_SUMMON_BLOOD_ELVES] =
{
- PHASE_ONE = 1,
- PHASE_TWO,
+ SPELL_SUMMON_BERSERKER,
+ SPELL_SUMMON_BERSERKER_2,
+ SPELL_SUMMON_FURY_MAGE,
+ SPELL_SUMMON_FURY_MAGE_2
};
-enum CreatureGroups
+class VoidSpawnSummon : public BasicEvent
{
- CREATURE_GROUP_HUMANOIDS,
- CREATURE_GROUP_DARKFIENDS
+ public:
+ explicit VoidSpawnSummon(Creature* owner)
+ : _owner(owner)
+ {
+ }
+
+ bool Execute(uint64 /*time*/, uint32 /*diff*/)
+ {
+ _owner->CastSpell((Unit*)nullptr, SPELL_SUMMON_VOID_SENTINEL, true);
+ return true;
+ }
+
+ private:
+ Creature* _owner;
};
class boss_entropius : public CreatureScript
@@ -110,53 +141,78 @@ public:
void Reset() override
{
- DoCastAOE(SPELL_NEGATIVE_ENERGY_E);
+ _Reset();
+ DoCast(me, SPELL_ENTROPIUS_COSMETIC_SPAWN, true);
}
- void EnterCombat(Unit* /*who*/) override
+ void ScheduleTasks() override
{
- _EnterCombat();
- DoCastAOE(SPELL_NEGATIVE_ENERGY_E, true);
- DoCast(me, SPELL_ENTROPIUS_SPAWN);
- events.ScheduleEvent(EVENT_SUMMON_BLACK_HOLE, 15000);
+ scheduler.Schedule(Milliseconds(2000), [this](TaskContext /*context*/)
+ {
+ DoResetPortals();
+ DoCastAOE(SPELL_NEGATIVE_ENERGY_PERIODIC_E, true);
+ });
+
+ scheduler.Schedule(Seconds(15), [this](TaskContext context)
+ {
+ DoCastAOE(SPELL_DARKNESS_E, true);
+ DoCastAOE(SPELL_BLACKHOLE, true);
+
+ context.Repeat();
+ });
}
- void JustSummoned(Creature* summoned) override
+ void JustSummoned(Creature* summon) override
{
- switch (summoned->GetEntry())
+ switch (summon->GetEntry())
{
case NPC_DARK_FIENDS:
- summoned->CastSpell(summoned, SPELL_DARKFIEND_VISUAL);
+ summon->CastSpell(summon, SPELL_DARKFIEND_VISUAL);
break;
case NPC_DARKNESS:
- summoned->AddUnitState(UNIT_STATE_STUNNED);
- float x, y, z, o;
- summoned->GetHomePosition(x, y, z, o);
- me->SummonCreature(NPC_DARK_FIENDS, x, y, z, o, TEMPSUMMON_CORPSE_DESPAWN, 0);
+ summon->SetReactState(REACT_PASSIVE);
+ summon->CastSpell(summon, SPELL_BLACKHOLE);
+ summon->CastSpell(summon, SPELL_SUMMON_DARKFIEND_E, true);
break;
}
- summoned->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0, 50, true));
- summons.Summon(summoned);
+ summons.Summon(summon);
}
- void ExecuteEvent(uint32 eventId) override
+ void EnterEvadeMode(EvadeReason /*why*/) override
{
- if (eventId == EVENT_SUMMON_BLACK_HOLE)
- {
- if (Unit* random = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(random, SPELL_DARKNESS_E);
- if (Unit* random = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- random->CastSpell(random, SPELL_BLACKHOLE);
- events.ScheduleEvent(EVENT_SUMMON_BLACK_HOLE, 15000);
- }
+ if (Creature* muru = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MURU)))
+ muru->AI()->EnterEvadeMode();
+
+ DoResetPortals();
+ summons.DespawnAll();
+ me->DespawnOrUnsummon();
}
- void EnterEvadeMode(EvadeReason /*why*/) override
+ void JustDied(Unit* /*killer*/) override
{
+ _JustDied();
+
if (Creature* muru = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MURU)))
- muru->AI()->Reset(); // Reset encounter.
- me->DisappearAndDie();
- summons.DespawnAll();
+ muru->DisappearAndDie();
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ scheduler.Update(diff, [this]
+ {
+ DoMeleeAttackIfReady();
+ });
+ }
+
+ void DoResetPortals()
+ {
+ std::list<Creature*> portals;
+ me->GetCreatureListWithEntryInGrid(portals, NPC_MURU_PORTAL_TARGET, 100.0f);
+ for (Creature* portal : portals)
+ portal->RemoveAllAuras();
}
};
@@ -181,101 +237,96 @@ public:
void Initialize()
{
- DarkFiend = false;
- HasEnraged = false;
- EntropiusGUID.Clear();
+ _hasEnraged = false;
+ _phase = PHASE_ONE;
+ _entropiusGUID.Clear();
}
void Reset() override
{
- Initialize();
_Reset();
+ Initialize();
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetVisible(true);
}
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ BossAI::EnterEvadeMode();
+ if (Creature* entropius = ObjectAccessor::GetCreature(*me, _entropiusGUID))
+ entropius->AI()->EnterEvadeMode();
+ }
+
+ void ScheduleTasks() override
+ {
+ scheduler.Schedule(Minutes(10), [this](TaskContext /*context*/)
+ {
+ if (Creature* entropius = ObjectAccessor::GetCreature(*me, _entropiusGUID))
+ entropius->CastSpell(entropius, SPELL_ENRAGE);
+ DoCast(me, SPELL_ENRAGE);
+ _hasEnraged = true;
+ });
+
+ scheduler.Schedule(Seconds(10), [this](TaskContext /*context*/)
+ {
+ DoCast(me, SPELL_SUMMON_BLOOD_ELVES_SCRIPT, true);
+ DoCast(me, SPELL_SUMMON_BLOOD_ELVES_PERIODIC, true);
+ });
+ }
+
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.SetPhase(PHASE_ONE);
- events.ScheduleEvent(EVENT_ENRAGE, 600000);
- events.ScheduleEvent(EVENT_DARKNESS, 45000, 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_SUMMON_HUMANOIDS, 10000, 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_SUMMON_SENTINEL, 31500, 0, PHASE_ONE);
- DoCastAOE(SPELL_NEGATIVE_ENERGY);
+ DoCast(me, SPELL_OPEN_PORTAL_PERIODIC, true);
+ DoCast(me, SPELL_DARKNESS_PERIODIC, true);
+ DoCast(me, SPELL_NEGATIVE_ENERGY_PERIODIC, true);
}
void DamageTaken(Unit* /*done_by*/, uint32 &damage) override
{
- if (damage >= me->GetHealth() && events.IsInPhase(PHASE_ONE))
+ if (damage >= me->GetHealth())
{
- damage = 0;
+ damage = me->GetHealth() - 1;
+ if (_phase != PHASE_ONE)
+ return;
+
+ _phase = PHASE_TWO;
me->RemoveAllAuras();
- DoCast(me, SPELL_OPEN_ALL_PORTALS);
+ DoCast(me, SPELL_OPEN_ALL_PORTALS, true);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- events.SetPhase(PHASE_TWO);
- events.ScheduleEvent(EVENT_PHASE_TRANSITION, 2000);
+
+ scheduler.Schedule(Seconds(6), [this](TaskContext /*context*/)
+ {
+ DoCast(me, SPELL_SUMMON_ENTROPIUS, true);
+ });
}
}
- void JustSummoned(Creature* summoned) override
+ void JustSummoned(Creature* summon) override
{
- switch (summoned->GetEntry())
+ if (summon->GetEntry() == NPC_ENTROPIUS)
{
- case NPC_ENTROPIUS:
- me->SetVisible(false);
- EntropiusGUID = summoned->GetGUID();
- if (HasEnraged) // If we hit phase transition while enraged, enrage Entropius as well.
- summoned->CastSpell(summoned, SPELL_ENRAGE);
- break;
- case NPC_DARK_FIENDS:
- summoned->CastSpell(summoned, SPELL_DARKFIEND_VISUAL);
- break;
+ me->SetVisible(false);
+ _entropiusGUID = summon->GetGUID();
+ if (_hasEnraged)
+ summon->CastSpell(summon, SPELL_ENRAGE, true);
+ return;
}
- summoned->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0, 50, true));
- summons.Summon(summoned);
+ BossAI::JustSummoned(summon);
}
- void ExecuteEvent(uint32 eventId) override
+ void UpdateAI(uint32 diff) override
{
- switch (eventId)
- {
- case EVENT_DARKNESS:
- if (!DarkFiend)
- {
- DarkFiend = true;
- DoCastAOE(SPELL_DARKNESS);
- }
- else
- me->SummonCreatureGroup(CREATURE_GROUP_DARKFIENDS);
- events.ScheduleEvent(EVENT_DARKNESS, DarkFiend ? 3000 : 42000, 0, PHASE_ONE);
- break;
- case EVENT_SUMMON_HUMANOIDS:
- me->SummonCreatureGroup(CREATURE_GROUP_HUMANOIDS);
- events.ScheduleEvent(EVENT_SUMMON_HUMANOIDS, 60000, 0, PHASE_ONE);
- break;
- case EVENT_SUMMON_SENTINEL:
- DoCastAOE(SPELL_OPEN_PORTAL_2);
- events.ScheduleEvent(EVENT_SUMMON_SENTINEL, 30000, 0, PHASE_ONE);
- break;
- case EVENT_PHASE_TRANSITION:
- DoCast(me, SPELL_SUMMON_ENTROPIUS);
- break;
- case EVENT_ENRAGE:
- if (Creature* entropius = ObjectAccessor::GetCreature(*me, EntropiusGUID))
- entropius->CastSpell(entropius, SPELL_ENRAGE);
- DoCast(me, SPELL_ENRAGE);
- HasEnraged = true;
- break;
- default:
- break;
- }
+ if (!UpdateVictim())
+ return;
+
+ scheduler.Update(diff);
}
private:
- bool DarkFiend;
- bool HasEnraged;
- ObjectGuid EntropiusGUID;
+ ObjectGuid _entropiusGUID;
+ bool _hasEnraged;
+ uint8 _phase;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -289,89 +340,50 @@ class npc_muru_portal : public CreatureScript
public:
npc_muru_portal() : CreatureScript("npc_muru_portal") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_muru_portalAI>(creature);
- }
-
struct npc_muru_portalAI : public ScriptedAI
{
- npc_muru_portalAI(Creature* creature) : ScriptedAI(creature), Summons(creature)
- {
- Initialize();
- SetCombatMovement(false);
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- SummonTimer = 5000;
-
- InAction = false;
- SummonSentinel = false;
- }
-
- InstanceScript* instance;
-
- SummonList Summons;
-
- bool SummonSentinel;
- bool InAction;
-
- uint32 SummonTimer;
-
- void Reset() override
- {
- Initialize();
+ npc_muru_portalAI(Creature* creature) : ScriptedAI(creature) { }
- me->AddUnitState(UNIT_STATE_STUNNED);
-
- Summons.DespawnAll();
- }
-
- void JustSummoned(Creature* summoned) override
+ void JustSummoned(Creature* summon) override
{
- if (Player* target = ObjectAccessor::GetPlayer(*me, instance->GetGuidData(DATA_PLAYER_GUID)))
- summoned->AI()->AttackStart(target);
+ DoCast(summon, SPELL_SUMMON_VOID_SENTINEL_SUMMONER_VISUAL, true);
- Summons.Summon(summoned);
+ summon->m_Events.AddEvent(new VoidSpawnSummon(summon), summon->m_Events.CalculateTime(1500));
}
- void SpellHit(Unit* /*caster*/, const SpellInfo* Spell) override
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- me->NearTeleportTo(x, y, z, o);
- InAction = true;
- switch (Spell->Id)
+ switch (spell->Id)
{
case SPELL_OPEN_ALL_PORTALS:
- DoCastAOE(SPELL_OPEN_PORTAL);
+ DoCastAOE(SPELL_OPEN_PORTAL, true);
+ DoCastAOE(SPELL_TRANSFORM_VISUAL_MISSILE, true);
break;
case SPELL_OPEN_PORTAL_2:
- DoCastAOE(SPELL_OPEN_PORTAL);
- SummonSentinel = true;
+ DoCastAOE(SPELL_OPEN_PORTAL, true);
+ _scheduler.Schedule(Seconds(6), [this](TaskContext /*context*/)
+ {
+ DoCastAOE(SPELL_SUMMON_VOID_SENTINEL_SUMMONER, true);
+ });
+ break;
+ default:
break;
}
}
void UpdateAI(uint32 diff) override
{
- if (!SummonSentinel)
- {
- if (InAction && instance->GetBossState(DATA_MURU) == NOT_STARTED)
- Reset();
- return;
- }
-
- if (SummonTimer <= diff)
- {
- DoCastAOE(SPELL_SUMMON_VOID_SENTINEL, false);
- SummonTimer = 5000;
- SummonSentinel = false;
- } else SummonTimer -= diff;
+ _scheduler.Update(diff);
}
+
+ private:
+ TaskScheduler _scheduler;
};
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetSunwellPlateauAI<npc_muru_portalAI>(creature);
+ }
};
class npc_dark_fiend : public CreatureScript
@@ -379,11 +391,6 @@ class npc_dark_fiend : public CreatureScript
public:
npc_dark_fiend() : CreatureScript("npc_dark_fiend") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_dark_fiendAI(creature);
- }
-
struct npc_dark_fiendAI : public ScriptedAI
{
npc_dark_fiendAI(Creature* creature) : ScriptedAI(creature)
@@ -393,54 +400,56 @@ public:
void Initialize()
{
- WaitTimer = 2000;
- InAction = false;
- }
+ me->SetDisplayId(me->GetCreatureTemplate()->Modelid2);
+ me->SetReactState(REACT_PASSIVE);
+ DoCast(me, SPELL_DARKFIEND_SKIN, true);
- uint32 WaitTimer;
- bool InAction;
+ _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/)
+ {
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- void Reset() override
- {
- Initialize();
+ if (Creature* _summoner = ObjectAccessor::GetCreature(*me, _summonerGUID))
+ if (Unit* target = _summoner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0))
+ AttackStart(target);
+ });
- me->AddUnitState(UNIT_STATE_STUNNED);
+ _scheduler.Schedule(Seconds(3), [this](TaskContext context)
+ {
+ if (me->IsWithinDist(me->GetVictim(), 5.0f) && me->HasAura(SPELL_DARKFIEND_SKIN))
+ {
+ DoCastAOE(SPELL_DARKFIEND_DAMAGE, false);
+ me->DisappearAndDie();
+ }
+
+ context.Repeat(Milliseconds(500));
+ });
}
- void SpellHit(Unit* /*caster*/, const SpellInfo* Spell) override
+ void IsSummonedBy(Unit* summoner) override
{
- for (uint8 i = 0; i < 3; ++i)
- if (Spell->Effects[i].Effect == 38)
- me->DisappearAndDie();
+ _summonerGUID = summoner->GetGUID();
}
- void UpdateAI(uint32 diff) override
+ bool CanAIAttack(Unit const* /*target*/) const override
{
- if (!UpdateVictim())
- return;
+ return me->HasAura(SPELL_DARKFIEND_SKIN);
+ }
- if (WaitTimer <= diff)
- {
- if (!InAction)
- {
- me->ClearUnitState(UNIT_STATE_STUNNED);
- DoCastAOE(SPELL_DARKFIEND_SKIN, false);
- AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true));
- InAction = true;
- WaitTimer = 500;
- }
- else
- {
- if (me->IsWithinDist(me->GetVictim(), 5))
- {
- DoCastAOE(SPELL_DARKFIEND_AOE, false);
- me->DisappearAndDie();
- }
- WaitTimer = 500;
- }
- } else WaitTimer -= diff;
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
}
+
+ private:
+ TaskScheduler _scheduler;
+ ObjectGuid _summonerGUID;
};
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetSunwellPlateauAI<npc_dark_fiendAI>(creature);
+ }
};
class npc_void_sentinel : public CreatureScript
@@ -448,63 +457,54 @@ class npc_void_sentinel : public CreatureScript
public:
npc_void_sentinel() : CreatureScript("npc_void_sentinel") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_void_sentinelAI(creature);
- }
-
struct npc_void_sentinelAI : public ScriptedAI
{
npc_void_sentinelAI(Creature* creature) : ScriptedAI(creature)
{
- Initialize();
+ _instance = me->GetInstanceScript();
}
- void Initialize()
+ void IsSummonedBy(Unit* /*summoner*/) override
{
- PulseTimer = 3000;
- VoidBlastTimer = 45000; //is this a correct timer?
+ if (Creature* muru = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MURU)))
+ muru->AI()->JustSummoned(me);
}
- uint32 PulseTimer;
- uint32 VoidBlastTimer;
-
- void Reset() override
+ void EnterCombat(Unit* /*who*/) override
{
- Initialize();
+ DoCast(me, SPELL_SHADOW_PULSE_PERIODIC, true);
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- me->NearTeleportTo(x, y, 71, o);
+ _scheduler.Schedule(Seconds(45), [this](TaskContext context)
+ {
+ DoCastVictim(SPELL_VOID_BLAST, false);
+
+ context.Repeat();
+ });
}
- void JustDied(Unit* killer) override
+ void JustDied(Unit* /*killer*/) override
{
- for (uint8 i = 0; i < 8; ++i)
- if (Creature* temp = me->SummonCreature(NPC_VOID_SPAWN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), float(rand32() % 6), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 180000))
- temp->AI()->AttackStart(killer);
+ for (uint8 i = 0; i < MAX_VOID_SPAWNS; ++i)
+ DoCastAOE(SPELL_SUMMON_VOID_SPAWN, true);
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
- return;
-
- if (PulseTimer <= diff)
- {
- DoCastAOE(SPELL_SHADOW_PULSE, true);
- PulseTimer = 3000;
- } else PulseTimer -= diff;
-
- if (VoidBlastTimer <= diff)
+ _scheduler.Update(diff, [this]
{
- DoCastVictim(SPELL_VOID_BLAST, false);
- VoidBlastTimer = 45000;
- } else VoidBlastTimer -= diff;
-
- DoMeleeAttackIfReady();
+ DoMeleeAttackIfReady();
+ });
}
+
+ private:
+ TaskScheduler _scheduler;
+ InstanceScript* _instance;
};
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetSunwellPlateauAI<npc_void_sentinelAI>(creature);
+ }
};
class npc_blackhole : public CreatureScript
@@ -512,84 +512,220 @@ class npc_blackhole : public CreatureScript
public:
npc_blackhole() : CreatureScript("npc_blackhole") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_blackholeAI>(creature);
- }
-
struct npc_blackholeAI : public ScriptedAI
{
npc_blackholeAI(Creature* creature) : ScriptedAI(creature)
{
- Initialize();
- instance = creature->GetInstanceScript();
+ _instance = creature->GetInstanceScript();
}
- void Initialize()
- {
- DespawnTimer = 15000;
- SpellTimer = 5000;
- Phase = 0;
- NeedForAHack = 0;
- }
-
- InstanceScript* instance;
-
- uint32 DespawnTimer;
- uint32 SpellTimer;
- uint8 Phase;
- uint8 NeedForAHack;
-
void Reset() override
{
- Initialize();
+ me->SetReactState(REACT_PASSIVE);
+ DoCast(SPELL_BLACKHOLE_SUMMON_VISUAL);
- me->AddUnitState(UNIT_STATE_STUNNED);
- DoCastAOE(SPELL_BLACKHOLE_SPAWN, true);
- }
+ _scheduler.Schedule(Seconds(15), [this](TaskContext /*context*/)
+ {
+ me->DisappearAndDie();
+ });
- void UpdateAI(uint32 diff) override
- {
- if (SpellTimer <= diff)
+ _scheduler.Schedule(Seconds(1), [this](TaskContext context)
{
- Unit* Victim = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PLAYER_GUID));
- switch (NeedForAHack)
+ switch (context.GetRepeatCounter())
{
case 0:
- me->ClearUnitState(UNIT_STATE_STUNNED);
- DoCastAOE(SPELL_BLACKHOLE_GROW, false);
- if (Victim)
- AttackStart(Victim);
- SpellTimer = 700;
- NeedForAHack = 2;
+ me->SetReactState(REACT_AGGRESSIVE);
+ DoCast(SPELL_BLACKHOLE_SUMMON_VISUAL_2);
+ if (Unit* victim = ObjectAccessor::GetUnit(*me, _instance->GetGuidData(DATA_PLAYER_GUID)))
+ AttackStart(victim);
+ context.Repeat(Milliseconds(1200));
break;
case 1:
- me->AddAura(SPELL_BLACKHOLE_GROW, me);
- NeedForAHack = 2;
- SpellTimer = 600;
+ DoCast(SPELL_BLACKHOLE_SUMMON_VISUAL);
+ context.Repeat(Seconds(2));
break;
case 2:
- SpellTimer = 400;
- NeedForAHack = 3;
- me->RemoveAura(SPELL_BLACKHOLE_GROW);
+ DoCast(SPELL_BLACKHOLE_PASSIVE);
+ DoCast(SPELL_BLACK_HOLE_VISUAL_2);
+ break;
+ default:
break;
- case 3:
- SpellTimer = urand(400, 900);
- NeedForAHack = 1;
- if (Unit* Temp = me->GetVictim())
- {
- if (Temp->GetPositionZ() > 73 && Victim)
- AttackStart(Victim);
- } else
- return;
}
- } else SpellTimer -= diff;
+ });
+ }
- if (DespawnTimer <= diff)
- me->DisappearAndDie();
- else DespawnTimer -= diff;
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
}
+
+ private:
+ TaskScheduler _scheduler;
+ InstanceScript* _instance;
};
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetSunwellPlateauAI<npc_blackholeAI>(creature);
+ }
+};
+
+class spell_summon_blood_elves_script : SpellScriptLoader
+{
+ public:
+ spell_summon_blood_elves_script() : SpellScriptLoader("spell_summon_blood_elves_script") { }
+
+ class spell_summon_blood_elves_script_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_summon_blood_elves_script_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ for (uint8 i = 0; i < MAX_SUMMON_BLOOD_ELVES; ++i)
+ if (!sSpellMgr->GetSpellInfo(SummonBloodElvesSpells[i]))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ for (uint8 i = 0; i < MAX_SUMMON_BLOOD_ELVES; ++i)
+ GetCaster()->CastSpell((Unit*)nullptr, SummonBloodElvesSpells[urand(0,3)], true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_summon_blood_elves_script_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_summon_blood_elves_script_SpellScript();
+ }
+};
+
+class spell_muru_darkness : SpellScriptLoader
+{
+ public:
+ spell_muru_darkness() : SpellScriptLoader("spell_muru_darkness") { }
+
+ class spell_muru_darkness_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_muru_darkness_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ for (uint8 i = 0; i < MAX_SUMMON_DARK_FIEND; ++i)
+ if (!sSpellMgr->GetSpellInfo(SummonDarkFiendSpells[i]))
+ return false;
+ return true;
+ }
+
+ void HandleAfterCast()
+ {
+ for (uint8 i = 0; i < MAX_SUMMON_DARK_FIEND; ++i)
+ GetCaster()->CastSpell((Unit*)nullptr, SummonDarkFiendSpells[i], true);
+ }
+
+ void Register() override
+ {
+ AfterCast += SpellCastFn(spell_muru_darkness_SpellScript::HandleAfterCast);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_muru_darkness_SpellScript();
+ }
+};
+
+class spell_dark_fiend_skin : public SpellScriptLoader
+{
+ public:
+ spell_dark_fiend_skin() : SpellScriptLoader("spell_dark_fiend_skin") { }
+
+ class spell_dark_fiend_skin_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dark_fiend_skin_AuraScript);
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL)
+ return;
+
+ if (Creature* target = GetTarget()->ToCreature())
+ {
+ target->SetReactState(REACT_PASSIVE);
+ target->AttackStop();
+ target->StopMoving();
+ target->CastSpell(target, SPELL_DARKFIEND_VISUAL, true);
+ target->DespawnOrUnsummon(3000);
+ }
+ }
+
+ void Register() override
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_dark_fiend_skin_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_dark_fiend_skin_AuraScript();
+ }
+};
+
+class spell_transform_visual_missile_periodic : public SpellScriptLoader
+{
+ public:
+ spell_transform_visual_missile_periodic() : SpellScriptLoader("spell_transform_visual_missile_periodic") { }
+
+ class spell_transform_visual_missile_periodic_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_transform_visual_missile_periodic_AuraScript);
+
+ void OnPeriodic(AuraEffect const* /*aurEff*/)
+ {
+ GetTarget()->CastSpell((Unit*)nullptr, RAND(TRANSFORM_VISUAL_MISSILE_1, TRANSFORM_VISUAL_MISSILE_2), true);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_transform_visual_missile_periodic_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_transform_visual_missile_periodic_AuraScript();
+ }
+};
+
+class spell_summon_blood_elves_periodic : public SpellScriptLoader
+{
+ public:
+ spell_summon_blood_elves_periodic() : SpellScriptLoader("spell_summon_blood_elves_periodic") { }
+
+ class spell_summon_blood_elves_periodic_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_summon_blood_elves_periodic_AuraScript);
+
+ void OnPeriodic(AuraEffect const* /*aurEff*/)
+ {
+ GetTarget()->CastSpell((Unit*)nullptr, SPELL_SUMMON_BLOOD_ELVES_SCRIPT, true);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_summon_blood_elves_periodic_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_summon_blood_elves_periodic_AuraScript();
+ }
};
void AddSC_boss_muru()
@@ -600,4 +736,9 @@ void AddSC_boss_muru()
new npc_dark_fiend();
new npc_void_sentinel();
new npc_blackhole();
+ new spell_summon_blood_elves_script();
+ new spell_muru_darkness();
+ new spell_dark_fiend_skin();
+ new spell_transform_visual_missile_periodic();
+ new spell_summon_blood_elves_periodic();
}
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h
index c6b4ae753a5..7ea0fc4bc7d 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h
@@ -97,7 +97,8 @@ enum CreatureIds
NPC_FURY_MAGE = 25799,
NPC_VOID_SENTINEL = 25772,
NPC_VOID_SPAWN = 25824,
- NPC_BLACK_HOLE = 25855
+ NPC_BLACK_HOLE = 25855,
+ NPC_MURU_PORTAL_TARGET = 25770
};
enum GameObjectIds
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
index 791f68bae31..72e76ba52bf 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
@@ -425,7 +425,7 @@ class npc_akilzon_eagle : public CreatureScript
if (Unit* target = ObjectAccessor::GetUnit(*me, TargetGUID))
DoCast(target, SPELL_EAGLE_SWOOP, true);
TargetGUID.Clear();
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
EagleSwoop_Timer = urand(5000, 10000);
}
}
@@ -454,7 +454,7 @@ class npc_akilzon_eagle : public CreatureScript
{
target->GetContactPoint(me, x, y, z);
z += 2;
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
TargetGUID = target->GetGUID();
}
me->GetMotionMaster()->MovePoint(0, x, y, z);
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp
index 0407cb6cd7c..188f9d0cc03 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp
@@ -142,7 +142,7 @@ class boss_nalorakk : public CreatureScript
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
inMove = false;
waitTimer = 0;
- me->SetSpeed(MOVE_RUN, 2);
+ me->SetSpeedRate(MOVE_RUN, 2);
me->SetWalk(false);
}else
{
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
index 96c0798ae05..a1c2369cf66 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
@@ -347,7 +347,7 @@ class boss_zuljin : public CreatureScript
Vortex->CastSpell(Vortex, SPELL_CYCLONE_PASSIVE, true);
Vortex->CastSpell(Vortex, SPELL_CYCLONE_VISUAL, true);
Vortex->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- Vortex->SetSpeed(MOVE_RUN, 1.0f);
+ Vortex->SetSpeedRate(MOVE_RUN, 1.0f);
Vortex->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0));
DoZoneInCombat(Vortex);
}
@@ -438,7 +438,7 @@ class boss_zuljin : public CreatureScript
{
if (me->GetVictim())
TankGUID = me->EnsureVictim()->GetGUID();
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
AttackStart(target); // change victim
Claw_Rage_Timer = 0;
Claw_Loop_Timer = 500;
@@ -462,7 +462,7 @@ class boss_zuljin : public CreatureScript
if (Claw_Counter == 12)
{
Claw_Rage_Timer = urand(15000, 20000);
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
AttackStart(ObjectAccessor::GetUnit(*me, TankGUID));
TankGUID.Clear();
return;
@@ -487,7 +487,7 @@ class boss_zuljin : public CreatureScript
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
TankGUID = me->EnsureVictim()->GetGUID();
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
AttackStart(target); // change victim
Lynx_Rush_Timer = 0;
Claw_Counter = 0;
@@ -510,7 +510,7 @@ class boss_zuljin : public CreatureScript
if (Claw_Counter == 9)
{
Lynx_Rush_Timer = urand(15000, 20000);
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
AttackStart(ObjectAccessor::GetUnit(*me, TankGUID));
TankGUID.Clear();
}
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp
index 8c179af2adf..3697b0876c8 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp
@@ -148,6 +148,11 @@ class boss_mandokir : public CreatureScript
instance->SaveToDB();
}
+ void JustReachedHome() override
+ {
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ }
+
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
@@ -194,9 +199,9 @@ class boss_mandokir : public CreatureScript
me->SetWalk(false);
if (id == POINT_MANDOKIR_END)
{
- me->SetHomePosition(PosMandokir[0]);
+ me->SetHomePosition(PosMandokir[1]);
+ me->GetMotionMaster()->MoveTargetedHome();
instance->SetBossState(DATA_MANDOKIR, NOT_STARTED);
- me->DespawnOrUnsummon(6000); // No idea how to respawn on wipe.
}
}
}
diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
index 8c781bb9001..b794a653791 100644
--- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
+++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
@@ -89,7 +89,6 @@ void AddSC_boss_sulfuron();
void AddSC_boss_majordomo();
void AddSC_boss_ragnaros();
void AddSC_instance_molten_core();
-void AddSC_instance_ragefire_chasm(); //Ragefire Chasm
void AddSC_the_scarlet_enclave(); //Scarlet Enclave
void AddSC_the_scarlet_enclave_c1();
void AddSC_the_scarlet_enclave_c2();
@@ -268,7 +267,6 @@ void AddEasternKingdomsScripts()
AddSC_boss_majordomo();
AddSC_boss_ragnaros();
AddSC_instance_molten_core();
- AddSC_instance_ragefire_chasm(); //Ragefire Chasm
AddSC_the_scarlet_enclave(); //Scarlet Enclave
AddSC_the_scarlet_enclave_c1();
AddSC_the_scarlet_enclave_c2();
diff --git a/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp b/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp
index b0d6e782052..03c957fa221 100644
--- a/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp
+++ b/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp
@@ -174,7 +174,7 @@ public:
if (state == DONE)
if (GameObject* go = instance->GetGameObject(shrineOfGelihastGUID))
go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
- break;
+ break;
case DATA_AKU_MAI:
if (state == DONE)
if (GameObject* go = instance->GetGameObject(altarOfTheDeepsGUID))
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp
index 44cbdec2cb5..8e53ab06ca5 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp
@@ -479,12 +479,12 @@ public:
void DoMount()
{
me->Mount(SKARLOC_MOUNT_MODEL);
- me->SetSpeed(MOVE_RUN, SPEED_MOUNT);
+ me->SetSpeedRate(MOVE_RUN, SPEED_MOUNT);
}
void DoUnmount()
{
me->Dismount();
- me->SetSpeed(MOVE_RUN, SPEED_RUN);
+ me->SetSpeedRate(MOVE_RUN, SPEED_RUN);
}
void EnterCombat(Unit* /*who*/) override
{
@@ -535,12 +535,12 @@ public:
if (!UpdateVictim())
return;
- /// @todo add his abilities'n-crap here
- if (!LowHp && HealthBelowPct(20))
- {
- Talk(SAY_TH_RANDOM_LOW_HP);
- LowHp = true;
- }
+ /// @todo add his abilities'n-crap here
+ if (!LowHp && HealthBelowPct(20))
+ {
+ Talk(SAY_TH_RANDOM_LOW_HP);
+ LowHp = true;
+ }
}
};
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp
index 8715f3ca0f6..4d0f80d502d 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp
@@ -106,33 +106,33 @@ public:
if (!UpdateVictim())
return;
- events.Update(diff);
+ events.Update(diff);
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- switch (eventId)
- {
- case EVENT_SANDBREATH:
- DoCastVictim(SPELL_SAND_BREATH);
- events.ScheduleEvent(EVENT_SANDBREATH, urand(15000, 25000));
- break;
- case EVENT_TIMESTOP:
- DoCastVictim(SPELL_TIME_STOP);
- events.ScheduleEvent(EVENT_TIMESTOP, urand(20000, 35000));
- break;
- case EVENT_FRENZY:
- Talk(EMOTE_FRENZY);
- DoCast(me, SPELL_ENRAGE);
- events.ScheduleEvent(EVENT_FRENZY, urand(20000, 35000));
- break;
- default:
- break;
- }
+ case EVENT_SANDBREATH:
+ DoCastVictim(SPELL_SAND_BREATH);
+ events.ScheduleEvent(EVENT_SANDBREATH, urand(15000, 25000));
+ break;
+ case EVENT_TIMESTOP:
+ DoCastVictim(SPELL_TIME_STOP);
+ events.ScheduleEvent(EVENT_TIMESTOP, urand(20000, 35000));
+ break;
+ case EVENT_FRENZY:
+ Talk(EMOTE_FRENZY);
+ DoCast(me, SPELL_ENRAGE);
+ events.ScheduleEvent(EVENT_FRENZY, urand(20000, 35000));
+ break;
+ default:
+ break;
}
- DoMeleeAttackIfReady();
+ }
+ DoMeleeAttackIfReady();
}
};
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp
index 2befdabe550..844ce239bdf 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp
@@ -107,36 +107,36 @@ public:
if (!UpdateVictim())
return;
- events.Update(diff);
+ events.Update(diff);
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- switch (eventId)
- {
- case EVENT_HASTE:
- DoCast(me, SPELL_HASTE);
- events.ScheduleEvent(EVENT_HASTE, urand(20000, 25000));
- break;
- case EVENT_MORTAL_WOUND:
- DoCast(me, SPELL_MORTAL_WOUND);
- events.ScheduleEvent(EVENT_MORTAL_WOUND, urand(10000, 20000));
- break;
- case EVENT_WING_BUFFET:
- DoCast(me, SPELL_WING_BUFFET);
- events.ScheduleEvent(EVENT_WING_BUFFET, urand(20000, 30000));
- break;
- case EVENT_SPELL_REFLECTION: // Only in Heroic
- DoCast(me, SPELL_REFLECT);
- events.ScheduleEvent(EVENT_SPELL_REFLECTION, urand(25000, 35000));
- break;
- default:
- break;
- }
+ case EVENT_HASTE:
+ DoCast(me, SPELL_HASTE);
+ events.ScheduleEvent(EVENT_HASTE, urand(20000, 25000));
+ break;
+ case EVENT_MORTAL_WOUND:
+ DoCast(me, SPELL_MORTAL_WOUND);
+ events.ScheduleEvent(EVENT_MORTAL_WOUND, urand(10000, 20000));
+ break;
+ case EVENT_WING_BUFFET:
+ DoCast(me, SPELL_WING_BUFFET);
+ events.ScheduleEvent(EVENT_WING_BUFFET, urand(20000, 30000));
+ break;
+ case EVENT_SPELL_REFLECTION: // Only in Heroic
+ DoCast(me, SPELL_REFLECT);
+ events.ScheduleEvent(EVENT_SPELL_REFLECTION, urand(25000, 35000));
+ break;
+ default:
+ break;
}
- DoMeleeAttackIfReady();
+ }
+ DoMeleeAttackIfReady();
}
};
diff --git a/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp b/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp
index 7da15b1fdce..71ebe870e3d 100644
--- a/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp
+++ b/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp
@@ -205,7 +205,7 @@ public:
PointData = GetMoveData();
MovePoint = PointData->LocIdEnd;
- me->SetSpeed(MOVE_FLIGHT, 1.5f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.5f);
me->GetMotionMaster()->MovePoint(8, MiddleRoomLocation);
}
}
@@ -220,7 +220,7 @@ public:
PointData = GetMoveData();
if (PointData)
{
- me->SetSpeed(MOVE_FLIGHT, 1.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
me->GetMotionMaster()->MovePoint(PointData->LocId, PointData->fX, PointData->fY, PointData->fZ);
}
break;
@@ -250,7 +250,7 @@ public:
if (Creature * trigger = me->SummonCreature(NPC_TRIGGER, MiddleRoomLocation, TEMPSUMMON_CORPSE_DESPAWN))
triggerGUID = trigger->GetGUID();
me->GetMotionMaster()->MoveTakeoff(11, Phase2Floating);
- me->SetSpeed(MOVE_FLIGHT, 1.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
Talk(SAY_PHASE_2_TRANS);
instance->SetData(DATA_ONYXIA_PHASE, Phase);
events.ScheduleEvent(EVENT_WHELP_SPAWN, 5000);
diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
index f1969a063d6..87edf4781d4 100644
--- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
+++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
@@ -47,6 +47,7 @@ void AddSC_boss_meathook();
void AddSC_culling_of_stratholme();
void AddSC_instance_culling_of_stratholme();
void AddSC_instance_dire_maul(); //Dire Maul
+void AddSC_instance_ragefire_chasm(); //Ragefire Chasm
void AddSC_boss_celebras_the_cursed(); //Maraudon
void AddSC_boss_landslide();
void AddSC_boss_noxxion();
@@ -143,6 +144,7 @@ void AddKalimdorScripts()
AddSC_culling_of_stratholme();
AddSC_instance_culling_of_stratholme();
AddSC_instance_dire_maul(); //Dire Maul
+ AddSC_instance_ragefire_chasm(); //Ragefire Chasm
AddSC_boss_celebras_the_cursed(); //Maraudon
AddSC_boss_landslide();
AddSC_boss_noxxion();
diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp
index 1ed95c16a0d..4847ac46542 100644
--- a/src/server/scripts/Kalimdor/zone_azshara.cpp
+++ b/src/server/scripts/Kalimdor/zone_azshara.cpp
@@ -404,7 +404,7 @@ public:
DoCast(me, SPELL_PERIODIC_DEPTH_CHARGE);
me->SetHover(true);
me->SetSwim(true);
- me->SetSpeed(MOVE_RUN, 0.85f, true);
+ me->SetSpeedRate(MOVE_RUN, 0.85f);
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MovePoint(CurrWP, WPs[CurrWP]);
Escape = true;
diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp
index ca32ea7420b..3f4a905147f 100644
--- a/src/server/scripts/Kalimdor/zone_desolace.cpp
+++ b/src/server/scripts/Kalimdor/zone_desolace.cpp
@@ -89,7 +89,7 @@ public:
me->UpdateEntry(NPC_TAMED_KODO);
me->CombatStop();
me->DeleteThreatList();
- me->SetSpeed(MOVE_RUN, 0.6f, true);
+ me->SetSpeedRate(MOVE_RUN, 0.6f);
me->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, me->GetFollowAngle());
me->setActive(true);
}
@@ -225,37 +225,9 @@ public:
}
};
-/*######
-## go_demon_portal
-######*/
-
-enum DemonPortal
-{
- NPC_DEMON_GUARDIAN = 11937,
- QUEST_PORTAL_OF_THE_LEGION = 5581
-};
-
-class go_demon_portal : public GameObjectScript
-{
- public:
- go_demon_portal() : GameObjectScript("go_demon_portal") { }
-
- bool OnGossipHello(Player* player, GameObject* go) override
- {
- if (player->GetQuestStatus(QUEST_PORTAL_OF_THE_LEGION) == QUEST_STATUS_INCOMPLETE && !go->FindNearestCreature(NPC_DEMON_GUARDIAN, 5.0f, true))
- {
- if (Creature* guardian = player->SummonCreature(NPC_DEMON_GUARDIAN, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ(), 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0))
- guardian->AI()->AttackStart(player);
- }
-
- return true;
- }
-};
-
void AddSC_desolace()
{
new npc_aged_dying_ancient_kodo();
new go_iruxos();
new npc_dalinda();
- new go_demon_portal();
}
diff --git a/src/server/scripts/Kalimdor/zone_durotar.cpp b/src/server/scripts/Kalimdor/zone_durotar.cpp
index 05caf9cca3a..62a041e7798 100644
--- a/src/server/scripts/Kalimdor/zone_durotar.cpp
+++ b/src/server/scripts/Kalimdor/zone_durotar.cpp
@@ -483,8 +483,8 @@ class spell_mount_check : public SpellScriptLoader
else if (!owner->IsMounted() && target->IsMounted())
target->Dismount();
- target->SetSpeed(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN));
- target->SetSpeed(MOVE_WALK, owner->GetSpeedRate(MOVE_WALK));
+ target->SetSpeedRate(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN));
+ target->SetSpeedRate(MOVE_WALK, owner->GetSpeedRate(MOVE_WALK));
}
void Register() override
diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
index 852cd62c277..7a963d066b6 100644
--- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp
+++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
@@ -43,38 +43,37 @@ EndContentData */
## npc_beaten_corpse
######*/
-#define GOSSIP_CORPSE "Examine corpse in detail..."
-
enum BeatenCorpse
{
- QUEST_LOST_IN_BATTLE = 4921
+ GOSSIP_OPTION_ID_BEATEN_CORPSE = 0,
+ GOSSIP_MENU_OPTION_INSPECT_BODY = 2871
};
class npc_beaten_corpse : public CreatureScript
{
-public:
- npc_beaten_corpse() : CreatureScript("npc_beaten_corpse") { }
+ public:
+ npc_beaten_corpse() : CreatureScript("npc_beaten_corpse") { }
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF +1)
+ struct npc_beaten_corpseAI : public ScriptedAI
{
- player->SEND_GOSSIP_MENU(3558, creature->GetGUID());
- player->TalkedToCreature(creature->GetEntry(), creature->GetGUID());
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (player->GetQuestStatus(QUEST_LOST_IN_BATTLE) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(QUEST_LOST_IN_BATTLE) == QUEST_STATUS_COMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_CORPSE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+ npc_beaten_corpseAI(Creature* creature) : ScriptedAI(creature)
+ {
+ }
- player->SEND_GOSSIP_MENU(3557, creature->GetGUID());
- return true;
- }
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
+ {
+ if (menuId == GOSSIP_MENU_OPTION_INSPECT_BODY && gossipListId == GOSSIP_OPTION_ID_BEATEN_CORPSE)
+ {
+ player->CLOSE_GOSSIP_MENU();
+ player->TalkedToCreature(me->GetEntry(), me->GetGUID());
+ }
+ }
+ };
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_beaten_corpseAI(creature);
+ }
};
/*######
@@ -552,7 +551,7 @@ public:
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
{
- if (me->getStandState() == UNIT_STAND_STATE_DEAD)
+ if (me->GetStandState() == UNIT_STAND_STATE_DEAD)
me->SetStandState(UNIT_STAND_STATE_STAND);
IsPostEvent = false;
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 ab09dd45710..3c11fd5d9a6 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
@@ -454,11 +454,11 @@ public:
float distance = me->GetDistance(JedogaPosition[1]);
if (distance < 9.0f)
- me->SetSpeed(MOVE_WALK, 0.5f, true);
+ me->SetSpeedRate(MOVE_WALK, 0.5f);
else if (distance < 15.0f)
- me->SetSpeed(MOVE_WALK, 0.75f, true);
+ me->SetSpeedRate(MOVE_WALK, 0.75f);
else if (distance < 20.0f)
- me->SetSpeed(MOVE_WALK, 1.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 1.0f);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(1, JedogaPosition[1]);
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
index e25f64f61aa..04b62f77e9a 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
@@ -178,7 +178,7 @@ class boss_prince_taldaram : public CreatureScript
if (Unit* embraceTarget = GetEmbraceTarget())
{
me->GetMotionMaster()->Clear();
- me->SetSpeed(MOVE_WALK, 2.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 2.0f);
me->GetMotionMaster()->MoveChase(embraceTarget);
}
events.ScheduleEvent(EVENT_VANISHED, 1300);
@@ -188,7 +188,7 @@ class boss_prince_taldaram : public CreatureScript
DoCast(embraceTarget, SPELL_EMBRACE_OF_THE_VAMPYR);
Talk(SAY_FEED);
me->GetMotionMaster()->Clear();
- me->SetSpeed(MOVE_WALK, 1.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 1.0f);
me->GetMotionMaster()->MoveChase(me->GetVictim());
events.ScheduleEvent(EVENT_FEEDING, 20000);
break;
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp
index ae4a5e2a69a..9235b75d53e 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp
@@ -18,6 +18,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
+#include "SpellScript.h"
#include "ruby_sanctum.h"
#include "Player.h"
@@ -45,6 +46,11 @@ enum Events
EVENT_XERESTRASZA_EVENT_7 = 7,
};
+enum Spells
+{
+ SPELL_RALLY = 75416
+};
+
Position const xerestraszaMovePos = {3151.236f, 379.8733f, 86.31996f, 0.0f};
class npc_xerestrasza : public CreatureScript
@@ -165,8 +171,53 @@ class at_baltharus_plateau : public AreaTriggerScript
}
};
+// 75415 - Rallying Shout
+class spell_ruby_sanctum_rallying_shout : public SpellScriptLoader
+{
+ public:
+ spell_ruby_sanctum_rallying_shout() : SpellScriptLoader("spell_ruby_sanctum_rallying_shout") { }
+
+ class spell_ruby_sanctum_rallying_shout_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ruby_sanctum_rallying_shout_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_RALLY))
+ return false;
+ return true;
+ }
+
+ void CountTargets(std::list<WorldObject*>& targets)
+ {
+ _targetCount = targets.size();
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ if (_targetCount && !GetCaster()->HasAura(SPELL_RALLY))
+ GetCaster()->CastCustomSpell(SPELL_RALLY, SPELLVALUE_AURA_STACK, _targetCount, GetCaster(), TRIGGERED_FULL_MASK);
+ }
+
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_ruby_sanctum_rallying_shout_SpellScript::CountTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
+ OnEffectHit += SpellEffectFn(spell_ruby_sanctum_rallying_shout_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+
+ private:
+ uint32 _targetCount = 0;
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ruby_sanctum_rallying_shout_SpellScript();
+ }
+};
+
void AddSC_ruby_sanctum()
{
new npc_xerestrasza();
new at_baltharus_plateau();
+ new spell_ruby_sanctum_rallying_shout();
}
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 c89510211b9..7440984d7c5 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
@@ -804,7 +804,7 @@ class npc_anubarak_spike : public CreatureScript
void StartChase(Unit* who)
{
DoCast(who, SPELL_MARK);
- me->SetSpeed(MOVE_RUN, 0.5f);
+ me->SetSpeedRate(MOVE_RUN, 0.5f);
// make sure the Spine will really follow the one he should
me->getThreatManager().clearReferences();
me->SetInCombatWithZone();
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 4c2b92da0ea..3ac85809fa2 100644
--- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
@@ -2580,7 +2580,7 @@ class npc_quel_delar_sword : public CreatureScript
void Reset() override
{
_events.Reset();
- me->SetSpeed(MOVE_FLIGHT, 4.5f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 4.5f);
DoCast(SPELL_WHIRLWIND_VISUAL);
if (_intro)
_events.ScheduleEvent(EVENT_QUEL_DELAR_INIT, 0);
diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp
index 03f12e46bc3..b24a36da3fb 100644
--- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp
@@ -418,7 +418,7 @@ class boss_krick : public CreatureScript
case EVENT_OUTRO_6:
if (Creature* tyrannus = ObjectAccessor::GetCreature(*me, _instanceScript->GetGuidData(DATA_TYRANNUS_EVENT)))
{
- tyrannus->SetSpeed(MOVE_FLIGHT, 3.5f, true);
+ tyrannus->SetSpeedRate(MOVE_FLIGHT, 3.5f);
tyrannus->GetMotionMaster()->MovePoint(1, outroPos[4]);
_tyrannusGUID = tyrannus->GetGUID();
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
index 166e9739a95..51711f9ded5 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
@@ -1069,7 +1069,7 @@ class npc_blood_queen_lana_thel : public CreatureScript
if (Creature* summon = DoSummon(NPC_FLOATING_TRIGGER, triggerPos, 15000, TEMPSUMMON_TIMED_DESPAWN))
{
summon->CastSpell(summon, SPELL_OOC_INVOCATION_VISUAL, true);
- summon->SetSpeed(MOVE_FLIGHT, 0.15f, true); // todo: creature is swimming, check if this is blizzlike or not.
+ summon->SetSpeedRate(MOVE_FLIGHT, 0.15f); // todo: creature is swimming, check if this is blizzlike or not.
summon->GetMotionMaster()->MovePoint(0, triggerEndPos);
}
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
index bc8c7f877a9..3d21388ca11 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
@@ -809,7 +809,7 @@ class spell_blood_queen_pact_of_the_darkfallen_dmg : public SpellScriptLoader
// this is an additional effect to be executed
void PeriodicTick(AuraEffect const* aurEff)
{
- SpellInfo const* damageSpell = sSpellMgr->EnsureSpellInfo(SPELL_PACT_OF_THE_DARKFALLEN_DAMAGE);
+ SpellInfo const* damageSpell = sSpellMgr->AssertSpellInfo(SPELL_PACT_OF_THE_DARKFALLEN_DAMAGE);
int32 damage = damageSpell->Effects[EFFECT_0].CalcValue();
float multiplier = 0.3375f + 0.1f * uint32(aurEff->GetTickNumber()/10); // do not convert to 0.01f - we need tick number/10 as INT (damage increases every 10 ticks)
damage = int32(damage * multiplier);
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 9b0693ec95e..73a7de36580 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
@@ -1843,7 +1843,7 @@ class spell_igb_rocket_pack : public SpellScriptLoader
void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
- SpellInfo const* damageInfo = sSpellMgr->EnsureSpellInfo(SPELL_ROCKET_PACK_DAMAGE);
+ SpellInfo const* damageInfo = sSpellMgr->AssertSpellInfo(SPELL_ROCKET_PACK_DAMAGE);
GetTarget()->CastCustomSpell(SPELL_ROCKET_PACK_DAMAGE, SPELLVALUE_BASE_POINT0, 2 * (damageInfo->Effects[EFFECT_0].CalcValue() + aurEff->GetTickNumber() * aurEff->GetAmplitude()), NULL, TRIGGERED_FULL_MASK);
GetTarget()->CastSpell(NULL, SPELL_ROCKET_BURST, TRIGGERED_FULL_MASK);
}
@@ -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_lord_marrowgar.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
index 056231285e2..9f20799df82 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
@@ -142,7 +142,7 @@ class boss_lord_marrowgar : public CreatureScript
void Reset() override
{
_Reset();
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
me->RemoveAurasDueToSpell(SPELL_BONE_STORM);
me->RemoveAurasDueToSpell(SPELL_BERSERK);
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
@@ -224,7 +224,7 @@ class boss_lord_marrowgar : public CreatureScript
case EVENT_BONE_STORM_BEGIN:
if (Aura* pStorm = me->GetAura(SPELL_BONE_STORM))
pStorm->SetDuration(int32(_boneStormDuration));
- me->SetSpeed(MOVE_RUN, _baseSpeed*3.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*3.0f);
Talk(SAY_BONE_STORM);
events.ScheduleEvent(EVENT_BONE_STORM_END, _boneStormDuration+1);
// no break here
@@ -242,7 +242,7 @@ class boss_lord_marrowgar : public CreatureScript
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MoveChase(me->GetVictim());
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
events.CancelEvent(EVENT_BONE_STORM_MOVE);
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
if (!IsHeroic())
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
index f4e9d4673f2..fe6e4081326 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
@@ -369,21 +369,21 @@ class boss_professor_putricide : public CreatureScript
{
case POINT_FESTERGUT:
instance->SetBossState(DATA_FESTERGUT, IN_PROGRESS); // needed here for delayed gate close
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
DoAction(ACTION_FESTERGUT_GAS);
if (Creature* festergut = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FESTERGUT)))
festergut->CastSpell(festergut, SPELL_GASEOUS_BLIGHT_LARGE, false, NULL, NULL, festergut->GetGUID());
break;
case POINT_ROTFACE:
instance->SetBossState(DATA_ROTFACE, IN_PROGRESS); // needed here for delayed gate close
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
DoAction(ACTION_ROTFACE_OOZE);
events.ScheduleEvent(EVENT_ROTFACE_OOZE_FLOOD, 25000, 0, PHASE_ROTFACE);
break;
case POINT_TABLE:
// stop attack
me->GetMotionMaster()->MoveIdle();
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
if (GameObject* table = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_PUTRICIDE_TABLE)))
me->SetFacingToObject(table);
// operating on new phase already
@@ -418,7 +418,7 @@ class boss_professor_putricide : public CreatureScript
{
case ACTION_FESTERGUT_COMBAT:
SetPhase(PHASE_FESTERGUT);
- me->SetSpeed(MOVE_RUN, _baseSpeed*2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*2.0f);
me->GetMotionMaster()->MovePoint(POINT_FESTERGUT, festergutWatchPos);
me->SetReactState(REACT_PASSIVE);
DoZoneInCombat(me);
@@ -435,7 +435,7 @@ class boss_professor_putricide : public CreatureScript
case ACTION_ROTFACE_COMBAT:
{
SetPhase(PHASE_ROTFACE);
- me->SetSpeed(MOVE_RUN, _baseSpeed*2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*2.0f);
me->GetMotionMaster()->MovePoint(POINT_ROTFACE, rotfaceWatchPos);
me->SetReactState(REACT_PASSIVE);
_oozeFloodStage = 0;
@@ -477,7 +477,7 @@ class boss_professor_putricide : public CreatureScript
events.ScheduleEvent(EVENT_ROTFACE_DIES, 4500, 0, PHASE_ROTFACE);
break;
case ACTION_CHANGE_PHASE:
- me->SetSpeed(MOVE_RUN, _baseSpeed*2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*2.0f);
events.DelayEvents(30000);
me->AttackStop();
if (!IsHeroic())
@@ -1257,7 +1257,7 @@ class spell_putricide_mutated_plague : public SpellScriptLoader
return;
uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell;
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(triggerSpell);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(triggerSpell);
spell = sSpellMgr->GetSpellForDifficultyFromSpell(spell, caster);
int32 damage = spell->Effects[EFFECT_0].CalcValue(caster);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
index 0b129f3aa89..1a37d5238d2 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
@@ -315,7 +315,7 @@ class boss_sindragosa : public CreatureScript
me->SetCanFly(true);
me->SetDisableGravity(true);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
- me->SetSpeed(MOVE_FLIGHT, 4.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 4.0f);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
float moveTime = me->GetExactDist(&SindragosaFlyPos) / (me->GetSpeed(MOVE_FLIGHT) * 0.001f);
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, SindragosaLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
@@ -351,7 +351,7 @@ class boss_sindragosa : public CreatureScript
me->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
me->SetHomePosition(SindragosaLandPos);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- me->SetSpeed(MOVE_FLIGHT, 2.5f);
+ me->SetSpeedRate(MOVE_FLIGHT, 2.5f);
// Sindragosa enters combat as soon as she lands
DoZoneInCombat();
@@ -604,6 +604,7 @@ class npc_ice_tomb : public CreatureScript
_trappedPlayerGUID.Clear();
player->RemoveAurasDueToSpell(SPELL_ICE_TOMB_DAMAGE);
player->RemoveAurasDueToSpell(SPELL_ASPHYXIATION);
+ player->RemoveAurasDueToSpell(SPELL_ICE_TOMB_UNTARGETABLE);
}
}
@@ -697,7 +698,7 @@ class npc_spinestalker : public CreatureScript
return;
me->setActive(true);
- me->SetSpeed(MOVE_FLIGHT, 2.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 2.0f);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
float moveTime = me->GetExactDist(&SpinestalkerFlyPos) / (me->GetSpeed(MOVE_FLIGHT) * 0.001f);
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, SpinestalkerLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
@@ -834,7 +835,7 @@ class npc_rimefang : public CreatureScript
return;
me->setActive(true);
- me->SetSpeed(MOVE_FLIGHT, 2.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 2.0f);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
float moveTime = me->GetExactDist(&RimefangFlyPos) / (me->GetSpeed(MOVE_FLIGHT) * 0.001f);
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, RimefangLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
@@ -1284,9 +1285,9 @@ class spell_sindragosa_ice_tomb : public SpellScriptLoader
public:
spell_sindragosa_ice_tomb() : SpellScriptLoader("spell_sindragosa_ice_tomb_trap") { }
- class spell_sindragosa_ice_tomb_SpellScript : public SpellScript
+ class spell_sindragosa_ice_tomb_AuraScript : public AuraScript
{
- PrepareSpellScript(spell_sindragosa_ice_tomb_SpellScript);
+ PrepareAuraScript(spell_sindragosa_ice_tomb_AuraScript);
bool Validate(SpellInfo const* /*spell*/) override
{
@@ -1297,46 +1298,42 @@ class spell_sindragosa_ice_tomb : public SpellScriptLoader
return true;
}
- void SummonTomb()
+ void PeriodicTick(AuraEffect const* aurEff)
{
- Position pos = GetHitUnit()->GetPosition();
- if (TempSummon* summon = GetCaster()->SummonCreature(NPC_ICE_TOMB, pos))
+ PreventDefaultAction();
+
+ if (aurEff->GetTickNumber() == 1)
{
- summon->AI()->SetGUID(GetHitUnit()->GetGUID(), DATA_TRAPPED_PLAYER);
- 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 (Unit* caster = GetCaster())
{
- go->SetSpellId(SPELL_ICE_TOMB_DAMAGE);
- summon->AddGameObject(go);
+ Position pos = GetTarget()->GetPosition();
+
+ if (TempSummon* summon = caster->SummonCreature(NPC_ICE_TOMB, pos))
+ {
+ 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))
+ {
+ go->SetSpellId(SPELL_ICE_TOMB_DAMAGE);
+ summon->AddGameObject(go);
+ }
+ }
}
}
}
- void Register() override
- {
- AfterHit += SpellHitFn(spell_sindragosa_ice_tomb_SpellScript::SummonTomb);
- }
- };
-
- class spell_sindragosa_ice_tomb_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_sindragosa_ice_tomb_AuraScript);
-
- void PeriodicTick(AuraEffect const* /*aurEff*/)
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- PreventDefaultAction();
+ GetTarget()->RemoveAurasDueToSpell(SPELL_ICE_TOMB_UNTARGETABLE);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_sindragosa_ice_tomb_AuraScript::PeriodicTick, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_sindragosa_ice_tomb_AuraScript::HandleRemove, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
}
};
- SpellScript* GetSpellScript() const override
- {
- return new spell_sindragosa_ice_tomb_SpellScript();
- }
-
AuraScript* GetAuraScript() const override
{
return new spell_sindragosa_ice_tomb_AuraScript();
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 4675989228a..4a0a8217af8 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -763,7 +763,7 @@ class boss_the_lich_king : public CreatureScript
{
summons.Summon(summon);
summon->SetReactState(REACT_PASSIVE);
- summon->SetSpeed(MOVE_FLIGHT, 0.5f);
+ summon->SetSpeedRate(MOVE_FLIGHT, 0.5f);
summon->GetMotionMaster()->MoveRandom(10.0f);
if (!events.IsInPhase(PHASE_FROSTMOURNE))
summon->m_Events.AddEvent(new VileSpiritActivateEvent(summon), summon->m_Events.CalculateTime(15000));
@@ -1448,7 +1448,7 @@ class npc_valkyr_shadowguard : public CreatureScript
_events.Reset();
me->SetReactState(REACT_PASSIVE);
DoCast(me, SPELL_WINGS_OF_THE_DAMNED, false);
- me->SetSpeed(MOVE_FLIGHT, 0.642857f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.642857f);
}
void IsSummonedBy(Unit* /*summoner*/) override
diff --git a/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp b/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp
new file mode 100644
index 00000000000..71d90da21a1
--- /dev/null
+++ b/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "ScriptMgr.h"
+#include "ScriptedCreature.h"
+#include "BattlegroundIC.h"
+
+enum BossSpells
+{
+ SPELL_BRUTAL_STRIKE = 58460,
+ SPELL_DAGGER_THROW = 67280,
+ SPELL_CRUSHING_LEAP = 68506,
+ SPELL_RAGE = 66776
+};
+
+enum BossEvents
+{
+ EVENT_BRUTAL_STRIKE = 1,
+ EVENT_DAGGER_THROW = 2,
+ EVENT_CRUSHING_LEAP = 3,
+ EVENT_CHECK_RANGE = 4
+};
+
+class boss_ioc_horde_alliance : public CreatureScript
+{
+public:
+ boss_ioc_horde_alliance() : CreatureScript("boss_ioc_horde_alliance") { }
+
+ struct boss_ioc_horde_allianceAI : public ScriptedAI
+ {
+ boss_ioc_horde_allianceAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void Reset() override
+ {
+ _events.Reset();
+
+ uint32 _npcGuard;
+ if (me->GetEntry() == NPC_HIGH_COMMANDER_HALFORD_WYRMBANE)
+ _npcGuard = NPC_SEVEN_TH_LEGION_INFANTRY;
+ else
+ _npcGuard = NPC_KOR_KRON_GUARD;
+
+ std::list<Creature*> guardsList;
+ me->GetCreatureListWithEntryInGrid(guardsList, _npcGuard, 100.0f);
+ for (std::list<Creature*>::const_iterator itr = guardsList.begin(); itr != guardsList.end(); ++itr)
+ (*itr)->Respawn();
+ };
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_BRUTAL_STRIKE, 5 * IN_MILLISECONDS);
+ _events.ScheduleEvent(EVENT_DAGGER_THROW, 7 * IN_MILLISECONDS);
+ _events.ScheduleEvent(EVENT_CHECK_RANGE, 1 * IN_MILLISECONDS);
+ _events.ScheduleEvent(EVENT_CRUSHING_LEAP, 15 * IN_MILLISECONDS);
+ }
+
+ void SpellHit(Unit* caster, SpellInfo const* /*spell*/) override
+ {
+ if (caster->IsVehicle())
+ me->Kill(caster);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_BRUTAL_STRIKE:
+ DoCastVictim(SPELL_BRUTAL_STRIKE);
+ _events.ScheduleEvent(EVENT_BRUTAL_STRIKE, 5 * IN_MILLISECONDS);
+ break;
+ case EVENT_DAGGER_THROW:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1))
+ DoCast(target, SPELL_DAGGER_THROW);
+ _events.ScheduleEvent(EVENT_DAGGER_THROW, 7 * IN_MILLISECONDS);
+ break;
+ case EVENT_CRUSHING_LEAP:
+ DoCastVictim(SPELL_CRUSHING_LEAP);
+ _events.ScheduleEvent(EVENT_CRUSHING_LEAP, 25 * IN_MILLISECONDS);
+ break;
+ case EVENT_CHECK_RANGE:
+ if (me->GetDistance(me->GetHomePosition()) > 25.0f)
+ DoCast(me, SPELL_RAGE);
+ else
+ me->RemoveAurasDueToSpell(SPELL_RAGE);
+ _events.ScheduleEvent(EVENT_CHECK_RANGE, 1 * IN_MILLISECONDS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap _events;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new boss_ioc_horde_allianceAI(creature);
+ }
+};
+
+void AddSC_boss_ioc_horde_alliance()
+{
+ new boss_ioc_horde_alliance();
+}
diff --git a/src/server/scripts/Northrend/isle_of_conquest.cpp b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp
index 11cc645f0cb..11cc645f0cb 100644
--- a/src/server/scripts/Northrend/isle_of_conquest.cpp
+++ b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
index e8c4216b00e..60c60640c04 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
@@ -162,13 +162,13 @@ public:
summons.DoZoneInCombat();
events.SetPhase(PHASE_NORMAL);
- events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL);
- events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL);
- events.ScheduleEvent(EVENT_LOCUST, urand(80,120) * IN_MILLISECONDS, 0, PHASE_NORMAL);
- events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_IMPALE, randtime(Seconds(10), Seconds(20)), 0, PHASE_NORMAL);
+ events.ScheduleEvent(EVENT_SCARABS, randtime(Seconds(20), Seconds(30)), 0, PHASE_NORMAL);
+ events.ScheduleEvent(EVENT_LOCUST, Minutes(1)+randtime(Seconds(40), Seconds(60)), 0, PHASE_NORMAL);
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
if (!Is25ManRaid())
- events.ScheduleEvent(EVENT_SPAWN_GUARD, urand(15, 20) * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SPAWN_GUARD, randtime(Seconds(15), Seconds(20)));
}
void UpdateAI(uint32 diff) override
@@ -189,11 +189,9 @@ public:
else
EnterEvadeMode();
- events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL);
+ events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_SCARABS:
- events.ScheduleEvent(EVENT_SCARABS, urand(40 * IN_MILLISECONDS, 60 * IN_MILLISECONDS), 0, PHASE_NORMAL);
-
if (!guardCorpses.empty())
{
if (ObjectGuid target = Trinity::Containers::SelectRandomContainerElement(guardCorpses))
@@ -204,27 +202,28 @@ public:
creatureTarget->DespawnOrUnsummon();
}
}
+ events.Repeat(randtime(Seconds(40), Seconds(60)));
break;
case EVENT_LOCUST:
Talk(EMOTE_LOCUST);
+ events.SetPhase(PHASE_SWARM);
DoCast(me, SPELL_LOCUST_SWARM);
- events.ScheduleEvent(EVENT_SPAWN_GUARD, 3 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(19, 23) * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_LOCUST, 90000);
- events.SetPhase(PHASE_SWARM);
+ events.ScheduleEvent(EVENT_SPAWN_GUARD, Seconds(3));
+ events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(Seconds(19), Seconds(23)));
+ events.Repeat(Minutes(1)+Seconds(30));
break;
case EVENT_LOCUST_ENDS:
- events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL);
- events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL);
events.SetPhase(PHASE_NORMAL);
+ events.ScheduleEvent(EVENT_IMPALE, randtime(Seconds(10), Seconds(20)), 0, PHASE_NORMAL);
+ events.ScheduleEvent(EVENT_SCARABS, randtime(Seconds(20), Seconds(30)), 0, PHASE_NORMAL);
break;
case EVENT_SPAWN_GUARD:
me->SummonCreatureGroup(GROUP_SINGLE_SPAWN);
break;
case EVENT_BERSERK:
DoCast(me, SPELL_BERSERK, true);
- events.ScheduleEvent(EVENT_BERSERK, 600000);
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
break;
}
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp
index 39c41c935bf..8a7bdd293ba 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp
@@ -100,9 +100,9 @@ class boss_faerlina : public CreatureScript
_EnterCombat();
Talk(SAY_AGGRO);
summons.DoZoneInCombat();
- events.ScheduleEvent(EVENT_POISON, urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS));
- events.ScheduleEvent(EVENT_FIRE, urand(6 * IN_MILLISECONDS, 18 * IN_MILLISECONDS));
- events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS));
+ events.ScheduleEvent(EVENT_POISON, randtime(Seconds(10), Seconds(15)));
+ events.ScheduleEvent(EVENT_FIRE, randtime(Seconds(6), Seconds(18)));
+ events.ScheduleEvent(EVENT_FRENZY, Minutes(1)+randtime(Seconds(0), Seconds(20)));
}
void Reset() override
@@ -111,9 +111,9 @@ class boss_faerlina : public CreatureScript
_frenzyDispels = 0;
}
- void KilledUnit(Unit* /*victim*/) override
+ void KilledUnit(Unit* victim) override
{
- if (!urand(0, 2))
+ if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
@@ -158,21 +158,21 @@ class boss_faerlina : public CreatureScript
case EVENT_POISON:
if (!me->HasAura(SPELL_WIDOWS_EMBRACE_HELPER))
DoCastAOE(SPELL_POISON_BOLT_VOLLEY);
- events.ScheduleEvent(EVENT_POISON, urand(8000, 15000));
+ events.Repeat(randtime(Seconds(8), Seconds(15)));
break;
case EVENT_FIRE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_RAIN_OF_FIRE);
- events.ScheduleEvent(EVENT_FIRE, urand(6000, 18000));
+ events.Repeat(randtime(Seconds(6), Seconds(18)));
break;
case EVENT_FRENZY:
if (Aura* widowsEmbrace = me->GetAura(SPELL_WIDOWS_EMBRACE_HELPER))
- events.ScheduleEvent(EVENT_FRENZY, widowsEmbrace->GetDuration()+1 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_FRENZY, Milliseconds(widowsEmbrace->GetDuration()+1));
else
{
DoCast(SPELL_FRENZY);
Talk(EMOTE_FRENZY);
- events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS));
+ events.Repeat(Minutes(1) + randtime(Seconds(0), Seconds(20)));
}
break;
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp
index a7a89f44d81..0de1c4785b8 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp
@@ -285,7 +285,7 @@ struct boss_four_horsemen_baseAI : public BossAI
void EnterCombat(Unit* /*who*/) override
{
- if (instance->GetBossState(BOSS_HORSEMEN) != NOT_STARTED) // another horseman already did it
+ if (instance->GetBossState(BOSS_HORSEMEN) == IN_PROGRESS || instance->GetBossState(BOSS_HORSEMEN) == DONE) // another horseman already did it
return;
Talk(SAY_AGGRO);
BeginEncounter();
@@ -411,9 +411,9 @@ class boss_four_horsemen_baron : public CreatureScript
else
AttackStart(threat.getHostilTarget());
- events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(3,7));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
+ events.ScheduleEvent(EVENT_MARK, Seconds(24));
+ events.ScheduleEvent(EVENT_UNHOLYSHADOW, randtime(Seconds(3), Seconds(7)));
}
void _UpdateAI(uint32 diff) override
@@ -431,11 +431,11 @@ class boss_four_horsemen_baron : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_BARON_MARK, true);
- events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS);
+ events.Repeat(Seconds(12));
break;
case EVENT_UNHOLYSHADOW:
DoCastVictim(SPELL_UNHOLY_SHADOW);
- events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(10,30));
+ events.Repeat(randtime(Seconds(10), Seconds(30)));
break;
}
}
@@ -484,9 +484,9 @@ class boss_four_horsemen_thane : public CreatureScript
else
AttackStart(threat.getHostilTarget());
- events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_METEOR, urandms(10,15));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
+ events.ScheduleEvent(EVENT_MARK, Seconds(24));
+ events.ScheduleEvent(EVENT_METEOR, randtime(Seconds(10), Seconds(25)));
}
void _UpdateAI(uint32 diff) override
{
@@ -503,7 +503,7 @@ class boss_four_horsemen_thane : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_THANE_MARK, true);
- events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS);
+ events.Repeat(Seconds(12));
break;
case EVENT_METEOR:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true))
@@ -511,7 +511,7 @@ class boss_four_horsemen_thane : public CreatureScript
DoCast(target, SPELL_METEOR);
_shouldSay = true;
}
- events.ScheduleEvent(EVENT_METEOR, urandms(13,17));
+ events.Repeat(randtime(Seconds(13), Seconds(17)));
break;
}
}
@@ -550,9 +550,9 @@ class boss_four_horsemen_lady : public CreatureScript
boss_four_horsemen_ladyAI(Creature* creature) : boss_four_horsemen_baseAI(creature, LADY, ladyPath) { }
void BeginFighting() override
{
- events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_VOIDZONE, urandms(5,10));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
+ events.ScheduleEvent(EVENT_MARK, Seconds(24));
+ events.ScheduleEvent(EVENT_VOIDZONE, randtime(Seconds(5), Seconds(10)));
}
void _UpdateAI(uint32 diff) override
@@ -578,7 +578,7 @@ class boss_four_horsemen_lady : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_LADY_MARK, true);
- events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS);
+ events.Repeat(Seconds(15));
break;
case EVENT_VOIDZONE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true))
@@ -586,7 +586,7 @@ class boss_four_horsemen_lady : public CreatureScript
DoCast(target, SPELL_VOID_ZONE, true);
Talk(SAY_SPECIAL);
}
- events.ScheduleEvent(EVENT_VOIDZONE, urandms(12, 18));
+ events.Repeat(randtime(Seconds(12), Seconds(18)));
break;
}
}
@@ -620,9 +620,9 @@ class boss_four_horsemen_sir : public CreatureScript
boss_four_horsemen_sirAI(Creature* creature) : boss_four_horsemen_baseAI(creature, SIR, sirPath), _shouldSay(true) { }
void BeginFighting() override
{
- events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_HOLYWRATH, urandms(13,18));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
+ events.ScheduleEvent(EVENT_MARK, Seconds(24));
+ events.ScheduleEvent(EVENT_HOLYWRATH, randtime(Seconds(13), Seconds(18)));
}
void _UpdateAI(uint32 diff) override
@@ -648,7 +648,7 @@ class boss_four_horsemen_sir : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_SIR_MARK, true);
- events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS);
+ events.Repeat(Seconds(15));
break;
case EVENT_HOLYWRATH:
if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 45.0f, true))
@@ -656,7 +656,7 @@ class boss_four_horsemen_sir : public CreatureScript
DoCast(target, SPELL_HOLY_WRATH, true);
_shouldSay = true;
}
- events.ScheduleEvent(EVENT_HOLYWRATH, urandms(10,18));
+ events.Repeat(randtime(Seconds(10), Seconds(18)));
break;
}
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
index ec47b0db101..ef6ccf5bf4b 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
@@ -18,38 +18,74 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
+#include "SpellScript.h"
+#include <math.h>
-enum Spells
+enum Texts
{
- SPELL_MORTAL_WOUND = 25646,
- SPELL_ENRAGE = 28371,
- SPELL_DECIMATE = 28374,
- SPELL_BERSERK = 26662,
- SPELL_INFECTED_WOUND = 29306
+ EMOTE_SPOTS_ONE = 0,
+ EMOTE_DECIMATE = 1,
+ EMOTE_ENRAGE = 2,
+ EMOTE_DEVOURS_ALL = 3,
+ EMOTE_BERSERKER = 4
};
-enum Creatures
+enum Spells
{
- NPC_ZOMBIE = 16360
+ // Gluth
+ SPELL_MORTAL_WOUND = 54378, // spell effect dummy unused. what its supposed to do is unkown.
+ SPELL_ENRAGE = 28371, // 54427 in 25 man.
+ SPELL_DECIMATE = 28374, // 54426 in 25 man. Effect0 is handled by SpellScript (see below) and 2 rows in conditions table. Effect2 is handled by SpellScript (see below).
+ SPELL_DECIMATE_DMG = 28375,
+ SPELL_BERSERK = 26662,
+ SPELL_ZOMBIE_CHOW_SEARCH_SINGLE = 28239, // Insta kill spell. Single target. See spell script below.
+ SPELL_ZOMBIE_CHOW_SEARCH_MULTI = 28404, // Insta kill spell. Affect all zombies 10 yards around Gluth's center. See one row conditions table + spell script below.
+
+ // Zombies
+ SPELL_INFECTED_WOUND = 29307 // Used by the zombies on self.
};
Position const PosSummon[3] =
{
- {3267.9f, -3172.1f, 297.42f, 0.94f},
- {3253.2f, -3132.3f, 297.42f, 0},
- {3308.3f, -3185.8f, 297.42f, 1.58f},
+ { 3270.132f, -3169.948f, 297.5891f, 5.88176f },
+ { 3307.298f, -3183.449f, 297.5891f, 5.742133f },
+ { 3255.708f, -3135.677f, 297.5891f, 1.867502f }
};
enum Events
{
- EVENT_WOUND = 1,
+ EVENT_WOUND = 1,
EVENT_ENRAGE,
EVENT_DECIMATE,
EVENT_BERSERK,
EVENT_SUMMON,
+ EVENT_SEARCH_ZOMBIE_SINGLE,
+ EVENT_KILL_ZOMBIE_SINGLE,
+ EVENT_SEARCH_ZOMBIE_MULTI
};
-#define EMOTE_NEARBY " spots a nearby zombie to devour!"
+enum States
+{
+ STATE_GLUTH_NORMAL = 1,
+ STATE_GLUTH_EATING = 2,
+
+ STATE_ZOMBIE_NORMAL = 1,
+ STATE_ZOMBIE_DECIMATED = 2,
+ STATE_ZOMBIE_TOBE_EATEN = 3
+};
+
+enum SummonGroups
+{
+ SUMMON_GROUP_CHOW_10MAN = 1,
+ SUMMON_GROUP_CHOW_25MAN = 2
+};
+
+enum Misc
+{
+ EVENT_GLUTH_ZOMBIE_BEHAVIOR = 10495, // unused. event handled by spell_gluth_decimate_SpellScript::HandleEvent
+ DATA_ZOMBIE_STATE = 1,
+ ACTION_DECIMATE_EVENT = 2,
+};
class boss_gluth : public CreatureScript
{
@@ -58,99 +94,388 @@ public:
CreatureAI* GetAI(Creature* creature) const override
{
- return new boss_gluthAI(creature);
+ return GetInstanceAI<boss_gluthAI>(creature);
}
struct boss_gluthAI : public BossAI
{
- boss_gluthAI(Creature* creature) : BossAI(creature, BOSS_GLUTH)
- {
- // Do not let Gluth be affected by zombies' debuff
- me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_INFECTED_WOUND, true);
- }
- void MoveInLineOfSight(Unit* who) override
+ boss_gluthAI(Creature* creature) : BossAI(creature, BOSS_GLUTH), state(STATE_GLUTH_NORMAL) {}
+
+ void Reset() override
{
- if (who->GetEntry() == NPC_ZOMBIE && me->IsWithinDistInMap(who, 7))
- {
- SetGazeOn(who);
- /// @todo use a script text
- me->TextEmote(EMOTE_NEARBY, nullptr, true);
- }
- else
- BossAI::MoveInLineOfSight(who);
+ _Reset();
+ zombieToBeEatenGUID.Clear();
+ state = STATE_GLUTH_NORMAL;
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->SetSpeed(UnitMoveType::MOVE_RUN, 12.0f);
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.ScheduleEvent(EVENT_WOUND, 10000);
- events.ScheduleEvent(EVENT_ENRAGE, 15000);
- events.ScheduleEvent(EVENT_DECIMATE, 105000);
- events.ScheduleEvent(EVENT_BERSERK, 8*60000);
- events.ScheduleEvent(EVENT_SUMMON, 15000);
+ events.ScheduleEvent(EVENT_WOUND, Seconds(10));
+ events.ScheduleEvent(EVENT_ENRAGE, randtime(Seconds(16), Seconds(22)));
+ events.ScheduleEvent(EVENT_DECIMATE, randtime(Minutes(1)+Seconds(50), Minutes(2)));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(8));
+ events.ScheduleEvent(EVENT_SUMMON, Seconds(15));
+ events.ScheduleEvent(EVENT_SEARCH_ZOMBIE_SINGLE, Seconds(12));
}
- void JustSummoned(Creature* summon) override
+ void SummonedCreatureDies(Creature* summoned, Unit* /* who */) override
{
- if (summon->GetEntry() == NPC_ZOMBIE)
- summon->AI()->AttackStart(me);
- summons.Summon(summon);
+ summons.Despawn(summoned); // needed or else dead zombies not despawned yet will still be in the list
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictimWithGaze())
+ if (!UpdateVictim() || !CheckInRoom())
return;
events.Update(diff);
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_WOUND:
+ if (state == STATE_GLUTH_EATING)
+ {
+ events.Repeat(Seconds(3));
+ break;
+ }
+
DoCastVictim(SPELL_MORTAL_WOUND);
- events.ScheduleEvent(EVENT_WOUND, 10000);
+ events.Repeat(Seconds(10));
break;
case EVENT_ENRAGE:
- /// @todo Add missing text
+ if (state == STATE_GLUTH_EATING)
+ {
+ events.Repeat(Seconds(5));
+ break;
+ }
+
+ Talk(EMOTE_ENRAGE);
DoCast(me, SPELL_ENRAGE);
- events.ScheduleEvent(EVENT_ENRAGE, 15000);
+ events.Repeat(randtime(Seconds(16), Seconds(22)));
break;
case EVENT_DECIMATE:
- /// @todo Add missing text
+ if (state == STATE_GLUTH_EATING)
+ {
+ events.Repeat(Seconds(4));
+ break;
+ }
+
+ Talk(EMOTE_DECIMATE);
DoCastAOE(SPELL_DECIMATE);
- events.ScheduleEvent(EVENT_DECIMATE, 105000);
+ for (int i = 1; i <= 20; i++)
+ events.ScheduleEvent(EVENT_SEARCH_ZOMBIE_MULTI, Seconds(3*i));
+ events.ScheduleEvent(EVENT_DECIMATE, randtime(Minutes(1)+Seconds(50), Minutes(2)));
break;
case EVENT_BERSERK:
+ Talk(EMOTE_BERSERKER);
DoCast(me, SPELL_BERSERK);
- events.ScheduleEvent(EVENT_BERSERK, 5*60000);
+ events.Repeat(Minutes(5)); //refresh the hard enrage buff
break;
case EVENT_SUMMON:
- for (int32 i = 0; i < RAID_MODE(1, 2); ++i)
- DoSummon(NPC_ZOMBIE, PosSummon[rand32() % RAID_MODE(1, 3)]);
- events.ScheduleEvent(EVENT_SUMMON, 10000);
+ if (Is25ManRaid()) // one wave each 10s. one wave=1 zombie in 10man and 2 zombies in 25man.
+ me->SummonCreatureGroup(SUMMON_GROUP_CHOW_25MAN);
+ else
+ me->SummonCreatureGroup(SUMMON_GROUP_CHOW_10MAN);
+ events.Repeat(Seconds(10));
+ break;
+ case EVENT_SEARCH_ZOMBIE_SINGLE:
+ {
+ Creature* zombie = nullptr;
+ for (SummonList::const_iterator itr = summons.begin(); !zombie && itr != summons.end(); ++itr)
+ {
+ zombie=ObjectAccessor::GetCreature(*me, *itr);
+ if (!zombie || !zombie->IsAlive() || !zombie->IsWithinDistInMap(me, 10.0))
+ zombie = nullptr;
+ }
+
+ if (zombie)
+ {
+ zombieToBeEatenGUID = zombie->GetGUID(); // save for later use
+
+ // the soon-to-be-eaten zombie should stop moving and stop attacking
+ zombie->AI()->SetData(DATA_ZOMBIE_STATE, STATE_ZOMBIE_TOBE_EATEN);
+
+ // gluth should stop AAs on his primary target and turn toward the zombie (2 yards away). He then pauses for a few seconds.
+ me->SetSpeed(MOVE_RUN, 36.0f);
+
+ me->SetReactState(ReactStates::REACT_PASSIVE);
+ me->AttackStop();
+
+ Talk(EMOTE_SPOTS_ONE);
+
+ //me->SetTarget(ObjectGuid::Empty);
+
+ me->GetMotionMaster()->MoveCloserAndStop(1, zombie, 2.0f);
+
+ state = STATE_GLUTH_EATING;
+ }
+
+ events.Repeat(RAID_MODE(Seconds(7), Seconds(5)));
+ break;
+ }
+ case EVENT_KILL_ZOMBIE_SINGLE:
+ {
+ Creature* zombieToBeEaten = ObjectAccessor::GetCreature(*me, zombieToBeEatenGUID);
+ if (zombieToBeEaten && zombieToBeEaten->IsAlive() && zombieToBeEaten->IsWithinDistInMap(me, 10.0))
+ DoCast(zombieToBeEaten, SPELL_ZOMBIE_CHOW_SEARCH_SINGLE); // do the killing + healing in done inside by spell script see below.
+
+ zombieToBeEatenGUID = ObjectGuid::Empty;
+ state = STATE_GLUTH_NORMAL;
+ me->SetSpeed(UnitMoveType::MOVE_RUN, 12.0f);
+
+ // and then return on primary target
+ me->SetReactState(REACT_AGGRESSIVE);
+
+ break;
+ }
+ case EVENT_SEARCH_ZOMBIE_MULTI:
+ {
+ if (state == STATE_GLUTH_EATING) // skip and simply wait for the next occurence
+ break;
+
+ Creature* zombie = nullptr;
+ for (SummonList::const_iterator itr = summons.begin(); !zombie && itr != summons.end(); ++itr)
+ {
+ zombie = ObjectAccessor::GetCreature(*me, *itr);
+ if (zombie && zombie->IsAlive() && zombie->GetExactDist2d(me) > 18.0)
+ zombie = nullptr;
+ }
+
+ if (zombie) // cast the aoe spell only if at least one zombie is found nearby
+ {
+ Talk(EMOTE_DEVOURS_ALL);
+ DoCastAOE(SPELL_ZOMBIE_CHOW_SEARCH_MULTI);
+ }
break;
+ }
}
}
- if (me->GetVictim() && me->EnsureVictim()->GetEntry() == NPC_ZOMBIE)
+ DoMeleeAttackIfReady();
+ }
+
+ void MovementInform(uint32 /*type*/, uint32 id) override
+ {
+ if (id == 1){
+ me->GetMotionMaster()->MoveIdle();
+ events.ScheduleEvent(EVENT_KILL_ZOMBIE_SINGLE, Seconds(1));
+ }
+
+ }
+
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_DECIMATE_EVENT:
+ for (ObjectGuid zombieGuid : summons)
+ {
+ Creature* zombie = ObjectAccessor::GetCreature(*me, zombieGuid);
+ if (zombie && zombie->IsAlive())
+ zombie->AI()->SetData(DATA_ZOMBIE_STATE, STATE_ZOMBIE_DECIMATED);
+ }
+ break;
+ }
+ }
+
+ private:
+ ObjectGuid zombieToBeEatenGUID;
+ uint8 state;
+ };
+
+};
+
+// spell 28374 (10man) / 54426 (25man) - Decimate
+class spell_gluth_decimate : public SpellScriptLoader
+{
+public:
+ spell_gluth_decimate() : SpellScriptLoader("spell_gluth_decimate") { }
+
+ class spell_gluth_decimate_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gluth_decimate_SpellScript);
+
+ // handles the damaging effect of the decimate spell.
+ void HandleScriptEffect(SpellEffIndex /* index */)
+ {
+ if (Unit *unit = GetHitUnit())
{
- if (me->IsWithinMeleeRange(me->GetVictim()))
+ int32 damage = int32(unit->GetHealth()) - int32(unit->CountPctFromMaxHealth(5));
+ if (damage > 0)
+ GetCaster()->CastCustomSpell(SPELL_DECIMATE_DMG, SPELLVALUE_BASE_POINT0, damage, unit);
+ }
+ }
+
+ // handles the change of zombies behavior after the decimate spell
+ void HandleEvent(SpellEffIndex /* index */)
+ {
+ GetCaster()->GetAI()->DoAction(ACTION_DECIMATE_EVENT);
+ }
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return (sSpellMgr->GetSpellInfo(SPELL_DECIMATE_DMG) != nullptr);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ OnEffectHit += SpellEffectFn(spell_gluth_decimate_SpellScript::HandleEvent, EFFECT_2, SPELL_EFFECT_SEND_EVENT);
+ }
+
+ bool Load() override
+ {
+ return GetCaster() && GetCaster()->GetEntry() == NPC_GLUTH;
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gluth_decimate_SpellScript();
+ }
+
+};
+
+// used by both 28239 & 28404 (single target and aoe zombie-kill spell) to heal Gluth on each target hit.
+
+class spell_gluth_zombiechow_search : public SpellScriptLoader
+{
+public:
+ spell_gluth_zombiechow_search() : SpellScriptLoader("spell_gluth_zombiechow_search") { }
+
+ class spell_gluth_zombiechow_search_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gluth_zombiechow_search_SpellScript);
+
+ void HealForEachTargetHit()
+ {
+ GetCaster()->ModifyHealth(int32(GetCaster()->CountPctFromMaxHealth(5)));
+ }
+
+ void Register() override
+ {
+ AfterHit += SpellHitFn(spell_gluth_zombiechow_search_SpellScript::HealForEachTargetHit);
+ }
+
+ bool Load() override
+ {
+ return GetCaster() && GetCaster()->GetEntry() == NPC_GLUTH;
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gluth_zombiechow_search_SpellScript();
+ }
+
+};
+
+// creature 16360 (10man) / 30303 (25man)
+class npc_zombie_chow : public CreatureScript
+{
+public:
+
+ npc_zombie_chow() : CreatureScript("npc_zombie_chow") { }
+
+ struct npc_zombie_chowAI : public ScriptedAI
+ {
+ npc_zombie_chowAI(Creature* creature) : ScriptedAI(creature)
+ {
+ GluthGUID = creature->GetInstanceScript()->GetGuidData(DATA_GLUTH);
+
+ DoCast(me, SPELL_INFECTED_WOUND);
+ timer = 0;
+ state = STATE_ZOMBIE_NORMAL;
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ 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.
{
- me->Kill(me->GetVictim());
- me->ModifyHealth(int32(me->CountPctFromMaxHealth(5)));
+ 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();
}
- else
+ else if (state == STATE_ZOMBIE_NORMAL)
DoMeleeAttackIfReady();
}
+
+ void SetData(uint32 id, uint32 value) override
+ {
+ if (id == DATA_ZOMBIE_STATE) // change of state
+ {
+ state = value;
+ if (value == STATE_ZOMBIE_DECIMATED)
+ {
+ me->SetReactState(ReactStates::REACT_PASSIVE);
+ me->AttackStop();
+ me->SetTarget(ObjectGuid::Empty);
+ // at this point, the zombie should be non attacking and non moving.
+
+ me->SetWalk(true); // it doesnt seem to work with MoveFollow() (but it does work with MovePoint()).
+
+ timer = 1000;
+ }
+ else if (value == STATE_ZOMBIE_TOBE_EATEN)
+ {
+ // forced to stand still
+ me->GetMotionMaster()->Clear();
+ me->StopMoving();
+
+ // and loose aggro behavior
+ me->SetReactState(ReactStates::REACT_PASSIVE);
+ me->AttackStop();
+ me->SetTarget(ObjectGuid::Empty);
+
+ me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_GRIP, true); // not sure if this is blizz-like but this is very convenient
+ }
+ }
+ }
+
+ uint32 GetData(uint32 index) const override
+ {
+ if (index == DATA_ZOMBIE_STATE)
+ return state;
+ return 0;
+ }
+
+ private:
+ uint32 timer;
+ uint8 state;
+ ObjectGuid GluthGUID;
};
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_zombie_chowAI>(creature);
+ }
};
void AddSC_boss_gluth()
{
new boss_gluth();
+ new spell_gluth_decimate();
+ new spell_gluth_zombiechow_search();
+ new npc_zombie_chow();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
index cf083900e8b..30c05c9dca9 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
@@ -324,13 +324,13 @@ class boss_gothik : public CreatureScript
{
_EnterCombat();
events.SetPhase(PHASE_ONE);
- events.ScheduleEvent(EVENT_SUMMON, 25 * IN_MILLISECONDS, 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_DOORS_UNLOCK, 205 * IN_MILLISECONDS, 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_PHASE_TWO, 270 * IN_MILLISECONDS, 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_SUMMON, Seconds(25), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_DOORS_UNLOCK, Minutes(3) + Seconds(25), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(4) + Seconds(30), 0, PHASE_ONE);
Talk(SAY_INTRO_1);
- events.ScheduleEvent(EVENT_INTRO_2, 4 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_INTRO_3, 9 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_INTRO_4, 14 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_INTRO_2, Seconds(4));
+ events.ScheduleEvent(EVENT_INTRO_3, Seconds(9));
+ events.ScheduleEvent(EVENT_INTRO_4, Seconds(14));
instance->SetData(DATA_GOTHIK_GATE, GO_STATE_READY);
_gateIsOpen = false;
}
@@ -480,7 +480,7 @@ class boss_gothik : public CreatureScript
}
if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second)
- events.ScheduleEvent(EVENT_SUMMON, timeToNext * IN_MILLISECONDS, 0, PHASE_ONE);
+ events.Repeat(Seconds(timeToNext));
++_waveCount;
break;
@@ -497,9 +497,9 @@ class boss_gothik : public CreatureScript
break;
case EVENT_PHASE_TWO:
events.SetPhase(PHASE_TWO);
- events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TELEPORT, Seconds(20), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_HARVEST, Seconds(15), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_RESUME_ATTACK, Seconds(2), 0, PHASE_TWO);
Talk(SAY_PHASE_TWO);
Talk(EMOTE_PHASE_TWO);
me->SetReactState(REACT_PASSIVE);
@@ -518,23 +518,23 @@ class boss_gothik : public CreatureScript
_lastTeleportDead = !_lastTeleportDead;
events.CancelEvent(EVENT_BOLT);
- events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO);
+ events.Repeat(Seconds(20));
}
break;
case EVENT_HARVEST:
DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt
- events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO);
+ events.Repeat(Seconds(15));
break;
case EVENT_RESUME_ATTACK:
me->SetReactState(REACT_AGGRESSIVE);
- events.ScheduleEvent(EVENT_BOLT, 0, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_BOLT, Seconds(0), 0, PHASE_TWO);
// return to the start of this method so victim side etc is re-evaluated
return UpdateAI(0u); // tail recursion for efficiency
case EVENT_BOLT:
DoCastVictim(SPELL_SHADOW_BOLT);
- events.ScheduleEvent(EVENT_BOLT, 1 * IN_MILLISECONDS, 0, PHASE_TWO);
+ events.Repeat(Seconds(2));
break;
case EVENT_INTRO_2:
Talk(SAY_INTRO_2);
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
index a1d8f6e5e02..cf38d659d40 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
@@ -57,10 +57,10 @@ class boss_grobbulus : public CreatureScript
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.ScheduleEvent(EVENT_CLOUD, 15000);
- events.ScheduleEvent(EVENT_INJECT, 20000);
- events.ScheduleEvent(EVENT_SPRAY, urand(15000, 30000)); // not sure
- events.ScheduleEvent(EVENT_BERSERK, 12 * 60000);
+ events.ScheduleEvent(EVENT_CLOUD, Seconds(15));
+ events.ScheduleEvent(EVENT_INJECT, Seconds(20));
+ events.ScheduleEvent(EVENT_SPRAY, randtime(Seconds(15), Seconds(30))); // not sure
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(12));
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
@@ -82,19 +82,19 @@ class boss_grobbulus : public CreatureScript
{
case EVENT_CLOUD:
DoCastAOE(SPELL_POISON_CLOUD);
- events.ScheduleEvent(EVENT_CLOUD, 15000);
+ events.Repeat(Seconds(15));
return;
case EVENT_BERSERK:
DoCastAOE(SPELL_BERSERK, true);
return;
case EVENT_SPRAY:
DoCastAOE(SPELL_SLIME_SPRAY);
- events.ScheduleEvent(EVENT_SPRAY, urand(15000, 30000));
+ events.Repeat(randtime(Seconds(15), Seconds(30)));
return;
case EVENT_INJECT:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_MUTATING_INJECTION))
DoCast(target, SPELL_MUTATING_INJECTION);
- events.ScheduleEvent(EVENT_INJECT, 8000 + uint32(120 * me->GetHealthPct()));
+ events.Repeat(Seconds(8) + Milliseconds(uint32(std::round(120 * me->GetHealthPct()))));
return;
default:
break;
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp
index 9b9619fe5b9..41d718d188d 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp
@@ -27,6 +27,7 @@ enum Spells
SPELL_SPELL_DISRUPTION = 29310,
SPELL_PLAGUE_CLOUD = 29350,
SPELL_TELEPORT_SELF = 30211,
+ SPELL_ERUPTION = 29371
};
enum Yells
@@ -60,6 +61,15 @@ enum Misc
DATA_SAFETY_DANCE = 19962139
};
+static const uint32 firstEruptionDBGUID = 84980;
+static const uint8 numSections = 4;
+static const uint8 numEruptions[numSections] = { // count of sequential GO DBGUIDs in the respective section of the room
+ 15,
+ 25,
+ 23,
+ 13
+};
+
class boss_heigan : public CreatureScript
{
public:
@@ -72,7 +82,7 @@ public:
struct boss_heiganAI : public BossAI
{
- boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), eruptSection(0), eruptDirection(false), safetyDance(false) { }
+ boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), _safeSection(0), _danceDirection(false), _safetyDance(false) { }
void Reset() override
{
@@ -82,15 +92,16 @@ public:
void KilledUnit(Unit* who) override
{
- Talk(SAY_SLAY);
-
if (who->GetTypeId() == TYPEID_PLAYER)
- safetyDance = false;
+ {
+ Talk(SAY_SLAY);
+ _safetyDance = false;
+ }
}
uint32 GetData(uint32 type) const override
{
- return (type == DATA_SAFETY_DANCE && safetyDance) ? 1u : 0u;
+ return (type == DATA_SAFETY_DANCE && _safetyDance) ? 1u : 0u;
}
void JustDied(Unit* /*killer*/) override
@@ -104,13 +115,27 @@ public:
_EnterCombat();
Talk(SAY_AGGRO);
- eruptSection = 3;
- events.ScheduleEvent(EVENT_DISRUPT, urand(15 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT);
- events.ScheduleEvent(EVENT_FEVER, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT);
- events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT);
- events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT);
+ _safeSection = 0;
+ events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT);
+ events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(10), Seconds(20)), 0, PHASE_FIGHT);
+ events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT);
+ events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT);
+
+ _safetyDance = true;
- safetyDance = true;
+ // figure out the current GUIDs of our eruption tiles and which segment they belong in
+ std::unordered_multimap<uint32, GameObject*> const& mapGOs = me->GetMap()->GetGameObjectBySpawnIdStore();
+ uint32 spawnId = firstEruptionDBGUID;
+ for (uint8 section = 0; section < numSections; ++section)
+ {
+ _eruptTiles[section].clear();
+ for (uint8 i = 0; i < numEruptions[section]; ++i)
+ {
+ std::pair<std::unordered_multimap<uint32, GameObject*>::const_iterator, std::unordered_multimap<uint32, GameObject*>::const_iterator> tileIt = mapGOs.equal_range(spawnId++);
+ for (std::unordered_multimap<uint32, GameObject*>::const_iterator it = tileIt.first; it != tileIt.second; ++it)
+ _eruptTiles[section].push_back(it->second->GetGUID());
+ }
+ }
}
void UpdateAI(uint32 diff) override
@@ -126,52 +151,56 @@ public:
{
case EVENT_DISRUPT:
DoCastAOE(SPELL_SPELL_DISRUPTION);
- events.ScheduleEvent(EVENT_DISRUPT, 11 * IN_MILLISECONDS);
+ events.Repeat(Seconds(11));
break;
case EVENT_FEVER:
DoCastAOE(SPELL_DECREPIT_FEVER);
- events.ScheduleEvent(EVENT_FEVER, urand(20 * IN_MILLISECONDS, 25 * IN_MILLISECONDS));
+ events.Repeat(randtime(Seconds(20), Seconds(25)));
break;
case EVENT_DANCE:
events.SetPhase(PHASE_DANCE);
Talk(SAY_TAUNT);
Talk(EMOTE_DANCE);
- eruptSection = 3;
+ _safeSection = 0;
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->StopMoving();
DoCast(SPELL_TELEPORT_SELF);
DoCastAOE(SPELL_PLAGUE_CLOUD);
- events.ScheduleEvent(EVENT_DANCE_END, 45 * IN_MILLISECONDS, 0, PHASE_DANCE);
- events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_DANCE_END, Seconds(45), 0, PHASE_DANCE);
+ events.ScheduleEvent(EVENT_ERUPT, Seconds(10));
break;
case EVENT_DANCE_END:
events.SetPhase(PHASE_FIGHT);
Talk(EMOTE_DANCE_END);
- eruptSection = 3;
- events.ScheduleEvent(EVENT_DISRUPT, urand(10, 25) * IN_MILLISECONDS, 0, PHASE_FIGHT);
- events.ScheduleEvent(EVENT_FEVER, urand(15, 20) * IN_MILLISECONDS, 0, PHASE_FIGHT);
- events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT);
- events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT);
+ _safeSection = 0;
+ events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(10), Seconds(25)), 0, PHASE_FIGHT);
+ events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT);
+ events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT);
+ events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT);
me->CastStop();
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
break;
case EVENT_ERUPT:
- instance->SetData(DATA_HEIGAN_ERUPT, eruptSection);
TeleportCheaters();
-
- if (eruptSection == 0)
- eruptDirection = true;
- else if (eruptSection == 3)
- eruptDirection = false;
-
- eruptDirection ? ++eruptSection : --eruptSection;
-
- if (events.IsInPhase(PHASE_DANCE))
- events.ScheduleEvent(EVENT_ERUPT, 3 * IN_MILLISECONDS, 0, PHASE_DANCE);
- else
- events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS, 0, PHASE_FIGHT);
+ for (uint8 section = 0; section < numSections; ++section)
+ if (section != _safeSection)
+ for (ObjectGuid tileGUID : _eruptTiles[section])
+ if (GameObject* tile = ObjectAccessor::GetGameObject(*me, tileGUID))
+ {
+ tile->SendCustomAnim(0);
+ tile->CastSpell(nullptr, SPELL_ERUPTION);
+ }
+
+ if (_safeSection == 0)
+ _danceDirection = true;
+ else if (_safeSection == numSections-1)
+ _danceDirection = false;
+
+ _danceDirection ? ++_safeSection : --_safeSection;
+
+ events.Repeat(events.IsInPhase(PHASE_DANCE) ? Seconds(3) : Seconds(10));
break;
}
}
@@ -180,10 +209,11 @@ public:
}
private:
- uint32 eruptSection;
- bool eruptDirection;
+ std::vector<ObjectGuid> _eruptTiles[numSections]; // populated on encounter start
- bool safetyDance; // is achievement still possible? (= no player deaths yet)
+ uint32 _safeSection; // 0 is next to the entrance
+ bool _danceDirection; // true is counter-clockwise, false is clock-wise
+ bool _safetyDance; // is achievement still possible? (= no player deaths yet)
};
};
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp
index 086d21120e8..c6c9c76bed4 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp
@@ -24,7 +24,6 @@
enum Spells
{
SPELL_NECROTIC_AURA = 55593,
- SPELL_WARN_NECROTIC_AURA = 59481,
SPELL_SUMMON_SPORE = 29234,
SPELL_DEATHBLOOM = 29865,
SPELL_INEVITABLE_DOOM = 29204,
@@ -42,11 +41,12 @@ enum Texts
enum Events
{
- EVENT_NECROTIC_AURA = 1,
- EVENT_DEATHBLOOM = 2,
- EVENT_INEVITABLE_DOOM = 3,
- EVENT_SPORE = 4,
- EVENT_NECROTIC_AURA_FADING = 5,
+ EVENT_NECROTIC_AURA = 1,
+ EVENT_DEATHBLOOM,
+ EVENT_INEVITABLE_DOOM,
+ EVENT_SPORE,
+ EVENT_NECROTIC_AURA_FADING,
+ EVENT_NECROTIC_AURA_FADED
};
enum Achievement
@@ -82,10 +82,10 @@ class boss_loatheb : public CreatureScript
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.ScheduleEvent(EVENT_NECROTIC_AURA, 17 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_DEATHBLOOM, 5 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_SPORE, RAID_MODE(36,18) * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2 * MINUTE * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_NECROTIC_AURA, Seconds(17));
+ events.ScheduleEvent(EVENT_DEATHBLOOM, Seconds(5));
+ events.ScheduleEvent(EVENT_SPORE, Seconds(18));
+ events.ScheduleEvent(EVENT_INEVITABLE_DOOM, Minutes(2));
}
void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
@@ -94,13 +94,6 @@ class boss_loatheb : public CreatureScript
summon->CastSpell(summon,SPELL_FUNGAL_CREEP,true);
}
- void SummonedCreatureDespawn(Creature* summon) override
- {
- summons.Despawn(summon);
- if (summon->IsAlive())
- summon->CastSpell(summon,SPELL_FUNGAL_CREEP,true);
- }
-
uint32 GetData(uint32 id) const override
{
return (_sporeLoserData && id == DATA_ACHIEVEMENT_SPORE_LOSER) ? 1u : 0u;
@@ -119,34 +112,33 @@ class boss_loatheb : public CreatureScript
{
case EVENT_NECROTIC_AURA:
DoCastAOE(SPELL_NECROTIC_AURA);
- DoCast(me, SPELL_WARN_NECROTIC_AURA);
- events.ScheduleEvent(EVENT_NECROTIC_AURA, 20 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14 * IN_MILLISECONDS);
+ Talk(SAY_NECROTIC_AURA_APPLIED);
+ events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, Seconds(14));
+ events.ScheduleEvent(EVENT_NECROTIC_AURA_FADED, Seconds(17));
+ events.Repeat(Seconds(20));
break;
case EVENT_DEATHBLOOM:
DoCastAOE(SPELL_DEATHBLOOM);
- events.ScheduleEvent(EVENT_DEATHBLOOM, 30 * IN_MILLISECONDS);
+ events.Repeat(Seconds(30));
break;
case EVENT_INEVITABLE_DOOM:
_doomCounter++;
DoCastAOE(SPELL_INEVITABLE_DOOM);
if (_doomCounter > 6)
- {
- if (_doomCounter & 1) // odd
- events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 14 * IN_MILLISECONDS);
- else
- events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 17 * IN_MILLISECONDS);
- }
+ events.Repeat((_doomCounter & 1) ? Seconds(14) : Seconds(17));
else
- events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 30 * IN_MILLISECONDS);
+ events.Repeat(30);
break;
case EVENT_SPORE:
DoCast(me, SPELL_SUMMON_SPORE, false);
- events.ScheduleEvent(EVENT_SPORE, RAID_MODE(36,18) * IN_MILLISECONDS);
+ events.Repeat(RAID_MODE(Seconds(36), Seconds(15)));
break;
case EVENT_NECROTIC_AURA_FADING:
Talk(SAY_NECROTIC_AURA_FADING);
break;
+ case EVENT_NECROTIC_AURA_FADED:
+ Talk(SAY_NECROTIC_AURA_REMOVED);
+ break;
default:
break;
}
@@ -177,49 +169,6 @@ class achievement_spore_loser : public AchievementCriteriaScript
}
};
-typedef boss_loatheb::boss_loathebAI LoathebAI;
-
-class spell_loatheb_necrotic_aura_warning : public SpellScriptLoader
-{
- public:
- spell_loatheb_necrotic_aura_warning() : SpellScriptLoader("spell_loatheb_necrotic_aura_warning") { }
-
- class spell_loatheb_necrotic_aura_warning_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_loatheb_necrotic_aura_warning_AuraScript);
-
- bool Validate(SpellInfo const* /*spell*/) override
- {
- if (!sSpellStore.LookupEntry(SPELL_WARN_NECROTIC_AURA))
- return false;
- return true;
- }
-
- void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- if (GetTarget()->IsAIEnabled)
- ENSURE_AI(LoathebAI, GetTarget()->GetAI())->Talk(SAY_NECROTIC_AURA_APPLIED);
- }
-
- void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- if (GetTarget()->IsAIEnabled)
- ENSURE_AI(LoathebAI, GetTarget()->GetAI())->Talk(SAY_NECROTIC_AURA_REMOVED);
- }
-
- void Register() override
- {
- AfterEffectApply += AuraEffectApplyFn(spell_loatheb_necrotic_aura_warning_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
- AfterEffectRemove += AuraEffectRemoveFn(spell_loatheb_necrotic_aura_warning_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
- }
- };
-
- AuraScript* GetAuraScript() const override
- {
- return new spell_loatheb_necrotic_aura_warning_AuraScript();
- }
-};
-
class spell_loatheb_deathbloom : public SpellScriptLoader
{
public:
@@ -260,6 +209,5 @@ void AddSC_boss_loatheb()
{
new boss_loatheb();
new achievement_spore_loser();
- new spell_loatheb_necrotic_aura_warning();
new spell_loatheb_deathbloom();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
index 9d8f1365afb..9502193884a 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
@@ -73,6 +73,8 @@ struct WebTargetSelector : public std::unary_function<Unit*, bool>
WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {}
bool operator()(Unit const* target) const
{
+ if (target->GetTypeId() != TYPEID_PLAYER) // never web nonplayers (pets, guardians, etc.)
+ return false;
if (_maexxna->GetVictim() == target) // never target tank
return false;
if (target->HasAura(SPELL_WEB_WRAP)) // never target targets that are already webbed
@@ -101,11 +103,11 @@ public:
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.ScheduleEvent(EVENT_WRAP, 20 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_SPRAY, 40 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_SHOCK, urandms(5, 10));
- events.ScheduleEvent(EVENT_POISON, urandms(10, 15));
- events.ScheduleEvent(EVENT_SUMMON, 30 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_WRAP, Seconds(20));
+ events.ScheduleEvent(EVENT_SPRAY, Seconds(40));
+ events.ScheduleEvent(EVENT_SHOCK, randtime(Seconds(5), Seconds(10)));
+ events.ScheduleEvent(EVENT_POISON, randtime(Seconds(10), Seconds(15)));
+ events.ScheduleEvent(EVENT_SUMMON, Seconds(30));
}
void Reset() override
@@ -153,28 +155,28 @@ public:
}
}
}
- events.ScheduleEvent(EVENT_WRAP, 40000);
+ events.Repeat(Seconds(40));
break;
}
case EVENT_SPRAY:
Talk(EMOTE_WEB_SPRAY);
DoCastAOE(SPELL_WEB_SPRAY);
- events.ScheduleEvent(EVENT_SPRAY, 40000);
+ events.Repeat(Seconds(40));
break;
case EVENT_SHOCK:
DoCastAOE(SPELL_POISON_SHOCK);
- events.ScheduleEvent(EVENT_SHOCK, urandms(10, 20));
+ events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_POISON:
DoCastVictim(SPELL_NECROTIC_POISON);
- events.ScheduleEvent(EVENT_POISON, urandms(10, 20));
+ events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_SUMMON:
Talk(EMOTE_SPIDERS);
uint8 amount = urand(8, 10);
for (uint8 i = 0; i < amount; ++i)
DoSummon(NPC_SPIDERLING, me, 4.0f, 5 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- events.ScheduleEvent(EVENT_SUMMON, 40000);
+ events.Repeat(Seconds(40));
break;
}
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
index 8ee3936dee3..2108b4ce102 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
@@ -131,25 +131,25 @@ public:
Reset();
else
{
- uint8 secondsGround;
+ uint8 timeGround;
switch (balconyCount)
{
case 0:
- secondsGround = 90;
+ timeGround = 90;
break;
case 1:
- secondsGround = 110;
+ timeGround = 110;
break;
case 2:
default:
- secondsGround = 180;
+ timeGround = 180;
}
- events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, 2 * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_BALCONY, secondsGround * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_CURSE, urand(10,25) * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_WARRIOR, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
- events.ScheduleEvent(EVENT_BLINK, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
}
}
@@ -220,7 +220,7 @@ public:
case EVENT_CURSE:
{
DoCastAOE(SPELL_CURSE);
- events.ScheduleEvent(EVENT_CURSE, urand(50, 70) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.Repeat(randtime(Seconds(50), Seconds(70)));
break;
}
case EVENT_WARRIOR:
@@ -229,7 +229,7 @@ public:
CastSummon(RAID_MODE(2, 3), 0, 0);
- events.ScheduleEvent(EVENT_WARRIOR, 40 * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.Repeat(Seconds(40));
break;
case EVENT_BLINK:
DoCastAOE(SPELL_CRIPPLE, true);
@@ -237,7 +237,7 @@ public:
DoResetThreat();
justBlinked = true;
- events.ScheduleEvent(EVENT_BLINK, 40000, 0, PHASE_GROUND);
+ events.Repeat(Seconds(40));
break;
case EVENT_BALCONY:
events.SetPhase(PHASE_BALCONY);
@@ -247,24 +247,24 @@ public:
me->StopMoving();
me->RemoveAllAuras();
- events.ScheduleEvent(EVENT_BALCONY_TELEPORT, 3 * IN_MILLISECONDS, 0, PHASE_BALCONY);
- events.ScheduleEvent(EVENT_WAVE, urand(5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS), 0, PHASE_BALCONY);
+ events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY);
+ events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY);
- uint8 secondsBalcony;
+ uint8 timeBalcony;
switch (balconyCount)
{
case 0:
- secondsBalcony = 70;
+ timeBalcony = 70;
break;
case 1:
- secondsBalcony = 97;
+ timeBalcony = 97;
break;
case 2:
default:
- secondsBalcony = 120;
+ timeBalcony = 120;
break;
}
- events.ScheduleEvent(EVENT_GROUND, secondsBalcony * IN_MILLISECONDS, 0, PHASE_BALCONY);
+ events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY);
break;
case EVENT_BALCONY_TELEPORT:
Talk(EMOTE_TELEPORT_1);
@@ -287,7 +287,7 @@ public:
CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10));
break;
}
- events.ScheduleEvent(EVENT_WAVE, urand(30, 45) * IN_MILLISECONDS, 0, PHASE_BALCONY);
+ events.Repeat(randtime(Seconds(30), Seconds(45)));
break;
case EVENT_GROUND:
++balconyCount;
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp
index b8ce402a939..cfa6e2e9f30 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp
@@ -97,8 +97,8 @@ public:
_EnterCombat();
Enraged = false;
Talk(SAY_AGGRO);
- events.ScheduleEvent(EVENT_HATEFUL, 1 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_HATEFUL, Seconds(1));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(6));
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MAKE_QUICK_WERK_OF_HIM_STARTING_EVENT);
}
@@ -167,17 +167,17 @@ public:
if (thirdThreatTarget)
me->getThreatManager().addThreat(thirdThreatTarget, HATEFUL_THREAT_AMT); // this will only ever be used in 25m
- events.ScheduleEvent(EVENT_HATEFUL, 1 * IN_MILLISECONDS);
+ events.Repeat(Seconds(1));
break;
}
case EVENT_BERSERK:
DoCast(me, SPELL_BERSERK, true);
Talk(EMOTE_BERSERK);
- events.ScheduleEvent(EVENT_SLIME, 2 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SLIME, Seconds(2));
break;
case EVENT_SLIME:
- DoCastVictim(SPELL_SLIME_BOLT, true);
- events.ScheduleEvent(EVENT_SLIME, 2 * IN_MILLISECONDS);
+ DoCastAOE(SPELL_SLIME_BOLT, true);
+ events.Repeat(Seconds(2));
break;
}
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
index 1d12f64a949..548f8086eb8 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
@@ -104,6 +104,9 @@ public:
void JustDied(Unit* /*killer*/) override
{
+ for (ObjectGuid summonGuid : summons)
+ if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid))
+ summon->RemoveCharmAuras();
Talk(SAY_DEATH);
DoCastAOE(SPELL_HOPELESS, true);
@@ -117,10 +120,10 @@ public:
me->StopMoving();
summons.DoZoneInCombat();
Talk(SAY_AGGRO);
- events.ScheduleEvent(EVENT_ATTACK, 7 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_STRIKE, 21 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_KNIFE, 10 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_ATTACK, Seconds(7));
+ events.ScheduleEvent(EVENT_STRIKE, Seconds(21));
+ events.ScheduleEvent(EVENT_SHOUT, Seconds(16));
+ events.ScheduleEvent(EVENT_KNIFE, Seconds(10));
}
void UpdateAI(uint32 diff) override
@@ -141,16 +144,16 @@ public:
break;
case EVENT_STRIKE:
DoCastVictim(SPELL_UNBALANCING_STRIKE);
- events.ScheduleEvent(EVENT_STRIKE, 6 * IN_MILLISECONDS);
+ events.Repeat(Seconds(6));
return;
case EVENT_SHOUT:
DoCastAOE(SPELL_DISRUPTING_SHOUT);
- events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS);
+ events.Repeat(Seconds(16));
return;
case EVENT_KNIFE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f))
DoCast(target, SPELL_JAGGED_KNIFE);
- events.ScheduleEvent(EVENT_KNIFE, urandms(10,15));
+ events.Repeat(randtime(Seconds(10), Seconds(15)));
return;
}
}
@@ -174,7 +177,10 @@ class npc_dk_understudy : public CreatureScript
struct npc_dk_understudyAI : public ScriptedAI
{
- npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0) { }
+ npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0)
+ {
+ creature->LoadEquipment(1);
+ }
void EnterCombat(Unit* /*who*/) override
{
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp
index 68b9e252150..4596b17bc23 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp
@@ -16,7 +16,9 @@
*/
#include "ScriptMgr.h"
+#include "GameObjectAI.h"
#include "ScriptedCreature.h"
+#include "SpellScript.h"
#include "Player.h"
#include "SpellInfo.h"
#include "naxxramas.h"
@@ -31,25 +33,24 @@ enum Yells
enum Spells
{
- SPELL_FROST_AURA = 28531,
- SPELL_CLEAVE = 19983,
- SPELL_TAIL_SWEEP = 55697,
- SPELL_SUMMON_BLIZZARD = 28560,
- SPELL_LIFE_DRAIN = 28542,
- SPELL_ICEBOLT = 28522,
- SPELL_FROST_BREATH = 29318,
- SPELL_FROST_EXPLOSION = 28524,
- SPELL_FROST_MISSILE = 30101,
- SPELL_BERSERK = 26662,
- SPELL_DIES = 29357,
- SPELL_CHILL = 28547,
- SPELL_CHECK_RESISTS = 60539,
+ SPELL_FROST_AURA = 28531,
+ SPELL_CLEAVE = 19983,
+ SPELL_TAIL_SWEEP = 55697,
+ SPELL_SUMMON_BLIZZARD = 28560,
+ SPELL_LIFE_DRAIN = 28542,
+ SPELL_ICEBOLT = 28522,
+ SPELL_FROST_BREATH_ANTICHEAT = 29318, // damage effect ignoring LoS on the entrance platform to prevent cheese
+ SPELL_FROST_BREATH = 28524, // damage effect below sapphiron
+ SPELL_FROST_MISSILE = 30101, // visual only
+ SPELL_BERSERK = 26662,
+ SPELL_DIES = 29357,
+ SPELL_CHECK_RESISTS = 60539,
+ SPELL_SAPPHIRON_WING_BUFFET = 29328
};
enum Phases
{
- PHASE_NULL = 0,
- PHASE_BIRTH,
+ PHASE_BIRTH = 1,
PHASE_GROUND,
PHASE_FLIGHT
};
@@ -72,9 +73,15 @@ enum Events
EVENT_CHECK_RESISTS
};
+enum Actions
+{
+ ACTION_BIRTH = 1
+};
+
enum Misc
{
NPC_BLIZZARD = 16474,
+ NPC_WING_BUFFET = 17025,
GO_ICEBLOCK = 181247,
// The Hundred Club
@@ -92,77 +99,74 @@ class boss_sapphiron : public CreatureScript
struct boss_sapphironAI : public BossAI
{
boss_sapphironAI(Creature* creature) :
- BossAI(creature, BOSS_SAPPHIRON), _iceboltCount(0), _map(me->GetMap())
+ BossAI(creature, BOSS_SAPPHIRON)
{
Initialize();
}
void Initialize()
{
- _phase = PHASE_NULL;
-
+ _delayedDrain = false;
_canTheHundredClub = true;
}
void InitializeAI() override
{
+ if (instance->GetBossState(BOSS_SAPPHIRON) == DONE)
+ return;
+
_canTheHundredClub = true;
- float x, y, z;
- me->GetPosition(x, y, z);
- me->SummonGameObject(GO_BIRTH, x, y, z, 0, 0, 0, 0, 0, 0);
- me->SetVisible(false);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- me->SetReactState(REACT_PASSIVE);
+ if (!instance->GetData(DATA_HAD_SAPPHIRON_BIRTH))
+ {
+ me->SetVisible(false);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ me->SetReactState(REACT_PASSIVE);
+ }
BossAI::InitializeAI();
}
void Reset() override
{
- _Reset();
-
- if (_phase == PHASE_FLIGHT)
+ if (events.IsInPhase(PHASE_FLIGHT))
{
- ClearIceBlock();
+ instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ICEBOLT);
me->SetReactState(REACT_AGGRESSIVE);
if (me->IsHovering())
{
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetHover(false);
}
- me->SetDisableGravity(false);
}
+ _Reset();
Initialize();
}
+ void DamageTaken(Unit* /*who*/, uint32& damage) override
+ {
+ if (damage < me->GetHealth() || !events.IsInPhase(PHASE_FLIGHT))
+ return;
+ damage = me->GetHealth()-1; // don't die during air phase
+ }
+
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
me->CastSpell(me, SPELL_FROST_AURA, true);
- DoCast(me, SPELL_CHECK_RESISTS);
- events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_BERSERK, 15 * MINUTE * IN_MILLISECONDS);
- EnterPhaseGround();
+ events.SetPhase(PHASE_GROUND);
+ events.ScheduleEvent(EVENT_CHECK_RESISTS, Seconds(0));
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(15));
+ EnterPhaseGround(true);
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{
switch(spell->Id)
{
- case SPELL_ICEBOLT:
- {
- IceBlockMap::iterator itr = _iceblocks.find(target->GetGUID());
- if (itr != _iceblocks.end() && !itr->second)
- {
- if (GameObject* iceblock = me->SummonGameObject(GO_ICEBLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, 0, 0, 0, 0, 25))
- itr->second = iceblock->GetGUID();
- }
- break;
- }
case SPELL_CHECK_RESISTS:
if (target && target->GetResistance(SPELL_SCHOOL_FROST) > MAX_FROST_RESISTANCE)
_canTheHundredClub = false;
@@ -179,41 +183,37 @@ class boss_sapphiron : public CreatureScript
void MovementInform(uint32 /*type*/, uint32 id) override
{
if (id == 1)
- events.ScheduleEvent(EVENT_LIFTOFF, 0);
+ events.ScheduleEvent(EVENT_LIFTOFF, Seconds(0), 0, PHASE_FLIGHT);
}
void DoAction(int32 param) override
{
- if (param == DATA_SAPPHIRON_BIRTH)
+ if (param == ACTION_BIRTH)
{
- _phase = PHASE_BIRTH;
- events.ScheduleEvent(EVENT_BIRTH, 23 * IN_MILLISECONDS);
+ events.SetPhase(PHASE_BIRTH);
+ events.ScheduleEvent(EVENT_BIRTH, Seconds(23));
}
}
- void EnterPhaseGround()
+ void EnterPhaseGround(bool initial)
{
- _phase = PHASE_GROUND;
me->SetReactState(REACT_AGGRESSIVE);
- events.SetPhase(PHASE_GROUND);
- events.ScheduleEvent(EVENT_CLEAVE, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_TAIL, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_DRAIN, 24 * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_BLIZZARD, urand(5, 10) * IN_MILLISECONDS, 0, PHASE_GROUND);
- events.ScheduleEvent(EVENT_FLIGHT, 45 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(5), Seconds(15)), 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_TAIL, randtime(Seconds(7), Seconds(10)), 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_BLIZZARD, randtime(Seconds(5), Seconds(10)), 0, PHASE_GROUND);
+ if (initial)
+ {
+ events.ScheduleEvent(EVENT_DRAIN, randtime(Seconds(22), Seconds(28)));
+ events.ScheduleEvent(EVENT_FLIGHT, Seconds(48) + Milliseconds(500), 0, PHASE_GROUND);
+ }
+ else
+ events.ScheduleEvent(EVENT_FLIGHT, Minutes(1), 0, PHASE_GROUND);
}
- void ClearIceBlock()
+ inline void CastDrain()
{
- for (IceBlockMap::const_iterator itr = _iceblocks.begin(); itr != _iceblocks.end(); ++itr)
- {
- if (Player* player = ObjectAccessor::GetPlayer(*me, itr->first))
- player->RemoveAura(SPELL_ICEBOLT);
-
- if (GameObject* go = ObjectAccessor::GetGameObject(*me, itr->second))
- go->Delete();
- }
- _iceblocks.clear();
+ DoCastAOE(SPELL_LIFE_DRAIN);
+ events.ScheduleEvent(EVENT_DRAIN, randtime(Seconds(22), Seconds(28)));
}
uint32 GetData(uint32 data) const override
@@ -226,15 +226,12 @@ class boss_sapphiron : public CreatureScript
void UpdateAI(uint32 diff) override
{
- if (!_phase)
- return;
-
events.Update(diff);
- if (_phase != PHASE_BIRTH && !UpdateVictim())
+ if (!events.IsInPhase(PHASE_BIRTH) && !UpdateVictim())
return;
- if (_phase == PHASE_GROUND)
+ if (events.IsInPhase(PHASE_GROUND))
{
while (uint32 eventId = events.ExecuteEvent())
{
@@ -242,7 +239,10 @@ class boss_sapphiron : public CreatureScript
{
case EVENT_CHECK_RESISTS:
DoCast(me, SPELL_CHECK_RESISTS);
- events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS);
+ events.Repeat(Seconds(30));
+ return;
+ case EVENT_GROUND:
+ EnterPhaseGround(false);
return;
case EVENT_BERSERK:
Talk(EMOTE_ENRAGE);
@@ -250,27 +250,26 @@ class boss_sapphiron : public CreatureScript
return;
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE);
- events.ScheduleEvent(EVENT_CLEAVE, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(5), Seconds(15)), 0, PHASE_GROUND);
return;
case EVENT_TAIL:
DoCastAOE(SPELL_TAIL_SWEEP);
- events.ScheduleEvent(EVENT_TAIL, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_TAIL, randtime(Seconds(7), Seconds(10)), 0, PHASE_GROUND);
return;
case EVENT_DRAIN:
- DoCastAOE(SPELL_LIFE_DRAIN);
- events.ScheduleEvent(EVENT_DRAIN, 24 * IN_MILLISECONDS, 0, PHASE_GROUND);
+ if (events.IsInPhase(PHASE_FLIGHT))
+ _delayedDrain = true;
+ else
+ CastDrain();
return;
case EVENT_BLIZZARD:
- {
- if (Creature* summon = DoSummon(NPC_BLIZZARD, me, 0.0f, urand(25, 30) * IN_MILLISECONDS, TEMPSUMMON_TIMED_DESPAWN))
- summon->GetMotionMaster()->MoveRandom(40);
- events.ScheduleEvent(EVENT_BLIZZARD, RAID_MODE(20, 7) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ DoCastAOE(SPELL_SUMMON_BLIZZARD);
+ events.ScheduleEvent(EVENT_BLIZZARD, RAID_MODE(Seconds(20), Seconds(7)), 0, PHASE_GROUND);
break;
- }
case EVENT_FLIGHT:
if (HealthAbovePct(10))
{
- _phase = PHASE_FLIGHT;
+ _delayedDrain = false;
events.SetPhase(PHASE_FLIGHT);
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
@@ -293,61 +292,69 @@ class boss_sapphiron : public CreatureScript
{
case EVENT_CHECK_RESISTS:
DoCast(me, SPELL_CHECK_RESISTS);
- events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS);
+ events.Repeat(Seconds(30));
return;
case EVENT_LIFTOFF:
+ {
Talk(EMOTE_AIR_PHASE);
- me->SetDisableGravity(true);
+ if (Creature* buffet = DoSummon(NPC_WING_BUFFET, me, 0.0f, 0, TEMPSUMMON_MANUAL_DESPAWN))
+ _buffet = buffet->GetGUID();
+ me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetHover(true);
- events.ScheduleEvent(EVENT_ICEBOLT, 1500);
- _iceboltCount = RAID_MODE(2, 3);
+ events.ScheduleEvent(EVENT_ICEBOLT, Seconds(7), 0, PHASE_FLIGHT);
+
+ _iceboltTargets.clear();
+ std::list<Unit*> targets;
+ SelectTargetList(targets, RAID_MODE(2, 3), SELECT_TARGET_RANDOM, 200.0f, true);
+ for (Unit* target : targets)
+ if (target)
+ _iceboltTargets.push_back(target->GetGUID());
return;
+ }
case EVENT_ICEBOLT:
{
- std::vector<Unit*> targets;
- std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin();
- for (; i != me->getThreatManager().getThreatList().end(); ++i)
- if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER && !(*i)->getTarget()->HasAura(SPELL_ICEBOLT))
- targets.push_back((*i)->getTarget());
-
- if (targets.empty())
- _iceboltCount = 0;
- else
+ if (_iceboltTargets.empty())
{
- std::vector<Unit*>::const_iterator itr = targets.begin();
- advance(itr, rand32() % targets.size());
- _iceblocks.insert(std::make_pair((*itr)->GetGUID(), ObjectGuid::Empty));
- DoCast(*itr, SPELL_ICEBOLT);
- --_iceboltCount;
+ events.ScheduleEvent(EVENT_BREATH, Seconds(2), 0, PHASE_FLIGHT);
+ return;
}
-
- if (_iceboltCount)
- events.ScheduleEvent(EVENT_ICEBOLT, 1 * IN_MILLISECONDS);
+ ObjectGuid target = _iceboltTargets.back();
+ if (Player* pTarget = ObjectAccessor::GetPlayer(*me, target))
+ if (pTarget->IsAlive())
+ DoCast(pTarget, SPELL_ICEBOLT);
+ _iceboltTargets.pop_back();
+
+ if (_iceboltTargets.empty())
+ events.ScheduleEvent(EVENT_BREATH, Seconds(2), 0, PHASE_FLIGHT);
else
- events.ScheduleEvent(EVENT_BREATH, 1 * IN_MILLISECONDS);
+ events.Repeat(Seconds(3));
return;
}
case EVENT_BREATH:
{
Talk(EMOTE_BREATH);
DoCastAOE(SPELL_FROST_MISSILE);
- events.ScheduleEvent(EVENT_EXPLOSION, 8 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_EXPLOSION, Seconds(8), 0, PHASE_FLIGHT);
return;
}
case EVENT_EXPLOSION:
- CastExplosion();
- ClearIceBlock();
- events.ScheduleEvent(EVENT_LAND, 3 * IN_MILLISECONDS);
+ DoCastAOE(SPELL_FROST_BREATH);
+ DoCastAOE(SPELL_FROST_BREATH_ANTICHEAT);
+ events.ScheduleEvent(EVENT_LAND, Seconds(3) + Milliseconds(500), 0, PHASE_FLIGHT);
return;
case EVENT_LAND:
+ if (_delayedDrain)
+ CastDrain();
+ if (Creature* cBuffet = ObjectAccessor::GetCreature(*me, _buffet))
+ {
+ cBuffet->DespawnOrUnsummon(1 * IN_MILLISECONDS);
+ _buffet.Clear();
+ }
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
Talk(EMOTE_GROUND_PHASE);
me->SetHover(false);
- me->SetDisableGravity(false);
- events.ScheduleEvent(EVENT_GROUND, 1500);
- return;
- case EVENT_GROUND:
- EnterPhaseGround();
+ events.SetPhase(PHASE_GROUND);
+ events.ScheduleEvent(EVENT_GROUND, Seconds(3) + Milliseconds(500), 0, PHASE_GROUND);
return;
case EVENT_BIRTH:
me->SetVisible(true);
@@ -359,56 +366,261 @@ class boss_sapphiron : public CreatureScript
}
}
- void CastExplosion()
+ private:
+ std::vector<ObjectGuid> _iceboltTargets;
+ ObjectGuid _buffet;
+ bool _delayedDrain;
+ bool _canTheHundredClub;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new boss_sapphironAI(creature);
+ }
+};
+
+class go_sapphiron_birth : public GameObjectScript
+{
+ public:
+ go_sapphiron_birth() : GameObjectScript("go_sapphiron_birth") { }
+
+ struct go_sapphiron_birthAI : public GameObjectAI
+ {
+ go_sapphiron_birthAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { }
+
+ void OnStateChanged(uint32 state, Unit* who) override
+ {
+ if (state == GO_ACTIVATED)
+ {
+ if (who)
+ {
+ if (Creature* sapphiron = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_SAPPHIRON)))
+ sapphiron->AI()->DoAction(ACTION_BIRTH);
+ instance->SetData(DATA_HAD_SAPPHIRON_BIRTH, 1u);
+ }
+ }
+ else if (state == GO_JUST_DEACTIVATED)
+ { // prevent ourselves from going back to _READY and resetting the client anim
+ go->SetRespawnTime(0);
+ go->Delete();
+ }
+ }
+
+ InstanceScript* instance;
+ };
+
+ GameObjectAI* GetAI(GameObject* go) const override
+ {
+ return GetInstanceAI<go_sapphiron_birthAI>(go);
+ }
+};
+
+
+class spell_sapphiron_change_blizzard_target : public SpellScriptLoader
+{
+ public:
+ spell_sapphiron_change_blizzard_target() : SpellScriptLoader("spell_sapphiron_change_blizzard_target") { }
+
+ class spell_sapphiron_change_blizzard_target_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_sapphiron_change_blizzard_target_AuraScript);
+
+ void HandlePeriodic(AuraEffect const* /*eff*/)
+ {
+ TempSummon* me = GetTarget()->ToTempSummon();
+ if (Creature* owner = me ? me->GetSummonerCreatureBase() : nullptr)
+ {
+ Unit* newTarget = owner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true);
+ if (!newTarget)
+ newTarget = owner->getAttackerForHelper();
+ if (newTarget)
+ me->GetMotionMaster()->MoveFollow(newTarget, 0.1f, 0.0f);
+ else
+ {
+ me->StopMoving();
+ me->GetMotionMaster()->Clear();
+ }
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_sapphiron_change_blizzard_target_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_sapphiron_change_blizzard_target_AuraScript();
+ }
+};
+
+class spell_sapphiron_icebolt : public SpellScriptLoader
+{
+ public:
+ spell_sapphiron_icebolt() : SpellScriptLoader("spell_sapphiron_icebolt") { }
+
+ class spell_sapphiron_icebolt_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_sapphiron_icebolt_AuraScript);
+
+ void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/)
+ {
+ GetTarget()->ApplySpellImmune(SPELL_ICEBOLT, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, true);
+ }
+
+ void HandleRemove(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (_block)
+ if (GameObject* oBlock = ObjectAccessor::GetGameObject(*GetTarget(), _block))
+ oBlock->Delete();
+ GetTarget()->ApplySpellImmune(SPELL_ICEBOLT, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, false);
+ }
+
+ void HandlePeriodic(AuraEffect const* /*eff*/)
+ {
+ if (_block)
+ return;
+ if (GetTarget()->isMoving())
+ 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))
+ _block = block->GetGUID();
+ }
+
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_sapphiron_icebolt_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_sapphiron_icebolt_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_sapphiron_icebolt_AuraScript::HandlePeriodic, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+
+ ObjectGuid _block;
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_sapphiron_icebolt_AuraScript();
+ }
+};
+
+// @hack Hello, developer from the future! How has your day been?
+// Anyway, this is, as you can undoubtedly see, a hack to emulate line of sight checks on a spell that abides line of sight anyway.
+// In the current core, line of sight is not properly checked for people standing behind an ice block. This is not a good thing and kills people.
+// Thus, we have this hack to check for ice block LoS in a "safe" way. Kind of. It's inaccurate, but in a good way (tends to save people when it shouldn't in edge cases).
+// If LoS handling is better in whatever the current revision is when you read this, please get rid of the hack. Thanks!
+class spell_sapphiron_frost_breath : public SpellScriptLoader
+{
+ public:
+ spell_sapphiron_frost_breath() : SpellScriptLoader("spell_sapphiron_frost_breath") { }
+
+ class spell_sapphiron_frost_breath_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_sapphiron_frost_breath_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return !!sSpellMgr->GetSpellInfo(SPELL_FROST_BREATH);
+ }
+
+ void HandleTargets(std::list<WorldObject*>& targetList)
{
- DoZoneInCombat(); // make sure everyone is in threatlist
- std::vector<Unit*> targets;
- std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin();
- for (; i != me->getThreatManager().getThreatList().end(); ++i)
+ std::list<GameObject*> blocks;
+ if (GetCaster())
+ GetCaster()->GetGameObjectListWithEntryInGrid(blocks, GO_ICEBLOCK, 200.0f);
+
+ std::vector<Unit*> toRemove;
+ toRemove.reserve(3);
+ std::list<WorldObject*>::iterator it = targetList.begin();
+ while (it != targetList.end())
{
- Unit* target = (*i)->getTarget();
- if (target->GetTypeId() != TYPEID_PLAYER)
+ Unit* target = (*it)->ToUnit();
+ if (!target)
+ {
+ it = targetList.erase(it);
continue;
+ }
if (target->HasAura(SPELL_ICEBOLT))
{
- target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true);
- targets.push_back(target);
+ it = targetList.erase(it);
+ toRemove.push_back(target);
+ continue;
+ }
+
+ bool found = false;
+ for (GameObject* block : blocks)
+ if (block->IsInBetween(GetCaster(), target, 2.0f) && GetCaster()->GetExactDist2d(block) + 5 >= GetCaster()->GetExactDist2d(target))
+ {
+ found = true;
+ break;
+ }
+ if (found)
+ {
+ it = targetList.erase(it);
continue;
}
+ ++it;
+ }
+
+ for (Unit* block : toRemove)
+ block->RemoveAura(SPELL_ICEBOLT);
+ }
+
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_breath_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
+ }
+ };
- for (IceBlockMap::const_iterator itr = _iceblocks.begin(); itr != _iceblocks.end(); ++itr)
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_sapphiron_frost_breath_SpellScript();
+ }
+};
+
+class spell_sapphiron_summon_blizzard : public SpellScriptLoader
+{
+ public:
+ spell_sapphiron_summon_blizzard() : SpellScriptLoader("spell_sapphiron_summon_blizzard") { }
+
+ class spell_sapphiron_summon_blizzard_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_sapphiron_summon_blizzard_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return !!sSpellMgr->GetSpellInfo(SPELL_SUMMON_BLIZZARD);
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ if (Creature* blizzard = GetCaster()->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, urandms(25, 30)))
{
- if (GameObject* go = ObjectAccessor::GetGameObject(*me, itr->second))
+ blizzard->CastSpell(nullptr, blizzard->m_spells[0], TRIGGERED_NONE);
+ if (Creature* creatureCaster = GetCaster()->ToCreature())
{
- if (go->IsInBetween(me, target, 2.0f)
- && me->GetExactDist2d(target->GetPositionX(), target->GetPositionY()) - me->GetExactDist2d(go->GetPositionX(), go->GetPositionY()) < 5.0f)
+ if (Unit* newTarget = creatureCaster->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true))
{
- target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true);
- targets.push_back(target);
- break;
+ blizzard->GetMotionMaster()->MoveFollow(newTarget, 0.1f, 0.0f);
+ return;
}
}
+ blizzard->GetMotionMaster()->MoveFollow(target, 0.1f, 0.0f);
}
- }
-
- me->CastSpell(me, SPELL_FROST_EXPLOSION, true);
-
- for (std::vector<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
- (*itr)->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, false);
}
- private:
- Phases _phase;
- uint32 _iceboltCount;
- IceBlockMap _iceblocks;
- bool _canTheHundredClub;
- Map* _map;
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_sapphiron_summon_blizzard_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
};
- CreatureAI* GetAI(Creature* creature) const override
+ SpellScript* GetSpellScript() const override
{
- return new boss_sapphironAI(creature);
+ return new spell_sapphiron_summon_blizzard_SpellScript();
}
};
@@ -426,5 +638,10 @@ class achievement_the_hundred_club : public AchievementCriteriaScript
void AddSC_boss_sapphiron()
{
new boss_sapphiron();
+ new go_sapphiron_birth();
+ new spell_sapphiron_change_blizzard_target();
+ new spell_sapphiron_icebolt();
+ new spell_sapphiron_frost_breath();
+ new spell_sapphiron_summon_blizzard();
new achievement_the_hundred_club();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp
index 66a00b2b634..2cf3a4664dd 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp
@@ -109,6 +109,10 @@ enum PetSpells
SPELL_MAGNETIC_PULL = 54517,
SPELL_MAGNETIC_PULL_EFFECT = 28337,
+ // @hack feugen/stalagg use this in P1 after gripping tanks to prevent mmaps from pathing them off the platform once they approach the ramp
+ // developer from the future, if you read this and mmaps in the room has been fixed, then get rid of the hackfix, please
+ SPELL_ROOT_SELF = 75215,
+
SPELL_TESLA_SHOCK = 28099
};
@@ -166,7 +170,9 @@ public:
struct boss_thaddiusAI : public BossAI
{
public:
- boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningEnabled(false), shockingEligibility(true)
+ boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {}
+
+ void InitializeAI() override
{
if (instance->GetBossState(BOSS_THADDIUS) != DONE)
{
@@ -184,12 +190,33 @@ public:
Talk(SAY_SLAY);
}
- void Reset() override
+ void Reset() override { }
+
+ void EnterEvadeMode(EvadeReason why) override
{
+ if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES)
+ {
+ ballLightningEnabled = true;
+ return; // try again
+ }
if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive()))
BeginResetEncounter();
}
+ bool CanAIAttack(Unit const* who) const override
+ {
+ if (ballLightningEnabled || me->IsWithinMeleeRange(who))
+ return BossAI::CanAIAttack(who);
+ else
+ return false;
+ }
+
+ void JustRespawned() override
+ {
+ if (events.IsInPhase(PHASE_RESETTING))
+ ResetEncounter();
+ }
+
void JustDied(Unit* /*killer*/) override
{
_JustDied();
@@ -217,7 +244,10 @@ public:
case ACTION_FEUGEN_AGGRO:
case ACTION_STALAGG_AGGRO:
if (events.IsInPhase(PHASE_RESETTING))
- return BeginResetEncounter();
+ {
+ BeginResetEncounter();
+ return;
+ }
if (!events.IsInPhase(PHASE_NOT_ENGAGED))
return;
events.SetPhase(PHASE_PETS);
@@ -225,10 +255,14 @@ public:
shockingEligibility = true;
if (!instance->CheckRequiredBosses(BOSS_THADDIUS))
- return BeginResetEncounter();
+ {
+ BeginResetEncounter();
+ return;
+ }
instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS);
me->setActive(true);
+ DoZoneInCombat();
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->setActive(true);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
@@ -239,7 +273,7 @@ public:
feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX);
feugenAlive = false;
if (stalaggAlive)
- events.ScheduleEvent(EVENT_REVIVE_FEUGEN, 5 * IN_MILLISECONDS, 0, PHASE_PETS);
+ events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS);
else
Transition();
@@ -249,7 +283,7 @@ public:
stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX);
stalaggAlive = false;
if (feugenAlive)
- events.ScheduleEvent(EVENT_REVIVE_STALAGG, 5 * IN_MILLISECONDS, 0, PHASE_PETS);
+ events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS);
else
Transition();
@@ -274,9 +308,9 @@ public:
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_TRANSITION_1, 10 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
- events.ScheduleEvent(EVENT_TRANSITION_2, 12 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
- events.ScheduleEvent(EVENT_TRANSITION_3, 14 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION);
}
void BeginResetEncounter(bool initial = false)
@@ -286,16 +320,12 @@ public:
if (events.IsInPhase(PHASE_RESETTING))
return;
- if (initial) // signal shorter spawn timer to instance script
- instance->SetBossState(BOSS_THADDIUS, SPECIAL);
- instance->ProcessEvent(me, EVENT_THADDIUS_BEGIN_RESET);
- instance->SetBossState(BOSS_THADDIUS, NOT_STARTED);
-
// remove polarity shift debuffs on reset
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY);
me->DespawnOrUnsummon();
+ me->SetRespawnTime(initial ? 5 : 30);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
events.SetPhase(PHASE_RESETTING);
@@ -312,11 +342,9 @@ public:
feugenAlive = true;
stalaggAlive = true;
- me->Respawn(true);
_Reset();
events.SetPhase(PHASE_NOT_ENGAGED);
-
- me->CastSpell(me, SPELL_THADDIUS_INACTIVE_VISUAL, true);
+ me->SetReactState(REACT_PASSIVE);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER);
@@ -361,16 +389,20 @@ public:
break;
case EVENT_TRANSITION_3: // thaddius becomes active
me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
- ballLightningEnabled = false;
+ ballLightningUnlocked = false;
me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f))
AttackStart(closest);
else // if there is no nearest target, then there is no target, meaning we should reset
- return BeginResetEncounter();
+ {
+ BeginResetEncounter();
+ return;
+ }
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION_3);
@@ -381,20 +413,20 @@ public:
Talk(SAY_AGGRO);
- events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, 5 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
- events.ScheduleEvent(EVENT_SHIFT, 10 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
- events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, 0, PHASE_THADDIUS);
- events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS);
break;
case EVENT_ENABLE_BALL_LIGHTNING:
- ballLightningEnabled = true;
+ ballLightningUnlocked = true;
break;
case EVENT_SHIFT:
me->CastStop(); // shift overrides all other spells
DoCastAOE(SPELL_POLARITY_SHIFT);
- events.ScheduleEvent(EVENT_SHIFT_TALK, 3 * IN_MILLISECONDS, PHASE_THADDIUS);
- events.ScheduleEvent(EVENT_SHIFT, 30 * IN_MILLISECONDS, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS);
break;
case EVENT_SHIFT_TALK:
Talk(SAY_ELECT);
@@ -402,12 +434,12 @@ public:
break;
case EVENT_CHAIN:
if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over
- events.ScheduleEvent(EVENT_CHAIN, 3 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+ events.Repeat(Seconds(3));
else
{
me->CastStop();
DoCastVictim(SPELL_CHAIN_LIGHTNING);
- events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, PHASE_THADDIUS);
+ events.Repeat(randtime(Seconds(10), Seconds(20)));
}
break;
case EVENT_BERSERK:
@@ -418,22 +450,24 @@ public:
break;
}
}
-
- if (events.IsInPhase(PHASE_THADDIUS))
+ if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady())
{
if (me->IsWithinMeleeRange(me->GetVictim()))
+ {
+ ballLightningEnabled = false;
DoMeleeAttackIfReady();
- else
- if (ballLightningEnabled)
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
- DoCast(target, SPELL_BALL_LIGHTNING);
+ }
+ else if (ballLightningUnlocked)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
+ DoCast(target, SPELL_BALL_LIGHTNING);
}
}
private:
bool stalaggAlive;
bool feugenAlive;
- bool ballLightningEnabled;
+ bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice
+ bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range
bool shockingEligibility;
};
@@ -652,7 +686,7 @@ public:
else
{
DoCast(me, SPELL_STALAGG_POWERSURGE);
- powerSurgeTimer = urand(25, 30) * IN_MILLISECONDS;
+ powerSurgeTimer = urandms(25, 30);
}
}
else
@@ -836,7 +870,7 @@ public:
Talk(SAY_FEUGEN_AGGRO);
if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
- thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO);
+ thaddius->AI()->DoAction(ACTION_FEUGEN_AGGRO);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
if (!stalagg->IsInCombat())
@@ -1094,7 +1128,7 @@ class spell_thaddius_polarity_charge : public SpellScriptLoader
SpellScript* GetSpellScript() const override
{
- return new spell_thaddius_polarity_charge_SpellScript;
+ return new spell_thaddius_polarity_charge_SpellScript();
}
};
@@ -1212,6 +1246,10 @@ class spell_thaddius_magnetic_pull : public SpellScriptLoader
feugenTank->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true);
stalaggTank->CastSpell(feugenTank, SPELL_MAGNETIC_PULL_EFFECT, true);
+ // @hack prevent mmaps clusterfucks from breaking tesla while tanks are midair
+ feugen->AddAura(SPELL_ROOT_SELF, feugen);
+ stalagg->AddAura(SPELL_ROOT_SELF, stalagg);
+
// and make both attack their respective new tanks
if (feugen->GetAI())
feugen->GetAI()->AttackStart(stalaggTank);
diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
index 77657d71ffc..e3971248513 100644
--- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
@@ -100,36 +100,6 @@ ObjectData const objectData[] =
{ 0, 0, }
};
-// from P2 teleport spell stored target
-float const HeiganPos[2] = { 2793.86f, -3707.38f };
-float const HeiganEruptionSlope[3] =
-{
- (-3703.303223f - HeiganPos[1]) / (2777.494141f - HeiganPos[0]), // between right center and far right
- (-3696.948242f - HeiganPos[1]) / (2785.624268f - HeiganPos[0]), // between left and right halves
- (-3691.880615f - HeiganPos[1]) / (2790.280029f - HeiganPos[0]) // between far left and left center
-};
-
-// 0 H x
-// 1 ^
-// 2 |
-// 3 y<--o
-inline uint32 GetEruptionSection(float x, float y)
-{
- y -= HeiganPos[1];
- if (y < 1.0f)
- return 0;
-
- x -= HeiganPos[0];
- if (x > -1.0f)
- return 3;
-
- float slope = y/x;
- for (uint32 i = 0; i < 3; ++i)
- if (slope > HeiganEruptionSlope[i])
- return i;
- return 3;
-}
-
class instance_naxxramas : public InstanceMapScript
{
public:
@@ -149,6 +119,7 @@ class instance_naxxramas : public InstanceMapScript
hadAnubRekhanGreet = false;
hadFaerlinaGreet = false;
hadThaddiusGreet = false;
+ hadSapphironBirth = false;
CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT;
playerDied = 0;
@@ -182,6 +153,9 @@ class instance_naxxramas : public InstanceMapScript
case NPC_SIR:
SirGUID = creature->GetGUID();
break;
+ case NPC_GLUTH:
+ GluthGUID = creature->GetGUID();
+ break;
case NPC_HEIGAN:
HeiganGUID = creature->GetGUID();
break;
@@ -208,28 +182,8 @@ class instance_naxxramas : public InstanceMapScript
}
}
- void ProcessEvent(WorldObject* /*source*/, uint32 eventId) override
- {
- switch (eventId)
- {
- case EVENT_THADDIUS_BEGIN_RESET:
- if (GetBossState(BOSS_THADDIUS) == SPECIAL) // this is the initial spawn, we want a shorter spawn time
- events.ScheduleEvent(EVENT_THADDIUS_RESET, 5 * IN_MILLISECONDS);
- else
- events.ScheduleEvent(EVENT_THADDIUS_RESET, 30 * IN_MILLISECONDS);
- break;
- }
- }
-
void OnGameObjectCreate(GameObject* go) override
{
- if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287)
- {
- uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY());
- HeiganEruptionGUID[section].insert(go->GetGUID());
- return;
- }
-
switch (go->GetEntry())
{
case GO_GOTHIK_GATE:
@@ -273,37 +227,18 @@ class instance_naxxramas : public InstanceMapScript
if (GetBossState(BOSS_HORSEMEN) == DONE)
go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
break;
- default:
- break;
- }
-
- InstanceScript::OnGameObjectCreate(go);
- }
-
- void OnGameObjectRemove(GameObject* go) override
- {
- if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287)
- {
- uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY());
- HeiganEruptionGUID[section].erase(go->GetGUID());
- return;
- }
-
- switch (go->GetEntry())
- {
case GO_BIRTH:
- if (SapphironGUID)
+ if (hadSapphironBirth || GetBossState(BOSS_SAPPHIRON) == DONE)
{
- if (Creature* sapphiron = instance->GetCreature(SapphironGUID))
- sapphiron->AI()->DoAction(DATA_SAPPHIRON_BIRTH);
- return;
+ hadSapphironBirth = true;
+ go->Delete();
}
break;
default:
break;
}
- InstanceScript::OnGameObjectRemove(go);
+ InstanceScript::OnGameObjectCreate(go);
}
void OnUnitDeath(Unit* unit) override
@@ -328,9 +263,6 @@ class instance_naxxramas : public InstanceMapScript
{
switch (id)
{
- case DATA_HEIGAN_ERUPT:
- HeiganErupt(value);
- break;
case DATA_GOTHIK_GATE:
if (GameObject* gate = instance->GetGameObject(GothikGateGUID))
gate->SetGoState(GOState(value));
@@ -347,6 +279,9 @@ class instance_naxxramas : public InstanceMapScript
case DATA_HAD_THADDIUS_GREET:
hadThaddiusGreet = (value == 1u);
break;
+ case DATA_HAD_SAPPHIRON_BIRTH:
+ hadSapphironBirth = (value == 1u);
+ break;
default:
break;
}
@@ -364,6 +299,8 @@ class instance_naxxramas : public InstanceMapScript
return hadFaerlinaGreet ? 1u : 0u;
case DATA_HAD_THADDIUS_GREET:
return hadThaddiusGreet ? 1u : 0u;
+ case DATA_HAD_SAPPHIRON_BIRTH:
+ return hadSapphironBirth ? 1u : 0u;
default:
break;
}
@@ -393,12 +330,16 @@ class instance_naxxramas : public InstanceMapScript
return SirGUID;
case DATA_HEIGAN:
return HeiganGUID;
+ case DATA_GLUTH:
+ return GluthGUID;
case DATA_FEUGEN:
return FeugenGUID;
case DATA_STALAGG:
return StalaggGUID;
case DATA_THADDIUS:
return ThaddiusGUID;
+ case DATA_SAPPHIRON:
+ return SapphironGUID;
case DATA_KELTHUZAD:
return KelthuzadGUID;
case DATA_KELTHUZAD_PORTAL01:
@@ -431,7 +372,7 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_ARACHNID))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
+ events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_LOATHEB:
@@ -440,7 +381,7 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_PLAGUE))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
+ events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_THADDIUS:
@@ -449,12 +390,12 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_CONSTRUCT))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
+ events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_GOTHIK:
if (state == DONE)
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ, 10000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ, Seconds(10));
break;
case BOSS_HORSEMEN:
if (state == DONE)
@@ -468,12 +409,12 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_MILITARY))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
- events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
+ events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_SAPPHIRON:
if (state == DONE)
- events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, Seconds(6));
break;
default:
break;
@@ -493,37 +434,37 @@ class instance_naxxramas : public InstanceMapScript
case EVENT_DIALOGUE_GOTHIK_KORTHAZZ:
if (Creature* korthazz = instance->GetCreature(ThaneGUID))
korthazz->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK, 5000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK, Seconds(5));
break;
case EVENT_DIALOGUE_GOTHIK_ZELIEK:
if (Creature* zeliek = instance->GetCreature(SirGUID))
zeliek->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_BLAUMEUX:
if (Creature* blaumeux = instance->GetCreature(LadyGUID))
blaumeux->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_RIVENDARE:
if (Creature* rivendare = instance->GetCreature(BaronGUID))
rivendare->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX2, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_BLAUMEUX2:
if (Creature* blaumeux = instance->GetCreature(LadyGUID))
blaumeux->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK2, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_ZELIEK2:
if (Creature* zeliek = instance->GetCreature(SirGUID))
zeliek->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_KORTHAZZ2:
if (Creature* korthazz = instance->GetCreature(ThaneGUID))
korthazz->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2);
- events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE2, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_RIVENDARE2:
if (Creature* rivendare = instance->GetCreature(BaronGUID))
@@ -540,62 +481,39 @@ class instance_naxxramas : public InstanceMapScript
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD);
HandleGameObject(KelthuzadDoorGUID, false);
- events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING, Seconds(6));
break;
case EVENT_DIALOGUE_SAPPHIRON_LICHKING:
if (Creature* lichKing = instance->GetCreature(LichKingGUID))
lichKing->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_LICH_KING);
- events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2, 16000);
+ events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2, Seconds(16));
break;
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD2);
- events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING2, 9000);
+ events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING2, Seconds(9));
break;
case EVENT_DIALOGUE_SAPPHIRON_LICHKING2:
if (Creature* lichKing = instance->GetCreature(LichKingGUID))
lichKing->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_LICH_KING2);
- events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3, 12000);
+ events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3, Seconds(12));
break;
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD3);
- events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4, 6000);
+ events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4, Seconds(6));
break;
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD4);
HandleGameObject(KelthuzadDoorGUID, true);
break;
- case EVENT_THADDIUS_RESET:
- if (GetBossState(BOSS_THADDIUS) != DONE)
- if (Creature* thaddius = instance->GetCreature(ThaddiusGUID))
- thaddius->AI()->DoAction(-1);
- break;
default:
break;
}
}
}
- void HeiganErupt(uint32 section)
- {
- for (uint32 i = 0; i < 4; ++i)
- {
- if (i == section)
- continue;
-
- for (ObjectGuid guid : HeiganEruptionGUID[i])
- {
- if (GameObject* heiganEruption = instance->GetGameObject(guid))
- {
- heiganEruption->SendCustomAnim(heiganEruption->GetGoAnimProgress());
- heiganEruption->CastSpell(NULL, SPELL_ERUPTION);
- }
- }
- }
- }
-
// This Function is called in CheckAchievementCriteriaMeet and CheckAchievementCriteriaMeet is called before SetBossState(bossId, DONE),
// so to check if all bosses are done the checker must exclude 1 boss, the last done, if there is at most 1 encouter in progress when is
// called this function then all bosses are done. The one boss that check is the boss that calls this function, so it is dead.
@@ -653,7 +571,6 @@ class instance_naxxramas : public InstanceMapScript
/* The Plague Quarter */
// Heigan the Unclean
- GuidSet HeiganEruptionGUID[4];
ObjectGuid HeiganGUID;
/* The Military Quarter */
@@ -670,6 +587,8 @@ class instance_naxxramas : public InstanceMapScript
ObjectGuid HorsemenChestGUID;
/* The Construct Quarter */
+ // Gluth
+ ObjectGuid GluthGUID;
// Thaddius
ObjectGuid ThaddiusGUID;
ObjectGuid FeugenGUID;
@@ -688,6 +607,7 @@ class instance_naxxramas : public InstanceMapScript
bool hadAnubRekhanGreet;
bool hadFaerlinaGreet;
bool hadThaddiusGreet;
+ bool hadSapphironBirth;
uint8 CurrentWingTaunt;
/* The Immortal / The Undying */
diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h
index c0caa86e93f..a3fedf5aa40 100644
--- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h
+++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h
@@ -43,12 +43,11 @@ enum Encounter
enum Data
{
- DATA_HEIGAN_ERUPT,
DATA_GOTHIK_GATE,
- DATA_SAPPHIRON_BIRTH,
DATA_HAD_ANUBREKHAN_GREET,
DATA_HAD_FAERLINA_GREET,
DATA_HAD_THADDIUS_GREET,
+ DATA_HAD_SAPPHIRON_BIRTH,
DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT,
DATA_ABOMINATION_KILLED,
@@ -69,10 +68,12 @@ enum Data64
DATA_LADY,
DATA_BARON,
DATA_SIR,
+ DATA_GLUTH,
DATA_THADDIUS,
DATA_HEIGAN,
DATA_FEUGEN,
DATA_STALAGG,
+ DATA_SAPPHIRON,
DATA_KELTHUZAD,
DATA_KELTHUZAD_PORTAL01,
DATA_KELTHUZAD_PORTAL02,
@@ -92,6 +93,7 @@ enum CreaturesIds
NPC_LADY = 16065,
NPC_BARON = 30549,
NPC_SIR = 16063,
+ NPC_GLUTH = 15932,
NPC_HEIGAN = 15936,
NPC_THADDIUS = 15928,
NPC_FEUGEN = 15930,
@@ -158,12 +160,6 @@ enum GameObjectsIds
GO_NAXX_PORTAL_MILITARY = 181578
};
-enum SpellIds
-{
- SPELL_ERUPTION = 29371,
- SPELL_SLIME = 28801
-};
-
enum InstanceEvents
{
// Dialogue that happens after Gothik's death.
@@ -176,10 +172,6 @@ enum InstanceEvents
EVENT_DIALOGUE_GOTHIK_KORTHAZZ2,
EVENT_DIALOGUE_GOTHIK_RIVENDARE2,
- // Thaddius AI requesting timed encounter (re-)spawn
- EVENT_THADDIUS_BEGIN_RESET,
- EVENT_THADDIUS_RESET,
-
// Dialogue that happens after each wing.
EVENT_KELTHUZAD_WING_TAUNT,
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
index 277ca793a8f..3d5a6ee8dfb 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
@@ -377,7 +377,7 @@ public:
me->SetDisableGravity(true);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
// 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->SetSpeed(MOVE_FLIGHT, _flySpeed * 0.25f);
+ me->SetSpeedRate(MOVE_FLIGHT, _flySpeed * 0.25f);
if (_despawned)
DoAction(ACTION_HANDLE_RESPAWN);
@@ -603,7 +603,7 @@ public:
me->SetRespawnDelay(respawnDelay);
// Set speed to normal value
- me->SetSpeed(MOVE_FLIGHT, _flySpeed);
+ 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();
@@ -1165,7 +1165,7 @@ public:
_instance = creature->GetInstanceScript();
me->SetReactState(REACT_PASSIVE);
// TO DO: These were a bit faster than what they should be. Not sure what is the reason.
- me->SetSpeed(MOVE_FLIGHT, 1.25f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.25f);
}
void Initialize()
@@ -1224,13 +1224,15 @@ public:
void DoAction(int32 /*action*/) override
{
if (Vehicle* vehicleTemp = me->GetVehicleKit())
+ {
if (vehicleTemp->GetPassenger(0) && vehicleTemp->GetPassenger(0)->GetTypeId() == TYPEID_PLAYER)
{
vehicleTemp->RemoveAllPassengers();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+ }
- me->DespawnOrUnsummon(3*IN_MILLISECONDS);
+ me->DespawnOrUnsummon(3*IN_MILLISECONDS);
}
void MovementInform(uint32 type, uint32 id) override
@@ -1274,7 +1276,7 @@ public:
me->SetReactState(REACT_PASSIVE);
// TO DO: Something is wrong with calculations for flying creatures that are on WP/Cyclic path.
// They should get the same difference as to when at ground from run creature switch to walk.
- me->SetSpeed(MOVE_FLIGHT, 0.45f);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.45f);
}
void Reset() override
@@ -1566,7 +1568,7 @@ public:
{
me->DespawnOrUnsummon(2050);
me->SetOrientation(2.5f);
- me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
Position pos = me->GetPosition();
pos.m_positionX += 10.0f;
pos.m_positionY += 10.0f;
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
index e1a4a6ed7b8..3d9ea97b136 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
@@ -375,7 +375,7 @@ class npc_ruby_emerald_amber_drake : public CreatureScript
{
me->DespawnOrUnsummon(2050);
me->SetOrientation(2.5f);
- me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
Talk(SAY_DRAKES_TAKEOFF);
Position pos = me->GetPosition();
Position offset = { 10.0f, 10.0f, 12.0f, 0.0f };
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
index a98da2c6e3d..8f7687d0fca 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
@@ -174,7 +174,7 @@ public:
{
if (pSpark->IsAlive())
{
- pSpark->SetSpeed(MOVE_RUN, 2.0f);
+ pSpark->SetSpeedRate(MOVE_RUN, 2.0f);
pSpark->GetMotionMaster()->Clear();
pSpark->GetMotionMaster()->MovePoint(DATA_POINT_CALLBACK, pos);
}
@@ -355,7 +355,7 @@ public:
{
Position pos = ionar->GetPosition();
- me->SetSpeed(MOVE_RUN, 2.0f);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(DATA_POINT_CALLBACK, pos);
}
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_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
index 309d5d4c62f..8aa443cba3f 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
@@ -359,9 +359,9 @@ static bool IsEncounterFinished(Unit* who)
if (!mkii || !vx001 || !aerial)
return false;
- if (mkii->getStandState() == UNIT_STAND_STATE_DEAD &&
- vx001->getStandState() == UNIT_STAND_STATE_DEAD &&
- aerial->getStandState() == UNIT_STAND_STATE_DEAD)
+ if (mkii->GetStandState() == UNIT_STAND_STATE_DEAD &&
+ vx001->GetStandState() == UNIT_STAND_STATE_DEAD &&
+ aerial->GetStandState() == UNIT_STAND_STATE_DEAD)
{
who->Kill(mkii);
who->Kill(vx001);
@@ -703,7 +703,7 @@ class boss_leviathan_mk_ii : public CreatureScript
if (Unit* turret = me->GetVehicleKit()->GetPassenger(3))
turret->KillSelf();
- me->SetSpeed(MOVE_RUN, 1.5f, true);
+ me->SetSpeedRate(MOVE_RUN, 1.5f);
me->GetMotionMaster()->MovePoint(WP_MKII_P1_IDLE, VehicleRelocation[WP_MKII_P1_IDLE]);
}
else if (events.IsInPhase(PHASE_VOL7RON))
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
index f5337b2dca5..363f5907048 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
@@ -366,7 +366,7 @@ class boss_razorscale : public CreatureScript
_EnterCombat();
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAZORSCALE_CONTROL)))
controller->AI()->DoAction(ACTION_HARPOON_BUILD);
- me->SetSpeed(MOVE_FLIGHT, 3.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 3.0f);
me->SetReactState(REACT_PASSIVE);
phase = PHASE_GROUND;
events.SetPhase(PHASE_GROUND);
@@ -550,7 +550,7 @@ class boss_razorscale : public CreatureScript
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveAurasDueToSpell(SPELL_HARPOON_TRIGGER);
- me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
PermaGround = true;
DoCastAOE(SPELL_FLAMEBREATH);
events.ScheduleEvent(EVENT_FLAME, 15000, 0, PHASE_PERMAGROUND);
@@ -677,7 +677,7 @@ class npc_expedition_commander : public CreatureScript
if (Creature* summonedEngineer = me->SummonCreature(NPC_ENGINEER, PosEngSpawn, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000))
{
summonedEngineer->SetWalk(false);
- summonedEngineer->SetSpeed(MOVE_RUN, 0.5f);
+ summonedEngineer->SetSpeedRate(MOVE_RUN, 0.5f);
summonedEngineer->SetHomePosition(PosEngRepair[n]);
summonedEngineer->GetMotionMaster()->MoveTargetedHome();
Engineer[n] = summonedEngineer->GetGUID();
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
index 1af45f3031a..3dec0c60991 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
@@ -102,7 +102,7 @@ public:
Sequence[i] = Phase(i);
/// This ensures a random order and only executes each phase once.
- std::random_shuffle(Sequence, Sequence + PHASE_GORTOK_PALEHOOF);
+ Trinity::Containers::RandomShuffle(Sequence);
uiArcingSmashTimer = 15000;
uiImpaleTimer = 12000;
@@ -118,7 +118,7 @@ public:
uint32 uiWhiteringRoarTimer;
Phase currentPhase;
uint8 AddCount;
- Phase Sequence[4];
+ std::array<Phase, 4> Sequence;
void Reset() override
{
@@ -746,7 +746,7 @@ public:
//! HACK: Creature's can't have MOVEMENTFLAG_FLYING
me->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
me->RemoveAurasDueToSpell(SPELL_ORB_VISUAL);
- me->SetSpeed(MOVE_FLIGHT, 0.5f);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.5f);
}
void UpdateAI(uint32 diff) override
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp
index d7b65093898..7615217a794 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp
@@ -209,7 +209,7 @@ public:
Initialize();
Summons.DespawnAll();
- me->SetSpeed(MOVE_FLIGHT, 3.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 3.0f);
if ((ObjectAccessor::GetCreature(*me, m_uiGraufGUID) == NULL) && !me->IsMounted())
me->SummonCreature(NPC_GRAUF, Location[0].GetPositionX(), Location[0].GetPositionY(), Location[0].GetPositionZ(), 3.0f);
instance->SetBossState(DATA_SKADI_THE_RUTHLESS, NOT_STARTED);
diff --git a/src/server/scripts/Northrend/northrend_script_loader.cpp b/src/server/scripts/Northrend/northrend_script_loader.cpp
index 7d104b85f6d..d84bb1c4072 100644
--- a/src/server/scripts/Northrend/northrend_script_loader.cpp
+++ b/src/server/scripts/Northrend/northrend_script_loader.cpp
@@ -178,6 +178,8 @@ void AddSC_boss_baltharus_the_warborn();
void AddSC_boss_saviana_ragefire();
void AddSC_boss_general_zarithrian();
void AddSC_boss_halion();
+void AddSC_isle_of_conquest(); // Isle of Conquest
+void AddSC_boss_ioc_horde_alliance();
void AddSC_dalaran();
void AddSC_borean_tundra();
@@ -190,7 +192,6 @@ void AddSC_storm_peaks();
void AddSC_wintergrasp();
void AddSC_zuldrak();
void AddSC_crystalsong_forest();
-void AddSC_isle_of_conquest();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
@@ -358,6 +359,8 @@ void AddNorthrendScripts()
AddSC_boss_saviana_ragefire();
AddSC_boss_general_zarithrian();
AddSC_boss_halion();
+ AddSC_isle_of_conquest(); // Isle of Conquest
+ AddSC_boss_ioc_horde_alliance();
AddSC_dalaran();
AddSC_borean_tundra();
@@ -370,5 +373,4 @@ void AddNorthrendScripts()
AddSC_wintergrasp();
AddSC_zuldrak();
AddSC_crystalsong_forest();
- AddSC_isle_of_conquest();
}
diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp
index 59802165a94..adade245c2b 100644
--- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp
+++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp
@@ -22,6 +22,7 @@
#include "Player.h"
#include "SpellScript.h"
#include "CreatureTextMgr.h"
+#include "CombatAI.h"
/*######
## Quest 12027: Mr. Floppy's Perilous Adventure
@@ -854,6 +855,260 @@ class spell_infected_worgen_bite : public SpellScriptLoader
}
};
+/*######
+## Quest: Riding the Red Rocket
+######*/
+
+enum RedRocket
+{
+ SPELL_VEHICLE_WARHEAD_FUSE = 49107,
+ SPELL_ALLIANCE_KILL_CREDIT_TORPEDO = 49510,
+ SPELL_HORDE_KILL_CREDIT_TORPEDO = 49340,
+ NPC_HORDE_LUMBERBOAT = 27702,
+ NPC_ALLIANCE_LUMBERBOAT = 27688,
+ SPELL_DETONATE = 49250
+};
+
+class npc_rocket_propelled_warhead : public CreatureScript
+{
+public:
+ npc_rocket_propelled_warhead() : CreatureScript("npc_rocket_propelled_warhead") { }
+
+ struct npc_rocket_propelled_warheadAI : public VehicleAI
+ {
+ npc_rocket_propelled_warheadAI(Creature* creature) : VehicleAI(creature)
+ {
+ _finished = false;
+ _faction = ALLIANCE;
+ }
+
+ void PassengerBoarded(Unit* who, int8 /*seatId*/, bool apply) override
+ {
+ if (apply && who->ToPlayer())
+ {
+ DoCast(me, SPELL_VEHICLE_WARHEAD_FUSE);
+ _faction = who->ToPlayer()->GetTeam();
+ }
+ }
+
+ void JustReachedHome() override
+ {
+ _finished = false;
+ me->SetVisible(true);
+ me->GetMotionMaster()->Clear(true);
+ }
+
+ void DoAction(int32 /*action*/) override
+ {
+ FinishQuest(false, _faction);
+ }
+
+ void SpellHit(Unit* caster, SpellInfo const* /*spellInfo*/) override
+ {
+ if (caster->GetEntry() == NPC_HORDE_LUMBERBOAT || caster->GetEntry() == NPC_ALLIANCE_LUMBERBOAT)
+ FinishQuest(true, _faction);
+ }
+
+ void FinishQuest(bool success, uint32 faction)
+ {
+ if (_finished)
+ return;
+
+ _finished = true;
+
+ if (success)
+ DoCast(me, faction == ALLIANCE ? SPELL_ALLIANCE_KILL_CREDIT_TORPEDO : SPELL_HORDE_KILL_CREDIT_TORPEDO);
+
+ DoCast(me, SPELL_DETONATE);
+ me->RemoveAllAuras();
+ me->SetVisible(false);
+ me->GetMotionMaster()->MoveTargetedHome();
+ }
+
+ private:
+ uint32 _faction;
+ bool _finished;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_rocket_propelled_warheadAI(creature);
+ }
+};
+
+enum WarheadSpells
+{
+ SPELL_WARHEAD_Z_CHECK = 61678,
+ SPELL_WARHEAD_SEEKING_LUMBERSHIP = 49331,
+ SPELL_WARHEAD_FUSE = 49181
+};
+// 49107 - Vehicle: Warhead Fuse
+class spell_vehicle_warhead_fuse : public SpellScriptLoader
+{
+public:
+ spell_vehicle_warhead_fuse() : SpellScriptLoader("spell_vehicle_warhead_fuse") { }
+
+ class spell_vehicle_warhead_fuse_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_vehicle_warhead_fuse_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARHEAD_Z_CHECK) || !sSpellMgr->GetSpellInfo(SPELL_WARHEAD_SEEKING_LUMBERSHIP) || !sSpellMgr->GetSpellInfo(SPELL_WARHEAD_FUSE))
+ return false;
+ return true;
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+
+ caster->CastSpell(caster, SPELL_WARHEAD_Z_CHECK, true);
+ caster->CastSpell(caster, SPELL_WARHEAD_SEEKING_LUMBERSHIP, true);
+ caster->CastSpell(caster, SPELL_WARHEAD_FUSE, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_vehicle_warhead_fuse_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_vehicle_warhead_fuse_SpellScript();
+ }
+};
+
+enum WarheadDenonate
+{
+ SPELL_PARACHUTE = 66154,
+ SPELL_TORPEDO_EXPLOSION = 49290,
+ NPC_ALLIANCE_LUMBERBOAT_EXPLOSIONS = 27689
+};
+// 49250 - Detonate
+class spell_warhead_detonate : public SpellScriptLoader
+{
+public:
+ spell_warhead_detonate() : SpellScriptLoader("spell_warhead_detonate") { }
+
+ class spell_warhead_detonate_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warhead_detonate_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PARACHUTE) || !sSpellMgr->GetSpellInfo(SPELL_TORPEDO_EXPLOSION))
+ return false;
+ return true;
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+ Player* player = GetHitPlayer();
+ if (!player)
+ return;
+
+ player->ExitVehicle();
+ float horizontalSpeed = 3.0f;
+ float verticalSpeed = 40.0f;
+ player->KnockbackFrom(caster->GetPositionX(), caster->GetPositionY(), horizontalSpeed, verticalSpeed);
+ caster->CastSpell(player, SPELL_PARACHUTE, true);
+
+ std::list<Creature*> explosionBunnys;
+ caster->GetCreatureListWithEntryInGrid(explosionBunnys, NPC_ALLIANCE_LUMBERBOAT_EXPLOSIONS, 90.0f);
+ for (std::list<Creature*>::const_iterator itr = explosionBunnys.begin(); itr != explosionBunnys.end(); ++itr)
+ (*itr)->CastSpell((*itr), SPELL_TORPEDO_EXPLOSION, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warhead_detonate_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_warhead_detonate_SpellScript();
+ }
+};
+
+// 61678 - Z Check
+class spell_z_check : public SpellScriptLoader
+{
+public:
+ spell_z_check() : SpellScriptLoader("spell_z_check") { }
+
+ class spell_z_check_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_z_check_AuraScript);
+
+ public:
+ spell_z_check_AuraScript()
+ {
+ _posZ = 0.0f;
+ }
+
+ void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ _posZ = GetTarget()->GetPositionZ();
+ }
+
+ void HandleEffectPeriodic(AuraEffect const* /*aurEff*/)
+ {
+ PreventDefaultAction();
+
+ if (_posZ != GetTarget()->GetPositionZ())
+ if (Creature* target = GetTarget()->ToCreature())
+ target->AI()->DoAction(0);
+ }
+
+ private:
+ float _posZ;
+
+ void Register() override
+ {
+ OnEffectApply += AuraEffectApplyFn(spell_z_check_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_z_check_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_z_check_AuraScript();
+ }
+};
+
+// 49181 - Warhead Fuse
+class spell_warhead_fuse : public SpellScriptLoader
+{
+public:
+ spell_warhead_fuse() : SpellScriptLoader("spell_warhead_fuse") { }
+
+ class spell_warhead_fuse_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warhead_fuse_AuraScript);
+
+ void HandleOnEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Unit* rocketUnit = GetTarget()->GetVehicleBase())
+ if (Creature* rocketCrea = rocketUnit->ToCreature())
+ rocketCrea->AI()->DoAction(0);
+ }
+
+ void Register() override
+ {
+ OnEffectRemove += AuraEffectRemoveFn(spell_warhead_fuse_AuraScript::HandleOnEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_warhead_fuse_AuraScript();
+ }
+};
+
void AddSC_grizzly_hills()
{
new npc_emily();
@@ -866,4 +1121,9 @@ void AddSC_grizzly_hills()
new npc_lake_frog();
new spell_shredder_delivery();
new spell_infected_worgen_bite();
+ new npc_rocket_propelled_warhead();
+ new spell_z_check();
+ new spell_warhead_detonate();
+ new spell_vehicle_warhead_fuse();
+ new spell_warhead_fuse();
}
diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp
index 1e020edd10a..3d801cc2fbb 100644
--- a/src/server/scripts/Northrend/zone_icecrown.cpp
+++ b/src/server/scripts/Northrend/zone_icecrown.cpp
@@ -712,7 +712,7 @@ enum BorrowedTechnologyAndVolatility
SPELL_PING_BUNNY = 59375,
SPELL_IMMOLATION = 54690,
SPELL_EXPLOSION = 59335,
- SPELL_RIDE = 56687,
+ SPELL_RIDE = 59319,
// Points
POINT_GRAB_DECOY = 1,
diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
index 7902c585509..14aeda04a7e 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
@@ -745,7 +745,7 @@ public:
if (!Trigger)
return;
- Trigger->SetSpeed(MOVE_WALK, 3);
+ Trigger->SetSpeedRate(MOVE_WALK, 3);
Trigger->SetWalk(true);
Trigger->GetMotionMaster()->MovePoint(0, final.x, final.y, final.z);
@@ -1301,15 +1301,17 @@ public:
EventMaiev Event = EVENT_MAIEV_NULL;
for (uint8 i = 1; i <= MaxTimer; ++i)
+ {
if (Timer[i])
{
if (Timer[i] <= diff)
Event = (EventMaiev)i;
else Timer[i] -= diff;
}
+ }
- switch (Event)
- {
+ switch (Event)
+ {
case EVENT_MAIEV_STEALTH:
{
me->SetFullHealth();
@@ -1345,21 +1347,21 @@ public:
break;
default:
break;
- }
+ }
- if (HealthBelowPct(50))
- {
- me->SetVisible(false);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID))
- ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->DeleteFromThreatList(me->GetGUID());
- me->AttackStop();
- Timer[EVENT_MAIEV_STEALTH] = 60000; // reappear after 1 minute
- MaxTimer = 1;
- }
+ if (HealthBelowPct(50))
+ {
+ me->SetVisible(false);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID))
+ ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->DeleteFromThreatList(me->GetGUID());
+ me->AttackStop();
+ Timer[EVENT_MAIEV_STEALTH] = 60000; // reappear after 1 minute
+ MaxTimer = 1;
+ }
- if (Phase == PHASE_NORMAL_MAIEV)
- DoMeleeAttackIfReady();
+ if (Phase == PHASE_NORMAL_MAIEV)
+ DoMeleeAttackIfReady();
}
private:
@@ -1524,7 +1526,7 @@ public:
void BeginWalk()
{
me->SetWalk(false);
- me->SetSpeed(MOVE_RUN, 1.0f);
+ me->SetSpeedRate(MOVE_RUN, 1.0f);
me->GetMotionMaster()->MovePoint(0, AkamaWP[WalkCount].x, AkamaWP[WalkCount].y, AkamaWP[WalkCount].z);
}
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 7e1215488e1..1a9074c2464 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
@@ -238,8 +238,8 @@ public:
if (me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2) <= 3)
{
moveSpeed = (2.0f - (0.6f * me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2)));
- me->SetSpeed(MOVE_WALK, moveSpeed / 2.5f);
- me->SetSpeed(MOVE_RUN, (moveSpeed * 2) / 7);
+ me->SetSpeedRate(MOVE_WALK, moveSpeed / 2.5f);
+ me->SetSpeedRate(MOVE_RUN, (moveSpeed * 2) / 7);
me->ClearUnitState(UNIT_STATE_ROOT);
}
else
diff --git a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
index ae4d17bdad4..0999f1fcd6b 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
@@ -117,7 +117,7 @@ public:
DummyEntryCheckPredicate pred;
summons.DoAction(EVENT_VOLCANO, pred);
events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 5000, GCD_CAST, PHASE_STRIKE);
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false);
}
@@ -126,7 +126,7 @@ public:
phase = PHASE_CHASE;
events.ScheduleEvent(EVENT_VOLCANO, 5000, GCD_CAST, PHASE_CHASE);
events.ScheduleEvent(EVENT_SWITCH_TARGET, 10000, 0, PHASE_CHASE);
- me->SetSpeed(MOVE_RUN, 0.9f);
+ me->SetSpeedRate(MOVE_RUN, 0.9f);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
}
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
index e24499c3aee..3ddf0fec416 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
@@ -585,8 +585,8 @@ public:
void Reset() override
{
- me->SetSpeed(MOVE_WALK, 0.6f); // walk
- me->SetSpeed(MOVE_RUN, 0.6f); // run
+ me->SetSpeedRate(MOVE_WALK, 0.6f); // walk
+ me->SetSpeedRate(MOVE_RUN, 0.6f); // run
Initialize();
//search for nearest waypoint (up on stairs)
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp
index 8d117f7c3ef..f72b9a8d2ca 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp
@@ -244,7 +244,7 @@ public:
CheckChannelers();
Initialize();
me->SetCanDualWield(true);
- me->SetSpeed(MOVE_RUN, 2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
me->SetDisplayId(MODEL_NIGHTELF);
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID , 0);
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID+1, 0);
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
new file mode 100644
index 00000000000..89b7a8cbd46
--- /dev/null
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
@@ -0,0 +1,1052 @@
+/*
+ * 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 "CreatureTextMgr.h"
+#include "LFGMgr.h"
+#include "ScriptedGossip.h"
+#include "ScriptedCreature.h"
+#include "ScriptMgr.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
+#include "the_slave_pens.h"
+
+enum Spells
+{
+ // Ahune
+ SPELL_SYNCH_HEALTH = 46430,
+ SPELL_AHUNES_SHIELD = 45954,
+ SPELL_STAY_SUBMERGED = 46981,
+ SPELL_AHUNE_SELF_STUN = 46416,
+ SPELL_AHUNE_ACHIEVEMENT = 62043,
+ SPELL_AHUNE_SPANKY_HANDS = 46146,
+ SPELL_COLD_SLAP = 46145,
+ SPELL_RESURFACE = 46402,
+ SPELL_SUBMERGED = 37751,
+ SPELL_STAND = 37752,
+
+ //Earther Ring Flamecaller
+ SPELL_FIND_OPENING_VISUAL = 45964,
+ SPELL_FIND_OPENING_BEAM_END = 46333,
+ SPELL_FIND_OPENING_TRIGGER = 46341,
+ SPELL_FIND_OPENING_CHANNEL = 46345,
+ SPELL_BONFIRE_VISUAL = 46339,
+ SPELL_FOUND_OPENING = 46421,
+
+ //Ahune Bunny
+ SPELL_SUMMON_COLDWEAVE = 46143,
+ SPELL_SUMMON_FROSTWIND = 46382,
+ SPELL_SUMMON_HAILSTONE = 46176,
+ SPELL_SUMMONING_VISUAL_1 = 45937,
+ SPELL_SUMMONING_RHYME_AURA = 45926,
+ SPELL_SUMMONING_RHYME_BONFIRE = 45930,
+ SPELL_FORCE_WHISP_FLIGHT = 46603,
+ SPELL_SHAMANS_LOOK_FOR_OPENING = 46422,
+ SPELL_CLOSE_OPENING_VISUAL = 46236,
+ SPELL_ICE_BOMBARD = 46397,
+ SPELL_ICE_BOMBARDMENT_DEST_PICKER = 46398,
+ SPELL_ICE_BOMBARDMENT = 46396,
+
+ // Ice Spear
+ SPELL_SUMMON_ICE_SPEAR_BUNNY = 46359,
+ SPELL_ICE_SPEAR_KNOCKBACK = 46360,
+ SPELL_SUMMON_ICE_SPEAR_GO = 46369,
+ SPELL_ICE_SPEAR_AURA = 46371,
+ SPELL_ICE_SPEAR_TARGET_PICKER = 46372,
+ SPELL_ICE_SPEAR_DELAY = 46878,
+ SPELL_ICE_SPEAR_VISUAL = 75498,
+
+ // Slippery Floor
+ SPELL_SLIPPERY_FLOOR_AMBIENT = 46314,
+ SPELL_SLIPPERY_FLOOR_PERIODIC = 46320,
+ SPELL_SLIPPERY_FLOOR_SLIP = 45947,
+
+ // Frozen Core
+ SPELL_SUICIDE = 45254,
+ SPELL_SUMMON_LOOT_MISSILE = 45941,
+ SPELL_FROZEN_CORE_GETS_HIT = 46810,
+ SPELL_MINION_DESPAWNER = 46843,
+ SPELL_GHOST_DISGUISE = 46786
+};
+
+enum Emotes
+{
+ EMOTE_EARTHEN_ASSAULT = 0,
+ EMOTE_RETREAT = 0,
+ EMOTE_RESURFACE = 1
+};
+
+enum Says
+{
+ SAY_PLAYER_TEXT_1 = 0,
+ SAY_PLAYER_TEXT_2 = 1,
+ SAY_PLAYER_TEXT_3 = 2
+};
+
+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
+};
+
+enum Actions
+{
+ ACTION_START_EVENT = -2574500,
+ ACTION_AHUNE_RETREAT = -2586500,
+ ACTION_AHUNE_RESURFACE = -2586501,
+ ACTION_EMOTE_RESURFACE = -2575400
+};
+
+enum Phases
+{
+ PHASE_ONE = 0,
+ PHASE_TWO = 1
+};
+
+enum Points
+{
+ POINT_FLAMECALLER_000,
+ POINT_FLAMECALLER_001,
+ POINT_FLAMECALLER_002
+};
+
+enum Misc
+{
+ MAX_FLAMECALLERS = 3
+};
+
+Position const SummonPositions[] =
+{
+ { -99.1021f, -233.7526f, -1.22307f, 1.588250f }, // Ahune
+ { -98.0151f, -230.4555f, -1.21089f, 1.797689f }, // Frozen Core
+ { -143.172f, -147.6801f, -3.16113f, 4.852015f }, // Bonfire Bunny 000
+ { -134.304f, -145.7803f, -1.70332f, 4.677482f }, // Bonfire Bunny 001
+ { -125.036f, -144.2065f, -1.91660f, 4.991642f }, // Bonfire Bunny 002
+ { -69.8121f, -162.4954f, -2.30451f, 1.710423f }, // Wisp Source Bunny
+ { -98.1029f, -230.7864f, -10.8085f, 1.448623f } // Wisp Dest Bunny
+};
+
+Position const FlameCallerSpots[] =
+{
+ { -145.2233f, -137.5543f, -1.59056f, 5.427049f },
+ { -137.4383f, -136.4050f, -1.72384f, 5.336747f },
+ { -129.0413f, -132.1494f, -2.09285f, 5.460842f }
+};
+
+class boss_ahune : public CreatureScript
+{
+public:
+ boss_ahune() : CreatureScript("boss_ahune") { }
+
+ struct boss_ahuneAI : public BossAI
+ {
+ boss_ahuneAI(Creature* creature) : BossAI(creature, DATA_AHUNE)
+ {
+ Initialize();
+ }
+
+ void Initialize()
+ {
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_INITIAL_EMERGE, 4);
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AHUNE_BUNNY)))
+ ahuneBunny->AI()->EnterEvadeMode();
+ summons.DespawnAll();
+ me->DespawnOrUnsummon();
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ instance->DoCastSpellOnPlayers(SPELL_AHUNE_ACHIEVEMENT);
+
+ if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AHUNE_BUNNY)))
+ me->Kill(ahuneBunny);
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ me->Kill(frozenCore);
+
+ Map::PlayerList const& players = me->GetMap()->GetPlayers();
+ if (!players.isEmpty())
+ {
+ if (Group* group = players.begin()->GetSource()->GetGroup())
+ if (group->isLFGGroup())
+ sLFGMgr->FinishDungeon(group->GetGUID(), 286);
+ }
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ BossAI::JustSummoned(summon);
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_AHUNE_RETREAT)
+ {
+ Submerge();
+ events.ScheduleEvent(EVENT_EMERGE, 35000);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_INITIAL_EMERGE:
+ DoCast(me, SPELL_STAND);
+ DoCast(me, SPELL_AHUNE_SPANKY_HANDS);
+ DoCast(me, SPELL_AHUNES_SHIELD);
+ break;
+ case EVENT_EMERGE:
+ Emerge();
+ break;
+ case EVENT_SYNCH_HEALTH:
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ DoCast(frozenCore, SPELL_SYNCH_HEALTH);
+ else
+ DoCast(me, SPELL_SUICIDE);
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ break;
+ default:
+ break;
+ }
+ }
+ DoMeleeAttackIfReady();
+ }
+
+ void Emerge()
+ {
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ frozenCore->AI()->DoAction(ACTION_AHUNE_RESURFACE);
+
+ DoCast(me, SPELL_AHUNES_SHIELD);
+ me->RemoveAurasDueToSpell(SPELL_AHUNE_SELF_STUN);
+ 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);
+ }
+
+ void Submerge()
+ {
+ 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);
+ DoCast(me, SPELL_SUBMERGED, true);
+ DoCast(me, SPELL_AHUNE_SELF_STUN, true);
+ DoCast(me, SPELL_STAY_SUBMERGED, true);
+ me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE);
+ events.Reset();
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<boss_ahuneAI>(creature);
+ }
+};
+
+class npc_frozen_core : public CreatureScript
+{
+public:
+ npc_frozen_core() : CreatureScript("npc_frozen_core") { }
+
+ struct npc_frozen_coreAI : public ScriptedAI
+ {
+ npc_frozen_coreAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _instance = me->GetInstanceScript();
+ Initialize();
+ }
+
+ void Initialize()
+ {
+ me->SetReactState(REACT_PASSIVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ 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)))
+ me->Kill(ahune);
+
+ DoCast(SPELL_SUMMON_LOOT_MISSILE);
+ DoCast(SPELL_MINION_DESPAWNER);
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_AHUNE_RETREAT)
+ {
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ me->RemoveAurasDueToSpell(SPELL_ICE_SPEAR_AURA);
+ _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000, 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);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SYNCH_HEALTH:
+ if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
+ DoCast(ahune, SPELL_SYNCH_HEALTH);
+ else
+ DoCast(me, SPELL_SUICIDE);
+ _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ InstanceScript* _instance;
+ EventMap _events;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_frozen_coreAI>(creature);
+ }
+};
+
+class npc_ahune_bunny : public CreatureScript
+{
+public:
+ npc_ahune_bunny() : CreatureScript("npc_ahune_bunny") { }
+
+ struct npc_ahune_bunnyAI : public ScriptedAI
+ {
+ npc_ahune_bunnyAI(Creature* creature) : ScriptedAI(creature), _summons(me)
+ {
+ _instance = me->GetInstanceScript();
+ _submerged = false;
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ if (summon->GetEntry() == NPC_AHUNE)
+ return;
+
+ summon->SetInCombatWithZone();
+ _summons.Summon(summon);
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _summons.DespawnAll();
+ ResetFlameCallers();
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ _EnterEvadeMode();
+ _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);
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_START_EVENT)
+ {
+ DoCast(me, SPELL_SUMMONING_VISUAL_1);
+ me->SummonCreature(NPC_WHISP_SOURCE_BUNNY, SummonPositions[5], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_WHISP_DEST_BUNNY, SummonPositions[6], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_SHAMAN_BONFIRE_BUNNY_000, SummonPositions[2], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_SHAMAN_BONFIRE_BUNNY_001, SummonPositions[3], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_SHAMAN_BONFIRE_BUNNY_002, SummonPositions[4], TEMPSUMMON_MANUAL_DESPAWN);
+
+ for (uint8 counter = 0; counter < MAX_FLAMECALLERS; ++counter)
+ if (Creature* flameCaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000 + counter)))
+ flameCaller->GetMotionMaster()->MovePoint(counter, FlameCallerSpots[counter].GetPosition());
+
+ _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);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_START_LOOKING_FOR_OPENING:
+ Talk(EMOTE_EARTHEN_ASSAULT);
+ for (uint8 counter = 0; counter < MAX_FLAMECALLERS; ++counter)
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000 + counter)))
+ DoCast(flamecaller, SPELL_SHAMANS_LOOK_FOR_OPENING, true);
+ break;
+ case EVENT_SUMMON_HAILSTONE:
+ DoCast(SPELL_SUMMON_HAILSTONE);
+ break;
+ case EVENT_SUMMON_COLDWEAVE:
+ DoCast(SPELL_SUMMON_COLDWEAVE);
+ DoCast(SPELL_SUMMON_COLDWEAVE);
+ _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE);
+ if (_submerged)
+ _events.ScheduleEvent(EVENT_SUMMON_FROSTWIND, 4000, 0, PHASE_ONE);
+ break;
+ case EVENT_SUMMON_FROSTWIND:
+ DoCast(SPELL_SUMMON_FROSTWIND);
+ break;
+ case EVENT_SUMMON_AHUNE:
+ if (TempSummon* ahune = me->SummonCreature(NPC_AHUNE, SummonPositions[0], TEMPSUMMON_DEAD_DESPAWN))
+ {
+ ahune->SummonCreature(NPC_FROZEN_CORE, SummonPositions[1], TEMPSUMMON_CORPSE_DESPAWN);
+ ahune->SetInCombatWithZone();
+ DoCast(ahune, SPELL_RESURFACE);
+ }
+ break;
+ case EVENT_CLOSE_OPENING:
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000)))
+ flamecaller->AI()->DoAction(ACTION_EMOTE_RESURFACE);
+ DoCast(SPELL_CLOSE_OPENING_VISUAL);
+ DoCast(me, SPELL_ICE_BOMBARD);
+ break;
+ case EVENT_AHUNE_PHASE_TWO:
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000)))
+ DoCast(flamecaller, SPELL_FOUND_OPENING);
+ if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
+ 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);
+ 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);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void ResetFlameCallers()
+ {
+ for (uint8 counter = 0; counter < MAX_FLAMECALLERS; ++counter)
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000 + counter)))
+ flamecaller->AI()->EnterEvadeMode();
+ }
+
+ private:
+ InstanceScript* _instance;
+ EventMap _events;
+ SummonList _summons;
+ bool _submerged;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_ahune_bunnyAI>(creature);
+ }
+};
+
+class npc_earthen_ring_flamecaller : public CreatureScript
+{
+public:
+ npc_earthen_ring_flamecaller() : CreatureScript("npc_earthen_ring_flamecaller") { }
+
+ struct npc_earthen_ring_flamecallerAI : public ScriptedAI
+ {
+ npc_earthen_ring_flamecallerAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _instance = me->GetInstanceScript();
+ _mySpot = 0;
+ }
+
+ void Reset() override
+ {
+ _events.Reset();
+ }
+
+ void MovementInform(uint32 motionType, uint32 pointId) override
+ {
+ if (motionType != POINT_MOTION_TYPE)
+ return;
+
+ switch (pointId)
+ {
+ case POINT_FLAMECALLER_000:
+ _mySpot = POINT_FLAMECALLER_000;
+ me->SetOrientation(FlameCallerSpots[_mySpot].GetOrientation());
+ break;
+ case POINT_FLAMECALLER_001:
+ _mySpot = POINT_FLAMECALLER_001;
+ me->SetOrientation(FlameCallerSpots[_mySpot].GetOrientation());
+ break;
+ case POINT_FLAMECALLER_002:
+ _mySpot = POINT_FLAMECALLER_002;
+ me->SetOrientation(FlameCallerSpots[_mySpot].GetOrientation());
+ break;
+ default:
+ break;
+ }
+
+ DoCast(me, SPELL_FIND_OPENING_CHANNEL);
+ }
+
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ switch (spellInfo->Id)
+ {
+ case SPELL_SHAMANS_LOOK_FOR_OPENING:
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_0, 17000);
+ break;
+ case SPELL_FOUND_OPENING:
+ _events.ScheduleEvent(EVENT_FOUND_OPENING, 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void DoAction(int action) override
+ {
+ if (action == ACTION_EMOTE_RESURFACE)
+ Talk(EMOTE_RESURFACE);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_LOOKFOROPENING_0:
+ LookOpening(true, 0);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_1, 26000);
+ break;
+ case EVENT_LOOKFOROPENING_1:
+ LookOpening(true, 1);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_2, 25000);
+ break;
+ case EVENT_LOOKFOROPENING_2:
+ LookOpening(true, 2);
+ _events.ScheduleEvent(EVENT_STOP_LOOKING_FOR_OPENING, 27000);
+ break;
+ case EVENT_STOP_LOOKING_FOR_OPENING:
+ LookOpening(false, _mySpot);
+ break;
+ case EVENT_FOUND_OPENING:
+ Talk(EMOTE_RETREAT);
+ default:
+ break;
+ }
+ }
+ }
+
+ void LookOpening(bool activate, uint8 spot)
+ {
+ if (_mySpot != spot)
+ return;
+
+ if (Creature* bonfireBunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_BONFIRE_BUNNY_000 + spot)))
+ if (Creature* beamBunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_BEAM_BUNNY_000 + spot)))
+ {
+ if (activate)
+ {
+ DoCast(bonfireBunny, SPELL_FIND_OPENING_TRIGGER);
+ bonfireBunny->CastSpell(beamBunny, SPELL_FIND_OPENING_VISUAL, true);
+ }
+ else
+ {
+ DoCast(me, SPELL_FIND_OPENING_CHANNEL);
+ bonfireBunny->CastStop();
+ beamBunny->RemoveAurasDueToSpell(SPELL_FIND_OPENING_BEAM_END);
+ }
+ }
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ uint8 _mySpot;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_earthen_ring_flamecallerAI>(creature);
+ }
+};
+
+class go_ahune_ice_stone : public GameObjectScript
+{
+public:
+ go_ahune_ice_stone() : GameObjectScript("go_ahune_ice_stone") { }
+
+ bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 /*action*/)
+ {
+ InstanceScript* instance = go->GetInstanceScript();
+ if (!instance)
+ return false;
+
+ player->PlayerTalkClass->ClearMenus();
+
+ if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_AHUNE_BUNNY)))
+ {
+ ahuneBunny->AI()->DoAction(ACTION_START_EVENT);
+ ahuneBunny->SetInCombatWithZone();
+ }
+ if (Creature* luma = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_LUMA_SKYMOTHER)))
+ luma->CastSpell(player, SPELL_SUMMONING_RHYME_AURA, true);
+ player->CLOSE_GOSSIP_MENU();
+ go->Delete();
+
+ return true;
+ }
+};
+
+// 46430 - Synch Health
+class spell_ahune_synch_health : public SpellScriptLoader
+{
+public:
+ spell_ahune_synch_health() : SpellScriptLoader("spell_ahune_synch_health") { }
+
+ class spell_ahune_synch_health_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ahune_synch_health_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SYNCH_HEALTH))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ if (Unit* caster = GetCaster())
+ target->SetHealth(caster->GetHealth());
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ahune_synch_health_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ahune_synch_health_SpellScript();
+ }
+};
+
+// 45926 - Summoning Rhyme Aura
+class spell_summoning_rhyme_aura : public SpellScriptLoader
+{
+public:
+ spell_summoning_rhyme_aura() : SpellScriptLoader("spell_summoning_rhyme_aura") { }
+
+ class spell_summoning_rhyme_aura_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_summoning_rhyme_aura_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_FORCE_WHISP_FLIGHT) || !sSpellMgr->GetSpellInfo(SPELL_SUMMONING_RHYME_BONFIRE))
+ return false;
+ return true;
+ }
+
+ void PeriodicTick(AuraEffect const* aurEff)
+ {
+ Creature* caster = GetCaster()->ToCreature();
+ Player* player = GetTarget()->ToPlayer();
+
+ if (!caster || !player)
+ return;
+
+ player->CastSpell(player, SPELL_FORCE_WHISP_FLIGHT);
+
+ switch (aurEff->GetTickNumber())
+ {
+ case 1:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TEXT_1, NULL, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, player);
+ player->CastSpell(player, SPELL_SUMMONING_RHYME_BONFIRE, true);
+ break;
+ case 2:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TEXT_2, NULL, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, player);
+ break;
+ case 3:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TEXT_3, NULL, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, player);
+ Remove();
+ break;
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_summoning_rhyme_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_summoning_rhyme_aura_AuraScript();
+ }
+};
+
+// 46878 - Summon Ice Spear Delayer
+class spell_summon_ice_spear_delayer : public SpellScriptLoader
+{
+public:
+ spell_summon_ice_spear_delayer() : SpellScriptLoader("spell_summon_ice_spear_delayer") { }
+
+ class spell_summon_ice_spear_delayer_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_summon_ice_spear_delayer_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ICE_SPEAR_GO) || !sSpellMgr->GetSpellInfo(SPELL_ICE_SPEAR_KNOCKBACK))
+ return false;
+ return true;
+ }
+
+ 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;
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_summon_ice_spear_delayer_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_summon_ice_spear_delayer_AuraScript();
+ }
+};
+
+// 46371 - Ice Spear Control Aura
+class spell_ice_spear_control_aura : public SpellScriptLoader
+{
+public:
+ spell_ice_spear_control_aura() : SpellScriptLoader("spell_ice_spear_control_aura") { }
+
+ class spell_ice_spear_control_aura_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_ice_spear_control_aura_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ICE_SPEAR_TARGET_PICKER))
+ return false;
+ return true;
+ }
+
+ void PeriodicTick(AuraEffect const* /*aurEff*/)
+ {
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(caster, SPELL_ICE_SPEAR_TARGET_PICKER);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_ice_spear_control_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ice_spear_control_aura_AuraScript();
+ }
+};
+
+// 46372 - Ice Spear Target Picker
+class spell_ice_spear_target_picker : public SpellScriptLoader
+{
+public:
+ spell_ice_spear_target_picker() : SpellScriptLoader("spell_ice_spear_target_picker") { }
+
+ class spell_ice_spear_target_picker_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ice_spear_target_picker_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ICE_SPEAR_BUNNY))
+ return false;
+ return true;
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ICE_SPEAR_BUNNY, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ice_spear_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ice_spear_target_picker_SpellScript();
+ }
+};
+
+// 46320 - Spell Slippery Floor Periodic
+class spell_slippery_floor_periodic : public SpellScriptLoader
+{
+public:
+ spell_slippery_floor_periodic() : SpellScriptLoader("spell_slippery_floor_periodic") { }
+
+ class spell_slippery_floor_periodic_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_slippery_floor_periodic_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SLIPPERY_FLOOR_SLIP))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ if (target->isMoving())
+ target->CastSpell(target, SPELL_SLIPPERY_FLOOR_SLIP, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_slippery_floor_periodic_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_slippery_floor_periodic_SpellScript();
+ }
+};
+
+// 46146 - Ahune Spanky Hands
+class spell_ahune_spanky_hands : public SpellScriptLoader
+{
+public:
+ spell_ahune_spanky_hands() : SpellScriptLoader("spell_ahune_spanky_hands") { }
+
+ class spell_ahune_spanky_hands_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_ahune_spanky_hands_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_COLD_SLAP))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_COLD_SLAP, true);
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_ahune_spanky_hands_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ahune_spanky_hands_AuraScript();
+ }
+};
+
+class spell_ahune_minion_despawner : public SpellScriptLoader
+{
+public:
+ spell_ahune_minion_despawner() : SpellScriptLoader("spell_ahune_minion_despawner") { }
+
+ class spell_ahune_minion_despawner_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ahune_minion_despawner_SpellScript);
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (GetHitCreature())
+ GetHitCreature()->DespawnOrUnsummon();
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ahune_minion_despawner_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ahune_minion_despawner_SpellScript();
+ }
+};
+
+// 46398 - Spell Ice Bombardment Dest Picker
+class spell_ice_bombardment_dest_picker : public SpellScriptLoader
+{
+public:
+ spell_ice_bombardment_dest_picker() : SpellScriptLoader("spell_ice_bombardment_dest_picker") { }
+
+ class spell_ice_bombardment_dest_picker_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ice_bombardment_dest_picker_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ICE_BOMBARDMENT))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitDest()->GetPositionX(), GetHitDest()->GetPositionY(), GetHitDest()->GetPositionZ(), SPELL_ICE_BOMBARDMENT, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHit += SpellEffectFn(spell_ice_bombardment_dest_picker_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ice_bombardment_dest_picker_SpellScript();
+ }
+};
+
+void AddSC_boss_ahune()
+{
+ new boss_ahune();
+ new npc_frozen_core();
+ new npc_earthen_ring_flamecaller();
+ new npc_ahune_bunny();
+ new go_ahune_ice_stone();
+ new spell_ahune_synch_health();
+ new spell_summoning_rhyme_aura();
+ new spell_summon_ice_spear_delayer();
+ new spell_ice_spear_control_aura();
+ new spell_slippery_floor_periodic();
+ new spell_ahune_spanky_hands();
+ new spell_ahune_minion_despawner();
+ new spell_ice_spear_target_picker();
+ new spell_ice_bombardment_dest_picker();
+}
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp
index 87a07cd1e5e..0fac7b5a39d 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp
@@ -31,15 +31,125 @@ class instance_the_slave_pens : public InstanceMapScript
public:
instance_the_slave_pens() : InstanceMapScript(SPScriptName, 547) { }
+ struct instance_the_slave_pens_InstanceMapScript : public InstanceScript
+ {
+ instance_the_slave_pens_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ counter = DATA_FLAMECALLER_000;
+ }
+
+ void OnCreatureCreate(Creature* creature) override
+ {
+ switch (creature->GetEntry())
+ {
+ case NPC_AHUNE:
+ AhuneGUID = creature->GetGUID();
+ break;
+ case NPC_FROZEN_CORE:
+ FrozenCoreGUID = creature->GetGUID();
+ break;
+ case NPC_AHUNE_LOC_BUNNY:
+ AhuneBunnyGUID = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BONFIRE_BUNNY_000:
+ BonfireBunnyGUIDs[0] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BONFIRE_BUNNY_001:
+ BonfireBunnyGUIDs[1] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BONFIRE_BUNNY_002:
+ BonfireBunnyGUIDs[2] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BEAM_BUNNY_000:
+ BeamBunnyGUIDs[0] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BEAM_BUNNY_001:
+ BeamBunnyGUIDs[1] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BEAM_BUNNY_002:
+ BeamBunnyGUIDs[2] = creature->GetGUID();
+ break;
+ case NPC_LUMA_SKYMOTHER:
+ LumaGUID = creature->GetGUID();
+ break;
+ case NPC_EARTHEN_RING_FLAMECALLER:
+ SetGuidData(counter, creature->GetGUID());
+ ++counter;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SetGuidData(uint32 data, ObjectGuid guid) override
+ {
+ switch (data)
+ {
+ case DATA_FLAMECALLER_000:
+ FlameCallerGUIDs[0] = guid;
+ break;
+ case DATA_FLAMECALLER_001:
+ FlameCallerGUIDs[1] = guid;
+ break;
+ case DATA_FLAMECALLER_002:
+ FlameCallerGUIDs[2] = guid;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ObjectGuid GetGuidData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_AHUNE:
+ return AhuneGUID;
+ case DATA_AHUNE_BUNNY:
+ return AhuneBunnyGUID;
+ case DATA_FROZEN_CORE:
+ return FrozenCoreGUID;
+ case DATA_FLAMECALLER_000:
+ return FlameCallerGUIDs[0];
+ case DATA_FLAMECALLER_001:
+ return FlameCallerGUIDs[1];
+ case DATA_FLAMECALLER_002:
+ return FlameCallerGUIDs[2];
+ case DATA_BONFIRE_BUNNY_000:
+ return BonfireBunnyGUIDs[0];
+ case DATA_BONFIRE_BUNNY_001:
+ return BonfireBunnyGUIDs[1];
+ case DATA_BONFIRE_BUNNY_002:
+ return BonfireBunnyGUIDs[2];
+ case DATA_BEAM_BUNNY_000:
+ return BeamBunnyGUIDs[0];
+ case DATA_BEAM_BUNNY_001:
+ return BeamBunnyGUIDs[1];
+ case DATA_BEAM_BUNNY_002:
+ return BeamBunnyGUIDs[2];
+ case DATA_LUMA_SKYMOTHER:
+ return LumaGUID;
+ default:
+ break;
+ }
+ return ObjectGuid::Empty;
+ }
+
+ protected:
+ ObjectGuid AhuneGUID;
+ ObjectGuid AhuneBunnyGUID;
+ ObjectGuid FrozenCoreGUID;
+ ObjectGuid LumaGUID;
+ ObjectGuid FlameCallerGUIDs[3];
+ ObjectGuid BonfireBunnyGUIDs[3];
+ ObjectGuid BeamBunnyGUIDs[3];
+ uint8 counter;
+ };
+
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_the_slave_pens_InstanceMapScript(map);
}
-
- struct instance_the_slave_pens_InstanceMapScript : public InstanceScript
- {
- instance_the_slave_pens_InstanceMapScript(Map* map) : InstanceScript(map) { }
- };
};
void AddSC_instance_the_slave_pens()
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h
index 95e6e44121e..544e98fd206 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h
@@ -27,7 +27,43 @@ enum DataTypes
{
DATA_MENNU_THE_BETRAYER = 1,
DATA_ROKMAR_THE_CRACKLER = 2,
- DATA_QUAGMIRRAN = 3
+ DATA_QUAGMIRRAN = 3,
+ DATA_AHUNE = 4,
+ DATA_AHUNE_BUNNY = 5,
+ DATA_FROZEN_CORE = 6,
+ DATA_FLAMECALLER_000 = 7,
+ DATA_FLAMECALLER_001 = 8,
+ DATA_FLAMECALLER_002 = 9,
+ DATA_BONFIRE_BUNNY_000 = 10,
+ DATA_BONFIRE_BUNNY_001 = 11,
+ DATA_BONFIRE_BUNNY_002 = 12,
+ DATA_BEAM_BUNNY_000 = 13,
+ DATA_BEAM_BUNNY_001 = 14,
+ DATA_BEAM_BUNNY_002 = 15,
+ DATA_LUMA_SKYMOTHER = 16
+};
+
+enum CreaturesIds
+{
+ NPC_AHUNE = 25740,
+ NPC_FROZEN_CORE = 25865,
+ NPC_LUMA_SKYMOTHER = 25697,
+ NPC_AHUNE_LOC_BUNNY = 25745,
+ NPC_EARTHEN_RING_FLAMECALLER = 25754,
+ NPC_SHAMAN_BONFIRE_BUNNY_000 = 25971,
+ NPC_SHAMAN_BONFIRE_BUNNY_001 = 25972,
+ NPC_SHAMAN_BONFIRE_BUNNY_002 = 25973,
+ NPC_SHAMAN_BEAM_BUNNY_000 = 25964,
+ NPC_SHAMAN_BEAM_BUNNY_001 = 25965,
+ NPC_SHAMAN_BEAM_BUNNY_002 = 25966,
+ NPC_WHISP_DEST_BUNNY = 26120,
+ NPC_WHISP_SOURCE_BUNNY = 26121
+};
+
+enum GameObjectIds
+{
+ GO_ICE_SPEAR = 188077,
+ GO_ICE_STONE = 187882
};
#endif // SLAVE_PENS_H
diff --git a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp
index 4e20e6b0953..9b8220596c9 100644
--- a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp
+++ b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp
@@ -372,7 +372,7 @@ class boss_vazruden_the_herald : public CreatureScript
if (summon->GetEntry() == NPC_NAZAN)
{
summon->SetDisableGravity(true);
- summon->SetSpeed(MOVE_FLIGHT, 2.5f);
+ summon->SetSpeedRate(MOVE_FLIGHT, 2.5f);
if (victim)
AttackStartNoMove(victim);
}
diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp
index 499550945c6..2592ed3b262 100644
--- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp
+++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp
@@ -180,24 +180,23 @@ class boss_grand_warlock_nethekurse : public CreatureScript
}
void MoveInLineOfSight(Unit* who) override
-
{
if (!IntroOnce && me->IsWithinDistInMap(who, 30.0f))
- {
+ {
if (who->GetTypeId() != TYPEID_PLAYER)
return;
- Talk(SAY_INTRO);
- IntroOnce = true;
- IsIntroEvent = true;
+ Talk(SAY_INTRO);
+ IntroOnce = true;
+ IsIntroEvent = true;
- instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS);
- }
+ instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS);
+ }
- if (IsIntroEvent || !IsMainEvent)
- return;
+ if (IsIntroEvent || !IsMainEvent)
+ return;
- ScriptedAI::MoveInLineOfSight(who);
+ ScriptedAI::MoveInLineOfSight(who);
}
void EnterCombat(Unit* /*who*/) override
diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp
index 4faab709e16..c29d560529d 100644
--- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp
+++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp
@@ -109,7 +109,7 @@ class boss_warchief_kargath_bladefist : public CreatureScript
{
removeAdds();
_Reset();
- me->SetSpeed(MOVE_RUN, 2);
+ me->SetSpeedRate(MOVE_RUN, 2);
me->SetWalk(false);
Initialize();
@@ -231,7 +231,7 @@ class boss_warchief_kargath_bladefist : public CreatureScript
{
// stop bladedance
InBlade = false;
- me->SetSpeed(MOVE_RUN, 2);
+ me->SetSpeedRate(MOVE_RUN, 2);
me->GetMotionMaster()->MoveChase(me->GetVictim());
Blade_Dance_Timer = 30000;
Wait_Timer = 0;
@@ -264,7 +264,7 @@ class boss_warchief_kargath_bladefist : public CreatureScript
Wait_Timer = 1;
InBlade = true;
Blade_Dance_Timer = 0;
- me->SetSpeed(MOVE_RUN, 4);
+ me->SetSpeedRate(MOVE_RUN, 4);
return;
}
else
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp
index 56010c09897..20d96ecd29c 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp
@@ -130,7 +130,7 @@ class boss_alar : public CreatureScript
_Reset();
me->SetDisplayId(me->GetNativeDisplayId());
- me->SetSpeed(MOVE_RUN, DefaultMoveSpeedRate);
+ me->SetSpeedRate(MOVE_RUN, DefaultMoveSpeedRate);
//me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 10);
//me->SetFloatValue(UNIT_FIELD_COMBATREACH, 10);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true);
@@ -178,7 +178,7 @@ class boss_alar : public CreatureScript
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->AttackStop();
me->SetTarget(ObjectGuid::Empty);
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(0, waypoint[5][0], waypoint[5][1], waypoint[5][2]);
}
@@ -261,7 +261,7 @@ class boss_alar : public CreatureScript
case WE_REVIVE:
me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_STAND);
me->SetFullHealth();
- me->SetSpeed(MOVE_RUN, DefaultMoveSpeedRate);
+ me->SetSpeedRate(MOVE_RUN, DefaultMoveSpeedRate);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
DoZoneInCombat();
DoCast(me, SPELL_REBIRTH, true);
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
index 9fd1c5c7388..30b3fd67687 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
@@ -325,86 +325,85 @@ class boss_high_astromancer_solarian : public CreatureScript
else
Phase1_Timer-=diff;
}
- else
- if (Phase == 2)
+ else if (Phase == 2)
+ {
+ //10 seconds after Solarian disappears, 12 mobs spawn out of the three portals.
+ me->AttackStop();
+ me->StopMoving();
+ if (Phase2_Timer <= diff)
{
- //10 seconds after Solarian disappears, 12 mobs spawn out of the three portals.
- me->AttackStop();
- me->StopMoving();
- if (Phase2_Timer <= diff)
- {
- Phase = 3;
- for (int i=0; i <= 2; ++i)
- for (int j=1; j <= 4; j++)
- SummonMinion(NPC_SOLARIUM_AGENT, Portals[i][0], Portals[i][1], Portals[i][2]);
+ Phase = 3;
+ for (int i=0; i <= 2; ++i)
+ for (int j=1; j <= 4; j++)
+ SummonMinion(NPC_SOLARIUM_AGENT, Portals[i][0], Portals[i][1], Portals[i][2]);
- Talk(SAY_SUMMON1);
- Phase2_Timer = 10000;
- }
- else
- Phase2_Timer -= diff;
+ Talk(SAY_SUMMON1);
+ Phase2_Timer = 10000;
}
else
- if (Phase == 3)
- {
- me->AttackStop();
- me->StopMoving();
- //Check Phase3_Timer
- if (Phase3_Timer <= diff)
- {
- Phase = 1;
- //15 seconds later Solarian reappears out of one of the 3 portals. Simultaneously, 2 healers appear in the two other portals.
- int i = rand32() % 3;
- me->GetMotionMaster()->Clear();
- me->SetPosition(Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O);
-
- for (int j=0; j <= 2; j++)
- if (j != i)
- SummonMinion(NPC_SOLARIUM_PRIEST, Portals[j][0], Portals[j][1], Portals[j][2]);
-
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetVisible(true);
-
- Talk(SAY_SUMMON2);
- AppearDelay = true;
- Phase3_Timer = 15000;
- }
- else
- Phase3_Timer -= diff;
- }
- else
- if (Phase == 4)
- {
- //Fear_Timer
- if (Fear_Timer <= diff)
- {
- DoCast(me, SPELL_FEAR);
- Fear_Timer = 20000;
- }
- else
- Fear_Timer -= diff;
- //VoidBolt_Timer
- if (VoidBolt_Timer <= diff)
- {
- DoCastVictim(SPELL_VOID_BOLT);
- VoidBolt_Timer = 10000;
- }
- else
- VoidBolt_Timer -= diff;
- }
- //When Solarian reaches 20% she will transform into a huge void walker.
- if (Phase != 4 && me->HealthBelowPct(20))
- {
- Phase = 4;
- //To make sure she wont be invisible or not selecatble
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetVisible(true);
- Talk(SAY_VOIDA);
- Talk(SAY_VOIDB);
- me->SetArmor(WV_ARMOR);
- me->SetDisplayId(MODEL_VOIDWALKER);
- me->SetObjectScale(defaultsize*2.5f);
- }
+ Phase2_Timer -= diff;
+ }
+ else if (Phase == 3)
+ {
+ me->AttackStop();
+ me->StopMoving();
+ //Check Phase3_Timer
+ if (Phase3_Timer <= diff)
+ {
+ Phase = 1;
+ //15 seconds later Solarian reappears out of one of the 3 portals. Simultaneously, 2 healers appear in the two other portals.
+ int i = rand32() % 3;
+ me->GetMotionMaster()->Clear();
+ me->SetPosition(Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O);
+
+ for (int j=0; j <= 2; j++)
+ if (j != i)
+ SummonMinion(NPC_SOLARIUM_PRIEST, Portals[j][0], Portals[j][1], Portals[j][2]);
+
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetVisible(true);
+
+ Talk(SAY_SUMMON2);
+ AppearDelay = true;
+ Phase3_Timer = 15000;
+ }
+ else
+ Phase3_Timer -= diff;
+ }
+ else if (Phase == 4)
+ {
+ //Fear_Timer
+ if (Fear_Timer <= diff)
+ {
+ DoCast(me, SPELL_FEAR);
+ Fear_Timer = 20000;
+ }
+ else
+ Fear_Timer -= diff;
+ //VoidBolt_Timer
+ if (VoidBolt_Timer <= diff)
+ {
+ DoCastVictim(SPELL_VOID_BOLT);
+ VoidBolt_Timer = 10000;
+ }
+ else
+ VoidBolt_Timer -= diff;
+ }
+
+ //When Solarian reaches 20% she will transform into a huge void walker.
+ if (Phase != 4 && me->HealthBelowPct(20))
+ {
+ Phase = 4;
+ //To make sure she wont be invisible or not selecatble
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetVisible(true);
+ Talk(SAY_VOIDA);
+ Talk(SAY_VOIDB);
+ me->SetArmor(WV_ARMOR);
+ me->SetDisplayId(MODEL_VOIDWALKER);
+ me->SetObjectScale(defaultsize*2.5f);
+ }
+
DoMeleeAttackIfReady();
}
};
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp
index 46388c3a185..edfa2aedf92 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp
@@ -1,6 +1,5 @@
/*
* 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
@@ -16,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Boss_Void_Reaver
-SD%Complete: 90
-SDComment: Should reset if raid are out of room.
-SDCategory: Tempest Keep, The Eye
-EndScriptData */
-
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "the_eye.h"
@@ -43,10 +35,17 @@ enum Spells
SPELL_BERSERK = 27680
};
+enum Events
+{
+ EVENT_POUNDING = 1,
+ EVENT_ARCANE_ORB,
+ EVENT_KNOCK_AWAY,
+ EVENT_BERSERK
+};
+
class boss_void_reaver : public CreatureScript
{
public:
-
boss_void_reaver() : CreatureScript("boss_void_reaver") { }
struct boss_void_reaverAI : public BossAI
@@ -58,21 +57,9 @@ class boss_void_reaver : public CreatureScript
void Initialize()
{
- Pounding_Timer = 15000;
- ArcaneOrb_Timer = 3000;
- KnockAway_Timer = 30000;
- Berserk_Timer = 600000;
-
Enraged = false;
}
- uint32 Pounding_Timer;
- uint32 ArcaneOrb_Timer;
- uint32 KnockAway_Timer;
- uint32 Berserk_Timer;
-
- bool Enraged;
-
void Reset() override
{
Initialize();
@@ -95,71 +82,84 @@ class boss_void_reaver : public CreatureScript
{
Talk(SAY_AGGRO);
_EnterCombat();
+
+ events.ScheduleEvent(EVENT_POUNDING, 15000);
+ events.ScheduleEvent(EVENT_ARCANE_ORB, 3000);
+ events.ScheduleEvent(EVENT_KNOCK_AWAY, 30000);
+ events.ScheduleEvent(EVENT_BERSERK, 600000);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
- // Pounding
- if (Pounding_Timer <= diff)
- {
- DoCastVictim(SPELL_POUNDING);
- Talk(SAY_POUNDING);
- Pounding_Timer = 15000; //cast time(3000) + cooldown time(12000)
- }
- else
- Pounding_Timer -= diff;
- // Arcane Orb
- if (ArcaneOrb_Timer <= diff)
- {
- Unit* target = NULL;
- std::list<HostileReference*> t_list = me->getThreatManager().getThreatList();
- std::vector<Unit*> target_list;
- for (std::list<HostileReference*>::const_iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
- {
- target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
- if (!target)
- continue;
- // exclude pets & totems, 18 yard radius minimum
- if (target->GetTypeId() == TYPEID_PLAYER && target->IsAlive() && !target->IsWithinDist(me, 18, false))
- target_list.push_back(target);
- target = NULL;
- }
- if (!target_list.empty())
- target = *(target_list.begin() + rand32() % target_list.size());
- else
- target = me->GetVictim();
+ events.Update(diff);
- if (target)
- me->CastSpell(target, SPELL_ARCANE_ORB, false, NULL, NULL);
- ArcaneOrb_Timer = 3000;
- }
- else
- ArcaneOrb_Timer -= diff;
- // Single Target knock back, reduces aggro
- if (KnockAway_Timer <= diff)
- {
- DoCastVictim(SPELL_KNOCK_AWAY);
- //Drop 25% aggro
- if (DoGetThreat(me->GetVictim()))
- DoModifyThreatPercent(me->GetVictim(), -25);
- KnockAway_Timer = 30000;
- }
- else
- KnockAway_Timer -= diff;
- //Berserk
- if (Berserk_Timer < diff && !Enraged)
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- DoCast(me, SPELL_BERSERK);
- Enraged = true;
+ switch (eventId)
+ {
+ case EVENT_POUNDING:
+ DoCastVictim(SPELL_POUNDING);
+ Talk(SAY_POUNDING);
+ events.ScheduleEvent(EVENT_POUNDING, 15000);
+ break;
+ case EVENT_ARCANE_ORB:
+ {
+ Unit* target = NULL;
+ std::list<HostileReference*> t_list = me->getThreatManager().getThreatList();
+ std::vector<Unit*> target_list;
+ for (std::list<HostileReference*>::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr)
+ {
+ target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
+ if (!target)
+ continue;
+ // exclude pets & totems, 18 yard radius minimum
+ if (target->GetTypeId() == TYPEID_PLAYER && target->IsAlive() && !target->IsWithinDist(me, 18, false))
+ target_list.push_back(target);
+ target = NULL;
+ }
+
+ if (!target_list.empty())
+ target = *(target_list.begin() + rand32() % target_list.size());
+ else
+ target = me->GetVictim();
+
+ if (target)
+ me->CastSpell(target, SPELL_ARCANE_ORB, false, NULL, NULL);
+
+ events.ScheduleEvent(EVENT_ARCANE_ORB, 3000);
+ break;
+ }
+ case EVENT_KNOCK_AWAY:
+ DoCastVictim(SPELL_KNOCK_AWAY);
+ // Drop 25% aggro
+ if (DoGetThreat(me->GetVictim()))
+ DoModifyThreatPercent(me->GetVictim(), -25);
+
+ events.ScheduleEvent(EVENT_KNOCK_AWAY, 30000);
+ break;
+ case EVENT_BERSERK:
+ if (!Enraged)
+ {
+ DoCast(me, SPELL_BERSERK);
+ Enraged = true;
+ }
+ break;
+ default:
+ break;
+ }
}
- else
- Berserk_Timer -= diff;
DoMeleeAttackIfReady();
}
+
+ private:
+ bool Enraged;
};
CreatureAI* GetAI(Creature* creature) const override
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
index 3aa2674aec6..d45e17bd28d 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
@@ -176,7 +176,7 @@ class npc_ragin_flames : public CreatureScript
Initialize();
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true);
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true);
- me->SetSpeed(MOVE_RUN, DUNGEON_MODE(0.5f, 0.7f));
+ me->SetSpeedRate(MOVE_RUN, DUNGEON_MODE(0.5f, 0.7f));
}
void EnterCombat(Unit* /*who*/) override
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 856649c6c5e..e64c0fe9f5e 100644
--- a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
+++ b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
@@ -167,7 +167,7 @@ class boss_warp_splinter : public CreatureScript
{
Initialize();
- me->SetSpeed(MOVE_RUN, 0.7f, true);
+ me->SetSpeedRate(MOVE_RUN, 0.7f);
}
void EnterCombat(Unit* /*who*/) override
diff --git a/src/server/scripts/Outland/outland_script_loader.cpp b/src/server/scripts/Outland/outland_script_loader.cpp
index 91ba4e5689f..ed2c21da6c3 100644
--- a/src/server/scripts/Outland/outland_script_loader.cpp
+++ b/src/server/scripts/Outland/outland_script_loader.cpp
@@ -72,6 +72,7 @@ void AddSC_instance_the_slave_pens();
void AddSC_boss_mennu_the_betrayer();
void AddSC_boss_rokmar_the_crackler();
void AddSC_boss_quagmirran();
+void AddSC_boss_ahune();
// Coilfang Reservoir - The Underbog
void AddSC_instance_the_underbog();
@@ -193,6 +194,7 @@ void AddOutlandScripts()
AddSC_boss_mennu_the_betrayer();
AddSC_boss_rokmar_the_crackler();
AddSC_boss_quagmirran();
+ AddSC_boss_ahune();
// Coilfang Reservoir - The Underbog
AddSC_instance_the_underbog();
diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp
index 404cdc7ceb2..668c484dd63 100644
--- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp
+++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp
@@ -159,7 +159,7 @@ public:
else
TC_LOG_ERROR("scripts", "TRINITY: npc_ancestral_wolf can not obtain owner or owner is not a player.");
- creature->SetSpeed(MOVE_WALK, 1.5f);
+ creature->SetSpeedRate(MOVE_WALK, 1.5f);
Reset();
}
@@ -192,7 +192,7 @@ public:
if (ryga->IsAlive() && !ryga->IsInCombat())
{
ryga->SetWalk(true);
- ryga->SetSpeed(MOVE_WALK, 1.5f);
+ ryga->SetSpeedRate(MOVE_WALK, 1.5f);
ryga->GetMotionMaster()->MovePoint(0, 517.340698f, 3885.03975f, 190.455978f, true);
Reset();
}
@@ -778,7 +778,7 @@ public:
me->AddAura(SPELL_JULES_THREATENS_AURA, me);
me->SetCanFly(true);
- me->SetSpeed(MOVE_RUN, 0.2f);
+ me->SetSpeedRate(MOVE_RUN, 0.2f);
me->SetFacingTo(3.207566f);
me->GetMotionMaster()->MoveJump(exorcismPos[2], 2.0f, 2.0f);
@@ -798,7 +798,7 @@ public:
break;
case ACTION_JULES_MOVE_HOME:
wpreached = false;
- me->SetSpeed(MOVE_RUN, 1.0f);
+ me->SetSpeedRate(MOVE_RUN, 1.0f);
me->GetMotionMaster()->MovePoint(11, exorcismPos[2]);
events.CancelEvent(EVENT_SUMMON_SKULL);
diff --git a/src/server/scripts/Outland/zone_shattrath_city.cpp b/src/server/scripts/Outland/zone_shattrath_city.cpp
index c734d000077..e4e51a98348 100644
--- a/src/server/scripts/Outland/zone_shattrath_city.cpp
+++ b/src/server/scripts/Outland/zone_shattrath_city.cpp
@@ -29,7 +29,6 @@ npc_salsalabim
npc_shattrathflaskvendors
npc_zephyr
npc_kservant
-npc_ishanah
EndContentData */
#include "ScriptMgr.h"
@@ -43,13 +42,15 @@ EndContentData */
## npc_raliq_the_drunk
######*/
-#define GOSSIP_RALIQ "You owe Sim'salabim money. Hand them over or die!"
-
-enum Raliq
+enum RaliqTheDrunk
{
- SPELL_UPPERCUT = 10966,
- QUEST_CRACK_SKULLS = 10009,
- FACTION_HOSTILE_RD = 45
+ SAY_RALIQ_ATTACK = 0,
+ OPTION_ID_COLLECT_A_DEBT = 0,
+ FACTION_OGRE_HOSTILE = 45,
+ MENU_ID_COLLECT_A_DEBT = 7729,
+ NPC_TEXT_WUT_YOU_WANT = 9440,
+ CRACKIN_SOME_SKULLS = 10009,
+ SPELL_UPPERCUT = 10966
};
class npc_raliq_the_drunk : public CreatureScript
@@ -63,7 +64,8 @@ public:
if (action == GOSSIP_ACTION_INFO_DEF+1)
{
player->CLOSE_GOSSIP_MENU();
- creature->setFaction(FACTION_HOSTILE_RD);
+ creature->setFaction(FACTION_OGRE_HOSTILE);
+ creature->AI()->Talk(SAY_RALIQ_ATTACK, player);
creature->AI()->AttackStart(player);
}
return true;
@@ -71,10 +73,16 @@ public:
bool OnGossipHello(Player* player, Creature* creature) override
{
- if (player->GetQuestStatus(QUEST_CRACK_SKULLS) == QUEST_STATUS_INCOMPLETE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_RALIQ, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
-
- player->SEND_GOSSIP_MENU(9440, creature->GetGUID());
+ if (player->GetQuestStatus(CRACKIN_SOME_SKULLS) == QUEST_STATUS_INCOMPLETE)
+ {
+ player->ADD_GOSSIP_ITEM_DB(MENU_ID_COLLECT_A_DEBT, OPTION_ID_COLLECT_A_DEBT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+ player->SEND_GOSSIP_MENU(NPC_TEXT_WUT_YOU_WANT, creature->GetGUID());
+ }
+ else
+ {
+ player->PlayerTalkClass->ClearMenus();
+ player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
+ }
return true;
}
@@ -125,16 +133,14 @@ public:
enum Salsalabim
{
- // Factions
- FACTION_HOSTILE_SA = 90,
- FACTION_FRIENDLY_SA = 35,
-
- // Quests
- QUEST_10004 = 10004,
-
- // Spells
- SPELL_MAGNETIC_PULL = 31705
-
+ SAY_DEMONIC_AGGRO = 0,
+ OPTION_ID_ALTRUIS_SENT_ME = 0,
+ FACTION_FRIENDLY = 35,
+ FACTION_DEMON_HOSTILE = 90,
+ MENU_ID_ALTRUIS_SENT_ME = 7725,
+ NPC_TEXT_SAL_GROWLS_AT_YOU = 9435,
+ PATIENCE_AND_UNDERSTANDING = 10004,
+ SPELL_MAGNETIC_PULL = 31705
};
class npc_salsalabim : public CreatureScript
@@ -142,13 +148,26 @@ class npc_salsalabim : public CreatureScript
public:
npc_salsalabim() : CreatureScript("npc_salsalabim") { }
- bool OnGossipHello(Player* player, Creature* creature) override
+ bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
- if (player->GetQuestStatus(QUEST_10004) == QUEST_STATUS_INCOMPLETE)
+ player->PlayerTalkClass->ClearMenus();
+ if (action == GOSSIP_ACTION_INFO_DEF+1)
{
- creature->setFaction(FACTION_HOSTILE_SA);
+ player->CLOSE_GOSSIP_MENU();
+ creature->setFaction(FACTION_DEMON_HOSTILE);
+ creature->AI()->Talk(SAY_DEMONIC_AGGRO, player);
creature->AI()->AttackStart(player);
}
+ return true;
+ }
+
+ bool OnGossipHello(Player* player, Creature* creature) override
+ {
+ if (player->GetQuestStatus(PATIENCE_AND_UNDERSTANDING) == QUEST_STATUS_INCOMPLETE)
+ {
+ player->ADD_GOSSIP_ITEM_DB(MENU_ID_ALTRUIS_SENT_ME, OPTION_ID_ALTRUIS_SENT_ME, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+ player->SEND_GOSSIP_MENU(NPC_TEXT_SAL_GROWLS_AT_YOU, creature->GetGUID());
+ }
else
{
if (creature->IsQuestGiver())
@@ -187,7 +206,7 @@ public:
{
if (done_by->GetTypeId() == TYPEID_PLAYER && me->HealthBelowPctDamaged(20, damage))
{
- done_by->ToPlayer()->GroupEventHappens(QUEST_10004, me);
+ done_by->ToPlayer()->GroupEventHappens(PATIENCE_AND_UNDERSTANDING, me);
damage = 0;
EnterEvadeMode();
}
@@ -220,6 +239,19 @@ Purchase requires exalted reputation with Scryers/Aldor, Cenarion Expedition and
##################################################
*/
+enum FlaskVendors
+{
+ ALDOR_REPUTATION = 932,
+ SCRYERS_REPUTATION = 934,
+ THE_SHA_TAR_REPUTATION = 935,
+ CENARION_EXPEDITION_REP = 942,
+ NPC_TEXT_EXALTED_ALDOR = 11083, // (need to be exalted with Sha'tar, Cenarion Expedition and the Aldor)
+ NPC_TEXT_EXALTED_SCRYERS = 11084, // (need to be exalted with Sha'tar, Cenarion Expedition and the Scryers)
+ NPC_TEXT_WELCOME_NAME = 11085, // Access granted
+ ARCANIST_XORITH = 23483, // Scryer Apothecary
+ HALDOR_THE_COMPULSIVE = 23484 // Aldor Apothecary
+};
+
class npc_shattrathflaskvendors : public CreatureScript
{
public:
@@ -236,31 +268,31 @@ public:
bool OnGossipHello(Player* player, Creature* creature) override
{
- if (creature->GetEntry() == 23484)
+ if (creature->GetEntry() == HALDOR_THE_COMPULSIVE)
{
// Aldor vendor
- if (creature->IsVendor() && (player->GetReputationRank(932) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED))
+ if (creature->IsVendor() && (player->GetReputationRank(ALDOR_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(THE_SHA_TAR_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(CENARION_EXPEDITION_REP) == REP_EXALTED))
{
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
- player->SEND_GOSSIP_MENU(11085, creature->GetGUID());
+ player->SEND_GOSSIP_MENU(NPC_TEXT_WELCOME_NAME, creature->GetGUID());
}
else
{
- player->SEND_GOSSIP_MENU(11083, creature->GetGUID());
+ player->SEND_GOSSIP_MENU(NPC_TEXT_EXALTED_ALDOR, creature->GetGUID());
}
}
- if (creature->GetEntry() == 23483)
+ if (creature->GetEntry() == ARCANIST_XORITH)
{
// Scryers vendor
- if (creature->IsVendor() && (player->GetReputationRank(934) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED))
+ if (creature->IsVendor() && (player->GetReputationRank(SCRYERS_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(THE_SHA_TAR_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(CENARION_EXPEDITION_REP) == REP_EXALTED))
{
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
- player->SEND_GOSSIP_MENU(11085, creature->GetGUID());
+ player->SEND_GOSSIP_MENU(NPC_TEXT_WELCOME_NAME, creature->GetGUID());
}
else
{
- player->SEND_GOSSIP_MENU(11084, creature->GetGUID());
+ player->SEND_GOSSIP_MENU(NPC_TEXT_EXALTED_SCRYERS, creature->GetGUID());
}
}
@@ -272,7 +304,13 @@ public:
# npc_zephyr
######*/
-#define GOSSIP_HZ "Take me to the Caverns of Time."
+enum Zephyr
+{
+ OPTION_ID_TAKE_ME_TO_C_O_T = 0,
+ KEEPERS_OF_TIME_REPUTATION = 989,
+ MENU_ID_TAKE_ME_TO_C_O_T = 9205,
+ TELEPORT_CAVERNS_OF_TIME = 37778
+};
class npc_zephyr : public CreatureScript
{
@@ -283,15 +321,15 @@ public:
{
player->PlayerTalkClass->ClearMenus();
if (action == GOSSIP_ACTION_INFO_DEF+1)
- player->CastSpell(player, 37778, false);
+ player->CastSpell(player, TELEPORT_CAVERNS_OF_TIME, false);
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
- if (player->GetReputationRank(989) >= REP_REVERED)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HZ, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+ if (player->GetReputationRank(KEEPERS_OF_TIME_REPUTATION) >= REP_REVERED)
+ player->ADD_GOSSIP_ITEM_DB(MENU_ID_TAKE_ME_TO_C_O_T, OPTION_ID_TAKE_ME_TO_C_O_T, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
@@ -305,28 +343,29 @@ public:
enum KServant
{
- SAY1 = 0,
- WHISP1 = 1,
- WHISP2 = 2,
- WHISP3 = 3,
- WHISP4 = 4,
- WHISP5 = 5,
- WHISP6 = 6,
- WHISP7 = 7,
- WHISP8 = 8,
- WHISP9 = 9,
- WHISP10 = 10,
- WHISP11 = 11,
- WHISP12 = 12,
- WHISP13 = 13,
- WHISP14 = 14,
- WHISP15 = 15,
- WHISP16 = 16,
- WHISP17 = 17,
- WHISP18 = 18,
- WHISP19 = 19,
- WHISP20 = 20,
- WHISP21 = 21
+ SAY1 = 0,
+ WHISP1 = 1,
+ WHISP2 = 2,
+ WHISP3 = 3,
+ WHISP4 = 4,
+ WHISP5 = 5,
+ WHISP6 = 6,
+ WHISP7 = 7,
+ WHISP8 = 8,
+ WHISP9 = 9,
+ WHISP10 = 10,
+ WHISP11 = 11,
+ WHISP12 = 12,
+ WHISP13 = 13,
+ WHISP14 = 14,
+ WHISP15 = 15,
+ WHISP16 = 16,
+ WHISP17 = 17,
+ WHISP18 = 18,
+ WHISP19 = 19,
+ WHISP20 = 20,
+ WHISP21 = 21,
+ CITY_OF_LIGHT = 10211
};
class npc_kservant : public CreatureScript
@@ -417,7 +456,7 @@ public:
break;
case 56:
Talk(WHISP21, player);
- player->GroupEventHappens(10211, me);
+ player->GroupEventHappens(CITY_OF_LIGHT, me);
break;
}
}
@@ -429,7 +468,7 @@ public:
return;
Player* player = who->ToPlayer();
- if (player && player->GetQuestStatus(10211) == QUEST_STATUS_INCOMPLETE)
+ if (player && player->GetQuestStatus(CITY_OF_LIGHT) == QUEST_STATUS_INCOMPLETE)
{
float Radius = 10.0f;
if (me->IsWithinDistInMap(who, Radius))
@@ -443,43 +482,6 @@ public:
};
};
-/*######
-# npc_ishanah
-######*/
-
-#define ISANAH_GOSSIP_1 "Who are the Sha'tar?"
-#define ISANAH_GOSSIP_2 "Isn't Shattrath a draenei city? Why do you allow others here?"
-
-class npc_ishanah : public CreatureScript
-{
-public:
- npc_ishanah() : CreatureScript("npc_ishanah") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF+1)
- player->SEND_GOSSIP_MENU(9458, creature->GetGUID());
- else if (action == GOSSIP_ACTION_INFO_DEF+2)
- player->SEND_GOSSIP_MENU(9459, creature->GetGUID());
-
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (creature->IsQuestGiver())
- player->PrepareQuestMenu(creature->GetGUID());
-
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, ISANAH_GOSSIP_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, ISANAH_GOSSIP_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
-
- return true;
- }
-};
-
void AddSC_shattrath_city()
{
new npc_raliq_the_drunk();
@@ -487,5 +489,4 @@ void AddSC_shattrath_city()
new npc_shattrathflaskvendors();
new npc_zephyr();
new npc_kservant();
- new npc_ishanah();
}
diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp
index 80b3a00774b..113b14a0d54 100644
--- a/src/server/scripts/Pet/pet_dk.cpp
+++ b/src/server/scripts/Pet/pet_dk.cpp
@@ -103,8 +103,8 @@ class npc_pet_dk_ebon_gargoyle : public CreatureScript
//! HACK: Creature's can't have MOVEMENTFLAG_FLYING
// Fly Away
me->SetCanFly(true);
- me->SetSpeed(MOVE_FLIGHT, 0.75f, true);
- me->SetSpeed(MOVE_RUN, 0.75f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.75f);
+ me->SetSpeedRate(MOVE_RUN, 0.75f);
float x = me->GetPositionX() + 20 * std::cos(me->GetOrientation());
float y = me->GetPositionY() + 20 * std::sin(me->GetOrientation());
float z = me->GetPositionZ() + 40;
diff --git a/src/server/scripts/Pet/pet_generic.cpp b/src/server/scripts/Pet/pet_generic.cpp
index 0ec6f08ae58..ff57bc0415c 100644
--- a/src/server/scripts/Pet/pet_generic.cpp
+++ b/src/server/scripts/Pet/pet_generic.cpp
@@ -21,9 +21,10 @@
*/
/* ContentData
- npc_pet_gen_egbert 100% Egbert run's around
- npc_pet_gen_pandaren_monk 100% Pandaren Monk drinks and bows with you
- npc_pet_gen_mojo 100% Mojo follows you when you kiss it
+ npc_pet_gen_baby_blizzard_bear 100% Baby Blizzard Bear sits down occasionally
+ npc_pet_gen_egbert 100% Egbert run's around
+ npc_pet_gen_pandaren_monk 100% Pandaren Monk drinks and bows with you
+ npc_pet_gen_mojo 100% Mojo follows you when you kiss it
EndContentData */
#include "ScriptMgr.h"
@@ -31,6 +32,63 @@
#include "PassiveAI.h"
#include "Player.h"
+enum BabyBlizzardBearMisc
+{
+ SPELL_BBB_PET_SIT = 61853,
+ EVENT_BBB_PET_SIT = 1,
+ EVENT_BBB_PET_SIT_INTER = 2
+};
+
+class npc_pet_gen_baby_blizzard_bear : public CreatureScript
+{
+public:
+ npc_pet_gen_baby_blizzard_bear() : CreatureScript("npc_pet_gen_baby_blizzard_bear") {}
+
+ struct npc_pet_gen_baby_blizzard_bearAI : public NullCreatureAI
+ {
+ npc_pet_gen_baby_blizzard_bearAI(Creature* creature) : NullCreatureAI(creature)
+ {
+ if (Unit* owner = me->GetCharmerOrOwner())
+ me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle());
+ _events.ScheduleEvent(EVENT_BBB_PET_SIT, urandms(10, 30));
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ if (Unit* owner = me->GetCharmerOrOwner())
+ if (!me->IsWithinDist(owner, 25.f))
+ me->InterruptSpell(CURRENT_CHANNELED_SPELL);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_BBB_PET_SIT:
+ me->CastSpell(me, SPELL_BBB_PET_SIT, false);
+ _events.ScheduleEvent(EVENT_BBB_PET_SIT_INTER, urandms(15, 30));
+ break;
+ case EVENT_BBB_PET_SIT_INTER:
+ me->InterruptSpell(CURRENT_CHANNELED_SPELL);
+ _events.ScheduleEvent(EVENT_BBB_PET_SIT, urandms(10, 30));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ EventMap _events;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_pet_gen_baby_blizzard_bearAI(creature);
+ }
+};
+
enum EgbertMisc
{
SPELL_EGBERT = 40669,
@@ -260,6 +318,7 @@ class npc_pet_gen_mojo : public CreatureScript
void AddSC_generic_pet_scripts()
{
+ new npc_pet_gen_baby_blizzard_bear();
new npc_pet_gen_egbert();
new npc_pet_gen_pandaren_monk();
new npc_pet_gen_mojo();
diff --git a/src/server/scripts/ScriptLoader.cpp b/src/server/scripts/ScriptLoader.cpp
deleted file mode 100644
index 57c848d5981..00000000000
--- a/src/server/scripts/ScriptLoader.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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 "ScriptLoader.h"
-#include "World.h"
-
-void AddSpellsScripts();
-void AddCommandsScripts();
-
-#ifdef SCRIPTS
-void AddWorldScripts();
-void AddEasternKingdomsScripts();
-void AddKalimdorScripts();
-void AddOutlandScripts();
-void AddNorthrendScripts();
-void AddEventsScripts();
-void AddPetScripts();
-void AddOutdoorPvPScripts();
-void AddCustomScripts();
-#endif
-
-void AddScripts()
-{
- AddSpellsScripts();
- AddCommandsScripts();
-#ifdef SCRIPTS
- AddWorldScripts();
- AddEasternKingdomsScripts();
- AddKalimdorScripts();
- AddOutlandScripts();
- AddNorthrendScripts();
- AddEventsScripts();
- AddPetScripts();
- AddOutdoorPvPScripts();
- AddCustomScripts();
-#endif
-}
diff --git a/src/server/scripts/ScriptLoader.cpp.in.cmake b/src/server/scripts/ScriptLoader.cpp.in.cmake
new file mode 100644
index 00000000000..33c336a9a93
--- /dev/null
+++ b/src/server/scripts/ScriptLoader.cpp.in.cmake
@@ -0,0 +1,64 @@
+/*
+ * 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/>.
+ */
+
+// This file was created automatically from your script configuration!
+// Use CMake to reconfigure this file, never change it on your own!
+
+#cmakedefine TRINITY_IS_DYNAMIC_SCRIPTLOADER
+
+#include "Define.h"
+#include <vector>
+#include <string>
+
+@TRINITY_SCRIPTS_FORWARD_DECL@
+#ifdef TRINITY_IS_DYNAMIC_SCRIPTLOADER
+# include "revision_data.h"
+# define TC_SCRIPT_API TC_API_EXPORT
+extern "C" {
+
+/// Exposed in script modules to return the script module revision hash.
+TC_SCRIPT_API char const* GetScriptModuleRevisionHash()
+{
+ return _HASH;
+}
+
+/// Exposed in script module to return the name of the script module
+/// contained in this shared library.
+TC_SCRIPT_API char const* GetScriptModule()
+{
+ return "@TRINITY_CURRENT_SCRIPT_PROJECT@";
+}
+
+#else
+# include "ScriptLoader.h"
+# define TC_SCRIPT_API
+#endif
+
+/// Exposed in script modules to register all scripts to the ScriptMgr.
+TC_SCRIPT_API void AddScripts()
+{
+@TRINITY_SCRIPTS_INVOKE@}
+
+/// Exposed in script modules to get the build directive of the module.
+TC_SCRIPT_API char const* GetBuildDirective()
+{
+ return _BUILD_DIRECTIVE;
+}
+
+#ifdef TRINITY_IS_DYNAMIC_SCRIPTLOADER
+} // extern "C"
+#endif
diff --git a/src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp b/src/server/scripts/ScriptPCH.cpp
index 41fecf3c60d..41fecf3c60d 100644
--- a/src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp
+++ b/src/server/scripts/ScriptPCH.cpp
diff --git a/src/server/scripts/PrecompiledHeaders/ScriptPCH.h b/src/server/scripts/ScriptPCH.h
index 1cd25309055..1cd25309055 100644
--- a/src/server/scripts/PrecompiledHeaders/ScriptPCH.h
+++ b/src/server/scripts/ScriptPCH.h
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index 7c2bb0bfaa5..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,
@@ -65,8 +73,6 @@ enum DeathKnightSpells
SPELL_DK_UNHOLY_PRESENCE_TRIGGERED = 49772,
SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189,
SPELL_DK_WILL_OF_THE_NECROPOLIS_AURA_R1 = 52284,
- SPELL_DK_RAISE_ALLY_INITIAL = 61999,
- SPELL_DK_RAISE_ALLY = 46619,
SPELL_DK_GHOUL_THRASH = 47480
};
@@ -80,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
{
@@ -228,7 +369,7 @@ class spell_dk_anti_magic_zone : public SpellScriptLoader
void CalculateAmount(AuraEffect const* /*aurEff*/, int32 & amount, bool & /*canBeRecalculated*/)
{
- SpellInfo const* talentSpell = sSpellMgr->EnsureSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT);
+ SpellInfo const* talentSpell = sSpellMgr->AssertSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT);
amount = talentSpell->Effects[EFFECT_0].CalcValue(GetCaster());
if (Player* player = GetCaster()->ToPlayer())
amount += int32(2 * player->GetTotalAttackPowerValue(BASE_ATTACK));
@@ -921,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
{
@@ -1636,7 +1823,7 @@ class spell_dk_will_of_the_necropolis : public SpellScriptLoader
{
// min pct of hp is stored in effect 0 of talent spell
uint8 rank = GetSpellInfo()->GetRank();
- SpellInfo const* talentProto = sSpellMgr->EnsureSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1, rank));
+ SpellInfo const* talentProto = sSpellMgr->AssertSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1, rank));
int32 remainingHp = int32(GetTarget()->GetHealth() - dmgInfo.GetDamage());
int32 minHp = int32(GetTarget()->CountPctFromMaxHealth(talentProto->Effects[EFFECT_0].CalcValue(GetCaster())));
@@ -1733,36 +1920,39 @@ public:
{
PrepareSpellScript(spell_dk_raise_ally_initial_SpellScript);
- bool Validate(SpellInfo const* /*spellInfo*/) override
+ bool Validate(SpellInfo const* spellInfo) override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_DK_RAISE_ALLY_INITIAL))
+ if (!sSpellMgr->GetSpellInfo(uint32(spellInfo->Effects[EFFECT_0].CalcValue())))
return false;
return true;
}
+ bool Load() override
+ {
+ return GetCaster()->GetTypeId() == TYPEID_PLAYER;
+ }
+
SpellCastResult CheckCast()
{
- // Raise Ally cannot be casted on alive players
Unit* target = GetExplTargetUnit();
if (!target)
return SPELL_FAILED_NO_VALID_TARGETS;
if (target->IsAlive())
return SPELL_FAILED_TARGET_NOT_DEAD;
- if (Player* playerCaster = GetCaster()->ToPlayer())
- if (playerCaster->InArena())
- return SPELL_FAILED_NOT_IN_ARENA;
if (target->IsGhouled())
return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW;
-
return SPELL_CAST_OK;
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
- Player* caster = GetCaster()->ToPlayer();
- Player* target = GetHitPlayer();
- if (caster && target)
- caster->SendGhoulResurrectRequest(target);
+ if (Player* target = GetHitPlayer())
+ {
+ if (target->IsResurrectRequested()) // already have one active request
+ return;
+ target->SetResurrectRequestData(GetCaster(), 0, 0, uint32(GetEffectValue()));
+ GetSpell()->SendResurrectRequest(target);
+ }
}
void Register() override
@@ -1785,12 +1975,8 @@ class player_ghoulAI : public PlayerAI
void UpdateAI(uint32 /*diff*/) override
{
- if (Creature* ghoul = ObjectAccessor::GetCreature(*me, _ghoulGUID))
- {
- if (!ghoul->IsAlive())
- me->RemoveAura(SPELL_DK_RAISE_ALLY);
- }
- else
+ Creature* ghoul = ObjectAccessor::GetCreature(*me, _ghoulGUID);
+ if (!ghoul || !ghoul->IsAlive())
me->RemoveAura(SPELL_DK_RAISE_ALLY);
}
@@ -1799,28 +1985,25 @@ class player_ghoulAI : public PlayerAI
};
// 46619 - Raise Ally
+#define DkRaiseAllyScriptName "spell_dk_raise_ally"
class spell_dk_raise_ally : public SpellScriptLoader
{
public:
- spell_dk_raise_ally() : SpellScriptLoader("spell_dk_raise_ally") { }
+ spell_dk_raise_ally() : SpellScriptLoader(DkRaiseAllyScriptName) { }
class spell_dk_raise_ally_SpellScript : public SpellScript
{
PrepareSpellScript(spell_dk_raise_ally_SpellScript);
- bool Validate(SpellInfo const* /*spellInfo*/) override
+ bool Load() override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_DK_RAISE_ALLY))
- return false;
- return true;
+ return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
void SendText()
{
- Player* caster = GetCaster()->ToPlayer();
- Unit* original = GetOriginalCaster();
- if (caster && original)
- original->Whisper(TEXT_RISE_ALLY, caster, true);
+ if (Unit* original = GetOriginalCaster())
+ original->Whisper(TEXT_RISE_ALLY, GetCaster()->ToPlayer(), true);
}
void HandleSummon(SpellEffIndex effIndex)
@@ -1839,9 +2022,8 @@ public:
SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(829);
uint32 duration = uint32(GetSpellInfo()->GetDuration());
- Position pos = caster->GetPosition();
- TempSummon* summon = originalCaster->GetMap()->SummonCreature(entry, pos, properties, duration, originalCaster, GetSpellInfo()->Id);
+ TempSummon* summon = originalCaster->GetMap()->SummonCreature(entry, *GetHitDest(), properties, duration, originalCaster, GetSpellInfo()->Id);
if (!summon)
return;
@@ -1868,15 +2050,25 @@ public:
// SMSG_POWER_UPDATE is sent
summon->SetMaxPower(POWER_ENERGY, 100);
- if (Player* player = GetCaster()->ToPlayer())
- player->SetGhoulResurrectGhoulGUID(summon->GetGUID());
+ _ghoulGuid = summon->GetGUID();
+ }
+
+ void SetGhoul(SpellEffIndex /*effIndex*/)
+ {
+ if (Aura* aura = GetHitAura())
+ if (spell_dk_raise_ally_AuraScript* script = dynamic_cast<spell_dk_raise_ally_AuraScript*>(aura->GetScriptByName(DkRaiseAllyScriptName)))
+ script->SetGhoulGuid(_ghoulGuid);
}
void Register() override
{
AfterHit += SpellHitFn(spell_dk_raise_ally_SpellScript::SendText);
OnEffectHit += SpellEffectFn(spell_dk_raise_ally_SpellScript::HandleSummon, EFFECT_0, SPELL_EFFECT_SUMMON);
+ OnEffectHitTarget += SpellEffectFn(spell_dk_raise_ally_SpellScript::SetGhoul, EFFECT_1, SPELL_EFFECT_APPLY_AURA);
}
+
+ private:
+ ObjectGuid _ghoulGuid;
};
SpellScript* GetSpellScript() const override
@@ -1895,31 +2087,32 @@ public:
oldAIState = false;
}
+ void SetGhoulGuid(ObjectGuid guid)
+ {
+ ghoulGuid = guid;
+ }
+
private:
- bool Validate(SpellInfo const* /*spellInfo*/) override
+ bool Load() override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_DK_RAISE_ALLY))
- return false;
- return true;
+ return GetUnitOwner()->GetTypeId() == TYPEID_PLAYER;
}
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
Player* player = GetTarget()->ToPlayer();
- if (!player || player->GetGhoulResurrectGhoulGUID().IsEmpty())
+ if (ghoulGuid.IsEmpty())
return;
oldAI = player->AI();
oldAIState = player->IsAIEnabled;
- player->SetAI(new player_ghoulAI(player, player->GetGhoulResurrectGhoulGUID()));
+ player->SetAI(new player_ghoulAI(player, ghoulGuid));
player->IsAIEnabled = true;
}
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
Player* player = GetTarget()->ToPlayer();
- if (!player)
- return;
player->IsAIEnabled = oldAIState;
PlayerAI* thisAI = player->AI();
@@ -1927,13 +2120,12 @@ public:
delete thisAI;
// Dismiss ghoul if necessary
- if (Creature* ghoul = ObjectAccessor::GetCreature(*player, player->GetGhoulResurrectGhoulGUID()))
+ if (Creature* ghoul = ObjectAccessor::GetCreature(*player, ghoulGuid))
{
- ghoul->RemoveCharmedBy(nullptr);
+ ghoul->RemoveCharmedBy(player);
ghoul->DespawnOrUnsummon(1000);
}
- player->SetGhoulResurrectGhoulGUID(ObjectGuid::Empty);
player->RemoveAura(SPELL_GHOUL_FRENZY);
}
@@ -1943,6 +2135,7 @@ public:
AfterEffectRemove += AuraEffectRemoveFn(spell_dk_raise_ally_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
+ ObjectGuid ghoulGuid;
PlayerAI* oldAI;
bool oldAIState;
};
@@ -1965,7 +2158,7 @@ public:
bool Validate(SpellInfo const* /*spellInfo*/) override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_DK_GHOUL_THRASH))
+ if (!sSpellMgr->GetSpellInfo(SPELL_GHOUL_FRENZY))
return false;
return true;
}
@@ -2002,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();
@@ -2018,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 abde43ef952..233819b04e8 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,
@@ -1245,7 +1300,7 @@ class spell_gen_defend : public SpellScriptLoader
void Register() override
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(m_scriptSpellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId);
// Defend spells cast by NPCs (add visuals)
if (spell->Effects[EFFECT_0].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN)
@@ -2148,7 +2203,7 @@ class spell_gen_mounted_charge: public SpellScriptLoader
void Register() override
{
- SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(m_scriptSpellId);
+ SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId);
if (spell->HasEffect(SPELL_EFFECT_SCRIPT_EFFECT))
OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge_SpellScript::HandleScriptEffect, EFFECT_FIRST_FOUND, SPELL_EFFECT_SCRIPT_EFFECT);
@@ -4155,6 +4210,40 @@ public:
}
};
+// 34098 - ClearAllDebuffs
+class spell_gen_clear_debuffs : public SpellScriptLoader
+{
+ public:
+ spell_gen_clear_debuffs() : SpellScriptLoader("spell_gen_clear_debuffs") { }
+
+ class spell_gen_clear_debuffs_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_clear_debuffs_SpellScript);
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ {
+ target->RemoveOwnedAuras([](Aura const* aura)
+ {
+ SpellInfo const* spellInfo = aura->GetSpellInfo();
+ return !spellInfo->IsPositive() && !spellInfo->IsPassive();
+ });
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_clear_debuffs_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_gen_clear_debuffs_SpellScript();
+ }
+};
+
void AddSC_generic_spell_scripts()
{
new spell_gen_absorb0_hitlimit1();
@@ -4165,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");
@@ -4241,4 +4331,5 @@ void AddSC_generic_spell_scripts()
new spell_gen_stand();
new spell_gen_mixology_bonus();
new spell_gen_landmine_knockback_achievement();
+ new spell_gen_clear_debuffs();
}
diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp
index a3359fdf6f9..6442eb8acca 100644
--- a/src/server/scripts/Spells/spell_holiday.cpp
+++ b/src/server/scripts/Spells/spell_holiday.cpp
@@ -65,7 +65,7 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader
Unit* caster = GetCaster();
// If our player is no longer sit, remove all auras
- if (target->getStandState() != UNIT_STAND_STATE_SIT)
+ if (target->GetStandState() != UNIT_STAND_STATE_SIT)
{
target->RemoveAura(SPELL_ROMANTIC_PICNIC_ACHIEV);
target->RemoveAura(GetAura());
@@ -84,7 +84,7 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader
target->VisitNearbyWorldObject(INTERACTION_DISTANCE*2, searcher);
for (std::list<Player*>::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
{
- if ((*itr) != target && (*itr)->HasAura(GetId())) // && (*itr)->getStandState() == UNIT_STAND_STATE_SIT)
+ if ((*itr) != target && (*itr)->HasAura(GetId())) // && (*itr)->GetStandState() == UNIT_STAND_STATE_SIT)
{
if (caster)
{
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index 4d7cc277ff6..0e952818a2f 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -51,9 +51,11 @@ 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
@@ -704,6 +706,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
{
@@ -763,7 +822,7 @@ class spell_hun_readiness : public SpellScriptLoader
// immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath
GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
///! If spellId in cooldown map isn't valid, the above will return a null pointer.
if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER &&
@@ -967,6 +1026,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 +1129,13 @@ 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_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 0abff255e4b..f7c2cefa202 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -201,6 +201,55 @@ class spell_item_blessing_of_ancient_kings : public SpellScriptLoader
}
};
+// 47770 - Roll Dice
+class spell_item_decahedral_dwarven_dice : public SpellScriptLoader
+{
+ public:
+ spell_item_decahedral_dwarven_dice() : SpellScriptLoader("spell_item_decahedral_dwarven_dice") { }
+
+ class spell_item_decahedral_dwarven_dice_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_decahedral_dwarven_dice_SpellScript);
+
+ enum
+ {
+ TEXT_DECAHEDRAL_DWARVEN_DICE = 26147
+ };
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sObjectMgr->GetBroadcastText(TEXT_DECAHEDRAL_DWARVEN_DICE))
+ return false;
+ return true;
+ }
+
+ bool Load() override
+ {
+ return GetCaster()->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->TextEmote(TEXT_DECAHEDRAL_DWARVEN_DICE, GetHitUnit());
+
+ static uint32 const minimum = 1;
+ static uint32 const maximum = 100;
+
+ GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_decahedral_dwarven_dice_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_item_decahedral_dwarven_dice_SpellScript();
+ }
+};
+
// 8342 - Defibrillate (Goblin Jumper Cables) have 33% chance on success
// 22999 - Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
// 54732 - Defibrillate (Gnomish Army Knife) have 67% chance on success
@@ -1319,6 +1368,57 @@ class spell_item_underbelly_elixir : public SpellScriptLoader
}
};
+// 47776 - Roll 'dem Bones
+class spell_item_worn_troll_dice : public SpellScriptLoader
+{
+ public:
+ spell_item_worn_troll_dice() : SpellScriptLoader("spell_item_worn_troll_dice") { }
+
+ class spell_item_worn_troll_dice_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_worn_troll_dice_SpellScript);
+
+ enum
+ {
+ TEXT_WORN_TROLL_DICE = 26152
+ };
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sObjectMgr->GetBroadcastText(TEXT_WORN_TROLL_DICE))
+ return false;
+ return true;
+ }
+
+ bool Load() override
+ {
+ return GetCaster()->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->TextEmote(TEXT_WORN_TROLL_DICE, GetHitUnit());
+
+ static uint32 const minimum = 1;
+ static uint32 const maximum = 6;
+
+ // roll twice
+ GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum);
+ GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_worn_troll_dice_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_item_worn_troll_dice_SpellScript();
+ }
+};
+
enum AirRifleSpells
{
SPELL_AIR_RIFLE_HOLD_VISUAL = 65582,
@@ -2631,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:
@@ -2668,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
@@ -2682,6 +3176,7 @@ void AddSC_item_spell_scripts()
new spell_item_aegis_of_preservation();
new spell_item_arcane_shroud();
new spell_item_blessing_of_ancient_kings();
+ new spell_item_decahedral_dwarven_dice();
new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL);
new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL);
new spell_item_defibrillate("spell_item_gnomish_army_knife", 33);
@@ -2706,6 +3201,7 @@ void AddSC_item_spell_scripts()
new spell_item_six_demon_bag();
new spell_item_the_eye_of_diminution();
new spell_item_underbelly_elixir();
+ new spell_item_worn_troll_dice();
new spell_item_red_rider_air_rifle();
new spell_item_create_heart_candy();
@@ -2736,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 d08968fbf55..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
{
@@ -180,7 +217,7 @@ class spell_mage_cold_snap : public SpellScriptLoader
{
GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) &&
spellInfo->Id != SPELL_MAGE_COLD_SNAP && spellInfo->GetRecoveryTime() > 0;
}, true);
@@ -405,7 +442,7 @@ class spell_mage_ignite : public SpellScriptLoader
{
PreventDefaultAction();
- SpellInfo const* igniteDot = sSpellMgr->EnsureSpellInfo(SPELL_MAGE_IGNITE);
+ SpellInfo const* igniteDot = sSpellMgr->AssertSpellInfo(SPELL_MAGE_IGNITE);
int32 pct = 8 * GetSpellInfo()->GetRank();
int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks());
@@ -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 8bd4b3eb070..eed84d4ff6b 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())
+ {
+ if (eventInfo.GetHealInfo()->GetSpellInfo()->SpellFamilyFlags[1] & 0x00010000)
+ {
+ PreventDefaultAction();
+ Unit* target = eventInfo.GetActor(); // Paladin is the target of the energize
+
+ // proc comes from the Holy Shock heal, need to get mana cost of original spell
+ uint32 originalspellid = sSpellMgr->GetSpellWithRank(SPELL_PALADIN_HOLY_SHOCK_R1, eventInfo.GetHealInfo()->GetSpellInfo()->GetRank());
+ SpellInfo const* originalSpell = sSpellMgr->GetSpellInfo(originalspellid);
+ if (originalSpell && aurEff->GetSpellInfo())
+ {
+ 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
@@ -1195,7 +1251,7 @@ class spell_pal_light_s_beacon : public SpellScriptLoader
if (!procSpell)
return;
- uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->EnsureSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3;
+ uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3;
uint32 heal = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount());
Unit* beaconTarget = GetCaster();
@@ -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_pet.cpp b/src/server/scripts/Spells/spell_pet.cpp
index 7393a7d3bcb..cde291cd82b 100644
--- a/src/server/scripts/Spells/spell_pet.cpp
+++ b/src/server/scripts/Spells/spell_pet.cpp
@@ -916,7 +916,7 @@ public:
if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
AddPct(mod, spellInfo->Effects[EFFECT_0].CalcValue());
}
@@ -959,7 +959,7 @@ public:
if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
mod += CalculatePct(1.0f, spellInfo->Effects[EFFECT_1].CalcValue());
}
@@ -989,7 +989,7 @@ public:
if (itr != pet->ToPet()->m_spells.end()) // If pet has Wild Hunt
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
mod += CalculatePct(1.0f, spellInfo->Effects[EFFECT_1].CalcValue());
}
diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp
index ecf5b7b5acf..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
{
@@ -236,7 +281,7 @@ class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader
{
PreventDefaultAction();
- SpellInfo const* triggeredSpellInfo = sSpellMgr->EnsureSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL);
+ SpellInfo const* triggeredSpellInfo = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL);
int32 heal = int32(CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks());
GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, NULL, aurEff);
}
@@ -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 3121a18734d..ae761413ca5 100644
--- a/src/server/scripts/Spells/spell_quest.cpp
+++ b/src/server/scripts/Spells/spell_quest.cpp
@@ -1707,7 +1707,7 @@ enum Quest13291_13292_13239_13261Data
NPC_SKYTALON = 31583,
NPC_DECOY = 31578,
// Spells
- SPELL_RIDE = 56687
+ SPELL_RIDE = 59319
};
class spell_q13291_q13292_q13239_q13261_frostbrood_skytalon_grab_decoy : public SpellScriptLoader
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index 9b577d4e140..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
@@ -450,7 +451,7 @@ class spell_rog_preparation : public SpellScriptLoader
Unit* caster = GetCaster();
caster->GetSpellHistory()->ResetCooldowns([caster](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
if (spellInfo->SpellFamilyName != SPELLFAMILY_ROGUE)
return false;
@@ -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 7cc6fe888e9..d889cfced80 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
@@ -271,7 +277,7 @@ class spell_warl_demonic_circle_summon : public SpellScriptLoader
// WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST; allowing him to cast the WARLOCK_DEMONIC_CIRCLE_TELEPORT.
// If not in range remove the WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST.
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT);
if (GetTarget()->IsWithinDist(circle, spellInfo->GetMaxRange(true)))
{
@@ -362,7 +368,7 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader
break;
case CREATURE_FAMILY_VOIDWALKER:
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER);
int32 hp = int32(targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(targetCreature, spellInfo, 0)));
targetCreature->CastCustomSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER, &hp, NULL, NULL, true);
//unitTarget->CastSpell(unitTarget, 54441, true);
@@ -682,6 +688,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 +1012,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/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index d224c234cb4..ea9ccc956e5 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -271,7 +271,7 @@ class spell_warr_deep_wounds : public SpellScriptLoader
{
ApplyPct(damage, 16 * GetSpellInfo()->GetRank());
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_DEEP_WOUNDS_PERIODIC);
uint32 ticks = uint32(spellInfo->GetDuration()) / spellInfo->Effects[EFFECT_0].Amplitude;
// Add remaining ticks to damage done
diff --git a/src/server/scripts/World/boss_emerald_dragons.cpp b/src/server/scripts/World/boss_emerald_dragons.cpp
index 1f89720803d..65dec74414b 100644
--- a/src/server/scripts/World/boss_emerald_dragons.cpp
+++ b/src/server/scripts/World/boss_emerald_dragons.cpp
@@ -205,7 +205,7 @@ class npc_dream_fog : public CreatureScript
}
// Seeping fog movement is slow enough for a player to be able to walk backwards and still outpace it
me->SetWalk(true);
- me->SetSpeed(MOVE_WALK, 0.75f);
+ me->SetSpeedRate(MOVE_WALK, 0.75f);
}
else
_roamTimer -= diff;
diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp
index 7982a5639ec..b04f3ec0aa7 100644
--- a/src/server/scripts/World/duel_reset.cpp
+++ b/src/server/scripts/World/duel_reset.cpp
@@ -105,7 +105,7 @@ class DuelResetScript : public PlayerScript
{
SpellHistory::Clock::time_point now = SpellHistory::Clock::now();
uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.CooldownEnd - now).count() : 0;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& !itr->second.OnHold
@@ -119,7 +119,7 @@ class DuelResetScript : public PlayerScript
// remove cooldowns on spells that have < 10 min CD and has no onHold
player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS
&& !itr->second.OnHold;
diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp
index 3094ecd660a..8f2ea5887d2 100644
--- a/src/server/scripts/World/go_scripts.cpp
+++ b/src/server/scripts/World/go_scripts.cpp
@@ -1171,7 +1171,7 @@ public:
player->CastSpell(player, SPELL_CLEANSING_SOUL);
player->SetStandState(UNIT_STAND_STATE_SIT);
}
- return true;
+ return true;
}
};
diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp
index f4241ba0819..52174e1b012 100644
--- a/src/server/scripts/World/item_scripts.cpp
+++ b/src/server/scripts/World/item_scripts.cpp
@@ -57,18 +57,18 @@ public:
//for special scripts
switch (itemId)
{
- case 24538:
+ case 24538:
if (player->GetAreaId() != 3628)
disabled = true;
- break;
- case 34489:
+ break;
+ case 34489:
if (player->GetZoneId() != 4080)
disabled = true;
- break;
- case 34475:
- if (const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(SPELL_ARCANE_CHARGES))
+ break;
+ case 34475:
+ if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_ARCANE_CHARGES))
Spell::SendCastResult(player, spellInfo, 1, SPELL_FAILED_NOT_ON_GROUND);
- break;
+ break;
}
// allow use in flight only
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index aedbe68d51c..e99a81a084b 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -21,9 +21,11 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+add_definitions(-DTRINITY_API_EXPORT_SHARED)
+
add_library(shared
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
CollectIncludeDirectories(
@@ -47,6 +49,18 @@ set_target_properties(shared
FOLDER
"server")
+if( BUILD_SHARED_LIBS )
+ if( UNIX )
+ install(TARGETS shared
+ LIBRARY
+ DESTINATION lib)
+ elseif( WIN32 )
+ install(TARGETS shared
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+endif()
+
# Generate precompiled header
if (USE_COREPCH)
add_cxx_pch(shared ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
diff --git a/src/server/shared/DataStores/DBCFileLoader.h b/src/server/shared/DataStores/DBCFileLoader.h
index 00b1ee54a4a..abe18d1425e 100644
--- a/src/server/shared/DataStores/DBCFileLoader.h
+++ b/src/server/shared/DataStores/DBCFileLoader.h
@@ -37,7 +37,7 @@ enum DbcFieldFormat
FT_SQL_ABSENT='a' //Used in sql format to mark column absent in sql dbc
};
-class DBCFileLoader
+class TC_SHARED_API DBCFileLoader
{
public:
DBCFileLoader();
diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h
index b14aac4ca47..e479cd2450d 100644
--- a/src/server/shared/Networking/SocketMgr.h
+++ b/src/server/shared/Networking/SocketMgr.h
@@ -19,7 +19,6 @@
#define SocketMgr_h__
#include "AsyncAcceptor.h"
-#include "Config.h"
#include "Errors.h"
#include "NetworkThread.h"
#include <boost/asio/ip/tcp.hpp>
@@ -36,15 +35,9 @@ public:
ASSERT(!_threads && !_acceptor && !_threadCount, "StopNetwork must be called prior to SocketMgr destruction");
}
- virtual bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port)
+ virtual bool StartNetwork(boost::asio::io_service& service, std::string const& bindIp, uint16 port, int threadCount)
{
- _threadCount = sConfigMgr->GetIntDefault("Network.Threads", 1);
-
- if (_threadCount <= 0)
- {
- TC_LOG_ERROR("misc", "Network.Threads is wrong in your config file");
- return false;
- }
+ ASSERT(threadCount > 0);
try
{
@@ -56,6 +49,7 @@ public:
return false;
}
+ _threadCount = threadCount;
_threads = CreateThreads();
ASSERT(_threads);
diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h
index 2aff6b5aabf..d24a91ed458 100644
--- a/src/server/shared/Packets/ByteBuffer.h
+++ b/src/server/shared/Packets/ByteBuffer.h
@@ -23,21 +23,12 @@
#include "Errors.h"
#include "ByteConverter.h"
#include "Util.h"
-
-#include <exception>
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
#include <cstring>
-#include <time.h>
-#include <cmath>
-#include <type_traits>
class MessageBuffer;
// Root of ByteBuffer exception hierarchy
-class ByteBufferException : public std::exception
+class TC_SHARED_API ByteBufferException : public std::exception
{
public:
~ByteBufferException() throw() { }
@@ -51,7 +42,7 @@ private:
std::string msg_;
};
-class ByteBufferPositionException : public ByteBufferException
+class TC_SHARED_API ByteBufferPositionException : public ByteBufferException
{
public:
ByteBufferPositionException(bool add, size_t pos, size_t size, size_t valueSize);
@@ -59,7 +50,7 @@ public:
~ByteBufferPositionException() throw() { }
};
-class ByteBufferSourceException : public ByteBufferException
+class TC_SHARED_API ByteBufferSourceException : public ByteBufferException
{
public:
ByteBufferSourceException(size_t pos, size_t size, size_t valueSize);
@@ -67,7 +58,7 @@ public:
~ByteBufferSourceException() throw() { }
};
-class ByteBuffer
+class TC_SHARED_API ByteBuffer
{
public:
const static size_t DEFAULT_SIZE = 0x1000;
diff --git a/src/server/shared/Realm/Realm.cpp b/src/server/shared/Realm/Realm.cpp
index 0c8f4d1d492..11c52f281a9 100644
--- a/src/server/shared/Realm/Realm.cpp
+++ b/src/server/shared/Realm/Realm.cpp
@@ -16,3 +16,38 @@
*/
#include "Realm.h"
+
+ip::tcp::endpoint Realm::GetAddressForClient(ip::address const& clientAddr) const
+{
+ ip::address realmIp;
+
+ // Attempt to send best address for client
+ if (clientAddr.is_loopback())
+ {
+ // Try guessing if realm is also connected locally
+ if (LocalAddress.is_loopback() || ExternalAddress.is_loopback())
+ realmIp = clientAddr;
+ else
+ {
+ // Assume that user connecting from the machine that bnetserver is located on
+ // has all realms available in his local network
+ realmIp = LocalAddress;
+ }
+ }
+ else
+ {
+ if (clientAddr.is_v4() &&
+ (clientAddr.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()) ==
+ (LocalAddress.to_v4().to_ulong() & LocalSubnetMask.to_v4().to_ulong()))
+ {
+ realmIp = LocalAddress;
+ }
+ else
+ realmIp = ExternalAddress;
+ }
+
+ ip::tcp::endpoint endpoint(realmIp, Port);
+
+ // Return external IP
+ return endpoint;
+}
diff --git a/src/server/shared/Realm/Realm.h b/src/server/shared/Realm/Realm.h
index 83a344dd817..241ccd2bca8 100644
--- a/src/server/shared/Realm/Realm.h
+++ b/src/server/shared/Realm/Realm.h
@@ -37,7 +37,7 @@ enum RealmFlags
REALM_FLAG_FULL = 0x80
};
-struct RealmHandle
+struct TC_SHARED_API RealmHandle
{
RealmHandle() : Realm(0) { }
RealmHandle(uint32 index) : Realm(index) { }
@@ -53,20 +53,20 @@ struct RealmHandle
/// Type of server, this is values from second column of Cfg_Configs.dbc
enum RealmType
{
- REALM_TYPE_NORMAL = 0,
- REALM_TYPE_PVP = 1,
- REALM_TYPE_NORMAL2 = 4,
- REALM_TYPE_RP = 6,
- REALM_TYPE_RPPVP = 8,
+ REALM_TYPE_NORMAL = 0,
+ REALM_TYPE_PVP = 1,
+ REALM_TYPE_NORMAL2 = 4,
+ REALM_TYPE_RP = 6,
+ REALM_TYPE_RPPVP = 8,
- MAX_CLIENT_REALM_TYPE = 14,
+ MAX_CLIENT_REALM_TYPE = 14,
- REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
- // replaced by REALM_PVP in realm list
+ REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries
+ // replaced by REALM_PVP in realm list
};
// Storage object for a realm
-struct Realm
+struct TC_SHARED_API Realm
{
RealmHandle Id;
uint32 Build;
@@ -75,11 +75,13 @@ struct Realm
ip::address LocalSubnetMask;
uint16 Port;
std::string Name;
- uint8 Type; // icon
+ uint8 Type;
RealmFlags Flags;
uint8 Timezone;
AccountTypes AllowedSecurityLevel;
float PopulationLevel;
+
+ ip::tcp::endpoint GetAddressForClient(ip::address const& clientAddr) const;
};
#endif // Realm_h__
diff --git a/src/server/authserver/Realms/RealmList.cpp b/src/server/shared/Realm/RealmList.cpp
index f1b25d8554d..e941800cd76 100644
--- a/src/server/authserver/Realms/RealmList.cpp
+++ b/src/server/shared/Realm/RealmList.cpp
@@ -16,20 +16,25 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "Common.h"
-#include "Database/DatabaseEnv.h"
#include "RealmList.h"
-#include <boost/asio/ip/tcp.hpp>
+#include "Database/DatabaseEnv.h"
+#include "Util.h"
-namespace boost { namespace asio { namespace ip { class address; } } }
+RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr)
+{
+}
-RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr) { }
RealmList::~RealmList()
{
- delete _resolver;
delete _updateTimer;
}
+RealmList* RealmList::Instance()
+{
+ static RealmList instance;
+ return &instance;
+}
+
// Load the realm list from the database
void RealmList::Initialize(boost::asio::io_service& ioService, uint32 updateInterval)
{
@@ -38,7 +43,7 @@ void RealmList::Initialize(boost::asio::io_service& ioService, uint32 updateInte
_resolver = new boost::asio::ip::tcp::resolver(ioService);
// Get the content of the realmlist table in the database
- UpdateRealms(true, boost::system::error_code());
+ UpdateRealms(boost::system::error_code());
}
void RealmList::Close()
@@ -67,16 +72,22 @@ void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, const std::stri
realm.Port = port;
}
-void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
+void RealmList::UpdateRealms(boost::system::error_code const& error)
{
if (error)
return;
- TC_LOG_INFO("server.authserver", "Updating Realm List...");
+ TC_LOG_DEBUG("server.authserver", "Updating Realm List...");
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
PreparedQueryResult result = LoginDatabase.Query(stmt);
+ std::map<RealmHandle, std::string> existingRealms;
+ for (auto const& p : _realms)
+ existingRealms[p.first] = p.second.Name;
+
+ _realms.clear();
+
// Circle through results and add them to the realm map
if (result)
{
@@ -95,8 +106,8 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
boost::asio::ip::tcp::resolver::iterator endPoint = _resolver->resolve(externalAddressQuery, ec);
if (endPoint == end || ec)
{
- TC_LOG_ERROR("server.authserver", "Could not resolve address %s", fields[2].GetString().c_str());
- return;
+ TC_LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", fields[2].GetString().c_str(), name.c_str(), realmId);
+ continue;
}
ip::address externalAddress = (*endPoint).endpoint().address();
@@ -105,8 +116,8 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
endPoint = _resolver->resolve(localAddressQuery, ec);
if (endPoint == end || ec)
{
- TC_LOG_ERROR("server.authserver", "Could not resolve address %s", fields[3].GetString().c_str());
- return;
+ TC_LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", fields[3].GetString().c_str(), name.c_str(), realmId);
+ continue;
}
ip::address localAddress = (*endPoint).endpoint().address();
@@ -115,8 +126,8 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
endPoint = _resolver->resolve(localSubmaskQuery, ec);
if (endPoint == end || ec)
{
- TC_LOG_ERROR("server.authserver", "Could not resolve address %s", fields[4].GetString().c_str());
- return;
+ TC_LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", fields[4].GetString().c_str(), name.c_str(), realmId);
+ continue;
}
ip::address localSubmask = (*endPoint).endpoint().address();
@@ -138,8 +149,12 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
UpdateRealm(id, build, name, externalAddress, localAddress, localSubmask, port, icon, flag,
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
- if (init)
+ if (!existingRealms.count(id))
TC_LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
+ else
+ TC_LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
+
+ existingRealms.erase(id);
}
catch (std::exception& ex)
{
@@ -150,10 +165,13 @@ void RealmList::UpdateRealms(bool init, boost::system::error_code const& error)
while (result->NextRow());
}
+ for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr)
+ TC_LOG_INFO("server.authserver", "Removed realm \"%s\".", itr->second.c_str());
+
if (_updateInterval)
{
_updateTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
- _updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, false, std::placeholders::_1));
+ _updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, std::placeholders::_1));
}
}
diff --git a/src/server/authserver/Realms/RealmList.h b/src/server/shared/Realm/RealmList.h
index e35975b215a..3b81337e762 100644
--- a/src/server/authserver/Realms/RealmList.h
+++ b/src/server/shared/Realm/RealmList.h
@@ -29,16 +29,12 @@
using namespace boost::asio;
/// Storage object for the list of realms on the server
-class RealmList
+class TC_SHARED_API RealmList
{
public:
typedef std::map<RealmHandle, Realm> RealmMap;
- static RealmList* instance()
- {
- static RealmList instance;
- return &instance;
- }
+ static RealmList* Instance();
~RealmList();
@@ -51,7 +47,7 @@ public:
private:
RealmList();
- void UpdateRealms(bool init, boost::system::error_code const& error);
+ void UpdateRealms(boost::system::error_code const& error);
void UpdateRealm(RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
@@ -61,5 +57,5 @@ private:
boost::asio::ip::tcp::resolver* _resolver;
};
-#define sRealmList RealmList::instance()
+#define sRealmList RealmList::Instance()
#endif
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 7fb4c6d2b75..0de8d6054f3 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -29,8 +29,8 @@ endif()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(worldserver
- ${PRIVATE_SOURCES}
${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
if( NOT WIN32 )
@@ -48,6 +48,7 @@ set_target_properties(worldserver PROPERTIES LINK_FLAGS "${worldserver_LINK_FLAG
target_link_libraries(worldserver
PUBLIC
scripts
+ game
gsoap
readline)
@@ -68,6 +69,11 @@ set_target_properties(worldserver
FOLDER
"server")
+# Add all dynamic projects as dependency to the worldserver
+if (WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES)
+ add_dependencies(worldserver ${WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES})
+endif()
+
if( WIN32 )
if ( MSVC )
add_custom_command(TARGET worldserver
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index 229375c4e51..f4d736ac675 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -24,6 +24,7 @@
#include <openssl/crypto.h>
#include <boost/asio/io_service.hpp>
#include <boost/asio/deadline_timer.hpp>
+#include <boost/filesystem/path.hpp>
#include <boost/program_options.hpp>
#include "Common.h"
@@ -39,6 +40,7 @@
#include "InstanceSaveMgr.h"
#include "ObjectAccessor.h"
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "ScriptLoader.h"
#include "OutdoorPvP/OutdoorPvPMgr.h"
#include "BattlegroundMgr.h"
@@ -52,6 +54,7 @@
#include "AppenderDB.h"
using namespace boost::program_options;
+namespace fs = boost::filesystem;
#ifndef _TRINITY_CORE_CONFIG
#define _TRINITY_CORE_CONFIG "worldserver.conf"
@@ -89,14 +92,14 @@ void ClearOnlineAccounts();
void ShutdownCLIThread(std::thread* cliThread);
void ShutdownThreadPool(std::vector<std::thread>& threadPool);
bool LoadRealmInfo();
-variables_map GetConsoleArguments(int argc, char** argv, std::string& cfg_file, std::string& cfg_service);
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& cfg_service);
/// Launch the Trinity server
extern int main(int argc, char** argv)
{
signal(SIGABRT, &Trinity::AbortHandler);
- std::string configFile = _TRINITY_CORE_CONFIG;
+ auto configFile = fs::absolute(_TRINITY_CORE_CONFIG);
std::string configService;
auto vm = GetConsoleArguments(argc, argv, configFile, configService);
@@ -114,7 +117,9 @@ extern int main(int argc, char** argv)
#endif
std::string configError;
- if (!sConfigMgr->LoadInitial(configFile, configError))
+ if (!sConfigMgr->LoadInitial(configFile.generic_string(),
+ std::vector<std::string>(argv, argv + argc),
+ configError))
{
printf("Error in config file: %s\n", configError.c_str());
return 1;
@@ -135,7 +140,7 @@ extern int main(int argc, char** argv)
TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\");
TC_LOG_INFO("server.worldserver", " C O R E /\\___/");
TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n");
- TC_LOG_INFO("server.worldserver", "Using configuration file %s.", configFile.c_str());
+ TC_LOG_INFO("server.worldserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str());
TC_LOG_INFO("server.worldserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
@@ -222,7 +227,15 @@ extern int main(int argc, char** argv)
uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
std::string worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
- sWorldSocketMgr.StartNetwork(_ioService, worldListener, worldPort);
+ int networkThreads = sConfigMgr->GetIntDefault("Network.Threads", 1);
+
+ if (networkThreads <= 0)
+ {
+ TC_LOG_ERROR("server.worldserver", "Network.Threads must be greater than 0");
+ return false;
+ }
+
+ sWorldSocketMgr.StartNetwork(_ioService, worldListener, worldPort, networkThreads);
// Set server online (allow connecting now)
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_OFFLINE, realm.Id.Realm);
@@ -263,6 +276,7 @@ extern int main(int argc, char** argv)
sOutdoorPvPMgr->Die();
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
sScriptMgr->Unload();
+ sScriptReloadMgr->Unload();
// set server offline
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
@@ -561,7 +575,7 @@ void ClearOnlineAccounts()
/// @}
-variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService)
+variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService)
{
// Silences warning about configService not be used if the OS is not Windows
(void)configService;
@@ -570,7 +584,8 @@ variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile
all.add_options()
("help,h", "print usage message")
("version,v", "print version build info")
- ("config,c", value<std::string>(&configFile)->default_value(_TRINITY_CORE_CONFIG), "use <arg> as configuration file")
+ ("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_CORE_CONFIG)),
+ "use <arg> as configuration file")
;
#ifdef _WIN32
options_description win("Windows platform specific options");
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 8e68d9f08fe..ccb784f8c2b 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -12,6 +12,7 @@
# SERVER LOGGING
# SERVER SETTINGS
# UPDATE SETTINGS
+# HOTSWAP SETTINGS
# WARDEN SETTINGS
# PLAYER INTERACTION
# CREATURE SETTINGS
@@ -349,6 +350,13 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
#
+# DisconnectToleranceInterval
+# Description: Tolerance (in seconds) for disconnected players before reentering the queue.
+# Default: 0 (disabled)
+
+DisconnectToleranceInterval = 0
+
+#
# mmap.enablePathFinding
# Description: Enable/Disable pathfinding using mmaps - recommended.
# Default: 0 - (Disabled)
@@ -717,7 +725,7 @@ CharacterCreating.Disabled.RaceMask = 0
# 2 - (Disabled, Paladin)
# 4 - (Disabled, Hunter)
# 8 - (Disabled, Rogue)
-# 16 - (Disabled, Undead)
+# 16 - (Disabled, Priest)
# 32 - (Disabled, Death Knight)
# 64 - (Disabled, Shaman)
# 128 - (Disabled, Mage)
@@ -1273,6 +1281,78 @@ Updates.CleanDeadRefMaxCount = 3
###################################################################################################
###################################################################################################
+# HOTSWAP SETTINGS
+#
+# HotSwap.Enabled (Requires compilation with DYNAMIC_LINKING=1)
+# Description: Enables dynamic script hotswapping.
+# Reloads scripts on changes.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.Enabled = 1
+
+#
+# HotSwap.ScriptDir
+# Description: Directory containing the script shared libraries (.dll/.so).
+# Example: "/usr/local/scripts"
+# Default: "scripts"
+
+HotSwap.ScriptDir = "scripts"
+
+# HotSwap.EnableReCompiler
+# Description: Enables the dynamic script recompiler.
+# Watches your script source directories and recompiles the
+# script modules on changes.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableReCompiler = 1
+
+# HotSwap.EnableEarlyTermination
+# Description: Terminate the build of a module when an associated
+# source file was changed meanwhile.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableEarlyTermination = 1
+
+# HotSwap.EnableBuildFileRecreation
+# Description: Recreate build files when sources to a module
+# were added or removed.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableBuildFileRecreation = 1
+
+#
+# HotSwap.EnableInstall
+# Description: Enables cmake install after automatic builds have finished
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableInstall = 1
+
+#
+# HotSwap.EnablePrefixCorrection
+# Description: Allows the core to automatic set the CMAKE_INSTALL_PREFIX
+# to it's current location in the filesystem.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnablePrefixCorrection = 1
+
+# HotSwap.ReCompilerBuildType
+# Description: Defines the build type of the builds invoked by the recompiler.
+# Default: "" - Built-in build type of the module is used.
+# "Release" - Release builds only
+# "Debug" - Debug builds only
+
+HotSwap.ReCompilerBuildType = ""
+
+#
+###################################################################################################
+
+###################################################################################################
# WARDEN SETTINGS
#
# Warden.Enabled
@@ -1578,6 +1658,14 @@ ListenRange.TextEmote = 40
ListenRange.Yell = 300
#
+# Creature.MovingStopTimeForPlayer
+# Description: Time (in milliseconds) during which creature will not move after
+# interaction with player.
+# Default: 180000
+
+Creature.MovingStopTimeForPlayer = 180000
+
+#
###################################################################################################
###################################################################################################
@@ -1789,6 +1877,13 @@ GM.LowerSecurity = 0
GM.TicketSystem.ChanceOfGMSurvey = 50
#
+# GM.ForceShutdownThreshold
+# Description: Minimum shutdown time in seconds before 'force' is required if other players are connected.
+# Default: 30
+
+GM.ForceShutdownThreshold = 30
+
+#
###################################################################################################
###################################################################################################
@@ -2297,16 +2392,16 @@ Battleground.InvitationType = 0
# Default: 300000 - (Enabled, 5 minutes)
# 0 - (Disabled, Not recommended)
-BattleGround.PrematureFinishTimer = 300000
+Battleground.PrematureFinishTimer = 300000
#
-# BattleGround.PremadeGroupWaitForMatch
+# Battleground.PremadeGroupWaitForMatch
# Description: Time (in milliseconds) a pre-made group has to wait for matching group of the
# other faction.
# Default: 1800000 - (Enabled, 30 minutes)
# 0 - (Disabled, Not recommended)
-BattleGround.PremadeGroupWaitForMatch = 1800000
+Battleground.PremadeGroupWaitForMatch = 1800000
#
# Battleground.GiveXPForKills
@@ -2989,6 +3084,27 @@ AuctionHouseBot.MinTime = 1
AuctionHouseBot.MaxTime = 72
#
+# AuctionHouseBot.Class.CLASS.Allow.Zero = 0
+# Description: Include items without a sell or buy price.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+AuctionHouseBot.Class.Consumable.Allow.Zero = 0
+AuctionHouseBot.Class.Container.Allow.Zero = 0
+AuctionHouseBot.Class.Weapon.Allow.Zero = 0
+AuctionHouseBot.Class.Gem.Allow.Zero = 0
+AuctionHouseBot.Class.Armor.Allow.Zero = 0
+AuctionHouseBot.Class.Reagent.Allow.Zero = 0
+AuctionHouseBot.Class.Projectile.Allow.Zero = 0
+AuctionHouseBot.Class.TradeGood.Allow.Zero = 0
+AuctionHouseBot.Class.Recipe.Allow.Zero = 0
+AuctionHouseBot.Class.Quiver.Allow.Zero = 0
+AuctionHouseBot.Class.Quest.Allow.Zero = 0
+AuctionHouseBot.Class.Key.Allow.Zero = 0
+AuctionHouseBot.Class.Misc.Allow.Zero = 0
+AuctionHouseBot.Class.Glyph.Allow.Zero = 0
+
+#
# AuctionHouseBot.Items.Vendor
# Description: Include items that can be bought from vendors.
# Default: 0 - (Disabled)
@@ -3116,8 +3232,6 @@ AuctionHouseBot.Class.Key.Price.Ratio = 100
AuctionHouseBot.Class.Misc.Price.Ratio = 100
AuctionHouseBot.Class.Glyph.Price.Ratio = 100
-
-
#
# AuctionHouseBot.Items.ItemLevel.*
# Description: Prevent seller from listing items below/above this item level
@@ -3194,7 +3308,6 @@ AuctionHouseBot.Class.Key = 1
AuctionHouseBot.Class.Misc = 5
AuctionHouseBot.Class.Glyph = 3
-
#
###################################################################################################
@@ -3483,6 +3596,7 @@ Appender.DBErrors=2,2,0,DBErrors.log
Logger.root=5,Console Server
Logger.server=3,Console Server
Logger.commands.gm=3,Console GM
+Logger.scripts.hotswap=3,Console Server
Logger.sql.sql=5,Console DBErrors
Logger.sql.updates=3,Console Server
Logger.mmaps=3,Server
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index f3a761fd437..9d3dc47bce0 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -53,12 +53,13 @@ char input_path[MAX_PATH_LENGTH] = ".";
// **************************************************
enum Extract
{
- EXTRACT_MAP = 1,
- EXTRACT_DBC = 2
+ EXTRACT_MAP = 1,
+ EXTRACT_DBC = 2,
+ EXTRACT_CAMERA = 4
};
// Select data for extract
-int CONF_extract = EXTRACT_MAP | EXTRACT_DBC;
+int CONF_extract = EXTRACT_MAP | EXTRACT_DBC | EXTRACT_CAMERA;
// This option allow limit minimum height to some value (Allow save some memory)
bool CONF_allow_height_limit = true;
float CONF_use_minHeight = -500.0f;
@@ -103,7 +104,7 @@ void Usage(char* prg)
"%s -[var] [value]\n"\
"-i set input path (max %d characters)\n"\
"-o set output path (max %d characters)\n"\
- "-e extract only MAP(1)/DBC(2) - standard: both(3)\n"\
+ "-e extract only MAP(1)/DBC(2)/Camera(4) - standard: all(7)\n"\
"-f height stored as int (less map size but lost some accuracy) 1 by default\n"\
"Example: %s -f 0 -i \"c:\\games\\game\"", prg, MAX_PATH_LENGTH - 1, MAX_PATH_LENGTH - 1, prg);
exit(1);
@@ -151,7 +152,7 @@ void HandleArgs(int argc, char * arg[])
if(c + 1 < argc) // all ok
{
CONF_extract=atoi(arg[(c++) + 1]);
- if(!(CONF_extract > 0 && CONF_extract < 4))
+ if(!(CONF_extract > 0 && CONF_extract < 8))
Usage(arg[0]);
}
else
@@ -1025,6 +1026,56 @@ void ExtractDBCFiles(int locale, bool basicLocale)
printf("Extracted %u DBC files\n\n", count);
}
+void ExtractCameraFiles(int locale, bool basicLocale)
+{
+ printf("Extracting camera files...\n");
+ DBCFile camdbc("DBFilesClient\\CinematicCamera.dbc");
+
+ if (!camdbc.open())
+ {
+ printf("Unable to open CinematicCamera.dbc. Camera extract aborted.\n");
+ return;
+ }
+
+ // get camera file list from DBC
+ std::vector<std::string> camerafiles;
+ size_t cam_count = camdbc.getRecordCount();
+
+ for (size_t i = 0; i < cam_count; ++i)
+ {
+ std::string camFile(camdbc.getRecord(i).getString(1));
+ size_t loc = camFile.find(".mdx");
+ if (loc != std::string::npos)
+ camFile.replace(loc, 4, ".m2");
+ camerafiles.push_back(std::string(camFile));
+ }
+
+ std::string path = output_path;
+ path += "/Cameras/";
+ CreateDir(path);
+ if (!basicLocale)
+ {
+ path += langs[locale];
+ path += "/";
+ CreateDir(path);
+ }
+
+ // extract M2s
+ uint32 count = 0;
+ for (std::string thisFile : camerafiles)
+ {
+ std::string filename = path;
+ filename += (thisFile.c_str() + strlen("Cameras\\"));
+
+ if (boost::filesystem::exists(filename))
+ continue;
+
+ if (ExtractFile(thisFile.c_str(), filename))
+ ++count;
+ }
+ printf("Extracted %u camera files\n", count);
+}
+
void LoadLocaleMPQFiles(int const locale)
{
std::string fileName = Trinity::StringFormat("%s/Data/%s/locale-%s.MPQ", input_path, langs[locale], langs[locale]);
@@ -1111,6 +1162,19 @@ int main(int argc, char * arg[])
return 0;
}
+ if (CONF_extract & EXTRACT_CAMERA)
+ {
+ printf("Using locale: %s\n", langs[FirstLocale]);
+
+ // Open MPQs
+ LoadLocaleMPQFiles(FirstLocale);
+ LoadCommonMPQFiles();
+
+ ExtractCameraFiles(FirstLocale, true);
+ // Close MPQs
+ CloseMPQFiles();
+ }
+
if (CONF_extract & EXTRACT_MAP)
{
printf("Using locale: %s\n", langs[FirstLocale]);