From 75b80d9f5b02a643c983b2fb1ededed79fd5d133 Mon Sep 17 00:00:00 2001 From: Rat Date: Sat, 5 Jun 2010 23:40:08 +0200 Subject: rearranged core files --HG-- branch : trunk --- .../game/AI/AuctionHouseBot/AuctionHouseBot.cpp | 1860 ++ .../game/AI/AuctionHouseBot/AuctionHouseBot.h | 1225 + src/server/game/AI/CombatAI.cpp | 370 + src/server/game/AI/CombatAI.h | 129 + src/server/game/AI/CreatureAI.cpp | 162 + src/server/game/AI/CreatureAI.h | 192 + src/server/game/AI/CreatureAIFactory.h | 53 + src/server/game/AI/CreatureAIImpl.h | 638 + src/server/game/AI/CreatureAIRegistry.cpp | 59 + src/server/game/AI/CreatureAIRegistry.h | 29 + src/server/game/AI/CreatureAISelector.cpp | 136 + src/server/game/AI/CreatureAISelector.h | 34 + src/server/game/AI/EventAI/CreatureEventAI.cpp | 1384 ++ src/server/game/AI/EventAI/CreatureEventAI.h | 637 + src/server/game/AI/EventAI/CreatureEventAIMgr.cpp | 745 + src/server/game/AI/EventAI/CreatureEventAIMgr.h | 46 + src/server/game/AI/GuardAI.cpp | 137 + src/server/game/AI/GuardAI.h | 55 + src/server/game/AI/PassiveAI.cpp | 81 + src/server/game/AI/PassiveAI.h | 86 + src/server/game/AI/PetAI.cpp | 471 + src/server/game/AI/PetAI.h | 64 + src/server/game/AI/ReactorAI.cpp | 59 + src/server/game/AI/ReactorAI.h | 40 + src/server/game/AI/ScriptedAI/ScriptedCreature.cpp | 740 + src/server/game/AI/ScriptedAI/ScriptedCreature.h | 290 + src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp | 506 + src/server/game/AI/ScriptedAI/ScriptedEscortAI.h | 116 + .../game/AI/ScriptedAI/ScriptedFollowerAI.cpp | 387 + src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h | 67 + src/server/game/AI/ScriptedAI/ScriptedGossip.h | 187 + src/server/game/AI/ScriptedAI/ScriptedGuardAI.cpp | 194 + src/server/game/AI/ScriptedAI/ScriptedGuardAI.h | 45 + src/server/game/AI/ScriptedAI/ScriptedInstance.h | 20 + src/server/game/AI/ScriptedAI/ScriptedSimpleAI.cpp | 278 + src/server/game/AI/ScriptedAI/ScriptedSimpleAI.h | 71 + src/server/game/AI/ScriptedAI/ScriptedSmartAI.cpp | 8 + src/server/game/AI/ScriptedAI/ScriptedSmartAI.h | 8 + src/server/game/AI/TotemAI.cpp | 116 + src/server/game/AI/TotemAI.h | 47 + src/server/game/AI/UnitAI.cpp | 327 + src/server/game/AI/UnitAI.h | 161 + src/server/game/Account/AccountMgr.cpp | 246 + src/server/game/Account/AccountMgr.h | 64 + src/server/game/AccountMgr.cpp | 246 - src/server/game/AccountMgr.h | 64 - src/server/game/AchievementMgr.cpp | 2385 -- src/server/game/AchievementMgr.h | 346 - src/server/game/Achievements/AchievementMgr.cpp | 2385 ++ src/server/game/Achievements/AchievementMgr.h | 346 + src/server/game/AddonHandler.cpp | 234 - src/server/game/AddonHandler.h | 41 - src/server/game/AddonMgr.cpp | 93 - src/server/game/AddonMgr.h | 89 - src/server/game/Addons/AddonHandler.cpp | 234 + src/server/game/Addons/AddonHandler.h | 41 + src/server/game/Addons/AddonMgr.cpp | 93 + src/server/game/Addons/AddonMgr.h | 89 + src/server/game/ArenaTeam.cpp | 764 - src/server/game/ArenaTeam.h | 224 - src/server/game/ArenaTeamHandler.cpp | 391 - .../game/AuctionHouse/AuctionHouseHandler.cpp | 664 + src/server/game/AuctionHouse/AuctionHouseMgr.cpp | 750 + src/server/game/AuctionHouse/AuctionHouseMgr.h | 171 + src/server/game/AuctionHouseBot.cpp | 1860 -- src/server/game/AuctionHouseBot.h | 1225 - src/server/game/AuctionHouseHandler.cpp | 664 - src/server/game/AuctionHouseMgr.cpp | 750 - src/server/game/AuctionHouseMgr.h | 171 - src/server/game/Bag.cpp | 235 - src/server/game/Bag.h | 75 - src/server/game/BattleGround.cpp | 1965 -- src/server/game/BattleGround.h | 664 - src/server/game/BattleGroundAA.cpp | 84 - src/server/game/BattleGroundAA.h | 53 - src/server/game/BattleGroundAB.cpp | 715 - src/server/game/BattleGroundAB.h | 301 - src/server/game/BattleGroundAV.cpp | 1490 -- src/server/game/BattleGroundAV.h | 1614 -- src/server/game/BattleGroundBE.cpp | 201 - src/server/game/BattleGroundBE.h | 78 - src/server/game/BattleGroundDS.cpp | 182 - src/server/game/BattleGroundDS.h | 89 - src/server/game/BattleGroundEY.cpp | 938 - src/server/game/BattleGroundEY.h | 410 - src/server/game/BattleGroundHandler.cpp | 803 - src/server/game/BattleGroundIC.cpp | 132 - src/server/game/BattleGroundIC.h | 64 - src/server/game/BattleGroundMgr.cpp | 2232 -- src/server/game/BattleGroundMgr.h | 276 - src/server/game/BattleGroundNA.cpp | 177 - src/server/game/BattleGroundNA.h | 76 - src/server/game/BattleGroundRB.cpp | 81 - src/server/game/BattleGroundRB.h | 54 - src/server/game/BattleGroundRL.cpp | 176 - src/server/game/BattleGroundRL.h | 72 - src/server/game/BattleGroundRV.cpp | 234 - src/server/game/BattleGroundRV.h | 140 - src/server/game/BattleGroundSA.cpp | 822 - src/server/game/BattleGroundSA.h | 384 - src/server/game/BattleGroundWS.cpp | 841 - src/server/game/BattleGroundWS.h | 223 - src/server/game/BattleGrounds/ArenaTeam.cpp | 764 + src/server/game/BattleGrounds/ArenaTeam.h | 224 + src/server/game/BattleGrounds/ArenaTeamHandler.cpp | 391 + src/server/game/BattleGrounds/BattleGround.cpp | 1965 ++ src/server/game/BattleGrounds/BattleGround.h | 664 + src/server/game/BattleGrounds/BattleGroundAA.cpp | 84 + src/server/game/BattleGrounds/BattleGroundAA.h | 53 + src/server/game/BattleGrounds/BattleGroundAB.cpp | 715 + src/server/game/BattleGrounds/BattleGroundAB.h | 301 + src/server/game/BattleGrounds/BattleGroundAV.cpp | 1490 ++ src/server/game/BattleGrounds/BattleGroundAV.h | 1614 ++ src/server/game/BattleGrounds/BattleGroundBE.cpp | 201 + src/server/game/BattleGrounds/BattleGroundBE.h | 78 + src/server/game/BattleGrounds/BattleGroundDS.cpp | 182 + src/server/game/BattleGrounds/BattleGroundDS.h | 89 + src/server/game/BattleGrounds/BattleGroundEY.cpp | 938 + src/server/game/BattleGrounds/BattleGroundEY.h | 410 + .../game/BattleGrounds/BattleGroundHandler.cpp | 803 + src/server/game/BattleGrounds/BattleGroundIC.cpp | 132 + src/server/game/BattleGrounds/BattleGroundIC.h | 64 + src/server/game/BattleGrounds/BattleGroundMgr.cpp | 2232 ++ src/server/game/BattleGrounds/BattleGroundMgr.h | 276 + src/server/game/BattleGrounds/BattleGroundNA.cpp | 177 + src/server/game/BattleGrounds/BattleGroundNA.h | 76 + src/server/game/BattleGrounds/BattleGroundRB.cpp | 81 + src/server/game/BattleGrounds/BattleGroundRB.h | 54 + src/server/game/BattleGrounds/BattleGroundRL.cpp | 176 + src/server/game/BattleGrounds/BattleGroundRL.h | 72 + src/server/game/BattleGrounds/BattleGroundRV.cpp | 234 + src/server/game/BattleGrounds/BattleGroundRV.h | 140 + src/server/game/BattleGrounds/BattleGroundSA.cpp | 822 + src/server/game/BattleGrounds/BattleGroundSA.h | 384 + src/server/game/BattleGrounds/BattleGroundWS.cpp | 841 + src/server/game/BattleGrounds/BattleGroundWS.h | 223 + src/server/game/Calendar.cpp | 17 - src/server/game/Calendar.h | 26 - src/server/game/CalendarHandler.cpp | 318 - src/server/game/Calender/Calendar.cpp | 17 + src/server/game/Calender/Calendar.h | 26 + src/server/game/Calender/CalendarHandler.cpp | 318 + src/server/game/Cell.h | 177 - src/server/game/CellImpl.h | 297 - src/server/game/Channel.cpp | 1102 - src/server/game/Channel.h | 292 - src/server/game/ChannelHandler.cpp | 323 - src/server/game/ChannelMgr.cpp | 110 - src/server/game/ChannelMgr.h | 57 - src/server/game/CharacterHandler.cpp | 1406 -- src/server/game/Chat.cpp | 2458 -- src/server/game/Chat.h | 654 - src/server/game/Chat/Channel.cpp | 1102 + src/server/game/Chat/Channel.h | 292 + src/server/game/Chat/ChannelHandler.cpp | 323 + src/server/game/Chat/ChannelMgr.cpp | 110 + src/server/game/Chat/ChannelMgr.h | 57 + src/server/game/Chat/Chat.cpp | 2458 ++ src/server/game/Chat/Chat.h | 654 + src/server/game/Chat/ChatHandler.cpp | 756 + src/server/game/Chat/Debugcmds.cpp | 1127 + src/server/game/Chat/Level0.cpp | 295 + src/server/game/Chat/Level1.cpp | 2960 +++ src/server/game/Chat/Level2.cpp | 4526 ++++ src/server/game/Chat/Level3.cpp | 7743 ++++++ src/server/game/ChatHandler.cpp | 756 - src/server/game/Combat/CombatHandler.cpp | 91 + src/server/game/Combat/HostileRefManager.cpp | 189 + src/server/game/Combat/HostileRefManager.h | 74 + src/server/game/Combat/ThreatManager.cpp | 553 + src/server/game/Combat/ThreatManager.h | 279 + src/server/game/CombatAI.cpp | 370 - src/server/game/CombatAI.h | 129 - src/server/game/CombatHandler.cpp | 91 - src/server/game/ConditionMgr.cpp | 1145 - src/server/game/ConditionMgr.h | 167 - src/server/game/ConditionMgr/ConditionMgr.cpp | 1145 + src/server/game/ConditionMgr/ConditionMgr.h | 167 + src/server/game/ConfusedMovementGenerator.cpp | 181 - src/server/game/ConfusedMovementGenerator.h | 59 - src/server/game/Corpse.cpp | 243 - src/server/game/Corpse.h | 97 - src/server/game/Creature.cpp | 2423 -- src/server/game/Creature.h | 738 - src/server/game/CreatureAI.cpp | 162 - src/server/game/CreatureAI.h | 192 - src/server/game/CreatureAIFactory.h | 53 - src/server/game/CreatureAIImpl.h | 638 - src/server/game/CreatureAIRegistry.cpp | 59 - src/server/game/CreatureAIRegistry.h | 29 - src/server/game/CreatureAISelector.cpp | 136 - src/server/game/CreatureAISelector.h | 34 - src/server/game/CreatureEventAI.cpp | 1384 -- src/server/game/CreatureEventAI.h | 637 - src/server/game/CreatureEventAIMgr.cpp | 745 - src/server/game/CreatureEventAIMgr.h | 46 - src/server/game/CreatureGroups.cpp | 256 - src/server/game/CreatureGroups.h | 78 - src/server/game/DBCEnums.h | 397 - src/server/game/DBCStores.cpp | 836 - src/server/game/DBCStores.h | 177 - src/server/game/DBCStructure.h | 1930 -- src/server/game/DBCfmt.h | 122 - src/server/game/DataStores/DBCEnums.h | 397 + src/server/game/DataStores/DBCStores.cpp | 836 + src/server/game/DataStores/DBCStores.h | 177 + src/server/game/DataStores/DBCStructure.h | 1930 ++ src/server/game/DataStores/DBCfmt.h | 122 + src/server/game/Debugcmds.cpp | 1127 - src/server/game/DestinationHolder.cpp | 22 - src/server/game/DestinationHolder.h | 67 - src/server/game/DestinationHolderImp.h | 217 - src/server/game/DuelHandler.cpp | 84 - src/server/game/DynamicObject.cpp | 189 - src/server/game/DynamicObject.h | 61 - src/server/game/Entities/Creature/Creature.cpp | 2423 ++ src/server/game/Entities/Creature/Creature.h | 738 + .../game/Entities/Creature/CreatureGroups.cpp | 256 + src/server/game/Entities/Creature/CreatureGroups.h | 78 + src/server/game/Entities/Creature/GossipDef.cpp | 882 + src/server/game/Entities/Creature/GossipDef.h | 272 + src/server/game/Entities/Creature/NPCHandler.cpp | 869 + src/server/game/Entities/Creature/NPCHandler.h | 79 + .../game/Entities/Creature/TemporarySummon.cpp | 376 + .../game/Entities/Creature/TemporarySummon.h | 101 + src/server/game/Entities/GameObject/GameObject.cpp | 1698 ++ src/server/game/Entities/GameObject/GameObject.h | 764 + src/server/game/Entities/Item/Bag.cpp | 235 + src/server/game/Entities/Item/Bag.h | 75 + src/server/game/Entities/Item/Item.cpp | 1150 + src/server/game/Entities/Item/Item.h | 356 + .../game/Entities/Item/ItemEnchantmentMgr.cpp | 214 + src/server/game/Entities/Item/ItemEnchantmentMgr.h | 30 + src/server/game/Entities/Item/ItemHandler.cpp | 1430 ++ src/server/game/Entities/Item/ItemPrototype.h | 670 + src/server/game/Entities/Object/Corpse.cpp | 243 + src/server/game/Entities/Object/Corpse.h | 97 + src/server/game/Entities/Object/DynamicObject.cpp | 189 + src/server/game/Entities/Object/DynamicObject.h | 61 + src/server/game/Entities/Object/Object.cpp | 2374 ++ src/server/game/Entities/Object/Object.h | 743 + src/server/game/Entities/Object/ObjectAccessor.cpp | 375 + src/server/game/Entities/Object/ObjectAccessor.h | 257 + src/server/game/Entities/Object/ObjectDefines.h | 127 + src/server/game/Entities/Object/ObjectMgr.cpp | 8724 +++++++ src/server/game/Entities/Object/ObjectMgr.h | 1085 + src/server/game/Entities/Object/UpdateData.cpp | 157 + src/server/game/Entities/Object/UpdateData.h | 74 + src/server/game/Entities/Object/UpdateFields.h | 435 + src/server/game/Entities/Object/UpdateMask.h | 127 + src/server/game/Entities/Pet/Pet.cpp | 1994 ++ src/server/game/Entities/Pet/Pet.h | 254 + src/server/game/Entities/Pet/PetHandler.cpp | 813 + .../game/Entities/Player/CharacterHandler.cpp | 1406 ++ src/server/game/Entities/Player/DuelHandler.cpp | 84 + src/server/game/Entities/Player/MiscHandler.cpp | 1721 ++ .../game/Entities/Player/PetitionsHandler.cpp | 938 + src/server/game/Entities/Player/Player.cpp | 23935 +++++++++++++++++++ src/server/game/Entities/Player/Player.h | 2646 ++ src/server/game/Entities/Player/SocialMgr.cpp | 323 + src/server/game/Entities/Player/SocialMgr.h | 161 + src/server/game/Entities/Player/TicketHandler.cpp | 161 + src/server/game/Entities/Player/TradeHandler.cpp | 656 + .../game/Entities/Player/VoiceChatHandler.cpp | 50 + src/server/game/Entities/Totem/Totem.cpp | 182 + src/server/game/Entities/Totem/Totem.h | 64 + src/server/game/Entities/Unit/StatSystem.cpp | 1263 + src/server/game/Entities/Unit/Unit.cpp | 16510 +++++++++++++ src/server/game/Entities/Unit/Unit.h | 2119 ++ src/server/game/Entities/Vehicle/Vehicle.cpp | 375 + src/server/game/Entities/Vehicle/Vehicle.h | 88 + src/server/game/Events/GameEventMgr.cpp | 1721 ++ src/server/game/Events/GameEventMgr.h | 181 + src/server/game/Events/GlobalEvents.cpp | 87 + src/server/game/Events/GlobalEvents.h | 32 + src/server/game/Events/UnitEvents.h | 139 + src/server/game/FleeingMovementGenerator.cpp | 444 - src/server/game/FleeingMovementGenerator.h | 83 - src/server/game/FollowerRefManager.h | 34 - src/server/game/FollowerReference.cpp | 39 - src/server/game/FollowerReference.h | 37 - src/server/game/Formulas.h | 164 - src/server/game/GameEventMgr.cpp | 1721 -- src/server/game/GameEventMgr.h | 181 - src/server/game/GameObject.cpp | 1698 -- src/server/game/GameObject.h | 764 - src/server/game/GlobalEvents.cpp | 87 - src/server/game/GlobalEvents.h | 32 - src/server/game/Globals/Formulas.h | 164 + src/server/game/Globals/Language.h | 1007 + src/server/game/Globals/SharedDefines.h | 2772 +++ src/server/game/GossipDef.cpp | 882 - src/server/game/GossipDef.h | 272 - src/server/game/GridDefines.h | 193 - src/server/game/GridNotifiers.cpp | 353 - src/server/game/GridNotifiers.h | 1232 - src/server/game/GridNotifiersImpl.h | 453 - src/server/game/GridStates.cpp | 76 - src/server/game/GridStates.h | 76 - src/server/game/Group.cpp | 1867 -- src/server/game/Group.h | 461 - src/server/game/GroupHandler.cpp | 940 - src/server/game/GroupRefManager.h | 36 - src/server/game/GroupReference.cpp | 42 - src/server/game/GroupReference.h | 44 - src/server/game/Groups/Group.cpp | 1867 ++ src/server/game/Groups/Group.h | 461 + src/server/game/Groups/GroupHandler.cpp | 940 + src/server/game/Groups/GroupRefManager.h | 36 + src/server/game/Groups/GroupReference.cpp | 42 + src/server/game/Groups/GroupReference.h | 44 + src/server/game/GuardAI.cpp | 137 - src/server/game/GuardAI.h | 55 - src/server/game/Guild.cpp | 2323 -- src/server/game/Guild.h | 481 - src/server/game/GuildHandler.cpp | 1225 - src/server/game/Guilds/Guild.cpp | 2323 ++ src/server/game/Guilds/Guild.h | 481 + src/server/game/Guilds/GuildHandler.cpp | 1225 + src/server/game/HomeMovementGenerator.cpp | 92 - src/server/game/HomeMovementGenerator.h | 60 - src/server/game/HostileRefManager.cpp | 189 - src/server/game/HostileRefManager.h | 74 - src/server/game/IdleMovementGenerator.cpp | 114 - src/server/game/IdleMovementGenerator.h | 82 - src/server/game/InstanceData.cpp | 380 - src/server/game/InstanceData.h | 201 - src/server/game/InstanceSaveMgr.cpp | 710 - src/server/game/InstanceSaveMgr.h | 193 - src/server/game/Instances/InstanceData.cpp | 380 + src/server/game/Instances/InstanceData.h | 201 + src/server/game/Instances/InstanceSaveMgr.cpp | 710 + src/server/game/Instances/InstanceSaveMgr.h | 193 + src/server/game/Item.cpp | 1150 - src/server/game/Item.h | 356 - src/server/game/ItemEnchantmentMgr.cpp | 214 - src/server/game/ItemEnchantmentMgr.h | 30 - src/server/game/ItemHandler.cpp | 1430 -- src/server/game/ItemPrototype.h | 670 - src/server/game/LFG.h | 72 - src/server/game/LFGHandler.cpp | 251 - src/server/game/LFGMgr.cpp | 1100 - src/server/game/LFGMgr.h | 279 - src/server/game/Language.h | 1007 - src/server/game/Level0.cpp | 295 - src/server/game/Level1.cpp | 2960 --- src/server/game/Level2.cpp | 4526 ---- src/server/game/Level3.cpp | 7743 ------ src/server/game/LookingForGroup/LFG.h | 72 + src/server/game/LookingForGroup/LFGHandler.cpp | 251 + src/server/game/LookingForGroup/LFGMgr.cpp | 1100 + src/server/game/LookingForGroup/LFGMgr.h | 279 + src/server/game/Loot/LootHandler.cpp | 549 + src/server/game/Loot/LootMgr.cpp | 1639 ++ src/server/game/Loot/LootMgr.h | 403 + src/server/game/LootHandler.cpp | 549 - src/server/game/LootMgr.cpp | 1639 -- src/server/game/LootMgr.h | 403 - src/server/game/Mail.cpp | 1011 - src/server/game/Mail.h | 202 - src/server/game/Mails/Mail.cpp | 1011 + src/server/game/Mails/Mail.h | 202 + src/server/game/Map.cpp | 3936 --- src/server/game/Map.h | 689 - src/server/game/Map/Cell/Cell.h | 177 + src/server/game/Map/Cell/CellImpl.h | 297 + src/server/game/Map/Grid/GridDefines.h | 193 + src/server/game/Map/Grid/GridNotifiers.cpp | 353 + src/server/game/Map/Grid/GridNotifiers.h | 1232 + src/server/game/Map/Grid/GridNotifiersImpl.h | 453 + src/server/game/Map/Grid/GridStates.cpp | 76 + src/server/game/Map/Grid/GridStates.h | 76 + src/server/game/Map/Grid/ObjectGridLoader.cpp | 325 + src/server/game/Map/Grid/ObjectGridLoader.h | 134 + src/server/game/Map/Map.cpp | 3936 +++ src/server/game/Map/Map.h | 689 + src/server/game/Map/MapInstanced.cpp | 268 + src/server/game/Map/MapInstanced.h | 80 + src/server/game/Map/MapManager.cpp | 395 + src/server/game/Map/MapManager.h | 158 + src/server/game/Map/MapRefManager.h | 45 + src/server/game/Map/MapReference.h | 53 + src/server/game/Map/MapUpdater.cpp | 129 + src/server/game/Map/MapUpdater.h | 40 + src/server/game/Map/ObjectPosSelector.cpp | 157 + src/server/game/Map/ObjectPosSelector.h | 155 + src/server/game/Map/ZoneScript.h | 51 + src/server/game/MapInstanced.cpp | 268 - src/server/game/MapInstanced.h | 80 - src/server/game/MapManager.cpp | 395 - src/server/game/MapManager.h | 158 - src/server/game/MapRefManager.h | 45 - src/server/game/MapReference.h | 53 - src/server/game/MapUpdater.cpp | 129 - src/server/game/MapUpdater.h | 40 - src/server/game/MiscHandler.cpp | 1721 -- src/server/game/MotionMaster.cpp | 591 - src/server/game/MotionMaster.h | 189 - src/server/game/Movement/DestinationHolder.cpp | 22 + src/server/game/Movement/DestinationHolder.h | 67 + src/server/game/Movement/DestinationHolderImp.h | 217 + src/server/game/Movement/FollowerRefManager.h | 34 + src/server/game/Movement/FollowerReference.cpp | 39 + src/server/game/Movement/FollowerReference.h | 37 + src/server/game/Movement/MotionMaster.cpp | 591 + src/server/game/Movement/MotionMaster.h | 189 + .../ConfusedMovementGenerator.cpp | 181 + .../MovementGenerators/ConfusedMovementGenerator.h | 59 + .../FleeingMovementGenerator.cpp | 444 + .../MovementGenerators/FleeingMovementGenerator.h | 83 + .../MovementGenerators/HomeMovementGenerator.cpp | 92 + .../MovementGenerators/HomeMovementGenerator.h | 60 + .../MovementGenerators/IdleMovementGenerator.cpp | 114 + .../MovementGenerators/IdleMovementGenerator.h | 82 + .../MovementGenerators/MovementGenerator.cpp | 27 + .../MovementGenerators/MovementGenerator.h | 102 + .../MovementGenerators/MovementGeneratorImpl.h | 34 + .../MovementGenerators/PointMovementGenerator.cpp | 106 + .../MovementGenerators/PointMovementGenerator.h | 68 + .../MovementGenerators/RandomMovementGenerator.cpp | 205 + .../MovementGenerators/RandomMovementGenerator.h | 56 + .../TargetedMovementGenerator.cpp | 280 + .../MovementGenerators/TargetedMovementGenerator.h | 76 + .../WaypointMovementGenerator.cpp | 682 + .../MovementGenerators/WaypointMovementGenerator.h | 129 + src/server/game/Movement/MovementHandler.cpp | 746 + src/server/game/Movement/Path.h | 88 + src/server/game/Movement/TaxiHandler.cpp | 287 + src/server/game/Movement/Transports.cpp | 589 + src/server/game/Movement/Transports.h | 108 + src/server/game/Movement/Traveller.h | 155 + src/server/game/Movement/WaypointManager.cpp | 152 + src/server/game/Movement/WaypointManager.h | 68 + src/server/game/MovementGenerator.cpp | 27 - src/server/game/MovementGenerator.h | 102 - src/server/game/MovementGeneratorImpl.h | 34 - src/server/game/MovementHandler.cpp | 746 - src/server/game/NPCHandler.cpp | 869 - src/server/game/NPCHandler.h | 79 - src/server/game/Object.cpp | 2374 -- src/server/game/Object.h | 743 - src/server/game/ObjectAccessor.cpp | 375 - src/server/game/ObjectAccessor.h | 257 - src/server/game/ObjectDefines.h | 127 - src/server/game/ObjectGridLoader.cpp | 325 - src/server/game/ObjectGridLoader.h | 134 - src/server/game/ObjectMgr.cpp | 8724 ------- src/server/game/ObjectMgr.h | 1085 - src/server/game/ObjectPosSelector.cpp | 157 - src/server/game/ObjectPosSelector.h | 155 - src/server/game/Opcodes.cpp | 1338 -- src/server/game/Opcodes.h | 1378 -- src/server/game/Opcodes/Opcodes.cpp | 1338 ++ src/server/game/Opcodes/Opcodes.h | 1378 ++ src/server/game/OutdoorPvP.cpp | 598 - src/server/game/OutdoorPvP.h | 260 - src/server/game/OutdoorPvP/OutdoorPvP.cpp | 598 + src/server/game/OutdoorPvP/OutdoorPvP.h | 260 + src/server/game/OutdoorPvP/OutdoorPvPEP.cpp | 765 + src/server/game/OutdoorPvP/OutdoorPvPEP.h | 280 + src/server/game/OutdoorPvP/OutdoorPvPHP.cpp | 332 + src/server/game/OutdoorPvP/OutdoorPvPHP.h | 118 + src/server/game/OutdoorPvP/OutdoorPvPImpl.h | 28 + src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp | 249 + src/server/game/OutdoorPvP/OutdoorPvPMgr.h | 85 + src/server/game/OutdoorPvP/OutdoorPvPNA.cpp | 664 + src/server/game/OutdoorPvP/OutdoorPvPNA.h | 297 + src/server/game/OutdoorPvP/OutdoorPvPSI.cpp | 227 + src/server/game/OutdoorPvP/OutdoorPvPSI.h | 75 + src/server/game/OutdoorPvP/OutdoorPvPTF.cpp | 310 + src/server/game/OutdoorPvP/OutdoorPvPTF.h | 117 + src/server/game/OutdoorPvP/OutdoorPvPZM.cpp | 423 + src/server/game/OutdoorPvP/OutdoorPvPZM.h | 219 + src/server/game/OutdoorPvPEP.cpp | 765 - src/server/game/OutdoorPvPEP.h | 280 - src/server/game/OutdoorPvPHP.cpp | 332 - src/server/game/OutdoorPvPHP.h | 118 - src/server/game/OutdoorPvPImpl.h | 28 - src/server/game/OutdoorPvPMgr.cpp | 249 - src/server/game/OutdoorPvPMgr.h | 85 - src/server/game/OutdoorPvPNA.cpp | 664 - src/server/game/OutdoorPvPNA.h | 297 - src/server/game/OutdoorPvPSI.cpp | 227 - src/server/game/OutdoorPvPSI.h | 75 - src/server/game/OutdoorPvPTF.cpp | 310 - src/server/game/OutdoorPvPTF.h | 117 - src/server/game/OutdoorPvPZM.cpp | 423 - src/server/game/OutdoorPvPZM.h | 219 - src/server/game/PassiveAI.cpp | 81 - src/server/game/PassiveAI.h | 86 - src/server/game/Path.h | 88 - src/server/game/Pet.cpp | 1994 -- src/server/game/Pet.h | 254 - src/server/game/PetAI.cpp | 471 - src/server/game/PetAI.h | 64 - src/server/game/PetHandler.cpp | 813 - src/server/game/PetitionsHandler.cpp | 938 - src/server/game/Player.cpp | 23935 ------------------- src/server/game/Player.h | 2646 -- src/server/game/PlayerDump.cpp | 631 - src/server/game/PlayerDump.h | 100 - src/server/game/PointMovementGenerator.cpp | 106 - src/server/game/PointMovementGenerator.h | 68 - src/server/game/PoolHandler.cpp | 753 - src/server/game/PoolHandler.h | 172 - src/server/game/Pools/PoolHandler.cpp | 753 + src/server/game/Pools/PoolHandler.h | 172 + src/server/game/PrecompiledHeaders/pchdef.cpp | 1 + src/server/game/PrecompiledHeaders/pchdef.h | 14 + src/server/game/PrecompiledHeaders/pchlinux.cpp | 1 + src/server/game/PrecompiledHeaders/pchlinux.h | 12 + src/server/game/QueryHandler.cpp | 539 - src/server/game/QuestDef.cpp | 206 - src/server/game/QuestDef.h | 372 - src/server/game/QuestHandler.cpp | 721 - src/server/game/Quests/QueryHandler.cpp | 539 + src/server/game/Quests/QuestDef.cpp | 206 + src/server/game/Quests/QuestDef.h | 372 + src/server/game/Quests/QuestHandler.cpp | 721 + src/server/game/RandomMovementGenerator.cpp | 205 - src/server/game/RandomMovementGenerator.h | 56 - src/server/game/ReactorAI.cpp | 59 - src/server/game/ReactorAI.h | 40 - src/server/game/Reputation/ReputationMgr.cpp | 547 + src/server/game/Reputation/ReputationMgr.h | 154 + src/server/game/ReputationMgr.cpp | 547 - src/server/game/ReputationMgr.h | 154 - src/server/game/ScriptLoader.cpp | 1023 - src/server/game/ScriptLoader.h | 10 - src/server/game/ScriptMgr.cpp | 549 - src/server/game/ScriptMgr.h | 163 - src/server/game/ScriptMgr/ScriptLoader.cpp | 1023 + src/server/game/ScriptMgr/ScriptLoader.h | 10 + src/server/game/ScriptMgr/ScriptMgr.cpp | 549 + src/server/game/ScriptMgr/ScriptMgr.h | 163 + src/server/game/ScriptMgr/ScriptSystem.cpp | 248 + src/server/game/ScriptMgr/ScriptSystem.h | 100 + src/server/game/ScriptMgr/ScriptedPch.cpp | 6 + src/server/game/ScriptMgr/ScriptedPch.h | 32 + src/server/game/ScriptSystem.cpp | 248 - src/server/game/ScriptSystem.h | 100 - src/server/game/ScriptedCreature.cpp | 740 - src/server/game/ScriptedCreature.h | 290 - src/server/game/ScriptedEscortAI.cpp | 506 - src/server/game/ScriptedEscortAI.h | 116 - src/server/game/ScriptedFollowerAI.cpp | 387 - src/server/game/ScriptedFollowerAI.h | 67 - src/server/game/ScriptedGossip.h | 187 - src/server/game/ScriptedGuardAI.cpp | 194 - src/server/game/ScriptedGuardAI.h | 45 - src/server/game/ScriptedInstance.h | 20 - src/server/game/ScriptedPch.cpp | 6 - src/server/game/ScriptedPch.h | 32 - src/server/game/ScriptedSimpleAI.cpp | 278 - src/server/game/ScriptedSimpleAI.h | 71 - src/server/game/ScriptedSmartAI.cpp | 8 - src/server/game/ScriptedSmartAI.h | 8 - src/server/game/SharedDefines.h | 2772 --- src/server/game/SkillDiscovery.cpp | 239 - src/server/game/SkillDiscovery.h | 32 - src/server/game/SkillExtraItems.cpp | 145 - src/server/game/SkillExtraItems.h | 33 - src/server/game/SkillHandler.cpp | 95 - src/server/game/Skills/SkillDiscovery.cpp | 239 + src/server/game/Skills/SkillDiscovery.h | 32 + src/server/game/Skills/SkillExtraItems.cpp | 145 + src/server/game/Skills/SkillExtraItems.h | 33 + src/server/game/Skills/SkillHandler.cpp | 95 + src/server/game/SocialMgr.cpp | 323 - src/server/game/SocialMgr.h | 161 - src/server/game/Spell.cpp | 6935 ------ src/server/game/Spell.h | 800 - src/server/game/SpellAuraDefines.h | 372 - src/server/game/SpellAuraEffects.cpp | 6228 ----- src/server/game/SpellAuraEffects.h | 266 - src/server/game/SpellAuras.cpp | 1689 -- src/server/game/SpellAuras.h | 226 - src/server/game/SpellEffects.cpp | 7990 ------- src/server/game/SpellHandler.cpp | 649 - src/server/game/SpellMgr.cpp | 3888 --- src/server/game/SpellMgr.h | 1400 -- src/server/game/Spells/Auras/SpellAuraDefines.h | 372 + src/server/game/Spells/Auras/SpellAuraEffects.cpp | 6228 +++++ src/server/game/Spells/Auras/SpellAuraEffects.h | 266 + src/server/game/Spells/Auras/SpellAuras.cpp | 1689 ++ src/server/game/Spells/Auras/SpellAuras.h | 226 + src/server/game/Spells/Auras/SpellEffects.cpp | 7990 +++++++ src/server/game/Spells/Spell.cpp | 6935 ++++++ src/server/game/Spells/Spell.h | 800 + src/server/game/Spells/SpellHandler.cpp | 649 + src/server/game/Spells/SpellMgr.cpp | 3888 +++ src/server/game/Spells/SpellMgr.h | 1400 ++ src/server/game/StatSystem.cpp | 1263 - src/server/game/TargetedMovementGenerator.cpp | 280 - src/server/game/TargetedMovementGenerator.h | 76 - src/server/game/TaxiHandler.cpp | 287 - src/server/game/TemporarySummon.cpp | 376 - src/server/game/TemporarySummon.h | 101 - src/server/game/ThreatManager.cpp | 553 - src/server/game/ThreatManager.h | 279 - src/server/game/TicketHandler.cpp | 161 - src/server/game/TimeMgr.cpp | 36 - src/server/game/TimeMgr.h | 92 - src/server/game/Tools.cpp | 117 - src/server/game/Tools.h | 29 - src/server/game/Tools/PlayerDump.cpp | 631 + src/server/game/Tools/PlayerDump.h | 100 + src/server/game/Tools/Tools.cpp | 117 + src/server/game/Tools/Tools.h | 29 + src/server/game/Totem.cpp | 182 - src/server/game/Totem.h | 64 - src/server/game/TotemAI.cpp | 116 - src/server/game/TotemAI.h | 47 - src/server/game/TradeHandler.cpp | 656 - src/server/game/Transports.cpp | 589 - src/server/game/Transports.h | 108 - src/server/game/Traveller.h | 155 - src/server/game/Unit.cpp | 16510 ------------- src/server/game/Unit.h | 2119 -- src/server/game/UnitAI.cpp | 327 - src/server/game/UnitAI.h | 161 - src/server/game/UnitEvents.h | 139 - src/server/game/UpdateData.cpp | 157 - src/server/game/UpdateData.h | 74 - src/server/game/UpdateFields.h | 435 - src/server/game/UpdateMask.h | 127 - src/server/game/Vehicle.cpp | 375 - src/server/game/Vehicle.h | 88 - src/server/game/VoiceChatHandler.cpp | 50 - src/server/game/WaypointManager.cpp | 152 - src/server/game/WaypointManager.h | 68 - src/server/game/WaypointMovementGenerator.cpp | 682 - src/server/game/WaypointMovementGenerator.h | 129 - src/server/game/Weather.cpp | 320 - src/server/game/Weather.h | 75 - src/server/game/Weather/Weather.cpp | 320 + src/server/game/Weather/Weather.h | 75 + src/server/game/World.cpp | 2733 --- src/server/game/World.h | 784 - src/server/game/World/TimeMgr.cpp | 36 + src/server/game/World/TimeMgr.h | 92 + src/server/game/World/World.cpp | 2733 +++ src/server/game/World/World.h | 784 + src/server/game/World/WorldLog.cpp | 122 + src/server/game/World/WorldLog.h | 63 + src/server/game/World/WorldSession.cpp | 959 + src/server/game/World/WorldSession.h | 829 + src/server/game/World/WorldSocket.cpp | 1064 + src/server/game/World/WorldSocket.h | 223 + src/server/game/World/WorldSocketMgr.cpp | 369 + src/server/game/World/WorldSocketMgr.h | 80 + src/server/game/WorldLog.cpp | 122 - src/server/game/WorldLog.h | 63 - src/server/game/WorldSession.cpp | 959 - src/server/game/WorldSession.h | 829 - src/server/game/WorldSocket.cpp | 1064 - src/server/game/WorldSocket.h | 223 - src/server/game/WorldSocketMgr.cpp | 369 - src/server/game/WorldSocketMgr.h | 80 - src/server/game/ZoneScript.h | 51 - src/server/game/pchdef.cpp | 1 - src/server/game/pchdef.h | 14 - src/server/game/pchlinux.cpp | 1 - src/server/game/pchlinux.h | 12 - 664 files changed, 235776 insertions(+), 235776 deletions(-) create mode 100644 src/server/game/AI/AuctionHouseBot/AuctionHouseBot.cpp create mode 100644 src/server/game/AI/AuctionHouseBot/AuctionHouseBot.h create mode 100644 src/server/game/AI/CombatAI.cpp create mode 100644 src/server/game/AI/CombatAI.h create mode 100644 src/server/game/AI/CreatureAI.cpp create mode 100644 src/server/game/AI/CreatureAI.h create mode 100644 src/server/game/AI/CreatureAIFactory.h create mode 100644 src/server/game/AI/CreatureAIImpl.h create mode 100644 src/server/game/AI/CreatureAIRegistry.cpp create mode 100644 src/server/game/AI/CreatureAIRegistry.h create mode 100644 src/server/game/AI/CreatureAISelector.cpp create mode 100644 src/server/game/AI/CreatureAISelector.h create mode 100644 src/server/game/AI/EventAI/CreatureEventAI.cpp create mode 100644 src/server/game/AI/EventAI/CreatureEventAI.h create mode 100644 src/server/game/AI/EventAI/CreatureEventAIMgr.cpp create mode 100644 src/server/game/AI/EventAI/CreatureEventAIMgr.h create mode 100644 src/server/game/AI/GuardAI.cpp create mode 100644 src/server/game/AI/GuardAI.h create mode 100644 src/server/game/AI/PassiveAI.cpp create mode 100644 src/server/game/AI/PassiveAI.h create mode 100644 src/server/game/AI/PetAI.cpp create mode 100644 src/server/game/AI/PetAI.h create mode 100644 src/server/game/AI/ReactorAI.cpp create mode 100644 src/server/game/AI/ReactorAI.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedCreature.cpp create mode 100644 src/server/game/AI/ScriptedAI/ScriptedCreature.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp create mode 100644 src/server/game/AI/ScriptedAI/ScriptedEscortAI.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp create mode 100644 src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedGossip.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedGuardAI.cpp create mode 100644 src/server/game/AI/ScriptedAI/ScriptedGuardAI.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedInstance.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedSimpleAI.cpp create mode 100644 src/server/game/AI/ScriptedAI/ScriptedSimpleAI.h create mode 100644 src/server/game/AI/ScriptedAI/ScriptedSmartAI.cpp create mode 100644 src/server/game/AI/ScriptedAI/ScriptedSmartAI.h create mode 100644 src/server/game/AI/TotemAI.cpp create mode 100644 src/server/game/AI/TotemAI.h create mode 100644 src/server/game/AI/UnitAI.cpp create mode 100644 src/server/game/AI/UnitAI.h create mode 100644 src/server/game/Account/AccountMgr.cpp create mode 100644 src/server/game/Account/AccountMgr.h delete mode 100644 src/server/game/AccountMgr.cpp delete mode 100644 src/server/game/AccountMgr.h delete mode 100644 src/server/game/AchievementMgr.cpp delete mode 100644 src/server/game/AchievementMgr.h create mode 100644 src/server/game/Achievements/AchievementMgr.cpp create mode 100644 src/server/game/Achievements/AchievementMgr.h delete mode 100644 src/server/game/AddonHandler.cpp delete mode 100644 src/server/game/AddonHandler.h delete mode 100644 src/server/game/AddonMgr.cpp delete mode 100644 src/server/game/AddonMgr.h create mode 100644 src/server/game/Addons/AddonHandler.cpp create mode 100644 src/server/game/Addons/AddonHandler.h create mode 100644 src/server/game/Addons/AddonMgr.cpp create mode 100644 src/server/game/Addons/AddonMgr.h delete mode 100644 src/server/game/ArenaTeam.cpp delete mode 100644 src/server/game/ArenaTeam.h delete mode 100644 src/server/game/ArenaTeamHandler.cpp create mode 100644 src/server/game/AuctionHouse/AuctionHouseHandler.cpp create mode 100644 src/server/game/AuctionHouse/AuctionHouseMgr.cpp create mode 100644 src/server/game/AuctionHouse/AuctionHouseMgr.h delete mode 100644 src/server/game/AuctionHouseBot.cpp delete mode 100644 src/server/game/AuctionHouseBot.h delete mode 100644 src/server/game/AuctionHouseHandler.cpp delete mode 100755 src/server/game/AuctionHouseMgr.cpp delete mode 100644 src/server/game/AuctionHouseMgr.h delete mode 100644 src/server/game/Bag.cpp delete mode 100644 src/server/game/Bag.h delete mode 100644 src/server/game/BattleGround.cpp delete mode 100644 src/server/game/BattleGround.h delete mode 100644 src/server/game/BattleGroundAA.cpp delete mode 100644 src/server/game/BattleGroundAA.h delete mode 100644 src/server/game/BattleGroundAB.cpp delete mode 100644 src/server/game/BattleGroundAB.h delete mode 100644 src/server/game/BattleGroundAV.cpp delete mode 100644 src/server/game/BattleGroundAV.h delete mode 100644 src/server/game/BattleGroundBE.cpp delete mode 100644 src/server/game/BattleGroundBE.h delete mode 100644 src/server/game/BattleGroundDS.cpp delete mode 100644 src/server/game/BattleGroundDS.h delete mode 100644 src/server/game/BattleGroundEY.cpp delete mode 100644 src/server/game/BattleGroundEY.h delete mode 100644 src/server/game/BattleGroundHandler.cpp delete mode 100644 src/server/game/BattleGroundIC.cpp delete mode 100644 src/server/game/BattleGroundIC.h delete mode 100644 src/server/game/BattleGroundMgr.cpp delete mode 100644 src/server/game/BattleGroundMgr.h delete mode 100644 src/server/game/BattleGroundNA.cpp delete mode 100644 src/server/game/BattleGroundNA.h delete mode 100644 src/server/game/BattleGroundRB.cpp delete mode 100644 src/server/game/BattleGroundRB.h delete mode 100644 src/server/game/BattleGroundRL.cpp delete mode 100644 src/server/game/BattleGroundRL.h delete mode 100644 src/server/game/BattleGroundRV.cpp delete mode 100644 src/server/game/BattleGroundRV.h delete mode 100644 src/server/game/BattleGroundSA.cpp delete mode 100644 src/server/game/BattleGroundSA.h delete mode 100644 src/server/game/BattleGroundWS.cpp delete mode 100644 src/server/game/BattleGroundWS.h create mode 100644 src/server/game/BattleGrounds/ArenaTeam.cpp create mode 100644 src/server/game/BattleGrounds/ArenaTeam.h create mode 100644 src/server/game/BattleGrounds/ArenaTeamHandler.cpp create mode 100644 src/server/game/BattleGrounds/BattleGround.cpp create mode 100644 src/server/game/BattleGrounds/BattleGround.h create mode 100644 src/server/game/BattleGrounds/BattleGroundAA.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundAA.h create mode 100644 src/server/game/BattleGrounds/BattleGroundAB.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundAB.h create mode 100644 src/server/game/BattleGrounds/BattleGroundAV.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundAV.h create mode 100644 src/server/game/BattleGrounds/BattleGroundBE.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundBE.h create mode 100644 src/server/game/BattleGrounds/BattleGroundDS.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundDS.h create mode 100644 src/server/game/BattleGrounds/BattleGroundEY.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundEY.h create mode 100644 src/server/game/BattleGrounds/BattleGroundHandler.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundIC.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundIC.h create mode 100644 src/server/game/BattleGrounds/BattleGroundMgr.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundMgr.h create mode 100644 src/server/game/BattleGrounds/BattleGroundNA.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundNA.h create mode 100644 src/server/game/BattleGrounds/BattleGroundRB.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundRB.h create mode 100644 src/server/game/BattleGrounds/BattleGroundRL.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundRL.h create mode 100644 src/server/game/BattleGrounds/BattleGroundRV.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundRV.h create mode 100644 src/server/game/BattleGrounds/BattleGroundSA.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundSA.h create mode 100644 src/server/game/BattleGrounds/BattleGroundWS.cpp create mode 100644 src/server/game/BattleGrounds/BattleGroundWS.h delete mode 100644 src/server/game/Calendar.cpp delete mode 100644 src/server/game/Calendar.h delete mode 100644 src/server/game/CalendarHandler.cpp create mode 100644 src/server/game/Calender/Calendar.cpp create mode 100644 src/server/game/Calender/Calendar.h create mode 100644 src/server/game/Calender/CalendarHandler.cpp delete mode 100644 src/server/game/Cell.h delete mode 100644 src/server/game/CellImpl.h delete mode 100644 src/server/game/Channel.cpp delete mode 100644 src/server/game/Channel.h delete mode 100644 src/server/game/ChannelHandler.cpp delete mode 100644 src/server/game/ChannelMgr.cpp delete mode 100644 src/server/game/ChannelMgr.h delete mode 100644 src/server/game/CharacterHandler.cpp delete mode 100644 src/server/game/Chat.cpp delete mode 100644 src/server/game/Chat.h create mode 100644 src/server/game/Chat/Channel.cpp create mode 100644 src/server/game/Chat/Channel.h create mode 100644 src/server/game/Chat/ChannelHandler.cpp create mode 100644 src/server/game/Chat/ChannelMgr.cpp create mode 100644 src/server/game/Chat/ChannelMgr.h create mode 100644 src/server/game/Chat/Chat.cpp create mode 100644 src/server/game/Chat/Chat.h create mode 100644 src/server/game/Chat/ChatHandler.cpp create mode 100644 src/server/game/Chat/Debugcmds.cpp create mode 100644 src/server/game/Chat/Level0.cpp create mode 100644 src/server/game/Chat/Level1.cpp create mode 100644 src/server/game/Chat/Level2.cpp create mode 100644 src/server/game/Chat/Level3.cpp delete mode 100644 src/server/game/ChatHandler.cpp create mode 100644 src/server/game/Combat/CombatHandler.cpp create mode 100644 src/server/game/Combat/HostileRefManager.cpp create mode 100644 src/server/game/Combat/HostileRefManager.h create mode 100644 src/server/game/Combat/ThreatManager.cpp create mode 100644 src/server/game/Combat/ThreatManager.h delete mode 100644 src/server/game/CombatAI.cpp delete mode 100644 src/server/game/CombatAI.h delete mode 100644 src/server/game/CombatHandler.cpp delete mode 100644 src/server/game/ConditionMgr.cpp delete mode 100644 src/server/game/ConditionMgr.h create mode 100644 src/server/game/ConditionMgr/ConditionMgr.cpp create mode 100644 src/server/game/ConditionMgr/ConditionMgr.h delete mode 100644 src/server/game/ConfusedMovementGenerator.cpp delete mode 100644 src/server/game/ConfusedMovementGenerator.h delete mode 100644 src/server/game/Corpse.cpp delete mode 100644 src/server/game/Corpse.h delete mode 100644 src/server/game/Creature.cpp delete mode 100644 src/server/game/Creature.h delete mode 100644 src/server/game/CreatureAI.cpp delete mode 100644 src/server/game/CreatureAI.h delete mode 100644 src/server/game/CreatureAIFactory.h delete mode 100644 src/server/game/CreatureAIImpl.h delete mode 100644 src/server/game/CreatureAIRegistry.cpp delete mode 100644 src/server/game/CreatureAIRegistry.h delete mode 100644 src/server/game/CreatureAISelector.cpp delete mode 100644 src/server/game/CreatureAISelector.h delete mode 100644 src/server/game/CreatureEventAI.cpp delete mode 100644 src/server/game/CreatureEventAI.h delete mode 100644 src/server/game/CreatureEventAIMgr.cpp delete mode 100644 src/server/game/CreatureEventAIMgr.h delete mode 100644 src/server/game/CreatureGroups.cpp delete mode 100644 src/server/game/CreatureGroups.h delete mode 100644 src/server/game/DBCEnums.h delete mode 100644 src/server/game/DBCStores.cpp delete mode 100644 src/server/game/DBCStores.h delete mode 100644 src/server/game/DBCStructure.h delete mode 100644 src/server/game/DBCfmt.h create mode 100644 src/server/game/DataStores/DBCEnums.h create mode 100644 src/server/game/DataStores/DBCStores.cpp create mode 100644 src/server/game/DataStores/DBCStores.h create mode 100644 src/server/game/DataStores/DBCStructure.h create mode 100644 src/server/game/DataStores/DBCfmt.h delete mode 100644 src/server/game/Debugcmds.cpp delete mode 100644 src/server/game/DestinationHolder.cpp delete mode 100644 src/server/game/DestinationHolder.h delete mode 100644 src/server/game/DestinationHolderImp.h delete mode 100644 src/server/game/DuelHandler.cpp delete mode 100644 src/server/game/DynamicObject.cpp delete mode 100644 src/server/game/DynamicObject.h create mode 100644 src/server/game/Entities/Creature/Creature.cpp create mode 100644 src/server/game/Entities/Creature/Creature.h create mode 100644 src/server/game/Entities/Creature/CreatureGroups.cpp create mode 100644 src/server/game/Entities/Creature/CreatureGroups.h create mode 100644 src/server/game/Entities/Creature/GossipDef.cpp create mode 100644 src/server/game/Entities/Creature/GossipDef.h create mode 100644 src/server/game/Entities/Creature/NPCHandler.cpp create mode 100644 src/server/game/Entities/Creature/NPCHandler.h create mode 100644 src/server/game/Entities/Creature/TemporarySummon.cpp create mode 100644 src/server/game/Entities/Creature/TemporarySummon.h create mode 100644 src/server/game/Entities/GameObject/GameObject.cpp create mode 100644 src/server/game/Entities/GameObject/GameObject.h create mode 100644 src/server/game/Entities/Item/Bag.cpp create mode 100644 src/server/game/Entities/Item/Bag.h create mode 100644 src/server/game/Entities/Item/Item.cpp create mode 100644 src/server/game/Entities/Item/Item.h create mode 100644 src/server/game/Entities/Item/ItemEnchantmentMgr.cpp create mode 100644 src/server/game/Entities/Item/ItemEnchantmentMgr.h create mode 100644 src/server/game/Entities/Item/ItemHandler.cpp create mode 100644 src/server/game/Entities/Item/ItemPrototype.h create mode 100644 src/server/game/Entities/Object/Corpse.cpp create mode 100644 src/server/game/Entities/Object/Corpse.h create mode 100644 src/server/game/Entities/Object/DynamicObject.cpp create mode 100644 src/server/game/Entities/Object/DynamicObject.h create mode 100644 src/server/game/Entities/Object/Object.cpp create mode 100644 src/server/game/Entities/Object/Object.h create mode 100644 src/server/game/Entities/Object/ObjectAccessor.cpp create mode 100644 src/server/game/Entities/Object/ObjectAccessor.h create mode 100644 src/server/game/Entities/Object/ObjectDefines.h create mode 100644 src/server/game/Entities/Object/ObjectMgr.cpp create mode 100644 src/server/game/Entities/Object/ObjectMgr.h create mode 100644 src/server/game/Entities/Object/UpdateData.cpp create mode 100644 src/server/game/Entities/Object/UpdateData.h create mode 100644 src/server/game/Entities/Object/UpdateFields.h create mode 100644 src/server/game/Entities/Object/UpdateMask.h create mode 100644 src/server/game/Entities/Pet/Pet.cpp create mode 100644 src/server/game/Entities/Pet/Pet.h create mode 100644 src/server/game/Entities/Pet/PetHandler.cpp create mode 100644 src/server/game/Entities/Player/CharacterHandler.cpp create mode 100644 src/server/game/Entities/Player/DuelHandler.cpp create mode 100644 src/server/game/Entities/Player/MiscHandler.cpp create mode 100644 src/server/game/Entities/Player/PetitionsHandler.cpp create mode 100644 src/server/game/Entities/Player/Player.cpp create mode 100644 src/server/game/Entities/Player/Player.h create mode 100644 src/server/game/Entities/Player/SocialMgr.cpp create mode 100644 src/server/game/Entities/Player/SocialMgr.h create mode 100644 src/server/game/Entities/Player/TicketHandler.cpp create mode 100644 src/server/game/Entities/Player/TradeHandler.cpp create mode 100644 src/server/game/Entities/Player/VoiceChatHandler.cpp create mode 100644 src/server/game/Entities/Totem/Totem.cpp create mode 100644 src/server/game/Entities/Totem/Totem.h create mode 100644 src/server/game/Entities/Unit/StatSystem.cpp create mode 100644 src/server/game/Entities/Unit/Unit.cpp create mode 100644 src/server/game/Entities/Unit/Unit.h create mode 100644 src/server/game/Entities/Vehicle/Vehicle.cpp create mode 100644 src/server/game/Entities/Vehicle/Vehicle.h create mode 100644 src/server/game/Events/GameEventMgr.cpp create mode 100644 src/server/game/Events/GameEventMgr.h create mode 100644 src/server/game/Events/GlobalEvents.cpp create mode 100644 src/server/game/Events/GlobalEvents.h create mode 100644 src/server/game/Events/UnitEvents.h delete mode 100644 src/server/game/FleeingMovementGenerator.cpp delete mode 100644 src/server/game/FleeingMovementGenerator.h delete mode 100644 src/server/game/FollowerRefManager.h delete mode 100644 src/server/game/FollowerReference.cpp delete mode 100644 src/server/game/FollowerReference.h delete mode 100644 src/server/game/Formulas.h delete mode 100644 src/server/game/GameEventMgr.cpp delete mode 100644 src/server/game/GameEventMgr.h delete mode 100644 src/server/game/GameObject.cpp delete mode 100644 src/server/game/GameObject.h delete mode 100644 src/server/game/GlobalEvents.cpp delete mode 100644 src/server/game/GlobalEvents.h create mode 100644 src/server/game/Globals/Formulas.h create mode 100644 src/server/game/Globals/Language.h create mode 100644 src/server/game/Globals/SharedDefines.h delete mode 100644 src/server/game/GossipDef.cpp delete mode 100644 src/server/game/GossipDef.h delete mode 100644 src/server/game/GridDefines.h delete mode 100644 src/server/game/GridNotifiers.cpp delete mode 100644 src/server/game/GridNotifiers.h delete mode 100644 src/server/game/GridNotifiersImpl.h delete mode 100644 src/server/game/GridStates.cpp delete mode 100644 src/server/game/GridStates.h delete mode 100644 src/server/game/Group.cpp delete mode 100644 src/server/game/Group.h delete mode 100644 src/server/game/GroupHandler.cpp delete mode 100644 src/server/game/GroupRefManager.h delete mode 100644 src/server/game/GroupReference.cpp delete mode 100644 src/server/game/GroupReference.h create mode 100644 src/server/game/Groups/Group.cpp create mode 100644 src/server/game/Groups/Group.h create mode 100644 src/server/game/Groups/GroupHandler.cpp create mode 100644 src/server/game/Groups/GroupRefManager.h create mode 100644 src/server/game/Groups/GroupReference.cpp create mode 100644 src/server/game/Groups/GroupReference.h delete mode 100644 src/server/game/GuardAI.cpp delete mode 100644 src/server/game/GuardAI.h delete mode 100644 src/server/game/Guild.cpp delete mode 100644 src/server/game/Guild.h delete mode 100644 src/server/game/GuildHandler.cpp create mode 100644 src/server/game/Guilds/Guild.cpp create mode 100644 src/server/game/Guilds/Guild.h create mode 100644 src/server/game/Guilds/GuildHandler.cpp delete mode 100644 src/server/game/HomeMovementGenerator.cpp delete mode 100644 src/server/game/HomeMovementGenerator.h delete mode 100644 src/server/game/HostileRefManager.cpp delete mode 100644 src/server/game/HostileRefManager.h delete mode 100644 src/server/game/IdleMovementGenerator.cpp delete mode 100644 src/server/game/IdleMovementGenerator.h delete mode 100644 src/server/game/InstanceData.cpp delete mode 100644 src/server/game/InstanceData.h delete mode 100644 src/server/game/InstanceSaveMgr.cpp delete mode 100644 src/server/game/InstanceSaveMgr.h create mode 100644 src/server/game/Instances/InstanceData.cpp create mode 100644 src/server/game/Instances/InstanceData.h create mode 100644 src/server/game/Instances/InstanceSaveMgr.cpp create mode 100644 src/server/game/Instances/InstanceSaveMgr.h delete mode 100644 src/server/game/Item.cpp delete mode 100644 src/server/game/Item.h delete mode 100644 src/server/game/ItemEnchantmentMgr.cpp delete mode 100644 src/server/game/ItemEnchantmentMgr.h delete mode 100644 src/server/game/ItemHandler.cpp delete mode 100644 src/server/game/ItemPrototype.h delete mode 100644 src/server/game/LFG.h delete mode 100644 src/server/game/LFGHandler.cpp delete mode 100644 src/server/game/LFGMgr.cpp delete mode 100644 src/server/game/LFGMgr.h delete mode 100644 src/server/game/Language.h delete mode 100644 src/server/game/Level0.cpp delete mode 100644 src/server/game/Level1.cpp delete mode 100644 src/server/game/Level2.cpp delete mode 100644 src/server/game/Level3.cpp create mode 100644 src/server/game/LookingForGroup/LFG.h create mode 100644 src/server/game/LookingForGroup/LFGHandler.cpp create mode 100644 src/server/game/LookingForGroup/LFGMgr.cpp create mode 100644 src/server/game/LookingForGroup/LFGMgr.h create mode 100644 src/server/game/Loot/LootHandler.cpp create mode 100644 src/server/game/Loot/LootMgr.cpp create mode 100644 src/server/game/Loot/LootMgr.h delete mode 100644 src/server/game/LootHandler.cpp delete mode 100644 src/server/game/LootMgr.cpp delete mode 100644 src/server/game/LootMgr.h delete mode 100644 src/server/game/Mail.cpp delete mode 100644 src/server/game/Mail.h create mode 100644 src/server/game/Mails/Mail.cpp create mode 100644 src/server/game/Mails/Mail.h delete mode 100644 src/server/game/Map.cpp delete mode 100644 src/server/game/Map.h create mode 100644 src/server/game/Map/Cell/Cell.h create mode 100644 src/server/game/Map/Cell/CellImpl.h create mode 100644 src/server/game/Map/Grid/GridDefines.h create mode 100644 src/server/game/Map/Grid/GridNotifiers.cpp create mode 100644 src/server/game/Map/Grid/GridNotifiers.h create mode 100644 src/server/game/Map/Grid/GridNotifiersImpl.h create mode 100644 src/server/game/Map/Grid/GridStates.cpp create mode 100644 src/server/game/Map/Grid/GridStates.h create mode 100644 src/server/game/Map/Grid/ObjectGridLoader.cpp create mode 100644 src/server/game/Map/Grid/ObjectGridLoader.h create mode 100644 src/server/game/Map/Map.cpp create mode 100644 src/server/game/Map/Map.h create mode 100644 src/server/game/Map/MapInstanced.cpp create mode 100644 src/server/game/Map/MapInstanced.h create mode 100644 src/server/game/Map/MapManager.cpp create mode 100644 src/server/game/Map/MapManager.h create mode 100644 src/server/game/Map/MapRefManager.h create mode 100644 src/server/game/Map/MapReference.h create mode 100644 src/server/game/Map/MapUpdater.cpp create mode 100644 src/server/game/Map/MapUpdater.h create mode 100644 src/server/game/Map/ObjectPosSelector.cpp create mode 100644 src/server/game/Map/ObjectPosSelector.h create mode 100644 src/server/game/Map/ZoneScript.h delete mode 100644 src/server/game/MapInstanced.cpp delete mode 100644 src/server/game/MapInstanced.h delete mode 100644 src/server/game/MapManager.cpp delete mode 100644 src/server/game/MapManager.h delete mode 100644 src/server/game/MapRefManager.h delete mode 100644 src/server/game/MapReference.h delete mode 100644 src/server/game/MapUpdater.cpp delete mode 100644 src/server/game/MapUpdater.h delete mode 100644 src/server/game/MiscHandler.cpp delete mode 100644 src/server/game/MotionMaster.cpp delete mode 100644 src/server/game/MotionMaster.h create mode 100644 src/server/game/Movement/DestinationHolder.cpp create mode 100644 src/server/game/Movement/DestinationHolder.h create mode 100644 src/server/game/Movement/DestinationHolderImp.h create mode 100644 src/server/game/Movement/FollowerRefManager.h create mode 100644 src/server/game/Movement/FollowerReference.cpp create mode 100644 src/server/game/Movement/FollowerReference.h create mode 100644 src/server/game/Movement/MotionMaster.cpp create mode 100644 src/server/game/Movement/MotionMaster.h create mode 100644 src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/MovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/MovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/MovementGeneratorImpl.h create mode 100644 src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/PointMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h create mode 100644 src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp create mode 100644 src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h create mode 100644 src/server/game/Movement/MovementHandler.cpp create mode 100644 src/server/game/Movement/Path.h create mode 100644 src/server/game/Movement/TaxiHandler.cpp create mode 100644 src/server/game/Movement/Transports.cpp create mode 100644 src/server/game/Movement/Transports.h create mode 100644 src/server/game/Movement/Traveller.h create mode 100644 src/server/game/Movement/WaypointManager.cpp create mode 100644 src/server/game/Movement/WaypointManager.h delete mode 100644 src/server/game/MovementGenerator.cpp delete mode 100644 src/server/game/MovementGenerator.h delete mode 100644 src/server/game/MovementGeneratorImpl.h delete mode 100644 src/server/game/MovementHandler.cpp delete mode 100644 src/server/game/NPCHandler.cpp delete mode 100644 src/server/game/NPCHandler.h delete mode 100644 src/server/game/Object.cpp delete mode 100644 src/server/game/Object.h delete mode 100644 src/server/game/ObjectAccessor.cpp delete mode 100644 src/server/game/ObjectAccessor.h delete mode 100644 src/server/game/ObjectDefines.h delete mode 100644 src/server/game/ObjectGridLoader.cpp delete mode 100644 src/server/game/ObjectGridLoader.h delete mode 100644 src/server/game/ObjectMgr.cpp delete mode 100644 src/server/game/ObjectMgr.h delete mode 100644 src/server/game/ObjectPosSelector.cpp delete mode 100644 src/server/game/ObjectPosSelector.h delete mode 100644 src/server/game/Opcodes.cpp delete mode 100644 src/server/game/Opcodes.h create mode 100644 src/server/game/Opcodes/Opcodes.cpp create mode 100644 src/server/game/Opcodes/Opcodes.h delete mode 100644 src/server/game/OutdoorPvP.cpp delete mode 100644 src/server/game/OutdoorPvP.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvP.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvP.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPEP.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPEP.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPHP.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPHP.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPImpl.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPMgr.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPNA.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPNA.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPSI.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPSI.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPTF.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPTF.h create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPZM.cpp create mode 100644 src/server/game/OutdoorPvP/OutdoorPvPZM.h delete mode 100644 src/server/game/OutdoorPvPEP.cpp delete mode 100644 src/server/game/OutdoorPvPEP.h delete mode 100644 src/server/game/OutdoorPvPHP.cpp delete mode 100644 src/server/game/OutdoorPvPHP.h delete mode 100644 src/server/game/OutdoorPvPImpl.h delete mode 100644 src/server/game/OutdoorPvPMgr.cpp delete mode 100644 src/server/game/OutdoorPvPMgr.h delete mode 100644 src/server/game/OutdoorPvPNA.cpp delete mode 100644 src/server/game/OutdoorPvPNA.h delete mode 100644 src/server/game/OutdoorPvPSI.cpp delete mode 100644 src/server/game/OutdoorPvPSI.h delete mode 100644 src/server/game/OutdoorPvPTF.cpp delete mode 100644 src/server/game/OutdoorPvPTF.h delete mode 100644 src/server/game/OutdoorPvPZM.cpp delete mode 100644 src/server/game/OutdoorPvPZM.h delete mode 100644 src/server/game/PassiveAI.cpp delete mode 100644 src/server/game/PassiveAI.h delete mode 100644 src/server/game/Path.h delete mode 100644 src/server/game/Pet.cpp delete mode 100644 src/server/game/Pet.h delete mode 100644 src/server/game/PetAI.cpp delete mode 100644 src/server/game/PetAI.h delete mode 100644 src/server/game/PetHandler.cpp delete mode 100644 src/server/game/PetitionsHandler.cpp delete mode 100644 src/server/game/Player.cpp delete mode 100644 src/server/game/Player.h delete mode 100644 src/server/game/PlayerDump.cpp delete mode 100644 src/server/game/PlayerDump.h delete mode 100644 src/server/game/PointMovementGenerator.cpp delete mode 100644 src/server/game/PointMovementGenerator.h delete mode 100644 src/server/game/PoolHandler.cpp delete mode 100644 src/server/game/PoolHandler.h create mode 100644 src/server/game/Pools/PoolHandler.cpp create mode 100644 src/server/game/Pools/PoolHandler.h create mode 100644 src/server/game/PrecompiledHeaders/pchdef.cpp create mode 100644 src/server/game/PrecompiledHeaders/pchdef.h create mode 100644 src/server/game/PrecompiledHeaders/pchlinux.cpp create mode 100644 src/server/game/PrecompiledHeaders/pchlinux.h delete mode 100644 src/server/game/QueryHandler.cpp delete mode 100644 src/server/game/QuestDef.cpp delete mode 100644 src/server/game/QuestDef.h delete mode 100644 src/server/game/QuestHandler.cpp create mode 100644 src/server/game/Quests/QueryHandler.cpp create mode 100644 src/server/game/Quests/QuestDef.cpp create mode 100644 src/server/game/Quests/QuestDef.h create mode 100644 src/server/game/Quests/QuestHandler.cpp delete mode 100644 src/server/game/RandomMovementGenerator.cpp delete mode 100644 src/server/game/RandomMovementGenerator.h delete mode 100644 src/server/game/ReactorAI.cpp delete mode 100644 src/server/game/ReactorAI.h create mode 100644 src/server/game/Reputation/ReputationMgr.cpp create mode 100644 src/server/game/Reputation/ReputationMgr.h delete mode 100644 src/server/game/ReputationMgr.cpp delete mode 100644 src/server/game/ReputationMgr.h delete mode 100644 src/server/game/ScriptLoader.cpp delete mode 100644 src/server/game/ScriptLoader.h delete mode 100644 src/server/game/ScriptMgr.cpp delete mode 100644 src/server/game/ScriptMgr.h create mode 100644 src/server/game/ScriptMgr/ScriptLoader.cpp create mode 100644 src/server/game/ScriptMgr/ScriptLoader.h create mode 100644 src/server/game/ScriptMgr/ScriptMgr.cpp create mode 100644 src/server/game/ScriptMgr/ScriptMgr.h create mode 100644 src/server/game/ScriptMgr/ScriptSystem.cpp create mode 100644 src/server/game/ScriptMgr/ScriptSystem.h create mode 100644 src/server/game/ScriptMgr/ScriptedPch.cpp create mode 100644 src/server/game/ScriptMgr/ScriptedPch.h delete mode 100644 src/server/game/ScriptSystem.cpp delete mode 100644 src/server/game/ScriptSystem.h delete mode 100644 src/server/game/ScriptedCreature.cpp delete mode 100644 src/server/game/ScriptedCreature.h delete mode 100644 src/server/game/ScriptedEscortAI.cpp delete mode 100644 src/server/game/ScriptedEscortAI.h delete mode 100644 src/server/game/ScriptedFollowerAI.cpp delete mode 100644 src/server/game/ScriptedFollowerAI.h delete mode 100644 src/server/game/ScriptedGossip.h delete mode 100644 src/server/game/ScriptedGuardAI.cpp delete mode 100644 src/server/game/ScriptedGuardAI.h delete mode 100644 src/server/game/ScriptedInstance.h delete mode 100644 src/server/game/ScriptedPch.cpp delete mode 100644 src/server/game/ScriptedPch.h delete mode 100644 src/server/game/ScriptedSimpleAI.cpp delete mode 100644 src/server/game/ScriptedSimpleAI.h delete mode 100644 src/server/game/ScriptedSmartAI.cpp delete mode 100644 src/server/game/ScriptedSmartAI.h delete mode 100644 src/server/game/SharedDefines.h delete mode 100644 src/server/game/SkillDiscovery.cpp delete mode 100644 src/server/game/SkillDiscovery.h delete mode 100644 src/server/game/SkillExtraItems.cpp delete mode 100644 src/server/game/SkillExtraItems.h delete mode 100644 src/server/game/SkillHandler.cpp create mode 100644 src/server/game/Skills/SkillDiscovery.cpp create mode 100644 src/server/game/Skills/SkillDiscovery.h create mode 100644 src/server/game/Skills/SkillExtraItems.cpp create mode 100644 src/server/game/Skills/SkillExtraItems.h create mode 100644 src/server/game/Skills/SkillHandler.cpp delete mode 100644 src/server/game/SocialMgr.cpp delete mode 100644 src/server/game/SocialMgr.h delete mode 100644 src/server/game/Spell.cpp delete mode 100644 src/server/game/Spell.h delete mode 100644 src/server/game/SpellAuraDefines.h delete mode 100644 src/server/game/SpellAuraEffects.cpp delete mode 100644 src/server/game/SpellAuraEffects.h delete mode 100644 src/server/game/SpellAuras.cpp delete mode 100644 src/server/game/SpellAuras.h delete mode 100644 src/server/game/SpellEffects.cpp delete mode 100644 src/server/game/SpellHandler.cpp delete mode 100644 src/server/game/SpellMgr.cpp delete mode 100644 src/server/game/SpellMgr.h create mode 100644 src/server/game/Spells/Auras/SpellAuraDefines.h create mode 100644 src/server/game/Spells/Auras/SpellAuraEffects.cpp create mode 100644 src/server/game/Spells/Auras/SpellAuraEffects.h create mode 100644 src/server/game/Spells/Auras/SpellAuras.cpp create mode 100644 src/server/game/Spells/Auras/SpellAuras.h create mode 100644 src/server/game/Spells/Auras/SpellEffects.cpp create mode 100644 src/server/game/Spells/Spell.cpp create mode 100644 src/server/game/Spells/Spell.h create mode 100644 src/server/game/Spells/SpellHandler.cpp create mode 100644 src/server/game/Spells/SpellMgr.cpp create mode 100644 src/server/game/Spells/SpellMgr.h delete mode 100644 src/server/game/StatSystem.cpp delete mode 100644 src/server/game/TargetedMovementGenerator.cpp delete mode 100644 src/server/game/TargetedMovementGenerator.h delete mode 100644 src/server/game/TaxiHandler.cpp delete mode 100644 src/server/game/TemporarySummon.cpp delete mode 100644 src/server/game/TemporarySummon.h delete mode 100644 src/server/game/ThreatManager.cpp delete mode 100644 src/server/game/ThreatManager.h delete mode 100644 src/server/game/TicketHandler.cpp delete mode 100644 src/server/game/TimeMgr.cpp delete mode 100644 src/server/game/TimeMgr.h delete mode 100644 src/server/game/Tools.cpp delete mode 100644 src/server/game/Tools.h create mode 100644 src/server/game/Tools/PlayerDump.cpp create mode 100644 src/server/game/Tools/PlayerDump.h create mode 100644 src/server/game/Tools/Tools.cpp create mode 100644 src/server/game/Tools/Tools.h delete mode 100644 src/server/game/Totem.cpp delete mode 100644 src/server/game/Totem.h delete mode 100644 src/server/game/TotemAI.cpp delete mode 100644 src/server/game/TotemAI.h delete mode 100644 src/server/game/TradeHandler.cpp delete mode 100644 src/server/game/Transports.cpp delete mode 100644 src/server/game/Transports.h delete mode 100644 src/server/game/Traveller.h delete mode 100644 src/server/game/Unit.cpp delete mode 100644 src/server/game/Unit.h delete mode 100644 src/server/game/UnitAI.cpp delete mode 100644 src/server/game/UnitAI.h delete mode 100644 src/server/game/UnitEvents.h delete mode 100644 src/server/game/UpdateData.cpp delete mode 100644 src/server/game/UpdateData.h delete mode 100644 src/server/game/UpdateFields.h delete mode 100644 src/server/game/UpdateMask.h delete mode 100644 src/server/game/Vehicle.cpp delete mode 100644 src/server/game/Vehicle.h delete mode 100644 src/server/game/VoiceChatHandler.cpp delete mode 100644 src/server/game/WaypointManager.cpp delete mode 100644 src/server/game/WaypointManager.h delete mode 100644 src/server/game/WaypointMovementGenerator.cpp delete mode 100644 src/server/game/WaypointMovementGenerator.h delete mode 100644 src/server/game/Weather.cpp delete mode 100644 src/server/game/Weather.h create mode 100644 src/server/game/Weather/Weather.cpp create mode 100644 src/server/game/Weather/Weather.h delete mode 100644 src/server/game/World.cpp delete mode 100644 src/server/game/World.h create mode 100644 src/server/game/World/TimeMgr.cpp create mode 100644 src/server/game/World/TimeMgr.h create mode 100644 src/server/game/World/World.cpp create mode 100644 src/server/game/World/World.h create mode 100644 src/server/game/World/WorldLog.cpp create mode 100644 src/server/game/World/WorldLog.h create mode 100644 src/server/game/World/WorldSession.cpp create mode 100644 src/server/game/World/WorldSession.h create mode 100644 src/server/game/World/WorldSocket.cpp create mode 100644 src/server/game/World/WorldSocket.h create mode 100644 src/server/game/World/WorldSocketMgr.cpp create mode 100644 src/server/game/World/WorldSocketMgr.h delete mode 100644 src/server/game/WorldLog.cpp delete mode 100644 src/server/game/WorldLog.h delete mode 100644 src/server/game/WorldSession.cpp delete mode 100644 src/server/game/WorldSession.h delete mode 100644 src/server/game/WorldSocket.cpp delete mode 100644 src/server/game/WorldSocket.h delete mode 100644 src/server/game/WorldSocketMgr.cpp delete mode 100644 src/server/game/WorldSocketMgr.h delete mode 100644 src/server/game/ZoneScript.h delete mode 100644 src/server/game/pchdef.cpp delete mode 100644 src/server/game/pchdef.h delete mode 100644 src/server/game/pchlinux.cpp delete mode 100644 src/server/game/pchlinux.h (limited to 'src') diff --git a/src/server/game/AI/AuctionHouseBot/AuctionHouseBot.cpp b/src/server/game/AI/AuctionHouseBot/AuctionHouseBot.cpp new file mode 100644 index 00000000000..ebbf48e6476 --- /dev/null +++ b/src/server/game/AI/AuctionHouseBot/AuctionHouseBot.cpp @@ -0,0 +1,1860 @@ +#include "ObjectMgr.h" +#include "AuctionHouseMgr.h" +#include "AuctionHouseBot.h" +#include + +#include "Policies/SingletonImp.h" +INSTANTIATE_SINGLETON_1(AuctionHouseBot); + +using namespace std; +vector npcItems; +vector lootItems; +vector greyTradeGoodsBin; +vector whiteTradeGoodsBin; +vector greenTradeGoodsBin; +vector blueTradeGoodsBin; +vector purpleTradeGoodsBin; +vector orangeTradeGoodsBin; +vector yellowTradeGoodsBin; +vector greyItemsBin; +vector whiteItemsBin; +vector greenItemsBin; +vector blueItemsBin; +vector purpleItemsBin; +vector orangeItemsBin; +vector yellowItemsBin; +AuctionHouseBot::AuctionHouseBot() +{ + debug_Out = false; + debug_Out_Filters = false; + AHBSeller = false; + AHBBuyer = false; + + //Begin Filters + + Vendor_Items = false; + Loot_Items = false; + Other_Items = false; + Vendor_TGs = false; + Loot_TGs = false; + Other_TGs = false; + + No_Bind = false; + Bind_When_Picked_Up = false; + Bind_When_Equipped = false; + Bind_When_Use = false; + Bind_Quest_Item = false; + + DisableBeta_PTR_Unused = false; + DisablePermEnchant = false; + DisableConjured = false; + DisableGems = false; + DisableMoney = false; + DisableMoneyLoot = false; + DisableLootable = false; + DisableKeys = false; + DisableDuration = false; + DisableBOP_Or_Quest_NoReqLevel = false; + + DisableWarriorItems = false; + DisablePaladinItems = false; + DisableHunterItems = false; + DisableRogueItems = false; + DisablePriestItems = false; + DisableDKItems = false; + DisableShamanItems = false; + DisableMageItems = false; + DisableWarlockItems = false; + DisableUnusedClassItems = false; + DisableDruidItems = false; + + DisableItemsBelowLevel = 0; + DisableItemsAboveLevel = 0; + DisableTGsBelowLevel = 0; + DisableTGsAboveLevel = 0; + DisableItemsBelowGUID = 0; + DisableItemsAboveGUID = 0; + DisableTGsBelowGUID = 0; + DisableTGsAboveGUID = 0; + DisableItemsBelowReqLevel = 0; + DisableItemsAboveReqLevel = 0; + DisableTGsBelowReqLevel = 0; + DisableTGsAboveReqLevel = 0; + DisableItemsBelowReqSkillRank = 0; + DisableItemsAboveReqSkillRank = 0; + DisableTGsBelowReqSkillRank = 0; + DisableTGsAboveReqSkillRank = 0; + + //End Filters + + _lastrun_a = time(NULL); + _lastrun_h = time(NULL); + _lastrun_n = time(NULL); + + AllianceConfig = AHBConfig(2); + HordeConfig = AHBConfig(6); + NeutralConfig = AHBConfig(7); +} + +AuctionHouseBot::~AuctionHouseBot() +{ +} + +void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config) +{ + if (!AHBSeller) + { + if (debug_Out) sLog.outError("AHSeller: Disabled"); + return; + } + + uint32 minItems = config->GetMinItems(); + uint32 maxItems = config->GetMaxItems(); + + if (maxItems == 0) + { + //if (debug_Out) sLog.outString("AHSeller: Auctions disabled"); + return; + } + + AuctionHouseEntry const* ahEntry = auctionmgr.GetAuctionHouseEntry(config->GetAHFID()); + if (!ahEntry) + { + return; + } + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); + if (!auctionHouse) + { + return; + } + + uint32 auctions = auctionHouse->Getcount(); + + if (auctions >= minItems) + { + //if (debug_Out) sLog.outString("AHSeller: Auctions above minimum"); + return; + } + + if (auctions >= maxItems) + { + //if (debug_Out) sLog.outString("AHSeller: Auctions at or above maximum"); + return; + } + + uint32 items = 0; + if ((maxItems - auctions) >= ItemsPerCycle) + items = ItemsPerCycle; + else + items = (maxItems - auctions); + + if (debug_Out) sLog.outString("AHSeller: Adding %u Auctions", items); + + uint32 AuctioneerGUID = 0; + + switch (config->GetAHID()) + { + case 2: + AuctioneerGUID = 79707; //Human in stormwind. + break; + case 6: + AuctioneerGUID = 4656; //orc in Orgrimmar + break; + case 7: + AuctioneerGUID = 23442; //goblin in GZ + break; + default: + if (debug_Out) sLog.outError("AHSeller: GetAHID() - Default switch reached"); + AuctioneerGUID = 23442; //default to neutral 7 + break; + } + + if (debug_Out) sLog.outString("AHSeller: Current Auctineer GUID is %u", AuctioneerGUID); + + uint32 greyTGcount = config->GetPercents(AHB_GREY_TG); + uint32 whiteTGcount = config->GetPercents(AHB_WHITE_TG); + uint32 greenTGcount = config->GetPercents(AHB_GREEN_TG); + uint32 blueTGcount = config->GetPercents(AHB_BLUE_TG); + uint32 purpleTGcount = config->GetPercents(AHB_PURPLE_TG); + uint32 orangeTGcount = config->GetPercents(AHB_ORANGE_TG); + uint32 yellowTGcount = config->GetPercents(AHB_YELLOW_TG); + uint32 greyIcount = config->GetPercents(AHB_GREY_I); + uint32 whiteIcount = config->GetPercents(AHB_WHITE_I); + uint32 greenIcount = config->GetPercents(AHB_GREEN_I); + uint32 blueIcount = config->GetPercents(AHB_BLUE_I); + uint32 purpleIcount = config->GetPercents(AHB_PURPLE_I); + uint32 orangeIcount = config->GetPercents(AHB_ORANGE_I); + uint32 yellowIcount = config->GetPercents(AHB_YELLOW_I); +/* uint32 total = greyTGcount + whiteTGcount + greenTGcount + blueTGcount + + purpleTGcount + orangeTGcount + yellowTGcount + + whiteIcount + greenIcount + blueIcount + purpleIcount + + orangeIcount + yellowIcount; +*/ + uint32 greyTGoods = config->GetItemCounts(AHB_GREY_TG); + uint32 whiteTGoods = config->GetItemCounts(AHB_WHITE_TG); + uint32 greenTGoods = config->GetItemCounts(AHB_GREEN_TG); + uint32 blueTGoods = config->GetItemCounts(AHB_BLUE_TG); + uint32 purpleTGoods = config->GetItemCounts(AHB_PURPLE_TG); + uint32 orangeTGoods = config->GetItemCounts(AHB_ORANGE_TG); + uint32 yellowTGoods = config->GetItemCounts(AHB_YELLOW_TG); + + uint32 greyItems = config->GetItemCounts(AHB_GREY_I); + uint32 whiteItems = config->GetItemCounts(AHB_WHITE_I); + uint32 greenItems = config->GetItemCounts(AHB_GREEN_I); + uint32 blueItems = config->GetItemCounts(AHB_BLUE_I); + uint32 purpleItems = config->GetItemCounts(AHB_PURPLE_I); + uint32 orangeItems = config->GetItemCounts(AHB_ORANGE_I); + uint32 yellowItems = config->GetItemCounts(AHB_YELLOW_I); + if (debug_Out) sLog.outString("AHSeller: %u items", items); + + // only insert a few at a time, so as not to peg the processor + for (uint32 cnt = 1; cnt <= items; cnt++) + { + if (debug_Out) sLog.outString("AHSeller: %u count", cnt); + uint32 itemID = 0; + uint32 itemColor = 99; + uint32 loopbreaker = 0; + while (itemID == 0 && loopbreaker <= 50) + { + ++loopbreaker; + uint32 choice = urand(0, 13); + itemColor = choice; + switch (choice) + { + case 0: + { + if ((greyItemsBin.size() > 0) && (greyItems < greyIcount)) + itemID = greyItemsBin[urand(0, greyItemsBin.size() - 1)]; + else continue; + break; + } + case 1: + { + if ((whiteItemsBin.size() > 0) && (whiteItems < whiteIcount)) + itemID = whiteItemsBin[urand(0, whiteItemsBin.size() - 1)]; + else continue; + break; + } + case 2: + { + if ((greenItemsBin.size() > 0) && (greenItems < greenIcount)) + itemID = greenItemsBin[urand(0, greenItemsBin.size() - 1)]; + else continue; + break; + } + case 3: + { + if ((blueItemsBin.size() > 0) && (blueItems < blueIcount)) + itemID = blueItemsBin[urand(0, blueItemsBin.size() - 1)]; + else continue; + break; + } + case 4: + { + if ((purpleItemsBin.size() > 0) && (purpleItems < purpleIcount)) + itemID = purpleItemsBin[urand(0, purpleItemsBin.size() - 1)]; + else continue; + break; + } + case 5: + { + if ((orangeItemsBin.size() > 0) && (orangeItems < orangeIcount)) + itemID = orangeItemsBin[urand(0, orangeItemsBin.size() - 1)]; + else continue; + break; + } + case 6: + { + if ((yellowItemsBin.size() > 0) && (yellowItems < yellowIcount)) + itemID = yellowItemsBin[urand(0, yellowItemsBin.size() - 1)]; + else continue; + break; + } + case 7: + { + if ((greyTradeGoodsBin.size() > 0) && (greyTGoods < greyTGcount)) + itemID = greyTradeGoodsBin[urand(0, greyTradeGoodsBin.size() - 1)]; + else continue; + break; + } + case 8: + { + if ((whiteTradeGoodsBin.size() > 0) && (whiteTGoods < whiteTGcount)) + itemID = whiteTradeGoodsBin[urand(0, whiteTradeGoodsBin.size() - 1)]; + else continue; + break; + } + case 9: + { + if ((greenTradeGoodsBin.size() > 0) && (greenTGoods < greenTGcount)) + itemID = greenTradeGoodsBin[urand(0, greenTradeGoodsBin.size() - 1)]; + else continue; + break; + } + case 10: + { + if ((blueTradeGoodsBin.size() > 0) && (blueTGoods < blueTGcount)) + itemID = blueTradeGoodsBin[urand(0, blueTradeGoodsBin.size() - 1)]; + else continue; + break; + } + case 11: + { + if ((purpleTradeGoodsBin.size() > 0) && (purpleTGoods < purpleTGcount)) + itemID = purpleTradeGoodsBin[urand(0, purpleTradeGoodsBin.size() - 1)]; + else continue; + break; + } + case 12: + { + if ((orangeTradeGoodsBin.size() > 0) && (orangeTGoods < orangeTGcount)) + itemID = orangeTradeGoodsBin[urand(0, orangeTradeGoodsBin.size() - 1)]; + else continue; + break; + } + case 13: + { + if ((yellowTradeGoodsBin.size() > 0) && (yellowTGoods < yellowTGcount)) + itemID = yellowTradeGoodsBin[urand(0, yellowTradeGoodsBin.size() - 1)]; + else continue; + break; + } + default: + { + if (debug_Out) sLog.outError("AHSeller: itemID Switch - Default Reached"); + break; + } + } + + if (itemID == 0) + { + if (debug_Out) sLog.outError("AHSeller: Item::CreateItem() - ItemID is 0"); + continue; + } + + ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); + if (prototype == NULL) + { + if (debug_Out) sLog.outError("AHSeller: Huh?!?! prototype == NULL"); + continue; + } + + Item* item = Item::CreateItem(itemID, 1, AHBplayer); + if (item == NULL) + { + if (debug_Out) sLog.outError("AHSeller: Item::CreateItem() returned NULL"); + break; + } + item->AddToUpdateQueueOf(AHBplayer); + + uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID); + if (randomPropertyId != 0) + item->SetItemRandomProperties(randomPropertyId); + + uint64 buyoutPrice = 0; + uint64 bidPrice = 0; + uint32 stackCount = 1; + + switch (SellMethod) + { + case 0: + buyoutPrice = prototype->SellPrice; + break; + case 1: + buyoutPrice = prototype->BuyPrice; + break; + } + + if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY)) + { + if (config->GetMaxStack(prototype->Quality) > 1 && item->GetMaxStackCount() > 1) + stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(prototype->Quality))); + else if (config->GetMaxStack(prototype->Quality) == 0 && item->GetMaxStackCount() > 1) + stackCount = urand(1, item->GetMaxStackCount()); + else + stackCount = 1; + buyoutPrice *= urand(config->GetMinPrice(prototype->Quality), config->GetMaxPrice(prototype->Quality)); + buyoutPrice /= 100; + bidPrice = buyoutPrice * urand(config->GetMinBidPrice(prototype->Quality), config->GetMaxBidPrice(prototype->Quality)); + bidPrice /= 100; + } + else + { + // quality is something it shouldn't be, let's get out of here + if (debug_Out) sLog.outError("AHBuyer: Quality %u not Supported", prototype->Quality); + item->RemoveFromUpdateQueueOf(AHBplayer); + continue; + } + + uint32 etime = urand(1,3); + switch(etime) + { + case 1: + etime = 43200; + break; + case 2: + etime = 86400; + break; + case 3: + etime = 172800; + break; + default: + etime = 86400; + break; + } + item->SetCount(stackCount); + + uint32 dep = auctionmgr.GetAuctionDeposit(ahEntry, etime, item); + + AuctionEntry* auctionEntry = new AuctionEntry; + auctionEntry->Id = objmgr.GenerateAuctionID(); + auctionEntry->auctioneer = AuctioneerGUID; + auctionEntry->item_guidlow = item->GetGUIDLow(); + auctionEntry->item_template = item->GetEntry(); + auctionEntry->owner = AHBplayer->GetGUIDLow(); + auctionEntry->startbid = bidPrice * stackCount; + auctionEntry->buyout = buyoutPrice * stackCount; + auctionEntry->bidder = 0; + auctionEntry->bid = 0; + auctionEntry->deposit = dep; + auctionEntry->expire_time = (time_t) etime + time(NULL); + auctionEntry->auctionHouseEntry = ahEntry; + item->SaveToDB(); + item->RemoveFromUpdateQueueOf(AHBplayer); + auctionmgr.AddAItem(item); + auctionHouse->AddAuction(auctionEntry); + auctionEntry->SaveToDB(); + + switch(itemColor) + { + case 0: + ++greyItems; + break; + case 1: + ++whiteItems; + break; + case 2: + ++greenItems; + break; + case 3: + ++blueItems; + break; + case 4: + ++purpleItems; + break; + case 5: + ++orangeItems; + break; + case 6: + ++yellowItems; + break; + case 7: + ++greyTGoods; + break; + case 8: + ++whiteTGoods; + break; + case 9: + ++greenTGoods; + break; + case 10: + ++blueTGoods; + break; + case 11: + ++purpleTGoods; + break; + case 12: + ++orangeTGoods; + break; + case 13: + ++yellowTGoods; + break; + default: + break; + } + } + } +} +void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session) +{ + if (!AHBBuyer) + { + if (debug_Out) sLog.outError("AHBuyer: Disabled"); + return; + } + + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT id FROM auctionhouse WHERE itemowner<>%u AND buyguid<>%u", AHBplayerGUID, AHBplayerGUID); + + if (!result) + return; + + if (result->GetRowCount() == 0) + return; + + // Fetches content of selected AH + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); + vector possibleBids; + + do + { + uint32 tmpdata = result->Fetch()->GetUInt32(); + possibleBids.push_back(tmpdata); + }while (result->NextRow()); + + for (uint32 count = 1; count <= config->GetBidsPerInterval(); ++count) + { + // Do we have anything to bid? If not, stop here. + if (possibleBids.empty()) + { + //if (debug_Out) sLog.outString("AHBuyer: I have no items to bid on."); + count = config->GetBidsPerInterval(); + continue; + } + + // Choose random auction from possible auctions + uint32 vectorPos = urand(0, possibleBids.size() - 1); + vector::iterator iter = possibleBids.begin(); + advance(iter, vectorPos); + + // from auctionhousehandler.cpp, creates auction pointer & player pointer + AuctionEntry* auction = auctionHouse->GetAuction(*iter); + + // Erase the auction from the vector to prevent bidding on item in next iteration. + possibleBids.erase(iter); + + if (!auction) + continue; + + // get exact item information + Item *pItem = auctionmgr.GetAItem(auction->item_guidlow); + if (!pItem) + { + if (debug_Out) sLog.outError("AHBuyer: Item %u doesn't exist, perhaps bought already?", auction->item_guidlow); + continue; + } + + // get item prototype + ItemPrototype const* prototype = objmgr.GetItemPrototype(auction->item_template); + + // check which price we have to use, startbid or if it is bidded already + uint32 currentprice; + if (auction->bid) + currentprice = auction->bid; + else + currentprice = auction->startbid; + + // Prepare portion from maximum bid + double bidrate = static_cast(urand(1, 100)) / 100; + long double bidMax = 0; + + // check that bid has acceptable value and take bid based on vendorprice, stacksize and quality + switch (BuyMethod) + { + case 0: + { + if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY)) + { + if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality)) + bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality); + } + else + { + // quality is something it shouldn't be, let's get out of here + if (debug_Out) sLog.outError("AHBuyer: Quality %u not Supported", prototype->Quality); + continue; + } + break; + } + case 1: + { + if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY)) + { + if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality)) + bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality); + } + else + { + // quality is something it shouldn't be, let's get out of here + if (debug_Out) sLog.outError("AHBuyer: Quality %u not Supported", prototype->Quality); + continue; + } + break; + } + } + + // check some special items, and do recalculating to their prices + switch (prototype->Class) + { + // ammo + case 6: + bidMax = 0; + break; + default: + break; + } + + if (bidMax == 0) + { + // quality check failed to get bidmax, let's get out of here + continue; + } + + // Calculate our bid + long double bidvalue = currentprice + ((bidMax - currentprice) * bidrate); + // Convert to uint32 + uint32 bidprice = static_cast(bidvalue); + + // Check our bid is high enough to be valid. If not, correct it to minimum. + if ((currentprice + auction->GetAuctionOutBid()) > bidprice) + bidprice = currentprice + auction->GetAuctionOutBid(); + + if (debug_Out) + { + sLog.outString("-------------------------------------------------"); + sLog.outString("AHBuyer: Info for Auction #%u:", auction->Id); + sLog.outString("AHBuyer: AuctionHouse: %u", auction->GetHouseId()); + sLog.outString("AHBuyer: Auctioneer: %u", auction->auctioneer); + sLog.outString("AHBuyer: Owner: %u", auction->owner); + sLog.outString("AHBuyer: Bidder: %u", auction->bidder); + sLog.outString("AHBuyer: Starting Bid: %u", auction->startbid); + sLog.outString("AHBuyer: Current Bid: %u", currentprice); + sLog.outString("AHBuyer: Buyout: %u", auction->buyout); + sLog.outString("AHBuyer: Deposit: %u", auction->deposit); + sLog.outString("AHBuyer: Expire Time: %u", auction->expire_time); + sLog.outString("AHBuyer: Bid Rate: %f", bidrate); + sLog.outString("AHBuyer: Bid Max: %f", bidMax); + sLog.outString("AHBuyer: Bid Value: %f", bidvalue); + sLog.outString("AHBuyer: Bid Price: %u", bidprice); + sLog.outString("AHBuyer: Item GUID: %u", auction->item_guidlow); + sLog.outString("AHBuyer: Item Template: %u", auction->item_template); + sLog.outString("AHBuyer: Item Info:"); + sLog.outString("AHBuyer: Item ID: %u", prototype->ItemId); + sLog.outString("AHBuyer: Buy Price: %u", prototype->BuyPrice); + sLog.outString("AHBuyer: Sell Price: %u", prototype->SellPrice); + sLog.outString("AHBuyer: Bonding: %u", prototype->Bonding); + sLog.outString("AHBuyer: Quality: %u", prototype->Quality); + sLog.outString("AHBuyer: Item Level: %u", prototype->ItemLevel); + sLog.outString("AHBuyer: Ammo Type: %u", prototype->AmmoType); + sLog.outString("-------------------------------------------------"); + } + + // Check whether we do normal bid, or buyout + if ((bidprice < auction->buyout) || (auction->buyout == 0)) + { + + if (auction->bidder > 0) + { + if (auction->bidder == AHBplayer->GetGUIDLow()) + { + //pl->ModifyMoney(-int32(price - auction->bid)); + } + else + { + // mail to last bidder and return money + session->SendAuctionOutbiddedMail(auction , bidprice); + //pl->ModifyMoney(-int32(price)); + } + } + + auction->bidder = AHBplayer->GetGUIDLow(); + auction->bid = bidprice; + + // Saving auction into database + CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); + } + else + { + //buyout + if ((auction->bidder) && (AHBplayer->GetGUIDLow() != auction->bidder)) + { + session->SendAuctionOutbiddedMail(auction, auction->buyout); + } + auction->bidder = AHBplayer->GetGUIDLow(); + auction->bid = auction->buyout; + + // Send mails to buyer & seller + auctionmgr.SendAuctionSalePendingMail(auction); + auctionmgr.SendAuctionSuccessfulMail(auction); + auctionmgr.SendAuctionWonMail(auction); + auction->DeleteFromDB(); + uint32 item_template = auction->item_template; + auctionmgr.RemoveAItem(auction->item_guidlow); + auctionHouse->RemoveAuction(auction, item_template); + } + } +} + +void AuctionHouseBot::Update() +{ + time_t _newrun = time(NULL); + if ((!AHBSeller) && (!AHBBuyer)) + return; + + WorldSession _session(AHBplayerAccount, NULL, SEC_PLAYER, true, 0, LOCALE_enUS); + Player _AHBplayer(&_session); + _AHBplayer.Initialize(AHBplayerGUID); + ObjectAccessor::Instance().AddObject(&_AHBplayer); + + // Add New Bids + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + { + addNewAuctions(&_AHBplayer, &AllianceConfig); + if (((_newrun - _lastrun_a) >= (AllianceConfig.GetBiddingInterval() * MINUTE)) && (AllianceConfig.GetBidsPerInterval() > 0)) + { + //if (debug_Out) sLog.outString("AHBuyer: %u seconds have passed since last bid", (_newrun - _lastrun_a)); + //if (debug_Out) sLog.outString("AHBuyer: Bidding on Alliance Auctions"); + addNewAuctionBuyerBotBid(&_AHBplayer, &AllianceConfig, &_session); + _lastrun_a = _newrun; + } + + addNewAuctions(&_AHBplayer, &HordeConfig); + if (((_newrun - _lastrun_h) >= (HordeConfig.GetBiddingInterval() * MINUTE)) && (HordeConfig.GetBidsPerInterval() > 0)) + { + //if (debug_Out) sLog.outString("AHBuyer: %u seconds have passed since last bid", (_newrun - _lastrun_h)); + //if (debug_Out) sLog.outString("AHBuyer: Bidding on Horde Auctions"); + addNewAuctionBuyerBotBid(&_AHBplayer, &HordeConfig, &_session); + _lastrun_h = _newrun; + } + } + + addNewAuctions(&_AHBplayer, &NeutralConfig); + if (((_newrun - _lastrun_n) >= (NeutralConfig.GetBiddingInterval() * MINUTE)) && (NeutralConfig.GetBidsPerInterval() > 0)) + { + //if (debug_Out) sLog.outString("AHBuyer: %u seconds have passed since last bid", (_newrun - _lastrun_n)); + //if (debug_Out) sLog.outString("AHBuyer: Bidding on Neutral Auctions"); + addNewAuctionBuyerBotBid(&_AHBplayer, &NeutralConfig, &_session); + _lastrun_n = _newrun; + } + ObjectAccessor::Instance().RemoveObject(&_AHBplayer); +} + +void AuctionHouseBot::Initialize() +{ + debug_Out = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG", false); + debug_Out_Filters = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG_FILTERS", false); + + AHBSeller = sConfig.GetBoolDefault("AuctionHouseBot.EnableSeller", false); + AHBBuyer = sConfig.GetBoolDefault("AuctionHouseBot.EnableBuyer", false); + SellMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForSeller", false); + BuyMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForBuyer", false); + + AHBplayerAccount = sConfig.GetIntDefault("AuctionHouseBot.Account", 0); + AHBplayerGUID = sConfig.GetIntDefault("AuctionHouseBot.GUID", 0); + ItemsPerCycle = sConfig.GetIntDefault("AuctionHouseBot.ItemsPerCycle", 200); + + //Begin Filters + + Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", false); + Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", true); + Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", false); + Vendor_TGs = sConfig.GetBoolDefault("AuctionHouseBot.VendorTradeGoods", false); + Loot_TGs = sConfig.GetBoolDefault("AuctionHouseBot.LootTradeGoods", true); + Other_TGs = sConfig.GetBoolDefault("AuctionHouseBot.OtherTradeGoods", false); + + No_Bind = sConfig.GetBoolDefault("AuctionHouseBot.No_Bind", true); + Bind_When_Picked_Up = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Picked_Up", false); + Bind_When_Equipped = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Equipped", true); + Bind_When_Use = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Use", true); + Bind_Quest_Item = sConfig.GetBoolDefault("AuctionHouseBot.Bind_Quest_Item", false); + + DisableBeta_PTR_Unused = sConfig.GetBoolDefault("AuctionHouseBot.DisableBeta_PTR_Unused", false); + DisablePermEnchant = sConfig.GetBoolDefault("AuctionHouseBot.DisablePermEnchant", false); + DisableConjured = sConfig.GetBoolDefault("AuctionHouseBot.DisableConjured", false); + DisableGems = sConfig.GetBoolDefault("AuctionHouseBot.DisableGems", false); + DisableMoney = sConfig.GetBoolDefault("AuctionHouseBot.DisableMoney", false); + DisableMoneyLoot = sConfig.GetBoolDefault("AuctionHouseBot.DisableMoneyLoot", false); + DisableLootable = sConfig.GetBoolDefault("AuctionHouseBot.DisableLootable", false); + DisableKeys = sConfig.GetBoolDefault("AuctionHouseBot.DisableKeys", false); + DisableDuration = sConfig.GetBoolDefault("AuctionHouseBot.DisableDuration", false); + DisableBOP_Or_Quest_NoReqLevel = sConfig.GetBoolDefault("AuctionHouseBot.DisableBOP_Or_Quest_NoReqLevel", false); + + DisableWarriorItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarriorItems", false); + DisablePaladinItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePaladinItems", false); + DisableHunterItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableHunterItems", false); + DisableRogueItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableRogueItems", false); + DisablePriestItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePriestItems", false); + DisableDKItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDKItems", false); + DisableShamanItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableShamanItems", false); + DisableMageItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableMageItems", false); + DisableWarlockItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarlockItems", false); + DisableUnusedClassItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableUnusedClassItems", false); + DisableDruidItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDruidItems", false); + + DisableItemsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowLevel", 0); + DisableItemsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveLevel", 0); + DisableTGsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowLevel", 0); + DisableTGsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveLevel", 0); + DisableItemsBelowGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowGUID", 0); + DisableItemsAboveGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveGUID", 0); + DisableTGsBelowGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowGUID", 0); + DisableTGsAboveGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveGUID", 0); + DisableItemsBelowReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowReqLevel", 0); + DisableItemsAboveReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqLevel", 0); + DisableTGsBelowReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqLevel", 0); + DisableTGsAboveReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqLevel", 0); + DisableItemsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowReqSkillRank", 0); + DisableItemsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqSkillRank", 0); + DisableTGsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqSkillRank", 0); + DisableTGsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqSkillRank", 0); + + //End Filters + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + { + LoadValues(&AllianceConfig); + LoadValues(&HordeConfig); + } + LoadValues(&NeutralConfig); + + // + // check if the AHBot account/GUID in the config actually exists + // + + if ((AHBplayerAccount != 0) || (AHBplayerGUID != 0)) + { + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT 1 FROM characters WHERE account = %u AND guid = %u", AHBplayerAccount, AHBplayerGUID); + if (!result) + { + sLog.outError("AuctionHouseBot: The account/GUID-information set for your AHBot is incorrect (account: %u guid: %u)", AHBplayerAccount, AHBplayerGUID); + return; + } + } + + if (AHBSeller) + { + QueryResult_AutoPtr results = QueryResult_AutoPtr(NULL); + char npcQuery[] = "SELECT distinct item FROM npc_vendor"; + results = WorldDatabase.Query(npcQuery); + if (results != NULL) + { + do + { + Field* fields = results->Fetch(); + npcItems.push_back(fields[0].GetUInt32()); + + } while (results->NextRow()); + } + else + { + if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", npcQuery); + } + + char lootQuery[] = "SELECT item FROM creature_loot_template UNION " + "SELECT item FROM reference_loot_template UNION " + "SELECT item FROM disenchant_loot_template UNION " + "SELECT item FROM fishing_loot_template UNION " + "SELECT item FROM gameobject_loot_template UNION " + "SELECT item FROM item_loot_template UNION " + "SELECT item FROM milling_loot_template UNION " + "SELECT item FROM pickpocketing_loot_template UNION " + "SELECT item FROM prospecting_loot_template UNION " + "SELECT item FROM skinning_loot_template"; + + results = WorldDatabase.Query(lootQuery); + if (results != NULL) + { + do + { + Field* fields = results->Fetch(); + lootItems.push_back(fields[0].GetUInt32()); + + } while (results->NextRow()); + } + else + { + if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", lootQuery); + } + + for (uint32 itemID = 0; itemID < sItemStorage.MaxEntry; itemID++) + { + ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); + + if (prototype == NULL) + continue; + + switch (prototype->Bonding) + { + case NO_BIND: + if (!No_Bind) + continue; + break; + case BIND_WHEN_PICKED_UP: + if (!Bind_When_Picked_Up) + continue; + break; + case BIND_WHEN_EQUIPED: + if (!Bind_When_Equipped) + continue; + break; + case BIND_WHEN_USE: + if (!Bind_When_Use) + continue; + break; + case BIND_QUEST_ITEM: + if (!Bind_Quest_Item) + continue; + break; + default: + continue; + break; + } + + switch (SellMethod) + { + case 0: + if (prototype->SellPrice == 0) + continue; + break; + case 1: + if (prototype->BuyPrice == 0) + continue; + break; + } + + if ((prototype->Quality < 0) || (prototype->Quality > 6)) + continue; + + if ((Vendor_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS)) + { + bool isVendorItem = false; + + for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++) + { + if (itemID == npcItems[i]) + isVendorItem = true; + } + + if (isVendorItem) + continue; + } + + if ((Vendor_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS)) + { + bool isVendorTG = false; + + for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorTG); i++) + { + if (itemID == npcItems[i]) + isVendorTG = true; + } + + if (isVendorTG) + continue; + } + + if ((Loot_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS)) + { + bool isLootItem = false; + + for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++) + { + if (itemID == lootItems[i]) + isLootItem = true; + } + + if (isLootItem) + continue; + } + + if ((Loot_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS)) + { + bool isLootTG = false; + + for (unsigned int i = 0; (i < lootItems.size()) && (!isLootTG); i++) + { + if (itemID == lootItems[i]) + isLootTG = true; + } + + if (isLootTG) + continue; + } + + if ((Other_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS)) + { + bool isVendorItem = false; + bool isLootItem = false; + + for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++) + { + if (itemID == npcItems[i]) + isVendorItem = true; + } + for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++) + { + if (itemID == lootItems[i]) + isLootItem = true; + } + if ((!isLootItem) && (!isVendorItem)) + continue; + } + + if ((Other_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS)) + { + bool isVendorTG = false; + bool isLootTG = false; + + for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorTG); i++) + { + if (itemID == npcItems[i]) + isVendorTG = true; + } + for (unsigned int i = 0; (i < lootItems.size()) && (!isLootTG); i++) + { + if (itemID == lootItems[i]) + isLootTG = true; + } + if ((!isLootTG) && (!isVendorTG)) + continue; + } + + //TODO:Make list of items and create a vector + // Disable PTR/Beta/Unused items + if ((DisableBeta_PTR_Unused) && ((prototype->ItemId == 21878) || (prototype->ItemId == 27774) || (prototype->ItemId == 27811) || (prototype->ItemId == 28117) || (prototype->ItemId == 28112))) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (PTR/Beta/Unused Item)", prototype->ItemId); + continue; + } + + // Disable permanent enchants items + if ((DisablePermEnchant) && (prototype->Class == ITEM_CLASS_PERMANENT)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Permanent Enchant Item)", prototype->ItemId); + continue; + } + + // Disable conjured items + if ((DisableConjured) && (prototype->IsConjuredConsumable())) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Conjured Consumable)", prototype->ItemId); + continue; + } + + // Disable gems + if ((DisableGems) && (prototype->Class == ITEM_CLASS_GEM)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Gem)", prototype->ItemId); + continue; + } + + // Disable money + if ((DisableMoney) && (prototype->Class == ITEM_CLASS_MONEY)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Money)", prototype->ItemId); + continue; + } + + // Disable moneyloot + if ((DisableMoneyLoot) && (prototype->MinMoneyLoot > 0)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (MoneyLoot)", prototype->ItemId); + continue; + } + + // Disable lootable items + if ((DisableLootable) && (prototype->Flags & 4)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Lootable Item)", prototype->ItemId); + continue; + } + + // Disable Keys + if ((DisableKeys) && (prototype->Class == ITEM_CLASS_KEY)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Quest Item)", prototype->ItemId); + continue; + } + + // Disable items with duration + if ((DisableDuration) && (prototype->Duration > 0)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Has a Duration)", prototype->ItemId); + continue; + } + + // Disable items which are BOP or Quest Items and have a required level lower than the item level + if ((DisableBOP_Or_Quest_NoReqLevel) && ((prototype->Bonding == BIND_WHEN_PICKED_UP || prototype->Bonding == BIND_QUEST_ITEM) && (prototype->RequiredLevel < prototype->ItemLevel))) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (BOP or BQI and Required Level is less than Item Level)", prototype->ItemId); + continue; + } + + // Disable items specifically for Warrior + if ((DisableWarriorItems) && (prototype->AllowableClass == 1)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Warrior Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Paladin + if ((DisablePaladinItems) && (prototype->AllowableClass == 2)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Paladin Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Hunter + if ((DisableHunterItems) && (prototype->AllowableClass == 4)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Hunter Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Rogue + if ((DisableRogueItems) && (prototype->AllowableClass == 8)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Rogue Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Priest + if ((DisablePriestItems) && (prototype->AllowableClass == 16)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Priest Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for DK + if ((DisableDKItems) && (prototype->AllowableClass == 32)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (DK Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Shaman + if ((DisableShamanItems) && (prototype->AllowableClass == 64)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Shaman Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Mage + if ((DisableMageItems) && (prototype->AllowableClass == 128)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Mage Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Warlock + if ((DisableWarlockItems) && (prototype->AllowableClass == 256)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Warlock Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Unused Class + if ((DisableUnusedClassItems) && (prototype->AllowableClass == 512)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Unused Item)", prototype->ItemId); + continue; + } + + // Disable items specifically for Druid + if ((DisableDruidItems) && (prototype->AllowableClass == 1024)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Druid Item)", prototype->ItemId); + continue; + } + + // Disable Items below level X + if ((DisableItemsBelowLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableItemsBelowLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items above level X + if ((DisableItemsAboveLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableItemsAboveLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods below level X + if ((DisableTGsBelowLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableTGsBelowLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods above level X + if ((DisableTGsAboveLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableTGsAboveLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items below GUID X + if ((DisableItemsBelowGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableItemsBelowGUID)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items above GUID X + if ((DisableItemsAboveGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableItemsAboveGUID)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods below GUID X + if ((DisableTGsBelowGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableTGsBelowGUID)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Trade Goods above GUID X + if ((DisableTGsAboveGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableTGsAboveGUID)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); + continue; + } + + // Disable Items for level lower than X + if ((DisableItemsBelowReqLevel) && (prototype->RequiredLevel < DisableItemsBelowReqLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Items for level higher than X + if ((DisableItemsAboveReqLevel) && (prototype->RequiredLevel > DisableItemsAboveReqLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Trade Goods for level lower than X + if ((DisableTGsBelowReqLevel) && (prototype->RequiredLevel < DisableTGsBelowReqLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Trade Goods for level higher than X + if ((DisableTGsAboveReqLevel) && (prototype->RequiredLevel > DisableTGsAboveReqLevel)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); + continue; + } + + // Disable Items that require skill lower than X + if ((DisableItemsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableItemsBelowReqSkillRank)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + // Disable Items that require skill higher than X + if ((DisableItemsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableItemsAboveReqSkillRank)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + // Disable Trade Goods that require skill lower than X + if ((DisableTGsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableTGsBelowReqSkillRank)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + // Disable Trade Goods that require skill higher than X + if ((DisableTGsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableTGsAboveReqSkillRank)) + { + if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); + continue; + } + + switch (prototype->Quality) + { + case AHB_GREY: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + greyTradeGoodsBin.push_back(itemID); + else + greyItemsBin.push_back(itemID); + break; + + case AHB_WHITE: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + whiteTradeGoodsBin.push_back(itemID); + else + whiteItemsBin.push_back(itemID); + break; + + case AHB_GREEN: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + greenTradeGoodsBin.push_back(itemID); + else + greenItemsBin.push_back(itemID); + break; + + case AHB_BLUE: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + blueTradeGoodsBin.push_back(itemID); + else + blueItemsBin.push_back(itemID); + break; + + case AHB_PURPLE: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + purpleTradeGoodsBin.push_back(itemID); + else + purpleItemsBin.push_back(itemID); + break; + + case AHB_ORANGE: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + orangeTradeGoodsBin.push_back(itemID); + else + orangeItemsBin.push_back(itemID); + break; + + case AHB_YELLOW: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + yellowTradeGoodsBin.push_back(itemID); + else + yellowItemsBin.push_back(itemID); + break; + } + } + + if ((greyTradeGoodsBin.size() == 0) && + (whiteTradeGoodsBin.size() == 0) && + (greenTradeGoodsBin.size() == 0) && + (blueTradeGoodsBin.size() == 0) && + (purpleTradeGoodsBin.size() == 0) && + (orangeTradeGoodsBin.size() == 0) && + (yellowTradeGoodsBin.size() == 0) && + (greyItemsBin.size() == 0) && + (whiteItemsBin.size() == 0) && + (greenItemsBin.size() == 0) && + (blueItemsBin.size() == 0) && + (purpleItemsBin.size() == 0) && + (orangeItemsBin.size() == 0) && + (yellowItemsBin.size() == 0)) + { + sLog.outError("AuctionHouseBot: No items"); + AHBSeller = 0; + } + + sLog.outString("AuctionHouseBot:"); + sLog.outString("loaded %u grey trade goods", greyTradeGoodsBin.size()); + sLog.outString("loaded %u white trade goods", whiteTradeGoodsBin.size()); + sLog.outString("loaded %u green trade goods", greenTradeGoodsBin.size()); + sLog.outString("loaded %u blue trade goods", blueTradeGoodsBin.size()); + sLog.outString("loaded %u purple trade goods", purpleTradeGoodsBin.size()); + sLog.outString("loaded %u orange trade goods", orangeTradeGoodsBin.size()); + sLog.outString("loaded %u yellow trade goods", yellowTradeGoodsBin.size()); + sLog.outString("loaded %u grey items", greyItemsBin.size()); + sLog.outString("loaded %u white items", whiteItemsBin.size()); + sLog.outString("loaded %u green items", greenItemsBin.size()); + sLog.outString("loaded %u blue items", blueItemsBin.size()); + sLog.outString("loaded %u purple items", purpleItemsBin.size()); + sLog.outString("loaded %u orange items", orangeItemsBin.size()); + sLog.outString("loaded %u yellow items", yellowItemsBin.size()); + } + sLog.outString("AuctionHouseBot and AuctionHouseBuyer have been loaded."); +} + +void AuctionHouseBot::IncrementItemCounts(AuctionEntry* ah) +{ + // from auctionhousehandler.cpp, creates auction pointer & player pointer + + // get exact item information + Item *pItem = auctionmgr.GetAItem(ah->item_guidlow); + if (!pItem) + { + if (debug_Out) sLog.outError("AHBot: Item %u doesn't exist, perhaps bought already?", ah->item_guidlow); + return; + } + + // get item prototype + ItemPrototype const* prototype = objmgr.GetItemPrototype(ah->item_template); + + AHBConfig *config; + + FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(ah->GetHouseFaction()); + if (!u_entry) + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); + config = &NeutralConfig; + } + else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Alliance", ah->GetHouseFaction()); + config = &AllianceConfig; + } + else if (u_entry->ourMask & FACTION_MASK_HORDE) + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Horde", ah->GetHouseFaction()); + config = &HordeConfig; + } + else + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); + config = &NeutralConfig; + } + + config->IncItemCounts(prototype->Class, prototype->Quality); +} + +void AuctionHouseBot::DecrementItemCounts(AuctionEntry* ah, uint32 item_template) +{ + // get item prototype + ItemPrototype const* prototype = objmgr.GetItemPrototype(item_template); + + AHBConfig *config; + + FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(ah->GetHouseFaction()); + if (!u_entry) + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); + config = &NeutralConfig; + } + else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Alliance", ah->GetHouseFaction()); + config = &AllianceConfig; + } + else if (u_entry->ourMask & FACTION_MASK_HORDE) + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Horde", ah->GetHouseFaction()); + config = &HordeConfig; + } + else + { + if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); + config = &NeutralConfig; + } + + config->DecItemCounts(prototype->Class, prototype->Quality); +} + +void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char* args) +{ + AHBConfig *config = NULL; + switch (ahMapID) + { + case 2: + config = &AllianceConfig; + break; + case 6: + config = &HordeConfig; + break; + case 7: + config = &NeutralConfig; + break; + } + std::string color; + switch (col) + { + case AHB_GREY: + color = "grey"; + break; + case AHB_WHITE: + color = "white"; + break; + case AHB_GREEN: + color = "green"; + break; + case AHB_BLUE: + color = "blue"; + break; + case AHB_PURPLE: + color = "purple"; + break; + case AHB_ORANGE: + color = "orange"; + break; + case AHB_YELLOW: + color = "yellow"; + break; + default: + break; + } + switch (command) + { + case 0: //ahexpire + { + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); + + AuctionHouseObject::AuctionEntryMap::iterator itr; + itr = auctionHouse->GetAuctionsBegin(); + + while (itr != auctionHouse->GetAuctionsEnd()) + { + if (itr->second->owner == AHBplayerGUID) + { + itr->second->expire_time = sWorld.GetGameTime(); + uint32 id = itr->second->Id; + uint32 expire_time = itr->second->expire_time; + CharacterDatabase.PExecute("UPDATE auctionhouse SET time = '%u' WHERE id = '%u'", expire_time, id); + } + ++itr; + } + } + break; + case 1: //min items + { + char * param1 = strtok(args, " "); + uint32 minItems = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET minitems = '%u' WHERE auctionhouse = '%u'", minItems, ahMapID); + config->SetMinItems(minItems); + } + break; + case 2: //max items + { + char * param1 = strtok(args, " "); + uint32 maxItems = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxitems = '%u' WHERE auctionhouse = '%u'", maxItems, ahMapID); + config->SetMaxItems(maxItems); + } + break; + case 3: //min time Deprecated (Place holder for future commands) + break; + case 4: //max time Deprecated (Place holder for future commands) + break; + case 5: //percentages + { + char * param1 = strtok(args, " "); + char * param2 = strtok(NULL, " "); + char * param3 = strtok(NULL, " "); + char * param4 = strtok(NULL, " "); + char * param5 = strtok(NULL, " "); + char * param6 = strtok(NULL, " "); + char * param7 = strtok(NULL, " "); + char * param8 = strtok(NULL, " "); + char * param9 = strtok(NULL, " "); + char * param10 = strtok(NULL, " "); + char * param11 = strtok(NULL, " "); + char * param12 = strtok(NULL, " "); + char * param13 = strtok(NULL, " "); + char * param14 = strtok(NULL, " "); + uint32 greytg = (uint32) strtoul(param1, NULL, 0); + uint32 whitetg = (uint32) strtoul(param2, NULL, 0); + uint32 greentg = (uint32) strtoul(param3, NULL, 0); + uint32 bluetg = (uint32) strtoul(param4, NULL, 0); + uint32 purpletg = (uint32) strtoul(param5, NULL, 0); + uint32 orangetg = (uint32) strtoul(param6, NULL, 0); + uint32 yellowtg = (uint32) strtoul(param7, NULL, 0); + uint32 greyi = (uint32) strtoul(param8, NULL, 0); + uint32 whitei = (uint32) strtoul(param9, NULL, 0); + uint32 greeni = (uint32) strtoul(param10, NULL, 0); + uint32 bluei = (uint32) strtoul(param11, NULL, 0); + uint32 purplei = (uint32) strtoul(param12, NULL, 0); + uint32 orangei = (uint32) strtoul(param13, NULL, 0); + uint32 yellowi = (uint32) strtoul(param14, NULL, 0); + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreytradegoods = '%u' WHERE auctionhouse = '%u'", greytg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentwhitetradegoods = '%u' WHERE auctionhouse = '%u'", whitetg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreentradegoods = '%u' WHERE auctionhouse = '%u'", greentg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentbluetradegoods = '%u' WHERE auctionhouse = '%u'", bluetg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentpurpletradegoods = '%u' WHERE auctionhouse = '%u'", purpletg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentorangetradegoods = '%u' WHERE auctionhouse = '%u'", orangetg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentyellowtradegoods = '%u' WHERE auctionhouse = '%u'", yellowtg, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreyitems = '%u' WHERE auctionhouse = '%u'", greyi, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentwhiteitems = '%u' WHERE auctionhouse = '%u'", whitei, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreenitems = '%u' WHERE auctionhouse = '%u'", greeni, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentblueitems = '%u' WHERE auctionhouse = '%u'", bluei, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentpurpleitems = '%u' WHERE auctionhouse = '%u'", purplei, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentorangeitems = '%u' WHERE auctionhouse = '%u'", orangei, ahMapID); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentyellowitems = '%u' WHERE auctionhouse = '%u'", yellowi, ahMapID); + CharacterDatabase.CommitTransaction(); + config->SetPercentages(greytg, whitetg, greentg, bluetg, purpletg, orangetg, yellowtg, greyi, whitei, greeni, bluei, purplei, orangei, yellowi); + } + break; + case 6: //min prices + { + char * param1 = strtok(args, " "); + uint32 minPrice = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET minprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), minPrice, ahMapID); + config->SetMinPrice(col, minPrice); + } + break; + case 7: //max prices + { + char * param1 = strtok(args, " "); + uint32 maxPrice = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxPrice, ahMapID); + config->SetMaxPrice(col, maxPrice); + } + break; + case 8: //min bid price + { + char * param1 = strtok(args, " "); + uint32 minBidPrice = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET minbidprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), minBidPrice, ahMapID); + config->SetMinBidPrice(col, minBidPrice); + } + break; + case 9: //max bid price + { + char * param1 = strtok(args, " "); + uint32 maxBidPrice = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxbidprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxBidPrice, ahMapID); + config->SetMaxBidPrice(col, maxBidPrice); + } + break; + case 10: //max stacks + { + char * param1 = strtok(args, " "); + uint32 maxStack = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxstack%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxStack, ahMapID); + config->SetMaxStack(col, maxStack); + } + break; + case 11: //buyer bid prices + { + char * param1 = strtok(args, " "); + uint32 buyerPrice = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), buyerPrice, ahMapID); + config->SetBuyerPrice(col, buyerPrice); + } + break; + case 12: //buyer bidding interval + { + char * param1 = strtok(args, " "); + uint32 bidInterval = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerbiddinginterval = '%u' WHERE auctionhouse = '%u'", bidInterval, ahMapID); + config->SetBiddingInterval(bidInterval); + } + break; + case 13: //buyer bids per interval + { + char * param1 = strtok(args, " "); + uint32 bidsPerInterval = (uint32) strtoul(param1, NULL, 0); + CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerbidsperinterval = '%u' WHERE auctionhouse = '%u'", bidsPerInterval, ahMapID); + config->SetBidsPerInterval(bidsPerInterval); + } + break; + default: + break; + } +} + +void AuctionHouseBot::LoadValues(AHBConfig *config) +{ + if (debug_Out) sLog.outString("Start Settings for %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString()); + if (AHBSeller) + { + //load min and max items + config->SetMinItems(CharacterDatabase.PQuery("SELECT minitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxItems(CharacterDatabase.PQuery("SELECT maxitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + //load percentages + uint32 greytg = CharacterDatabase.PQuery("SELECT percentgreytradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 whitetg = CharacterDatabase.PQuery("SELECT percentwhitetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 greentg = CharacterDatabase.PQuery("SELECT percentgreentradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 bluetg = CharacterDatabase.PQuery("SELECT percentbluetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 purpletg = CharacterDatabase.PQuery("SELECT percentpurpletradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 orangetg = CharacterDatabase.PQuery("SELECT percentorangetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 yellowtg = CharacterDatabase.PQuery("SELECT percentyellowtradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 greyi = CharacterDatabase.PQuery("SELECT percentgreyitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 whitei = CharacterDatabase.PQuery("SELECT percentwhiteitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 greeni = CharacterDatabase.PQuery("SELECT percentgreenitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 bluei = CharacterDatabase.PQuery("SELECT percentblueitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 purplei = CharacterDatabase.PQuery("SELECT percentpurpleitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 orangei = CharacterDatabase.PQuery("SELECT percentorangeitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + uint32 yellowi = CharacterDatabase.PQuery("SELECT percentyellowitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); + config->SetPercentages(greytg, whitetg, greentg, bluetg, purpletg, orangetg, yellowtg, greyi, whitei, greeni, bluei, purplei, orangei, yellowi); + //load min and max prices + config->SetMinPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT minpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT maxpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT minpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT minpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT minpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT minpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT minpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT minpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + //load min and max bid prices + config->SetMinBidPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT minbidpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT maxbidpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinBidPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT minbidpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxbidpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinBidPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT minbidpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxbidpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinBidPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT minbidpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxbidpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinBidPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT minbidpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxbidpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinBidPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT minbidpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxbidpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMinBidPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT minbidpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxBidPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxbidpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + //load max stacks + config->SetMaxStack(AHB_GREY, CharacterDatabase.PQuery("SELECT maxstackgrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxStack(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxstackwhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxStack(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxstackgreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxStack(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxstackblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxStack(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxstackpurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxStack(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxstackorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetMaxStack(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxstackyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + if (debug_Out) + { + sLog.outString("minItems = %u", config->GetMinItems()); + sLog.outString("maxItems = %u", config->GetMaxItems()); + sLog.outString("percentGreyTradeGoods = %u", config->GetPercentages(AHB_GREY_TG)); + sLog.outString("percentWhiteTradeGoods = %u", config->GetPercentages(AHB_WHITE_TG)); + sLog.outString("percentGreenTradeGoods = %u", config->GetPercentages(AHB_GREEN_TG)); + sLog.outString("percentBlueTradeGoods = %u", config->GetPercentages(AHB_BLUE_TG)); + sLog.outString("percentPurpleTradeGoods = %u", config->GetPercentages(AHB_PURPLE_TG)); + sLog.outString("percentOrangeTradeGoods = %u", config->GetPercentages(AHB_ORANGE_TG)); + sLog.outString("percentYellowTradeGoods = %u", config->GetPercentages(AHB_YELLOW_TG)); + sLog.outString("percentGreyItems = %u", config->GetPercentages(AHB_GREY_I)); + sLog.outString("percentWhiteItems = %u", config->GetPercentages(AHB_WHITE_I)); + sLog.outString("percentGreenItems = %u", config->GetPercentages(AHB_GREEN_I)); + sLog.outString("percentBlueItems = %u", config->GetPercentages(AHB_BLUE_I)); + sLog.outString("percentPurpleItems = %u", config->GetPercentages(AHB_PURPLE_I)); + sLog.outString("percentOrangeItems = %u", config->GetPercentages(AHB_ORANGE_I)); + sLog.outString("percentYellowItems = %u", config->GetPercentages(AHB_YELLOW_I)); + sLog.outString("minPriceGrey = %u", config->GetMinPrice(AHB_GREY)); + sLog.outString("maxPriceGrey = %u", config->GetMaxPrice(AHB_GREY)); + sLog.outString("minPriceWhite = %u", config->GetMinPrice(AHB_WHITE)); + sLog.outString("maxPriceWhite = %u", config->GetMaxPrice(AHB_WHITE)); + sLog.outString("minPriceGreen = %u", config->GetMinPrice(AHB_GREEN)); + sLog.outString("maxPriceGreen = %u", config->GetMaxPrice(AHB_GREEN)); + sLog.outString("minPriceBlue = %u", config->GetMinPrice(AHB_BLUE)); + sLog.outString("maxPriceBlue = %u", config->GetMaxPrice(AHB_BLUE)); + sLog.outString("minPricePurple = %u", config->GetMinPrice(AHB_PURPLE)); + sLog.outString("maxPricePurple = %u", config->GetMaxPrice(AHB_PURPLE)); + sLog.outString("minPriceOrange = %u", config->GetMinPrice(AHB_ORANGE)); + sLog.outString("maxPriceOrange = %u", config->GetMaxPrice(AHB_ORANGE)); + sLog.outString("minPriceYellow = %u", config->GetMinPrice(AHB_YELLOW)); + sLog.outString("maxPriceYellow = %u", config->GetMaxPrice(AHB_YELLOW)); + sLog.outString("minBidPriceGrey = %u", config->GetMinBidPrice(AHB_GREY)); + sLog.outString("maxBidPriceGrey = %u", config->GetMaxBidPrice(AHB_GREY)); + sLog.outString("minBidPriceWhite = %u", config->GetMinBidPrice(AHB_WHITE)); + sLog.outString("maxBidPriceWhite = %u", config->GetMaxBidPrice(AHB_WHITE)); + sLog.outString("minBidPriceGreen = %u", config->GetMinBidPrice(AHB_GREEN)); + sLog.outString("maxBidPriceGreen = %u", config->GetMaxBidPrice(AHB_GREEN)); + sLog.outString("minBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE)); + sLog.outString("maxBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE)); + sLog.outString("minBidPricePurple = %u", config->GetMinBidPrice(AHB_PURPLE)); + sLog.outString("maxBidPricePurple = %u", config->GetMaxBidPrice(AHB_PURPLE)); + sLog.outString("minBidPriceOrange = %u", config->GetMinBidPrice(AHB_ORANGE)); + sLog.outString("maxBidPriceOrange = %u", config->GetMaxBidPrice(AHB_ORANGE)); + sLog.outString("minBidPriceYellow = %u", config->GetMinBidPrice(AHB_YELLOW)); + sLog.outString("maxBidPriceYellow = %u", config->GetMaxBidPrice(AHB_YELLOW)); + sLog.outString("maxStackGrey = %u", config->GetMaxStack(AHB_GREY)); + sLog.outString("maxStackWhite = %u", config->GetMaxStack(AHB_WHITE)); + sLog.outString("maxStackGreen = %u", config->GetMaxStack(AHB_GREEN)); + sLog.outString("maxStackBlue = %u", config->GetMaxStack(AHB_BLUE)); + sLog.outString("maxStackPurple = %u", config->GetMaxStack(AHB_PURPLE)); + sLog.outString("maxStackOrange = %u", config->GetMaxStack(AHB_ORANGE)); + sLog.outString("maxStackYellow = %u", config->GetMaxStack(AHB_YELLOW)); + } + //AuctionHouseEntry const* ahEntry = auctionmgr.GetAuctionHouseEntry(config->GetAHFID()); + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); + + config->ResetItemCounts(); + uint32 auctions = auctionHouse->Getcount(); + + if (auctions) + { + for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin(); itr != auctionHouse->GetAuctionsEnd(); ++itr) + { + AuctionEntry *Aentry = itr->second; + Item *item = auctionmgr.GetAItem(Aentry->item_guidlow); + if (item) + { + ItemPrototype const *prototype = item->GetProto(); + if (prototype) + { + switch (prototype->Quality) + { + case 0: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_GREY_TG); + else + config->IncItemCounts(AHB_GREY_I); + break; + case 1: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_WHITE_TG); + else + config->IncItemCounts(AHB_WHITE_I); + break; + case 2: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_GREEN_TG); + else + config->IncItemCounts(AHB_GREEN_I); + break; + case 3: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_BLUE_TG); + else + config->IncItemCounts(AHB_BLUE_I); + break; + case 4: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_PURPLE_TG); + else + config->IncItemCounts(AHB_PURPLE_I); + break; + case 5: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_ORANGE_TG); + else + config->IncItemCounts(AHB_ORANGE_I); + break; + case 6: + if (prototype->Class == ITEM_CLASS_TRADE_GOODS) + config->IncItemCounts(AHB_YELLOW_TG); + else + config->IncItemCounts(AHB_YELLOW_I); + break; + } + } + } + } + } + if (debug_Out) + { + sLog.outString("Current Items in %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString()); + sLog.outString("Grey Trade Goods\t%u\tGrey Items\t%u", config->GetItemCounts(AHB_GREY_TG), config->GetItemCounts(AHB_GREY_I)); + sLog.outString("White Trade Goods\t%u\tWhite Items\t%u", config->GetItemCounts(AHB_WHITE_TG), config->GetItemCounts(AHB_WHITE_I)); + sLog.outString("Green Trade Goods\t%u\tGreen Items\t%u", config->GetItemCounts(AHB_GREEN_TG), config->GetItemCounts(AHB_GREEN_I)); + sLog.outString("Blue Trade Goods\t%u\tBlue Items\t%u", config->GetItemCounts(AHB_BLUE_TG), config->GetItemCounts(AHB_BLUE_I)); + sLog.outString("Purple Trade Goods\t%u\tPurple Items\t%u", config->GetItemCounts(AHB_PURPLE_TG), config->GetItemCounts(AHB_PURPLE_I)); + sLog.outString("Orange Trade Goods\t%u\tOrange Items\t%u", config->GetItemCounts(AHB_ORANGE_TG), config->GetItemCounts(AHB_ORANGE_I)); + sLog.outString("Yellow Trade Goods\t%u\tYellow Items\t%u", config->GetItemCounts(AHB_YELLOW_TG), config->GetItemCounts(AHB_YELLOW_I)); + } + } + if (AHBBuyer) + { + //load buyer bid prices + config->SetBuyerPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT buyerpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetBuyerPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT buyerpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetBuyerPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT buyerpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetBuyerPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT buyerpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetBuyerPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT buyerpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetBuyerPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT buyerpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + config->SetBuyerPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT buyerpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + //load bidding interval + config->SetBiddingInterval(CharacterDatabase.PQuery("SELECT buyerbiddinginterval FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + //load bids per interval + config->SetBidsPerInterval(CharacterDatabase.PQuery("SELECT buyerbidsperinterval FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); + if (debug_Out) + { + sLog.outString("buyerPriceGrey = %u", config->GetBuyerPrice(AHB_GREY)); + sLog.outString("buyerPriceWhite = %u", config->GetBuyerPrice(AHB_WHITE)); + sLog.outString("buyerPriceGreen = %u", config->GetBuyerPrice(AHB_GREEN)); + sLog.outString("buyerPriceBlue = %u", config->GetBuyerPrice(AHB_BLUE)); + sLog.outString("buyerPricePurple = %u", config->GetBuyerPrice(AHB_PURPLE)); + sLog.outString("buyerPriceOrange = %u", config->GetBuyerPrice(AHB_ORANGE)); + sLog.outString("buyerPriceYellow = %u", config->GetBuyerPrice(AHB_YELLOW)); + sLog.outString("buyerBiddingInterval = %u", config->GetBiddingInterval()); + sLog.outString("buyerBidsPerInterval = %u", config->GetBidsPerInterval()); + } + } + if (debug_Out) sLog.outString("End Settings for %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString()); +} diff --git a/src/server/game/AI/AuctionHouseBot/AuctionHouseBot.h b/src/server/game/AI/AuctionHouseBot/AuctionHouseBot.h new file mode 100644 index 00000000000..208a09aa0b2 --- /dev/null +++ b/src/server/game/AI/AuctionHouseBot/AuctionHouseBot.h @@ -0,0 +1,1225 @@ +#ifndef AUCTION_HOUSE_BOT_H +#define AUCTION_HOUSE_BOT_H + +#include "World.h" +#include "Config/ConfigEnv.h" +#include "ItemPrototype.h" + +#define AHB_GREY 0 +#define AHB_WHITE 1 +#define AHB_GREEN 2 +#define AHB_BLUE 3 +#define AHB_PURPLE 4 +#define AHB_ORANGE 5 +#define AHB_YELLOW 6 +#define AHB_MAX_QUALITY 6 +#define AHB_GREY_TG 0 +#define AHB_WHITE_TG 1 +#define AHB_GREEN_TG 2 +#define AHB_BLUE_TG 3 +#define AHB_PURPLE_TG 4 +#define AHB_ORANGE_TG 5 +#define AHB_YELLOW_TG 6 +#define AHB_GREY_I 7 +#define AHB_WHITE_I 8 +#define AHB_GREEN_I 9 +#define AHB_BLUE_I 10 +#define AHB_PURPLE_I 11 +#define AHB_ORANGE_I 12 +#define AHB_YELLOW_I 13 + +class AHBConfig +{ +private: + uint32 AHID; + uint32 AHFID; + uint32 minItems; + uint32 maxItems; + uint32 percentGreyTradeGoods; + uint32 percentWhiteTradeGoods; + uint32 percentGreenTradeGoods; + uint32 percentBlueTradeGoods; + uint32 percentPurpleTradeGoods; + uint32 percentOrangeTradeGoods; + uint32 percentYellowTradeGoods; + uint32 percentGreyItems; + uint32 percentWhiteItems; + uint32 percentGreenItems; + uint32 percentBlueItems; + uint32 percentPurpleItems; + uint32 percentOrangeItems; + uint32 percentYellowItems; + uint32 minPriceGrey; + uint32 maxPriceGrey; + uint32 minBidPriceGrey; + uint32 maxBidPriceGrey; + uint32 maxStackGrey; + uint32 minPriceWhite; + uint32 maxPriceWhite; + uint32 minBidPriceWhite; + uint32 maxBidPriceWhite; + uint32 maxStackWhite; + uint32 minPriceGreen; + uint32 maxPriceGreen; + uint32 minBidPriceGreen; + uint32 maxBidPriceGreen; + uint32 maxStackGreen; + uint32 minPriceBlue; + uint32 maxPriceBlue; + uint32 minBidPriceBlue; + uint32 maxBidPriceBlue; + uint32 maxStackBlue; + uint32 minPricePurple; + uint32 maxPricePurple; + uint32 minBidPricePurple; + uint32 maxBidPricePurple; + uint32 maxStackPurple; + uint32 minPriceOrange; + uint32 maxPriceOrange; + uint32 minBidPriceOrange; + uint32 maxBidPriceOrange; + uint32 maxStackOrange; + uint32 minPriceYellow; + uint32 maxPriceYellow; + uint32 minBidPriceYellow; + uint32 maxBidPriceYellow; + uint32 maxStackYellow; + + uint32 buyerPriceGrey; + uint32 buyerPriceWhite; + uint32 buyerPriceGreen; + uint32 buyerPriceBlue; + uint32 buyerPricePurple; + uint32 buyerPriceOrange; + uint32 buyerPriceYellow; + uint32 buyerBiddingInterval; + uint32 buyerBidsPerInterval; + + uint32 greytgp; + uint32 whitetgp; + uint32 greentgp; + uint32 bluetgp; + uint32 purpletgp; + uint32 orangetgp; + uint32 yellowtgp; + uint32 greyip; + uint32 whiteip; + uint32 greenip; + uint32 blueip; + uint32 purpleip; + uint32 orangeip; + uint32 yellowip; + + uint32 greyTGoods; + uint32 whiteTGoods; + uint32 greenTGoods; + uint32 blueTGoods; + uint32 purpleTGoods; + uint32 orangeTGoods; + uint32 yellowTGoods; + + uint32 greyItems; + uint32 whiteItems; + uint32 greenItems; + uint32 blueItems; + uint32 purpleItems; + uint32 orangeItems; + uint32 yellowItems; + +public: + AHBConfig(uint32 ahid) + { + AHID = ahid; + switch(ahid) + { + case 2: + AHFID = 55; + break; + case 6: + AHFID = 29; + break; + case 7: + AHFID = 120; + break; + default: + AHFID = 120; + break; + } + } + AHBConfig() + { + } + uint32 GetAHID() + { + return AHID; + } + uint32 GetAHFID() + { + return AHFID; + } + void SetMinItems(uint32 value) + { + minItems = value; + } + uint32 GetMinItems() + { + if ((minItems == 0) && (maxItems)) + return maxItems; + else if ((maxItems) && (minItems > maxItems)) + return maxItems; + else + return minItems; + } + void SetMaxItems(uint32 value) + { + maxItems = value; + CalculatePercents(); + } + uint32 GetMaxItems() + { + return maxItems; + } + void SetPercentages(uint32 greytg, uint32 whitetg, uint32 greentg, uint32 bluetg, uint32 purpletg, uint32 orangetg, uint32 yellowtg, uint32 greyi, uint32 whitei, uint32 greeni, uint32 bluei, uint32 purplei, uint32 orangei, uint32 yellowi) + { + uint32 totalPercent = greytg + whitetg + greentg + bluetg + purpletg + orangetg + yellowtg + greyi + whitei + greeni + bluei + purplei + orangei + yellowi; + + if (totalPercent == 0) + { + maxItems = 0; + } + else if (totalPercent != 100) + { + greytg = 0; + whitetg = 27; + greentg = 12; + bluetg = 10; + purpletg = 1; + orangetg = 0; + yellowtg = 0; + greyi = 0; + whitei = 10; + greeni = 30; + bluei = 8; + purplei = 2; + orangei = 0; + yellowi = 0; + } + percentGreyTradeGoods = greytg; + percentWhiteTradeGoods = whitetg; + percentGreenTradeGoods = greentg; + percentBlueTradeGoods = bluetg; + percentPurpleTradeGoods = purpletg; + percentOrangeTradeGoods = orangetg; + percentYellowTradeGoods = yellowtg; + percentGreyItems = greyi; + percentWhiteItems = whitei; + percentGreenItems = greeni; + percentBlueItems = bluei; + percentPurpleItems = purplei; + percentOrangeItems = orangei; + percentYellowItems = yellowi; + CalculatePercents(); + } + uint32 GetPercentages(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: + return percentGreyTradeGoods; + break; + case AHB_WHITE_TG: + return percentWhiteTradeGoods; + break; + case AHB_GREEN_TG: + return percentGreenTradeGoods; + break; + case AHB_BLUE_TG: + return percentBlueTradeGoods; + break; + case AHB_PURPLE_TG: + return percentPurpleTradeGoods; + break; + case AHB_ORANGE_TG: + return percentOrangeTradeGoods; + break; + case AHB_YELLOW_TG: + return percentYellowTradeGoods; + break; + case AHB_GREY_I: + return percentGreyItems; + break; + case AHB_WHITE_I: + return percentWhiteItems; + break; + case AHB_GREEN_I: + return percentGreenItems; + break; + case AHB_BLUE_I: + return percentBlueItems; + break; + case AHB_PURPLE_I: + return percentPurpleItems; + break; + case AHB_ORANGE_I: + return percentOrangeItems; + break; + case AHB_YELLOW_I: + return percentYellowItems; + break; + default: + return 0; + break; + } + } + void SetMinPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: + minPriceGrey = value; + break; + case AHB_WHITE: + minPriceWhite = value; + break; + case AHB_GREEN: + minPriceGreen = value; + break; + case AHB_BLUE: + minPriceBlue = value; + break; + case AHB_PURPLE: + minPricePurple = value; + break; + case AHB_ORANGE: + minPriceOrange = value; + break; + case AHB_YELLOW: + minPriceYellow = value; + break; + default: + break; + } + } + uint32 GetMinPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: + { + if (minPriceGrey == 0) + return 100; + else if (minPriceGrey > maxPriceGrey) + return maxPriceGrey; + else + return minPriceGrey; + break; + } + case AHB_WHITE: + { + if (minPriceWhite == 0) + return 150; + else if (minPriceWhite > maxPriceWhite) + return maxPriceWhite; + else + return minPriceWhite; + break; + } + case AHB_GREEN: + { + if (minPriceGreen == 0) + return 200; + else if (minPriceGreen > maxPriceGreen) + return maxPriceGreen; + else + return minPriceGreen; + break; + } + case AHB_BLUE: + { + if (minPriceBlue == 0) + return 250; + else if (minPriceBlue > maxPriceBlue) + return maxPriceBlue; + else + return minPriceBlue; + break; + } + case AHB_PURPLE: + { + if (minPricePurple == 0) + return 300; + else if (minPricePurple > maxPricePurple) + return maxPricePurple; + else + return minPricePurple; + break; + } + case AHB_ORANGE: + { + if (minPriceOrange == 0) + return 400; + else if (minPriceOrange > maxPriceOrange) + return maxPriceOrange; + else + return minPriceOrange; + break; + } + case AHB_YELLOW: + { + if (minPriceYellow == 0) + return 500; + else if (minPriceYellow > maxPriceYellow) + return maxPriceYellow; + else + return minPriceYellow; + break; + } + default: + { + return 0; + break; + } + } + } + void SetMaxPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: + maxPriceGrey = value; + break; + case AHB_WHITE: + maxPriceWhite = value; + break; + case AHB_GREEN: + maxPriceGreen = value; + break; + case AHB_BLUE: + maxPriceBlue = value; + break; + case AHB_PURPLE: + maxPricePurple = value; + break; + case AHB_ORANGE: + maxPriceOrange = value; + break; + case AHB_YELLOW: + maxPriceYellow = value; + break; + default: + break; + } + } + uint32 GetMaxPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: + { + if (maxPriceGrey == 0) + return 150; + else + return maxPriceGrey; + break; + } + case AHB_WHITE: + { + if (maxPriceWhite == 0) + return 250; + else + return maxPriceWhite; + break; + } + case AHB_GREEN: + { + if (maxPriceGreen == 0) + return 300; + else + return maxPriceGreen; + break; + } + case AHB_BLUE: + { + if (maxPriceBlue == 0) + return 350; + else + return maxPriceBlue; + break; + } + case AHB_PURPLE: + { + if (maxPricePurple == 0) + return 450; + else + return maxPricePurple; + break; + } + case AHB_ORANGE: + { + if (maxPriceOrange == 0) + return 550; + else + return maxPriceOrange; + break; + } + case AHB_YELLOW: + { + if (maxPriceYellow == 0) + return 650; + else + return maxPriceYellow; + break; + } + default: + { + return 0; + break; + } + } + } + void SetMinBidPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: + minBidPriceGrey = value; + break; + case AHB_WHITE: + minBidPriceWhite = value; + break; + case AHB_GREEN: + minBidPriceGreen = value; + break; + case AHB_BLUE: + minBidPriceBlue = value; + break; + case AHB_PURPLE: + minBidPricePurple = value; + break; + case AHB_ORANGE: + minBidPriceOrange = value; + break; + case AHB_YELLOW: + minBidPriceYellow = value; + break; + default: + break; + } + } + uint32 GetMinBidPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: + { + if (minBidPriceGrey > 100) + return 100; + else + return minBidPriceGrey; + break; + } + case AHB_WHITE: + { + if (minBidPriceWhite > 100) + return 100; + else + return minBidPriceWhite; + break; + } + case AHB_GREEN: + { + if (minBidPriceGreen > 100) + return 100; + else + return minBidPriceGreen; + break; + } + case AHB_BLUE: + { + if (minBidPriceBlue > 100) + return 100; + else + return minBidPriceBlue; + break; + } + case AHB_PURPLE: + { + if (minBidPricePurple > 100) + return 100; + else + return minBidPricePurple; + break; + } + case AHB_ORANGE: + { + if (minBidPriceOrange > 100) + return 100; + else + return minBidPriceOrange; + break; + } + case AHB_YELLOW: + { + if (minBidPriceYellow > 100) + return 100; + else + return minBidPriceYellow; + break; + } + default: + { + return 0; + break; + } + } + } + void SetMaxBidPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: + maxBidPriceGrey = value; + break; + case AHB_WHITE: + maxBidPriceWhite = value; + break; + case AHB_GREEN: + maxBidPriceGreen = value; + break; + case AHB_BLUE: + maxBidPriceBlue = value; + break; + case AHB_PURPLE: + maxBidPricePurple = value; + break; + case AHB_ORANGE: + maxBidPriceOrange = value; + break; + case AHB_YELLOW: + maxBidPriceYellow = value; + break; + default: + break; + } + } + uint32 GetMaxBidPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: + { + if (maxBidPriceGrey > 100) + return 100; + else + return maxBidPriceGrey; + break; + } + case AHB_WHITE: + { + if (maxBidPriceWhite > 100) + return 100; + else + return maxBidPriceWhite; + break; + } + case AHB_GREEN: + { + if (maxBidPriceGreen > 100) + return 100; + else + return maxBidPriceGreen; + break; + } + case AHB_BLUE: + { + if (maxBidPriceBlue > 100) + return 100; + else + return maxBidPriceBlue; + break; + } + case AHB_PURPLE: + { + if (maxBidPricePurple > 100) + return 100; + else + return maxBidPricePurple; + break; + } + case AHB_ORANGE: + { + if (maxBidPriceOrange > 100) + return 100; + else + return maxBidPriceOrange; + break; + } + case AHB_YELLOW: + { + if (maxBidPriceYellow > 100) + return 100; + else + return maxBidPriceYellow; + break; + } + default: + { + return 0; + break; + } + } + } + void SetMaxStack(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: + maxStackGrey = value; + break; + case AHB_WHITE: + maxStackWhite = value; + break; + case AHB_GREEN: + maxStackGreen = value; + break; + case AHB_BLUE: + maxStackBlue = value; + break; + case AHB_PURPLE: + maxStackPurple = value; + break; + case AHB_ORANGE: + maxStackOrange = value; + break; + case AHB_YELLOW: + maxStackYellow = value; + break; + default: + break; + } + } + uint32 GetMaxStack(uint32 color) + { + switch(color) + { + case AHB_GREY: + { + return maxStackGrey; + break; + } + case AHB_WHITE: + { + return maxStackWhite; + break; + } + case AHB_GREEN: + { + return maxStackGreen; + break; + } + case AHB_BLUE: + { + return maxStackBlue; + break; + } + case AHB_PURPLE: + { + return maxStackPurple; + break; + } + case AHB_ORANGE: + { + return maxStackOrange; + break; + } + case AHB_YELLOW: + { + return maxStackYellow; + break; + } + default: + { + return 0; + break; + } + } + } + void SetBuyerPrice(uint32 color, uint32 value) + { + switch(color) + { + case AHB_GREY: + buyerPriceGrey = value; + break; + case AHB_WHITE: + buyerPriceWhite = value; + break; + case AHB_GREEN: + buyerPriceGreen = value; + break; + case AHB_BLUE: + buyerPriceBlue = value; + break; + case AHB_PURPLE: + buyerPricePurple = value; + break; + case AHB_ORANGE: + buyerPriceOrange = value; + break; + case AHB_YELLOW: + buyerPriceYellow = value; + break; + default: + break; + } + } + uint32 GetBuyerPrice(uint32 color) + { + switch(color) + { + case AHB_GREY: + return buyerPriceGrey; + break; + case AHB_WHITE: + return buyerPriceWhite; + break; + case AHB_GREEN: + return buyerPriceGreen; + break; + case AHB_BLUE: + return buyerPriceBlue; + break; + case AHB_PURPLE: + return buyerPricePurple; + break; + case AHB_ORANGE: + return buyerPriceOrange; + break; + case AHB_YELLOW: + return buyerPriceYellow; + break; + default: + return 0; + break; + } + } + void SetBiddingInterval(uint32 value) + { + buyerBiddingInterval = value; + } + uint32 GetBiddingInterval() + { + return buyerBiddingInterval; + } + void CalculatePercents() + { + greytgp = (uint32) (((double)percentGreyTradeGoods / 100.0) * maxItems); + whitetgp = (uint32) (((double)percentWhiteTradeGoods / 100.0) * maxItems); + greentgp = (uint32) (((double)percentGreenTradeGoods / 100.0) * maxItems); + bluetgp = (uint32) (((double)percentBlueTradeGoods / 100.0) * maxItems); + purpletgp = (uint32) (((double)percentPurpleTradeGoods / 100.0) * maxItems); + orangetgp = (uint32) (((double)percentOrangeTradeGoods / 100.0) * maxItems); + yellowtgp = (uint32) (((double)percentYellowTradeGoods / 100.0) * maxItems); + greyip = (uint32) (((double)percentGreyItems / 100.0) * maxItems); + whiteip = (uint32) (((double)percentWhiteItems / 100.0) * maxItems); + greenip = (uint32) (((double)percentGreenItems / 100.0) * maxItems); + blueip = (uint32) (((double)percentBlueItems / 100.0) * maxItems); + purpleip = (uint32) (((double)percentPurpleItems / 100.0) * maxItems); + orangeip = (uint32) (((double)percentOrangeItems / 100.0) * maxItems); + yellowip = (uint32) (((double)percentYellowItems / 100.0) * maxItems); + uint32 total = greytgp + whitetgp + greentgp + bluetgp + purpletgp + orangetgp + yellowtgp + greyip + whiteip + greenip + blueip + purpleip + orangeip + yellowip; + int32 diff = (maxItems - total); + if (diff < 0) + { + if ((whiteip - diff) > 0) + whiteip -= diff; + else if ((greenip - diff) > 0) + greenip -= diff; + } + else if (diff < 0) + { + whiteip += diff; + } + } + uint32 GetPercents(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: + return greytgp; + break; + case AHB_WHITE_TG: + return whitetgp; + break; + case AHB_GREEN_TG: + return greentgp; + break; + case AHB_BLUE_TG: + return bluetgp; + break; + case AHB_PURPLE_TG: + return purpletgp; + break; + case AHB_ORANGE_TG: + return orangetgp; + break; + case AHB_YELLOW_TG: + return yellowtgp; + break; + case AHB_GREY_I: + return greyip; + break; + case AHB_WHITE_I: + return whiteip; + break; + case AHB_GREEN_I: + return greenip; + break; + case AHB_BLUE_I: + return blueip; + break; + case AHB_PURPLE_I: + return purpleip; + break; + case AHB_ORANGE_I: + return orangeip; + break; + case AHB_YELLOW_I: + return yellowip; + break; + default: + return 0; + break; + } + } + + void DecItemCounts(uint32 Class, uint32 Quality) + { + switch(Class) + { + case ITEM_CLASS_TRADE_GOODS: + DecItemCounts(Quality); + break; + default: + DecItemCounts(Quality + 7); + break; + } + } + + void DecItemCounts(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: + --greyTGoods; + break; + case AHB_WHITE_TG: + --whiteTGoods; + break; + case AHB_GREEN_TG: + --greenTGoods; + break; + case AHB_BLUE_TG: + --blueTGoods; + break; + case AHB_PURPLE_TG: + --purpleTGoods; + break; + case AHB_ORANGE_TG: + --orangeTGoods; + break; + case AHB_YELLOW_TG: + --yellowTGoods; + break; + case AHB_GREY_I: + --greyItems; + break; + case AHB_WHITE_I: + --whiteItems; + break; + case AHB_GREEN_I: + --greenItems; + break; + case AHB_BLUE_I: + --blueItems; + break; + case AHB_PURPLE_I: + --purpleItems; + break; + case AHB_ORANGE_I: + --orangeItems; + break; + case AHB_YELLOW_I: + --yellowItems; + break; + default: + break; + } + } + + void IncItemCounts(uint32 Class, uint32 Quality) + { + switch(Class) + { + case ITEM_CLASS_TRADE_GOODS: + IncItemCounts(Quality); + break; + default: + IncItemCounts(Quality + 7); + break; + } + } + + void IncItemCounts(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: + ++greyTGoods; + break; + case AHB_WHITE_TG: + ++whiteTGoods; + break; + case AHB_GREEN_TG: + ++greenTGoods; + break; + case AHB_BLUE_TG: + ++blueTGoods; + break; + case AHB_PURPLE_TG: + ++purpleTGoods; + break; + case AHB_ORANGE_TG: + ++orangeTGoods; + break; + case AHB_YELLOW_TG: + ++yellowTGoods; + break; + case AHB_GREY_I: + ++greyItems; + break; + case AHB_WHITE_I: + ++whiteItems; + break; + case AHB_GREEN_I: + ++greenItems; + break; + case AHB_BLUE_I: + ++blueItems; + break; + case AHB_PURPLE_I: + ++purpleItems; + break; + case AHB_ORANGE_I: + ++orangeItems; + break; + case AHB_YELLOW_I: + ++yellowItems; + break; + default: + break; + } + } + + void ResetItemCounts() + { + greyTGoods = 0; + whiteTGoods = 0; + greenTGoods = 0; + blueTGoods = 0; + purpleTGoods = 0; + orangeTGoods = 0; + yellowTGoods = 0; + + greyItems = 0; + whiteItems = 0; + greenItems = 0; + blueItems = 0; + purpleItems = 0; + orangeItems = 0; + yellowItems = 0; + } + + uint32 TotalItemCounts() + { + return( + greyTGoods + + whiteTGoods + + greenTGoods + + blueTGoods + + purpleTGoods + + orangeTGoods + + yellowTGoods + + + greyItems + + whiteItems + + greenItems + + blueItems + + purpleItems + + orangeItems + + yellowItems); + } + + uint32 GetItemCounts(uint32 color) + { + switch(color) + { + case AHB_GREY_TG: + return greyTGoods; + break; + case AHB_WHITE_TG: + return whiteTGoods; + break; + case AHB_GREEN_TG: + return greenTGoods; + break; + case AHB_BLUE_TG: + return blueTGoods; + break; + case AHB_PURPLE_TG: + return purpleTGoods; + break; + case AHB_ORANGE_TG: + return orangeTGoods; + break; + case AHB_YELLOW_TG: + return yellowTGoods; + break; + case AHB_GREY_I: + return greyItems; + break; + case AHB_WHITE_I: + return whiteItems; + break; + case AHB_GREEN_I: + return greenItems; + break; + case AHB_BLUE_I: + return blueItems; + break; + case AHB_PURPLE_I: + return purpleItems; + break; + case AHB_ORANGE_I: + return orangeItems; + break; + case AHB_YELLOW_I: + return yellowItems; + break; + default: + return 0; + break; + } + } + void SetBidsPerInterval(uint32 value) + { + buyerBidsPerInterval = value; + } + uint32 GetBidsPerInterval() + { + return buyerBidsPerInterval; + } + ~AHBConfig() + { + } +}; +class AuctionHouseBot +{ +private: + + bool debug_Out; + bool debug_Out_Filters; + + bool AHBSeller; + bool AHBBuyer; + bool BuyMethod; + bool SellMethod; + + uint32 AHBplayerAccount; + uint32 AHBplayerGUID; + uint32 ItemsPerCycle; + + //Begin Filters + + bool Vendor_Items; + bool Loot_Items; + bool Other_Items; + bool Vendor_TGs; + bool Loot_TGs; + bool Other_TGs; + + bool No_Bind; + bool Bind_When_Picked_Up; + bool Bind_When_Equipped; + bool Bind_When_Use; + bool Bind_Quest_Item; + + bool DisableBeta_PTR_Unused; + bool DisablePermEnchant; + bool DisableConjured; + bool DisableGems; + bool DisableMoney; + bool DisableMoneyLoot; + bool DisableLootable; + bool DisableKeys; + bool DisableDuration; + bool DisableBOP_Or_Quest_NoReqLevel; + + bool DisableWarriorItems; + bool DisablePaladinItems; + bool DisableHunterItems; + bool DisableRogueItems; + bool DisablePriestItems; + bool DisableDKItems; + bool DisableShamanItems; + bool DisableMageItems; + bool DisableWarlockItems; + bool DisableUnusedClassItems; + bool DisableDruidItems; + + uint32 DisableItemsBelowLevel; + uint32 DisableItemsAboveLevel; + uint32 DisableTGsBelowLevel; + uint32 DisableTGsAboveLevel; + uint32 DisableItemsBelowGUID; + uint32 DisableItemsAboveGUID; + uint32 DisableTGsBelowGUID; + uint32 DisableTGsAboveGUID; + uint32 DisableItemsBelowReqLevel; + uint32 DisableItemsAboveReqLevel; + uint32 DisableTGsBelowReqLevel; + uint32 DisableTGsAboveReqLevel; + uint32 DisableItemsBelowReqSkillRank; + uint32 DisableItemsAboveReqSkillRank; + uint32 DisableTGsBelowReqSkillRank; + uint32 DisableTGsAboveReqSkillRank; + + //End Filters + + AHBConfig AllianceConfig; + AHBConfig HordeConfig; + AHBConfig NeutralConfig; + + time_t _lastrun_a; + time_t _lastrun_h; + time_t _lastrun_n; + + inline uint32 minValue(uint32 a, uint32 b) { return a <= b ? a : b; }; + void addNewAuctions(Player *AHBplayer, AHBConfig *config); + void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session); + +public: + AuctionHouseBot(); + ~AuctionHouseBot(); + void Update(); + void Initialize(); + void LoadValues(AHBConfig*); + void DecrementItemCounts(AuctionEntry* ah, uint32 item_template); + void IncrementItemCounts(AuctionEntry* ah); + void Commands(uint32, uint32, uint32, char*); + uint32 GetAHBplayerGUID() { return AHBplayerGUID; }; +}; + +#define auctionbot Trinity::Singleton::Instance() + +#endif diff --git a/src/server/game/AI/CombatAI.cpp b/src/server/game/AI/CombatAI.cpp new file mode 100644 index 00000000000..0d0ff17ffd7 --- /dev/null +++ b/src/server/game/AI/CombatAI.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CombatAI.h" +#include "SpellMgr.h" +#include "Vehicle.h" + +int AggressorAI::Permissible(const Creature *creature) +{ + // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight + if (!creature->isCivilian() && !creature->IsNeutralToAll()) + return PERMIT_BASE_PROACTIVE; + + return PERMIT_BASE_NO; +} + +void AggressorAI::UpdateAI(const uint32 /*diff*/) +{ + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); +} + +// some day we will delete these useless things +int CombatAI::Permissible(const Creature * /*creature*/) +{ + return PERMIT_BASE_NO; +} + +int ArchorAI::Permissible(const Creature * /*creature*/) +{ + return PERMIT_BASE_NO; +} + +int TurretAI::Permissible(const Creature * /*creature*/) +{ + return PERMIT_BASE_NO; +} + +int AOEAI::Permissible(const Creature * /*creature*/) +{ + return PERMIT_BASE_NO; +} + +int VehicleAI::Permissible(const Creature * /*creature*/) +{ + return PERMIT_BASE_NO; +} + +void CombatAI::InitializeAI() +{ + for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + if (me->m_spells[i] && GetSpellStore()->LookupEntry(me->m_spells[i])) + spells.push_back(me->m_spells[i]); + + CreatureAI::InitializeAI(); +} + +void CombatAI::Reset() +{ + events.Reset(); +} + +void CombatAI::JustDied(Unit *killer) +{ + for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i) + if (AISpellInfo[*i].condition == AICOND_DIE) + me->CastSpell(killer, *i, true); +} + +void CombatAI::EnterCombat(Unit *who) +{ + for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i) + { + if (AISpellInfo[*i].condition == AICOND_AGGRO) + me->CastSpell(who, *i, false); + else if (AISpellInfo[*i].condition == AICOND_COMBAT) + events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown); + } +} + +void CombatAI::UpdateAI(const uint32 diff) +{ + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->hasUnitState(UNIT_STAT_CASTING)) + return; + + if (uint32 spellId = events.ExecuteEvent()) + { + DoCast(spellId); + events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown); + } + else + DoMeleeAttackIfReady(); +} + +///////////////// +//CasterAI +///////////////// + +void CasterAI::InitializeAI() +{ + CombatAI::InitializeAI(); + + float m_attackDist = 30.0f; + for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr) + if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange) + m_attackDist = GetAISpellInfo(*itr)->maxRange; + if (m_attackDist == 30.0f) + m_attackDist = MELEE_RANGE; +} + +void CasterAI::EnterCombat(Unit *who) +{ + if (spells.empty()) + return; + + uint32 spell = rand()%spells.size(); + uint32 count = 0; + for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count) + { + if (AISpellInfo[*itr].condition == AICOND_AGGRO) + me->CastSpell(who, *itr, false); + else if (AISpellInfo[*itr].condition == AICOND_COMBAT) + { + uint32 cooldown = GetAISpellInfo(*itr)->realCooldown; + if (count == spell) + { + DoCast(spells[spell]); + cooldown += me->GetCurrentSpellCastTime(*itr); + } + events.ScheduleEvent(*itr, cooldown); + } + } +} + +void CasterAI::UpdateAI(const uint32 diff) +{ + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->hasUnitState(UNIT_STAT_CASTING)) + return; + + if (uint32 spellId = events.ExecuteEvent()) + { + DoCast(spellId); + uint32 casttime = me->GetCurrentSpellCastTime(spellId); + events.ScheduleEvent(spellId, (casttime ? casttime : 500) + GetAISpellInfo(spellId)->realCooldown); + } +} + +////////////// +//ArchorAI +////////////// + +ArchorAI::ArchorAI(Creature *c) : CreatureAI(c) +{ + if (!me->m_spells[0]) + sLog.outError("ArchorAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); + + m_minRange = GetSpellMinRange(me->m_spells[0], false); + if (!m_minRange) + m_minRange = MELEE_RANGE; + me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false); + me->m_SightDistance = me->m_CombatDistance; +} + +void ArchorAI::AttackStart(Unit *who) +{ + if (!who) + return; + + if (me->IsWithinCombatRange(who, m_minRange)) + { + if (me->Attack(who, true) && !who->IsFlying()) + me->GetMotionMaster()->MoveChase(who); + } + else + { + if (me->Attack(who, false) && !who->IsFlying()) + me->GetMotionMaster()->MoveChase(who, me->m_CombatDistance); + } + + if (who->IsFlying()) + me->GetMotionMaster()->MoveIdle(); +} + +void ArchorAI::UpdateAI(const uint32 /*diff*/) +{ + if (!UpdateVictim()) + return; + + if (!me->IsWithinCombatRange(me->getVictim(), m_minRange)) + DoSpellAttackIfReady(me->m_spells[0]); + else + DoMeleeAttackIfReady(); +} + +////////////// +//TurretAI +////////////// + +TurretAI::TurretAI(Creature *c) : CreatureAI(c) +{ + if (!me->m_spells[0]) + sLog.outError("TurretAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); + + m_minRange = GetSpellMinRange(me->m_spells[0], false); + me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false); + me->m_SightDistance = me->m_CombatDistance; +} + +bool TurretAI::CanAIAttack(const Unit * /*who*/) const +{ + // TODO: use one function to replace it + if (!me->IsWithinCombatRange(me->getVictim(), me->m_CombatDistance) + || m_minRange && me->IsWithinCombatRange(me->getVictim(), m_minRange)) + return false; + return true; +} + +void TurretAI::AttackStart(Unit *who) +{ + if (who) + me->Attack(who, false); +} + +void TurretAI::UpdateAI(const uint32 /*diff*/) +{ + if (!UpdateVictim()) + return; + + DoSpellAttackIfReady(me->m_spells[0]); +} + +////////////// +//AOEAI +////////////// + +AOEAI::AOEAI(Creature *c) : CreatureAI(c) +{ + if (!me->m_spells[0]) + sLog.outError("AOEAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); + + me->SetVisibility(VISIBILITY_ON);//visible to see all spell anims + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);//can't be targeted + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_1);//can't be damaged + me->SetDisplayId(11686);//invisible model,around a size of a player +} + +bool AOEAI::CanAIAttack(const Unit * /*who*/) const +{ + return false; +} + +void AOEAI::AttackStart(Unit * /*who*/) +{ +} + +void AOEAI::UpdateAI(const uint32 /*diff*/) +{ + if (!me->HasAura(me->m_spells[0])) + me->CastSpell(me, me->m_spells[0],false); +} + +////////////// +//VehicleAI +////////////// + +VehicleAI::VehicleAI(Creature *c) : CreatureAI(c), m_vehicle(c->GetVehicleKit()), m_IsVehicleInUse(false), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME) +{ + LoadConditions(); + m_DoDismiss = false; + m_DismissTimer = VEHICLE_DISMISS_TIME; +} + + +//NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted +void VehicleAI::UpdateAI(const uint32 diff) +{ + CheckConditions(diff); + + if (m_DoDismiss) + { + if (m_DismissTimer < diff) + { + m_DoDismiss = false; + me->SetVisibility(VISIBILITY_OFF); + me->ForcedDespawn(); + }else m_DismissTimer -= diff; + } +} + +void VehicleAI::Reset() +{ + me->SetVisibility(VISIBILITY_ON); + + m_vehicle->Reset(); +} + +void VehicleAI::OnCharmed(bool apply) +{ + if (m_IsVehicleInUse && !apply && !conditions.empty())//was used and has conditions + { + m_DoDismiss = true;//needs reset + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + } + else if (apply) + m_DoDismiss = false;//in use again + m_DismissTimer = VEHICLE_DISMISS_TIME;//reset timer + m_IsVehicleInUse = apply; +} + +void VehicleAI::LoadConditions() +{ + conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry()); + if (!conditions.empty()) + { + sLog.outDebug("VehicleAI::LoadConditions: loaded %u conditions", uint32(conditions.size())); + } +} + +void VehicleAI::CheckConditions(const uint32 diff) +{ + if(m_ConditionsTimer < diff) + { + if (!conditions.empty()) + { + for (SeatMap::iterator itr = m_vehicle->m_Seats.begin(); itr != m_vehicle->m_Seats.end(); ++itr) + if (Unit *passenger = itr->second.passenger) + { + if (Player* plr = passenger->ToPlayer()) + { + if (!sConditionMgr.IsPlayerMeetToConditions(plr, conditions)) + { + plr->ExitVehicle(); + return;//check other pessanger in next tick + } + } + } + } + m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME; + } else m_ConditionsTimer -= diff; +} \ No newline at end of file diff --git a/src/server/game/AI/CombatAI.h b/src/server/game/AI/CombatAI.h new file mode 100644 index 00000000000..8626b38dd37 --- /dev/null +++ b/src/server/game/AI/CombatAI.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_COMBATAI_H +#define TRINITY_COMBATAI_H + +#include "CreatureAI.h" +#include "CreatureAIImpl.h" +#include "ConditionMgr.h" + +class Creature; + +class AggressorAI : public CreatureAI +{ + public: + explicit AggressorAI(Creature *c) : CreatureAI(c) {} + + void UpdateAI(const uint32); + static int Permissible(const Creature *); +}; + +typedef std::vector SpellVct; + +class CombatAI : public CreatureAI +{ + public: + explicit CombatAI(Creature *c) : CreatureAI(c) {} + + void InitializeAI(); + void Reset(); + void EnterCombat(Unit* who); + void JustDied(Unit *killer); + void UpdateAI(const uint32 diff); + static int Permissible(const Creature *); + protected: + EventMap events; + SpellVct spells; +}; + +class CasterAI : public CombatAI +{ + public: + explicit CasterAI(Creature *c) : CombatAI(c) { m_attackDist = MELEE_RANGE; } + void InitializeAI(); + void AttackStart(Unit * victim) { AttackStartCaster(victim, m_attackDist); } + void UpdateAI(const uint32 diff); + void EnterCombat(Unit * /*who*/); + private: + float m_attackDist; +}; + +struct ArchorAI : public CreatureAI +{ + public: + explicit ArchorAI(Creature *c); + void AttackStart(Unit *who); + void UpdateAI(const uint32 diff); + + static int Permissible(const Creature *); + protected: + float m_minRange; +}; + +struct TurretAI : public CreatureAI +{ + public: + explicit TurretAI(Creature *c); + bool CanAIAttack(const Unit *who) const; + void AttackStart(Unit *who); + void UpdateAI(const uint32 diff); + + static int Permissible(const Creature *); + protected: + float m_minRange; +}; + +struct AOEAI : public CreatureAI +{ + public: + explicit AOEAI(Creature *c); + bool CanAIAttack(const Unit *who) const; + void AttackStart(Unit *who); + void UpdateAI(const uint32 diff); + + static int Permissible(const Creature *); +}; +#define VEHICLE_CONDITION_CHECK_TIME 1000 +#define VEHICLE_DISMISS_TIME 5000 +struct VehicleAI : public CreatureAI +{ + public: + explicit VehicleAI(Creature *c); + + void UpdateAI(const uint32 diff); + static int Permissible(const Creature *); + void Reset(); + void MoveInLineOfSight(Unit *) {} + void AttackStart(Unit *) {} + void OnCharmed(bool apply); + + private: + Vehicle* m_vehicle; + bool m_IsVehicleInUse; + void LoadConditions(); + void CheckConditions(const uint32 diff); + ConditionList conditions; + uint32 m_ConditionsTimer; + bool m_DoDismiss; + uint32 m_DismissTimer; +}; + +#endif diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp new file mode 100644 index 00000000000..fb8f37ae492 --- /dev/null +++ b/src/server/game/AI/CreatureAI.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CreatureAI.h" +#include "CreatureAIImpl.h" +#include "Creature.h" +#include "World.h" +#include "SpellMgr.h" +#include "Vehicle.h" + +//Disable CreatureAI when charmed +void CreatureAI::OnCharmed(bool /*apply*/) +{ + //me->IsAIEnabled = !apply;*/ + me->NeedChangeAI = true; + me->IsAIEnabled = false; +} + +AISpellInfoType * UnitAI::AISpellInfo; + AISpellInfoType * GetAISpellInfo(uint32 i) { return &CreatureAI::AISpellInfo[i]; } + +void CreatureAI::DoZoneInCombat(Creature* creature) +{ + if (!creature) + creature = me; + + if (!creature->CanHaveThreatList()) + return; + + Map *map = creature->GetMap(); + if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated + { + sLog.outError("DoZoneInCombat call for map that isn't an instance (creature entry = %d)", creature->GetTypeId() == TYPEID_UNIT ? creature->ToCreature()->GetEntry() : 0); + return; + } + + if (!creature->HasReactState(REACT_PASSIVE) && !creature->getVictim()) + { + if (Unit *target = creature->SelectNearestTarget(50)) + creature->AI()->AttackStart(target); + else if (creature->isSummon()) + { + if (Unit *summoner = creature->ToTempSummon()->GetSummoner()) + { + Unit *target = summoner->getAttackerForHelper(); + if (!target && summoner->CanHaveThreatList() && !summoner->getThreatManager().isThreatListEmpty()) + target = summoner->getThreatManager().getHostilTarget(); + if (target && (creature->IsFriendlyTo(summoner) || creature->IsHostileTo(target))) + creature->AI()->AttackStart(target); + } + } + } + + if (!creature->HasReactState(REACT_PASSIVE) && !creature->getVictim()) + { + sLog.outError("DoZoneInCombat called for creature that has empty threat list (creature entry = %u)", creature->GetEntry()); + return; + } + + Map::PlayerList const &PlList = map->GetPlayers(); + + if (PlList.isEmpty()) + return; + + for (Map::PlayerList::const_iterator i = PlList.begin(); i != PlList.end(); ++i) + { + if (Player* pPlayer = i->getSource()) + { + if (pPlayer->isGameMaster()) + continue; + + if (pPlayer->isAlive()) + { + creature->SetInCombatWith(pPlayer); + pPlayer->SetInCombatWith(creature); + creature->AddThreat(pPlayer, 0.0f); + } + + /* Causes certain things to never leave the threat list (Priest Lightwell, etc): + for (Unit::ControlList::const_iterator itr = pPlayer->m_Controlled.begin(); itr != pPlayer->m_Controlled.end(); ++itr) + { + creature->SetInCombatWith(*itr); + (*itr)->SetInCombatWith(creature); + creature->AddThreat(*itr, 0.0f); + }*/ + } + } +} + +// scripts does not take care about MoveInLineOfSight loops +// MoveInLineOfSight can be called inside another MoveInLineOfSight and cause stack overflow +void CreatureAI::MoveInLineOfSight_Safe(Unit *who) +{ + if (m_MoveInLineOfSight_locked == true) + return; + m_MoveInLineOfSight_locked = true; + MoveInLineOfSight(who); + m_MoveInLineOfSight_locked = false; +} + +void CreatureAI::MoveInLineOfSight(Unit *who) +{ + if (me->getVictim()) + return; + + if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) // non-combat pets should just stand there and look good;) + return; + + if (me->canStartAttack(who, false)) + AttackStart(who); + //else if (who->getVictim() && me->IsFriendlyTo(who) + // && me->IsWithinDistInMap(who, sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)) + // && me->canStartAttack(who->getVictim(), true)) // TODO: if we use true, it will not attack it when it arrives + // me->GetMotionMaster()->MoveChase(who->getVictim()); +} + +void CreatureAI::EnterEvadeMode() +{ + if (!_EnterEvadeMode()) + return; + + sLog.outDebug("Creature %u enters evade mode.", me->GetEntry()); + + if (!me->GetVehicle()) // otherwise me will be in evade mode forever + { + if (Unit *owner = me->GetCharmerOrOwner()) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } + else + me->GetMotionMaster()->MoveTargetedHome(); + } + + Reset(); + + if (me->IsVehicle()) // use the same sequence of addtoworld, aireset may remove all summons! + me->GetVehicleKit()->Reset(); +} + +/*void CreatureAI::AttackedBy(Unit* attacker) +{ + if (!me->getVictim()) + AttackStart(attacker); +}*/ diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h new file mode 100644 index 00000000000..c03d3dd09d0 --- /dev/null +++ b/src/server/game/AI/CreatureAI.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CREATUREAI_H +#define TRINITY_CREATUREAI_H + +#include "UnitAI.h" +#include "Common.h" + +class WorldObject; +class Unit; +class Creature; +class Player; +struct SpellEntry; + +#define TIME_INTERVAL_LOOK 5000 +#define VISIBILITY_RANGE 10000 + +//Spell targets used by SelectSpell +enum SelectTargetType +{ + SELECT_TARGET_DONTCARE = 0, //All target types allowed + + SELECT_TARGET_SELF, //Only Self casting + + SELECT_TARGET_SINGLE_ENEMY, //Only Single Enemy + SELECT_TARGET_AOE_ENEMY, //Only AoE Enemy + SELECT_TARGET_ANY_ENEMY, //AoE or Single Enemy + + SELECT_TARGET_SINGLE_FRIEND, //Only Single Friend + SELECT_TARGET_AOE_FRIEND, //Only AoE Friend + SELECT_TARGET_ANY_FRIEND, //AoE or Single Friend +}; + +//Spell Effects used by SelectSpell +enum SelectEffect +{ + SELECT_EFFECT_DONTCARE = 0, //All spell effects allowed + SELECT_EFFECT_DAMAGE, //Spell does damage + SELECT_EFFECT_HEALING, //Spell does healing + SELECT_EFFECT_AURA, //Spell applies an aura +}; + +enum SCEquip +{ + EQUIP_NO_CHANGE = -1, + EQUIP_UNEQUIP = 0 +}; + +class CreatureAI : public UnitAI +{ + protected: + Creature * const me; + + bool UpdateVictim(); + bool UpdateVictimWithGaze(); + bool UpdateCombatState(); + + void SetGazeOn(Unit *target); + + Creature *DoSummon(uint32 uiEntry, const Position &pos, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); + Creature *DoSummon(uint32 uiEntry, WorldObject *obj, float fRadius = 5.0f, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); + Creature *DoSummonFlyer(uint32 uiEntry, WorldObject *obj, float fZ, float fRadius = 5.0f, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); + + public: + explicit CreatureAI(Creature *c) : UnitAI((Unit*)c), me(c), m_MoveInLineOfSight_locked(false) {} + + virtual ~CreatureAI() {} + + /// == Reactions At ================================= + + // Called if IsVisible(Unit *who) is true at each *who move, reaction at visibility zone enter + void MoveInLineOfSight_Safe(Unit *who); + + // Called for reaction at stopping attack at no attackers or targets + virtual void EnterEvadeMode(); + + // Called for reaction at enter to combat if not in combat yet (enemy can be NULL) + virtual void EnterCombat(Unit* /*enemy*/) {} + + // Called at any Damage from any attacker (before damage apply) + // Note: it for recalculation damage or special reaction at damage + // for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also + virtual void DamageTaken(Unit * /*done_by*/, uint32 & /*damage*/) {} + + // Called when the creature is killed + virtual void JustDied(Unit *) {} + + // Called when the creature kills a unit + virtual void KilledUnit(Unit *) {} + + // Called when the creature summon successfully other creature + virtual void JustSummoned(Creature*) {} + virtual void IsSummonedBy(Unit * /*summoner*/) {} + + virtual void SummonedCreatureDespawn(Creature* /*unit*/) {} + + // Called when hit by a spell + virtual void SpellHit(Unit*, const SpellEntry*) {} + + // Called when spell hits a target + virtual void SpellHitTarget(Unit* /*target*/, const SpellEntry*) {} + + // Called to get trigger target for aura effect + virtual Unit * GetAuraEffectTriggerTarget(uint32 /*spellId*/, uint8 /*effIndex*/) {return NULL;} + + // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) + //virtual void AttackedBy(Unit* attacker); + virtual bool IsEscorted() { return false; } + + // Called when creature is spawned or respawned (for reseting variables) + virtual void JustRespawned() { Reset(); } + + // Called at waypoint reached or point movement finished + virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {} + + void OnCharmed(bool apply); + + //virtual void SpellClick(Player *player) {} + + // Called at reaching home after evade + virtual void JustReachedHome() {} + + void DoZoneInCombat(Creature* pUnit = NULL); + + // Called at text emote receive from player + virtual void ReceiveEmote(Player* /*pPlayer*/, uint32 /*text_emote*/) {} + + /// == Triggered Actions Requested ================== + + // Called when creature attack expected (if creature can and no have current victim) + // Note: for reaction at hostile action must be called AttackedBy function. + //virtual void AttackStart(Unit *) {} + + // Called at World update tick + //virtual void UpdateAI(const uint32 /*diff*/) {} + + /// == State checks ================================= + + // Is unit visible for MoveInLineOfSight + //virtual bool IsVisible(Unit *) const { return false; } + + // called when the corpse of this creature gets removed + virtual void CorpseRemoved(uint32 & /*respawnDelay*/) {} + + // Called when victim entered water and creature can not enter water + //virtual bool canReachByRangeAttack(Unit*) { return false; } + + /// == Fields ======================================= + + // Pointer to controlled by AI creature + //Creature* const me; + + virtual void PassengerBoarded(Unit * /*who*/, int8 /*seatId*/, bool /*apply*/) {} + + protected: + virtual void MoveInLineOfSight(Unit *); + + bool _EnterEvadeMode(); + + private: + bool m_MoveInLineOfSight_locked; +}; + +enum Permitions +{ + PERMIT_BASE_NO = -1, + PERMIT_BASE_IDLE = 1, + PERMIT_BASE_REACTIVE = 100, + PERMIT_BASE_PROACTIVE = 200, + PERMIT_BASE_FACTION_SPECIFIC = 400, + PERMIT_BASE_SPECIAL = 800 +}; + +#endif diff --git a/src/server/game/AI/CreatureAIFactory.h b/src/server/game/AI/CreatureAIFactory.h new file mode 100644 index 00000000000..6aa69eaaa29 --- /dev/null +++ b/src/server/game/AI/CreatureAIFactory.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef TRINITY_CREATUREAIFACTORY_H +#define TRINITY_CREATUREAIFACTORY_H + +//#include "Policies/Singleton.h" +#include "Dynamic/ObjectRegistry.h" +#include "Dynamic/FactoryHolder.h" + +struct SelectableAI : public FactoryHolder, public Permissible +{ + SelectableAI(const char *id) : FactoryHolder(id) {} +}; + +template +struct CreatureAIFactory : public SelectableAI +{ + CreatureAIFactory(const char *name) : SelectableAI(name) {} + + CreatureAI* Create(void *) const; + + int Permit(const Creature *c) const { return REAL_AI::Permissible(c); } +}; + +template +inline CreatureAI* +CreatureAIFactory::Create(void *data) const +{ + Creature* creature = reinterpret_cast(data); + return (new REAL_AI(creature)); +} + +typedef FactoryHolder CreatureAICreator; +typedef FactoryHolder::FactoryHolderRegistry CreatureAIRegistry; +typedef FactoryHolder::FactoryHolderRepository CreatureAIRepository; +#endif diff --git a/src/server/game/AI/CreatureAIImpl.h b/src/server/game/AI/CreatureAIImpl.h new file mode 100644 index 00000000000..d2f96ffcabc --- /dev/null +++ b/src/server/game/AI/CreatureAIImpl.h @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CREATUREAIIMPL_H +#define CREATUREAIIMPL_H + +#include "Common.h" +#include "Platform/Define.h" +#include "TemporarySummon.h" +#include "CreatureAI.h" +#include "SpellMgr.h" + +template +inline +const T& RAND(const T& v1, const T& v2) +{ + return (urand(0,1)) ? v1 : v2; +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3) +{ + switch (urand(0,2)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4) +{ + switch (urand(0,3)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) +{ + switch (urand(0,4)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6) +{ + switch (urand(0,5)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7) +{ + switch (urand(0,6)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8) +{ + switch (urand(0,7)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9) +{ + switch (urand(0,8)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10) +{ + switch (urand(0,9)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10, const T& v11) +{ + switch (urand(0,10)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + case 10: return v11; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10, const T& v11, const T& v12) +{ + switch (urand(0,11)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + case 10: return v11; + case 11: return v12; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10, const T& v11, const T& v12, const T& v13) +{ + switch (urand(0,12)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + case 10: return v11; + case 11: return v12; + case 12: return v13; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14) +{ + switch (urand(0,13)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + case 10: return v11; + case 11: return v12; + case 12: return v13; + case 13: return v14; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15) +{ + switch (urand(0,14)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + case 10: return v11; + case 11: return v12; + case 12: return v13; + case 13: return v14; + case 14: return v15; + } +} + +template +inline +const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, + const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15, const T& v16) +{ + switch (urand(0,15)) + { + default: + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v4; + case 4: return v5; + case 5: return v6; + case 6: return v7; + case 7: return v8; + case 8: return v9; + case 9: return v10; + case 10: return v11; + case 11: return v12; + case 12: return v13; + case 13: return v14; + case 14: return v15; + case 15: return v16; + } +} + +class EventMap : private std::map +{ + private: + uint32 m_time, m_phase; + public: + explicit EventMap() : m_phase(0), m_time(0) {} + + uint32 GetTimer() const { return m_time; } + + void Reset() { clear(); m_time = 0; m_phase = 0; } + + void Update(uint32 time) { m_time += time; } + + void SetPhase(uint32 phase) + { + if (phase && phase < 9) + m_phase = (1 << (phase + 24)); + } + + void ScheduleEvent(uint32 eventId, uint32 time, uint32 gcd = 0, uint32 phase = 0) + { + time += m_time; + if (gcd && gcd < 9) + eventId |= (1 << (gcd + 16)); + if (phase && phase < 9) + eventId |= (1 << (phase + 24)); + iterator itr = find(time); + while (itr != end()) + { + ++time; + itr = find(time); + } + insert(std::make_pair(time, eventId)); + } + + void RescheduleEvent(uint32 eventId, uint32 time, uint32 gcd = 0, uint32 phase = 0) + { + CancelEvent(eventId); + ScheduleEvent(eventId, time, gcd, phase); + } + + void RepeatEvent(uint32 time) + { + if (empty()) + return; + uint32 eventId = begin()->second; + erase(begin()); + time += m_time; + iterator itr = find(time); + while (itr != end()) + { + ++time; + itr = find(time); + } + insert(std::make_pair(time, eventId)); + } + + void PopEvent() + { + erase(begin()); + } + + uint32 ExecuteEvent() + { + while (!empty()) + { + if (begin()->first > m_time) + return 0; + else if (m_phase && (begin()->second & 0xFF000000) && !(begin()->second & m_phase)) + erase(begin()); + else + { + uint32 eventId = (begin()->second & 0x0000FFFF); + erase(begin()); + return eventId; + } + } + return 0; + } + + uint32 GetEvent() + { + while (!empty()) + { + if (begin()->first > m_time) + return 0; + else if (m_phase && (begin()->second & 0xFF000000) && !(begin()->second & m_phase)) + erase(begin()); + else + return (begin()->second & 0x0000FFFF); + } + return 0; + } + + // Delay all events + void DelayEvents(uint32 delay) + { + if (delay < m_time) + m_time -= delay; + else + m_time = 0; + } + + // Delay all events having the specified Global Cooldown. + void DelayEvents(uint32 delay, uint32 gcd) + { + uint32 nextTime = m_time + delay; + gcd = (1 << (gcd + 16)); + for (iterator itr = begin(); itr != end() && itr->first < nextTime;) + { + if (itr->second & gcd) + { + ScheduleEvent(itr->second, itr->first-m_time+delay); + erase(itr); + itr = begin(); + } + else + ++itr; + } + } + + void CancelEvent(uint32 eventId) + { + for (iterator itr = begin(); itr != end();) + { + if (eventId == (itr->second & 0x0000FFFF)) + { + erase(itr); + itr = begin(); + } + else + ++itr; + } + } + + void CancelEventsByGCD(uint32 gcd) + { + gcd = (1 << (gcd + 16)); + + for (iterator itr = begin(); itr != end();) + { + if (itr->second & gcd) + { + erase(itr); + itr = begin(); + } + else + ++itr; + } + } +}; + +enum AITarget +{ + AITARGET_SELF, + AITARGET_VICTIM, + AITARGET_ENEMY, + AITARGET_ALLY, + AITARGET_BUFF, + AITARGET_DEBUFF, +}; + +enum AICondition +{ + AICOND_AGGRO, + AICOND_COMBAT, + AICOND_DIE, +}; + +#define AI_DEFAULT_COOLDOWN 5000 + +struct AISpellInfoType +{ + AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT) + , cooldown(AI_DEFAULT_COOLDOWN), realCooldown(0), maxRange(0.0f){} + AITarget target; + AICondition condition; + uint32 cooldown; + uint32 realCooldown; + float maxRange; +}; + + AISpellInfoType * GetAISpellInfo(uint32 i); + +inline void CreatureAI::SetGazeOn(Unit *target) +{ + if (me->canAttack(target)) + { + AttackStart(target); + me->SetReactState(REACT_PASSIVE); + } +} + +inline bool CreatureAI::UpdateVictimWithGaze() +{ + if (!me->isInCombat()) + return false; + + if (me->HasReactState(REACT_PASSIVE)) + { + if (me->getVictim()) + return true; + else + me->SetReactState(REACT_AGGRESSIVE); + } + + if (Unit *victim = me->SelectVictim()) + AttackStart(victim); + return me->getVictim(); +} + +inline bool CreatureAI::UpdateCombatState() +{ + if (!me->isInCombat()) + return false; + + if (!me->HasReactState(REACT_PASSIVE)) + { + if (Unit *victim = me->SelectVictim()) + AttackStart(victim); + return me->getVictim(); + } + else if (me->getThreatManager().isThreatListEmpty()) + { + EnterEvadeMode(); + return false; + } + + return true; +} + +inline bool CreatureAI::UpdateVictim() +{ + if (!me->isInCombat()) + return false; + + if (!me->HasReactState(REACT_PASSIVE)) + { + if (Unit *victim = me->SelectVictim()) + AttackStart(victim); + return me->getVictim(); + } + else if (me->getThreatManager().isThreatListEmpty()) + { + EnterEvadeMode(); + return false; + } + + return true; +} + +/* +inline bool CreatureAI::UpdateVictim() +{ + if (!me->isInCombat()) + return false; + if (Unit *victim = me->SelectVictim()) + AttackStart(victim); + return me->getVictim(); +} +*/ + +inline bool CreatureAI::_EnterEvadeMode() +{ + if (!me->isAlive()) + return false; + + // sometimes bosses stuck in combat? + me->RemoveAllAuras(); + me->DeleteThreatList(); + me->CombatStop(true); + me->LoadCreaturesAddon(); + me->SetLootRecipient(NULL); + me->ResetPlayerDamageReq(); + + if (me->IsInEvadeMode()) + return false; + + return true; +} + +inline void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered) +{ + if (!victim || (me->hasUnitState(UNIT_STAT_CASTING) && !triggered)) + return; + + me->CastSpell(victim, spellId, triggered); +} + +inline void UnitAI::DoCastVictim(uint32 spellId, bool triggered) +{ + me->CastSpell(me->getVictim(), spellId, triggered); +} + +inline void UnitAI::DoCastAOE(uint32 spellId, bool triggered) +{ + if (!triggered && me->hasUnitState(UNIT_STAT_CASTING)) + return; + + me->CastSpell((Unit*)NULL, spellId, triggered); +} + +inline Creature *CreatureAI::DoSummon(uint32 uiEntry, const Position &pos, uint32 uiDespawntime, TempSummonType uiType) +{ + return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime); +} + +inline Creature *CreatureAI::DoSummon(uint32 uiEntry, WorldObject* obj, float fRadius, uint32 uiDespawntime, TempSummonType uiType) +{ + Position pos; + obj->GetRandomNearPosition(pos, fRadius); + return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime); +} + +inline Creature *CreatureAI::DoSummonFlyer(uint32 uiEntry, WorldObject *obj, float _fZ, float fRadius, uint32 uiDespawntime, TempSummonType uiType) +{ + Position pos; + obj->GetRandomNearPosition(pos, fRadius); + pos.m_positionZ += _fZ; + return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime); +} + +#endif + diff --git a/src/server/game/AI/CreatureAIRegistry.cpp b/src/server/game/AI/CreatureAIRegistry.cpp new file mode 100644 index 00000000000..9db30a0a5c4 --- /dev/null +++ b/src/server/game/AI/CreatureAIRegistry.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PassiveAI.h" +#include "ReactorAI.h" +#include "CombatAI.h" +#include "GuardAI.h" +#include "PetAI.h" +#include "TotemAI.h" +#include "CreatureEventAI.h" +#include "RandomMovementGenerator.h" +#include "MovementGeneratorImpl.h" +#include "CreatureAIRegistry.h" +#include "WaypointMovementGenerator.h" +#include "CreatureAIFactory.h" + +//#include "CreatureAIImpl.h" +namespace AIRegistry +{ + void Initialize() + { + (new CreatureAIFactory("NullCreatureAI"))->RegisterSelf(); + (new CreatureAIFactory("TriggerAI"))->RegisterSelf(); + (new CreatureAIFactory("AggressorAI"))->RegisterSelf(); + (new CreatureAIFactory("ReactorAI"))->RegisterSelf(); + (new CreatureAIFactory("PassiveAI"))->RegisterSelf(); + (new CreatureAIFactory("CritterAI"))->RegisterSelf(); + (new CreatureAIFactory("GuardAI"))->RegisterSelf(); + (new CreatureAIFactory("PetAI"))->RegisterSelf(); + (new CreatureAIFactory("TotemAI"))->RegisterSelf(); + (new CreatureAIFactory("CombatAI"))->RegisterSelf(); + (new CreatureAIFactory("ArchorAI"))->RegisterSelf(); + (new CreatureAIFactory("TurretAI"))->RegisterSelf(); + (new CreatureAIFactory("EventAI"))->RegisterSelf(); + (new CreatureAIFactory("AOEAI"))->RegisterSelf(); + (new CreatureAIFactory("VehicleAI"))->RegisterSelf(); + + (new MovementGeneratorFactory >(RANDOM_MOTION_TYPE))->RegisterSelf(); + (new MovementGeneratorFactory >(WAYPOINT_MOTION_TYPE))->RegisterSelf(); + } +} + diff --git a/src/server/game/AI/CreatureAIRegistry.h b/src/server/game/AI/CreatureAIRegistry.h new file mode 100644 index 00000000000..0d83d29cf45 --- /dev/null +++ b/src/server/game/AI/CreatureAIRegistry.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CREATUREAIREGISTRY_H +#define TRINITY_CREATUREAIREGISTRY_H + +namespace AIRegistry +{ + void Initialize(void); +} +#endif + diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp new file mode 100644 index 00000000000..d3fd5a8aed9 --- /dev/null +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Creature.h" +#include "CreatureAISelector.h" +#include "PassiveAI.h" +#include "Policies/SingletonImp.h" +#include "MovementGenerator.h" +#include "Pet.h" +#include "TemporarySummon.h" +#include "CreatureAIFactory.h" +#include "ScriptMgr.h" + +INSTANTIATE_SINGLETON_1(CreatureAIRegistry); +INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry); + +namespace FactorySelector +{ + CreatureAI* selectAI(Creature *creature) + { + const CreatureAICreator *ai_factory = NULL; + CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance()); + + if (creature->isPet()) + ai_factory = ai_registry.GetRegistryItem("PetAI"); + + //scriptname in db + if (!ai_factory) + if (CreatureAI* scriptedAI = sScriptMgr.GetAI(creature)) + return scriptedAI; + + // AIname in db + std::string ainame=creature->GetAIName(); + if (!ai_factory && !ainame.empty()) + ai_factory = ai_registry.GetRegistryItem(ainame.c_str()); + + // select by NPC flags + if (!ai_factory) + { + if (creature->IsVehicle()) + ai_factory = ai_registry.GetRegistryItem("VehicleAI"); + else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) + ai_factory = ai_registry.GetRegistryItem("PetAI"); + else if (creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) + ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); + else if (creature->isGuard()) + ai_factory = ai_registry.GetRegistryItem("GuardAI"); + else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) + ai_factory = ai_registry.GetRegistryItem("PetAI"); + else if (creature->isTotem()) + ai_factory = ai_registry.GetRegistryItem("TotemAI"); + else if (creature->isTrigger()) + { + if (creature->m_spells[0]) + ai_factory = ai_registry.GetRegistryItem("TriggerAI"); + else + ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); + } + else if (creature->GetCreatureType() == CREATURE_TYPE_CRITTER && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) + ai_factory = ai_registry.GetRegistryItem("CritterAI"); + } + + // select by permit check + if (!ai_factory) + { + int best_val = -1; + typedef CreatureAIRegistry::RegistryMapType RMT; + RMT const &l = ai_registry.GetRegisteredItems(); + for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter) + { + const CreatureAICreator *factory = iter->second; + const SelectableAI *p = dynamic_cast(factory); + assert(p != NULL); + int val = p->Permit(creature); + if (val > best_val) + { + best_val = val; + ai_factory = p; + } + } + } + + // select NullCreatureAI if not another cases + ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key(); + + DEBUG_LOG("Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str()); + return (ai_factory == NULL ? new NullCreatureAI(creature) : ai_factory->Create(creature)); + } + + MovementGenerator* selectMovementGenerator(Creature *creature) + { + MovementGeneratorRegistry &mv_registry(MovementGeneratorRepository::Instance()); + assert(creature->GetCreatureInfo() != NULL); + const MovementGeneratorCreator *mv_factory = mv_registry.GetRegistryItem(creature->GetDefaultMovementType()); + + /* if (mv_factory == NULL) + { + int best_val = -1; + std::vector l; + mv_registry.GetRegisteredItems(l); + for (std::vector::iterator iter = l.begin(); iter != l.end(); ++iter) + { + const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str()); + const SelectableMovement *p = dynamic_cast(factory); + assert(p != NULL); + int val = p->Permit(creature); + if (val > best_val) + { + best_val = val; + mv_factory = p; + } + } + }*/ + + return (mv_factory == NULL ? NULL : mv_factory->Create(creature)); + + } +} + diff --git a/src/server/game/AI/CreatureAISelector.h b/src/server/game/AI/CreatureAISelector.h new file mode 100644 index 00000000000..98eeee809d2 --- /dev/null +++ b/src/server/game/AI/CreatureAISelector.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CREATUREAISELECTOR_H +#define TRINITY_CREATUREAISELECTOR_H + +class CreatureAI; +class Creature; +class MovementGenerator; + +namespace FactorySelector +{ + CreatureAI* selectAI(Creature *); + MovementGenerator* selectMovementGenerator(Creature *); +} +#endif + diff --git a/src/server/game/AI/EventAI/CreatureEventAI.cpp b/src/server/game/AI/EventAI/CreatureEventAI.cpp new file mode 100644 index 00000000000..47c8e9e6ad8 --- /dev/null +++ b/src/server/game/AI/EventAI/CreatureEventAI.cpp @@ -0,0 +1,1384 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "CreatureEventAI.h" +#include "CreatureEventAIMgr.h" +#include "ObjectMgr.h" +#include "Spell.h" +#include "World.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GameEventMgr.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "InstanceData.h" +#include "SpellMgr.h" +#include "CreatureAIImpl.h" +#include "ConditionMgr.h" + +bool CreatureEventAIHolder::UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax) +{ + if (repeatMin == repeatMax) + Time = repeatMin; + else if (repeatMax > repeatMin) + Time = urand(repeatMin, repeatMax); + else + { + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", creature->GetEntry(), Event.event_id, Event.event_type); + Enabled = false; + return false; + } + + return true; +} + +int CreatureEventAI::Permissible(const Creature *creature) +{ + if (creature->GetAIName() == "EventAI") + return PERMIT_BASE_SPECIAL; + return PERMIT_BASE_NO; +} + +CreatureEventAI::CreatureEventAI(Creature *c) : CreatureAI(c) +{ + // Need make copy for filter unneeded steps and safe in case table reload + CreatureEventAI_Event_Map::const_iterator CreatureEvents = CreatureEAI_Mgr.GetCreatureEventAIMap().find(me->GetEntry()); + if (CreatureEvents != CreatureEAI_Mgr.GetCreatureEventAIMap().end()) + { + std::vector::const_iterator i; + for (i = (*CreatureEvents).second.begin(); i != (*CreatureEvents).second.end(); ++i) + { + + //Debug check + #ifndef TRINITY_DEBUG + if ((*i).event_flags & EFLAG_DEBUG_ONLY) + continue; + #endif + if (me->GetMap()->IsDungeon()) + { + if ((1 << (me->GetMap()->GetSpawnMode()+1)) & (*i).event_flags) + { + //event flagged for instance mode + CreatureEventAIList.push_back(CreatureEventAIHolder(*i)); + } + continue; + } + CreatureEventAIList.push_back(CreatureEventAIHolder(*i)); + } + //EventMap had events but they were not added because they must be for instance + if (CreatureEventAIList.empty()) + sLog.outError("CreatureEventAI: Creature %u has events but no events added to list because of instance flags.", me->GetEntry()); + } + else + sLog.outError("CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI.", me->GetEntry()); + + bEmptyList = CreatureEventAIList.empty(); + Phase = 0; + CombatMovementEnabled = true; + MeleeEnabled = true; + AttackDistance = 0.0f; + AttackAngle = 0.0f; + + InvinceabilityHpLevel = 0; + + //Handle Spawned Events + if (!bEmptyList) + { + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + if (SpawnedEventConditionsCheck((*i).Event)) + ProcessEvent(*i); + } +} + +bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker /*=NULL*/) +{ + if (!pHolder.Enabled || pHolder.Time) + return false; + + //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask) + if (pHolder.Event.event_inverse_phase_mask & (1 << Phase)) + return false; + + CreatureEventAI_Event const& event = pHolder.Event; + + //Check event conditions based on the event type, also reset events + switch (event.event_type) + { + case EVENT_T_TIMER: + if (!me->isInCombat()) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.timer.repeatMin,event.timer.repeatMax); + break; + case EVENT_T_TIMER_OOC: + if (me->isInCombat()) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.timer.repeatMin,event.timer.repeatMax); + break; + case EVENT_T_HP: + { + if (!me->isInCombat() || !me->GetMaxHealth()) + return false; + + uint32 perc = (me->GetHealth()*100) / me->GetMaxHealth(); + + if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); + break; + } + case EVENT_T_MANA: + { + if (!me->isInCombat() || !me->GetMaxPower(POWER_MANA)) + return false; + + uint32 perc = (me->GetPower(POWER_MANA)*100) / me->GetMaxPower(POWER_MANA); + + if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); + break; + } + case EVENT_T_AGGRO: + break; + case EVENT_T_KILL: + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.kill.repeatMin,event.kill.repeatMax); + break; + case EVENT_T_DEATH: + case EVENT_T_EVADE: + break; + case EVENT_T_SPELLHIT: + //Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.spell_hit.repeatMin,event.spell_hit.repeatMax); + break; + case EVENT_T_RANGE: + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.range.repeatMin,event.range.repeatMax); + break; + case EVENT_T_OOC_LOS: + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.ooc_los.repeatMin,event.ooc_los.repeatMax); + break; + case EVENT_T_RESET: + case EVENT_T_SPAWNED: + break; + case EVENT_T_TARGET_HP: + { + if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxHealth()) + return false; + + uint32 perc = (me->getVictim()->GetHealth()*100) / me->getVictim()->GetMaxHealth(); + + if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); + break; + } + case EVENT_T_TARGET_CASTING: + if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->IsNonMeleeSpellCasted(false, false, true)) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.target_casting.repeatMin,event.target_casting.repeatMax); + break; + case EVENT_T_FRIENDLY_HP: + { + if (!me->isInCombat()) + return false; + + Unit* pUnit = DoSelectLowestHpFriendly(event.friendly_hp.radius, event.friendly_hp.hpDeficit); + if (!pUnit) + return false; + + pActionInvoker = pUnit; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.friendly_hp.repeatMin,event.friendly_hp.repeatMax); + break; + } + case EVENT_T_FRIENDLY_IS_CC: + { + if (!me->isInCombat()) + return false; + + std::list pList; + DoFindFriendlyCC(pList, event.friendly_is_cc.radius); + + //List is empty + if (pList.empty()) + return false; + + //We don't really care about the whole list, just return first available + pActionInvoker = *(pList.begin()); + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.friendly_is_cc.repeatMin,event.friendly_is_cc.repeatMax); + break; + } + case EVENT_T_FRIENDLY_MISSING_BUFF: + { + std::list pList; + DoFindFriendlyMissingBuff(pList, event.friendly_buff.radius, event.friendly_buff.spellId); + + //List is empty + if (pList.empty()) + return false; + + //We don't really care about the whole list, just return first available + pActionInvoker = *(pList.begin()); + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.friendly_buff.repeatMin,event.friendly_buff.repeatMax); + break; + } + case EVENT_T_SUMMONED_UNIT: + { + //Prevent event from occuring on no unit or non creatures + if (!pActionInvoker || pActionInvoker->GetTypeId() != TYPEID_UNIT) + return false; + + //Creature id doesn't match up + if (pActionInvoker->ToCreature()->GetEntry() != event.summon_unit.creatureId) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.summon_unit.repeatMin,event.summon_unit.repeatMax); + break; + } + case EVENT_T_TARGET_MANA: + { + if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxPower(POWER_MANA)) + return false; + + uint32 perc = (me->getVictim()->GetPower(POWER_MANA)*100) / me->getVictim()->GetMaxPower(POWER_MANA); + + if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); + break; + } + case EVENT_T_REACHED_HOME: + case EVENT_T_RECEIVE_EMOTE: + break; + case EVENT_T_BUFFED: + { + //Note: checked only aura for effect 0, if need check aura for effect 1/2 then + // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) + Aura const * aura = me->GetAura(event.buffed.spellId); + if (!aura || aura->GetStackAmount() < event.buffed.amount) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.buffed.repeatMin,event.buffed.repeatMax); + break; + } + case EVENT_T_TARGET_BUFFED: + { + //Prevent event from occuring on no unit + if (!pActionInvoker) + return false; + + //Note: checked only aura for effect 0, if need check aura for effect 1/2 then + // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) + Aura const * aura = pActionInvoker->GetAura(event.buffed.spellId); + if (!aura || aura->GetStackAmount() < event.buffed.amount) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(me,event.buffed.repeatMin,event.buffed.repeatMax); + break; + } + default: + sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", me->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + break; + } + + //Disable non-repeatable events + if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) + pHolder.Enabled = false; + + //Store random here so that all random actions match up + uint32 rnd = rand(); + + //Return if chance for event is not met + if (pHolder.Event.event_chance <= rnd % 100) + return false; + + //Process actions + for (uint8 j = 0; j < MAX_ACTIONS; ++j) + ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker); + + return true; +} + +void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker) +{ + switch (action.type) + { + case ACTION_T_TEXT: + { + if (!action.text.TextId1) + return; + + int32 temp = 0; + + if (action.text.TextId2 && action.text.TextId3) + temp = RAND(action.text.TextId1,action.text.TextId2,action.text.TextId3); + else if (action.text.TextId2 && urand(0,1)) + temp = action.text.TextId2; + else + temp = action.text.TextId1; + + if (temp) + { + Unit* target = NULL; + + if (pActionInvoker) + { + if (pActionInvoker->GetTypeId() == TYPEID_PLAYER) + target = pActionInvoker; + else if (Unit* owner = pActionInvoker->GetOwner()) + { + if (owner->GetTypeId() == TYPEID_PLAYER) + target = owner; + } + } + else + { + target = me->getVictim(); + if (target && target->GetTypeId() != TYPEID_PLAYER) + if (Unit* owner = target->GetOwner()) + if (owner->GetTypeId() == TYPEID_PLAYER) + target = owner; + } + + DoScriptText(temp, me, target); + } + break; + } + case ACTION_T_SET_FACTION: + { + if (action.set_faction.factionId) + me->setFaction(action.set_faction.factionId); + else + { + if (CreatureInfo const* ci = GetCreatureTemplateStore(me->GetEntry())) + { + //if no id provided, assume reset and then use default + if (me->getFaction() != ci->faction_A) + me->setFaction(ci->faction_A); + } + } + break; + } + case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: + { + if (action.morph.creatureId || action.morph.modelId) + { + //set model based on entry from creature_template + if (action.morph.creatureId) + { + if (CreatureInfo const* ci = GetCreatureTemplateStore(action.morph.creatureId)) + { + uint32 display_id = objmgr.ChooseDisplayId(0,ci); + me->SetDisplayId(display_id); + } + } + //if no param1, then use value from param2 (modelId) + else + me->SetDisplayId(action.morph.modelId); + } + else + me->DeMorph(); + break; + } + case ACTION_T_SOUND: + me->PlayDirectSound(action.sound.soundId); + break; + case ACTION_T_EMOTE: + me->HandleEmoteCommand(action.emote.emoteId); + break; + case ACTION_T_RANDOM_SOUND: + { + int32 temp = GetRandActionParam(rnd, action.random_sound.soundId1, action.random_sound.soundId2, action.random_sound.soundId3); + if (temp >= 0) + me->PlayDirectSound(temp); + break; + } + case ACTION_T_RANDOM_EMOTE: + { + int32 temp = GetRandActionParam(rnd, action.random_emote.emoteId1, action.random_emote.emoteId2, action.random_emote.emoteId3); + if (temp >= 0) + me->HandleEmoteCommand(temp); + break; + } + case ACTION_T_CAST: + { + Unit* target = GetTargetByType(action.cast.target, pActionInvoker); + Unit* caster = me; + + if (!target) + return; + + if (action.cast.castFlags & CAST_FORCE_TARGET_SELF) + caster = target; + + //Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered + bool canCast = !caster->IsNonMeleeSpellCasted(false) || (action.cast.castFlags & (CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS)); + + // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them + if (action.cast.castFlags & CAST_AURA_NOT_PRESENT) + { + if (target->HasAura(action.cast.spellId)) + return; + } + + if (canCast) + { + const SpellEntry* tSpell = GetSpellStore()->LookupEntry(action.cast.spellId); + + //Verify that spell exists + if (tSpell) + { + //Check if cannot cast spell + if (!(action.cast.castFlags & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) && + !CanCast(target, tSpell, (action.cast.castFlags & CAST_TRIGGERED))) + { + //Melee current victim if flag not set + if (!(action.cast.castFlags & CAST_NO_MELEE_IF_OOM)) + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + AttackDistance = 0.0f; + AttackAngle = 0.0f; + + me->GetMotionMaster()->MoveChase(me->getVictim(), AttackDistance, AttackAngle); + } + } + + } + else + { + //Interrupt any previous spell + if (caster->IsNonMeleeSpellCasted(false) && action.cast.castFlags & CAST_INTURRUPT_PREVIOUS) + caster->InterruptNonMeleeSpells(false); + + caster->CastSpell(target, action.cast.spellId, (action.cast.castFlags & CAST_TRIGGERED)); + } + + } + else + sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, me->GetEntry(), action.cast.spellId); + } + break; + } + case ACTION_T_SUMMON: + { + Unit* target = GetTargetByType(action.summon.target, pActionInvoker); + + Creature* pCreature = NULL; + + if (action.summon.duration) + pCreature = me->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, action.summon.duration); + else + pCreature = me->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); + + if (!pCreature) + sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", action.summon.creatureId, EventId, me->GetEntry()); + else if (action.summon.target != TARGET_T_SELF && target) + pCreature->AI()->AttackStart(target); + break; + } + case ACTION_T_THREAT_SINGLE_PCT: + if (Unit* target = GetTargetByType(action.threat_single_pct.target, pActionInvoker)) + me->getThreatManager().modifyThreatPercent(target, action.threat_single_pct.percent); + break; + case ACTION_T_THREAT_ALL_PCT: + { + std::list& threatList = me->getThreatManager().getThreatList(); + for (std::list::iterator i = threatList.begin(); i != threatList.end(); ++i) + if (Unit* Temp = Unit::GetUnit(*me,(*i)->getUnitGuid())) + me->getThreatManager().modifyThreatPercent(Temp, action.threat_all_pct.percent); + break; + } + case ACTION_T_QUEST_EVENT: + if (Unit* target = GetTargetByType(action.quest_event.target, pActionInvoker)) + if (target->GetTypeId() == TYPEID_PLAYER) + target->ToPlayer()->AreaExploredOrEventHappens(action.quest_event.questId); + break; + case ACTION_T_CAST_EVENT: + if (Unit* target = GetTargetByType(action.cast_event.target, pActionInvoker)) + if (target->GetTypeId() == TYPEID_PLAYER) + target->ToPlayer()->CastedCreatureOrGO(action.cast_event.creatureId, me->GetGUID(), action.cast_event.spellId); + break; + case ACTION_T_SET_UNIT_FIELD: + { + Unit* target = GetTargetByType(action.set_unit_field.target, pActionInvoker); + + // not allow modify important for integrity object fields + if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END) + return; + + if (target) + target->SetUInt32Value(action.set_unit_field.field, action.set_unit_field.value); + + break; + } + case ACTION_T_SET_UNIT_FLAG: + if (Unit* target = GetTargetByType(action.unit_flag.target, pActionInvoker)) + target->SetFlag(UNIT_FIELD_FLAGS, action.unit_flag.value); + break; + case ACTION_T_REMOVE_UNIT_FLAG: + if (Unit* target = GetTargetByType(action.unit_flag.target, pActionInvoker)) + target->RemoveFlag(UNIT_FIELD_FLAGS, action.unit_flag.value); + break; + case ACTION_T_AUTO_ATTACK: + MeleeEnabled = action.auto_attack.state != 0; + break; + case ACTION_T_COMBAT_MOVEMENT: + // ignore no affect case + if (CombatMovementEnabled == (action.combat_movement.state != 0)) + return; + + CombatMovementEnabled = action.combat_movement.state != 0; + + //Allow movement (create new targeted movement gen only if idle) + if (CombatMovementEnabled) + { + Unit* victim = me->getVictim(); + if (me->isInCombat() && victim) + { + if (action.combat_movement.melee) + { + me->addUnitState(UNIT_STAT_MELEE_ATTACKING); + me->SendMeleeAttackStart(victim); + } + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) + me->GetMotionMaster()->MoveChase(victim, AttackDistance, AttackAngle); // Targeted movement generator will start melee automatically, no need to send it explicitly + } + } + else + { + if (me->isInCombat()) + { + Unit* victim = me->getVictim(); + if (action.combat_movement.melee && victim) + { + me->clearUnitState(UNIT_STAT_MELEE_ATTACKING); + me->SendMeleeAttackStop(victim); + } + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + me->GetMotionMaster()->MoveIdle(); + } + } + break; + case ACTION_T_SET_PHASE: + Phase = action.set_phase.phase; + break; + case ACTION_T_INC_PHASE: + { + int32 new_phase = int32(Phase)+action.set_inc_phase.step; + if (new_phase < 0) + { + sLog.outErrorDb("CreatureEventAI: Event %d decrease Phase under 0. CreatureEntry = %d", EventId, me->GetEntry()); + Phase = 0; + } + else if (new_phase >= MAX_PHASE) + { + sLog.outErrorDb("CreatureEventAI: Event %d incremented Phase above %u. Phase mask cannot be used with phases past %u. CreatureEntry = %d", EventId, MAX_PHASE-1, MAX_PHASE-1, me->GetEntry()); + Phase = MAX_PHASE-1; + } + else + Phase = new_phase; + + break; + } + case ACTION_T_EVADE: + EnterEvadeMode(); + break; + case ACTION_T_FLEE_FOR_ASSIST: + me->DoFleeToGetAssistance(); + break; + case ACTION_T_QUEST_EVENT_ALL: + if (pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER) + { + if (Unit* Temp = Unit::GetUnit(*me,pActionInvoker->GetGUID())) + if (Temp->GetTypeId() == TYPEID_PLAYER) + Temp->ToPlayer()->GroupEventHappens(action.quest_event_all.questId,me); + } + break; + case ACTION_T_CAST_EVENT_ALL: + { + std::list& threatList = me->getThreatManager().getThreatList(); + for (std::list::iterator i = threatList.begin(); i != threatList.end(); ++i) + if (Unit* Temp = Unit::GetUnit(*me,(*i)->getUnitGuid())) + if (Temp->GetTypeId() == TYPEID_PLAYER) + Temp->ToPlayer()->CastedCreatureOrGO(action.cast_event_all.creatureId, me->GetGUID(), action.cast_event_all.spellId); + break; + } + case ACTION_T_REMOVEAURASFROMSPELL: + if (Unit* target = GetTargetByType(action.remove_aura.target, pActionInvoker)) + target->RemoveAurasDueToSpell(action.remove_aura.spellId); + break; + case ACTION_T_RANGED_MOVEMENT: + AttackDistance = (float)action.ranged_movement.distance; + AttackAngle = action.ranged_movement.angle/180.0f*M_PI; + + if (CombatMovementEnabled) + { + me->GetMotionMaster()->MoveChase(me->getVictim(), AttackDistance, AttackAngle); + } + break; + case ACTION_T_RANDOM_PHASE: + Phase = GetRandActionParam(rnd, action.random_phase.phase1, action.random_phase.phase2, action.random_phase.phase3); + break; + case ACTION_T_RANDOM_PHASE_RANGE: + if (action.random_phase_range.phaseMin <= action.random_phase_range.phaseMax) + Phase = urand(action.random_phase_range.phaseMin, action.random_phase_range.phaseMax); + else + sLog.outErrorDb("CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 < Param1. Event = %d. CreatureEntry = %d", EventId, me->GetEntry()); + break; + case ACTION_T_SUMMON_ID: + { + Unit* target = GetTargetByType(action.summon_id.target, pActionInvoker); + + CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(action.summon_id.spawnId); + if (i == CreatureEAI_Mgr.GetCreatureEventAISummonMap().end()) + { + sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", action.summon_id.creatureId, action.summon_id.spawnId, EventId, me->GetEntry()); + return; + } + + Creature* pCreature = NULL; + if ((*i).second.SpawnTimeSecs) + pCreature = me->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs); + else + pCreature = me->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); + + if (!pCreature) + sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, me->GetEntry()); + else if (action.summon_id.target != TARGET_T_SELF && target) + pCreature->AI()->AttackStart(target); + + break; + } + case ACTION_T_KILLED_MONSTER: + //first attempt player who tapped creature + if (Player* pPlayer = me->GetLootRecipient()) + pPlayer->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, me); + else + { + //if not available, use pActionInvoker + if (Unit* pTarget = GetTargetByType(action.killed_monster.target, pActionInvoker)) + if (Player* pPlayer2 = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself()) + pPlayer2->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, me); + } + break; + case ACTION_T_SET_INST_DATA: + { + InstanceData* pInst = (InstanceData*)me->GetInstanceData(); + if (!pInst) + { + sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d", EventId, me->GetEntry()); + return; + } + + pInst->SetData(action.set_inst_data.field, action.set_inst_data.value); + break; + } + case ACTION_T_SET_INST_DATA64: + { + Unit* target = GetTargetByType(action.set_inst_data64.target, pActionInvoker); + if (!target) + { + sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, me->GetEntry()); + return; + } + + InstanceData* pInst = (InstanceData*)me->GetInstanceData(); + if (!pInst) + { + sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, me->GetEntry()); + return; + } + + pInst->SetData64(action.set_inst_data64.field, target->GetGUID()); + break; + } + case ACTION_T_UPDATE_TEMPLATE: + if (me->GetEntry() == action.update_template.creatureId) + { + + sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, me->GetEntry()); + return; + } + + me->UpdateEntry(action.update_template.creatureId, action.update_template.team ? HORDE : ALLIANCE); + break; + case ACTION_T_DIE: + if (me->isDead()) + { + + sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, me->GetEntry()); + return; + } + me->Kill(me); + break; + case ACTION_T_ZONE_COMBAT_PULSE: + { + me->SetInCombatWithZone(); + break; + } + case ACTION_T_CALL_FOR_HELP: + { + me->CallForHelp(action.call_for_help.radius); + break; + } + break; + + // TRINITY ONLY + case ACTION_T_MOVE_RANDOM_POINT: //dosen't work in combat + { + float x,y,z; + me->GetClosePoint(x, y, z, me->GetObjectSize() / 3, action.raw.param1); + me->GetMotionMaster()->MovePoint(0,x,y,z); + break; + } + case ACTION_T_SET_STAND_STATE: + me->SetStandState(UnitStandStateType(action.raw.param1)); + break; + case ACTION_T_SET_PHASE_MASK: + me->SetPhaseMask(action.raw.param1, true); + break; + case ACTION_T_SET_VISIBILITY: + me->SetVisibility(UnitVisibility(action.raw.param1)); + break; + case ACTION_T_SET_ACTIVE: + me->setActive(action.raw.param1 ? true : false); + break; + case ACTION_T_SET_AGGRESSIVE: + me->SetReactState(ReactStates(action.raw.param1)); + break; + case ACTION_T_ATTACK_START_PULSE: + AttackStart(me->SelectNearestTarget((float)action.raw.param1)); + break; + case ACTION_T_SUMMON_GO: + { + GameObject* pObject = NULL; + + float x,y,z; + me->GetPosition(x,y,z); + pObject = me->SummonGameObject(action.raw.param1, x, y, z, 0, 0, 0, 0, 0, action.raw.param2); + if (!pObject) + { + sLog.outErrorDb("TSCR: EventAI failed to spawn object %u. Spawn event %d is on creature %d", action.raw.param1, EventId, me->GetEntry()); + } + break; + } + + case ACTION_T_SET_SHEATH: + { + me->SetSheath(SheathState(action.set_sheath.sheath)); + break; + } + case ACTION_T_FORCE_DESPAWN: + { + me->ForcedDespawn(action.forced_despawn.msDelay); + break; + } + case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: + { + if (action.invincibility_hp_level.is_percent) + InvinceabilityHpLevel = me->GetMaxHealth()*action.invincibility_hp_level.hp_level/100; + else + InvinceabilityHpLevel = action.invincibility_hp_level.hp_level; + break; + } + } +} + +void CreatureEventAI::JustRespawned() +{ + Reset(); + + if (bEmptyList) + return; + + //Handle Spawned Events + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + if (SpawnedEventConditionsCheck((*i).Event)) + ProcessEvent(*i); +} + +void CreatureEventAI::Reset() +{ + EventUpdateTime = EVENT_UPDATE_TIME; + EventDiff = 0; + + if (bEmptyList) + return; + + + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_RESET) + ProcessEvent(*i); + } + + + //Reset all events to enabled + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + CreatureEventAI_Event const& event = (*i).Event; + switch (event.event_type) + { + //Reset all out of combat timers + case EVENT_T_TIMER_OOC: + { + if ((*i).UpdateRepeatTimer(me,event.timer.initialMin,event.timer.initialMax)) + (*i).Enabled = true; + break; + } + //default: + //TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void EnterCombat() + //(*i).Enabled = true; + //(*i).Time = 0; + //break; + } + } +} + +void CreatureEventAI::JustReachedHome() +{ + me->LoadCreaturesAddon(); + + if (!bEmptyList) + { + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_REACHED_HOME) + ProcessEvent(*i); + } + } + + Reset(); +} + +void CreatureEventAI::EnterEvadeMode() +{ + CreatureAI::EnterEvadeMode(); + + if (bEmptyList) + return; + + //Handle Evade events + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_EVADE) + ProcessEvent(*i); + } +} + +void CreatureEventAI::JustDied(Unit* killer) +{ + Reset(); + + if (bEmptyList) + return; + + //Handle Evade events + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_DEATH) + ProcessEvent(*i, killer); + } + + // reset phase after any death state events + Phase = 0; +} + +void CreatureEventAI::KilledUnit(Unit* victim) +{ + if (bEmptyList || victim->GetTypeId() != TYPEID_PLAYER) + return; + + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_KILL) + ProcessEvent(*i, victim); + } +} + +void CreatureEventAI::JustSummoned(Creature* pUnit) +{ + if (bEmptyList || !pUnit) + return; + + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT) + ProcessEvent(*i, pUnit); + } +} + +void CreatureEventAI::EnterCombat(Unit *enemy) +{ + //Check for on combat start events + if (!bEmptyList) + { + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + CreatureEventAI_Event const& event = (*i).Event; + switch (event.event_type) + { + case EVENT_T_AGGRO: + (*i).Enabled = true; + ProcessEvent(*i, enemy); + break; + //Reset all in combat timers + case EVENT_T_TIMER: + if ((*i).UpdateRepeatTimer(me,event.timer.initialMin,event.timer.initialMax)) + (*i).Enabled = true; + break; + //All normal events need to be re-enabled and their time set to 0 + default: + (*i).Enabled = true; + (*i).Time = 0; + break; + } + } + } + + EventUpdateTime = EVENT_UPDATE_TIME; + EventDiff = 0; +} + +void CreatureEventAI::AttackStart(Unit *who) +{ + if (!who) + return; + + if (me->Attack(who, MeleeEnabled)) + { + if (CombatMovementEnabled) + { + me->GetMotionMaster()->MoveChase(who, AttackDistance, AttackAngle); + } + else + { + me->GetMotionMaster()->MoveIdle(); + } + } +} + +void CreatureEventAI::MoveInLineOfSight(Unit *who) +{ + if (me->getVictim()) + return; + + //Check for OOC LOS Event + if (!bEmptyList) + { + for (std::list::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr) + { + if ((*itr).Event.event_type == EVENT_T_OOC_LOS) + { + //can trigger if closer than fMaxAllowedRange + float fMaxAllowedRange = (*itr).Event.ooc_los.maxRange; + + //if range is ok and we are actually in LOS + if (me->IsWithinDistInMap(who, fMaxAllowedRange) && me->IsWithinLOSInMap(who)) + { + //if friendly event&&who is not hostile OR hostile event&&who is hostile + if (((*itr).Event.ooc_los.noHostile && !me->IsHostileTo(who)) || + ((!(*itr).Event.ooc_los.noHostile) && me->IsHostileTo(who))) + ProcessEvent(*itr, who); + } + } + } + } + + CreatureAI::MoveInLineOfSight(who); +} + +void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell) +{ + + if (bEmptyList) + return; + + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + if ((*i).Event.event_type == EVENT_T_SPELLHIT) + //If spell id matches (or no spell id) & if spell school matches (or no spell school) + if (!(*i).Event.spell_hit.spellId || pSpell->Id == (*i).Event.spell_hit.spellId) + if (pSpell->SchoolMask & (*i).Event.spell_hit.schoolMask) + ProcessEvent(*i, pUnit); +} + +void CreatureEventAI::UpdateAI(const uint32 diff) +{ + //Check if we are in combat (also updates calls threat update code) + bool Combat = UpdateVictim(); + + if (!bEmptyList) + { + //Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events + if (EventUpdateTime <= diff) + { + EventDiff += diff; + + //Check for time based events + for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) + { + //Decrement Timers + if ((*i).Time) + { + if (EventDiff <= (*i).Time) + { + //Do not decrement timers if event cannot trigger in this phase + if (!((*i).Event.event_inverse_phase_mask & (1 << Phase))) + (*i).Time -= EventDiff; + + //Skip processing of events that have time remaining + continue; + } + else (*i).Time = 0; + } + + //Events that are updated every EVENT_UPDATE_TIME + switch ((*i).Event.event_type) + { + case EVENT_T_TIMER_OOC: + ProcessEvent(*i); + break; + case EVENT_T_TIMER: + case EVENT_T_MANA: + case EVENT_T_HP: + case EVENT_T_TARGET_HP: + case EVENT_T_TARGET_CASTING: + case EVENT_T_FRIENDLY_HP: + if (me->getVictim()) + ProcessEvent(*i); + break; + case EVENT_T_RANGE: + if (me->getVictim()) + if (me->IsInMap(me->getVictim())) + if (me->IsInRange(me->getVictim(),(float)(*i).Event.range.minDist,(float)(*i).Event.range.maxDist)) + ProcessEvent(*i); + break; + } + } + + EventDiff = 0; + EventUpdateTime = EVENT_UPDATE_TIME; + } + else + { + EventDiff += diff; + EventUpdateTime -= diff; + } + } + + //Melee Auto-Attack + if (Combat && MeleeEnabled) + DoMeleeAttackIfReady(); +} + +inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3) +{ + switch (rnd % 3) + { + case 0: return param1; + case 1: return param2; + case 2: return param3; + } + return 0; +} + +inline int32 CreatureEventAI::GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3) +{ + switch (rnd % 3) + { + case 0: return param1; + case 1: return param2; + case 2: return param3; + } + return 0; +} + +inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker) +{ + switch (Target) + { + case TARGET_T_SELF: + return me; + case TARGET_T_HOSTILE: + return me->getVictim(); + case TARGET_T_HOSTILE_SECOND_AGGRO: + return SelectTarget(SELECT_TARGET_TOPAGGRO,1); + case TARGET_T_HOSTILE_LAST_AGGRO: + return SelectTarget(SELECT_TARGET_BOTTOMAGGRO,0); + case TARGET_T_HOSTILE_RANDOM: + return SelectTarget(SELECT_TARGET_RANDOM,0); + case TARGET_T_HOSTILE_RANDOM_NOT_TOP: + return SelectTarget(SELECT_TARGET_RANDOM,1); + case TARGET_T_ACTION_INVOKER: + return pActionInvoker; + default: + return NULL; + }; +} + +Unit* CreatureEventAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff) +{ + CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Unit* pUnit = NULL; + + Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff); + Trinity::UnitLastSearcher searcher(me, pUnit, u_check); + + /* + typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes; + This means that if we only search grid then we cannot possibly return pets or players so this is safe + */ + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, range); + return pUnit; +} + +void CreatureEventAI::DoFindFriendlyCC(std::list& _list, float range) +{ + CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::FriendlyCCedInRange u_check(me, range); + Trinity::CreatureListSearcher searcher(me, _list, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + + cell.Visit(p, grid_creature_searcher, *me->GetMap()); +} + +void CreatureEventAI::DoFindFriendlyMissingBuff(std::list& _list, float range, uint32 spellid) +{ + CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::FriendlyMissingBuffInRange u_check(me, range, spellid); + Trinity::CreatureListSearcher searcher(me, _list, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + + cell.Visit(p, grid_creature_searcher, *me->GetMap()); +} + +//********************************* +//*** Functions used globally *** + +void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target) +{ + if (!pSource) + { + sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i, invalid Source pointer.",textEntry); + return; + } + + if (textEntry >= 0) + { + sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry); + return; + } + + CreatureEventAI_TextMap::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAITextMap().find(textEntry); + + if (i == CreatureEAI_Mgr.GetCreatureEventAITextMap().end()) + { + sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry); + return; + } + + sLog.outDebug("CreatureEventAI: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u",textEntry,(*i).second.SoundId,(*i).second.Type,(*i).second.Language,(*i).second.Emote); + + if ((*i).second.SoundId) + { + if (GetSoundEntriesStore()->LookupEntry((*i).second.SoundId)) + pSource->PlayDirectSound((*i).second.SoundId); + else + sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u.",textEntry,(*i).second.SoundId); + } + + if ((*i).second.Emote) + { + if (pSource->GetTypeId() == TYPEID_UNIT || pSource->GetTypeId() == TYPEID_PLAYER) + { + ((Unit*)pSource)->HandleEmoteCommand((*i).second.Emote); + } + else + sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u).",textEntry,pSource->GetTypeId()); + } + + switch((*i).second.Type) + { + case CHAT_TYPE_SAY: + pSource->MonsterSay(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); + break; + case CHAT_TYPE_YELL: + pSource->MonsterYell(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); + break; + case CHAT_TYPE_TEXT_EMOTE: + pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0); + break; + case CHAT_TYPE_BOSS_EMOTE: + pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0, true); + break; + case CHAT_TYPE_WHISPER: + { + if (target && target->GetTypeId() == TYPEID_PLAYER) + pSource->MonsterWhisper(textEntry, target->GetGUID()); + else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry); + }break; + case CHAT_TYPE_BOSS_WHISPER: + { + if (target && target->GetTypeId() == TYPEID_PLAYER) + pSource->MonsterWhisper(textEntry, target->GetGUID(), true); + else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry); + }break; + case CHAT_TYPE_ZONE_YELL: + pSource->MonsterYellToZone(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); + break; + } +} + +bool CreatureEventAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered) +{ + //No target so we can't cast + if (!Target || !Spell) + return false; + + //Silenced so we can't cast + if (!Triggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return false; + + //Check for power + if (!Triggered && me->GetPower((Powers)Spell->powerType) < CalculatePowerCost(Spell, me, GetSpellSchoolMask(Spell))) + return false; + + SpellRangeEntry const *TempRange = NULL; + + TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex); + + //Spell has invalid range store so we can't use it + if (!TempRange) + return false; + + //Unit is out of range of this spell + if (!me->IsInRange(Target,TempRange->minRangeHostile,TempRange->maxRangeHostile)) + return false; + + return true; +} + +void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote) +{ + if (bEmptyList) + return; + + for (std::list::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr) + { + if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE) + { + if ((*itr).Event.receive_emote.emoteId != text_emote) + return; + + Condition* cond = new Condition(); + cond->mConditionType = ConditionType((*itr).Event.receive_emote.condition); + cond->mConditionValue1 = (*itr).Event.receive_emote.conditionValue1; + cond->mConditionValue2 = (*itr).Event.receive_emote.conditionValue2; + + if (cond->Meets(pPlayer)) + { + sLog.outDebug("CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing"); + ProcessEvent(*itr, pPlayer); + } + } + } +} + +void CreatureEventAI::DamageTaken(Unit* /*done_by*/, uint32& damage) +{ + if (InvinceabilityHpLevel > 0 && me->GetHealth() < InvinceabilityHpLevel+damage) + { + if (me->GetHealth() <= InvinceabilityHpLevel) + damage = 0; + else + damage = me->GetHealth() - InvinceabilityHpLevel; + } +} + +bool CreatureEventAI::SpawnedEventConditionsCheck(CreatureEventAI_Event const& event) +{ + if (event.event_type != EVENT_T_SPAWNED) + return false; + + switch (event.spawned.condition) + { + case SPAWNED_EVENT_ALWAY: + // always + return true; + case SPAWNED_EVENT_MAP: + // map ID check + return me->GetMapId() == event.spawned.conditionValue1; + case SPAWNED_EVENT_ZONE: + { + // zone ID check + uint32 zone, area; + me->GetZoneAndAreaId(zone,area); + return zone == event.spawned.conditionValue1 || area == event.spawned.conditionValue1; + } + default: + break; + } + + return false; +} diff --git a/src/server/game/AI/EventAI/CreatureEventAI.h b/src/server/game/AI/EventAI/CreatureEventAI.h new file mode 100644 index 00000000000..2fc5de46089 --- /dev/null +++ b/src/server/game/AI/EventAI/CreatureEventAI.h @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CREATURE_EAI_H +#define TRINITY_CREATURE_EAI_H + +#include "Common.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "Unit.h" + +class Player; +class WorldObject; + +#define EVENT_UPDATE_TIME 500 +#define MAX_ACTIONS 3 +#define MAX_PHASE 32 + +enum EventAI_Type +{ + EVENT_T_TIMER = 0, // InitialMin, InitialMax, RepeatMin, RepeatMax + EVENT_T_TIMER_OOC = 1, // InitialMin, InitialMax, RepeatMin, RepeatMax + EVENT_T_HP = 2, // HPMax%, HPMin%, RepeatMin, RepeatMax + EVENT_T_MANA = 3, // ManaMax%,ManaMin% RepeatMin, RepeatMax + EVENT_T_AGGRO = 4, // NONE + EVENT_T_KILL = 5, // RepeatMin, RepeatMax + EVENT_T_DEATH = 6, // NONE + EVENT_T_EVADE = 7, // NONE + EVENT_T_SPELLHIT = 8, // SpellID, School, RepeatMin, RepeatMax + EVENT_T_RANGE = 9, // MinDist, MaxDist, RepeatMin, RepeatMax + EVENT_T_OOC_LOS = 10, // NoHostile, MaxRnage, RepeatMin, RepeatMax + EVENT_T_SPAWNED = 11, // Condition, CondValue1 + EVENT_T_TARGET_HP = 12, // HPMax%, HPMin%, RepeatMin, RepeatMax + EVENT_T_TARGET_CASTING = 13, // RepeatMin, RepeatMax + EVENT_T_FRIENDLY_HP = 14, // HPDeficit, Radius, RepeatMin, RepeatMax + EVENT_T_FRIENDLY_IS_CC = 15, // DispelType, Radius, RepeatMin, RepeatMax + EVENT_T_FRIENDLY_MISSING_BUFF = 16, // SpellId, Radius, RepeatMin, RepeatMax + EVENT_T_SUMMONED_UNIT = 17, // CreatureId, RepeatMin, RepeatMax + EVENT_T_TARGET_MANA = 18, // ManaMax%, ManaMin%, RepeatMin, RepeatMax + EVENT_T_QUEST_ACCEPT = 19, // QuestID + EVENT_T_QUEST_COMPLETE = 20, // + EVENT_T_REACHED_HOME = 21, // NONE + EVENT_T_RECEIVE_EMOTE = 22, // EmoteId, Condition, CondValue1, CondValue2 + EVENT_T_BUFFED = 23, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max + EVENT_T_TARGET_BUFFED = 24, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max + EVENT_T_RESET = 35, // Is it called after combat, when the creature respawn and spawn. -- TRINITY ONLY + + EVENT_T_END, +}; + +enum EventAI_ActionType +{ + ACTION_T_NONE = 0, // No action + ACTION_T_TEXT = 1, // TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values. + ACTION_T_SET_FACTION = 2, // FactionId (or 0 for default) + ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph) + ACTION_T_SOUND = 4, // SoundId + ACTION_T_EMOTE = 5, // EmoteId + ACTION_T_RANDOM_SAY = 6, // UNUSED + ACTION_T_RANDOM_YELL = 7, // UNUSED + ACTION_T_RANDOM_TEXTEMOTE = 8, // UNUSED + ACTION_T_RANDOM_SOUND = 9, // SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field) + ACTION_T_CAST = 11, // SpellId, Target, CastFlags + ACTION_T_SUMMON = 12, // CreatureID, Target, Duration in ms + ACTION_T_THREAT_SINGLE_PCT = 13, // Threat%, Target + ACTION_T_THREAT_ALL_PCT = 14, // Threat% + ACTION_T_QUEST_EVENT = 15, // QuestID, Target + ACTION_T_CAST_EVENT = 16, // QuestID, SpellId, Target - must be removed as hack? + ACTION_T_SET_UNIT_FIELD = 17, // Field_Number, Value, Target + ACTION_T_SET_UNIT_FLAG = 18, // Flags (may be more than one field OR'd together), Target + ACTION_T_REMOVE_UNIT_FLAG = 19, // Flags (may be more than one field OR'd together), Target + ACTION_T_AUTO_ATTACK = 20, // AllowAttackState (0 = stop attack, anything else means continue attacking) + ACTION_T_COMBAT_MOVEMENT = 21, // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) + ACTION_T_SET_PHASE = 22, // Phase + ACTION_T_INC_PHASE = 23, // Value (may be negative to decrement phase, should not be 0) + ACTION_T_EVADE = 24, // No Params + ACTION_T_FLEE_FOR_ASSIST = 25, // No Params + ACTION_T_QUEST_EVENT_ALL = 26, // QuestID + ACTION_T_CAST_EVENT_ALL = 27, // CreatureId, SpellId + ACTION_T_REMOVEAURASFROMSPELL = 28, // Target, Spellid + ACTION_T_RANGED_MOVEMENT = 29, // Distance, Angle + ACTION_T_RANDOM_PHASE = 30, // PhaseId1, PhaseId2, PhaseId3 + ACTION_T_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax + ACTION_T_SUMMON_ID = 32, // CreatureId, Target, SpawnId + ACTION_T_KILLED_MONSTER = 33, // CreatureId, Target + ACTION_T_SET_INST_DATA = 34, // Field, Data + ACTION_T_SET_INST_DATA64 = 35, // Field, Target + ACTION_T_UPDATE_TEMPLATE = 36, // Entry, Team + ACTION_T_DIE = 37, // No Params + ACTION_T_ZONE_COMBAT_PULSE = 38, // No Params + ACTION_T_CALL_FOR_HELP = 39, // Radius + ACTION_T_SET_SHEATH = 40, // Sheath (0-passive,1-melee,2-ranged) + ACTION_T_FORCE_DESPAWN = 41, // No Params + ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue, format(0-flat,1-percent from max health) + + ACTION_T_SET_PHASE_MASK = 97, + ACTION_T_SET_STAND_STATE = 98, + ACTION_T_MOVE_RANDOM_POINT = 99, + ACTION_T_SET_VISIBILITY = 100, + ACTION_T_SET_ACTIVE = 101, //Apply + ACTION_T_SET_AGGRESSIVE = 102, //Apply + ACTION_T_ATTACK_START_PULSE = 103, //Distance + ACTION_T_SUMMON_GO = 104, //GameObjectID, DespawnTime in ms + + ACTION_T_END = 105, +}; + +enum Target +{ + //Self (me) + TARGET_T_SELF = 0, //Self cast + + //Hostile targets (if pet then returns pet owner) + TARGET_T_HOSTILE, //Our current target (ie: highest aggro) + TARGET_T_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + TARGET_T_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + TARGET_T_HOSTILE_RANDOM, //Just any random target on our threat list + TARGET_T_HOSTILE_RANDOM_NOT_TOP, //Any random target except top threat + + //Invoker targets (if pet then returns pet owner) + TARGET_T_ACTION_INVOKER, //Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF) + + //Hostile targets (including pets) + TARGET_T_HOSTILE_WPET, //Current target (can be a pet) + TARGET_T_HOSTILE_WPET_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + TARGET_T_HOSTILE_WPET_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + TARGET_T_HOSTILE_WPET_RANDOM, //Just any random target on our threat list + TARGET_T_HOSTILE_WPET_RANDOM_NOT_TOP, //Any random target except top threat + + TARGET_T_ACTION_INVOKER_WPET, + + TARGET_T_END +}; + +enum CastFlags +{ + CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting + CAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time) + CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range + CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range + CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself + CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell +}; + +enum EventFlags +{ + EFLAG_REPEATABLE = 0x01, //Event repeats + EFLAG_DIFFICULTY_0 = 0x02, //Event only occurs in instance difficulty 0 + EFLAG_DIFFICULTY_1 = 0x04, //Event only occurs in instance difficulty 1 + EFLAG_DIFFICULTY_2 = 0x08, //Event only occurs in instance difficulty 2 + EFLAG_DIFFICULTY_3 = 0x10, //Event only occurs in instance difficulty 3 + EFLAG_RESERVED_5 = 0x20, + EFLAG_RESERVED_6 = 0x40, + EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build + + EFLAG_DIFFICULTY_ALL = (EFLAG_DIFFICULTY_0|EFLAG_DIFFICULTY_1|EFLAG_DIFFICULTY_2|EFLAG_DIFFICULTY_3) +}; + +enum SpawnedEventMode +{ + SPAWNED_EVENT_ALWAY = 0, + SPAWNED_EVENT_MAP = 1, + SPAWNED_EVENT_ZONE = 2 +}; + +// String text additional data, used in (CreatureEventAI) +struct StringTextData +{ + uint32 SoundId; + uint8 Type; + uint32 Language; + uint32 Emote; +}; +// Text Maps +typedef UNORDERED_MAP CreatureEventAI_TextMap; + +struct CreatureEventAI_Action +{ + EventAI_ActionType type: 16; + union + { + // ACTION_T_TEXT = 1 + struct + { + int32 TextId1; + int32 TextId2; + int32 TextId3; + } text; + // ACTION_T_SET_FACTION = 2 + struct + { + uint32 factionId; // faction or 0 for default) + } set_faction; + // ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3 + struct + { + uint32 creatureId; // set one from fields (or 0 for both to demorph) + uint32 modelId; + } morph; + // ACTION_T_SOUND = 4 + struct + { + uint32 soundId; + } sound; + // ACTION_T_EMOTE = 5 + struct + { + uint32 emoteId; + } emote; + // ACTION_T_RANDOM_SOUND = 9 + struct + { + int32 soundId1; // (-1 in any field means no output if randomed that field) + int32 soundId2; + int32 soundId3; + } random_sound; + // ACTION_T_RANDOM_EMOTE = 10 + struct + { + int32 emoteId1; // (-1 in any field means no output if randomed that field) + int32 emoteId2; + int32 emoteId3; + } random_emote; + // ACTION_T_CAST = 11 + struct + { + uint32 spellId; + uint32 target; + uint32 castFlags; + } cast; + // ACTION_T_SUMMON = 12 + struct + { + uint32 creatureId; + uint32 target; + uint32 duration; + } summon; + // ACTION_T_THREAT_SINGLE_PCT = 13 + struct + { + int32 percent; + uint32 target; + } threat_single_pct; + // ACTION_T_THREAT_ALL_PCT = 14 + struct + { + int32 percent; + } threat_all_pct; + // ACTION_T_QUEST_EVENT = 15 + struct + { + uint32 questId; + uint32 target; + } quest_event; + // ACTION_T_CAST_EVENT = 16 + struct + { + uint32 creatureId; + uint32 spellId; + uint32 target; + } cast_event; + // ACTION_T_SET_UNIT_FIELD = 17 + struct + { + uint32 field; + uint32 value; + uint32 target; + } set_unit_field; + // ACTION_T_SET_UNIT_FLAG = 18, // value provided mask bits that will be set + // ACTION_T_REMOVE_UNIT_FLAG = 19, // value provided mask bits that will be clear + struct + { + uint32 value; + uint32 target; + } unit_flag; + // ACTION_T_AUTO_ATTACK = 20 + struct + { + uint32 state; // 0 = stop attack, anything else means continue attacking + } auto_attack; + // ACTION_T_COMBAT_MOVEMENT = 21 + struct + { + uint32 state; // 0 = stop combat based movement, anything else continue attacking + uint32 melee; // if set: at stop send melee combat stop if in combat, use for terminate melee fighting state for switch to ranged + } combat_movement; + // ACTION_T_SET_PHASE = 22 + struct + { + uint32 phase; + } set_phase; + // ACTION_T_INC_PHASE = 23 + struct + { + int32 step; + } set_inc_phase; + // ACTION_T_QUEST_EVENT_ALL = 26 + struct + { + uint32 questId; + } quest_event_all; + // ACTION_T_CAST_EVENT_ALL = 27 + struct + { + uint32 creatureId; + uint32 spellId; + } cast_event_all; + // ACTION_T_REMOVEAURASFROMSPELL = 28 + struct + { + uint32 target; + uint32 spellId; + } remove_aura; + // ACTION_T_RANGED_MOVEMENT = 29 + struct + { + uint32 distance; + int32 angle; + } ranged_movement; + // ACTION_T_RANDOM_PHASE = 30 + struct + { + uint32 phase1; + uint32 phase2; + uint32 phase3; + } random_phase; + // ACTION_T_RANDOM_PHASE_RANGE = 31 + struct + { + uint32 phaseMin; + uint32 phaseMax; + } random_phase_range; + // ACTION_T_SUMMON_ID = 32 + struct + { + uint32 creatureId; + uint32 target; + uint32 spawnId; + } summon_id; + // ACTION_T_KILLED_MONSTER = 33 + struct + { + uint32 creatureId; + uint32 target; + } killed_monster; + // ACTION_T_SET_INST_DATA = 34 + struct + { + uint32 field; + uint32 value; + } set_inst_data; + // ACTION_T_SET_INST_DATA64 = 35 + struct + { + uint32 field; + uint32 target; + } set_inst_data64; + // ACTION_T_UPDATE_TEMPLATE = 36 + struct + { + uint32 creatureId; + uint32 team; + } update_template; + // ACTION_T_CALL_FOR_HELP = 39 + struct + { + uint32 radius; + } call_for_help; + // ACTION_T_SET_SHEATH = 40 + struct + { + uint32 sheath; + } set_sheath; + // ACTION_T_FORCE_DESPAWN = 41 + struct + { + uint32 msDelay; + } forced_despawn; + // ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42 + struct + { + uint32 hp_level; + uint32 is_percent; + } invincibility_hp_level; + // RAW + struct + { + uint32 param1; + uint32 param2; + uint32 param3; + } raw; + }; +}; + +struct CreatureEventAI_Event +{ + uint32 event_id; + + uint32 creature_id; + + uint32 event_inverse_phase_mask; + + EventAI_Type event_type : 16; + uint8 event_chance : 8; + uint8 event_flags : 8; + + union + { + // EVENT_T_TIMER = 0 + // EVENT_T_TIMER_OOC = 1 + struct + { + uint32 initialMin; + uint32 initialMax; + uint32 repeatMin; + uint32 repeatMax; + } timer; + // EVENT_T_HP = 2 + // EVENT_T_MANA = 3 + // EVENT_T_TARGET_HP = 12 + // EVENT_T_TARGET_MANA = 18 + struct + { + uint32 percentMax; + uint32 percentMin; + uint32 repeatMin; + uint32 repeatMax; + } percent_range; + // EVENT_T_KILL = 5 + struct + { + uint32 repeatMin; + uint32 repeatMax; + } kill; + // EVENT_T_SPELLHIT = 8 + struct + { + uint32 spellId; + uint32 schoolMask; // -1 ( == 0xffffffff) is ok value for full mask, or must be more limited mask like (0 < 1) = 1 for normal/physical school + uint32 repeatMin; + uint32 repeatMax; + } spell_hit; + // EVENT_T_RANGE = 9 + struct + { + uint32 minDist; + uint32 maxDist; + uint32 repeatMin; + uint32 repeatMax; + } range; + // EVENT_T_OOC_LOS = 10 + struct + { + uint32 noHostile; + uint32 maxRange; + uint32 repeatMin; + uint32 repeatMax; + } ooc_los; + // EVENT_T_SPAWNED = 11 + struct + { + uint32 condition; + uint32 conditionValue1; + } spawned; + // EVENT_T_TARGET_CASTING = 13 + struct + { + uint32 repeatMin; + uint32 repeatMax; + } target_casting; + // EVENT_T_FRIENDLY_HP = 14 + struct + { + uint32 hpDeficit; + uint32 radius; + uint32 repeatMin; + uint32 repeatMax; + } friendly_hp; + // EVENT_T_FRIENDLY_IS_CC = 15 + struct + { + uint32 dispelType; // unused ? + uint32 radius; + uint32 repeatMin; + uint32 repeatMax; + } friendly_is_cc; + // EVENT_T_FRIENDLY_MISSING_BUFF = 16 + struct + { + uint32 spellId; + uint32 radius; + uint32 repeatMin; + uint32 repeatMax; + } friendly_buff; + // EVENT_T_SUMMONED_UNIT = 17 + struct + { + uint32 creatureId; + uint32 repeatMin; + uint32 repeatMax; + } summon_unit; + // EVENT_T_QUEST_ACCEPT = 19 + // EVENT_T_QUEST_COMPLETE = 20 + struct + { + uint32 questId; + } quest; + // EVENT_T_RECEIVE_EMOTE = 22 + struct + { + uint32 emoteId; + uint32 condition; + uint32 conditionValue1; + uint32 conditionValue2; + } receive_emote; + // EVENT_T_BUFFED = 23 + // EVENT_T_TARGET_BUFFED = 24 + struct + { + uint32 spellId; + uint32 amount; + uint32 repeatMin; + uint32 repeatMax; + } buffed; + + // RAW + struct + { + uint32 param1; + uint32 param2; + uint32 param3; + uint32 param4; + } raw; + }; + + CreatureEventAI_Action action[MAX_ACTIONS]; +}; +//Event_Map +typedef UNORDERED_MAP > CreatureEventAI_Event_Map; + +struct CreatureEventAI_Summon +{ + uint32 id; + + float position_x; + float position_y; + float position_z; + float orientation; + uint32 SpawnTimeSecs; +}; + +//EventSummon_Map +typedef UNORDERED_MAP CreatureEventAI_Summon_Map; + +struct CreatureEventAIHolder +{ + CreatureEventAIHolder(CreatureEventAI_Event p) : Event(p), Time(0), Enabled(true){} + + CreatureEventAI_Event Event; + uint32 Time; + bool Enabled; + + // helper + bool UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax); +}; + +class CreatureEventAI : public CreatureAI +{ + + public: + explicit CreatureEventAI(Creature *c); + ~CreatureEventAI() + { + CreatureEventAIList.clear(); + } + void JustRespawned(); + void Reset(); + void JustReachedHome(); + void EnterCombat(Unit *enemy); + void EnterEvadeMode(); + void JustDied(Unit* /*killer*/); + void KilledUnit(Unit* victim); + void JustSummoned(Creature* pUnit); + void AttackStart(Unit *who); + void MoveInLineOfSight(Unit *who); + void SpellHit(Unit* pUnit, const SpellEntry* pSpell); + void DamageTaken(Unit* done_by, uint32& damage); + void UpdateAI(const uint32 diff); + void ReceiveEmote(Player* pPlayer, uint32 text_emote); + static int Permissible(const Creature *); + + bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL); + void ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker); + inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3); + inline int32 GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3); + inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker); + + void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target); + bool CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered); + + bool SpawnedEventConditionsCheck(CreatureEventAI_Event const& event); + + Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff); + void DoFindFriendlyMissingBuff(std::list& _list, float range, uint32 spellid); + void DoFindFriendlyCC(std::list& _list, float range); + + //Holder for events (stores enabled, time, and eventid) + std::list CreatureEventAIList; + uint32 EventUpdateTime; //Time between event updates + uint32 EventDiff; //Time between the last event call + bool bEmptyList; + + //Variables used by Events themselves + uint8 Phase; // Current phase, max 32 phases + bool CombatMovementEnabled; // If we allow targeted movment gen (movement twoards top threat) + bool MeleeEnabled; // If we allow melee auto attack + float AttackDistance; // Distance to attack from + float AttackAngle; // Angle of attack + uint32 InvinceabilityHpLevel; // Minimal health level allowed at damage apply +}; +#endif diff --git a/src/server/game/AI/EventAI/CreatureEventAIMgr.cpp b/src/server/game/AI/EventAI/CreatureEventAIMgr.cpp new file mode 100644 index 00000000000..83d62ca74dc --- /dev/null +++ b/src/server/game/AI/EventAI/CreatureEventAIMgr.cpp @@ -0,0 +1,745 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Database/SQLStorage.h" +#include "CreatureEventAI.h" +#include "CreatureEventAIMgr.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "Policies/SingletonImp.h" +#include "ObjectDefines.h" +#include "GridDefines.h" +#include "ConditionMgr.h" + +INSTANTIATE_SINGLETON_1(CreatureEventAIMgr); + +// ------------------- +void CreatureEventAIMgr::LoadCreatureEventAI_Texts() +{ + // Drop Existing Text Map, only done once and we are ready to add data from multiple sources. + m_CreatureEventAI_TextMap.clear(); + + // Load EventAI Text + objmgr.LoadTrinityStrings(WorldDatabase,"creature_ai_texts",MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID); + + // Gather Additional data from EventAI Texts + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, sound, type, language, emote FROM creature_ai_texts"); + + sLog.outString("Loading EventAI Texts additional data..."); + if (result) + { + barGoLink bar(result->GetRowCount()); + uint32 count = 0; + + do + { + bar.step(); + Field* fields = result->Fetch(); + StringTextData temp; + + int32 i = fields[0].GetInt32(); + temp.SoundId = fields[1].GetInt32(); + temp.Type = fields[2].GetInt32(); + temp.Language = fields[3].GetInt32(); + temp.Emote = fields[4].GetInt32(); + + // range negative + if (i > MIN_CREATURE_AI_TEXT_STRING_ID || i <= MAX_CREATURE_AI_TEXT_STRING_ID) + { + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not in valid range(%d-%d)",i,MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID); + continue; + } + + // range negative (don't must be happen, loaded from same table) + if (!objmgr.GetTrinityStringLocale(i)) + { + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` not found",i); + continue; + } + + if (temp.SoundId) + { + if (!sSoundEntriesStore.LookupEntry(temp.SoundId)) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Sound %u but sound does not exist.",i,temp.SoundId); + } + + if (!GetLanguageDescByID(temp.Language)) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.",i,temp.Language); + + if (temp.Type > CHAT_TYPE_ZONE_YELL) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.",i,temp.Type); + + if (temp.Emote) + { + if (!sEmotesStore.LookupEntry(temp.Emote)) + sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Emote %u but emote does not exist.",i,temp.Emote); + } + + m_CreatureEventAI_TextMap[i] = temp; + ++count; + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u additional CreatureEventAI Texts data.", count); + } + else + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty."); + } + +} + +// ------------------- +void CreatureEventAIMgr::LoadCreatureEventAI_Summons() +{ + + //Drop Existing EventSummon Map + m_CreatureEventAI_Summon_Map.clear(); + + // Gather additional data for EventAI + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons"); + if (result) + { + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + CreatureEventAI_Summon temp; + + uint32 i = fields[0].GetUInt32(); + temp.position_x = fields[1].GetFloat(); + temp.position_y = fields[2].GetFloat(); + temp.position_z = fields[3].GetFloat(); + temp.orientation = fields[4].GetFloat(); + temp.SpawnTimeSecs = fields[5].GetUInt32(); + + if (!Trinity::IsValidMapCoord(temp.position_x,temp.position_y,temp.position_z,temp.orientation)) + { + sLog.outErrorDb("CreatureEventAI: Summon id %u have wrong coordinates (%f,%f,%f,%f), skipping.", i,temp.position_x,temp.position_y,temp.position_z,temp.orientation); + continue; + } + + //Add to map + m_CreatureEventAI_Summon_Map[i] = temp; + ++Count; + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count); + } + else + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty."); + } + +} + +// ------------------- +void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() +{ + //Drop Existing EventAI List + m_CreatureEventAI_Event_Map.clear(); + + // Gather event data + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " + "event_param1, event_param2, event_param3, event_param4, " + "action1_type, action1_param1, action1_param2, action1_param3, " + "action2_type, action2_param1, action2_param2, action2_param3, " + "action3_type, action3_param1, action3_param2, action3_param3 " + "FROM creature_ai_scripts"); + if (result) + { + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + CreatureEventAI_Event temp; + temp.event_id = EventAI_Type(fields[0].GetUInt32()); + uint32 i = temp.event_id; + + temp.creature_id = fields[1].GetUInt32(); + uint32 creature_id = temp.creature_id; + + uint32 e_type = fields[2].GetUInt32(); + //Report any errors in event + if (e_type >= EVENT_T_END) + { + sLog.outErrorDb("CreatureEventAI: Event %u have wrong type (%u), skipping.", i,e_type); + continue; + } + temp.event_type = EventAI_Type(e_type); + + temp.event_inverse_phase_mask = fields[3].GetUInt32(); + temp.event_chance = fields[4].GetUInt8(); + temp.event_flags = fields[5].GetUInt8(); + temp.raw.param1 = fields[6].GetUInt32(); + temp.raw.param2 = fields[7].GetUInt32(); + temp.raw.param3 = fields[8].GetUInt32(); + temp.raw.param4 = fields[9].GetUInt32(); + + //Creature does not exist in database + if (!sCreatureStorage.LookupEntry(temp.creature_id)) + { + sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id); + continue; + } + + //No chance of this event occuring + if (temp.event_chance == 0) + sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i); + //Chance above 100, force it to be 100 + else if (temp.event_chance > 100) + { + sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i); + temp.event_chance = 100; + } + + //Individual event checks + switch (temp.event_type) + { + case EVENT_T_TIMER: + case EVENT_T_TIMER_OOC: + if (temp.timer.initialMax < temp.timer.initialMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i); + if (temp.timer.repeatMax < temp.timer.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_HP: + case EVENT_T_MANA: + case EVENT_T_TARGET_HP: + case EVENT_T_TARGET_MANA: + if (temp.percent_range.percentMax > 100) + sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); + + if (temp.percent_range.percentMax <= temp.percent_range.percentMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i); + + if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i); + temp.event_flags &= ~EFLAG_REPEATABLE; + } + break; + case EVENT_T_SPELLHIT: + if (temp.spell_hit.spellId) + { + SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); + if (!pSpell) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); + continue; + } + + if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask) + sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i); + } + + if (!temp.spell_hit.schoolMask) + sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i); + + if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_RANGE: + if (temp.range.maxDist < temp.range.minDist) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i); + if (temp.range.repeatMax < temp.range.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_OOC_LOS: + if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_SPAWNED: + switch(temp.spawned.condition) + { + case SPAWNED_EVENT_ALWAY: + break; + case SPAWNED_EVENT_MAP: + if (!sMapStore.LookupEntry(temp.spawned.conditionValue1)) + sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'map specific' but with not existed map (%u) in param2. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); + break; + case SPAWNED_EVENT_ZONE: + if (!GetAreaEntryByAreaID(temp.spawned.conditionValue1)) + sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'area specific' but with not existed area (%u) in param2. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); + default: + sLog.outErrorDb("CreatureEventAI: Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition); + break; + } + break; + case EVENT_T_FRIENDLY_HP: + if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_FRIENDLY_IS_CC: + if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_FRIENDLY_MISSING_BUFF: + { + SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); + if (!pSpell) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); + continue; + } + if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + } + case EVENT_T_KILL: + if (temp.kill.repeatMax < temp.kill.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_TARGET_CASTING: + if (temp.target_casting.repeatMax < temp.target_casting.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_SUMMONED_UNIT: + if (!sCreatureStorage.LookupEntry(temp.summon_unit.creatureId)) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summon_unit.creatureId); + if (temp.summon_unit.repeatMax < temp.summon_unit.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + case EVENT_T_QUEST_ACCEPT: + case EVENT_T_QUEST_COMPLETE: + if (!objmgr.GetQuestTemplate(temp.quest.questId)) + sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed qyest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId); + sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i); + continue; + + case EVENT_T_AGGRO: + case EVENT_T_DEATH: + case EVENT_T_EVADE: + case EVENT_T_REACHED_HOME: + { + if (temp.event_flags & EFLAG_REPEATABLE) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i); + temp.event_flags &= ~EFLAG_REPEATABLE; + } + + break; + } + + case EVENT_T_RECEIVE_EMOTE: + { + if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId)) + { + sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId); + continue; + } + if (temp.receive_emote.condition) + { + Condition* cond = new Condition(); + cond->mConditionType = ConditionType(temp.receive_emote.condition); + cond->mConditionValue1 = temp.receive_emote.conditionValue1; + cond->mConditionValue2 = temp.receive_emote.conditionValue2; + if (!sConditionMgr.isConditionTypeValid(cond)) + { + sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition); + continue; + } + } + + if (!(temp.event_flags & EFLAG_REPEATABLE)) + { + sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i); + temp.event_flags |= EFLAG_REPEATABLE; + } + + break; + } + + case EVENT_T_BUFFED: + case EVENT_T_TARGET_BUFFED: + { + SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId); + if (!pSpell) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); + continue; + } + if (temp.buffed.repeatMax < temp.buffed.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + } + + default: + sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i); + break; + } + + for (uint32 j = 0; j < MAX_ACTIONS; j++) + { + uint16 action_type = fields[10+(j*4)].GetUInt16(); + if (action_type >= ACTION_T_END) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j+1, action_type); + temp.action[j].type = ACTION_T_NONE; + continue; + } + + CreatureEventAI_Action& action = temp.action[j]; + + action.type = EventAI_ActionType(action_type); + action.raw.param1 = fields[11+(j*4)].GetUInt32(); + action.raw.param2 = fields[12+(j*4)].GetUInt32(); + action.raw.param3 = fields[13+(j*4)].GetUInt32(); + + //Report any errors in actions + switch (action.type) + { + case ACTION_T_NONE: + break; + case ACTION_T_TEXT: + { + if (action.text.TextId1 < 0) + { + if (m_CreatureEventAI_TextMap.find(action.text.TextId1) == m_CreatureEventAI_TextMap.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1); + } + if (action.text.TextId2 < 0) + { + if (m_CreatureEventAI_TextMap.find(action.text.TextId2) == m_CreatureEventAI_TextMap.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1); + + if (!action.text.TextId1) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1); + } + if (action.text.TextId3 < 0) + { + if (m_CreatureEventAI_TextMap.find(action.text.TextId3) == m_CreatureEventAI_TextMap.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1); + + if (!action.text.TextId1 || !action.text.TextId2) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1); + } + break; + } + case ACTION_T_SET_FACTION: + if (action.set_faction.factionId !=0 && !sFactionStore.LookupEntry(action.set_faction.factionId)) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i, j+1, action.set_faction.factionId); + action.set_faction.factionId = 0; + } + break; + case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: + if (action.morph.creatureId !=0 || action.morph.modelId !=0) + { + if (action.morph.creatureId && !sCreatureStorage.LookupEntry(action.morph.creatureId)) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, action.morph.creatureId); + action.morph.creatureId = 0; + } + + if (action.morph.modelId) + { + if (action.morph.creatureId) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j+1, action.morph.modelId,action.morph.creatureId); + action.morph.modelId = 0; + } + else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId)) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, action.morph.modelId); + action.morph.modelId = 0; + } + } + } + break; + case ACTION_T_SOUND: + if (!sSoundEntriesStore.LookupEntry(action.sound.soundId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId); + break; + case ACTION_T_EMOTE: + if (!sEmotesStore.LookupEntry(action.emote.emoteId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.emote.emoteId); + break; + case ACTION_T_RANDOM_SOUND: + if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId1); + if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId2); + if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId3); + break; + case ACTION_T_RANDOM_EMOTE: + if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId1); + if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId2); + if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId3); + break; + case ACTION_T_CAST: + { + const SpellEntry *spell = sSpellStore.LookupEntry(action.cast.spellId); + if (!spell) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast.spellId); + /* FIXME: temp.raw.param3 not have event tipes with recovery time in it.... + else + { + if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE) + { + //output as debug for now, also because there's no general rule all spells have RecoveryTime + if (temp.event_param3 < spell->RecoveryTime) + sLog.outDebug("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3); + } + } + */ + + //Cast is always triggered if target is forced to cast on self + if (action.cast.castFlags & CAST_FORCE_TARGET_SELF) + action.cast.castFlags |= CAST_TRIGGERED; + + if (action.cast.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + } + case ACTION_T_SUMMON: + if (!sCreatureStorage.LookupEntry(action.summon.creatureId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.summon.creatureId); + + if (action.summon.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_THREAT_SINGLE_PCT: + if (std::abs(action.threat_single_pct.percent) > 100) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_single_pct.percent); + if (action.threat_single_pct.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_THREAT_ALL_PCT: + if (std::abs(action.threat_all_pct.percent) > 100) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_all_pct.percent); + break; + case ACTION_T_QUEST_EVENT: + if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event.questId)) + { + if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event.questId); + } + else + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event.questId); + + if (action.quest_event.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + + break; + case ACTION_T_CAST_EVENT: + if (!sCreatureStorage.LookupEntry(action.cast_event.creatureId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event.creatureId); + if (!sSpellStore.LookupEntry(action.cast_event.spellId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event.spellId); + if (action.cast_event.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_SET_UNIT_FIELD: + if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1); + if (action.set_unit_field.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_SET_UNIT_FLAG: + case ACTION_T_REMOVE_UNIT_FLAG: + if (action.unit_flag.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_SET_PHASE: + if (action.set_phase.phase >= MAX_PHASE) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); + break; + case ACTION_T_INC_PHASE: + if (action.set_inc_phase.step == 0) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1); + else if (std::abs(action.set_inc_phase.step) > MAX_PHASE-1) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u is change phase by too large for any use %i.", i, j+1, action.set_inc_phase.step); + break; + case ACTION_T_QUEST_EVENT_ALL: + if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event_all.questId)) + { + if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event_all.questId); + } + else + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event_all.questId); + break; + case ACTION_T_CAST_EVENT_ALL: + if (!sCreatureStorage.LookupEntry(action.cast_event_all.creatureId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event_all.creatureId); + if (!sSpellStore.LookupEntry(action.cast_event_all.spellId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event_all.spellId); + break; + case ACTION_T_REMOVEAURASFROMSPELL: + if (!sSpellStore.LookupEntry(action.remove_aura.spellId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.remove_aura.spellId); + if (action.remove_aura.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_RANDOM_PHASE: //PhaseId1, PhaseId2, PhaseId3 + if (action.random_phase.phase1 >= MAX_PHASE) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); + if (action.random_phase.phase2 >= MAX_PHASE) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); + if (action.random_phase.phase3 >= MAX_PHASE) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); + break; + case ACTION_T_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax + if (action.random_phase_range.phaseMin >= MAX_PHASE) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); + if (action.random_phase_range.phaseMin >= MAX_PHASE) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); + if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j+1); + std::swap(action.random_phase_range.phaseMin,action.random_phase_range.phaseMax); + // equal case processed at call + } + break; + case ACTION_T_SUMMON_ID: + if (!sCreatureStorage.LookupEntry(action.summon_id.creatureId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.summon_id.creatureId); + if (action.summon_id.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end()) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, action.summon_id.spawnId); + break; + case ACTION_T_KILLED_MONSTER: + if (!sCreatureStorage.LookupEntry(action.killed_monster.creatureId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.killed_monster.creatureId); + if (action.killed_monster.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_SET_INST_DATA: + if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1); + if (action.set_inst_data.value > 4/*SPECIAL*/) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1); + break; + case ACTION_T_SET_INST_DATA64: + if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1); + if (action.set_inst_data64.target >= TARGET_T_END) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); + break; + case ACTION_T_UPDATE_TEMPLATE: + if (!sCreatureStorage.LookupEntry(action.update_template.creatureId)) + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.update_template.creatureId); + break; + case ACTION_T_SET_SHEATH: + if (action.set_sheath.sheath >= MAX_SHEATH_STATE) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i, j+1, action.set_sheath.sheath); + action.set_sheath.sheath = SHEATH_STATE_UNARMED; + } + break; + case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: + if (action.invincibility_hp_level.is_percent) + { + if (action.invincibility_hp_level.hp_level > 100) + { + sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i, j+1, action.invincibility_hp_level.hp_level); + action.invincibility_hp_level.hp_level = 100; + } + } + break; + case ACTION_T_EVADE: //No Params + case ACTION_T_FLEE_FOR_ASSIST: //No Params + case ACTION_T_DIE: //No Params + case ACTION_T_ZONE_COMBAT_PULSE: //No Params + case ACTION_T_FORCE_DESPAWN: //No Params + case ACTION_T_AUTO_ATTACK: //AllowAttackState (0 = stop attack, anything else means continue attacking) + case ACTION_T_COMBAT_MOVEMENT: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) + case ACTION_T_RANGED_MOVEMENT: //Distance, Angle + case ACTION_T_CALL_FOR_HELP: //Distance + break; + + case ACTION_T_RANDOM_SAY: + case ACTION_T_RANDOM_YELL: + case ACTION_T_RANDOM_TEXTEMOTE: + sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1); + break; + + case ACTION_T_MOVE_RANDOM_POINT: + case ACTION_T_SET_STAND_STATE: + case ACTION_T_SET_PHASE_MASK: + case ACTION_T_SET_VISIBILITY: + case ACTION_T_SET_ACTIVE: + case ACTION_T_SET_AGGRESSIVE: + case ACTION_T_ATTACK_START_PULSE: + case ACTION_T_SUMMON_GO: + break; + + default: + sLog.outErrorDb("CreatureEventAI: Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j+1, temp.action[j].type); + break; + } + } + + //Add to list + m_CreatureEventAI_Event_Map[creature_id].push_back(temp); + ++Count; + + if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(temp.creature_id)) + { + if (!cInfo->AIName || !cInfo->AIName[0]) + { + //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but its AIName is empty. Set to EventAI as default.", cInfo->Entry); + size_t len = strlen("EventAI")+1; + const_cast(cInfo)->AIName = new char[len]; + strncpy(const_cast(cInfo->AIName), "EventAI", len); + } + if (strcmp(cInfo->AIName, "EventAI")) + { + //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it has AIName %s. EventAI script will be overriden.", cInfo->Entry, cInfo->AIName); + } + if (cInfo->ScriptID) + { + //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it also has C++ script. EventAI script will be overriden.", cInfo->Entry); + } + } + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u CreatureEventAI scripts", Count); + } + else + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty."); + } +} diff --git a/src/server/game/AI/EventAI/CreatureEventAIMgr.h b/src/server/game/AI/EventAI/CreatureEventAIMgr.h new file mode 100644 index 00000000000..ef191b22463 --- /dev/null +++ b/src/server/game/AI/EventAI/CreatureEventAIMgr.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CREATURE_EAI_MGR_H +#define TRINITY_CREATURE_EAI_MGR_H + +#include "Common.h" +#include "CreatureEventAI.h" + +class CreatureEventAIMgr +{ + public: + CreatureEventAIMgr(){}; + ~CreatureEventAIMgr(){}; + + void LoadCreatureEventAI_Texts(); + void LoadCreatureEventAI_Summons(); + void LoadCreatureEventAI_Scripts(); + + CreatureEventAI_Event_Map const& GetCreatureEventAIMap() const { return m_CreatureEventAI_Event_Map; } + CreatureEventAI_Summon_Map const& GetCreatureEventAISummonMap() const { return m_CreatureEventAI_Summon_Map; } + CreatureEventAI_TextMap const& GetCreatureEventAITextMap() const { return m_CreatureEventAI_TextMap; } + + private: + CreatureEventAI_Event_Map m_CreatureEventAI_Event_Map; + CreatureEventAI_Summon_Map m_CreatureEventAI_Summon_Map; + CreatureEventAI_TextMap m_CreatureEventAI_TextMap; +}; + +#define CreatureEAI_Mgr Trinity::Singleton::Instance() +#endif diff --git a/src/server/game/AI/GuardAI.cpp b/src/server/game/AI/GuardAI.cpp new file mode 100644 index 00000000000..19d5b5d8354 --- /dev/null +++ b/src/server/game/AI/GuardAI.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "GuardAI.h" +#include "Errors.h" +#include "Player.h" +#include "ObjectAccessor.h" +#include "World.h" +#include "CreatureAIImpl.h" + +int GuardAI::Permissible(const Creature *creature) +{ + if (creature->isGuard()) + return PERMIT_BASE_SPECIAL; + + return PERMIT_BASE_NO; +} + +GuardAI::GuardAI(Creature *c) : CreatureAI(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) +{ +} + +void GuardAI::MoveInLineOfSight(Unit *u) +{ + // Ignore Z for flying creatures + if (!me->canFly() && me->GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE) + return; + + if (!me->getVictim() && me->canAttack(u) && + (u->IsHostileToPlayers() || me->IsHostileTo(u) /*|| u->getVictim() && me->IsFriendlyTo(u->getVictim())*/) && + u->isInAccessiblePlaceFor(me)) + { + float attackRadius = me->GetAttackDistance(u); + if (me->IsWithinDistInMap(u,attackRadius)) + { + //Need add code to let guard support player + AttackStart(u); + //u->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); + } + } +} + +void GuardAI::EnterEvadeMode() +{ + if (!me->isAlive()) + { + DEBUG_LOG("Creature stopped attacking because he is dead [guid=%u]", me->GetGUIDLow()); + me->GetMotionMaster()->MoveIdle(); + + i_state = STATE_NORMAL; + + i_victimGuid = 0; + me->CombatStop(true); + me->DeleteThreatList(); + return; + } + + Unit* victim = ObjectAccessor::GetUnit(*me, i_victimGuid); + + if (!victim) + { + DEBUG_LOG("Creature stopped attacking because victim does not exist [guid=%u]", me->GetGUIDLow()); + } + else if (!victim ->isAlive()) + { + DEBUG_LOG("Creature stopped attacking because victim is dead [guid=%u]", me->GetGUIDLow()); + } + else if (victim ->HasStealthAura()) + { + DEBUG_LOG("Creature stopped attacking because victim is using stealth [guid=%u]", me->GetGUIDLow()); + } + else if (victim ->isInFlight()) + { + DEBUG_LOG("Creature stopped attacking because victim is flying away [guid=%u]", me->GetGUIDLow()); + } + else + { + DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", me->GetGUIDLow()); + } + + me->RemoveAllAuras(); + me->DeleteThreatList(); + i_victimGuid = 0; + me->CombatStop(true); + i_state = STATE_NORMAL; + + // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + me->GetMotionMaster()->MoveTargetedHome(); +} + +void GuardAI::UpdateAI(const uint32 /*diff*/) +{ + // update i_victimGuid if me->getVictim() !=0 and changed + if (!UpdateVictim()) + return; + + i_victimGuid = me->getVictim()->GetGUID(); + + if (me->isAttackReady()) + { + if (me->IsWithinMeleeRange(me->getVictim())) + { + me->AttackerStateUpdate(me->getVictim()); + me->resetAttackTimer(); + } + } +} + +bool GuardAI::IsVisible(Unit *pl) const +{ + return me->IsWithinDist(pl,sWorld.getConfig(CONFIG_SIGHT_GUARDER)) + && pl->isVisibleForOrDetect(me,true); +} + +void GuardAI::JustDied(Unit *killer) +{ + if (Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) + me->SendZoneUnderAttackMessage(pkiller); +} diff --git a/src/server/game/AI/GuardAI.h b/src/server/game/AI/GuardAI.h new file mode 100644 index 00000000000..73e3692a770 --- /dev/null +++ b/src/server/game/AI/GuardAI.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_GUARDAI_H +#define TRINITY_GUARDAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; + +class GuardAI : public CreatureAI +{ + enum GuardState + { + STATE_NORMAL = 1, + STATE_LOOK_AT_VICTIM = 2 + }; + + public: + + explicit GuardAI(Creature *c); + + void MoveInLineOfSight(Unit *); + void EnterEvadeMode(); + void JustDied(Unit *); + bool IsVisible(Unit *) const; + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + uint64 i_victimGuid; + GuardState i_state; + TimeTracker i_tracker; +}; +#endif + diff --git a/src/server/game/AI/PassiveAI.cpp b/src/server/game/AI/PassiveAI.cpp new file mode 100644 index 00000000000..c6c92f6df04 --- /dev/null +++ b/src/server/game/AI/PassiveAI.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PassiveAI.h" +#include "Creature.h" +#include "TemporarySummon.h" + +PassiveAI::PassiveAI(Creature *c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } +PossessedAI::PossessedAI(Creature *c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } +NullCreatureAI::NullCreatureAI(Creature *c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } + +void PassiveAI::UpdateAI(const uint32) +{ + if (me->isInCombat() && me->getAttackers().empty()) + EnterEvadeMode(); +} + +void PossessedAI::AttackStart(Unit *target) +{ + me->Attack(target, true); +} + +void PossessedAI::UpdateAI(const uint32 /*diff*/) +{ + if (me->getVictim()) + { + if (!me->canAttack(me->getVictim())) + me->AttackStop(); + else + DoMeleeAttackIfReady(); + } +} + +void PossessedAI::JustDied(Unit * /*u*/) +{ + // We died while possessed, disable our loot + me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); +} + +void PossessedAI::KilledUnit(Unit* victim) +{ + // We killed a creature, disable victim's loot + if (victim->GetTypeId() == TYPEID_UNIT) + victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); +} + +void CritterAI::DamageTaken(Unit * /*done_by*/, uint32 &) +{ + if (!me->hasUnitState(UNIT_STAT_FLEEING)) + me->SetControlled(true, UNIT_STAT_FLEEING); +} + +void CritterAI::EnterEvadeMode() +{ + if (me->hasUnitState(UNIT_STAT_FLEEING)) + me->SetControlled(false, UNIT_STAT_FLEEING); + CreatureAI::EnterEvadeMode(); +} + +void TriggerAI::IsSummonedBy(Unit *summoner) +{ + if (me->m_spells[0]) + me->CastSpell(me, me->m_spells[0], false, 0, 0, summoner->GetGUID()); +} diff --git a/src/server/game/AI/PassiveAI.h b/src/server/game/AI/PassiveAI.h new file mode 100644 index 00000000000..c13e2d1a63c --- /dev/null +++ b/src/server/game/AI/PassiveAI.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_PASSIVEAI_H +#define TRINITY_PASSIVEAI_H + +#include "CreatureAI.h" +//#include "CreatureAIImpl.h" + +class PassiveAI : public CreatureAI +{ + public: + explicit PassiveAI(Creature *c); + + void MoveInLineOfSight(Unit *) {} + void AttackStart(Unit *) {} + void UpdateAI(const uint32); + + static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; } +}; + +class PossessedAI : public CreatureAI +{ + public: + explicit PossessedAI(Creature *c); + + void MoveInLineOfSight(Unit *) {} + void AttackStart(Unit *target); + void UpdateAI(const uint32); + void EnterEvadeMode() {} + + void JustDied(Unit*); + void KilledUnit(Unit* victim); + + static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; } +}; + +class NullCreatureAI : public CreatureAI +{ + public: + explicit NullCreatureAI(Creature *c); + + void MoveInLineOfSight(Unit *) {} + void AttackStart(Unit *) {} + void UpdateAI(const uint32) {} + void EnterEvadeMode() {} + void OnCharmed(bool /*apply*/) {} + + static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; } +}; + +class CritterAI : public PassiveAI +{ + public: + explicit CritterAI(Creature *c) : PassiveAI(c) {} + + void DamageTaken(Unit *done_by, uint32 & /*damage*/); + void EnterEvadeMode(); +}; + +class TriggerAI : public NullCreatureAI +{ + public: + explicit TriggerAI(Creature *c) : NullCreatureAI(c) {} + void IsSummonedBy(Unit *summoner); +}; + +#endif + diff --git a/src/server/game/AI/PetAI.cpp b/src/server/game/AI/PetAI.cpp new file mode 100644 index 00000000000..09ec8fae53f --- /dev/null +++ b/src/server/game/AI/PetAI.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PetAI.h" +#include "Errors.h" +#include "Pet.h" +#include "Player.h" +#include "DBCStores.h" +#include "Spell.h" +#include "ObjectAccessor.h" +#include "SpellMgr.h" +#include "Creature.h" +#include "World.h" +#include "Util.h" + +int PetAI::Permissible(const Creature *creature) +{ + if (creature->isPet()) + return PERMIT_BASE_SPECIAL; + + return PERMIT_BASE_NO; +} + +PetAI::PetAI(Creature *c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK) +{ + m_AllySet.clear(); + UpdateAllies(); +} + +void PetAI::EnterEvadeMode() +{ +} + +bool PetAI::_needToStop() const +{ + // This is needed for charmed creatures, as once their target was reset other effects can trigger threat + if (me->isCharmed() && me->getVictim() == me->GetCharmer()) + return true; + + return !me->canAttack(me->getVictim()); +} + +void PetAI::_stopAttack() +{ + if (!me->isAlive()) + { + DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", me->GetGUIDLow()); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + me->CombatStop(); + me->getHostileRefManager().deleteReferences(); + + return; + } + + me->AttackStop(); + me->GetCharmInfo()->SetIsCommandAttack(false); + HandleReturnMovement(); +} + +void PetAI::UpdateAI(const uint32 diff) +{ + if (!me->isAlive()) + return; + + Unit* owner = me->GetCharmerOrOwner(); + + if (m_updateAlliesTimer <= diff) + // UpdateAllies self set update timer + UpdateAllies(); + else + m_updateAlliesTimer -= diff; + + // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc. + if (me->getVictim()) + { + if (_needToStop()) + { + DEBUG_LOG("Pet AI stoped attacking [guid=%u]", me->GetGUIDLow()); + _stopAttack(); + return; + } + + DoMeleeAttackIfReady(); + } + else if (owner && me->GetCharmInfo()) //no victim + { + Unit *nextTarget = SelectNextTarget(); + + if (nextTarget) + AttackStart(nextTarget); + else + HandleReturnMovement(); + } + else if (owner && !me->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim + me->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, me->GetFollowAngle()); + + if (!me->GetCharmInfo()) + return; + + // Autocast (casted only in combat or persistent spells in any state) + if (me->GetGlobalCooldown() == 0 && !me->hasUnitState(UNIT_STAT_CASTING)) + { + typedef std::vector > TargetSpellList; + TargetSpellList targetSpellStore; + + for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i) + { + uint32 spellID = me->GetPetAutoSpellOnPos(i); + if (!spellID) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); + if (!spellInfo) + continue; + + // ignore some combinations of combat state and combat/noncombat spells + if (!me->getVictim()) + { + // ignore attacking spells, and allow only self/around spells + if (!IsPositiveSpell(spellInfo->Id)) + continue; + + // non combat spells allowed + // only pet spells have IsNonCombatSpell and not fit this reqs: + // Consume Shadows, Lesser Invisibility, so ignore checks for its + if (!IsNonCombatSpell(spellInfo)) + { + // allow only spell without spell cost or with spell cost but not duration limit + int32 duration = GetSpellDuration(spellInfo); + if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0) + continue; + + // allow only spell without cooldown > duration + int32 cooldown = GetSpellRecoveryTime(spellInfo); + if (cooldown >= 0 && duration >= 0 && cooldown > duration) + continue; + } + } + else + { + // just ignore non-combat spells + if (IsNonCombatSpell(spellInfo)) + continue; + } + + Spell *spell = new Spell(me, spellInfo, false, 0); + + // Fix to allow pets on STAY to autocast + if (me->getVictim() && _CanAttack(me->getVictim()) && spell->CanAutoCast(me->getVictim())) + { + targetSpellStore.push_back(std::make_pair(me->getVictim(), spell)); + continue; + } + else + { + bool spellUsed = false; + for (std::set::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) + { + Unit* Target = ObjectAccessor::GetUnit(*me,*tar); + + //only buff targets that are in combat, unless the spell can only be cast while out of combat + if (!Target) + continue; + + if (spell->CanAutoCast(Target)) + { + targetSpellStore.push_back(std::make_pair(Target, spell)); + spellUsed = true; + break; + } + } + if (!spellUsed) + delete spell; + } + } + + //found units to cast on to + if (!targetSpellStore.empty()) + { + uint32 index = urand(0, targetSpellStore.size() - 1); + + Spell* spell = targetSpellStore[index].second; + Unit* target = targetSpellStore[index].first; + + targetSpellStore.erase(targetSpellStore.begin() + index); + + SpellCastTargets targets; + targets.setUnitTarget(target); + + if (!me->HasInArc(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()); + } + + me->AddCreatureSpellCooldown(spell->m_spellInfo->Id); + + spell->prepare(&targets); + } + + // deleted cached Spell objects + for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr) + delete itr->second; + } +} + +void PetAI::UpdateAllies() +{ + Unit* owner = me->GetCharmerOrOwner(); + Group *pGroup = NULL; + + m_updateAlliesTimer = 10*IN_MILISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance + + if (!owner) + return; + else if (owner->GetTypeId() == TYPEID_PLAYER) + pGroup = owner->ToPlayer()->GetGroup(); + + //only pet and owner/not in group->ok + if (m_AllySet.size() == 2 && !pGroup) + return; + //owner is in group; group members filled in already (no raid -> subgroupcount = whole count) + if (pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2)) + return; + + m_AllySet.clear(); + m_AllySet.insert(me->GetGUID()); + if (pGroup) //add group + { + for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); + if (!Target || !pGroup->SameSubGroup((Player*)owner, Target)) + continue; + + if (Target->GetGUID() == owner->GetGUID()) + continue; + + m_AllySet.insert(Target->GetGUID()); + } + } + else //remove group + m_AllySet.insert(owner->GetGUID()); +} + +void PetAI::KilledUnit(Unit *victim) +{ + // Called from Unit::Kill() in case where pet or owner kills something + // if owner killed this victim, pet may still be attacking something else + if (me->getVictim() && me->getVictim() != victim) + return; + + // Clear target just in case. May help problem where health / focus / mana + // regen gets stuck. Also resets attack command. + // Can't use _stopAttack() because that activates movement handlers and ignores + // next target selection + me->AttackStop(); + me->GetCharmInfo()->SetIsCommandAttack(false); + + Unit *nextTarget = SelectNextTarget(); + + if (nextTarget) + AttackStart(nextTarget); + else + HandleReturnMovement(); // Return +} + +void PetAI::AttackStart(Unit *target) +{ + // Overrides Unit::AttackStart to correctly evaluate Pet states + + // Check all pet states to decide if we can attack this target + if (!_CanAttack(target)) + return; + + // We can attack, should we chase or not? + if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + DoAttack(target,true); // FOLLOW, attack with chase + else + { + if (me->GetCharmInfo()->IsCommandAttack()) + DoAttack(target,true); // STAY or FOLLOW, player clicked "attack" so attack with chase + else + DoAttack(target,false); // STAY, target in range, attack not clicked so attack without chase + } +} + +Unit *PetAI::SelectNextTarget() +{ + // Provides next target selection after current target death + + // Passive pets don't do next target selection + if (me->HasReactState(REACT_PASSIVE)) + return NULL; + + // Check pet's attackers first to prevent dragging mobs back + // to owner + if (me->getAttackerForHelper()) + return me->getAttackerForHelper(); + + // Check owner's attackers if pet didn't have any + if (me->GetCharmerOrOwner()->getAttackerForHelper()) + return me->GetCharmerOrOwner()->getAttackerForHelper(); + + // 3.0.2 - Pets now start attacking their owners target in defensive mode as soon as the hunter does + if (me->GetCharmerOrOwner()->getVictim()) + return me->GetCharmerOrOwner()->getVictim(); + + // Default + return NULL; +} + +void PetAI::HandleReturnMovement() +{ + // Handles moving the pet back to stay or owner + + if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) + { + if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning()) + { + // Return to previous position where stay was clicked + if (!me->GetCharmInfo()->IsCommandAttack()) + { + float x,y,z; + + me->GetCharmInfo()->GetStayPosition(x, y, z); + me->GetCharmInfo()->SetIsReturning(true); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(me->GetGUIDLow(),x,y,z); + } + } + } + else // COMMAND_FOLLOW + { + if (!me->GetCharmInfo()->IsFollowing() && !me->GetCharmInfo()->IsReturning()) + { + if (!me->GetCharmInfo()->IsCommandAttack()) + { + me->GetCharmInfo()->SetIsReturning(true); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle()); + } + } + } + +} + +void PetAI::DoAttack(Unit *target, bool chase) +{ + // Handles attack with or without chase and also resets all + // PetAI flags for next update / creature kill + + // me->GetCharmInfo()->SetIsCommandAttack(false); + + // The following conditions are true if chase == true + // (Follow && (Aggressive || Defensive)) + // ((Stay || Follow) && (Passive && player clicked attack)) + + if (chase) + { + if (me->Attack(target,true)) + { + me->GetCharmInfo()->SetIsAtStay(false); + me->GetCharmInfo()->SetIsFollowing(false); + me->GetCharmInfo()->SetIsReturning(false); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(target); + } + } + else // (Stay && ((Aggressive || Defensive) && In Melee Range))) + { + me->GetCharmInfo()->SetIsAtStay(true); + me->GetCharmInfo()->SetIsFollowing(false); + me->GetCharmInfo()->SetIsReturning(false); + me->Attack(target,true); + } +} + +void PetAI::MovementInform(uint32 moveType, uint32 data) +{ + // Receives notification when pet reaches stay or follow owner + switch (moveType) + { + case POINT_MOTION_TYPE: + { + // Pet is returning to where stay was clicked. data should be + // pet's GUIDLow since we set that as the waypoint ID + if (data == me->GetGUIDLow() && me->GetCharmInfo()->IsReturning()) + { + me->GetCharmInfo()->SetIsAtStay(true); + me->GetCharmInfo()->SetIsReturning(false); + me->GetCharmInfo()->SetIsFollowing(false); + me->GetCharmInfo()->SetIsCommandAttack(false); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + } + } + break; + + case TARGETED_MOTION_TYPE: + { + // If data is owner's GUIDLow then we've reached follow point, + // otherwise we're probably chasing a creature + if (me->GetCharmerOrOwner() && me->GetCharmInfo() && data == me->GetCharmerOrOwner()->GetGUIDLow() && me->GetCharmInfo()->IsReturning()) + { + me->GetCharmInfo()->SetIsAtStay(false); + me->GetCharmInfo()->SetIsReturning(false); + me->GetCharmInfo()->SetIsFollowing(true); + me->GetCharmInfo()->SetIsCommandAttack(false); + me->addUnitState(UNIT_STAT_FOLLOW); + } + } + break; + + default: + break; + } +} + +bool PetAI::_CanAttack(Unit *target) +{ + // Evaluates wether a pet can attack a specific + // target based on CommandState, ReactState and other flags + + // Returning - check first since pets returning ignore attacks + if (me->GetCharmInfo()->IsReturning()) + return false; + + // Passive - check now so we don't have to worry about passive in later checks + if (me->HasReactState(REACT_PASSIVE)) + return me->GetCharmInfo()->IsCommandAttack(); + + // Pets commanded to attack should not stop their approach if attacked by another creature + if (me->getVictim() && (me->getVictim() != target)) + return !me->GetCharmInfo()->IsCommandAttack(); + + // From this point on, pet will always be either aggressive or defensive + + // Stay - can attack if target is within range or commanded to + if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY)) + return (me->IsWithinMeleeRange(target, MIN_MELEE_REACH) || me->GetCharmInfo()->IsCommandAttack()); + + // Follow + if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + return true; + + // default, though we shouldn't ever get here + return false; +} diff --git a/src/server/game/AI/PetAI.h b/src/server/game/AI/PetAI.h new file mode 100644 index 00000000000..f6087a129ae --- /dev/null +++ b/src/server/game/AI/PetAI.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_PETAI_H +#define TRINITY_PETAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; +class Spell; + +class PetAI : public CreatureAI +{ + public: + + explicit PetAI(Creature *c); + + void EnterEvadeMode(); + void JustDied(Unit * /*who*/) { _stopAttack(); } + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + void KilledUnit(Unit * /*victim*/); + void AttackStart(Unit *target); + void MovementInform(uint32 moveType, uint32 data); + + private: + bool _isVisible(Unit *) const; + bool _needToStop(void) const; + void _stopAttack(void); + + void UpdateAllies(); + + TimeTracker i_tracker; + bool inCombat; + std::set m_AllySet; + uint32 m_updateAlliesTimer; + + Unit *SelectNextTarget(); + void HandleReturnMovement(); + void DoAttack(Unit *target, bool chase); + bool _CanAttack(Unit *target); +}; +#endif + diff --git a/src/server/game/AI/ReactorAI.cpp b/src/server/game/AI/ReactorAI.cpp new file mode 100644 index 00000000000..fdca6314747 --- /dev/null +++ b/src/server/game/AI/ReactorAI.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ByteBuffer.h" +#include "ReactorAI.h" +#include "Errors.h" +#include "Log.h" +#include "ObjectAccessor.h" +#include "CreatureAIImpl.h" + +#define REACTOR_VISIBLE_RANGE (26.46f) + +int +ReactorAI::Permissible(const Creature *creature) +{ + if (creature->isCivilian() || creature->IsNeutralToAll()) + return PERMIT_BASE_REACTIVE; + + return PERMIT_BASE_NO; +} + +void +ReactorAI::MoveInLineOfSight(Unit *) +{ +} + +void +ReactorAI::UpdateAI(const uint32 /*time_diff*/) +{ + // update i_victimGuid if me->getVictim() !=0 and changed + if (!UpdateVictim()) + return; + + if (me->isAttackReady()) + { + if (me->IsWithinMeleeRange(me->getVictim())) + { + me->AttackerStateUpdate(me->getVictim()); + me->resetAttackTimer(); + } + } +} diff --git a/src/server/game/AI/ReactorAI.h b/src/server/game/AI/ReactorAI.h new file mode 100644 index 00000000000..ae4e6403962 --- /dev/null +++ b/src/server/game/AI/ReactorAI.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_REACTORAI_H +#define TRINITY_REACTORAI_H + +#include "CreatureAI.h" + +class Unit; + +class ReactorAI : public CreatureAI +{ + public: + + explicit ReactorAI(Creature *c) : CreatureAI(c) {} + + void MoveInLineOfSight(Unit *); + + void UpdateAI(const uint32); + static int Permissible(const Creature *); +}; +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp new file mode 100644 index 00000000000..8c4ddd14f07 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -0,0 +1,740 @@ +/* Copyright (C) 2008-2010 Trinity + * + * Thanks to the original authors: ScriptDev2 + * + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#include "ScriptedPch.h" +#include "Item.h" +#include "Spell.h" +#include "ObjectMgr.h" +#include "TemporarySummon.h" + +// Spell summary for ScriptedAI::SelectSpell +struct TSpellSummary +{ + uint8 Targets; // set of enum SelectTarget + uint8 Effects; // set of enum SelectEffect +} *SpellSummary; + +void SummonList::DoZoneInCombat(uint32 entry) +{ + for (iterator i = begin(); i != end();) + { + Creature *summon = Unit::GetCreature(*me, *i); + ++i; + if (summon && summon->IsAIEnabled + && (!entry || summon->GetEntry() == entry)) + summon->AI()->DoZoneInCombat(); + } +} + +void SummonList::DoAction(uint32 entry, uint32 info) +{ + for (iterator i = begin(); i != end();) + { + Creature *summon = Unit::GetCreature(*me, *i); + ++i; + if (summon && summon->IsAIEnabled + && (!entry || summon->GetEntry() == entry)) + summon->AI()->DoAction(info); + } +} + +void SummonList::DespawnEntry(uint32 entry) +{ + for (iterator i = begin(); i != end();) + { + Creature *summon = Unit::GetCreature(*me, *i); + if (!summon) + erase(i++); + else if (summon->GetEntry() == entry) + { + erase(i++); + summon->setDeathState(JUST_DIED); + summon->RemoveCorpse(); + } + else + ++i; + } +} + +void SummonList::DespawnAll() +{ + while (!empty()) + { + Creature *summon = Unit::GetCreature(*me, *begin()); + if (!summon) + erase(begin()); + else + { + erase(begin()); + if (summon->isSummon()) + { + summon->DestroyForNearbyPlayers(); + CAST_SUM(summon)->UnSummon(); + } + else + summon->DisappearAndDie(); + } + } +} + +ScriptedAI::ScriptedAI(Creature* pCreature) : CreatureAI(pCreature), + me(pCreature), + IsFleeing(false), + m_bCombatMovement(true), + m_uiEvadeCheckCooldown(2500) +{ + m_heroicMode = me->GetMap()->IsHeroic(); + m_difficulty = Difficulty(me->GetMap()->GetSpawnMode()); +} + +void ScriptedAI::AttackStartNoMove(Unit* pWho) +{ + if (!pWho) + return; + + if (me->Attack(pWho, false)) + DoStartNoMovement(pWho); +} + +void ScriptedAI::UpdateAI(const uint32 /*uiDiff*/) +{ + //Check if we have a current target + if (!UpdateVictim()) + return; + + if (me->isAttackReady()) + { + //If we are within range melee the target + if (me->IsWithinMeleeRange(me->getVictim())) + { + me->AttackerStateUpdate(me->getVictim()); + me->resetAttackTimer(); + } + } +} + +void ScriptedAI::DoStartMovement(Unit* pVictim, float fDistance, float fAngle) +{ + if (pVictim) + me->GetMotionMaster()->MoveChase(pVictim, fDistance, fAngle); +} + +void ScriptedAI::DoStartNoMovement(Unit* pVictim) +{ + if (!pVictim) + return; + + me->GetMotionMaster()->MoveIdle(); +} + +void ScriptedAI::DoStopAttack() +{ + if (me->getVictim()) + me->AttackStop(); +} + +void ScriptedAI::DoCastSpell(Unit* pTarget, SpellEntry const* pSpellInfo, bool bTriggered) +{ + if (!pTarget || me->IsNonMeleeSpellCasted(false)) + return; + + me->StopMoving(); + me->CastSpell(pTarget, pSpellInfo, bTriggered); +} + +void ScriptedAI::DoPlaySoundToSet(WorldObject* pSource, uint32 uiSoundId) +{ + if (!pSource) + return; + + if (!GetSoundEntriesStore()->LookupEntry(uiSoundId)) + { + error_log("TSCR: Invalid soundId %u used in DoPlaySoundToSet (Source: TypeId %u, GUID %u)", uiSoundId, pSource->GetTypeId(), pSource->GetGUIDLow()); + return; + } + + pSource->PlayDirectSound(uiSoundId); +} + +Creature* ScriptedAI::DoSpawnCreature(uint32 uiId, float fX, float fY, float fZ, float fAngle, uint32 uiType, uint32 uiDespawntime) +{ + return me->SummonCreature(uiId, me->GetPositionX()+fX, me->GetPositionY()+fY, me->GetPositionZ()+fZ, fAngle, (TempSummonType)uiType, uiDespawntime); +} + +Unit* ScriptedAI::SelectUnit(SelectAggroTarget pTarget, uint32 uiPosition) +{ + //ThreatList m_threatlist; + std::list& threatlist = me->getThreatManager().getThreatList(); + std::list::iterator itr = threatlist.begin(); + std::list::reverse_iterator ritr = threatlist.rbegin(); + + if (uiPosition >= threatlist.size() || !threatlist.size()) + return NULL; + + switch (pTarget) + { + case SELECT_TARGET_RANDOM: + advance (itr , uiPosition + (rand() % (threatlist.size() - uiPosition))); + return Unit::GetUnit((*me),(*itr)->getUnitGuid()); + break; + + case SELECT_TARGET_TOPAGGRO: + advance (itr , uiPosition); + return Unit::GetUnit((*me),(*itr)->getUnitGuid()); + break; + + case SELECT_TARGET_BOTTOMAGGRO: + advance (ritr , uiPosition); + return Unit::GetUnit((*me),(*ritr)->getUnitGuid()); + break; + + default: + return UnitAI::SelectTarget(pTarget, uiPosition); + } +} + +SpellEntry const* ScriptedAI::SelectSpell(Unit* pTarget, uint32 uiSchool, uint32 uiMechanic, SelectTargetType selectTargets, uint32 uiPowerCostMin, uint32 uiPowerCostMax, float fRangeMin, float fRangeMax, SelectEffect selectEffects) +{ + //No target so we can't cast + if (!pTarget) + return false; + + //Silenced so we can't cast + if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return false; + + //Using the extended script system we first create a list of viable spells + SpellEntry const* apSpell[CREATURE_MAX_SPELLS]; + memset(apSpell, 0, sizeof(SpellEntry*)*CREATURE_MAX_SPELLS); + + uint32 uiSpellCount = 0; + + SpellEntry const* pTempSpell; + SpellRangeEntry const* pTempRange; + + //Check if each spell is viable(set it to null if not) + for (uint32 i = 0; i < CREATURE_MAX_SPELLS; i++) + { + pTempSpell = GetSpellStore()->LookupEntry(me->m_spells[i]); + + //This spell doesn't exist + if (!pTempSpell) + continue; + + // Targets and Effects checked first as most used restrictions + //Check the spell targets if specified + if (selectTargets && !(SpellSummary[me->m_spells[i]].Targets & (1 << (selectTargets-1)))) + continue; + + //Check the type of spell if we are looking for a specific spell type + if (selectEffects && !(SpellSummary[me->m_spells[i]].Effects & (1 << (selectEffects-1)))) + continue; + + //Check for school if specified + if (uiSchool && (pTempSpell->SchoolMask & uiSchool) == 0) + continue; + + //Check for spell mechanic if specified + if (uiMechanic && pTempSpell->Mechanic != uiMechanic) + continue; + + //Make sure that the spell uses the requested amount of power + if (uiPowerCostMin && pTempSpell->manaCost < uiPowerCostMin) + continue; + + if (uiPowerCostMax && pTempSpell->manaCost > uiPowerCostMax) + continue; + + //Continue if we don't have the mana to actually cast this spell + if (pTempSpell->manaCost > me->GetPower((Powers)pTempSpell->powerType)) + continue; + + //Get the Range + pTempRange = GetSpellRangeStore()->LookupEntry(pTempSpell->rangeIndex); + + //Spell has invalid range store so we can't use it + if (!pTempRange) + continue; + + //Check if the spell meets our range requirements + if (fRangeMin && me->GetSpellMinRangeForTarget(pTarget, pTempRange) < fRangeMin) + continue; + if (fRangeMax && me->GetSpellMaxRangeForTarget(pTarget, pTempRange) > fRangeMax) + continue; + + //Check if our target is in range + if (me->IsWithinDistInMap(pTarget, me->GetSpellMinRangeForTarget(pTarget, pTempRange)) || !me->IsWithinDistInMap(pTarget, me->GetSpellMaxRangeForTarget(pTarget, pTempRange))) + continue; + + //All good so lets add it to the spell list + apSpell[uiSpellCount] = pTempSpell; + ++uiSpellCount; + } + + //We got our usable spells so now lets randomly pick one + if (!uiSpellCount) + return NULL; + + return apSpell[rand()%uiSpellCount]; +} + +bool ScriptedAI::CanCast(Unit* pTarget, SpellEntry const* pSpell, bool bTriggered) +{ + //No target so we can't cast + if (!pTarget || !pSpell) + return false; + + //Silenced so we can't cast + if (!bTriggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return false; + + //Check for power + if (!bTriggered && me->GetPower((Powers)pSpell->powerType) < pSpell->manaCost) + return false; + + SpellRangeEntry const* pTempRange = GetSpellRangeStore()->LookupEntry(pSpell->rangeIndex); + + //Spell has invalid range store so we can't use it + if (!pTempRange) + return false; + + //Unit is out of range of this spell + if (me->IsInRange(pTarget, me->GetSpellMinRangeForTarget(pTarget, pTempRange), me->GetSpellMaxRangeForTarget(pTarget, pTempRange))) + return false; + + return true; +} + +void FillSpellSummary() +{ + SpellSummary = new TSpellSummary[GetSpellStore()->GetNumRows()]; + + SpellEntry const* pTempSpell; + + for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i) + { + SpellSummary[i].Effects = 0; + SpellSummary[i].Targets = 0; + + pTempSpell = GetSpellStore()->LookupEntry(i); + //This spell doesn't exist + if (!pTempSpell) + continue; + + for (uint32 j = 0; j < 3; ++j) + { + //Spell targets self + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_CASTER) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SELF-1); + + //Spell targets a single enemy + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ENEMY || + pTempSpell->EffectImplicitTargetA[j] == TARGET_DST_TARGET_ENEMY) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1); + + //Spell targets AoE at enemy + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_AREA_ENEMY_SRC || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_AREA_ENEMY_DST || + pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER || + pTempSpell->EffectImplicitTargetA[j] == TARGET_DEST_DYNOBJ_ENEMY) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1); + + //Spell targets an enemy + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ENEMY || + pTempSpell->EffectImplicitTargetA[j] == TARGET_DST_TARGET_ENEMY || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_AREA_ENEMY_SRC || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_AREA_ENEMY_DST || + pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER || + pTempSpell->EffectImplicitTargetA[j] == TARGET_DEST_DYNOBJ_ENEMY) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1); + + //Spell targets a single friend(or self) + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_CASTER || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ALLY || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_PARTY) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1); + + //Spell targets aoe friends + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_CASTER || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_TARGET || + pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1); + + //Spell targets any friend(or self) + if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_CASTER || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ALLY || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_PARTY || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_CASTER || + pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_TARGET || + pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER) + SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1); + + //Make sure that this spell includes a damage effect + if (pTempSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || + pTempSpell->Effect[j] == SPELL_EFFECT_INSTAKILL || + pTempSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE || + pTempSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH) + SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_DAMAGE-1); + + //Make sure that this spell includes a healing effect (or an apply aura with a periodic heal) + if (pTempSpell->Effect[j] == SPELL_EFFECT_HEAL || + pTempSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH || + pTempSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL || + (pTempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA && pTempSpell->EffectApplyAuraName[j] == 8)) + SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_HEALING-1); + + //Make sure that this spell applies an aura + if (pTempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA) + SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_AURA-1); + } + } +} + +void ScriptedAI::DoResetThreat() +{ + if (!me->CanHaveThreatList() || me->getThreatManager().isThreatListEmpty()) + { + error_log("TSCR: DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = %d)", me->GetEntry()); + return; + } + + std::list& threatlist = me->getThreatManager().getThreatList(); + + for (std::list::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + Unit* pUnit = Unit::GetUnit((*me), (*itr)->getUnitGuid()); + + if (pUnit && DoGetThreat(pUnit)) + DoModifyThreatPercent(pUnit, -100); + } +} + +float ScriptedAI::DoGetThreat(Unit* pUnit) +{ + if (!pUnit) return 0.0f; + return me->getThreatManager().getThreat(pUnit); +} + +void ScriptedAI::DoModifyThreatPercent(Unit* pUnit, int32 pct) +{ + if (!pUnit) return; + me->getThreatManager().modifyThreatPercent(pUnit, pct); +} + +void ScriptedAI::DoTeleportTo(float fX, float fY, float fZ, uint32 uiTime) +{ + me->Relocate(fX, fY, fZ); + me->SendMonsterMove(fX, fY, fZ, uiTime); +} + +void ScriptedAI::DoTeleportTo(const float fPos[4]) +{ + me->NearTeleportTo(fPos[0], fPos[1], fPos[2], fPos[3]); +} + +void ScriptedAI::DoTeleportPlayer(Unit* pUnit, float fX, float fY, float fZ, float fO) +{ + if (!pUnit || pUnit->GetTypeId() != TYPEID_PLAYER) + { + if (pUnit) + error_log("TSCR: Creature %u (Entry: %u) Tried to teleport non-player unit (Type: %u GUID: %u) to x: %f y:%f z: %f o: %f. Aborted.", me->GetGUID(), me->GetEntry(), pUnit->GetTypeId(), pUnit->GetGUID(), fX, fY, fZ, fO); + return; + } + + CAST_PLR(pUnit)->TeleportTo(pUnit->GetMapId(), fX, fY, fZ, fO, TELE_TO_NOT_LEAVE_COMBAT); +} + +void ScriptedAI::DoTeleportAll(float fX, float fY, float fZ, float fO) +{ + Map *map = me->GetMap(); + if (!map->IsDungeon()) + return; + + Map::PlayerList const &PlayerList = map->GetPlayers(); + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + if (Player* i_pl = i->getSource()) + if (i_pl->isAlive()) + i_pl->TeleportTo(me->GetMapId(), fX, fY, fZ, fO, TELE_TO_NOT_LEAVE_COMBAT); +} + +Unit* ScriptedAI::DoSelectLowestHpFriendly(float fRange, uint32 uiMinHPDiff) +{ + Unit* pUnit = NULL; + Trinity::MostHPMissingInRange u_check(me, fRange, uiMinHPDiff); + Trinity::UnitLastSearcher searcher(me, pUnit, u_check); + me->VisitNearbyObject(fRange, searcher); + + return pUnit; +} + +std::list ScriptedAI::DoFindFriendlyCC(float fRange) +{ + std::list pList; + Trinity::FriendlyCCedInRange u_check(me, fRange); + Trinity::CreatureListSearcher searcher(me, pList, u_check); + me->VisitNearbyObject(fRange, searcher); + return pList; +} + +std::list ScriptedAI::DoFindFriendlyMissingBuff(float fRange, uint32 uiSpellid) +{ + std::list pList; + Trinity::FriendlyMissingBuffInRange u_check(me, fRange, uiSpellid); + Trinity::CreatureListSearcher searcher(me, pList, u_check); + me->VisitNearbyObject(fRange, searcher); + return pList; +} + +Player* ScriptedAI::GetPlayerAtMinimumRange(float fMinimumRange) +{ + Player* pPlayer = NULL; + + CellPair pair(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::PlayerAtMinimumRangeAway check(me, fMinimumRange); + Trinity::PlayerSearcher searcher(me, pPlayer, check); + TypeContainerVisitor, GridTypeMapContainer> visitor(searcher); + + cell.Visit(pair, visitor, *(me->GetMap())); + + return pPlayer; +} + +void ScriptedAI::SetEquipmentSlots(bool bLoadDefault, int32 uiMainHand, int32 uiOffHand, int32 uiRanged) +{ + if (bLoadDefault) + { + if (CreatureInfo const* pInfo = GetCreatureTemplateStore(me->GetEntry())) + me->LoadEquipment(pInfo->equipmentId,true); + + return; + } + + if (uiMainHand >= 0) + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(uiMainHand)); + + if (uiOffHand >= 0) + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(uiOffHand)); + + if (uiRanged >= 0) + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, uint32(uiRanged)); +} + +void ScriptedAI::SetCombatMovement(bool bCombatMove) +{ + m_bCombatMovement = bCombatMove; +} + +enum eNPCs +{ + NPC_BROODLORD = 12017, + NPC_VOID_REAVER = 19516, + NPC_JAN_ALAI = 23578, + NPC_SARTHARION = 28860 +}; + +// Hacklike storage used for misc creatures that are expected to evade of outside of a certain area. +// It is assumed the information is found elswehere and can be handled by the core. So far no luck finding such information/way to extract it. +bool ScriptedAI::EnterEvadeIfOutOfCombatArea(const uint32 uiDiff) +{ + if (m_uiEvadeCheckCooldown <= uiDiff) + m_uiEvadeCheckCooldown = 2500; + else + { + m_uiEvadeCheckCooldown -= uiDiff; + return false; + } + + if (me->IsInEvadeMode() || !me->getVictim()) + return false; + + float fX = me->GetPositionX(); + float fY = me->GetPositionY(); + float fZ = me->GetPositionZ(); + + switch(me->GetEntry()) + { + case NPC_BROODLORD: // broodlord (not move down stairs) + if (fZ > 448.60f) + return false; + break; + case NPC_VOID_REAVER: // void reaver (calculate from center of room) + if (me->GetDistance2d(432.59f, 371.93f) < 105.0f) + return false; + break; + case NPC_JAN_ALAI: // jan'alai (calculate by Z) + if (fZ > 12.0f) + return false; + break; + case NPC_SARTHARION: // sartharion (calculate box) + if (fX > 3218.86f && fX < 3275.69f && fY < 572.40f && fY > 484.68f) + return false; + break; + default: + error_log("TSCR: EnterEvadeIfOutOfCombatArea used for creature entry %u, but does not have any definition.", me->GetEntry()); + return false; + } + + EnterEvadeMode(); + return true; +} + +void Scripted_NoMovementAI::AttackStart(Unit* pWho) +{ + if (!pWho) + return; + + if (me->Attack(pWho, true)) + { + DoStartNoMovement(pWho); + } +} + +BossAI::BossAI(Creature *c, uint32 id) : ScriptedAI(c) +, bossId(id), summons(me), instance(c->GetInstanceData()) +, boundary(instance ? instance->GetBossBoundary(id) : NULL) +{ +} + +void BossAI::_Reset() +{ + if (!me->isAlive()) + return; + + events.Reset(); + summons.DespawnAll(); + if (instance) + instance->SetBossState(bossId, NOT_STARTED); +} + +void BossAI::_JustDied() +{ + events.Reset(); + summons.DespawnAll(); + if (instance) + { + instance->SetBossState(bossId, DONE); + instance->SaveToDB(); + } +} + +void BossAI::_EnterCombat() +{ + me->setActive(true); + DoZoneInCombat(); + if (instance) + instance->SetBossState(bossId, IN_PROGRESS); +} + +void BossAI::TeleportCheaters() +{ + float x, y, z; + me->GetPosition(x, y, z); + std::list &m_threatlist = me->getThreatManager().getThreatList(); + for (std::list::iterator itr = m_threatlist.begin(); itr != m_threatlist.end(); ++itr) + if ((*itr)->getTarget()->GetTypeId() == TYPEID_PLAYER && !CheckBoundary((*itr)->getTarget())) + (*itr)->getTarget()->NearTeleportTo(x, y, z, 0); +} + +bool BossAI::CheckBoundary(Unit *who) +{ + if (!boundary || !who) + return true; + + for (BossBoundaryMap::const_iterator itr = boundary->begin(); itr != boundary->end(); ++itr) + { + switch (itr->first) + { + case BOUNDARY_N: + if (me->GetPositionX() > itr->second) + return false; + break; + case BOUNDARY_S: + if (me->GetPositionX() < itr->second) + return false; + break; + case BOUNDARY_E: + if (me->GetPositionY() < itr->second) + return false; + break; + case BOUNDARY_W: + if (me->GetPositionY() > itr->second) + return false; + break; + case BOUNDARY_NW: + if (me->GetPositionX() + me->GetPositionY() > itr->second) + return false; + break; + case BOUNDARY_SE: + if (me->GetPositionX() + me->GetPositionY() < itr->second) + return false; + break; + case BOUNDARY_NE: + if (me->GetPositionX() - me->GetPositionY() > itr->second) + return false; + break; + case BOUNDARY_SW: + if (me->GetPositionX() - me->GetPositionY() < itr->second) + return false; + break; + } + } + + return true; +} + +void BossAI::JustSummoned(Creature *summon) +{ + summons.Summon(summon); + if (me->isInCombat()) + DoZoneInCombat(summon); +} + +void BossAI::SummonedCreatureDespawn(Creature *summon) +{ + summons.Despawn(summon); +} + +#define GOBJECT(x) (const_cast(GetGameObjectInfo(x))) + +void LoadOverridenSQLData() +{ + GameObjectInfo *goInfo; + + // Sunwell Plateau : Kalecgos : Spectral Rift + goInfo = GOBJECT(187055); + if (goInfo) + if (goInfo->type == GAMEOBJECT_TYPE_GOOBER) + goInfo->goober.lockId = 57; // need LOCKTYPE_QUICK_OPEN + + // Naxxramas : Sapphiron Birth + goInfo = GOBJECT(181356); + if (goInfo) + if (goInfo->type == GAMEOBJECT_TYPE_TRAP) + goInfo->trap.radius = 50; +} + +// SD2 grid searchers. +Creature *GetClosestCreatureWithEntry(WorldObject *pSource, uint32 uiEntry, float fMaxSearchRange, bool bAlive) +{ + return pSource->FindNearestCreature(uiEntry, fMaxSearchRange, bAlive); +} +GameObject *GetClosestGameObjectWithEntry(WorldObject *pSource, uint32 uiEntry, float fMaxSearchRange) +{ + return pSource->FindNearestGameObject(uiEntry, fMaxSearchRange); +} +void GetCreatureListWithEntryInGrid(std::list& lList, WorldObject *pSource, uint32 uiEntry, float fMaxSearchRange) +{ + return pSource->GetCreatureListWithEntryInGrid(lList, uiEntry, fMaxSearchRange); +} +void GetGameObjectListWithEntryInGrid(std::list& lList, WorldObject *pSource, uint32 uiEntry, float fMaxSearchRange) +{ + return pSource->GetGameObjectListWithEntryInGrid(lList, uiEntry, fMaxSearchRange); +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h new file mode 100644 index 00000000000..a7b8b530f66 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -0,0 +1,290 @@ +/* Copyright (C) 2008-2010 Trinity + * + * Thanks to the original authors: ScriptDev2 + * + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_CREATURE_H +#define SC_CREATURE_H + +#include "Creature.h" +#include "CreatureAI.h" +#include "CreatureAIImpl.h" +#include "InstanceData.h" + +#define SCRIPT_CAST_TYPE dynamic_cast +//#define SCRIPT_CAST_TYPE static_cast + +#define CAST_PLR(a) (SCRIPT_CAST_TYPE(a)) +#define CAST_CRE(a) (SCRIPT_CAST_TYPE(a)) +#define CAST_SUM(a) (SCRIPT_CAST_TYPE(a)) +#define CAST_PET(a) (SCRIPT_CAST_TYPE(a)) +#define CAST_AI(a,b) (SCRIPT_CAST_TYPE(b)) + +#define GET_SPELL(a) (const_cast(GetSpellStore()->LookupEntry(a))) + +class ScriptedInstance; + +class SummonList : public std::list +{ + public: + explicit SummonList(Creature* creature) : me(creature) {} + void Summon(Creature *summon) { push_back(summon->GetGUID()); } + void Despawn(Creature *summon) { remove(summon->GetGUID()); } + void DespawnEntry(uint32 entry); + void DespawnAll(); + void DoAction(uint32 entry, uint32 info); + void DoZoneInCombat(uint32 entry = 0); + private: + Creature *me; +}; + +struct ScriptedAI : public CreatureAI +{ + explicit ScriptedAI(Creature* pCreature); + virtual ~ScriptedAI() {} + + //************* + //CreatureAI Functions + //************* + + void AttackStartNoMove(Unit *pTarget); + + // Called at any Damage from any attacker (before damage apply) + void DamageTaken(Unit* /*pDone_by*/, uint32& /*uiDamage*/) {} + + //Called at World update tick + void UpdateAI(const uint32); + + //Called at creature death + void JustDied(Unit* /*who*/){} + + //Called at creature killing another unit + void KilledUnit(Unit* /*who*/){} + + // Called when the creature summon successfully other creature + void JustSummoned(Creature*) {} + + // Called when a summoned creature is despawned + void SummonedCreatureDespawn(Creature*) {} + + // Called when hit by a spell + void SpellHit(Unit* /*caster*/, const SpellEntry * /*spell*/) {} + + // Called when spell hits a target + void SpellHitTarget(Unit * /*pTarget*/, const SpellEntry * /*spell*/) {} + + //Called at waypoint reached or PointMovement end + void MovementInform(uint32 /*type*/, uint32 /*id*/){} + + // Called when AI is temporarily replaced or put back when possess is applied or removed + void OnPossess(bool /*apply*/) {} + + //************* + // Variables + //************* + + //Pointer to creature we are manipulating + Creature* me; + + //For fleeing + bool IsFleeing; + + //************* + //Pure virtual functions + //************* + + //Called at creature reset either by death or evade + void Reset() {} + + //Called at creature aggro either by MoveInLOS or Attack Start + void EnterCombat(Unit* /*who*/) {} + + //************* + //AI Helper Functions + //************* + + //Start movement toward victim + void DoStartMovement(Unit* pVictim, float fDistance = 0, float fAngle = 0); + + //Start no movement on victim + void DoStartNoMovement(Unit* pVictim); + + //Stop attack of current victim + void DoStopAttack(); + + //Cast spell by spell info + void DoCastSpell(Unit* pTarget, SpellEntry const* pSpellInfo, bool bTriggered = false); + + //Plays a sound to all nearby players + void DoPlaySoundToSet(WorldObject* pSource, uint32 sound); + + //Drops all threat to 0%. Does not remove players from the threat list + void DoResetThreat(); + + float DoGetThreat(Unit* u); + void DoModifyThreatPercent(Unit* pUnit, int32 pct); + + void DoTeleportTo(float fX, float fY, float fZ, uint32 uiTime = 0); + void DoTeleportTo(const float pos[4]); + + void DoAction(const int32 /*param*/) {} + + //Teleports a player without dropping threat (only teleports to same map) + void DoTeleportPlayer(Unit* pUnit, float fX, float fY, float fZ, float fO); + void DoTeleportAll(float fX, float fY, float fZ, float fO); + + //Returns friendly unit with the most amount of hp missing from max hp + Unit* DoSelectLowestHpFriendly(float fRange, uint32 uiMinHPDiff = 1); + + //Returns a list of friendly CC'd units within range + std::list DoFindFriendlyCC(float fRange); + + //Returns a list of all friendly units missing a specific buff within range + std::list DoFindFriendlyMissingBuff(float fRange, uint32 uiSpellId); + + //Return a player with at least minimumRange from me + Player* GetPlayerAtMinimumRange(float fMinimumRange); + + //Spawns a creature relative to me + Creature* DoSpawnCreature(uint32 uiId, float fX, float fY, float fZ, float fAngle, uint32 uiType, uint32 uiDespawntime); + + //Selects a unit from the creature's current aggro list + Unit* SelectUnit(SelectAggroTarget pTarget, uint32 uiPosition); + + bool HealthBelowPct(uint32 pct) const { return me->GetHealth() * 100 < me->GetMaxHealth() * pct; } + + //Returns spells that meet the specified criteria from the creatures spell list + SpellEntry const* SelectSpell(Unit* Target, uint32 School, uint32 Mechanic, SelectTargetType Targets, uint32 PowerCostMin, uint32 PowerCostMax, float RangeMin, float RangeMax, SelectEffect Effect); + + //Checks if you can cast the specified spell + bool CanCast(Unit* pTarget, SpellEntry const* pSpell, bool bTriggered = false); + + void SetEquipmentSlots(bool bLoadDefault, int32 uiMainHand = EQUIP_NO_CHANGE, int32 uiOffHand = EQUIP_NO_CHANGE, int32 uiRanged = EQUIP_NO_CHANGE); + + //Generally used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims + void SetCombatMovement(bool CombatMove); + bool IsCombatMovement() { return m_bCombatMovement; } + + bool EnterEvadeIfOutOfCombatArea(const uint32 uiDiff); + + // return true for heroic mode. i.e. + // - for dungeon in mode 10-heroic, + // - for raid in mode 10-Heroic + // - for raid in mode 25-heroic + // DO NOT USE to check raid in mode 25-normal. + bool IsHeroic() { return m_heroicMode; } + + // return the dungeon or raid difficulty + Difficulty getDifficulty() { return m_difficulty; } + + template inline + const T& DUNGEON_MODE(const T& normal5, const T& heroic10) + { + switch(m_difficulty) + { + case DUNGEON_DIFFICULTY_NORMAL: + return normal5; + case DUNGEON_DIFFICULTY_HEROIC: + return heroic10; + } + + return heroic10; + } + + template inline + const T& RAID_MODE(const T& normal10, const T& normal25) + { + switch(m_difficulty) + { + case RAID_DIFFICULTY_10MAN_NORMAL: + return normal10; + case RAID_DIFFICULTY_25MAN_NORMAL: + return normal25; + } + + return normal25; + } + + template inline + const T& RAID_MODE(const T& normal10, const T& normal25, const T& heroic10, const T& heroic25) + { + switch(m_difficulty) + { + case RAID_DIFFICULTY_10MAN_NORMAL: + return normal10; + case RAID_DIFFICULTY_25MAN_NORMAL: + return normal25; + case RAID_DIFFICULTY_10MAN_HEROIC: + return heroic10; + case RAID_DIFFICULTY_25MAN_HEROIC: + return heroic25; + } + + return heroic25; + } + + private: + bool m_bCombatMovement; + uint32 m_uiEvadeCheckCooldown; + + bool m_heroicMode; + Difficulty m_difficulty; +}; + +struct Scripted_NoMovementAI : public ScriptedAI +{ + Scripted_NoMovementAI(Creature* creature) : ScriptedAI(creature) {} + virtual ~Scripted_NoMovementAI() {} + + //Called at each attack of me by any victim + void AttackStart(Unit* who); +}; + +struct BossAI : public ScriptedAI +{ + BossAI(Creature *c, uint32 id); + virtual ~BossAI() {} + + const uint32 bossId; + EventMap events; + SummonList summons; + InstanceData * const instance; + const BossBoundaryMap * const boundary; + + void JustSummoned(Creature *summon); + void SummonedCreatureDespawn(Creature *summon); + + void UpdateAI(const uint32 diff) = 0; + + void Reset() { _Reset(); } + void EnterCombat(Unit * /*who*/) { _EnterCombat(); } + void JustDied(Unit * /*killer*/) { _JustDied(); } + void JustReachedHome() { me->setActive(false); } + + protected: + void _Reset(); + void _EnterCombat(); + void _JustDied(); + void _JustReachedHome() { me->setActive(false); } + + bool CheckInRoom() + { + if (CheckBoundary(me)) + return true; + EnterEvadeMode(); + return false; + } + bool CheckBoundary(Unit *who); + void TeleportCheaters(); +}; + +// SD2 grid searchers. +Creature *GetClosestCreatureWithEntry(WorldObject *pSource, uint32 uiEntry, float fMaxSearchRange, bool bAlive = true); +GameObject *GetClosestGameObjectWithEntry(WorldObject *pSource, uint32 uiEntry, float fMaxSearchRange); +void GetCreatureListWithEntryInGrid(std::list& lList, WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange); +void GetGameObjectListWithEntryInGrid(std::list& lList, WorldObject* pSource, uint32 uiEntry, float fMaxSearchRange); + +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp new file mode 100644 index 00000000000..8b7aca888e1 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -0,0 +1,506 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +/* ScriptData +SDName: Npc_EscortAI +SD%Complete: 100 +SDComment: +SDCategory: Npc +EndScriptData */ + +#include "ScriptedPch.h" +#include "ScriptedEscortAI.h" + +enum ePoints +{ + POINT_LAST_POINT = 0xFFFFFF, + POINT_HOME = 0xFFFFFE +}; + +npc_escortAI::npc_escortAI(Creature* pCreature) : ScriptedAI(pCreature), + m_uiPlayerGUID(0), + MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE), + m_uiPlayerCheckTimer(1000), + m_uiWPWaitTimer(2500), + m_uiEscortState(STATE_ESCORT_NONE), + m_bIsActiveAttacker(true), + m_bIsRunning(false), + DespawnAtEnd(true), + DespawnAtFar(true), + m_pQuestForEscort(NULL), + m_bCanInstantRespawn(false), + m_bCanReturnToStart(false), + ScriptWP(false) +{} + +void npc_escortAI::AttackStart(Unit* pWho) +{ + if (!pWho) + return; + + if (me->Attack(pWho, true)) + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + me->GetMotionMaster()->MovementExpired(); + + if (IsCombatMovement()) + me->GetMotionMaster()->MoveChase(pWho); + } +} + +//see followerAI +bool npc_escortAI::AssistPlayerInCombat(Unit* pWho) +{ + if (!pWho || !pWho->getVictim()) + return false; + + //experimental (unknown) flag not present + if (!(me->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13)) + return false; + + //not a player + if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()) + return false; + + //never attack friendly + if (me->IsFriendlyTo(pWho)) + return false; + + //too far away and no free sight? + if (me->IsWithinDistInMap(pWho, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(pWho)) + { + //already fighting someone? + if (!me->getVictim()) + { + AttackStart(pWho); + return true; + } + else + { + pWho->SetInCombatWith(me); + me->AddThreat(pWho, 0.0f); + return true; + } + } + + return false; +} + +void npc_escortAI::MoveInLineOfSight(Unit* pWho) +{ + if (!me->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(me)) + { + if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(pWho)) + return; + + if (!me->canFly() && me->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + return; + + if (me->IsHostileTo(pWho)) + { + float fAttackRadius = me->GetAttackDistance(pWho); + if (me->IsWithinDistInMap(pWho, fAttackRadius) && me->IsWithinLOSInMap(pWho)) + { + if (!me->getVictim()) + { + pWho->RemoveAurasDueToSpell(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + else if (me->GetMap()->IsDungeon()) + { + pWho->SetInCombatWith(me); + me->AddThreat(pWho, 0.0f); + } + } + } + } +} + +void npc_escortAI::JustDied(Unit* /*pKiller*/) +{ + if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort) + return; + + if (Player* pPlayer = GetPlayerForEscort()) + { + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + pMember->FailQuest(m_pQuestForEscort->GetQuestId()); + } + } + } + else + { + if (pPlayer->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + pPlayer->FailQuest(m_pQuestForEscort->GetQuestId()); + } + } +} + +void npc_escortAI::JustRespawned() +{ + m_uiEscortState = STATE_ESCORT_NONE; + + if (!IsCombatMovement()) + SetCombatMovement(true); + + //add a small delay before going to first waypoint, normal in near all cases + m_uiWPWaitTimer = 2500; + + if (me->getFaction() != me->GetCreatureInfo()->faction_A) + me->RestoreFaction(); + + Reset(); +} + +void npc_escortAI::ReturnToLastPoint() +{ + float x, y, z, o; + me->GetHomePosition(x, y, z, o); + me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z); +} + +void npc_escortAI::EnterEvadeMode() +{ + me->RemoveAllAuras(); + me->DeleteThreatList(); + me->CombatStop(true); + me->SetLootRecipient(NULL); + + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + AddEscortState(STATE_ESCORT_RETURNING); + ReturnToLastPoint(); + debug_log("TSCR: EscortAI has left combat and is now returning to last point"); + } + else + { + me->GetMotionMaster()->MoveTargetedHome(); + Reset(); + } +} + +bool npc_escortAI::IsPlayerOrGroupInRange() +{ + if (Player* pPlayer = GetPlayerForEscort()) + { + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + Player* pMember = pRef->getSource(); + + if (pMember && me->IsWithinDistInMap(pMember, GetMaxPlayerDistance())) + { + return true; + break; + } + } + } + else + { + if (me->IsWithinDistInMap(pPlayer, GetMaxPlayerDistance())) + return true; + } + } + return false; +} + +void npc_escortAI::UpdateAI(const uint32 uiDiff) +{ + //Waypoint Updating + if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->getVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING)) + { + if (m_uiWPWaitTimer <= uiDiff) + { + //End of the line + if (CurrentWP == WaypointList.end()) + { + if (DespawnAtEnd) + { + debug_log("TSCR: EscortAI reached end of waypoints"); + + if (m_bCanReturnToStart) + { + float fRetX, fRetY, fRetZ; + me->GetRespawnCoord(fRetX, fRetY, fRetZ); + + me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ); + + m_uiWPWaitTimer = 0; + + debug_log("TSCR: EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ); + return; + } + + if (m_bCanInstantRespawn) + { + me->setDeathState(JUST_DIED); + me->Respawn(); + } + else + me->ForcedDespawn(); + + return; + } + else + { + debug_log("TSCR: EscortAI reached end of waypoints with Despawn off"); + + return; + } + } + + if (!HasEscortState(STATE_ESCORT_PAUSED)) + { + me->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); + debug_log("TSCR: EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); + + WaypointStart(CurrentWP->id); + + m_uiWPWaitTimer = 0; + } + } + else + m_uiWPWaitTimer -= uiDiff; + } + + //Check if player or any member of his group is within range + if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !me->getVictim() && !HasEscortState(STATE_ESCORT_RETURNING)) + { + if (m_uiPlayerCheckTimer <= uiDiff) + { + if (DespawnAtFar && !IsPlayerOrGroupInRange()) + { + debug_log("TSCR: EscortAI failed because player/group was to far away or not found"); + + if (m_bCanInstantRespawn) + { + me->setDeathState(JUST_DIED); + me->Respawn(); + } + else + me->ForcedDespawn(); + + return; + } + + m_uiPlayerCheckTimer = 1000; + } + else + m_uiPlayerCheckTimer -= uiDiff; + } + + UpdateEscortAI(uiDiff); +} + +void npc_escortAI::UpdateEscortAI(const uint32 /*uiDiff*/) +{ + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); +} + +void npc_escortAI::MovementInform(uint32 uiMoveType, uint32 uiPointId) +{ + if (uiMoveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + //Combat start position reached, continue waypoint movement + if (uiPointId == POINT_LAST_POINT) + { + debug_log("TSCR: EscortAI has returned to original position before combat"); + + if (m_bIsRunning && me->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE)) + me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + else if (!m_bIsRunning && !me->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE)) + me->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + + RemoveEscortState(STATE_ESCORT_RETURNING); + + if (!m_uiWPWaitTimer) + m_uiWPWaitTimer = 1; + } + else if (uiPointId == POINT_HOME) + { + debug_log("TSCR: EscortAI has returned to original home location and will continue from beginning of waypoint list."); + + CurrentWP = WaypointList.begin(); + m_uiWPWaitTimer = 1; + } + else + { + //Make sure that we are still on the right waypoint + if (CurrentWP->id != uiPointId) + { + error_log("TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", uiPointId, CurrentWP->id, me->GetEntry()); + return; + } + + debug_log("TSCR: EscortAI Waypoint %u reached", CurrentWP->id); + + //Call WP function + WaypointReached(CurrentWP->id); + + m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1; + + ++CurrentWP; + } +} + +/* +void npc_escortAI::OnPossess(bool apply) +{ + // We got possessed in the middle of being escorted, store the point + // where we left off to come back to when possess is removed + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + if (apply) + me->GetPosition(LastPos.x, LastPos.y, LastPos.z); + else + { + Returning = true; + me->GetMotionMaster()->MovementExpired(); + me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z); + } + } +} +*/ + +void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs) +{ + Escort_Waypoint t(id, x, y, z, WaitTimeMs); + + WaypointList.push_back(t); + + // i think SD2 no longer uses this function + ScriptWP = true; + /*PointMovement wp; + wp.m_uiCreatureEntry = me->GetEntry(); + wp.m_uiPointId = id; + wp.m_fX = x; + wp.m_fY = y; + wp.m_fZ = z; + wp.m_uiWaitTime = WaitTimeMs; + PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/ +} + +void npc_escortAI::FillPointMovementListForCreature() +{ + std::vector const &pPointsEntries = pSystemMgr.GetPointMoveList(me->GetEntry()); + + if (pPointsEntries.empty()) + return; + + std::vector::const_iterator itr; + + for (itr = pPointsEntries.begin(); itr != pPointsEntries.end(); ++itr) + { + Escort_Waypoint pPoint(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime); + WaypointList.push_back(pPoint); + } +} + +void npc_escortAI::SetRun(bool bRun) +{ + if (bRun) + { + if (!m_bIsRunning) + me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + else + debug_log("TSCR: EscortAI attempt to set run mode, but is already running."); + } + else + { + if (m_bIsRunning) + me->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + else + debug_log("TSCR: EscortAI attempt to set walk mode, but is already walking."); + } + m_bIsRunning = bRun; +} + +//TODO: get rid of this many variables passed in function. +void npc_escortAI::Start(bool bIsActiveAttacker, bool bRun, uint64 uiPlayerGUID, const Quest* pQuest, bool bInstantRespawn, bool bCanLoopPath) +{ + if (me->getVictim()) + { + error_log("TSCR ERROR: EscortAI attempt to Start while in combat. Scriptname: %s, creature entry: %u", me->GetScriptName().c_str(), me->GetEntry()); + return; + } + + if (HasEscortState(STATE_ESCORT_ESCORTING)) + { + error_log("TSCR: EscortAI attempt to Start while already escorting."); + return; + } + + if (!ScriptWP) // sd2 never adds wp in script, but tc does + { + + if (!WaypointList.empty()) + WaypointList.clear(); + + FillPointMovementListForCreature(); + + } + + if (WaypointList.empty()) + { + error_db_log("TSCR: EscortAI Start with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).", pQuest ? pQuest->GetQuestId() : 0); + return; + } + + //set variables + m_bIsActiveAttacker = bIsActiveAttacker; + m_bIsRunning = bRun; + + m_uiPlayerGUID = uiPlayerGUID; + m_pQuestForEscort = pQuest; + + m_bCanInstantRespawn = bInstantRespawn; + m_bCanReturnToStart = bCanLoopPath; + + if (m_bCanReturnToStart && m_bCanInstantRespawn) + debug_log("TSCR: EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn."); + + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + me->GetMotionMaster()->MovementExpired(); + me->GetMotionMaster()->MoveIdle(); + debug_log("TSCR: EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); + } + + //disable npcflags + me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + + debug_log("TSCR: EscortAI started with %u waypoints. ActiveAttacker = %d, Run = %d, PlayerGUID = %u", WaypointList.size(), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID); + + CurrentWP = WaypointList.begin(); + + //Set initial speed + if (m_bIsRunning) + me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + else + me->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + + AddEscortState(STATE_ESCORT_ESCORTING); +} + +void npc_escortAI::SetEscortPaused(bool bPaused) +{ + if (!HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + if (bPaused) + AddEscortState(STATE_ESCORT_PAUSED); + else + RemoveEscortState(STATE_ESCORT_PAUSED); +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h new file mode 100644 index 00000000000..807a5644eb8 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -0,0 +1,116 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_ESCORTAI_H +#define SC_ESCORTAI_H + +#include "ScriptSystem.h" + +#define DEFAULT_MAX_PLAYER_DISTANCE 50 + +struct Escort_Waypoint +{ + Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w) + { + id = _id; + x = _x; + y = _y; + z = _z; + WaitTimeMs = _w; + } + + uint32 id; + float x; + float y; + float z; + uint32 WaitTimeMs; +}; + +enum eEscortState +{ + STATE_ESCORT_NONE = 0x000, //nothing in progress + STATE_ESCORT_ESCORTING = 0x001, //escort are in progress + STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat + STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed +}; + +struct npc_escortAI : public ScriptedAI +{ + public: + explicit npc_escortAI(Creature* pCreature); + ~npc_escortAI() {} + + // CreatureAI functions + void AttackStart(Unit* who); + + void MoveInLineOfSight(Unit* who); + + void JustDied(Unit*); + + void JustRespawned(); + + void ReturnToLastPoint(); + + void EnterEvadeMode(); + + void UpdateAI(const uint32); //the "internal" update, calls UpdateEscortAI() + virtual void UpdateEscortAI(const uint32); //used when it's needed to add code in update (abilities, scripted events, etc) + + void MovementInform(uint32, uint32); + + // EscortAI functions + void AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs = 0); + + virtual void WaypointReached(uint32 uiPointId) = 0; + virtual void WaypointStart(uint32 /*uiPointId*/) {} + + void Start(bool bIsActiveAttacker = true, bool bRun = false, uint64 uiPlayerGUID = 0, const Quest* pQuest = NULL, bool bInstantRespawn = false, bool bCanLoopPath = false); + + void SetRun(bool bRun = true); + void SetEscortPaused(bool uPaused); + + bool HasEscortState(uint32 uiEscortState) { return (m_uiEscortState & uiEscortState); } + virtual bool IsEscorted() { return (m_uiEscortState & STATE_ESCORT_ESCORTING); } + + void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; } + float GetMaxPlayerDistance() { return MaxPlayerDistance; } + + void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; } + void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; } + bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override + void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; } + uint64 GetEventStarterGUID() { return m_uiPlayerGUID; } + + protected: + Player* GetPlayerForEscort() { return (Player*)Unit::GetUnit(*me, m_uiPlayerGUID); } + + private: + bool AssistPlayerInCombat(Unit* pWho); + bool IsPlayerOrGroupInRange(); + void FillPointMovementListForCreature(); + + void AddEscortState(uint32 uiEscortState) { m_uiEscortState |= uiEscortState; } + void RemoveEscortState(uint32 uiEscortState) { m_uiEscortState &= ~uiEscortState; } + + uint64 m_uiPlayerGUID; + uint32 m_uiWPWaitTimer; + uint32 m_uiPlayerCheckTimer; + uint32 m_uiEscortState; + float MaxPlayerDistance; + + const Quest* m_pQuestForEscort; //generally passed in Start() when regular escort script. + + std::list WaypointList; + std::list::iterator CurrentWP; + + bool m_bIsActiveAttacker; //obsolete, determined by faction. + bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK) + bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used) + bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests. + bool DespawnAtEnd; + bool DespawnAtFar; + bool ScriptWP; +}; +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp new file mode 100644 index 00000000000..4f1543dc778 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -0,0 +1,387 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +/* ScriptData +SDName: FollowerAI +SD%Complete: 50 +SDComment: This AI is under development +SDCategory: Npc +EndScriptData */ + +#include "ScriptedPch.h" +#include "ScriptedFollowerAI.h" + +const float MAX_PLAYER_DISTANCE = 100.0f; + +enum ePoints +{ + POINT_COMBAT_START = 0xFFFFFF +}; + +FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature), + m_uiLeaderGUID(0), + m_pQuestForFollow(NULL), + m_uiUpdateFollowTimer(2500), + m_uiFollowState(STATE_FOLLOW_NONE) +{} + +void FollowerAI::AttackStart(Unit* pWho) +{ + if (!pWho) + return; + + if (me->Attack(pWho, true)) + { + me->AddThreat(pWho, 0.0f); + me->SetInCombatWith(pWho); + pWho->SetInCombatWith(me); + + if (me->hasUnitState(UNIT_STAT_FOLLOW)) + me->clearUnitState(UNIT_STAT_FOLLOW); + + if (IsCombatMovement()) + me->GetMotionMaster()->MoveChase(pWho); + } +} + +//This part provides assistance to a player that are attacked by pWho, even if out of normal aggro range +//It will cause me to attack pWho that are attacking _any_ player (which has been confirmed may happen also on offi) +//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate. +bool FollowerAI::AssistPlayerInCombat(Unit* pWho) +{ + if (!pWho || !pWho->getVictim()) + return false; + + //experimental (unknown) flag not present + if (!(me->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13)) + return false; + + //not a player + if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()) + return false; + + //never attack friendly + if (me->IsFriendlyTo(pWho)) + return false; + + //too far away and no free sight? + if (me->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(pWho)) + { + //already fighting someone? + if (!me->getVictim()) + { + AttackStart(pWho); + return true; + } + else + { + pWho->SetInCombatWith(me); + me->AddThreat(pWho, 0.0f); + return true; + } + } + + return false; +} + +void FollowerAI::MoveInLineOfSight(Unit* pWho) +{ + if (!me->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(me)) + { + if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho)) + return; + + if (!me->canFly() && me->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE) + return; + + if (me->IsHostileTo(pWho)) + { + float fAttackRadius = me->GetAttackDistance(pWho); + if (me->IsWithinDistInMap(pWho, fAttackRadius) && me->IsWithinLOSInMap(pWho)) + { + if (!me->getVictim()) + { + pWho->RemoveAurasDueToSpell(SPELL_AURA_MOD_STEALTH); + AttackStart(pWho); + } + else if (me->GetMap()->IsDungeon()) + { + pWho->SetInCombatWith(me); + me->AddThreat(pWho, 0.0f); + } + } + } + } +} + +void FollowerAI::JustDied(Unit* /*pKiller*/) +{ + if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow) + return; + + //TODO: need a better check for quests with time limit. + if (Player* pPlayer = GetLeaderForFollower()) + { + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + if (Player* pMember = pRef->getSource()) + { + if (pMember->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + pMember->FailQuest(m_pQuestForFollow->GetQuestId()); + } + } + } + else + { + if (pPlayer->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE) + pPlayer->FailQuest(m_pQuestForFollow->GetQuestId()); + } + } +} + +void FollowerAI::JustRespawned() +{ + m_uiFollowState = STATE_FOLLOW_NONE; + + if (!IsCombatMovement()) + SetCombatMovement(true); + + if (me->getFaction() != me->GetCreatureInfo()->faction_A) + me->setFaction(me->GetCreatureInfo()->faction_A); + + Reset(); +} + +void FollowerAI::EnterEvadeMode() +{ + me->RemoveAllAuras(); + me->DeleteThreatList(); + me->CombatStop(true); + me->SetLootRecipient(NULL); + + if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition."); + + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + float fPosX, fPosY, fPosZ; + me->GetPosition(fPosX, fPosY, fPosZ); + me->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ); + } + } + else + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + me->GetMotionMaster()->MoveTargetedHome(); + } + + Reset(); +} + +void FollowerAI::UpdateAI(const uint32 uiDiff) +{ + if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->getVictim()) + { + if (m_uiUpdateFollowTimer <= uiDiff) + { + if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT)) + { + debug_log("TSCR: FollowerAI is set completed, despawns."); + me->ForcedDespawn(); + return; + } + + bool bIsMaxRangeExceeded = true; + + if (Player* pPlayer = GetLeaderForFollower()) + { + if (HasFollowState(STATE_FOLLOW_RETURNING)) + { + debug_log("TSCR: FollowerAI is returning to leader."); + + RemoveFollowState(STATE_FOLLOW_RETURNING); + me->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + return; + } + + if (Group* pGroup = pPlayer->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + Player* pMember = pRef->getSource(); + + if (pMember && me->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE)) + { + bIsMaxRangeExceeded = false; + break; + } + } + } + else + { + if (me->IsWithinDistInMap(pPlayer, MAX_PLAYER_DISTANCE)) + bIsMaxRangeExceeded = false; + } + } + + if (bIsMaxRangeExceeded) + { + debug_log("TSCR: FollowerAI failed because player/group was to far away or not found"); + me->ForcedDespawn(); + return; + } + + m_uiUpdateFollowTimer = 1000; + } + else + m_uiUpdateFollowTimer -= uiDiff; + } + + UpdateFollowerAI(uiDiff); +} + +void FollowerAI::UpdateFollowerAI(const uint32 /*uiDiff*/) +{ + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); +} + +void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId) +{ + if (uiMotionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS)) + return; + + if (uiPointId == POINT_COMBAT_START) + { + if (GetLeaderForFollower()) + { + if (!HasFollowState(STATE_FOLLOW_PAUSED)) + AddFollowState(STATE_FOLLOW_RETURNING); + } + else + me->ForcedDespawn(); + } +} + +void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest) +{ + if (me->getVictim()) + { + debug_log("TSCR: FollowerAI attempt to StartFollow while in combat."); + return; + } + + if (HasFollowState(STATE_FOLLOW_INPROGRESS)) + { + error_log("TSCR: FollowerAI attempt to StartFollow while already following."); + return; + } + + //set variables + m_uiLeaderGUID = pLeader->GetGUID(); + + if (uiFactionForFollower) + me->setFaction(uiFactionForFollower); + + m_pQuestForFollow = pQuest; + + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + debug_log("TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle."); + } + + me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + + AddFollowState(STATE_FOLLOW_INPROGRESS); + + me->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + + debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID); +} + +Player* FollowerAI::GetLeaderForFollower() +{ + if (Player* pLeader = Unit::GetPlayer(m_uiLeaderGUID)) + { + if (pLeader->isAlive()) + return pLeader; + else + { + if (Group* pGroup = pLeader->GetGroup()) + { + for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next()) + { + Player* pMember = pRef->getSource(); + + if (pMember && pMember->isAlive() && me->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE)) + { + debug_log("TSCR: FollowerAI GetLeader changed and returned new leader."); + m_uiLeaderGUID = pMember->GetGUID(); + return pMember; + break; + } + } + } + } + } + + debug_log("TSCR: FollowerAI GetLeader can not find suitable leader."); + return NULL; +} + +void FollowerAI::SetFollowComplete(bool bWithEndEvent) +{ + if (me->hasUnitState(UNIT_STAT_FOLLOW)) + { + me->clearUnitState(UNIT_STAT_FOLLOW); + + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + } + + if (bWithEndEvent) + AddFollowState(STATE_FOLLOW_POSTEVENT); + else + { + if (HasFollowState(STATE_FOLLOW_POSTEVENT)) + RemoveFollowState(STATE_FOLLOW_POSTEVENT); + } + + AddFollowState(STATE_FOLLOW_COMPLETE); +} + +void FollowerAI::SetFollowPaused(bool bPaused) +{ + if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE)) + return; + + if (bPaused) + { + AddFollowState(STATE_FOLLOW_PAUSED); + + if (me->hasUnitState(UNIT_STAT_FOLLOW)) + { + me->clearUnitState(UNIT_STAT_FOLLOW); + + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + } + } + else + { + RemoveFollowState(STATE_FOLLOW_PAUSED); + + if (Player* pLeader = GetLeaderForFollower()) + me->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h new file mode 100644 index 00000000000..d352141e3e9 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_FOLLOWERAI_H +#define SC_FOLLOWERAI_H + +#include "ScriptSystem.h" + +enum eFollowState +{ + STATE_FOLLOW_NONE = 0x000, + STATE_FOLLOW_INPROGRESS = 0x001, //must always have this state for any follow + STATE_FOLLOW_RETURNING = 0x002, //when returning to combat start after being in combat + STATE_FOLLOW_PAUSED = 0x004, //disables following + STATE_FOLLOW_COMPLETE = 0x008, //follow is completed and may end + STATE_FOLLOW_PREEVENT = 0x010, //not implemented (allow pre event to run, before follow is initiated) + STATE_FOLLOW_POSTEVENT = 0x020 //can be set at complete and allow post event to run +}; + +class FollowerAI : public ScriptedAI +{ + public: + explicit FollowerAI(Creature* pCreature); + ~FollowerAI() {} + + //virtual void WaypointReached(uint32 uiPointId) = 0; + + void MovementInform(uint32 uiMotionType, uint32 uiPointId); + + void AttackStart(Unit*); + + void MoveInLineOfSight(Unit*); + + void EnterEvadeMode(); + + void JustDied(Unit*); + + void JustRespawned(); + + void UpdateAI(const uint32); //the "internal" update, calls UpdateFollowerAI() + virtual void UpdateFollowerAI(const uint32); //used when it's needed to add code in update (abilities, scripted events, etc) + + void StartFollow(Player* pPlayer, uint32 uiFactionForFollower = 0, const Quest* pQuest = NULL); + + void SetFollowPaused(bool bPaused); //if special event require follow mode to hold/resume during the follow + void SetFollowComplete(bool bWithEndEvent = false); + + bool HasFollowState(uint32 uiFollowState) { return (m_uiFollowState & uiFollowState); } + + protected: + Player* GetLeaderForFollower(); + + private: + void AddFollowState(uint32 uiFollowState) { m_uiFollowState |= uiFollowState; } + void RemoveFollowState(uint32 uiFollowState) { m_uiFollowState &= ~uiFollowState; } + + bool AssistPlayerInCombat(Unit* pWho); + + uint64 m_uiLeaderGUID; + uint32 m_uiUpdateFollowTimer; + uint32 m_uiFollowState; + + const Quest* m_pQuestForFollow; //normally we have a quest +}; + +#endif diff --git a/src/server/game/AI/ScriptedAI/ScriptedGossip.h b/src/server/game/AI/ScriptedAI/ScriptedGossip.h new file mode 100644 index 00000000000..6f1da291c45 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedGossip.h @@ -0,0 +1,187 @@ +/* Copyright (C) 2008-2010 Trinity + * + * Thanks to the original authors: ScriptDev2 + * + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_GOSSIP_H +#define SC_GOSSIP_H + +#include "Player.h" +#include "GossipDef.h" +#include "QuestDef.h" + +// Gossip Item Text +#define GOSSIP_TEXT_BROWSE_GOODS "I'd like to browse your goods." +#define GOSSIP_TEXT_TRAIN "Train me!" + +#define GOSSIP_TEXT_BANK "The bank" +#define GOSSIP_TEXT_IRONFORGE_BANK "The bank of Ironforge" +#define GOSSIP_TEXT_STORMWIND_BANK "The bank of Stormwind" +#define GOSSIP_TEXT_WINDRIDER "The wind rider master" +#define GOSSIP_TEXT_GRYPHON "The gryphon master" +#define GOSSIP_TEXT_BATHANDLER "The bat handler" +#define GOSSIP_TEXT_HIPPOGRYPH "The hippogryph master" +#define GOSSIP_TEXT_ZEPPLINMASTER "The zeppelin master" +#define GOSSIP_TEXT_DEEPRUNTRAM "The Deeprun Tram" +#define GOSSIP_TEXT_FERRY "The Rut'theran Ferry" +#define GOSSIP_TEXT_FLIGHTMASTER "The flight master" +#define GOSSIP_TEXT_AUCTIONHOUSE "The auction house" +#define GOSSIP_TEXT_GUILDMASTER "The guild master" +#define GOSSIP_TEXT_INN "The inn" +#define GOSSIP_TEXT_MAILBOX "The mailbox" +#define GOSSIP_TEXT_STABLEMASTER "The stable master" +#define GOSSIP_TEXT_WEAPONMASTER "The weapon master" +#define GOSSIP_TEXT_OFFICERS "The officers' lounge" +#define GOSSIP_TEXT_BATTLEMASTER "The battlemaster" +#define GOSSIP_TEXT_BARBER "Barber" +#define GOSSIP_TEXT_CLASSTRAINER "A class trainer" +#define GOSSIP_TEXT_PROFTRAINER "A profession trainer" +#define GOSSIP_TEXT_LEXICON "Lexicon of Power" + +#define GOSSIP_TEXT_ALTERACVALLEY "Alterac Valley" +#define GOSSIP_TEXT_ARATHIBASIN "Arathi Basin" +#define GOSSIP_TEXT_WARSONGULCH "Warsong Gulch" +#define GOSSIP_TEXT_ARENA "Arena" +#define GOSSIP_TEXT_EYEOFTHESTORM "Eye of The Storm" +#define GOSSIP_TEXT_STRANDOFANCIENT "Strand of the Ancients" + +#define GOSSIP_TEXT_DEATH_KNIGHT "Death Knight" +#define GOSSIP_TEXT_DRUID "Druid" +#define GOSSIP_TEXT_HUNTER "Hunter" +#define GOSSIP_TEXT_PRIEST "Priest" +#define GOSSIP_TEXT_ROGUE "Rogue" +#define GOSSIP_TEXT_WARRIOR "Warrior" +#define GOSSIP_TEXT_PALADIN "Paladin" +#define GOSSIP_TEXT_SHAMAN "Shaman" +#define GOSSIP_TEXT_MAGE "Mage" +#define GOSSIP_TEXT_WARLOCK "Warlock" + +#define GOSSIP_TEXT_ALCHEMY "Alchemy" +#define GOSSIP_TEXT_BLACKSMITHING "Blacksmithing" +#define GOSSIP_TEXT_COOKING "Cooking" +#define GOSSIP_TEXT_ENCHANTING "Enchanting" +#define GOSSIP_TEXT_ENGINEERING "Engineering" +#define GOSSIP_TEXT_FIRSTAID "First Aid" +#define GOSSIP_TEXT_HERBALISM "Herbalism" +#define GOSSIP_TEXT_INSCRIPTION "Inscription" +#define GOSSIP_TEXT_JEWELCRAFTING "Jewelcrafting" +#define GOSSIP_TEXT_LEATHERWORKING "Leatherworking" +#define GOSSIP_TEXT_TAILORING "Tailoring" +#define GOSSIP_TEXT_MINING "Mining" +#define GOSSIP_TEXT_FISHING "Fishing" +#define GOSSIP_TEXT_SKINNING "Skinning" + +enum eTradeskill +{ +// Skill defines + TRADESKILL_ALCHEMY = 1, + TRADESKILL_BLACKSMITHING = 2, + TRADESKILL_COOKING = 3, + TRADESKILL_ENCHANTING = 4, + TRADESKILL_ENGINEERING = 5, + TRADESKILL_FIRSTAID = 6, + TRADESKILL_HERBALISM = 7, + TRADESKILL_LEATHERWORKING = 8, + TRADESKILL_POISONS = 9, + TRADESKILL_TAILORING = 10, + TRADESKILL_MINING = 11, + TRADESKILL_FISHING = 12, + TRADESKILL_SKINNING = 13, + TRADESKILL_JEWLCRAFTING = 14, + TRADESKILL_INSCRIPTION = 15, + + TRADESKILL_LEVEL_NONE = 0, + TRADESKILL_LEVEL_APPRENTICE = 1, + TRADESKILL_LEVEL_JOURNEYMAN = 2, + TRADESKILL_LEVEL_EXPERT = 3, + TRADESKILL_LEVEL_ARTISAN = 4, + TRADESKILL_LEVEL_MASTER = 5, + TRADESKILL_LEVEL_GRAND_MASTER = 6, + +// Gossip defines + GOSSIP_ACTION_TRADE = 1, + GOSSIP_ACTION_TRAIN = 2, + GOSSIP_ACTION_TAXI = 3, + GOSSIP_ACTION_GUILD = 4, + GOSSIP_ACTION_BATTLE = 5, + GOSSIP_ACTION_BANK = 6, + GOSSIP_ACTION_INN = 7, + GOSSIP_ACTION_HEAL = 8, + GOSSIP_ACTION_TABARD = 9, + GOSSIP_ACTION_AUCTION = 10, + GOSSIP_ACTION_INN_INFO = 11, + GOSSIP_ACTION_UNLEARN = 12, + GOSSIP_ACTION_INFO_DEF = 1000, + + GOSSIP_SENDER_MAIN = 1, + GOSSIP_SENDER_INN_INFO = 2, + GOSSIP_SENDER_INFO = 3, + GOSSIP_SENDER_SEC_PROFTRAIN = 4, + GOSSIP_SENDER_SEC_CLASSTRAIN = 5, + GOSSIP_SENDER_SEC_BATTLEINFO = 6, + GOSSIP_SENDER_SEC_BANK = 7, + GOSSIP_SENDER_SEC_INN = 8, + GOSSIP_SENDER_SEC_MAILBOX = 9, + GOSSIP_SENDER_SEC_STABLEMASTER = 10 +}; + +extern uint32 GetSkillLevel(Player *player,uint32 skill); + +// Defined fuctions to use with player. + +// This fuction add's a menu item, +// a - Icon Id +// b - Text +// c - Sender(this is to identify the current Menu with this item) +// d - Action (identifys this Menu Item) +// e - Text to be displayed in pop up box +// f - Money value in pop up box +#define ADD_GOSSIP_ITEM(a,b,c,d) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,"",0) +#define ADD_GOSSIP_ITEM_EXTENDED(a,b,c,d,e,f,g) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,e,f,g) + +// This fuction Sends the current menu to show to client, a - NPCTEXTID(uint32) , b - npc guid(uint64) +#define SEND_GOSSIP_MENU(a,b) PlayerTalkClass->SendGossipMenu(a,b) + +// This fuction shows POI(point of interest) to client. +// a - position X +// b - position Y +// c - Icon Id +// d - Flags +// e - Data +// f - Location Name +#define SEND_POI(a,b,c,d,e,f) PlayerTalkClass->SendPointOfInterest(a,b,c,d,e,f) + +// Closes the Menu +#define CLOSE_GOSSIP_MENU() PlayerTalkClass->CloseGossip() + +// Fuction to tell to client the details +// a - quest object +// b - npc guid(uint64) +// c - Activate accept(bool) +#define SEND_QUEST_DETAILS(a,b,c) PlayerTalkClass->SendQuestDetails(a,b,c) + +// Fuction to tell to client the requested items to complete quest +// a - quest object +// b - npc guid(uint64) +// c - Iscompletable(bool) +// d - close at cancel(bool) - in case single incomplite ques +#define SEND_REQUESTEDITEMS(a,b,c,d) PlayerTalkClass->SendRequestedItems(a,b,c,d) + +// Fuctions to send NPC lists, a - is always the npc guid(uint64) +#define SEND_VENDORLIST(a) GetSession()->SendListInventory(a) +#define SEND_TRAINERLIST(a) GetSession()->SendTrainerList(a) + +// Ressurect's the player if is dead. +#define SEND_SPRESURRECT() GetSession()->SendSpiritResurrect() + +// Get the player's honor rank. +#define GET_HONORRANK() GetHonorRank() +// ----------------------------------- + +// defined fuctions to use with Creature + +#define QUEST_DIALOG_STATUS(a,b,c) GetSession()->getDialogStatus(a,b,c) +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedGuardAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedGuardAI.cpp new file mode 100644 index 00000000000..68dc8690470 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedGuardAI.cpp @@ -0,0 +1,194 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 .sourceforge.net/> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* ScriptData +SDName: Guard_AI +SD%Complete: 90 +SDComment: +SDCategory: Guards +EndScriptData */ + +#include "ScriptedPch.h" +#include "ScriptedGuardAI.h" + +// **** This script is for use within every single guard to save coding time **** + +#define GENERIC_CREATURE_COOLDOWN 5000 + +#define SAY_GUARD_SIL_AGGRO1 -1070001 +#define SAY_GUARD_SIL_AGGRO2 -1070002 +#define SAY_GUARD_SIL_AGGRO3 -1070003 + +guardAI::guardAI(Creature* pCreature) : ScriptedAI(pCreature), + GlobalCooldown(0), + BuffTimer(0) +{} + +void guardAI::Reset() +{ + GlobalCooldown = 0; + BuffTimer = 0; //Rebuff as soon as we can +} + +void guardAI::EnterCombat(Unit *who) +{ + if (me->GetEntry() == 15184) + DoScriptText(RAND(SAY_GUARD_SIL_AGGRO1,SAY_GUARD_SIL_AGGRO2,SAY_GUARD_SIL_AGGRO3), me, who); + + if (SpellEntry const *spell = me->reachWithSpellAttack(who)) + DoCastSpell(who, spell); +} + +void guardAI::JustDied(Unit *Killer) +{ + //Send Zone Under Attack message to the LocalDefense and WorldDefense Channels + if (Player* pKiller = Killer->GetCharmerOrOwnerPlayerOrPlayerItself()) + me->SendZoneUnderAttackMessage(pKiller); +} + +void guardAI::UpdateAI(const uint32 diff) +{ + //Always decrease our global cooldown first + if (GlobalCooldown > diff) + GlobalCooldown -= diff; + else GlobalCooldown = 0; + + //Buff timer (only buff when we are alive and not in combat + if (me->isAlive() && !me->isInCombat()) + if (BuffTimer <= diff) + { + //Find a spell that targets friendly and applies an aura (these are generally buffs) + SpellEntry const *info = SelectSpell(me, 0, 0, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_AURA); + + if (info && !GlobalCooldown) + { + //Cast the buff spell + DoCastSpell(me, info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + //Set our timer to 10 minutes before rebuff + BuffTimer = 600000; + } //Try again in 30 seconds + else BuffTimer = 30000; + } else BuffTimer -= diff; + + //Return since we have no target + if (!UpdateVictim()) + return; + + // Make sure our attack is ready and we arn't currently casting + if (me->isAttackReady() && !me->IsNonMeleeSpellCasted(false)) + { + //If we are within range melee the target + if (me->IsWithinMeleeRange(me->getVictim())) + { + bool Healing = false; + SpellEntry const *info = NULL; + + //Select a healing spell if less than 30% hp + if (me->GetHealth()*100 / me->GetMaxHealth() < 30) + info = SelectSpell(me, 0, 0, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + + //No healing spell available, select a hostile spell + if (info) Healing = true; + else info = SelectSpell(me->getVictim(), 0, 0, SELECT_TARGET_ANY_ENEMY, 0, 0, 0, 0, SELECT_EFFECT_DONTCARE); + + //20% chance to replace our white hit with a spell + if (info && rand() % 5 == 0 && !GlobalCooldown) + { + //Cast the spell + if (Healing)DoCastSpell(me, info); + else DoCastSpell(me->getVictim(), info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + } + else me->AttackerStateUpdate(me->getVictim()); + + me->resetAttackTimer(); + } + } + else + { + //Only run this code if we arn't already casting + if (!me->IsNonMeleeSpellCasted(false)) + { + bool Healing = false; + SpellEntry const *info = NULL; + + //Select a healing spell if less than 30% hp ONLY 33% of the time + if (me->GetHealth()*100 / me->GetMaxHealth() < 30 && rand() % 3 == 0) + info = SelectSpell(me, 0, 0, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + + //No healing spell available, See if we can cast a ranged spell (Range must be greater than ATTACK_DISTANCE) + if (info) Healing = true; + else info = SelectSpell(me->getVictim(), 0, 0, SELECT_TARGET_ANY_ENEMY, 0, 0, NOMINAL_MELEE_RANGE, 0, SELECT_EFFECT_DONTCARE); + + //Found a spell, check if we arn't on cooldown + if (info && !GlobalCooldown) + { + //If we are currently moving stop us and set the movement generator + if ((*me).GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE) + { + (*me).GetMotionMaster()->Clear(false); + (*me).GetMotionMaster()->MoveIdle(); + } + + //Cast spell + if (Healing) DoCastSpell(me,info); + else DoCastSpell(me->getVictim(),info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + } //If no spells available and we arn't moving run to target + else if ((*me).GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE) + { + //Cancel our current spell and then mutate new movement generator + me->InterruptNonMeleeSpells(false); + (*me).GetMotionMaster()->Clear(false); + (*me).GetMotionMaster()->MoveChase(me->getVictim()); + } + } + } +} + +void guardAI::DoReplyToTextEmote(uint32 em) +{ + switch(em) + { + case TEXTEMOTE_KISS: me->HandleEmoteCommand(EMOTE_ONESHOT_BOW); break; + case TEXTEMOTE_WAVE: me->HandleEmoteCommand(EMOTE_ONESHOT_WAVE); break; + case TEXTEMOTE_SALUTE: me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); break; + case TEXTEMOTE_SHY: me->HandleEmoteCommand(EMOTE_ONESHOT_FLEX); break; + case TEXTEMOTE_RUDE: + case TEXTEMOTE_CHICKEN: me->HandleEmoteCommand(EMOTE_ONESHOT_POINT); break; + } +} + +void guardAI_orgrimmar::ReceiveEmote(Player* pPlayer, uint32 text_emote) +{ + if (pPlayer->GetTeam() == HORDE) + DoReplyToTextEmote(text_emote); +} + +void guardAI_stormwind::ReceiveEmote(Player* pPlayer, uint32 text_emote) +{ + if (pPlayer->GetTeam() == ALLIANCE) + DoReplyToTextEmote(text_emote); +} diff --git a/src/server/game/AI/ScriptedAI/ScriptedGuardAI.h b/src/server/game/AI/ScriptedAI/ScriptedGuardAI.h new file mode 100644 index 00000000000..d28f612625e --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedGuardAI.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_GUARDAI_H +#define SC_GUARDAI_H + +#define GENERIC_CREATURE_COOLDOWN 5000 + +struct guardAI : public ScriptedAI +{ + public: + explicit guardAI(Creature* pCreature); + ~guardAI() {} + + uint32 GlobalCooldown; //This variable acts like the global cooldown that players have (1.5 seconds) + uint32 BuffTimer; //This variable keeps track of buffs + + void Reset(); + + void EnterCombat(Unit * /*who*/); + + void JustDied(Unit *Killer); + + void UpdateAI(const uint32 diff); + + //common used for guards in main cities + void DoReplyToTextEmote(uint32 em); +}; + +struct guardAI_orgrimmar : public guardAI +{ + guardAI_orgrimmar(Creature *c) : guardAI(c) {} + + void ReceiveEmote(Player *player, uint32 text_emote); +}; + +struct guardAI_stormwind : public guardAI +{ + guardAI_stormwind(Creature *c) : guardAI(c) {} + + void ReceiveEmote(Player *player, uint32 text_emote); +}; +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedInstance.h b/src/server/game/AI/ScriptedAI/ScriptedInstance.h new file mode 100644 index 00000000000..25593e05300 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedInstance.h @@ -0,0 +1,20 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_INSTANCE_H +#define SC_INSTANCE_H + +#include "InstanceData.h" +#include "Map.h" + +#define OUT_SAVE_INST_DATA debug_log("TSCR: Saving Instance Data for Instance %s (Map %d, Instance Id %d)", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_SAVE_INST_DATA_COMPLETE debug_log("TSCR: Saving Instance Data for Instance %s (Map %d, Instance Id %d) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_LOAD_INST_DATA(a) debug_log("TSCR: Loading Instance Data for Instance %s (Map %d, Instance Id %d). Input is '%s'", instance->GetMapName(), instance->GetId(), instance->GetInstanceId(), a) +#define OUT_LOAD_INST_DATA_COMPLETE debug_log("TSCR: Instance Data Load for Instance %s (Map %d, Instance Id: %d) is complete.",instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_LOAD_INST_DATA_FAIL error_log("TSCR: Unable to load Instance Data for Instance %s (Map %d, Instance Id: %d).",instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) + +#define ScriptedInstance InstanceData + +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedSimpleAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedSimpleAI.cpp new file mode 100644 index 00000000000..08797515837 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedSimpleAI.cpp @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 +* 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, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* ScriptData +SDName: SimpleAI +SD%Complete: 100 +SDComment: Base Class for SimpleAI creatures +SDCategory: Creatures +EndScriptData */ + +#include "ScriptedPch.h" +#include "ScriptedSimpleAI.h" + +SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c) +{ + //Clear all data + Aggro_TextId[0] = 0; + Aggro_TextId[1] = 0; + Aggro_TextId[2] = 0; + Aggro_Sound[0] = 0; + Aggro_Sound[1] = 0; + Aggro_Sound[2] = 0; + + Death_TextId[0] = 0; + Death_TextId[1] = 0; + Death_TextId[2] = 0; + Death_Sound[0] = 0; + Death_Sound[1] = 0; + Death_Sound[2] = 0; + Death_Spell = 0; + Death_Target_Type = 0; + + Kill_TextId[0] = 0; + Kill_TextId[1] = 0; + Kill_TextId[2] = 0; + Kill_Sound[0] = 0; + Kill_Sound[1] = 0; + Kill_Sound[2] = 0; + Kill_Spell = 0; + Kill_Target_Type = 0; + + memset(Spell,0,sizeof(Spell)); + + EnterEvadeMode(); +} + +void SimpleAI::Reset() +{ +} + +void SimpleAI::EnterCombat(Unit *who) +{ + //Reset cast timers + if (Spell[0].First_Cast >= 0) + Spell_Timer[0] = Spell[0].First_Cast; + else Spell_Timer[0] = 1000; + if (Spell[1].First_Cast >= 0) + Spell_Timer[1] = Spell[1].First_Cast; + else Spell_Timer[1] = 1000; + if (Spell[2].First_Cast >= 0) + Spell_Timer[2] = Spell[2].First_Cast; + else Spell_Timer[2] = 1000; + if (Spell[3].First_Cast >= 0) + Spell_Timer[3] = Spell[3].First_Cast; + else Spell_Timer[3] = 1000; + if (Spell[4].First_Cast >= 0) + Spell_Timer[4] = Spell[4].First_Cast; + else Spell_Timer[4] = 1000; + if (Spell[5].First_Cast >= 0) + Spell_Timer[5] = Spell[5].First_Cast; + else Spell_Timer[5] = 1000; + if (Spell[6].First_Cast >= 0) + Spell_Timer[6] = Spell[6].First_Cast; + else Spell_Timer[6] = 1000; + if (Spell[7].First_Cast >= 0) + Spell_Timer[7] = Spell[7].First_Cast; + else Spell_Timer[7] = 1000; + if (Spell[8].First_Cast >= 0) + Spell_Timer[8] = Spell[8].First_Cast; + else Spell_Timer[8] = 1000; + if (Spell[9].First_Cast >= 0) + Spell_Timer[9] = Spell[9].First_Cast; + else Spell_Timer[9] = 1000; + + uint8 random_text = urand(0,2); + + //Random text + if (Aggro_TextId[random_text]) + DoScriptText(Aggro_TextId[random_text], me, who); + + //Random sound + if (Aggro_Sound[random_text]) + DoPlaySoundToSet(me, Aggro_Sound[random_text]); +} + +void SimpleAI::KilledUnit(Unit *victim) +{ + uint8 random_text = urand(0,2); + + //Random yell + if (Kill_TextId[random_text]) + DoScriptText(Kill_TextId[random_text], me, victim); + + //Random sound + if (Kill_Sound[random_text]) + DoPlaySoundToSet(me, Kill_Sound[random_text]); + + if (!Kill_Spell) + return; + + Unit *pTarget = NULL; + + switch (Kill_Target_Type) + { + case CAST_SELF: + pTarget = me; + break; + case CAST_HOSTILE_TARGET: + pTarget = me->getVictim(); + break; + case CAST_HOSTILE_SECOND_AGGRO: + pTarget = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case CAST_HOSTILE_LAST_AGGRO: + pTarget = SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case CAST_HOSTILE_RANDOM: + pTarget = SelectUnit(SELECT_TARGET_RANDOM,0); + break; + case CAST_KILLEDUNIT_VICTIM: + pTarget = victim; + break; + } + + //Target is ok, cast a spell on it + if (pTarget) + DoCast(pTarget, Kill_Spell); +} + +void SimpleAI::DamageTaken(Unit *killer, uint32 &damage) +{ + //Return if damage taken won't kill us + if (me->GetHealth() > damage) + return; + + uint8 random_text = urand(0,2); + + //Random yell + if (Death_TextId[random_text]) + DoScriptText(Death_TextId[random_text], me, killer); + + //Random sound + if (Death_Sound[random_text]) + DoPlaySoundToSet(me, Death_Sound[random_text]); + + if (!Death_Spell) + return; + + Unit *pTarget = NULL; + + switch (Death_Target_Type) + { + case CAST_SELF: + pTarget = me; + break; + case CAST_HOSTILE_TARGET: + pTarget = me->getVictim(); + break; + case CAST_HOSTILE_SECOND_AGGRO: + pTarget = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case CAST_HOSTILE_LAST_AGGRO: + pTarget = SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case CAST_HOSTILE_RANDOM: + pTarget = SelectUnit(SELECT_TARGET_RANDOM,0); + break; + case CAST_JUSTDIED_KILLER: + pTarget = killer; + break; + } + + //Target is ok, cast a spell on it + if (pTarget) + DoCast(pTarget, Death_Spell); +} + +void SimpleAI::UpdateAI(const uint32 diff) +{ + //Return since we have no target + if (!UpdateVictim()) + return; + + //Spells + for (uint32 i = 0; i < 10; ++i) + { + //Spell not valid + if (!Spell[i].Enabled || !Spell[i].Spell_Id) + continue; + + if (Spell_Timer[i] <= diff) + { + //Check if this is a percentage based + if (Spell[i].First_Cast < 0 && Spell[i].First_Cast > -100 && me->GetHealth()*100 / me->GetMaxHealth() > -Spell[i].First_Cast) + continue; + + //Check Current spell + if (!(Spell[i].InterruptPreviousCast && me->IsNonMeleeSpellCasted(false))) + { + Unit *pTarget = NULL; + + switch (Spell[i].Cast_Target_Type) + { + case CAST_SELF: + pTarget = me; + break; + case CAST_HOSTILE_TARGET: + pTarget = me->getVictim(); + break; + case CAST_HOSTILE_SECOND_AGGRO: + pTarget = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case CAST_HOSTILE_LAST_AGGRO: + pTarget = SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case CAST_HOSTILE_RANDOM: + pTarget = SelectUnit(SELECT_TARGET_RANDOM,0); + break; + } + + //Target is ok, cast a spell on it and then do our random yell + if (pTarget) + { + if (me->IsNonMeleeSpellCasted(false)) + me->InterruptNonMeleeSpells(false); + + DoCast(pTarget, Spell[i].Spell_Id); + + //Yell and sound use the same number so that you can make + //the Creature yell with the correct sound effect attached + uint8 random_text = urand(0,2); + + //Random yell + if (Spell[i].TextId[random_text]) + DoScriptText(Spell[i].TextId[random_text], me, pTarget); + + //Random sound + if (Spell[i].Text_Sound[random_text]) + DoPlaySoundToSet(me, Spell[i].Text_Sound[random_text]); + } + + } + + //Spell will cast agian when the cooldown is up + if (Spell[i].CooldownRandomAddition) + Spell_Timer[i] = Spell[i].Cooldown + (rand() % Spell[i].CooldownRandomAddition); + else Spell_Timer[i] = Spell[i].Cooldown; + + } else Spell_Timer[i] -= diff; + + } + + DoMeleeAttackIfReady(); +} + diff --git a/src/server/game/AI/ScriptedAI/ScriptedSimpleAI.h b/src/server/game/AI/ScriptedAI/ScriptedSimpleAI.h new file mode 100644 index 00000000000..c4689ff3fab --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedSimpleAI.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2006 - 2009 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_SIMPLEAI_H +#define SC_SIMPLEAI_H + +enum CastTarget +{ + CAST_SELF = 0, //Self cast + CAST_HOSTILE_TARGET, //Our current target (ie: highest aggro) + CAST_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + CAST_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + CAST_HOSTILE_RANDOM, //Just any random target on our threat list + CAST_FRIENDLY_RANDOM, //NOT YET IMPLEMENTED + + //Special cases + CAST_KILLEDUNIT_VICTIM, //Only works within KilledUnit function + CAST_JUSTDIED_KILLER, //Only works within JustDied function +}; + +struct SimpleAI : public ScriptedAI +{ + SimpleAI(Creature *c);// : ScriptedAI(c); + + void Reset(); + + void EnterCombat(Unit * /*who*/); + + void KilledUnit(Unit * /*victim*/); + + void DamageTaken(Unit *killer, uint32 &damage); + + void UpdateAI(const uint32 diff); + +public: + + int32 Aggro_TextId[3]; + uint32 Aggro_Sound[3]; + + int32 Death_TextId[3]; + uint32 Death_Sound[3]; + uint32 Death_Spell; + uint32 Death_Target_Type; + + int32 Kill_TextId[3]; + uint32 Kill_Sound[3]; + uint32 Kill_Spell; + uint32 Kill_Target_Type; + + struct SimpleAI_Spell + { + uint32 Spell_Id; //Spell ID to cast + int32 First_Cast; //Delay for first cast + uint32 Cooldown; //Cooldown between casts + uint32 CooldownRandomAddition; //Random addition to cooldown (in range from 0 - CooldownRandomAddition) + uint32 Cast_Target_Type; //Target type (note that certain spells may ignore this) + bool InterruptPreviousCast; //Interrupt a previous cast if this spell needs to be cast + bool Enabled; //Spell enabled or disabled (default: false) + + //3 texts to many? + int32 TextId[3]; + uint32 Text_Sound[3]; + }Spell[10]; + +protected: + uint32 Spell_Timer[10]; +}; + +#endif + diff --git a/src/server/game/AI/ScriptedAI/ScriptedSmartAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedSmartAI.cpp new file mode 100644 index 00000000000..c5c11d5bef9 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedSmartAI.cpp @@ -0,0 +1,8 @@ +/* Copyright (C) 2008-2010 Trinity + * + * Thanks to the original authors: ScriptDev2 + * + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#include "ScriptedSmartAI.h" \ No newline at end of file diff --git a/src/server/game/AI/ScriptedAI/ScriptedSmartAI.h b/src/server/game/AI/ScriptedAI/ScriptedSmartAI.h new file mode 100644 index 00000000000..a1e10a06bb2 --- /dev/null +++ b/src/server/game/AI/ScriptedAI/ScriptedSmartAI.h @@ -0,0 +1,8 @@ +/* Copyright (C) 2008-2010 Trinity + * + * Thanks to the original authors: ScriptDev2 + * + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#include "Creature.h" \ No newline at end of file diff --git a/src/server/game/AI/TotemAI.cpp b/src/server/game/AI/TotemAI.cpp new file mode 100644 index 00000000000..a6464f189e8 --- /dev/null +++ b/src/server/game/AI/TotemAI.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "TotemAI.h" +#include "Totem.h" +#include "Creature.h" +#include "DBCStores.h" +#include "ObjectAccessor.h" +#include "SpellMgr.h" + +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" + +int +TotemAI::Permissible(const Creature *creature) +{ + if (creature->isTotem()) + return PERMIT_BASE_PROACTIVE; + + return PERMIT_BASE_NO; +} + +TotemAI::TotemAI(Creature *c) : CreatureAI(c), i_victimGuid(0) +{ + assert(c->isTotem()); +} + +void +TotemAI::MoveInLineOfSight(Unit *) +{ +} + +void TotemAI::EnterEvadeMode() +{ + me->CombatStop(true); +} + +void +TotemAI::UpdateAI(const uint32 /*diff*/) +{ + if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE) + return; + + if (!me->isAlive() || me->IsNonMeleeSpellCasted(false)) + return; + + // Search spell + SpellEntry const *spellInfo = sSpellStore.LookupEntry(me->ToTotem()->GetSpell()); + if (!spellInfo) + return; + + // Get spell range + SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); + float max_range = GetSpellMaxRangeForHostile(srange); + + // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems + + // pointer to appropriate target if found any + Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL; + + // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) + if (!victim || + !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || + me->IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(me,false)) + { + victim = NULL; + Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range); + Trinity::UnitLastSearcher checker(me, victim, u_check); + me->VisitNearbyObject(max_range, checker); + } + + // If have target + if (victim) + { + // remember + i_victimGuid = victim->GetGUID(); + + // attack + me->SetInFront(victim); // client change orientation by self + me->CastSpell(victim, me->ToTotem()->GetSpell(), false); + } + else + i_victimGuid = 0; +} + +void +TotemAI::AttackStart(Unit *) +{ + // Sentry totem sends ping on attack + if (me->GetEntry() == SENTRY_TOTEM_ENTRY && me->GetOwner()->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(MSG_MINIMAP_PING, (8+4+4)); + data << me->GetGUID(); + data << me->GetPositionX(); + data << me->GetPositionY(); + ((Player*)me->GetOwner())->GetSession()->SendPacket(&data); + } +} diff --git a/src/server/game/AI/TotemAI.h b/src/server/game/AI/TotemAI.h new file mode 100644 index 00000000000..34f4dfa9945 --- /dev/null +++ b/src/server/game/AI/TotemAI.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_TOTEMAI_H +#define TRINITY_TOTEMAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; +class Totem; + +class TotemAI : public CreatureAI +{ + public: + + explicit TotemAI(Creature *c); + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + uint64 i_victimGuid; +}; +#endif + diff --git a/src/server/game/AI/UnitAI.cpp b/src/server/game/AI/UnitAI.cpp new file mode 100644 index 00000000000..a156ec573d9 --- /dev/null +++ b/src/server/game/AI/UnitAI.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "UnitAI.h" +#include "Player.h" +#include "Creature.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" +#include "SpellMgr.h" +#include "CreatureAIImpl.h" + +void UnitAI::AttackStart(Unit *victim) +{ + if (victim && me->Attack(victim, true)) + me->GetMotionMaster()->MoveChase(victim); +} + +void UnitAI::AttackStartCaster(Unit *victim, float dist) +{ + if (victim && me->Attack(victim, false)) + me->GetMotionMaster()->MoveChase(victim, dist); +} + +void UnitAI::DoMeleeAttackIfReady() +{ + if (me->hasUnitState(UNIT_STAT_CASTING)) + return; + + //Make sure our attack is ready and we aren't currently casting before checking distance + if (me->isAttackReady()) + { + //If we are within range melee the target + if (me->IsWithinMeleeRange(me->getVictim())) + { + me->AttackerStateUpdate(me->getVictim()); + me->resetAttackTimer(); + } + } + if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK)) + { + //If we are within range melee the target + if (me->IsWithinMeleeRange(me->getVictim())) + { + me->AttackerStateUpdate(me->getVictim(), OFF_ATTACK); + me->resetAttackTimer(OFF_ATTACK); + } + } +} + +bool UnitAI::DoSpellAttackIfReady(uint32 spell) +{ + if (me->hasUnitState(UNIT_STAT_CASTING)) + return true; + + if (me->isAttackReady()) + { + if (me->IsWithinCombatRange(me->getVictim(), GetSpellMaxRange(spell, false))) + { + me->CastSpell(me->getVictim(), spell, false); + me->resetAttackTimer(); + } + else + return false; + } + return true; +} + +// default predicate function to select target based on distance, player and/or aura criteria +struct DefaultTargetSelector : public std::unary_function +{ + const Unit *me; + float m_dist; + bool m_playerOnly; + int32 m_aura; + + // pUnit: the reference unit + // dist: if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit + // playerOnly: self explaining + // aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura + DefaultTargetSelector(const Unit *pUnit, float dist, bool playerOnly, int32 aura) : me(pUnit), m_dist(dist), m_playerOnly(playerOnly), m_aura(aura) {} + + bool operator() (const Unit *pTarget) + { + if (!me) + return false; + + if (!pTarget) + return false; + + if (m_playerOnly && (pTarget->GetTypeId() != TYPEID_PLAYER)) + return false; + + if (m_dist > 0.0f && !me->IsWithinCombatRange(pTarget, m_dist)) + return false; + + if (m_dist < 0.0f && me->IsWithinCombatRange(pTarget, -m_dist)) + return false; + + if (m_aura) + { + if (m_aura > 0) + { + if (!pTarget->HasAura(m_aura)) + return false; + } + else + { + if (pTarget->HasAura(m_aura)) + return false; + } + } + + return true; + } +}; + +Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura) +{ + return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, aura)); +} + +void UnitAI::SelectTargetList(std::list &targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura) +{ + const std::list &threatlist = me->getThreatManager().getThreatList(); + + if (threatlist.empty()) + return; + + DefaultTargetSelector targetSelector(me, dist,playerOnly, aura); + for (std::list::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + if (targetSelector((*itr)->getTarget())) + targetList.push_back((*itr)->getTarget()); + + if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) + targetList.sort(Trinity::ObjectDistanceOrderPred(me)); + + if (targetType == SELECT_TARGET_FARTHEST || targetType == SELECT_TARGET_BOTTOMAGGRO) + targetList.reverse(); + + if (targetList.size() < num) + return; + + if (targetType == SELECT_TARGET_RANDOM) + { + while (num < targetList.size()) { + std::list::iterator itr = targetList.begin(); + advance(itr, urand(0, targetList.size()-1)); + targetList.erase(itr); + } + } + else + targetList.resize(num); +} + +float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive) +{ + return GetSpellMaxRange(spellId, positive); +} + +void UnitAI::DoAddAuraToAllHostilePlayers(uint32 spellid) +{ + if (me->isInCombat()) + { + std::list& threatlist = me->getThreatManager().getThreatList(); + for (std::list::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + if (Unit *pTemp = Unit::GetUnit(*me,(*itr)->getUnitGuid())) + if (pTemp->GetTypeId() == TYPEID_PLAYER) + me->AddAura(spellid, pTemp); + } + }else + return; +} + +void UnitAI::DoCastToAllHostilePlayers(uint32 spellid, bool triggered) +{ + if (me->isInCombat()) + { + std::list& threatlist = me->getThreatManager().getThreatList(); + for (std::list::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + if (Unit *pTemp = Unit::GetUnit(*me,(*itr)->getUnitGuid())) + if (pTemp->GetTypeId() == TYPEID_PLAYER) + me->CastSpell(pTemp, spellid, triggered); + } + }else + return; +} + +void UnitAI::DoCast(uint32 spellId) +{ + Unit *target = NULL; + //sLog.outError("aggre %u %u", spellId, (uint32)AISpellInfo[spellId].target); + switch(AISpellInfo[spellId].target) + { + default: + case AITARGET_SELF: target = me; break; + case AITARGET_VICTIM: target = me->getVictim(); break; + case AITARGET_ENEMY: + { + const SpellEntry * spellInfo = GetSpellStore()->LookupEntry(spellId); + bool playerOnly = spellInfo->AttributesEx3 & SPELL_ATTR_EX3_PLAYERS_ONLY; + //float range = GetSpellMaxRange(spellInfo, false); + target = SelectTarget(SELECT_TARGET_RANDOM, 0, GetSpellMaxRange(spellInfo, false), playerOnly); + break; + } + case AITARGET_ALLY: target = me; break; + case AITARGET_BUFF: target = me; break; + case AITARGET_DEBUFF: + { + const SpellEntry * spellInfo = GetSpellStore()->LookupEntry(spellId); + bool playerOnly = spellInfo->AttributesEx3 & SPELL_ATTR_EX3_PLAYERS_ONLY; + float range = GetSpellMaxRange(spellInfo, false); + + DefaultTargetSelector targetSelector(me, range, playerOnly, -(int32)spellId); + if (!(spellInfo->Attributes & SPELL_ATTR_BREAKABLE_BY_DAMAGE) + && !(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM) + && targetSelector(me->getVictim())) + target = me->getVictim(); + else + target = SelectTarget(SELECT_TARGET_RANDOM, 0, targetSelector); + break; + } + } + + if (target) + me->CastSpell(target, spellId, false); +} + +#define UPDATE_TARGET(a) {if (AIInfo->targettarget=a;} + +void UnitAI::FillAISpellInfo() +{ + AISpellInfo = new AISpellInfoType[GetSpellStore()->GetNumRows()]; + + AISpellInfoType *AIInfo = AISpellInfo; + const SpellEntry * spellInfo; + + for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i, ++AIInfo) + { + spellInfo = GetSpellStore()->LookupEntry(i); + if (!spellInfo) + continue; + + if (spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD) + AIInfo->condition = AICOND_DIE; + else if (IsPassiveSpell(i) || GetSpellDuration(spellInfo) == -1) + AIInfo->condition = AICOND_AGGRO; + else + AIInfo->condition = AICOND_COMBAT; + + if (AIInfo->cooldown < spellInfo->RecoveryTime) + AIInfo->cooldown = spellInfo->RecoveryTime; + + if (!GetSpellMaxRange(spellInfo, false)) + UPDATE_TARGET(AITARGET_SELF) + else + { + for (uint32 j = 0; j < 3; ++j) + { + uint32 targetType = spellInfo->EffectImplicitTargetA[j]; + + if (targetType == TARGET_UNIT_TARGET_ENEMY + || targetType == TARGET_DST_TARGET_ENEMY) + UPDATE_TARGET(AITARGET_VICTIM) + else if (targetType == TARGET_UNIT_AREA_ENEMY_DST) + UPDATE_TARGET(AITARGET_ENEMY) + + if (spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA) + { + if (targetType == TARGET_UNIT_TARGET_ENEMY) + UPDATE_TARGET(AITARGET_DEBUFF) + else if (IsPositiveSpell(i)) + UPDATE_TARGET(AITARGET_BUFF) + } + } + } + AIInfo->realCooldown = spellInfo->RecoveryTime + spellInfo->StartRecoveryTime; + SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); + if (srange) + AIInfo->maxRange = srange->maxRangeHostile * 3 / 4; + } +} + +//Enable PlayerAI when charmed +void PlayerAI::OnCharmed(bool apply) { me->IsAIEnabled = apply; } + +void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/) +{ + Creature *charmer = me->GetCharmer()->ToCreature(); + + //kill self if charm aura has infinite duration + if (charmer->IsInEvadeMode()) + { + Unit::AuraEffectList const& auras = me->GetAuraEffectsByType(SPELL_AURA_MOD_CHARM); + for (Unit::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) + if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent()) + { + charmer->Kill(me); + return; + } + } + + if (!charmer->isInCombat()) + me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle()); + + Unit *target = me->getVictim(); + if (!target || !charmer->canAttack(target)) + AttackStart(charmer->SelectNearestTargetInAttackDistance()); +} diff --git a/src/server/game/AI/UnitAI.h b/src/server/game/AI/UnitAI.h new file mode 100644 index 00000000000..62b7090a2d0 --- /dev/null +++ b/src/server/game/AI/UnitAI.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_UNITAI_H +#define TRINITY_UNITAI_H + +#include "Platform/Define.h" +#include +#include "Unit.h" + +class Unit; +class Player; +struct AISpellInfoType; + +//Selection method used by SelectTarget +enum SelectAggroTarget +{ + SELECT_TARGET_RANDOM = 0, //Just selects a random target + SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom + SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top + SELECT_TARGET_NEAREST, + SELECT_TARGET_FARTHEST, +}; + +class UnitAI +{ + protected: + Unit * const me; + public: + explicit UnitAI(Unit *u) : me(u) {} + virtual ~UnitAI() {} + + virtual bool CanAIAttack(const Unit * /*who*/) const { return true; } + virtual void AttackStart(Unit *); + virtual void UpdateAI(const uint32 diff) = 0; + + virtual void InitializeAI() { if (!me->isDead()) Reset(); } + + virtual void Reset() {}; + + // Called when unit is charmed + virtual void OnCharmed(bool apply) = 0; + + // Pass parameters between AI + virtual void DoAction(const int32 /*param*/ = 0) {} + virtual uint32 GetData(uint32 /*id = 0*/) { return 0; } + virtual void SetData(uint32 /*id*/, uint32 /*value*/) {} + virtual void SetGUID(const uint64 &/*guid*/, int32 /*id*/ = 0) {} + virtual uint64 GetGUID(int32 /*id*/ = 0) { return 0; } + + Unit* SelectTarget(SelectAggroTarget targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, int32 aura = 0); + void SelectTargetList(std::list &targetList, uint32 num, SelectAggroTarget targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0); + + // Select the targets satifying the predicate. + // predicate shall extend std::unary_function + template Unit* SelectTarget(SelectAggroTarget targetType, uint32 position, PREDICATE predicate) + { + const std::list &threatlist = me->getThreatManager().getThreatList(); + std::list targetList; + + if (position >= threatlist.size()) + return NULL; + + for (std::list::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + HostileReference* ref = (*itr); + if (predicate(ref->getTarget())) + targetList.push_back(ref->getTarget()); + } + + if (position >= targetList.size()) + return NULL; + + if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) + targetList.sort(Trinity::ObjectDistanceOrderPred(me)); + + switch(targetType) + { + case SELECT_TARGET_NEAREST: + case SELECT_TARGET_TOPAGGRO: + { + std::list::iterator itr = targetList.begin(); + advance(itr, position); + return *itr; + } + break; + + case SELECT_TARGET_FARTHEST: + case SELECT_TARGET_BOTTOMAGGRO: + { + std::list::reverse_iterator ritr = targetList.rbegin(); + advance(ritr, position); + return *ritr; + } + break; + + case SELECT_TARGET_RANDOM: + { + std::list::iterator itr = targetList.begin(); + advance(itr, urand(position, targetList.size()-1)); + return *itr; + } + break; + } + + return NULL; + } + + void AttackStartCaster(Unit *victim, float dist); + + void DoAddAuraToAllHostilePlayers(uint32 spellid); + void DoCast(uint32 spellId); + void DoCast(Unit* victim, uint32 spellId, bool triggered = false); + void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false); + void DoCastVictim(uint32 spellId, bool triggered = false); + void DoCastAOE(uint32 spellId, bool triggered = false); + + float DoGetSpellMaxRange(uint32 spellId, bool positive = false); + + void DoMeleeAttackIfReady(); + bool DoSpellAttackIfReady(uint32 spell); + + static AISpellInfoType *AISpellInfo; + static void FillAISpellInfo(); +}; + +class PlayerAI : public UnitAI +{ + protected: + Player* const me; + public: + explicit PlayerAI(Player *p) : UnitAI((Unit*)p), me(p) {} + + void OnCharmed(bool apply); +}; + +class SimpleCharmedAI : public PlayerAI +{ + public: + void UpdateAI(const uint32 diff); + SimpleCharmedAI(Player *p): PlayerAI(p) {} +}; + +#endif diff --git a/src/server/game/Account/AccountMgr.cpp b/src/server/game/Account/AccountMgr.cpp new file mode 100644 index 00000000000..52906fb5454 --- /dev/null +++ b/src/server/game/Account/AccountMgr.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/DatabaseEnv.h" +#include "Policies/SingletonImp.h" + +#include "AccountMgr.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "Util.h" +#include "Auth/Sha1.h" + +extern DatabaseType LoginDatabase; + +INSTANTIATE_SINGLETON_1(AccountMgr); + +AccountMgr::AccountMgr() +{} + +AccountMgr::~AccountMgr() +{} + +AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password) +{ + if (utf8length(username) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; // username's too long + + normalizeString(username); + normalizeString(password); + + if (GetId(username)) + { + return AOR_NAME_ALREDY_EXIST; // username does already exist + } + + if (!LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", username.c_str(), CalculateShaPassHash(username, password).c_str())) + return AOR_DB_INTERNAL_ERROR; // unexpected error + LoginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); + + return AOR_OK; // everything's fine +} + +AccountOpResult AccountMgr::DeleteAccount(uint32 accid) +{ + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + if (!result) + return AOR_NAME_NOT_EXIST; // account doesn't exist + + result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE account='%d'",accid); + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 guidlo = fields[0].GetUInt32(); + uint64 guid = MAKE_NEW_GUID(guidlo, 0, HIGHGUID_PLAYER); + + // kick if player currently + if (Player* p = ObjectAccessor::GetObjectInWorld(guid, (Player*)NULL)) + { + WorldSession* s = p->GetSession(); + s->KickPlayer(); // mark session to remove at next session list update + s->LogoutPlayer(false); // logout player without waiting next session list update + } + + Player::DeleteFromDB(guid, accid, false); // no need to update realm characters + } while (result->NextRow()); + } + + // table realm specific but common for all characters of account for realm + CharacterDatabase.PExecute("DELETE FROM character_tutorial WHERE account = '%u'",accid); + CharacterDatabase.PExecute("DELETE FROM account_data WHERE account = '%u'",accid); + + LoginDatabase.BeginTransaction(); + + bool res = + LoginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && + LoginDatabase.PExecute("DELETE FROM account_access WHERE id ='%d'", accid) && + LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); + + LoginDatabase.CommitTransaction(); + + if (!res) + return AOR_DB_INTERNAL_ERROR; // unexpected error; + + return AOR_OK; +} + +AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd) +{ + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + if (!result) + return AOR_NAME_NOT_EXIST; // account doesn't exist + + if (utf8length(new_uname) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; + + if (utf8length(new_passwd) > MAX_ACCOUNT_STR) + return AOR_PASS_TOO_LONG; + + normalizeString(new_uname); + normalizeString(new_passwd); + + std::string safe_new_uname = new_uname; + LoginDatabase.escape_string(safe_new_uname); + + if (!LoginDatabase.PExecute("UPDATE account SET v='0',s='0',username='%s',sha_pass_hash='%s' WHERE id='%d'", safe_new_uname.c_str(), + CalculateShaPassHash(new_uname, new_passwd).c_str(), accid)) + return AOR_DB_INTERNAL_ERROR; // unexpected error + + return AOR_OK; +} + +AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) +{ + std::string username; + + if (!GetName(accid, username)) + return AOR_NAME_NOT_EXIST; // account doesn't exist + + if (utf8length(new_passwd) > MAX_ACCOUNT_STR) + return AOR_PASS_TOO_LONG; + + normalizeString(username); + normalizeString(new_passwd); + + // also reset s and v to force update at next realmd login + if (!LoginDatabase.PExecute("UPDATE account SET v='0', s='0', sha_pass_hash='%s' WHERE id='%d'", + CalculateShaPassHash(username, new_passwd).c_str(), accid)) + return AOR_DB_INTERNAL_ERROR; // unexpected error + + return AOR_OK; +} + +uint32 AccountMgr::GetId(std::string username) +{ + LoginDatabase.escape_string(username); + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); + if (!result) + return 0; + else + { + uint32 id = (*result)[0].GetUInt32(); + return id; + } +} + +uint32 AccountMgr::GetSecurity(uint32 acc_id) +{ + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT gmlevel FROM account_access WHERE id = '%u'", acc_id); + if (result) + { + uint32 sec = (*result)[0].GetUInt32(); + return sec; + } + + return 0; +} + +uint32 AccountMgr::GetSecurity(uint32 acc_id, int32 realm_id) +{ + QueryResult_AutoPtr result = (realm_id == -1) + ? LoginDatabase.PQuery("SELECT gmlevel FROM account_access WHERE id = '%u' AND RealmID = '%d'", acc_id, realm_id) + : LoginDatabase.PQuery("SELECT gmlevel FROM account_access WHERE id = '%u' AND (RealmID = '%d' OR RealmID = '-1')", acc_id, realm_id); + if (result) + { + uint32 sec = (*result)[0].GetUInt32(); + return sec; + } + + return 0; +} + +bool AccountMgr::GetName(uint32 acc_id, std::string &name) +{ + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id); + if (result) + { + name = (*result)[0].GetCppString(); + return true; + } + + return false; +} + +bool AccountMgr::CheckPassword(uint32 accid, std::string passwd) +{ + std::string username; + if (!GetName(accid, username)) + return false; + + normalizeString(username); + normalizeString(passwd); + + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash='%s'", accid, CalculateShaPassHash(username, passwd).c_str()); + if (result) + return true; + + return false; +} + +bool AccountMgr::normalizeString(std::string& utf8str) +{ + wchar_t wstr_buf[MAX_ACCOUNT_STR+1]; + + size_t wstr_len = MAX_ACCOUNT_STR; + if (!Utf8toWStr(utf8str,wstr_buf,wstr_len)) + return false; + + std::transform(&wstr_buf[0], wstr_buf+wstr_len, &wstr_buf[0], wcharToUpperOnlyLatin); + + return WStrToUtf8(wstr_buf,wstr_len,utf8str); +} + +std::string AccountMgr::CalculateShaPassHash(std::string& name, std::string& password) +{ + Sha1Hash sha; + sha.Initialize(); + sha.UpdateData(name); + sha.UpdateData(":"); + sha.UpdateData(password); + sha.Finalize(); + + std::string encoded; + hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded); + + return encoded; +} + diff --git a/src/server/game/Account/AccountMgr.h b/src/server/game/Account/AccountMgr.h new file mode 100644 index 00000000000..b017bf1836a --- /dev/null +++ b/src/server/game/Account/AccountMgr.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ACCMGR_H +#define _ACCMGR_H + +#include + +#include "Common.h" +#include "Policies/Singleton.h" + +enum AccountOpResult +{ + AOR_OK, + AOR_NAME_TOO_LONG, + AOR_PASS_TOO_LONG, + AOR_NAME_ALREDY_EXIST, + AOR_NAME_NOT_EXIST, + AOR_DB_INTERNAL_ERROR +}; + +#define MAX_ACCOUNT_STR 16 + +class AccountMgr +{ + public: + AccountMgr(); + ~AccountMgr(); + + AccountOpResult CreateAccount(std::string username, std::string password); + AccountOpResult DeleteAccount(uint32 accid); + AccountOpResult ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd); + AccountOpResult ChangePassword(uint32 accid, std::string new_passwd); + bool CheckPassword(uint32 accid, std::string passwd); + + uint32 GetId(std::string username); + uint32 GetSecurity(uint32 acc_id); + uint32 GetSecurity(uint32 acc_id, int32 realm_id); + bool GetName(uint32 acc_id, std::string &name); + std::string CalculateShaPassHash(std::string& name, std::string& password); + + static bool normalizeString(std::string& utf8str); +}; + +#define accmgr Trinity::Singleton::Instance() +#endif + diff --git a/src/server/game/AccountMgr.cpp b/src/server/game/AccountMgr.cpp deleted file mode 100644 index 52906fb5454..00000000000 --- a/src/server/game/AccountMgr.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Database/DatabaseEnv.h" -#include "Policies/SingletonImp.h" - -#include "AccountMgr.h" -#include "ObjectAccessor.h" -#include "Player.h" -#include "Util.h" -#include "Auth/Sha1.h" - -extern DatabaseType LoginDatabase; - -INSTANTIATE_SINGLETON_1(AccountMgr); - -AccountMgr::AccountMgr() -{} - -AccountMgr::~AccountMgr() -{} - -AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password) -{ - if (utf8length(username) > MAX_ACCOUNT_STR) - return AOR_NAME_TOO_LONG; // username's too long - - normalizeString(username); - normalizeString(password); - - if (GetId(username)) - { - return AOR_NAME_ALREDY_EXIST; // username does already exist - } - - if (!LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", username.c_str(), CalculateShaPassHash(username, password).c_str())) - return AOR_DB_INTERNAL_ERROR; // unexpected error - LoginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); - - return AOR_OK; // everything's fine -} - -AccountOpResult AccountMgr::DeleteAccount(uint32 accid) -{ - QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); - if (!result) - return AOR_NAME_NOT_EXIST; // account doesn't exist - - result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE account='%d'",accid); - if (result) - { - do - { - Field *fields = result->Fetch(); - uint32 guidlo = fields[0].GetUInt32(); - uint64 guid = MAKE_NEW_GUID(guidlo, 0, HIGHGUID_PLAYER); - - // kick if player currently - if (Player* p = ObjectAccessor::GetObjectInWorld(guid, (Player*)NULL)) - { - WorldSession* s = p->GetSession(); - s->KickPlayer(); // mark session to remove at next session list update - s->LogoutPlayer(false); // logout player without waiting next session list update - } - - Player::DeleteFromDB(guid, accid, false); // no need to update realm characters - } while (result->NextRow()); - } - - // table realm specific but common for all characters of account for realm - CharacterDatabase.PExecute("DELETE FROM character_tutorial WHERE account = '%u'",accid); - CharacterDatabase.PExecute("DELETE FROM account_data WHERE account = '%u'",accid); - - LoginDatabase.BeginTransaction(); - - bool res = - LoginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && - LoginDatabase.PExecute("DELETE FROM account_access WHERE id ='%d'", accid) && - LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); - - LoginDatabase.CommitTransaction(); - - if (!res) - return AOR_DB_INTERNAL_ERROR; // unexpected error; - - return AOR_OK; -} - -AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd) -{ - QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); - if (!result) - return AOR_NAME_NOT_EXIST; // account doesn't exist - - if (utf8length(new_uname) > MAX_ACCOUNT_STR) - return AOR_NAME_TOO_LONG; - - if (utf8length(new_passwd) > MAX_ACCOUNT_STR) - return AOR_PASS_TOO_LONG; - - normalizeString(new_uname); - normalizeString(new_passwd); - - std::string safe_new_uname = new_uname; - LoginDatabase.escape_string(safe_new_uname); - - if (!LoginDatabase.PExecute("UPDATE account SET v='0',s='0',username='%s',sha_pass_hash='%s' WHERE id='%d'", safe_new_uname.c_str(), - CalculateShaPassHash(new_uname, new_passwd).c_str(), accid)) - return AOR_DB_INTERNAL_ERROR; // unexpected error - - return AOR_OK; -} - -AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) -{ - std::string username; - - if (!GetName(accid, username)) - return AOR_NAME_NOT_EXIST; // account doesn't exist - - if (utf8length(new_passwd) > MAX_ACCOUNT_STR) - return AOR_PASS_TOO_LONG; - - normalizeString(username); - normalizeString(new_passwd); - - // also reset s and v to force update at next realmd login - if (!LoginDatabase.PExecute("UPDATE account SET v='0', s='0', sha_pass_hash='%s' WHERE id='%d'", - CalculateShaPassHash(username, new_passwd).c_str(), accid)) - return AOR_DB_INTERNAL_ERROR; // unexpected error - - return AOR_OK; -} - -uint32 AccountMgr::GetId(std::string username) -{ - LoginDatabase.escape_string(username); - QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); - if (!result) - return 0; - else - { - uint32 id = (*result)[0].GetUInt32(); - return id; - } -} - -uint32 AccountMgr::GetSecurity(uint32 acc_id) -{ - QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT gmlevel FROM account_access WHERE id = '%u'", acc_id); - if (result) - { - uint32 sec = (*result)[0].GetUInt32(); - return sec; - } - - return 0; -} - -uint32 AccountMgr::GetSecurity(uint32 acc_id, int32 realm_id) -{ - QueryResult_AutoPtr result = (realm_id == -1) - ? LoginDatabase.PQuery("SELECT gmlevel FROM account_access WHERE id = '%u' AND RealmID = '%d'", acc_id, realm_id) - : LoginDatabase.PQuery("SELECT gmlevel FROM account_access WHERE id = '%u' AND (RealmID = '%d' OR RealmID = '-1')", acc_id, realm_id); - if (result) - { - uint32 sec = (*result)[0].GetUInt32(); - return sec; - } - - return 0; -} - -bool AccountMgr::GetName(uint32 acc_id, std::string &name) -{ - QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id); - if (result) - { - name = (*result)[0].GetCppString(); - return true; - } - - return false; -} - -bool AccountMgr::CheckPassword(uint32 accid, std::string passwd) -{ - std::string username; - if (!GetName(accid, username)) - return false; - - normalizeString(username); - normalizeString(passwd); - - QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash='%s'", accid, CalculateShaPassHash(username, passwd).c_str()); - if (result) - return true; - - return false; -} - -bool AccountMgr::normalizeString(std::string& utf8str) -{ - wchar_t wstr_buf[MAX_ACCOUNT_STR+1]; - - size_t wstr_len = MAX_ACCOUNT_STR; - if (!Utf8toWStr(utf8str,wstr_buf,wstr_len)) - return false; - - std::transform(&wstr_buf[0], wstr_buf+wstr_len, &wstr_buf[0], wcharToUpperOnlyLatin); - - return WStrToUtf8(wstr_buf,wstr_len,utf8str); -} - -std::string AccountMgr::CalculateShaPassHash(std::string& name, std::string& password) -{ - Sha1Hash sha; - sha.Initialize(); - sha.UpdateData(name); - sha.UpdateData(":"); - sha.UpdateData(password); - sha.Finalize(); - - std::string encoded; - hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded); - - return encoded; -} - diff --git a/src/server/game/AccountMgr.h b/src/server/game/AccountMgr.h deleted file mode 100644 index b017bf1836a..00000000000 --- a/src/server/game/AccountMgr.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _ACCMGR_H -#define _ACCMGR_H - -#include - -#include "Common.h" -#include "Policies/Singleton.h" - -enum AccountOpResult -{ - AOR_OK, - AOR_NAME_TOO_LONG, - AOR_PASS_TOO_LONG, - AOR_NAME_ALREDY_EXIST, - AOR_NAME_NOT_EXIST, - AOR_DB_INTERNAL_ERROR -}; - -#define MAX_ACCOUNT_STR 16 - -class AccountMgr -{ - public: - AccountMgr(); - ~AccountMgr(); - - AccountOpResult CreateAccount(std::string username, std::string password); - AccountOpResult DeleteAccount(uint32 accid); - AccountOpResult ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd); - AccountOpResult ChangePassword(uint32 accid, std::string new_passwd); - bool CheckPassword(uint32 accid, std::string passwd); - - uint32 GetId(std::string username); - uint32 GetSecurity(uint32 acc_id); - uint32 GetSecurity(uint32 acc_id, int32 realm_id); - bool GetName(uint32 acc_id, std::string &name); - std::string CalculateShaPassHash(std::string& name, std::string& password); - - static bool normalizeString(std::string& utf8str); -}; - -#define accmgr Trinity::Singleton::Instance() -#endif - diff --git a/src/server/game/AchievementMgr.cpp b/src/server/game/AchievementMgr.cpp deleted file mode 100644 index e0a79fc71ed..00000000000 --- a/src/server/game/AchievementMgr.cpp +++ /dev/null @@ -1,2385 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "DBCEnums.h" -#include "ObjectMgr.h" -#include "World.h" -#include "WorldPacket.h" -#include "Database/DatabaseEnv.h" -#include "Policies/SingletonImp.h" - -#include "AchievementMgr.h" -#include "ArenaTeam.h" -#include "CellImpl.h" -#include "GameEventMgr.h" -#include "GridNotifiersImpl.h" -#include "Guild.h" -#include "Language.h" -#include "Player.h" -#include "ProgressBar.h" -#include "SpellMgr.h" - -#include "MapManager.h" -#include "BattleGround.h" -#include "BattleGroundAB.h" -#include "Map.h" -#include "InstanceData.h" - -INSTANTIATE_SINGLETON_1(AchievementGlobalMgr); - -namespace Trinity -{ - class AchievementChatBuilder - { - public: - AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id) - : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {} - void operator()(WorldPacket& data, int32 loc_idx) - { - char const* text = objmgr.GetTrinityString(i_textId,loc_idx); - - data << uint8(i_msgtype); - data << uint32(LANG_UNIVERSAL); - data << uint64(i_player.GetGUID()); - data << uint32(5); - data << uint64(i_player.GetGUID()); - data << uint32(strlen(text)+1); - data << text; - data << uint8(0); - data << uint32(i_achievementId); - } - - private: - Player const& i_player; - ChatMsg i_msgtype; - int32 i_textId; - uint32 i_achievementId; - }; -} // namespace Trinity - -bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) -{ - if (dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) - { - sLog.outErrorDb("Table `achievement_criteria_data` for criteria (Entry: %u) has wrong data type (%u), ignored.", criteria->ID,dataType); - return false; - } - - switch(criteria->requiredType) - { - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - break; - default: - sLog.outErrorDb("Table `achievement_criteria_data` has data for non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->requiredType); - return false; - } - - switch(dataType) - { - case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: - case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: - case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED: - case ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT: - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: - if (!creature.id || !objmgr.GetCreatureTemplate(creature.id)) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing creature id in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,creature.id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: - if (!classRace.class_id && !classRace.race_id) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.", - criteria->ID, criteria->requiredType,dataType); - return false; - } - if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing class in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,classRace.class_id); - return false; - } - if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing race in value2 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,classRace.race_id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: - if (health.percent < 1 || health.percent > 100) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) has wrong percent value in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,health.percent); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: - if (player_dead.own_team_flag > 1) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD (%u) has wrong boolean value1 (%u).", - criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: - { - SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id); - if (!spellEntry) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell id in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,(dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id); - return false; - } - if (aura.effect_idx >= 3) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell effect index in value2 (%u), ignored.", - criteria->ID, criteria->requiredType,(dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.effect_idx); - return false; - } - if (!spellEntry->EffectApplyAuraName[aura.effect_idx]) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has non-aura spell effect (ID: %u Effect: %u), ignores.", - criteria->ID, criteria->requiredType,(dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id,aura.effect_idx); - return false; - } - return true; - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: - if (!GetAreaEntryByAreaID(area.id)) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA (%u) has wrong area id in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,area.id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: - if (level.minlevel < 0 || level.minlevel > STRONG_MAX_LEVEL) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) has wrong minlevel in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,level.minlevel); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: - if (gender.gender > GENDER_NONE) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) has wrong gender in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,gender.gender); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY: - if (difficulty.difficulty >= MAX_DIFFICULTY) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY (%u) has wrong difficulty in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,difficulty.difficulty); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: - if (map_players.maxcount <= 0) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) has wrong max players count in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,map_players.maxcount); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: - if (team.team != ALLIANCE && team.team != HORDE) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) has unknown team in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,team.team); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: - if (drunk.state >= MAX_DRUNKEN) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) has unknown drunken state in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,drunk.state); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: - if (!sHolidaysStore.LookupEntry(holiday.id)) - { - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) has unknown holiday in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,holiday.id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: - return true; // not check correctness node indexes - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: - if (equipped_item.item_quality >= MAX_ITEM_QUALITY) - { - sLog.outErrorDb("Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM (%u) has unknown quality state in value1 (%u), ignored.", - criteria->ID, criteria->requiredType,dataType,equipped_item.item_quality); - return false; - } - return true; - default: - sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->requiredType,dataType); - return false; - } -} - -bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const -{ - switch(dataType) - { - case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: - if (!target || target->GetTypeId() != TYPEID_UNIT) - return false; - return target->GetEntry() == creature.id; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return false; - if (classRace.class_id && classRace.class_id != target->ToPlayer()->getClass()) - return false; - if (classRace.race_id && classRace.race_id != target->ToPlayer()->getRace()) - return false; - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return false; - return target->GetHealth()*100 <= health.percent*target->GetMaxHealth(); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: - if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || target->ToPlayer()->GetDeathTimer() == 0) - return false; - // flag set == must be same team, not set == different team - return (target->ToPlayer()->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: - return source->HasAuraEffect(aura.spell_id,aura.effect_idx); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: - { - uint32 zone_id,area_id; - source->GetZoneAndAreaId(zone_id,area_id); - return area.id == zone_id || area.id == area_id; - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: - return target && target->HasAuraEffect(aura.spell_id,aura.effect_idx); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: - return miscvalue1 >= value.minvalue; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: - if (!target) - return false; - return target->getLevel() >= level.minlevel; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: - if (!target) - return false; - return target->getGender() == gender.gender; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED: - return false; // always fail - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY: - return source->GetMap()->GetSpawnMode() == difficulty.difficulty; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: - return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return false; - return target->ToPlayer()->GetTeam() == team.team; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: - return Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: - return IsHolidayActive(HolidayIds(holiday.id)); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: - { - BattleGround* bg = source->GetBattleGround(); - if (!bg) - return false; - return bg->IsTeamScoreInRange(source->GetTeam() == ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score); - } - case ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT: - { - if (!source->IsInWorld()) - return false; - Map* map = source->GetMap(); - if (!map->IsDungeon()) - { - sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-dungeon/non-raid map %u", - ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT, criteria_id, map->GetId()); - return false; - } - InstanceData* data = ((InstanceMap*)map)->GetInstanceData(); - if (!data) - { - sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map does not have a instance script", - ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT, criteria_id, map->GetId()); - return false; - } - return data->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1); - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: - { - ItemPrototype const *pProto = objmgr.GetItemPrototype(miscvalue1); - if (!pProto) - return false; - return pProto->ItemLevel >= equipped_item.item_level && pProto->Quality >= equipped_item.item_quality; - } - } - return false; -} - -bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const -{ - for (Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr) - if (!itr->Meets(criteria_id, source, target, miscvalue)) - return false; - - return true; -} - -AchievementMgr::AchievementMgr(Player *player) -{ - m_player = player; -} - -AchievementMgr::~AchievementMgr() -{ -} - -void AchievementMgr::Reset() -{ - for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) - { - WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4); - data << uint32(iter->first); - m_player->SendDirectMessage(&data); - } - - for (CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) - { - WorldPacket data(SMSG_CRITERIA_DELETED,4); - data << uint32(iter->first); - m_player->SendDirectMessage(&data); - } - - m_completedAchievements.clear(); - m_criteriaProgress.clear(); - DeleteFromDB(m_player->GetGUIDLow()); - - // re-fill data - CheckAllAchievementCriteria(); -} - -void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2) -{ - if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0) - sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2); - - if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) - return; - - AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) - { - AchievementCriteriaEntry const *achievementCriteria = (*i); - - AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); - if (!achievement) - continue; - - // don't update already completed criteria - if (IsCompletedCriteria(achievementCriteria,achievement)) - continue; - - switch (type) - { - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset - if (achievementCriteria->healing_done.flag == miscvalue1 && - achievementCriteria->healing_done.mapid == miscvalue2) - SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET); - break; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset - // reset only the criteria having the miscvalue1 condition - if (achievementCriteria->win_rated_arena.flag == miscvalue1) - SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET); - break; - default: // reset all cases - break; - } - } -} - -void AchievementMgr::DeleteFromDB(uint32 lowguid) -{ - CharacterDatabase.BeginTransaction (); - CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid); - CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid); - CharacterDatabase.CommitTransaction (); -} - -void AchievementMgr::SaveToDB() -{ - if (!m_completedAchievements.empty()) - { - bool need_execute = false; - std::ostringstream ssdel; - std::ostringstream ssins; - for (CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) - { - if (!iter->second.changed) - continue; - - /// first new/changed record prefix - if (!need_execute) - { - ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN ("; - ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES "; - need_execute = true; - } - /// next new/changed record prefix - else - { - ssdel << ", "; - ssins << ", "; - } - - // new/changed record data - ssdel << iter->first; - ssins << "("<GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")"; - - /// mark as saved in db - iter->second.changed = false; - } - - if (need_execute) - ssdel << ")"; - - if (need_execute) - { - CharacterDatabase.Execute(ssdel.str().c_str()); - CharacterDatabase.Execute(ssins.str().c_str()); - } - } - - if (!m_criteriaProgress.empty()) - { - /// prepare deleting and insert - bool need_execute_del = false; - bool need_execute_ins = false; - std::ostringstream ssdel; - std::ostringstream ssins; - for (CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) - { - if (!iter->second.changed) - continue; - - // deleted data (including 0 progress state) - { - /// first new/changed record prefix (for any counter value) - if (!need_execute_del) - { - ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN ("; - need_execute_del = true; - } - /// next new/changed record prefix - else - ssdel << ", "; - - // new/changed record data - ssdel << iter->first; - } - - // store data only for real progress - if (iter->second.counter != 0) - { - /// first new/changed record prefix - if (!need_execute_ins) - { - ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES "; - need_execute_ins = true; - } - /// next new/changed record prefix - else - ssins << ", "; - - // new/changed record data - ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")"; - } - - /// mark as updated in db - iter->second.changed = false; - } - - if (need_execute_del) // DELETE ... IN (.... _)_ - ssdel << ")"; - - if (need_execute_del || need_execute_ins) - { - if (need_execute_del) - CharacterDatabase.Execute(ssdel.str().c_str()); - if (need_execute_ins) - CharacterDatabase.Execute(ssins.str().c_str()); - } - } -} - -void AchievementMgr::LoadFromDB(QueryResult_AutoPtr achievementResult, QueryResult_AutoPtr criteriaResult) -{ - if (achievementResult) - { - do - { - Field *fields = achievementResult->Fetch(); - - uint32 achievement_id = fields[0].GetUInt32(); - - // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements() - if (!sAchievementStore.LookupEntry(achievement_id)) - continue; - - CompletedAchievementData& ca = m_completedAchievements[achievement_id]; - ca.date = time_t(fields[1].GetUInt64()); - ca.changed = false; - } while (achievementResult->NextRow()); - } - - if (criteriaResult) - { - do - { - Field *fields = criteriaResult->Fetch(); - - uint32 id = fields[0].GetUInt32(); - uint32 counter = fields[1].GetUInt32(); - time_t date = time_t(fields[2].GetUInt64()); - - AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id); - if (!criteria) - { - // we will remove not existed criteria for all characters - sLog.outError("Non-existing achievement criteria %u data removed from table `character_achievement_progress`.",id); - CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id); - continue; - } - - if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL)) - continue; - - CriteriaProgress& progress = m_criteriaProgress[id]; - progress.counter = counter; - progress.date = date; - progress.changed = false; - } while (criteriaResult->NextRow()); - } - -} - -void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) -{ - if (GetPlayer()->GetSession()->PlayerLoading()) - return; - - // Don't send for achievements with ACHIEVEMENT_FLAG_TRACKING - if (achievement->flags & ACHIEVEMENT_FLAG_TRACKING) - return; - - #ifdef TRINITY_DEBUG - if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0) - sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID); - #endif - - if (Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId())) - { - Trinity::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); - Trinity::LocalizedPacketDo say_do(say_builder); - guild->BroadcastWorker(say_do,GetPlayer()); - } - - if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) - { - // broadcast realm first reached - WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+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); - } - // if player is in world he can tell his friends about new achievement - else if (GetPlayer()->IsInWorld()) - { - CellPair p = Trinity::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); - - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Trinity::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); - Trinity::LocalizedPacketDo say_do(say_builder); - Trinity::PlayerDistWorker > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do); - TypeContainerVisitor >, WorldTypeMapContainer > message(say_worker); - cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY)); - } - - WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8); - data.append(GetPlayer()->GetPackGUID()); - data << uint32(achievement->ID); - data << uint32(secsToTimeBitFields(time(NULL))); - data << uint32(0); - GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true); -} - -void AchievementMgr::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) -{ - WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8); - data << uint32(entry->ID); - - // the counter is packed like a packed Guid - data.appendPackGUID(progress->counter); - - data.append(GetPlayer()->GetPackGUID()); - if (!entry->timeLimit) - data << uint32(0); - else - data << uint32(timedCompleted ? 0 : 1); // this are some flags, 1 is for keeping the counter at 0 in client - data << uint32(secsToTimeBitFields(progress->date)); - data << uint32(timeElapsed); // time elapsed in seconds - data << uint32(0); // unk - GetPlayer()->SendDirectMessage(&data); -} - -/** - * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet - */ -void AchievementMgr::CheckAllAchievementCriteria() -{ - // suppress sending packets - for (uint32 i=0; iGetSession()->GetSecurity() > SEC_PLAYER) - return; - - AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) - { - AchievementCriteriaEntry const *achievementCriteria = (*i); - - AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); - if (!achievement) - continue; - - if ((achievement->factionFlag == ACHIEVEMENT_FACTION_HORDE && GetPlayer()->GetTeam() != HORDE) || - (achievement->factionFlag == ACHIEVEMENT_FACTION_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE)) - continue; - - // don't update already completed criteria - if (IsCompletedCriteria(achievementCriteria,achievement)) - continue; - - switch (type) - { - // std. case: increment at 1 - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */ - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - // std case: increment at miscvalue1 - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:/* FIXME: for online player only currently */ - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); - break; - // std case: high value at miscvalue1 - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */ - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST); - break; - - // specialized cases - - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId()) - continue; - - if (achievementCriteria->win_bg.additionalRequirement1_type) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit)) - continue; - } - // some hardcoded requirements - else - { - BattleGround* bg = GetPlayer()->GetBattleGround(); - if (!bg) - continue; - - switch(achievementCriteria->referredAchievement) - { - case 161: // AB, Overcome a 500 resource disadvantage - { - if (bg->GetTypeID(true) != BATTLEGROUND_AB) - continue; - if (!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam())) - continue; - break; - } - case 156: // AB, win while controlling all 5 flags (all nodes) - case 784: // EY, win while holding 4 bases (all nodes) - { - if (!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam())) - continue; - break; - } - case 1762: // SA, win without losing any siege vehicles - case 2192: // SA, win without losing any siege vehicles - continue; // not implemented - } - } - - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (achievementCriteria->kill_creature.creatureID != miscvalue1) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit)) - continue; - - SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); - break; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - // update at loading or specific skill update - if (miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID) - continue; - if (uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) - SetCriteriaProgress(achievementCriteria, skillvalue); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - // update at loading or specific skill update - if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID) - continue; - if (uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID)) - SetCriteriaProgress(achievementCriteria, maxSkillvalue); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - if (m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end()) - SetCriteriaProgress(achievementCriteria, 1); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - { - uint32 counter =0; - for (QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr != GetPlayer()->getQuestStatusMap().end(); itr++) - if (itr->second.m_rewarded) - counter++; - SetCriteriaProgress(achievementCriteria, counter); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - { - // speedup for non-login case - if (miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID) - continue; - - uint32 counter =0; - for (QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr != GetPlayer()->getQuestStatusMap().end(); itr++) - { - Quest const* quest = objmgr.GetQuestTemplate(itr->first); - if (itr->second.m_rewarded && quest && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID) - counter++; - } - SetCriteriaProgress(achievementCriteria, counter); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) - continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter - bool notfit = false; - for (int j = 0; j < MAX_ARENA_SLOT; ++j) - { - if (achievIdByArenaSlot[j] == achievement->ID) - { - BattleGround* bg = GetPlayer()->GetBattleGround(); - if (!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j) - notfit = true; - - break; - } - } - if (notfit) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - - Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : MapManager::Instance().FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId()); - if (!map || !map->IsDungeon()) - continue; - - // search case - bool found = false; - for (int j = 0; achievIdForDangeon[j][0]; ++j) - { - if (achievIdForDangeon[j][0] == achievement->ID) - { - if (map->IsRaid()) - { - // if raid accepted (ignore difficulty) - if (!achievIdForDangeon[j][2]) - break; // for - } - else if (GetPlayer()->GetDungeonDifficulty() == DUNGEON_DIFFICULTY_NORMAL) - { - // dungeon in normal mode accepted - if (!achievIdForDangeon[j][1]) - break; // for - } - else - { - // dungeon in heroic mode accepted - if (!achievIdForDangeon[j][3]) - break; // for - } - - found = true; - break; // for - } - } - if (!found) - continue; - - //FIXME: work only for instances where max == min for players - if (((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - - } - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - - // if team check required: must kill by opposition faction - if (achievement->ID == 318 && miscvalue2 == GetPlayer()->GetTeam()) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit)) - continue; - - // miscvalue1 is the ingame fallheight*100 as stored in dbc - SetCriteriaProgress(achievementCriteria, miscvalue1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (miscvalue2 != achievementCriteria->death_from.type) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - { - // if miscvalues != 0, it contains the questID. - if (miscvalue1) - { - if (miscvalue1 != achievementCriteria->complete_quest.questID) - continue; - } - else - { - // login case. - if (!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) - continue; - } - - // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case - switch(achievement->ID) - { - case 31: - case 1275: - case 1276: - case 1277: - case 1282: - case 1789: - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit)) - continue; - break; - } - default: - break; - } - - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - { - if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data) - continue; - - if (!data->Meets(GetPlayer(),unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - { - if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) - continue; - - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data) - continue; - - if (!data->Meets(GetPlayer(),unit)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - if (miscvalue1 && miscvalue1 != achievementCriteria->learn_spell.spellID) - continue; - - if (GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) - SetCriteriaProgress(achievementCriteria, 1); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - { - // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored) - // miscvalue2=count of item loot - if (!miscvalue1 || !miscvalue2) - continue; - if (miscvalue1 != achievementCriteria->loot_type.lootType) - continue; - - // zone specific - if (achievementCriteria->loot_type.lootTypeCount == 1) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit)) - continue; - } - - SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - // speedup for non-login case - if (miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); - break; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - // miscvalue1 contains the personal rating - if (!miscvalue1) // no update at login - continue; - - // additional requirements - if (achievementCriteria->win_rated_arena.flag == ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit,miscvalue1)) - { - // reset the progress as we have a win without the requirement. - SetCriteriaProgress(achievementCriteria, 0); - continue; - } - } - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - if (achievementCriteria->use_item.itemID != miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - // You _have_ to loot that item, just owning it when logging in does _not_ count! - if (!miscvalue1) - continue; - if (miscvalue1 != achievementCriteria->own_item.itemID) - continue; - SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - { - WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); - if (!worldOverlayEntry) - break; - - bool matchFound = false; - for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) - { - uint32 area_id = worldOverlayEntry->areatableID[j]; - if (!area_id) // array have 0 only in empty tail - break; - - int32 exploreFlag = GetAreaFlagByAreaID(area_id); - if (exploreFlag < 0) - continue; - - uint32 playerIndexOffset = uint32(exploreFlag) / 32; - uint32 mask = 1<< (uint32(exploreFlag) % 32); - - if (GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) - { - matchFound = true; - break; - } - } - - if (matchFound) - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount()); - break; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - { - // skip faction check only at loading - if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID) - continue; - - int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); - if (reputation > 0) - SetCriteriaProgress(achievementCriteria, reputation); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - { - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount()); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - { - // skip for login case - if (!miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - { - // miscvalue1 = itemid - // miscvalue2 = itemSlot - if (!miscvalue1) - continue; - - if (miscvalue2 != achievementCriteria->equip_epic_item.itemSlot) - continue; - - // check item level and quality via achievement_criteria_data - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), 0, miscvalue1)) - continue; - - SetCriteriaProgress(achievementCriteria, 1); - break; - } - - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - { - // miscvalue1 = itemid - // miscvalue2 = diced value - if (!miscvalue1) - continue; - if (miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue) - continue; - - ItemPrototype const *pProto = objmgr.GetItemPrototype(miscvalue1); - if (!pProto) - continue; - - // check item level via achievement_criteria_data - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), 0, pProto->ItemLevel)) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - { - // miscvalue1 = emote - if (!miscvalue1) - continue; - if (miscvalue1 != achievementCriteria->do_emote.emoteID) - continue; - if (achievementCriteria->do_emote.count) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(),unit)) - continue; - } - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - { - if (!miscvalue1) - continue; - - if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP) - { - if (GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid) - continue; - - // map specific case (BG in fact) expected player targeted damage/heal - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) - continue; - } - - SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - // miscvalue1 = item_id - if (!miscvalue1) - continue; - if (miscvalue1 != achievementCriteria->equip_item.itemID) - continue; - - SetCriteriaProgress(achievementCriteria, 1); - break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - // miscvalue1 = go entry - if (!miscvalue1) - continue; - if (miscvalue1 != achievementCriteria->use_gameobject.goEntry) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - if (!miscvalue1) - continue; - if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - { - if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine) - continue; - - uint32 spellCount = 0; - for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); - spellIter != GetPlayer()->GetSpellMap().end(); - ++spellIter) - { - SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first); - for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) - { - if (skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine) - spellCount++; - } - } - SetCriteriaProgress(achievementCriteria, spellCount); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - - if (achievementCriteria->win_duel.duelCount) - { - // those requirements couldn't be found in the dbc - AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); - if (!data) - continue; - - if (!data->Meets(GetPlayer(),unit)) - continue; - } - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount()); - break; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount()); - break; - case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount()); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - { - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if (!miscvalue1) - continue; - ItemPrototype const* proto = ObjectMgr::GetItemPrototype(miscvalue1); - if (!proto || proto->Quality < ITEM_QUALITY_EPIC) - continue; - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - { - if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine) - continue; - - uint32 spellCount = 0; - for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); - spellIter != GetPlayer()->GetSpellMap().end(); - ++spellIter) - { - SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first); - for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) - if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine) - spellCount++; - } - SetCriteriaProgress(achievementCriteria, spellCount); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS)); - break; - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID) - continue; - - SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST); - break; - // std case: not exist in DBC, not triggered in code as result - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING: - break; - // FIXME: not triggered in code as result, need to implement - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE: - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL: - case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - break; // Not implemented yet :( - } - if (IsCompletedCriteria(achievementCriteria,achievement)) - CompletedCriteriaFor(achievement); - - // check again the completeness for SUMM and REQ COUNT achievements, - // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria - if (achievement->flags & ACHIEVEMENT_FLAG_SUMM) - { - if (IsCompletedAchievement(achievement)) - CompletedAchievement(achievement); - } - - if (AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID)) - { - for (AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) - if (IsCompletedAchievement(*itr)) - CompletedAchievement(*itr); - } - } -} - -static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 }; -static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 }; - -bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) -{ - // counter can never complete - if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER) - return false; - - if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - { - // someone on this realm has already completed that achievement - if (achievementmgr.IsRealmCompleted(achievement)) - return false; - } - - CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID); - if (itr == m_criteriaProgress.end()) - return false; - - CriteriaProgress const* progress = &itr->second; - - switch(achievementCriteria->requiredType) - { - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - return progress->counter >= achievementCriteria->win_bg.winCount; - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - return progress->counter >= achievementCriteria->kill_creature.creatureCount; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - { - // skip wrong class achievements - for (int i = 1; i < MAX_CLASSES; ++i) - if (achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass()) - return false; - - // skip wrong race achievements - for (int i = 1; i < MAX_RACES; ++i) - if (achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace()) - return false; - - // appropriate class/race or not class/race specific - return progress->counter >= achievementCriteria->reach_level.level; - } - case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - return progress->counter >= achievementCriteria->healing_done.count; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - return progress->counter >= achievementCriteria->complete_daily_quest.questCount; - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - return progress->counter >= achievementCriteria->be_spell_target.spellCount; - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - return progress->counter >= achievementCriteria->cast_spell.castCount; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - return progress->counter >= achievementCriteria->own_item.itemCount; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - return progress->counter >= achievementCriteria->win_rated_arena.count; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - return progress->counter >= achievementCriteria->use_item.itemCount; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - return progress->counter >= achievementCriteria->loot_item.itemCount; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - return progress->counter >= achievementCriteria->equip_epic_item.count; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - return progress->counter >= achievementCriteria->roll_greed_on_loot.count; - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - return progress->counter >= achievementCriteria->hk_class.count; - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - return progress->counter >= achievementCriteria->hk_race.count; - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - return progress->counter >= achievementCriteria->do_emote.count; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - return progress->counter >= achievementCriteria->equip_item.count; - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - return progress->counter >= achievementCriteria->loot_money.goldInCopper; - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - return progress->counter >= achievementCriteria->use_gameobject.useCount; - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - return progress->counter >= achievementCriteria->win_duel.duelCount; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - return progress->counter >= achievementCriteria->loot_type.lootTypeCount; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - return progress->counter >= achievementCriteria->learn_skill_line.spellCount; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - return progress->counter >= achievementCriteria->honorable_kill.killCount; - // handle all statistic-only criteria here - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: - case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: - return false; - } - return false; -} - -void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement) -{ - // counter can never complete - if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER) - return; - - // already completed and stored - if (HasAchieved(achievement)) - return; - - if (IsCompletedAchievement(achievement)) - CompletedAchievement(achievement); -} - -bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) -{ - // counter can never complete - if (entry->flags & ACHIEVEMENT_FLAG_COUNTER) - return false; - - // for achievement with referenced achievement criterias get from referenced and counter from self - uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID; - uint32 achievmentForTestCount = entry->count; - - AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId); - if (!cList) - return false; - uint32 count = 0; - - // For SUMM achievements, we have to count the progress of each criteria of the achievement. - // Oddly, the target count is NOT countained in the achievement, but in each individual criteria - if (entry->flags & ACHIEVEMENT_FLAG_SUMM) - { - for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) - { - AchievementCriteriaEntry const* criteria = *itr; - - CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID); - if (itrProgress == m_criteriaProgress.end()) - continue; - - CriteriaProgress const* progress = &itrProgress->second; - count += progress->counter; - - // for counters, field4 contains the main count requirement - if (count >= criteria->raw.count) - return true; - } - return false; - } - - // Default case - need complete all or - bool completed_all = true; - for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) - { - AchievementCriteriaEntry const* criteria = *itr; - - bool completed = IsCompletedCriteria(criteria,entry); - - // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL - if (completed) - ++count; - else - completed_all = false; - - // completed as have req. count of completed criterias - if (achievmentForTestCount > 0 && achievmentForTestCount <= count) - return true; - } - - // all criterias completed requirement - if (completed_all && achievmentForTestCount == 0) - return true; - - return false; -} - -void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype) -{ - // Don't allow to cheat - doing timed achievements without timer active - TimedAchievementMap::iterator timedIter = m_timedAchievements.find(entry->ID); - if (entry->timeLimit && timedIter == m_timedAchievements.end()) - return; - - if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0) - sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow()); - - CriteriaProgress *progress = NULL; - - CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); - - if (iter == m_criteriaProgress.end()) - { - // not create record for 0 counter but allow it for timed achievements - // we will need to send 0 progress to client to start the timer - if (changeValue == 0 && !entry->timeLimit) - return; - - progress = &m_criteriaProgress[entry->ID]; - progress->counter = changeValue; - progress->date = time(NULL); - } - else - { - progress = &iter->second; - - uint32 newValue = 0; - switch(ptype) - { - case PROGRESS_SET: - newValue = changeValue; - break; - case PROGRESS_ACCUMULATE: - { - // avoid overflow - uint32 max_value = std::numeric_limits::max(); - newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value; - break; - } - case PROGRESS_HIGHEST: - newValue = progress->counter < changeValue ? changeValue : progress->counter; - break; - } - - // not update (not mark as changed) if counter will have same value - if (progress->counter == newValue && !entry->timeLimit) - return; - - progress->counter = newValue; - } - - progress->changed = true; - - uint32 timeElapsed = 0; - bool timedCompleted = false; - - if (entry->timeLimit) - { - //has to exist else we wouldn't be here - timedCompleted = IsCompletedCriteria(entry, sAchievementStore.LookupEntry(entry->referredAchievement)); - // Client expects this in packet - timeElapsed = entry->timeLimit - (timedIter->second/IN_MILISECONDS); - - // Remove the timer, we wont need it anymore - if (timedCompleted) - m_timedAchievements.erase(timedIter); - } - - SendCriteriaUpdate(entry, progress, timeElapsed, timedCompleted); -} - -void AchievementMgr::UpdateTimedAchievements(uint32 timeDiff) -{ - if (!m_timedAchievements.empty()) - { - for (TimedAchievementMap::iterator itr = m_timedAchievements.begin(); itr != m_timedAchievements.end();) - { - // Time is up, remove timer and reset progress - if (itr->second <= timeDiff) - { - AchievementCriteriaEntry const *entry = sAchievementCriteriaStore.LookupEntry(itr->first); - SetCriteriaProgress(entry, 0, PROGRESS_SET); - m_timedAchievements.erase(itr++); - } - else - { - itr->second -= timeDiff; - ++itr; - } - } - } -} - -void AchievementMgr::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) -{ - AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetTimedAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) - { - if ((*i)->timerStartEvent != entry) - continue; - - AchievementEntry const *achievement = sAchievementStore.LookupEntry((*i)->referredAchievement); - if (m_timedAchievements.find((*i)->ID) == m_timedAchievements.end() && !IsCompletedCriteria(*i, achievement)) - { - // Start the timer - m_timedAchievements[(*i)->ID] = (*i)->timeLimit * IN_MILISECONDS; - - // and at client too - SetCriteriaProgress(*i, 0, PROGRESS_SET); - } - } -} - -void AchievementMgr::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) -{ - AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetTimedAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i) - { - if ((*i)->timerStartEvent != entry) - continue; - - TimedAchievementMap::iterator timedIter = m_timedAchievements.find((*i)->ID); - // We don't have timer for this achievement - if (timedIter == m_timedAchievements.end()) - continue; - - // 0 the progress to avoid saving to db - SetCriteriaProgress(*i, 0, PROGRESS_SET); - - // Remove the timer - m_timedAchievements.erase(timedIter); - } -} - -void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) -{ - sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID); - - if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) - return; - - if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement)) - return; - - SendAchievementEarned(achievement); - CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; - ca.date = time(NULL); - ca.changed = true; - - // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement - // TODO: where do set this instead? - if (!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - achievementmgr.SetRealmCompleted(achievement); - - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT); - - // reward items and titles if any - AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement); - - // no rewards - if (!reward) - return; - - // titles - if (uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1]) - { - if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) - GetPlayer()->SetTitle(titleEntry); - } - - // mail - if (reward->sender) - { - Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL; - - int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex(); - - // subject and text - std::string subject = reward->subject; - std::string text = reward->text; - if (loc_idx >= 0) - { - if (AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement)) - { - if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty()) - subject = loc->subject[loc_idx]; - if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty()) - text = loc->text[loc_idx]; - } - } - - MailDraft draft(subject, text); - - if (item) - { - // save new item before send - item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted - - // item - draft.AddItem(item); - } - - draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender)); - } -} - -void AchievementMgr::SendAllAchievementData() -{ - WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, m_completedAchievements.size()*8+4+m_criteriaProgress.size()*38+4); - BuildAllDataPacket(&data); - GetPlayer()->GetSession()->SendPacket(&data); -} - -void AchievementMgr::SendRespondInspectAchievements(Player* player) -{ - WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 9+m_completedAchievements.size()*8+4+m_criteriaProgress.size()*38+4); - data.append(GetPlayer()->GetPackGUID()); - BuildAllDataPacket(&data); - player->GetSession()->SendPacket(&data); -} - -/** - * used by SMSG_RESPOND_INSPECT_ACHIEVEMENT and SMSG_ALL_ACHIEVEMENT_DATA - */ -void AchievementMgr::BuildAllDataPacket(WorldPacket *data) -{ - AchievementEntry const *achievement; - for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) - { - // Skip tracking - they bug client UI - achievement = sAchievementStore.LookupEntry(iter->first); - if (achievement->flags & ACHIEVEMENT_FLAG_TRACKING) - continue; - - *data << uint32(iter->first); - *data << uint32(secsToTimeBitFields(iter->second.date)); - } - *data << int32(-1); - - for (CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) - { - *data << uint32(iter->first); - data->appendPackGUID(iter->second.counter); - data->append(GetPlayer()->GetPackGUID()); - *data << uint32(0); - *data << uint32(secsToTimeBitFields(iter->second.date)); - *data << uint32(0); - *data << uint32(0); - } - - *data << int32(-1); -} - -bool AchievementMgr::HasAchieved(AchievementEntry const* achievement) const -{ - return m_completedAchievements.find(achievement->ID) != m_completedAchievements.end(); -} - -//========================================================== -AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type) -{ - return m_AchievementCriteriasByType[type]; -} - -AchievementCriteriaEntryList const& AchievementGlobalMgr::GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type) -{ - return m_AchievementCriteriasByTimedType[type]; -} - -void AchievementGlobalMgr::LoadAchievementCriteriaList() -{ - if (sAchievementCriteriaStore.GetNumRows() == 0) - { - barGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded 0 achievement criteria."); - return; - } - - barGoLink bar(sAchievementCriteriaStore.GetNumRows()); - for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) - { - bar.step(); - - AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); - if (!criteria) - continue; - - m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria); - m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria); - - if (criteria->timeLimit) - m_AchievementCriteriasByTimedType[criteria->timedType].push_back(criteria); - } - - sLog.outString(); - sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size()); -} - -void AchievementGlobalMgr::LoadAchievementReferenceList() -{ - if (sAchievementStore.GetNumRows() == 0) - { - barGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded 0 achievement references."); - return; - } - - uint32 count = 0; - barGoLink bar(sAchievementStore.GetNumRows()); - for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId) - { - bar.step(); - - AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId); - if (!achievement || !achievement->refAchievement) - continue; - - m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement); - ++count; - } - - sLog.outString(); - sLog.outString(">> Loaded %u achievement references.",count); -} - -void AchievementGlobalMgr::LoadAchievementCriteriaData() -{ - m_criteriaDataMap.clear(); // need for reload case - - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data"); - - if (!result) - { - barGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty."); - return; - } - - uint32 count = 0; - uint32 disabled_count = 0; - barGoLink bar(result->GetRowCount()); - do - { - bar.step(); - Field *fields = result->Fetch(); - uint32 criteria_id = fields[0].GetUInt32(); - - AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id); - - if (!criteria) - { - sLog.outErrorDb("Table `achievement_criteria_data` has data for non-existing criteria (Entry: %u), ignore.", criteria_id); - continue; - } - - AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32()); - - if (!data.IsValid(criteria)) - { - continue; - } - - // this will allocate empty data set storage - AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id]; - dataSet.SetCriteriaId(criteria_id); - - if (data.dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED) - ++disabled_count; - - // add real data only for not NONE data types - if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) - dataSet.Add(data); - - // counting data by and data types - ++count; - } while (result->NextRow()); - - // post loading checks - for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) - { - AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); - if (!criteria) - continue; - - switch(criteria->requiredType) - { - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - if (!criteria->win_bg.additionalRequirement1_type) - continue; - break; - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - break; // any cases - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - { - AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement); - if (!achievement) - continue; - - // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case - switch(achievement->ID) - { - case 31: - case 1275: - case 1276: - case 1277: - case 1282: - case 1789: - break; - default: - continue; - } - } - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - break; // any cases - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: // any cases - break; - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases - break; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases - if (criteria->win_rated_arena.flag != ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE) - continue; - break; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: // any cases - break; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - break; // any cases - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - break; // any cases - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases - if (criteria->do_emote.count == 0) - continue; - break; - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - break; // any cases - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics - if (criteria->win_duel.duelCount == 0) - continue; - break; - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases - if (criteria->loot_type.lootTypeCount != 1) - continue; - break; - default: // type not use DB data, ignore - continue; - } - - if (!GetCriteriaDataSet(criteria)) - sLog.outErrorDb("Table `achievement_criteria_data` does not have expected data for criteria (Entry: %u Type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement); - } - - sLog.outString(); - sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count); -} - -void AchievementGlobalMgr::LoadCompletedAchievements() -{ - QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement"); - - if (!result) - { - barGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty."); - return; - } - - barGoLink bar(result->GetRowCount()); - do - { - bar.step(); - Field *fields = result->Fetch(); - - uint32 achievement_id = fields[0].GetUInt32(); - if (!sAchievementStore.LookupEntry(achievement_id)) - { - // we will remove not existed achievement for all characters - sLog.outError("Non-existing achievement %u data removed from table `character_achievement`.",achievement_id); - CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id); - continue; - } - - m_allCompletedAchievements.insert(achievement_id); - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size()); -} - -void AchievementGlobalMgr::LoadRewards() -{ - m_achievementRewards.clear(); // need for reload case - - // 0 1 2 3 4 5 6 - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward"); - - if (!result) - { - barGoLink bar(1); - - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty."); - return; - } - - uint32 count = 0; - barGoLink bar(result->GetRowCount()); - - do - { - bar.step(); - - Field *fields = result->Fetch(); - uint32 entry = fields[0].GetUInt32(); - const AchievementEntry* pAchievement = sAchievementStore.LookupEntry(entry); - if (!pAchievement) - { - sLog.outErrorDb("Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry); - continue; - } - - AchievementReward reward; - reward.titleId[0] = fields[1].GetUInt32(); - reward.titleId[1] = fields[2].GetUInt32(); - reward.itemId = fields[3].GetUInt32(); - reward.sender = fields[4].GetUInt32(); - reward.subject = fields[5].GetCppString(); - reward.text = fields[6].GetCppString(); - - // must be title or mail at least - if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender) - { - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry); - continue; - } - - if (pAchievement->factionFlag == ACHIEVEMENT_FACTION_ANY && ((reward.titleId[0] == 0) != (reward.titleId[1] == 0))) - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]); - - if (reward.titleId[0]) - { - CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]); - if (!titleEntry) - { - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]); - reward.titleId[0] = 0; - } - } - - if (reward.titleId[1]) - { - CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]); - if (!titleEntry) - { - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]); - reward.titleId[1] = 0; - } - } - - //check mail data before item for report including wrong item case - if (reward.sender) - { - if (!objmgr.GetCreatureTemplate(reward.sender)) - { - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender); - reward.sender = 0; - } - } - else - { - if (reward.itemId) - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) does not have sender data but has item reward, item will not be rewarded.", entry); - - if (!reward.subject.empty()) - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) does not have sender data but has mail subject.", entry); - - if (!reward.text.empty()) - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) does not have sender data but has mail text.", entry); - } - - if (reward.itemId) - { - if (!objmgr.GetItemPrototype(reward.itemId)) - { - sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will not contain item.", entry, reward.itemId); - reward.itemId = 0; - } - } - - m_achievementRewards[entry] = reward; - ++count; - - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u achievement rewards", count); -} - -void AchievementGlobalMgr::LoadRewardLocales() -{ - m_achievementRewardLocales.clear(); // need for reload case - - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,subject_loc1,text_loc1,subject_loc2,text_loc2,subject_loc3,text_loc3,subject_loc4,text_loc4,subject_loc5,text_loc5,subject_loc6,text_loc6,subject_loc7,text_loc7,subject_loc8,text_loc8 FROM locales_achievement_reward"); - - if (!result) - { - barGoLink bar(1); - - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded 0 achievement reward locale strings."); - sLog.outString(">> DB table `locales_achievement_reward` is empty."); - return; - } - - barGoLink bar(result->GetRowCount()); - - do - { - Field *fields = result->Fetch(); - bar.step(); - - uint32 entry = fields[0].GetUInt32(); - - if (m_achievementRewards.find(entry) == m_achievementRewards.end()) - { - sLog.outErrorDb("Table `locales_achievement_reward` (Entry: %u) has locale strings for non-existing achievement reward.", entry); - continue; - } - - AchievementRewardLocale& data = m_achievementRewardLocales[entry]; - - for (int i = 1; i < MAX_LOCALE; ++i) - { - std::string str = fields[1+2*(i-1)].GetCppString(); - if (!str.empty()) - { - int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i)); - if (idx >= 0) - { - if (data.subject.size() <= size_t(idx)) - data.subject.resize(idx+1); - - data.subject[idx] = str; - } - } - str = fields[1+2*(i-1)+1].GetCppString(); - if (!str.empty()) - { - int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i)); - if (idx >= 0) - { - if (data.text.size() <= size_t(idx)) - data.text.resize(idx+1); - - data.text[idx] = str; - } - } - } - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size()); -} diff --git a/src/server/game/AchievementMgr.h b/src/server/game/AchievementMgr.h deleted file mode 100644 index ea9e5e95142..00000000000 --- a/src/server/game/AchievementMgr.h +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __TRINITY_ACHIEVEMENTMGR_H -#define __TRINITY_ACHIEVEMENTMGR_H - -#include -#include - -#include "Common.h" -#include "Policies/Singleton.h" -#include "Database/DatabaseEnv.h" -#include "DBCEnums.h" -#include "DBCStores.h" - -typedef std::list AchievementCriteriaEntryList; -typedef std::list AchievementEntryList; - -typedef std::map AchievementCriteriaListByAchievement; -typedef std::map AchievementListByReferencedId; - -struct CriteriaProgress -{ - uint32 counter; - time_t date; - bool changed; -}; - -enum AchievementCriteriaDataType -{ // value1 value2 comment - ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0, // 0 0 - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1, // creature_id 0 - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2, // class_id race_id - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH= 3, // health_percent 0 - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4, // own_team 0 not corpse (not released body), own_team == false if enemy team expected - ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5, // spell_id effect_idx - ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6, // area id 0 - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7, // spell_id effect_idx - ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8, // minvalue value provided with achievement update must be not less that limit - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9, // minlevel minlevel of target - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10,// gender 0=male; 1=female - ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED = 11,// used to prevent achievement creteria complete if not all requirement implemented and listed in table - ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12,// difficulty normal/heroic difficulty for current event map - ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13,// count "with less than %u people in the zone" - ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14,// team HORDE(67), ALLIANCE(469) - ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15,// drunken_state 0 (enum DrunkenState) of player - ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16,// holiday_id 0 event in holiday time - ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE = 17,// min_score max_score player's team win bg and opposition team have team score in range - ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18,// 0 0 maker instance script call for check current criteria requirements fit - ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19,// item_level item_quality for equipped item in slot to check item level and quality -}; - -#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 20 // maximum value in AchievementCriteriaDataType enum - -class Player; -class Unit; - -struct AchievementCriteriaData -{ - AchievementCriteriaDataType dataType; - union - { - // ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0 (no data) - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1 - struct - { - uint32 id; - } creature; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2 - struct - { - uint32 class_id; - uint32 race_id; - } classRace; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH = 3 - struct - { - uint32 percent; - } health; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4 - struct - { - uint32 own_team_flag; - } player_dead; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5 - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7 - struct - { - uint32 spell_id; - uint32 effect_idx; - } aura; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6 - struct - { - uint32 id; - } area; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8 - struct - { - uint32 minvalue; - } value; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9 - struct - { - uint32 minlevel; - } level; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10 - struct - { - uint32 gender; - } gender; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED = 11 (no data) - // ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12 - struct - { - uint32 difficulty; - } difficulty; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13 - struct - { - uint32 maxcount; - } map_players; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14 - struct - { - uint32 team; - } team; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15 - struct - { - uint32 state; - } drunk; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16 - struct - { - uint32 id; - } holiday; - // ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE= 17 - struct - { - uint32 min_score; - uint32 max_score; - } bg_loss_team_score; - // ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18 (no data) - // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19 - struct - { - uint32 item_level; - uint32 item_quality; - } equipped_item; - // ... - struct - { - uint32 value1; - uint32 value2; - } raw; - }; - - AchievementCriteriaData() : dataType(ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) - { - raw.value1 = 0; - raw.value2 = 0; - } - - AchievementCriteriaData(uint32 _dataType, uint32 _value1, uint32 _value2) : dataType(AchievementCriteriaDataType(_dataType)) - { - raw.value1 = _value1; - raw.value2 = _value2; - } - - bool IsValid(AchievementCriteriaEntry const* criteria); - bool Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 = 0) const; -}; - -struct AchievementCriteriaDataSet -{ - AchievementCriteriaDataSet() : criteria_id(0) {} - typedef std::vector Storage; - void Add(AchievementCriteriaData const& data) { storage.push_back(data); } - bool Meets(Player const* source, Unit const* target, uint32 miscvalue = 0) const; - void SetCriteriaId(uint32 id) {criteria_id = id;} - private: - uint32 criteria_id; - Storage storage; -}; - -typedef std::map AchievementCriteriaDataMap; - -struct AchievementReward -{ - uint32 titleId[2]; - uint32 itemId; - uint32 sender; - std::string subject; - std::string text; -}; - -typedef std::map AchievementRewards; - -struct AchievementRewardLocale -{ - std::vector subject; - std::vector text; -}; - -typedef std::map AchievementRewardLocales; - -struct CompletedAchievementData -{ - time_t date; - bool changed; -}; - -typedef UNORDERED_MAP CriteriaProgressMap; -typedef UNORDERED_MAP CompletedAchievementMap; - -class Unit; -class Player; -class WorldPacket; - -class AchievementMgr -{ - public: - AchievementMgr(Player* pl); - ~AchievementMgr(); - - void Reset(); - static void DeleteFromDB(uint32 lowguid); - void LoadFromDB(QueryResult_AutoPtr achievementResult, QueryResult_AutoPtr criteriaResult); - void SaveToDB(); - void ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0); - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); - void CompletedAchievement(AchievementEntry const* entry); - void CheckAllAchievementCriteria(); - void SendAllAchievementData(); - void SendRespondInspectAchievements(Player* player); - bool HasAchieved(AchievementEntry const* achievement) const; - Player* GetPlayer() { return m_player; } - void UpdateTimedAchievements(uint32 timeDiff); - void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); - void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); // used for quest and scripted timed achievements - - private: - enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST }; - void SendAchievementEarned(AchievementEntry const* achievement); - void SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted); - void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); - void CompletedCriteriaFor(AchievementEntry const* achievement); - bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement); - bool IsCompletedAchievement(AchievementEntry const* entry); - void CompleteAchievementsWithRefs(AchievementEntry const* entry); - void BuildAllDataPacket(WorldPacket *data); - - Player* m_player; - CriteriaProgressMap m_criteriaProgress; - CompletedAchievementMap m_completedAchievements; - typedef std::map TimedAchievementMap; - TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS -}; - -class AchievementGlobalMgr -{ - public: - AchievementCriteriaEntryList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type); - AchievementCriteriaEntryList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type); - AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id) - { - AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id); - return itr != m_AchievementCriteriaListByAchievement.end() ? &itr->second : NULL; - } - - AchievementEntryList const* GetAchievementByReferencedId(uint32 id) const - { - AchievementListByReferencedId::const_iterator itr = m_AchievementListByReferencedId.find(id); - return itr != m_AchievementListByReferencedId.end() ? &itr->second : NULL; - } - - AchievementReward const* GetAchievementReward(AchievementEntry const* achievement) const - { - AchievementRewards::const_iterator iter = m_achievementRewards.find(achievement->ID); - return iter != m_achievementRewards.end() ? &iter->second : NULL; - } - - AchievementRewardLocale const* GetAchievementRewardLocale(AchievementEntry const* achievement) const - { - AchievementRewardLocales::const_iterator iter = m_achievementRewardLocales.find(achievement->ID); - return iter != m_achievementRewardLocales.end() ? &iter->second : NULL; - } - - AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteriaEntry const *achievementCriteria) - { - AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID); - return iter != m_criteriaDataMap.end() ? &iter->second : NULL; - } - - bool IsRealmCompleted(AchievementEntry const* achievement) const - { - return m_allCompletedAchievements.find(achievement->ID) != m_allCompletedAchievements.end(); - } - - void SetRealmCompleted(AchievementEntry const* achievement) - { - m_allCompletedAchievements.insert(achievement->ID); - } - - void LoadAchievementCriteriaList(); - void LoadAchievementCriteriaData(); - void LoadAchievementReferenceList(); - void LoadCompletedAchievements(); - void LoadRewards(); - void LoadRewardLocales(); - private: - AchievementCriteriaDataMap m_criteriaDataMap; - - // store achievement criterias by type to speed up lookup - AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; - AchievementCriteriaEntryList m_AchievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX]; - // store achievement criterias by achievement to speed up lookup - AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement; - // store achievements by referenced achievement id to speed up lookup - AchievementListByReferencedId m_AchievementListByReferencedId; - - typedef std::set AllCompletedAchievements; - AllCompletedAchievements m_allCompletedAchievements; - - AchievementRewards m_achievementRewards; - AchievementRewardLocales m_achievementRewardLocales; -}; - -#define achievementmgr Trinity::Singleton::Instance() - -#endif diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp new file mode 100644 index 00000000000..e0a79fc71ed --- /dev/null +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -0,0 +1,2385 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "DBCEnums.h" +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "Database/DatabaseEnv.h" +#include "Policies/SingletonImp.h" + +#include "AchievementMgr.h" +#include "ArenaTeam.h" +#include "CellImpl.h" +#include "GameEventMgr.h" +#include "GridNotifiersImpl.h" +#include "Guild.h" +#include "Language.h" +#include "Player.h" +#include "ProgressBar.h" +#include "SpellMgr.h" + +#include "MapManager.h" +#include "BattleGround.h" +#include "BattleGroundAB.h" +#include "Map.h" +#include "InstanceData.h" + +INSTANTIATE_SINGLETON_1(AchievementGlobalMgr); + +namespace Trinity +{ + class AchievementChatBuilder + { + public: + AchievementChatBuilder(Player const& pl, ChatMsg msgtype, int32 textId, uint32 ach_id) + : i_player(pl), i_msgtype(msgtype), i_textId(textId), i_achievementId(ach_id) {} + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* text = objmgr.GetTrinityString(i_textId,loc_idx); + + data << uint8(i_msgtype); + data << uint32(LANG_UNIVERSAL); + data << uint64(i_player.GetGUID()); + data << uint32(5); + data << uint64(i_player.GetGUID()); + data << uint32(strlen(text)+1); + data << text; + data << uint8(0); + data << uint32(i_achievementId); + } + + private: + Player const& i_player; + ChatMsg i_msgtype; + int32 i_textId; + uint32 i_achievementId; + }; +} // namespace Trinity + +bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) +{ + if (dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) + { + sLog.outErrorDb("Table `achievement_criteria_data` for criteria (Entry: %u) has wrong data type (%u), ignored.", criteria->ID,dataType); + return false; + } + + switch(criteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + break; + default: + sLog.outErrorDb("Table `achievement_criteria_data` has data for non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->requiredType); + return false; + } + + switch(dataType) + { + case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: + case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: + case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED: + case ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT: + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: + if (!creature.id || !objmgr.GetCreatureTemplate(creature.id)) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing creature id in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,creature.id); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: + if (!classRace.class_id && !classRace.race_id) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.", + criteria->ID, criteria->requiredType,dataType); + return false; + } + if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing class in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,classRace.class_id); + return false; + } + if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing race in value2 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,classRace.race_id); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: + if (health.percent < 1 || health.percent > 100) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) has wrong percent value in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,health.percent); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: + if (player_dead.own_team_flag > 1) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD (%u) has wrong boolean value1 (%u).", + criteria->ID, criteria->requiredType,dataType,player_dead.own_team_flag); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: + { + SpellEntry const* spellEntry = sSpellStore.LookupEntry(aura.spell_id); + if (!spellEntry) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell id in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,(dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id); + return false; + } + if (aura.effect_idx >= 3) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell effect index in value2 (%u), ignored.", + criteria->ID, criteria->requiredType,(dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.effect_idx); + return false; + } + if (!spellEntry->EffectApplyAuraName[aura.effect_idx]) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has non-aura spell effect (ID: %u Effect: %u), ignores.", + criteria->ID, criteria->requiredType,(dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"),dataType,aura.spell_id,aura.effect_idx); + return false; + } + return true; + } + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: + if (!GetAreaEntryByAreaID(area.id)) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA (%u) has wrong area id in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,area.id); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: + if (level.minlevel < 0 || level.minlevel > STRONG_MAX_LEVEL) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) has wrong minlevel in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,level.minlevel); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: + if (gender.gender > GENDER_NONE) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) has wrong gender in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,gender.gender); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY: + if (difficulty.difficulty >= MAX_DIFFICULTY) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY (%u) has wrong difficulty in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,difficulty.difficulty); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: + if (map_players.maxcount <= 0) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) has wrong max players count in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,map_players.maxcount); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: + if (team.team != ALLIANCE && team.team != HORDE) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) has unknown team in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,team.team); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: + if (drunk.state >= MAX_DRUNKEN) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) has unknown drunken state in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,drunk.state); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: + if (!sHolidaysStore.LookupEntry(holiday.id)) + { + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) has unknown holiday in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,holiday.id); + return false; + } + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: + return true; // not check correctness node indexes + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: + if (equipped_item.item_quality >= MAX_ITEM_QUALITY) + { + sLog.outErrorDb("Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM (%u) has unknown quality state in value1 (%u), ignored.", + criteria->ID, criteria->requiredType,dataType,equipped_item.item_quality); + return false; + } + return true; + default: + sLog.outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->requiredType,dataType); + return false; + } +} + +bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const +{ + switch(dataType) + { + case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: + if (!target || target->GetTypeId() != TYPEID_UNIT) + return false; + return target->GetEntry() == creature.id; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: + if (!target || target->GetTypeId() != TYPEID_PLAYER) + return false; + if (classRace.class_id && classRace.class_id != target->ToPlayer()->getClass()) + return false; + if (classRace.race_id && classRace.race_id != target->ToPlayer()->getRace()) + return false; + return true; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: + if (!target || target->GetTypeId() != TYPEID_PLAYER) + return false; + return target->GetHealth()*100 <= health.percent*target->GetMaxHealth(); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: + if (!target || target->GetTypeId() != TYPEID_PLAYER || target->isAlive() || target->ToPlayer()->GetDeathTimer() == 0) + return false; + // flag set == must be same team, not set == different team + return (target->ToPlayer()->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: + return source->HasAuraEffect(aura.spell_id,aura.effect_idx); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: + { + uint32 zone_id,area_id; + source->GetZoneAndAreaId(zone_id,area_id); + return area.id == zone_id || area.id == area_id; + } + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: + return target && target->HasAuraEffect(aura.spell_id,aura.effect_idx); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: + return miscvalue1 >= value.minvalue; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: + if (!target) + return false; + return target->getLevel() >= level.minlevel; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: + if (!target) + return false; + return target->getGender() == gender.gender; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED: + return false; // always fail + case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY: + return source->GetMap()->GetSpawnMode() == difficulty.difficulty; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: + return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: + if (!target || target->GetTypeId() != TYPEID_PLAYER) + return false; + return target->ToPlayer()->GetTeam() == team.team; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: + return Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= drunk.state; + case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: + return IsHolidayActive(HolidayIds(holiday.id)); + case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: + { + BattleGround* bg = source->GetBattleGround(); + if (!bg) + return false; + return bg->IsTeamScoreInRange(source->GetTeam() == ALLIANCE ? HORDE : ALLIANCE,bg_loss_team_score.min_score,bg_loss_team_score.max_score); + } + case ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT: + { + if (!source->IsInWorld()) + return false; + Map* map = source->GetMap(); + if (!map->IsDungeon()) + { + sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-dungeon/non-raid map %u", + ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT, criteria_id, map->GetId()); + return false; + } + InstanceData* data = ((InstanceMap*)map)->GetInstanceData(); + if (!data) + { + sLog.outErrorDb("Achievement system call ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map does not have a instance script", + ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT, criteria_id, map->GetId()); + return false; + } + return data->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1); + } + case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: + { + ItemPrototype const *pProto = objmgr.GetItemPrototype(miscvalue1); + if (!pProto) + return false; + return pProto->ItemLevel >= equipped_item.item_level && pProto->Quality >= equipped_item.item_quality; + } + } + return false; +} + +bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const +{ + for (Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr) + if (!itr->Meets(criteria_id, source, target, miscvalue)) + return false; + + return true; +} + +AchievementMgr::AchievementMgr(Player *player) +{ + m_player = player; +} + +AchievementMgr::~AchievementMgr() +{ +} + +void AchievementMgr::Reset() +{ + for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) + { + WorldPacket data(SMSG_ACHIEVEMENT_DELETED,4); + data << uint32(iter->first); + m_player->SendDirectMessage(&data); + } + + for (CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) + { + WorldPacket data(SMSG_CRITERIA_DELETED,4); + data << uint32(iter->first); + m_player->SendDirectMessage(&data); + } + + m_completedAchievements.clear(); + m_criteriaProgress.clear(); + DeleteFromDB(m_player->GetGUIDLow()); + + // re-fill data + CheckAllAchievementCriteria(); +} + +void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2) +{ + if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0) + sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2); + + if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) + return; + + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); + for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + { + AchievementCriteriaEntry const *achievementCriteria = (*i); + + AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); + if (!achievement) + continue; + + // don't update already completed criteria + if (IsCompletedCriteria(achievementCriteria,achievement)) + continue; + + switch (type) + { + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: // have total statistic also not expected to be reset + if (achievementCriteria->healing_done.flag == miscvalue1 && + achievementCriteria->healing_done.mapid == miscvalue2) + SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET); + break; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // have total statistic also not expected to be reset + // reset only the criteria having the miscvalue1 condition + if (achievementCriteria->win_rated_arena.flag == miscvalue1) + SetCriteriaProgress(achievementCriteria, 0, PROGRESS_SET); + break; + default: // reset all cases + break; + } + } +} + +void AchievementMgr::DeleteFromDB(uint32 lowguid) +{ + CharacterDatabase.BeginTransaction (); + CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = %u",lowguid); + CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid); + CharacterDatabase.CommitTransaction (); +} + +void AchievementMgr::SaveToDB() +{ + if (!m_completedAchievements.empty()) + { + bool need_execute = false; + std::ostringstream ssdel; + std::ostringstream ssins; + for (CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) + { + if (!iter->second.changed) + continue; + + /// first new/changed record prefix + if (!need_execute) + { + ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN ("; + ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES "; + need_execute = true; + } + /// next new/changed record prefix + else + { + ssdel << ", "; + ssins << ", "; + } + + // new/changed record data + ssdel << iter->first; + ssins << "("<GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")"; + + /// mark as saved in db + iter->second.changed = false; + } + + if (need_execute) + ssdel << ")"; + + if (need_execute) + { + CharacterDatabase.Execute(ssdel.str().c_str()); + CharacterDatabase.Execute(ssins.str().c_str()); + } + } + + if (!m_criteriaProgress.empty()) + { + /// prepare deleting and insert + bool need_execute_del = false; + bool need_execute_ins = false; + std::ostringstream ssdel; + std::ostringstream ssins; + for (CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) + { + if (!iter->second.changed) + continue; + + // deleted data (including 0 progress state) + { + /// first new/changed record prefix (for any counter value) + if (!need_execute_del) + { + ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN ("; + need_execute_del = true; + } + /// next new/changed record prefix + else + ssdel << ", "; + + // new/changed record data + ssdel << iter->first; + } + + // store data only for real progress + if (iter->second.counter != 0) + { + /// first new/changed record prefix + if (!need_execute_ins) + { + ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES "; + need_execute_ins = true; + } + /// next new/changed record prefix + else + ssins << ", "; + + // new/changed record data + ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")"; + } + + /// mark as updated in db + iter->second.changed = false; + } + + if (need_execute_del) // DELETE ... IN (.... _)_ + ssdel << ")"; + + if (need_execute_del || need_execute_ins) + { + if (need_execute_del) + CharacterDatabase.Execute(ssdel.str().c_str()); + if (need_execute_ins) + CharacterDatabase.Execute(ssins.str().c_str()); + } + } +} + +void AchievementMgr::LoadFromDB(QueryResult_AutoPtr achievementResult, QueryResult_AutoPtr criteriaResult) +{ + if (achievementResult) + { + do + { + Field *fields = achievementResult->Fetch(); + + uint32 achievement_id = fields[0].GetUInt32(); + + // don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements() + if (!sAchievementStore.LookupEntry(achievement_id)) + continue; + + CompletedAchievementData& ca = m_completedAchievements[achievement_id]; + ca.date = time_t(fields[1].GetUInt64()); + ca.changed = false; + } while (achievementResult->NextRow()); + } + + if (criteriaResult) + { + do + { + Field *fields = criteriaResult->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint32 counter = fields[1].GetUInt32(); + time_t date = time_t(fields[2].GetUInt64()); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id); + if (!criteria) + { + // we will remove not existed criteria for all characters + sLog.outError("Non-existing achievement criteria %u data removed from table `character_achievement_progress`.",id); + CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id); + continue; + } + + if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL)) + continue; + + CriteriaProgress& progress = m_criteriaProgress[id]; + progress.counter = counter; + progress.date = date; + progress.changed = false; + } while (criteriaResult->NextRow()); + } + +} + +void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) +{ + if (GetPlayer()->GetSession()->PlayerLoading()) + return; + + // Don't send for achievements with ACHIEVEMENT_FLAG_TRACKING + if (achievement->flags & ACHIEVEMENT_FLAG_TRACKING) + return; + + #ifdef TRINITY_DEBUG + if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0) + sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID); + #endif + + if (Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId())) + { + Trinity::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); + Trinity::LocalizedPacketDo say_do(say_builder); + guild->BroadcastWorker(say_do,GetPlayer()); + } + + if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) + { + // broadcast realm first reached + WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+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); + } + // if player is in world he can tell his friends about new achievement + else if (GetPlayer()->IsInWorld()) + { + CellPair p = Trinity::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID); + Trinity::LocalizedPacketDo say_do(say_builder); + Trinity::PlayerDistWorker > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do); + TypeContainerVisitor >, WorldTypeMapContainer > message(say_worker); + cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY)); + } + + WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8); + data.append(GetPlayer()->GetPackGUID()); + data << uint32(achievement->ID); + data << uint32(secsToTimeBitFields(time(NULL))); + data << uint32(0); + GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true); +} + +void AchievementMgr::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) +{ + WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8); + data << uint32(entry->ID); + + // the counter is packed like a packed Guid + data.appendPackGUID(progress->counter); + + data.append(GetPlayer()->GetPackGUID()); + if (!entry->timeLimit) + data << uint32(0); + else + data << uint32(timedCompleted ? 0 : 1); // this are some flags, 1 is for keeping the counter at 0 in client + data << uint32(secsToTimeBitFields(progress->date)); + data << uint32(timeElapsed); // time elapsed in seconds + data << uint32(0); // unk + GetPlayer()->SendDirectMessage(&data); +} + +/** + * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet + */ +void AchievementMgr::CheckAllAchievementCriteria() +{ + // suppress sending packets + for (uint32 i=0; iGetSession()->GetSecurity() > SEC_PLAYER) + return; + + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type); + for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + { + AchievementCriteriaEntry const *achievementCriteria = (*i); + + AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); + if (!achievement) + continue; + + if ((achievement->factionFlag == ACHIEVEMENT_FACTION_HORDE && GetPlayer()->GetTeam() != HORDE) || + (achievement->factionFlag == ACHIEVEMENT_FACTION_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE)) + continue; + + // don't update already completed criteria + if (IsCompletedCriteria(achievementCriteria,achievement)) + continue; + + switch (type) + { + // std. case: increment at 1 + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: + case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */ + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + // std case: increment at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:/* FIXME: for online player only currently */ + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + // std case: high value at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */ + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST); + break; + + // specialized cases + + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId()) + continue; + + if (achievementCriteria->win_bg.additionalRequirement1_type) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + } + // some hardcoded requirements + else + { + BattleGround* bg = GetPlayer()->GetBattleGround(); + if (!bg) + continue; + + switch(achievementCriteria->referredAchievement) + { + case 161: // AB, Overcome a 500 resource disadvantage + { + if (bg->GetTypeID(true) != BATTLEGROUND_AB) + continue; + if (!((BattleGroundAB*)bg)->IsTeamScores500Disadvantage(GetPlayer()->GetTeam())) + continue; + break; + } + case 156: // AB, win while controlling all 5 flags (all nodes) + case 784: // EY, win while holding 4 bases (all nodes) + { + if (!bg->IsAllNodesConrolledByTeam(GetPlayer()->GetTeam())) + continue; + break; + } + case 1762: // SA, win without losing any siege vehicles + case 2192: // SA, win without losing any siege vehicles + continue; // not implemented + } + } + + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (achievementCriteria->kill_creature.creatureID != miscvalue1) + continue; + + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + // update at loading or specific skill update + if (miscvalue1 && miscvalue1 != achievementCriteria->reach_skill_level.skillID) + continue; + if (uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) + SetCriteriaProgress(achievementCriteria, skillvalue); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + // update at loading or specific skill update + if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_level.skillID) + continue; + if (uint32 maxSkillvalue = GetPlayer()->GetPureMaxSkillValue(achievementCriteria->learn_skill_level.skillID)) + SetCriteriaProgress(achievementCriteria, maxSkillvalue); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + if (m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end()) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + { + uint32 counter =0; + for (QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr != GetPlayer()->getQuestStatusMap().end(); itr++) + if (itr->second.m_rewarded) + counter++; + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + { + // speedup for non-login case + if (miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID) + continue; + + uint32 counter =0; + for (QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr != GetPlayer()->getQuestStatusMap().end(); itr++) + { + Quest const* quest = objmgr.GetQuestTemplate(itr->first); + if (itr->second.m_rewarded && quest && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID) + counter++; + } + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter + bool notfit = false; + for (int j = 0; j < MAX_ARENA_SLOT; ++j) + { + if (achievIdByArenaSlot[j] == achievement->ID) + { + BattleGround* bg = GetPlayer()->GetBattleGround(); + if (!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j) + notfit = true; + + break; + } + } + if (notfit) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + + Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : MapManager::Instance().FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId()); + if (!map || !map->IsDungeon()) + continue; + + // search case + bool found = false; + for (int j = 0; achievIdForDangeon[j][0]; ++j) + { + if (achievIdForDangeon[j][0] == achievement->ID) + { + if (map->IsRaid()) + { + // if raid accepted (ignore difficulty) + if (!achievIdForDangeon[j][2]) + break; // for + } + else if (GetPlayer()->GetDungeonDifficulty() == DUNGEON_DIFFICULTY_NORMAL) + { + // dungeon in normal mode accepted + if (!achievIdForDangeon[j][1]) + break; // for + } + else + { + // dungeon in heroic mode accepted + if (!achievIdForDangeon[j][3]) + break; // for + } + + found = true; + break; // for + } + } + if (!found) + continue; + + //FIXME: work only for instances where max == min for players + if (((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + + } + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + + // if team check required: must kill by opposition faction + if (achievement->ID == 318 && miscvalue2 == GetPlayer()->GetTeam()) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + + // miscvalue1 is the ingame fallheight*100 as stored in dbc + SetCriteriaProgress(achievementCriteria, miscvalue1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (miscvalue2 != achievementCriteria->death_from.type) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + { + // if miscvalues != 0, it contains the questID. + if (miscvalue1) + { + if (miscvalue1 != achievementCriteria->complete_quest.questID) + continue; + } + else + { + // login case. + if (!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) + continue; + } + + // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case + switch(achievement->ID) + { + case 31: + case 1275: + case 1276: + case 1277: + case 1282: + case 1789: + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + break; + } + default: + break; + } + + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + { + if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID) + continue; + + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data) + continue; + + if (!data->Meets(GetPlayer(),unit)) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + { + if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) + continue; + + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data) + continue; + + if (!data->Meets(GetPlayer(),unit)) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + if (miscvalue1 && miscvalue1 != achievementCriteria->learn_spell.spellID) + continue; + + if (GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + { + // miscvalue1=loot_type (note: 0 = LOOT_CORSPE and then it ignored) + // miscvalue2=count of item loot + if (!miscvalue1 || !miscvalue2) + continue; + if (miscvalue1 != achievementCriteria->loot_type.lootType) + continue; + + // zone specific + if (achievementCriteria->loot_type.lootTypeCount == 1) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + } + + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + // speedup for non-login case + if (miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); + break; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + // miscvalue1 contains the personal rating + if (!miscvalue1) // no update at login + continue; + + // additional requirements + if (achievementCriteria->win_rated_arena.flag == ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit,miscvalue1)) + { + // reset the progress as we have a win without the requirement. + SetCriteriaProgress(achievementCriteria, 0); + continue; + } + } + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + if (achievementCriteria->use_item.itemID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + // You _have_ to loot that item, just owning it when logging in does _not_ count! + if (!miscvalue1) + continue; + if (miscvalue1 != achievementCriteria->own_item.itemID) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + { + WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); + if (!worldOverlayEntry) + break; + + bool matchFound = false; + for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) + { + uint32 area_id = worldOverlayEntry->areatableID[j]; + if (!area_id) // array have 0 only in empty tail + break; + + int32 exploreFlag = GetAreaFlagByAreaID(area_id); + if (exploreFlag < 0) + continue; + + uint32 playerIndexOffset = uint32(exploreFlag) / 32; + uint32 mask = 1<< (uint32(exploreFlag) % 32); + + if (GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) + { + matchFound = true; + break; + } + } + + if (matchFound) + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetBankBagSlotCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + { + // skip faction check only at loading + if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID) + continue; + + int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); + if (reputation > 0) + SetCriteriaProgress(achievementCriteria, reputation); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + { + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetExaltedFactionCount()); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + { + // skip for login case + if (!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + { + // miscvalue1 = itemid + // miscvalue2 = itemSlot + if (!miscvalue1) + continue; + + if (miscvalue2 != achievementCriteria->equip_epic_item.itemSlot) + continue; + + // check item level and quality via achievement_criteria_data + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(), 0, miscvalue1)) + continue; + + SetCriteriaProgress(achievementCriteria, 1); + break; + } + + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + { + // miscvalue1 = itemid + // miscvalue2 = diced value + if (!miscvalue1) + continue; + if (miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue) + continue; + + ItemPrototype const *pProto = objmgr.GetItemPrototype(miscvalue1); + if (!pProto) + continue; + + // check item level via achievement_criteria_data + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(), 0, pProto->ItemLevel)) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + { + // miscvalue1 = emote + if (!miscvalue1) + continue; + if (miscvalue1 != achievementCriteria->do_emote.emoteID) + continue; + if (achievementCriteria->do_emote.count) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(),unit)) + continue; + } + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: + { + if (!miscvalue1) + continue; + + if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP) + { + if (GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid) + continue; + + // map specific case (BG in fact) expected player targeted damage/heal + if (!unit || unit->GetTypeId() != TYPEID_PLAYER) + continue; + } + + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + // miscvalue1 = item_id + if (!miscvalue1) + continue; + if (miscvalue1 != achievementCriteria->equip_item.itemID) + continue; + + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + // miscvalue1 = go entry + if (!miscvalue1) + continue; + if (miscvalue1 != achievementCriteria->use_gameobject.goEntry) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: + if (!miscvalue1) + continue; + if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + { + if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine) + continue; + + uint32 spellCount = 0; + for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); + spellIter != GetPlayer()->GetSpellMap().end(); + ++spellIter) + { + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first); + for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) + { + if (skillIter->second->skillId == achievementCriteria->learn_skillline_spell.skillLine) + spellCount++; + } + } + SetCriteriaProgress(achievementCriteria, spellCount); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + + if (achievementCriteria->win_duel.duelCount) + { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria); + if (!data) + continue; + + if (!data->Meets(GetPlayer(),unit)) + continue; + } + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetReveredFactionCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetHonoredFactionCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetReputationMgr().GetVisibleFactionCount()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if (!miscvalue1) + continue; + ItemPrototype const* proto = ObjectMgr::GetItemPrototype(miscvalue1); + if (!proto || proto->Quality < ITEM_QUALITY_EPIC) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: + { + if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine) + continue; + + uint32 spellCount = 0; + for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); + spellIter != GetPlayer()->GetSpellMap().end(); + ++spellIter) + { + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellIter->first); + for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) + if (skillIter->second->skillId == achievementCriteria->learn_skill_line.skillLine) + spellCount++; + } + SetCriteriaProgress(achievementCriteria, spellCount); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS)); + break; + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetMoney(), PROGRESS_HIGHEST); + break; + // std case: not exist in DBC, not triggered in code as result + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING: + break; + // FIXME: not triggered in code as result, need to implement + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: + case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: + case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: + case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: + case ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING: + case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: + case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: + case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: + case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE: + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: + case ACHIEVEMENT_CRITERIA_TYPE_TOTAL: + case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: + case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: + break; // Not implemented yet :( + } + if (IsCompletedCriteria(achievementCriteria,achievement)) + CompletedCriteriaFor(achievement); + + // check again the completeness for SUMM and REQ COUNT achievements, + // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria + if (achievement->flags & ACHIEVEMENT_FLAG_SUMM) + { + if (IsCompletedAchievement(achievement)) + CompletedAchievement(achievement); + } + + if (AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID)) + { + for (AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) + if (IsCompletedAchievement(*itr)) + CompletedAchievement(*itr); + } + } +} + +static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 }; +static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 }; + +bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) +{ + // counter can never complete + if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return false; + + if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + { + // someone on this realm has already completed that achievement + if (achievementmgr.IsRealmCompleted(achievement)) + return false; + } + + CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID); + if (itr == m_criteriaProgress.end()) + return false; + + CriteriaProgress const* progress = &itr->second; + + switch(achievementCriteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + return progress->counter >= achievementCriteria->win_bg.winCount; + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + return progress->counter >= achievementCriteria->kill_creature.creatureCount; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + { + // skip wrong class achievements + for (int i = 1; i < MAX_CLASSES; ++i) + if (achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass()) + return false; + + // skip wrong race achievements + for (int i = 1; i < MAX_RACES; ++i) + if (achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace()) + return false; + + // appropriate class/race or not class/race specific + return progress->counter >= achievementCriteria->reach_level.level; + } + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: + case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: + return progress->counter >= achievementCriteria->healing_done.count; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + return progress->counter >= achievementCriteria->complete_daily_quest.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + return progress->counter >= achievementCriteria->be_spell_target.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + return progress->counter >= achievementCriteria->cast_spell.castCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + return progress->counter >= achievementCriteria->own_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: + return progress->counter >= achievementCriteria->win_rated_arena.count; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: + return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + return progress->counter >= achievementCriteria->use_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + return progress->counter >= achievementCriteria->loot_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: + return progress->counter >= achievementCriteria->equip_epic_item.count; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + return progress->counter >= achievementCriteria->roll_greed_on_loot.count; + case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: + return progress->counter >= achievementCriteria->hk_class.count; + case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: + return progress->counter >= achievementCriteria->hk_race.count; + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: + return progress->counter >= achievementCriteria->do_emote.count; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + return progress->counter >= achievementCriteria->equip_item.count; + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + return progress->counter >= achievementCriteria->loot_money.goldInCopper; + case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: + return progress->counter >= achievementCriteria->use_gameobject.useCount; + case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: + return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: + return progress->counter >= achievementCriteria->win_duel.duelCount; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: + return progress->counter >= achievementCriteria->loot_type.lootTypeCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: + return progress->counter >= achievementCriteria->learn_skill_line.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: + return progress->counter >= achievementCriteria->honorable_kill.killCount; + // handle all statistic-only criteria here + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: + case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: + case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: + case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: + case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + return false; + } + return false; +} + +void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement) +{ + // counter can never complete + if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return; + + // already completed and stored + if (HasAchieved(achievement)) + return; + + if (IsCompletedAchievement(achievement)) + CompletedAchievement(achievement); +} + +bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) +{ + // counter can never complete + if (entry->flags & ACHIEVEMENT_FLAG_COUNTER) + return false; + + // for achievement with referenced achievement criterias get from referenced and counter from self + uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID; + uint32 achievmentForTestCount = entry->count; + + AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId); + if (!cList) + return false; + uint32 count = 0; + + // For SUMM achievements, we have to count the progress of each criteria of the achievement. + // Oddly, the target count is NOT countained in the achievement, but in each individual criteria + if (entry->flags & ACHIEVEMENT_FLAG_SUMM) + { + for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) + { + AchievementCriteriaEntry const* criteria = *itr; + + CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID); + if (itrProgress == m_criteriaProgress.end()) + continue; + + CriteriaProgress const* progress = &itrProgress->second; + count += progress->counter; + + // for counters, field4 contains the main count requirement + if (count >= criteria->raw.count) + return true; + } + return false; + } + + // Default case - need complete all or + bool completed_all = true; + for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) + { + AchievementCriteriaEntry const* criteria = *itr; + + bool completed = IsCompletedCriteria(criteria,entry); + + // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL + if (completed) + ++count; + else + completed_all = false; + + // completed as have req. count of completed criterias + if (achievmentForTestCount > 0 && achievmentForTestCount <= count) + return true; + } + + // all criterias completed requirement + if (completed_all && achievmentForTestCount == 0) + return true; + + return false; +} + +void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype) +{ + // Don't allow to cheat - doing timed achievements without timer active + TimedAchievementMap::iterator timedIter = m_timedAchievements.find(entry->ID); + if (entry->timeLimit && timedIter == m_timedAchievements.end()) + return; + + if ((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES) == 0) + sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow()); + + CriteriaProgress *progress = NULL; + + CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); + + if (iter == m_criteriaProgress.end()) + { + // not create record for 0 counter but allow it for timed achievements + // we will need to send 0 progress to client to start the timer + if (changeValue == 0 && !entry->timeLimit) + return; + + progress = &m_criteriaProgress[entry->ID]; + progress->counter = changeValue; + progress->date = time(NULL); + } + else + { + progress = &iter->second; + + uint32 newValue = 0; + switch(ptype) + { + case PROGRESS_SET: + newValue = changeValue; + break; + case PROGRESS_ACCUMULATE: + { + // avoid overflow + uint32 max_value = std::numeric_limits::max(); + newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value; + break; + } + case PROGRESS_HIGHEST: + newValue = progress->counter < changeValue ? changeValue : progress->counter; + break; + } + + // not update (not mark as changed) if counter will have same value + if (progress->counter == newValue && !entry->timeLimit) + return; + + progress->counter = newValue; + } + + progress->changed = true; + + uint32 timeElapsed = 0; + bool timedCompleted = false; + + if (entry->timeLimit) + { + //has to exist else we wouldn't be here + timedCompleted = IsCompletedCriteria(entry, sAchievementStore.LookupEntry(entry->referredAchievement)); + // Client expects this in packet + timeElapsed = entry->timeLimit - (timedIter->second/IN_MILISECONDS); + + // Remove the timer, we wont need it anymore + if (timedCompleted) + m_timedAchievements.erase(timedIter); + } + + SendCriteriaUpdate(entry, progress, timeElapsed, timedCompleted); +} + +void AchievementMgr::UpdateTimedAchievements(uint32 timeDiff) +{ + if (!m_timedAchievements.empty()) + { + for (TimedAchievementMap::iterator itr = m_timedAchievements.begin(); itr != m_timedAchievements.end();) + { + // Time is up, remove timer and reset progress + if (itr->second <= timeDiff) + { + AchievementCriteriaEntry const *entry = sAchievementCriteriaStore.LookupEntry(itr->first); + SetCriteriaProgress(entry, 0, PROGRESS_SET); + m_timedAchievements.erase(itr++); + } + else + { + itr->second -= timeDiff; + ++itr; + } + } + } +} + +void AchievementMgr::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) +{ + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetTimedAchievementCriteriaByType(type); + for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + { + if ((*i)->timerStartEvent != entry) + continue; + + AchievementEntry const *achievement = sAchievementStore.LookupEntry((*i)->referredAchievement); + if (m_timedAchievements.find((*i)->ID) == m_timedAchievements.end() && !IsCompletedCriteria(*i, achievement)) + { + // Start the timer + m_timedAchievements[(*i)->ID] = (*i)->timeLimit * IN_MILISECONDS; + + // and at client too + SetCriteriaProgress(*i, 0, PROGRESS_SET); + } + } +} + +void AchievementMgr::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) +{ + AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetTimedAchievementCriteriaByType(type); + for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i) + { + if ((*i)->timerStartEvent != entry) + continue; + + TimedAchievementMap::iterator timedIter = m_timedAchievements.find((*i)->ID); + // We don't have timer for this achievement + if (timedIter == m_timedAchievements.end()) + continue; + + // 0 the progress to avoid saving to db + SetCriteriaProgress(*i, 0, PROGRESS_SET); + + // Remove the timer + m_timedAchievements.erase(timedIter); + } +} + +void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) +{ + sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID); + + if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER) + return; + + if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement)) + return; + + SendAchievementEarned(achievement); + CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; + ca.date = time(NULL); + ca.changed = true; + + // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement + // TODO: where do set this instead? + if (!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + achievementmgr.SetRealmCompleted(achievement); + + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT); + + // reward items and titles if any + AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement); + + // no rewards + if (!reward) + return; + + // titles + if (uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1]) + { + if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) + GetPlayer()->SetTitle(titleEntry); + } + + // mail + if (reward->sender) + { + Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL; + + int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex(); + + // subject and text + std::string subject = reward->subject; + std::string text = reward->text; + if (loc_idx >= 0) + { + if (AchievementRewardLocale const* loc = achievementmgr.GetAchievementRewardLocale(achievement)) + { + if (loc->subject.size() > size_t(loc_idx) && !loc->subject[loc_idx].empty()) + subject = loc->subject[loc_idx]; + if (loc->text.size() > size_t(loc_idx) && !loc->text[loc_idx].empty()) + text = loc->text[loc_idx]; + } + } + + MailDraft draft(subject, text); + + if (item) + { + // save new item before send + item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted + + // item + draft.AddItem(item); + } + + draft.SendMailTo(GetPlayer(), MailSender(MAIL_CREATURE, reward->sender)); + } +} + +void AchievementMgr::SendAllAchievementData() +{ + WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, m_completedAchievements.size()*8+4+m_criteriaProgress.size()*38+4); + BuildAllDataPacket(&data); + GetPlayer()->GetSession()->SendPacket(&data); +} + +void AchievementMgr::SendRespondInspectAchievements(Player* player) +{ + WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 9+m_completedAchievements.size()*8+4+m_criteriaProgress.size()*38+4); + data.append(GetPlayer()->GetPackGUID()); + BuildAllDataPacket(&data); + player->GetSession()->SendPacket(&data); +} + +/** + * used by SMSG_RESPOND_INSPECT_ACHIEVEMENT and SMSG_ALL_ACHIEVEMENT_DATA + */ +void AchievementMgr::BuildAllDataPacket(WorldPacket *data) +{ + AchievementEntry const *achievement; + for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) + { + // Skip tracking - they bug client UI + achievement = sAchievementStore.LookupEntry(iter->first); + if (achievement->flags & ACHIEVEMENT_FLAG_TRACKING) + continue; + + *data << uint32(iter->first); + *data << uint32(secsToTimeBitFields(iter->second.date)); + } + *data << int32(-1); + + for (CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) + { + *data << uint32(iter->first); + data->appendPackGUID(iter->second.counter); + data->append(GetPlayer()->GetPackGUID()); + *data << uint32(0); + *data << uint32(secsToTimeBitFields(iter->second.date)); + *data << uint32(0); + *data << uint32(0); + } + + *data << int32(-1); +} + +bool AchievementMgr::HasAchieved(AchievementEntry const* achievement) const +{ + return m_completedAchievements.find(achievement->ID) != m_completedAchievements.end(); +} + +//========================================================== +AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type) +{ + return m_AchievementCriteriasByType[type]; +} + +AchievementCriteriaEntryList const& AchievementGlobalMgr::GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type) +{ + return m_AchievementCriteriasByTimedType[type]; +} + +void AchievementGlobalMgr::LoadAchievementCriteriaList() +{ + if (sAchievementCriteriaStore.GetNumRows() == 0) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 achievement criteria."); + return; + } + + barGoLink bar(sAchievementCriteriaStore.GetNumRows()); + for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) + { + bar.step(); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); + if (!criteria) + continue; + + m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria); + m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria); + + if (criteria->timeLimit) + m_AchievementCriteriasByTimedType[criteria->timedType].push_back(criteria); + } + + sLog.outString(); + sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size()); +} + +void AchievementGlobalMgr::LoadAchievementReferenceList() +{ + if (sAchievementStore.GetNumRows() == 0) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 achievement references."); + return; + } + + uint32 count = 0; + barGoLink bar(sAchievementStore.GetNumRows()); + for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId) + { + bar.step(); + + AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId); + if (!achievement || !achievement->refAchievement) + continue; + + m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement); + ++count; + } + + sLog.outString(); + sLog.outString(">> Loaded %u achievement references.",count); +} + +void AchievementGlobalMgr::LoadAchievementCriteriaData() +{ + m_criteriaDataMap.clear(); // need for reload case + + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty."); + return; + } + + uint32 count = 0; + uint32 disabled_count = 0; + barGoLink bar(result->GetRowCount()); + do + { + bar.step(); + Field *fields = result->Fetch(); + uint32 criteria_id = fields[0].GetUInt32(); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id); + + if (!criteria) + { + sLog.outErrorDb("Table `achievement_criteria_data` has data for non-existing criteria (Entry: %u), ignore.", criteria_id); + continue; + } + + AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32()); + + if (!data.IsValid(criteria)) + { + continue; + } + + // this will allocate empty data set storage + AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id]; + dataSet.SetCriteriaId(criteria_id); + + if (data.dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED) + ++disabled_count; + + // add real data only for not NONE data types + if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) + dataSet.Add(data); + + // counting data by and data types + ++count; + } while (result->NextRow()); + + // post loading checks + for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) + { + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); + if (!criteria) + continue; + + switch(criteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: + if (!criteria->win_bg.additionalRequirement1_type) + continue; + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + break; // any cases + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + { + AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement); + if (!achievement) + continue; + + // exist many achievements with this criteria, use at this moment hardcoded check to skil simple case + switch(achievement->ID) + { + case 31: + case 1275: + case 1276: + case 1277: + case 1282: + case 1789: + break; + default: + continue; + } + } + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + break; // any cases + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: // any cases + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: // any cases + break; + case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: // need skip generic cases + if (criteria->win_rated_arena.flag != ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE) + continue; + break; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: // any cases + break; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + break; // any cases + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + break; // any cases + case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: // need skip generic cases + if (criteria->do_emote.count == 0) + continue; + break; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + break; // any cases + case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: // skip statistics + if (criteria->win_duel.duelCount == 0) + continue; + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: // any cases + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: // need skip generic cases + if (criteria->loot_type.lootTypeCount != 1) + continue; + break; + default: // type not use DB data, ignore + continue; + } + + if (!GetCriteriaDataSet(criteria)) + sLog.outErrorDb("Table `achievement_criteria_data` does not have expected data for criteria (Entry: %u Type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement); + } + + sLog.outString(); + sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count); +} + +void AchievementGlobalMgr::LoadCompletedAchievements() +{ + QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + do + { + bar.step(); + Field *fields = result->Fetch(); + + uint32 achievement_id = fields[0].GetUInt32(); + if (!sAchievementStore.LookupEntry(achievement_id)) + { + // we will remove not existed achievement for all characters + sLog.outError("Non-existing achievement %u data removed from table `character_achievement`.",achievement_id); + CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id); + continue; + } + + m_allCompletedAchievements.insert(achievement_id); + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size()); +} + +void AchievementGlobalMgr::LoadRewards() +{ + m_achievementRewards.clear(); // need for reload case + + // 0 1 2 3 4 5 6 + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty."); + return; + } + + uint32 count = 0; + barGoLink bar(result->GetRowCount()); + + do + { + bar.step(); + + Field *fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); + const AchievementEntry* pAchievement = sAchievementStore.LookupEntry(entry); + if (!pAchievement) + { + sLog.outErrorDb("Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry); + continue; + } + + AchievementReward reward; + reward.titleId[0] = fields[1].GetUInt32(); + reward.titleId[1] = fields[2].GetUInt32(); + reward.itemId = fields[3].GetUInt32(); + reward.sender = fields[4].GetUInt32(); + reward.subject = fields[5].GetCppString(); + reward.text = fields[6].GetCppString(); + + // must be title or mail at least + if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender) + { + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry); + continue; + } + + if (pAchievement->factionFlag == ACHIEVEMENT_FACTION_ANY && ((reward.titleId[0] == 0) != (reward.titleId[1] == 0))) + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]); + + if (reward.titleId[0]) + { + CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]); + if (!titleEntry) + { + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]); + reward.titleId[0] = 0; + } + } + + if (reward.titleId[1]) + { + CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]); + if (!titleEntry) + { + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]); + reward.titleId[1] = 0; + } + } + + //check mail data before item for report including wrong item case + if (reward.sender) + { + if (!objmgr.GetCreatureTemplate(reward.sender)) + { + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender); + reward.sender = 0; + } + } + else + { + if (reward.itemId) + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) does not have sender data but has item reward, item will not be rewarded.", entry); + + if (!reward.subject.empty()) + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) does not have sender data but has mail subject.", entry); + + if (!reward.text.empty()) + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) does not have sender data but has mail text.", entry); + } + + if (reward.itemId) + { + if (!objmgr.GetItemPrototype(reward.itemId)) + { + sLog.outErrorDb("Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will not contain item.", entry, reward.itemId); + reward.itemId = 0; + } + } + + m_achievementRewards[entry] = reward; + ++count; + + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u achievement rewards", count); +} + +void AchievementGlobalMgr::LoadRewardLocales() +{ + m_achievementRewardLocales.clear(); // need for reload case + + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,subject_loc1,text_loc1,subject_loc2,text_loc2,subject_loc3,text_loc3,subject_loc4,text_loc4,subject_loc5,text_loc5,subject_loc6,text_loc6,subject_loc7,text_loc7,subject_loc8,text_loc8 FROM locales_achievement_reward"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 achievement reward locale strings."); + sLog.outString(">> DB table `locales_achievement_reward` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + + if (m_achievementRewards.find(entry) == m_achievementRewards.end()) + { + sLog.outErrorDb("Table `locales_achievement_reward` (Entry: %u) has locale strings for non-existing achievement reward.", entry); + continue; + } + + AchievementRewardLocale& data = m_achievementRewardLocales[entry]; + + for (int i = 1; i < MAX_LOCALE; ++i) + { + std::string str = fields[1+2*(i-1)].GetCppString(); + if (!str.empty()) + { + int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i)); + if (idx >= 0) + { + if (data.subject.size() <= size_t(idx)) + data.subject.resize(idx+1); + + data.subject[idx] = str; + } + } + str = fields[1+2*(i-1)+1].GetCppString(); + if (!str.empty()) + { + int idx = objmgr.GetOrNewIndexForLocale(LocaleConstant(i)); + if (idx >= 0) + { + if (data.text.size() <= size_t(idx)) + data.text.resize(idx+1); + + data.text[idx] = str; + } + } + } + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size()); +} diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h new file mode 100644 index 00000000000..ea9e5e95142 --- /dev/null +++ b/src/server/game/Achievements/AchievementMgr.h @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __TRINITY_ACHIEVEMENTMGR_H +#define __TRINITY_ACHIEVEMENTMGR_H + +#include +#include + +#include "Common.h" +#include "Policies/Singleton.h" +#include "Database/DatabaseEnv.h" +#include "DBCEnums.h" +#include "DBCStores.h" + +typedef std::list AchievementCriteriaEntryList; +typedef std::list AchievementEntryList; + +typedef std::map AchievementCriteriaListByAchievement; +typedef std::map AchievementListByReferencedId; + +struct CriteriaProgress +{ + uint32 counter; + time_t date; + bool changed; +}; + +enum AchievementCriteriaDataType +{ // value1 value2 comment + ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0, // 0 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1, // creature_id 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2, // class_id race_id + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH= 3, // health_percent 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4, // own_team 0 not corpse (not released body), own_team == false if enemy team expected + ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5, // spell_id effect_idx + ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6, // area id 0 + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7, // spell_id effect_idx + ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8, // minvalue value provided with achievement update must be not less that limit + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9, // minlevel minlevel of target + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10,// gender 0=male; 1=female + ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED = 11,// used to prevent achievement creteria complete if not all requirement implemented and listed in table + ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12,// difficulty normal/heroic difficulty for current event map + ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13,// count "with less than %u people in the zone" + ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14,// team HORDE(67), ALLIANCE(469) + ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15,// drunken_state 0 (enum DrunkenState) of player + ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16,// holiday_id 0 event in holiday time + ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE = 17,// min_score max_score player's team win bg and opposition team have team score in range + ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18,// 0 0 maker instance script call for check current criteria requirements fit + ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19,// item_level item_quality for equipped item in slot to check item level and quality +}; + +#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 20 // maximum value in AchievementCriteriaDataType enum + +class Player; +class Unit; + +struct AchievementCriteriaData +{ + AchievementCriteriaDataType dataType; + union + { + // ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0 (no data) + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1 + struct + { + uint32 id; + } creature; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2 + struct + { + uint32 class_id; + uint32 race_id; + } classRace; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH = 3 + struct + { + uint32 percent; + } health; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4 + struct + { + uint32 own_team_flag; + } player_dead; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5 + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7 + struct + { + uint32 spell_id; + uint32 effect_idx; + } aura; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6 + struct + { + uint32 id; + } area; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8 + struct + { + uint32 minvalue; + } value; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9 + struct + { + uint32 minlevel; + } level; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10 + struct + { + uint32 gender; + } gender; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED = 11 (no data) + // ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12 + struct + { + uint32 difficulty; + } difficulty; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13 + struct + { + uint32 maxcount; + } map_players; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14 + struct + { + uint32 team; + } team; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15 + struct + { + uint32 state; + } drunk; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16 + struct + { + uint32 id; + } holiday; + // ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE= 17 + struct + { + uint32 min_score; + uint32 max_score; + } bg_loss_team_score; + // ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18 (no data) + // ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19 + struct + { + uint32 item_level; + uint32 item_quality; + } equipped_item; + // ... + struct + { + uint32 value1; + uint32 value2; + } raw; + }; + + AchievementCriteriaData() : dataType(ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) + { + raw.value1 = 0; + raw.value2 = 0; + } + + AchievementCriteriaData(uint32 _dataType, uint32 _value1, uint32 _value2) : dataType(AchievementCriteriaDataType(_dataType)) + { + raw.value1 = _value1; + raw.value2 = _value2; + } + + bool IsValid(AchievementCriteriaEntry const* criteria); + bool Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 = 0) const; +}; + +struct AchievementCriteriaDataSet +{ + AchievementCriteriaDataSet() : criteria_id(0) {} + typedef std::vector Storage; + void Add(AchievementCriteriaData const& data) { storage.push_back(data); } + bool Meets(Player const* source, Unit const* target, uint32 miscvalue = 0) const; + void SetCriteriaId(uint32 id) {criteria_id = id;} + private: + uint32 criteria_id; + Storage storage; +}; + +typedef std::map AchievementCriteriaDataMap; + +struct AchievementReward +{ + uint32 titleId[2]; + uint32 itemId; + uint32 sender; + std::string subject; + std::string text; +}; + +typedef std::map AchievementRewards; + +struct AchievementRewardLocale +{ + std::vector subject; + std::vector text; +}; + +typedef std::map AchievementRewardLocales; + +struct CompletedAchievementData +{ + time_t date; + bool changed; +}; + +typedef UNORDERED_MAP CriteriaProgressMap; +typedef UNORDERED_MAP CompletedAchievementMap; + +class Unit; +class Player; +class WorldPacket; + +class AchievementMgr +{ + public: + AchievementMgr(Player* pl); + ~AchievementMgr(); + + void Reset(); + static void DeleteFromDB(uint32 lowguid); + void LoadFromDB(QueryResult_AutoPtr achievementResult, QueryResult_AutoPtr criteriaResult); + void SaveToDB(); + void ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); + void CompletedAchievement(AchievementEntry const* entry); + void CheckAllAchievementCriteria(); + void SendAllAchievementData(); + void SendRespondInspectAchievements(Player* player); + bool HasAchieved(AchievementEntry const* achievement) const; + Player* GetPlayer() { return m_player; } + void UpdateTimedAchievements(uint32 timeDiff); + void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); + void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); // used for quest and scripted timed achievements + + private: + enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST }; + void SendAchievementEarned(AchievementEntry const* achievement); + void SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted); + void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); + void CompletedCriteriaFor(AchievementEntry const* achievement); + bool IsCompletedCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement); + bool IsCompletedAchievement(AchievementEntry const* entry); + void CompleteAchievementsWithRefs(AchievementEntry const* entry); + void BuildAllDataPacket(WorldPacket *data); + + Player* m_player; + CriteriaProgressMap m_criteriaProgress; + CompletedAchievementMap m_completedAchievements; + typedef std::map TimedAchievementMap; + TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS +}; + +class AchievementGlobalMgr +{ + public: + AchievementCriteriaEntryList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type); + AchievementCriteriaEntryList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type); + AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id) + { + AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id); + return itr != m_AchievementCriteriaListByAchievement.end() ? &itr->second : NULL; + } + + AchievementEntryList const* GetAchievementByReferencedId(uint32 id) const + { + AchievementListByReferencedId::const_iterator itr = m_AchievementListByReferencedId.find(id); + return itr != m_AchievementListByReferencedId.end() ? &itr->second : NULL; + } + + AchievementReward const* GetAchievementReward(AchievementEntry const* achievement) const + { + AchievementRewards::const_iterator iter = m_achievementRewards.find(achievement->ID); + return iter != m_achievementRewards.end() ? &iter->second : NULL; + } + + AchievementRewardLocale const* GetAchievementRewardLocale(AchievementEntry const* achievement) const + { + AchievementRewardLocales::const_iterator iter = m_achievementRewardLocales.find(achievement->ID); + return iter != m_achievementRewardLocales.end() ? &iter->second : NULL; + } + + AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteriaEntry const *achievementCriteria) + { + AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID); + return iter != m_criteriaDataMap.end() ? &iter->second : NULL; + } + + bool IsRealmCompleted(AchievementEntry const* achievement) const + { + return m_allCompletedAchievements.find(achievement->ID) != m_allCompletedAchievements.end(); + } + + void SetRealmCompleted(AchievementEntry const* achievement) + { + m_allCompletedAchievements.insert(achievement->ID); + } + + void LoadAchievementCriteriaList(); + void LoadAchievementCriteriaData(); + void LoadAchievementReferenceList(); + void LoadCompletedAchievements(); + void LoadRewards(); + void LoadRewardLocales(); + private: + AchievementCriteriaDataMap m_criteriaDataMap; + + // store achievement criterias by type to speed up lookup + AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; + AchievementCriteriaEntryList m_AchievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX]; + // store achievement criterias by achievement to speed up lookup + AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement; + // store achievements by referenced achievement id to speed up lookup + AchievementListByReferencedId m_AchievementListByReferencedId; + + typedef std::set AllCompletedAchievements; + AllCompletedAchievements m_allCompletedAchievements; + + AchievementRewards m_achievementRewards; + AchievementRewardLocales m_achievementRewardLocales; +}; + +#define achievementmgr Trinity::Singleton::Instance() + +#endif diff --git a/src/server/game/AddonHandler.cpp b/src/server/game/AddonHandler.cpp deleted file mode 100644 index a9c8101d7b1..00000000000 --- a/src/server/game/AddonHandler.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "zlib/zlib.h" - -#include "AddonHandler.h" -#include "Database/DatabaseEnv.h" -#include "Policies/SingletonImp.h" -#include "Opcodes.h" - -#include "Log.h" - -INSTANTIATE_SINGLETON_1( AddonHandler ); - -AddonHandler::AddonHandler() -{ -} - -AddonHandler::~AddonHandler() -{ -} - -bool AddonHandler::BuildAddonPacket(WorldPacket *Source, WorldPacket *Target) -{ - ByteBuffer AddOnPacked; - uLongf AddonRealSize; - uint32 CurrentPosition; - uint32 TempValue; - - unsigned char tdata[256] = - { - 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54, - 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75, - 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34, - 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8, - 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8, - 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A, - 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3, - 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9, - 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22, - 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E, - 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB, - 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7, - 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0, - 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6, - 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A, - 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2 - }; - - // broken addon packet, can't be received from real client - if (Source->rpos() + 4 > Source->size()) - return false; - - *Source >> TempValue; // get real size of the packed structure - - // empty addon packet, nothing process, can't be received from real client - if(!TempValue) - return false; - - AddonRealSize = TempValue; // temp value because ZLIB only excepts uLongf - - CurrentPosition = Source->rpos(); // get the position of the pointer in the structure - - AddOnPacked.resize(AddonRealSize); // resize target for zlib action - - if (!uncompress(const_cast(AddOnPacked.contents()), &AddonRealSize, const_cast((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK) - { - Target->Initialize(SMSG_ADDON_INFO); - - uint32 addonsCount; - AddOnPacked >> addonsCount; // addons count? - - for(uint32 i = 0; i < addonsCount; ++i) - { - std::string addonName; - uint8 enabled; - uint32 crc, unk2; - - // check next addon data format correctness - if(AddOnPacked.rpos()+1 > AddOnPacked.size()) - return false; - - AddOnPacked >> addonName; - - // recheck next addon data format correctness - if(AddOnPacked.rpos()+1+4+4 > AddOnPacked.size()) - return false; - - AddOnPacked >> enabled >> crc >> unk2; - - sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk2); - - uint8 state = (enabled ? 2 : 1); - *Target << uint8(state); - - uint8 unk1 = (enabled ? 1 : 0); - *Target << uint8(unk1); - if (unk1) - { - uint8 unk2 = (crc != 0x4c1c776d); // If addon is Standard addon CRC - *Target << uint8(unk2); - if (unk2) - Target->append(tdata, sizeof(tdata)); - - *Target << uint32(0); - } - - uint8 unk3 = (enabled ? 0 : 1); - *Target << uint8(unk3); - if (unk3) - { - // String, 256 (null terminated?) - *Target << uint8(0); - } - } - - uint32 unk4; - AddOnPacked >> unk4; - - uint32 count = 0; - *Target << uint32(count); - /*for(uint32 i = 0; i < count; ++i) - { - uint32 - string (16 bytes) - string (16 bytes) - uint32 - }*/ - - if(AddOnPacked.rpos() != AddOnPacked.size()) - sLog.outDebug("packet under read!"); - } - else - { - sLog.outError("Addon packet uncompress error :("); - return false; - } - return true; -} - -/* Code use in 1.10.2 when client not ignore ban state sended for addons. Saved for reference if client switch to use server ban state information -void AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target, uint32 Packetoffset) -{ - ByteBuffer AddOnPacked; - uLongf AddonRealSize; - uint32 CurrentPosition; - uint32 TempValue; - - *Source >> TempValue; //get real size of the packed structure - - AddonRealSize = TempValue; //temp value becouse ZLIB only excepts uLongf - - CurrentPosition = Source->rpos(); //get the position of the pointer in the structure - - AddOnPacked.resize(AddonRealSize); //resize target for zlib action - - if (!uncompress((uint8*)AddOnPacked.contents(), &AddonRealSize, (uint8*)(*Source).contents() + CurrentPosition, (*Source).size() - CurrentPosition)!= Z_OK) - { - bool* AddonAllowed = new bool; //handle addon check and enable-ing - - uint32 Unknown1; - uint8 Unknown0; - - AddOnPacked >> Unknown0; - AddOnPacked >> Unknown1; - - Target->Initialize(SMSG_ADDON_INFO); - - uint32 i = 5; //offset for addon extraction - while(i != AddOnPacked.size()) - { - std::string AddonNames; - AddOns* Addonstr = new AddOns; - uint8 unk6; - uint64 CRCCHECK; - AddOnPacked >> AddonNames >> CRCCHECK >> unk6; - - //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK,unk6); - - Addonstr->Name = AddonNames; - Addonstr->CRC = CRCCHECK; - - //if not allowed but unknown added to list - if (GetAddonStatus(Addonstr, AddonAllowed)) // If addon is new - { - Addonstr->Enabled = m_Addon_Default; // by default new addons are set from Config file - *AddonAllowed = m_Addon_Default; // Set addon allowed on default value - _AddAddon(Addonstr); - sLog.outDetail("Found new Addon, Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK, unk6); - } - - if (CRCCHECK == 0x4C1C776D01LL) //If addon is Standard addon CRC - { - //value's standard Addons - *Target << uint8(0) << uint8(2) << uint8(1) << uint8(0) << uint32(0); - } - else if (*AddonAllowed) //if addon is Custom addons - //value's enable addon - *Target << uint8(0x00) << uint8(0x01) << uint8(0x00) << uint8(0x01); - else - //value's disable addom - *Target << uint8(0x00) << uint8(0x0) << uint8(0x00) << uint8(0x0); - - i += AddonNames.size() + 10; - } - *Target << uint8(0x0); - - //delete mem allocation - delete AddonAllowed; - } - else - { - //handle uncompress error - } -} -*/ - diff --git a/src/server/game/AddonHandler.h b/src/server/game/AddonHandler.h deleted file mode 100644 index 999785339bc..00000000000 --- a/src/server/game/AddonHandler.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __ADDONHANDLER_H -#define __ADDONHANDLER_H - -#include "Common.h" -#include "Config/ConfigEnv.h" -#include "Policies/Singleton.h" - -#include "WorldPacket.h" - -class AddonHandler -{ - public: - /* Construction */ - AddonHandler(); - ~AddonHandler(); - //built addon packet - bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target); -}; -#define sAddOnHandler Trinity::Singleton::Instance() -#endif - diff --git a/src/server/game/AddonMgr.cpp b/src/server/game/AddonMgr.cpp deleted file mode 100644 index 66e4fbc8765..00000000000 --- a/src/server/game/AddonMgr.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2009 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Database/DatabaseEnv.h" -#include "Policies/SingletonImp.h" - -#include "AddonMgr.h" -#include "ObjectAccessor.h" -#include "Player.h" -#include "Util.h" -#include "Auth/Sha1.h" -#include "ProgressBar.h" - -extern DatabaseType LoginDatabase; - -INSTANTIATE_SINGLETON_1(AddonMgr); - -AddonMgr::AddonMgr() -{ -} - -AddonMgr::~AddonMgr() -{ -} - -void AddonMgr::LoadFromDB() -{ - QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT name, crc FROM addons"); - if (!result) - { - sLog.outErrorDb("The table `addons` is empty"); - return; - } - - barGoLink bar(result->GetRowCount()); - uint32 count = 0; - Field *fields; - - do - { - fields = result->Fetch(); - bar.step(); - count++; - - std::string name = fields[0].GetCppString(); - uint32 crc = fields[1].GetUInt32(); - - SavedAddon addon(name, crc); - m_knownAddons.push_back(addon); - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u known addons", count); -} - -void AddonMgr::SaveAddon(AddonInfo const& addon) -{ - std::string name = addon.Name; - CharacterDatabase.escape_string(name); - CharacterDatabase.PExecute("INSERT INTO addons (name, crc) VALUES ('%s', %u)", name.c_str(), addon.CRC); - - SavedAddon newAddon(addon.Name, addon.CRC); - m_knownAddons.push_back(newAddon); -} - -SavedAddon const* AddonMgr::GetAddonInfo(const std::string& name) const -{ - for (SavedAddonsList::const_iterator it = m_knownAddons.begin(); it != m_knownAddons.end(); ++it) - { - SavedAddon const& addon = (*it); - if (addon.Name == name) - return &addon; - } - - return NULL; -} diff --git a/src/server/game/AddonMgr.h b/src/server/game/AddonMgr.h deleted file mode 100644 index 8f5eed4918f..00000000000 --- a/src/server/game/AddonMgr.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2009 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _ADDONMGR_H -#define _ADDONMGR_H - -#include "Common.h" -#include "Policies/Singleton.h" - -#include - -class WorldSession; - -struct AddonInfo -{ - AddonInfo(const std::string& name, uint8 enabled, uint32 crc, uint8 state, bool crcOrPubKey) - { - Name = name; - Enabled = enabled; - CRC = crc; - State = state; - UsePublicKeyOrCRC = crcOrPubKey; - } - - std::string Name; - uint8 Enabled; - uint32 CRC; - uint8 State; - bool UsePublicKeyOrCRC; -}; - -struct SavedAddon -{ - SavedAddon(const std::string& name, uint32 crc) - { - Name = name; - CRC = crc; - } - - std::string Name; - uint32 CRC; -}; - -// List of client addons (for WorldSession). -typedef std::list AddonsList; - -// List of saved addons (in DB). -typedef std::list SavedAddonsList; - -#define STANDARD_ADDON_CRC 0x4c1c776d - -class AddonMgr -{ - public: - - AddonMgr(); - ~AddonMgr(); - - void LoadFromDB(); - void SaveAddon(AddonInfo const& addon); - - SavedAddon const* GetAddonInfo(const std::string& name) const; - - private: - - SavedAddonsList m_knownAddons; // Known addons. -}; - -#define sAddonMgr Trinity::Singleton::Instance() - -#endif - diff --git a/src/server/game/Addons/AddonHandler.cpp b/src/server/game/Addons/AddonHandler.cpp new file mode 100644 index 00000000000..a9c8101d7b1 --- /dev/null +++ b/src/server/game/Addons/AddonHandler.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "zlib/zlib.h" + +#include "AddonHandler.h" +#include "Database/DatabaseEnv.h" +#include "Policies/SingletonImp.h" +#include "Opcodes.h" + +#include "Log.h" + +INSTANTIATE_SINGLETON_1( AddonHandler ); + +AddonHandler::AddonHandler() +{ +} + +AddonHandler::~AddonHandler() +{ +} + +bool AddonHandler::BuildAddonPacket(WorldPacket *Source, WorldPacket *Target) +{ + ByteBuffer AddOnPacked; + uLongf AddonRealSize; + uint32 CurrentPosition; + uint32 TempValue; + + unsigned char tdata[256] = + { + 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54, + 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75, + 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34, + 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8, + 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8, + 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A, + 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3, + 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9, + 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22, + 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E, + 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB, + 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7, + 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0, + 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6, + 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A, + 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2 + }; + + // broken addon packet, can't be received from real client + if (Source->rpos() + 4 > Source->size()) + return false; + + *Source >> TempValue; // get real size of the packed structure + + // empty addon packet, nothing process, can't be received from real client + if(!TempValue) + return false; + + AddonRealSize = TempValue; // temp value because ZLIB only excepts uLongf + + CurrentPosition = Source->rpos(); // get the position of the pointer in the structure + + AddOnPacked.resize(AddonRealSize); // resize target for zlib action + + if (!uncompress(const_cast(AddOnPacked.contents()), &AddonRealSize, const_cast((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK) + { + Target->Initialize(SMSG_ADDON_INFO); + + uint32 addonsCount; + AddOnPacked >> addonsCount; // addons count? + + for(uint32 i = 0; i < addonsCount; ++i) + { + std::string addonName; + uint8 enabled; + uint32 crc, unk2; + + // check next addon data format correctness + if(AddOnPacked.rpos()+1 > AddOnPacked.size()) + return false; + + AddOnPacked >> addonName; + + // recheck next addon data format correctness + if(AddOnPacked.rpos()+1+4+4 > AddOnPacked.size()) + return false; + + AddOnPacked >> enabled >> crc >> unk2; + + sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk2); + + uint8 state = (enabled ? 2 : 1); + *Target << uint8(state); + + uint8 unk1 = (enabled ? 1 : 0); + *Target << uint8(unk1); + if (unk1) + { + uint8 unk2 = (crc != 0x4c1c776d); // If addon is Standard addon CRC + *Target << uint8(unk2); + if (unk2) + Target->append(tdata, sizeof(tdata)); + + *Target << uint32(0); + } + + uint8 unk3 = (enabled ? 0 : 1); + *Target << uint8(unk3); + if (unk3) + { + // String, 256 (null terminated?) + *Target << uint8(0); + } + } + + uint32 unk4; + AddOnPacked >> unk4; + + uint32 count = 0; + *Target << uint32(count); + /*for(uint32 i = 0; i < count; ++i) + { + uint32 + string (16 bytes) + string (16 bytes) + uint32 + }*/ + + if(AddOnPacked.rpos() != AddOnPacked.size()) + sLog.outDebug("packet under read!"); + } + else + { + sLog.outError("Addon packet uncompress error :("); + return false; + } + return true; +} + +/* Code use in 1.10.2 when client not ignore ban state sended for addons. Saved for reference if client switch to use server ban state information +void AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target, uint32 Packetoffset) +{ + ByteBuffer AddOnPacked; + uLongf AddonRealSize; + uint32 CurrentPosition; + uint32 TempValue; + + *Source >> TempValue; //get real size of the packed structure + + AddonRealSize = TempValue; //temp value becouse ZLIB only excepts uLongf + + CurrentPosition = Source->rpos(); //get the position of the pointer in the structure + + AddOnPacked.resize(AddonRealSize); //resize target for zlib action + + if (!uncompress((uint8*)AddOnPacked.contents(), &AddonRealSize, (uint8*)(*Source).contents() + CurrentPosition, (*Source).size() - CurrentPosition)!= Z_OK) + { + bool* AddonAllowed = new bool; //handle addon check and enable-ing + + uint32 Unknown1; + uint8 Unknown0; + + AddOnPacked >> Unknown0; + AddOnPacked >> Unknown1; + + Target->Initialize(SMSG_ADDON_INFO); + + uint32 i = 5; //offset for addon extraction + while(i != AddOnPacked.size()) + { + std::string AddonNames; + AddOns* Addonstr = new AddOns; + uint8 unk6; + uint64 CRCCHECK; + AddOnPacked >> AddonNames >> CRCCHECK >> unk6; + + //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK,unk6); + + Addonstr->Name = AddonNames; + Addonstr->CRC = CRCCHECK; + + //if not allowed but unknown added to list + if (GetAddonStatus(Addonstr, AddonAllowed)) // If addon is new + { + Addonstr->Enabled = m_Addon_Default; // by default new addons are set from Config file + *AddonAllowed = m_Addon_Default; // Set addon allowed on default value + _AddAddon(Addonstr); + sLog.outDetail("Found new Addon, Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK, unk6); + } + + if (CRCCHECK == 0x4C1C776D01LL) //If addon is Standard addon CRC + { + //value's standard Addons + *Target << uint8(0) << uint8(2) << uint8(1) << uint8(0) << uint32(0); + } + else if (*AddonAllowed) //if addon is Custom addons + //value's enable addon + *Target << uint8(0x00) << uint8(0x01) << uint8(0x00) << uint8(0x01); + else + //value's disable addom + *Target << uint8(0x00) << uint8(0x0) << uint8(0x00) << uint8(0x0); + + i += AddonNames.size() + 10; + } + *Target << uint8(0x0); + + //delete mem allocation + delete AddonAllowed; + } + else + { + //handle uncompress error + } +} +*/ + diff --git a/src/server/game/Addons/AddonHandler.h b/src/server/game/Addons/AddonHandler.h new file mode 100644 index 00000000000..999785339bc --- /dev/null +++ b/src/server/game/Addons/AddonHandler.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ADDONHANDLER_H +#define __ADDONHANDLER_H + +#include "Common.h" +#include "Config/ConfigEnv.h" +#include "Policies/Singleton.h" + +#include "WorldPacket.h" + +class AddonHandler +{ + public: + /* Construction */ + AddonHandler(); + ~AddonHandler(); + //built addon packet + bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target); +}; +#define sAddOnHandler Trinity::Singleton::Instance() +#endif + diff --git a/src/server/game/Addons/AddonMgr.cpp b/src/server/game/Addons/AddonMgr.cpp new file mode 100644 index 00000000000..66e4fbc8765 --- /dev/null +++ b/src/server/game/Addons/AddonMgr.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2009 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/DatabaseEnv.h" +#include "Policies/SingletonImp.h" + +#include "AddonMgr.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "Util.h" +#include "Auth/Sha1.h" +#include "ProgressBar.h" + +extern DatabaseType LoginDatabase; + +INSTANTIATE_SINGLETON_1(AddonMgr); + +AddonMgr::AddonMgr() +{ +} + +AddonMgr::~AddonMgr() +{ +} + +void AddonMgr::LoadFromDB() +{ + QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT name, crc FROM addons"); + if (!result) + { + sLog.outErrorDb("The table `addons` is empty"); + return; + } + + barGoLink bar(result->GetRowCount()); + uint32 count = 0; + Field *fields; + + do + { + fields = result->Fetch(); + bar.step(); + count++; + + std::string name = fields[0].GetCppString(); + uint32 crc = fields[1].GetUInt32(); + + SavedAddon addon(name, crc); + m_knownAddons.push_back(addon); + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u known addons", count); +} + +void AddonMgr::SaveAddon(AddonInfo const& addon) +{ + std::string name = addon.Name; + CharacterDatabase.escape_string(name); + CharacterDatabase.PExecute("INSERT INTO addons (name, crc) VALUES ('%s', %u)", name.c_str(), addon.CRC); + + SavedAddon newAddon(addon.Name, addon.CRC); + m_knownAddons.push_back(newAddon); +} + +SavedAddon const* AddonMgr::GetAddonInfo(const std::string& name) const +{ + for (SavedAddonsList::const_iterator it = m_knownAddons.begin(); it != m_knownAddons.end(); ++it) + { + SavedAddon const& addon = (*it); + if (addon.Name == name) + return &addon; + } + + return NULL; +} diff --git a/src/server/game/Addons/AddonMgr.h b/src/server/game/Addons/AddonMgr.h new file mode 100644 index 00000000000..8f5eed4918f --- /dev/null +++ b/src/server/game/Addons/AddonMgr.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2009 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ADDONMGR_H +#define _ADDONMGR_H + +#include "Common.h" +#include "Policies/Singleton.h" + +#include + +class WorldSession; + +struct AddonInfo +{ + AddonInfo(const std::string& name, uint8 enabled, uint32 crc, uint8 state, bool crcOrPubKey) + { + Name = name; + Enabled = enabled; + CRC = crc; + State = state; + UsePublicKeyOrCRC = crcOrPubKey; + } + + std::string Name; + uint8 Enabled; + uint32 CRC; + uint8 State; + bool UsePublicKeyOrCRC; +}; + +struct SavedAddon +{ + SavedAddon(const std::string& name, uint32 crc) + { + Name = name; + CRC = crc; + } + + std::string Name; + uint32 CRC; +}; + +// List of client addons (for WorldSession). +typedef std::list AddonsList; + +// List of saved addons (in DB). +typedef std::list SavedAddonsList; + +#define STANDARD_ADDON_CRC 0x4c1c776d + +class AddonMgr +{ + public: + + AddonMgr(); + ~AddonMgr(); + + void LoadFromDB(); + void SaveAddon(AddonInfo const& addon); + + SavedAddon const* GetAddonInfo(const std::string& name) const; + + private: + + SavedAddonsList m_knownAddons; // Known addons. +}; + +#define sAddonMgr Trinity::Singleton::Instance() + +#endif + diff --git a/src/server/game/ArenaTeam.cpp b/src/server/game/ArenaTeam.cpp deleted file mode 100644 index 4224ff6357a..00000000000 --- a/src/server/game/ArenaTeam.cpp +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ObjectMgr.h" -#include "WorldPacket.h" - -#include "ArenaTeam.h" -#include "World.h" - -void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot) -{ - if (int32(personal_rating) + mod < 0) - personal_rating = 0; - else - personal_rating += mod; - if (plr) - plr->SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING, personal_rating); -} - -ArenaTeam::ArenaTeam() -{ - m_TeamId = 0; - m_Type = 0; - m_Name = ""; - m_CaptainGuid = 0; - m_BackgroundColor = 0; // background - m_EmblemStyle = 0; // icon - m_EmblemColor = 0; // icon color - m_BorderStyle = 0; // border - m_BorderColor = 0; // border color - m_stats.games_week = 0; - m_stats.games_season = 0; - m_stats.rank = 0; - if (sWorld.getConfig(CONFIG_ARENA_START_RATING) >= 0) - m_stats.rating = sWorld.getConfig(CONFIG_ARENA_START_RATING); - else if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) - m_stats.rating = 0; - else - m_stats.rating = 1500; - m_stats.wins_week = 0; - m_stats.wins_season = 0; -} - -ArenaTeam::~ArenaTeam() -{ -} - -bool ArenaTeam::Create(uint64 captainGuid, uint32 type, std::string ArenaTeamName) -{ - if (!objmgr.GetPlayer(captainGuid)) // player not exist - return false; - if (objmgr.GetArenaTeamByName(ArenaTeamName)) // arena team with this name already exist - return false; - - sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(captainGuid)); - - m_CaptainGuid = captainGuid; - m_Name = ArenaTeamName; - m_Type = type; - - m_TeamId = objmgr.GenerateArenaTeamId(); - - // ArenaTeamName already assigned to ArenaTeam::name, use it to encode string for DB - CharacterDatabase.escape_string(ArenaTeamName); - - CharacterDatabase.BeginTransaction(); - // CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid='%u'", m_TeamId); - MAX(arenateam)+1 not exist - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", m_TeamId); - CharacterDatabase.PExecute("INSERT INTO arena_team (arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor) " - "VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')", - m_TeamId, ArenaTeamName.c_str(), GUID_LOPART(m_CaptainGuid), m_Type, m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor); - CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games, wins, played, wins2, rank) VALUES " - "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", m_TeamId, m_stats.rating, m_stats.games_week, m_stats.wins_week, m_stats.games_season, m_stats.wins_season, m_stats.rank); - - CharacterDatabase.CommitTransaction(); - - AddMember(m_CaptainGuid); - sLog.outArena("New ArenaTeam created [Id: %u] [Type: %u] [Captain GUID: %u]", GetId(), GetType(), GetCaptain()); - return true; -} - -bool ArenaTeam::AddMember(const uint64& PlayerGuid) -{ - std::string plName; - uint8 plClass; - - // arena team is full (can't have more than type * 2 players!) - if (GetMembersSize() >= GetType() * 2) - return false; - - Player *pl = objmgr.GetPlayer(PlayerGuid); - if (pl) - { - if (pl->GetArenaTeamId(GetSlot())) - { - sLog.outError("Arena::AddMember() : player already in this sized team"); - return false; - } - - plClass = pl->getClass(); - plName = pl->GetName(); - } - else - { - // 0 1 - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT name, class FROM characters WHERE guid='%u'", GUID_LOPART(PlayerGuid)); - if (!result) - return false; - - plName = (*result)[0].GetCppString(); - plClass = (*result)[1].GetUInt8(); - - // check if player already in arenateam of that size - if (Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0) - { - sLog.outError("Arena::AddMember() : player already in this sized team"); - return false; - } - } - - // remove all player signs from another petitions - // this will be prevent attempt joining player to many arenateams and corrupt arena team data integrity - Player::RemovePetitionsAndSigns(PlayerGuid, GetType()); - - ArenaTeamMember newmember; - newmember.name = plName; - newmember.guid = PlayerGuid; - newmember.Class = plClass; - newmember.games_season = 0; - newmember.games_week = 0; - newmember.wins_season = 0; - newmember.wins_week = 0; - newmember.personal_rating = 0; - - if (sWorld.getConfig(CONFIG_ARENA_START_PERSONAL_RATING) > 0) - newmember.personal_rating = sWorld.getConfig(CONFIG_ARENA_START_PERSONAL_RATING); - else - { - if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) < 6) - newmember.personal_rating = 1500; - else - if (GetRating() >= 1000) - newmember.personal_rating = 1000; - } - - m_members.push_back(newmember); - - CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", m_TeamId, GUID_LOPART(newmember.guid), newmember.personal_rating); - - if (pl) - { - pl->SetInArenaTeam(m_TeamId, GetSlot(), GetType()); - pl->SetArenaTeamIdInvited(0); - pl->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_PERSONAL_RATING, newmember.personal_rating); - - // hide promote/remove buttons - if (m_CaptainGuid != PlayerGuid) - pl->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1); - sLog.outArena("Player: %s [GUID: %u] joined arena team type: %u [Id: %u].", pl->GetName(), pl->GetGUIDLow(), GetType(), GetId()); - } - return true; -} - -bool ArenaTeam::LoadArenaTeamFromDB(QueryResult_AutoPtr arenaTeamDataResult) -{ - if (!arenaTeamDataResult) - return false; - - Field *fields = arenaTeamDataResult->Fetch(); - - m_TeamId = fields[0].GetUInt32(); - m_Name = fields[1].GetCppString(); - m_CaptainGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); - m_Type = fields[3].GetUInt32(); - m_BackgroundColor = fields[4].GetUInt32(); - m_EmblemStyle = fields[5].GetUInt32(); - m_EmblemColor = fields[6].GetUInt32(); - m_BorderStyle = fields[7].GetUInt32(); - m_BorderColor = fields[8].GetUInt32(); - //load team stats - m_stats.rating = fields[9].GetUInt32(); - m_stats.games_week = fields[10].GetUInt32(); - m_stats.wins_week = fields[11].GetUInt32(); - m_stats.games_season = fields[12].GetUInt32(); - m_stats.wins_season = fields[13].GetUInt32(); - m_stats.rank = fields[14].GetUInt32(); - - return true; -} - -bool ArenaTeam::LoadMembersFromDB(QueryResult_AutoPtr arenaTeamMembersResult) -{ - if (!arenaTeamMembersResult) - return false; - - bool captainPresentInTeam = false; - - do - { - Field *fields = arenaTeamMembersResult->Fetch(); - //prevent crash if db records are broken, when all members in result are already processed and current team hasn't got any members - if (!fields) - break; - uint32 arenaTeamId = fields[0].GetUInt32(); - if (arenaTeamId < m_TeamId) - { - //there is in table arena_team_member record which doesn't have arenateamid in arena_team table, report error - sLog.outErrorDb("ArenaTeam %u does not exist but it has record in arena_team_member table, deleting it!", arenaTeamId); - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", arenaTeamId); - continue; - } - - if (arenaTeamId > m_TeamId) - //we loaded all members for this arena_team already, break cycle - break; - - ArenaTeamMember newmember; - newmember.guid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_PLAYER); - newmember.games_week = fields[2].GetUInt32(); - newmember.wins_week = fields[3].GetUInt32(); - newmember.games_season = fields[4].GetUInt32(); - newmember.wins_season = fields[5].GetUInt32(); - newmember.personal_rating = fields[6].GetUInt32(); - newmember.name = fields[7].GetCppString(); - newmember.Class = fields[8].GetUInt8(); - - //check if member exists in characters table - if (newmember.name.empty()) - { - sLog.outErrorDb("ArenaTeam %u has member with empty name - probably player %u doesn't exist, deleting him from memberlist!", arenaTeamId, GUID_LOPART(newmember.guid)); - this->DelMember(newmember.guid); - continue; - } - - if (newmember.guid == GetCaptain()) - captainPresentInTeam = true; - - m_members.push_back(newmember); - }while (arenaTeamMembersResult->NextRow()); - - if (Empty() || !captainPresentInTeam) - { - // arena team is empty or captain is not in team, delete from db - sLog.outErrorDb("ArenaTeam %u does not have any members or its captain is not in team, disbanding it...", m_TeamId); - return false; - } - - return true; -} - -void ArenaTeam::SetCaptain(const uint64& guid) -{ - // disable remove/promote buttons - Player *oldcaptain = objmgr.GetPlayer(GetCaptain()); - if (oldcaptain) - oldcaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1); - - // set new captain - m_CaptainGuid = guid; - - // update database - CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), GetId()); - - // enable remove/promote buttons - Player *newcaptain = objmgr.GetPlayer(guid); - if (newcaptain) - { - newcaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 0); - sLog.outArena("Player: %s [GUID: %u] promoted player: %s [GUID: %u] to leader of arena team [Id: %u] [Type: %u].", oldcaptain->GetName(), oldcaptain->GetGUIDLow(), newcaptain->GetName(), newcaptain->GetGUIDLow(), GetId(), GetType()); - } -} - -void ArenaTeam::DelMember(uint64 guid) -{ - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (itr->guid == guid) - { - m_members.erase(itr); - break; - } - - if (Player *player = objmgr.GetPlayer(guid)) - { - player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0); - // delete all info regarding this team - for (uint32 i = 0; i < ARENA_TEAM_END; ++i) - player->SetArenaTeamInfoField(GetSlot(), ArenaTeamInfoType(i), 0); - sLog.outArena("Player: %s [GUID: %u] left arena team type: %u [Id: %u].", player->GetName(), player->GetGUIDLow(), GetType(), GetId()); - } - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u' AND guid = '%u'", GetId(), GUID_LOPART(guid)); -} - -void ArenaTeam::Disband(WorldSession *session) -{ - // event - if (session) - // probably only 1 string required... - BroadcastEvent(ERR_ARENA_TEAM_DISBANDED_S, 0, 2, session->GetPlayerName(), GetName(), ""); - - while (!m_members.empty()) - // Removing from members is done in DelMember. - DelMember(m_members.front().guid); - - if (session) - if (Player *player = session->GetPlayer()) - sLog.outArena("Player: %s [GUID: %u] disbanded arena team type: %u [Id: %u].", player->GetName(), player->GetGUIDLow(), GetType(), GetId()); - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", m_TeamId); - CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", m_TeamId); //< this should be alredy done by calling DelMember(memberGuids[j]); for each member - CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", m_TeamId); - CharacterDatabase.CommitTransaction(); - objmgr.RemoveArenaTeam(m_TeamId); -} - -void ArenaTeam::Roster(WorldSession *session) -{ - Player *pl = NULL; - - uint8 unk308 = 0; - - WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100); - data << uint32(GetId()); // team id - data << uint8(unk308); // 308 unknown value but affect packet structure - data << uint32(GetMembersSize()); // members count - data << uint32(GetType()); // arena team type? - - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - pl = objmgr.GetPlayer(itr->guid); - - data << uint64(itr->guid); // guid - data << uint8((pl ? 1 : 0)); // online flag - data << itr->name; // member name - data << uint32((itr->guid == GetCaptain() ? 0 : 1));// captain flag 0 captain 1 member - data << uint8((pl ? pl->getLevel() : 0)); // unknown, level? - data << uint8(itr->Class); // class - data << uint32(itr->games_week); // played this week - data << uint32(itr->wins_week); // wins this week - data << uint32(itr->games_season); // played this season - data << uint32(itr->wins_season); // wins this season - data << uint32(itr->personal_rating); // personal rating - if (unk308) - { - data << float(0.0); // 308 unk - data << float(0.0); // 308 unk - } - } - - session->SendPacket(&data); - sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER"); -} - -void ArenaTeam::Query(WorldSession *session) -{ - WorldPacket data(SMSG_ARENA_TEAM_QUERY_RESPONSE, 4*7+GetName().size()+1); - data << uint32(GetId()); // team id - data << GetName(); // team name - data << uint32(GetType()); // arena team type (2=2x2, 3=3x3 or 5=5x5) - data << uint32(m_BackgroundColor); // background color - data << uint32(m_EmblemStyle); // emblem style - data << uint32(m_EmblemColor); // emblem color - data << uint32(m_BorderStyle); // border style - data << uint32(m_BorderColor); // border color - session->SendPacket(&data); - sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE"); -} - -void ArenaTeam::Stats(WorldSession *session) -{ - WorldPacket data(SMSG_ARENA_TEAM_STATS, 4*7); - data << uint32(GetId()); // team id - data << uint32(m_stats.rating); // rating - data << uint32(m_stats.games_week); // games this week - data << uint32(m_stats.wins_week); // wins this week - data << uint32(m_stats.games_season); // played this season - data << uint32(m_stats.wins_season); // wins this season - data << uint32(m_stats.rank); // rank - session->SendPacket(&data); -} - -void ArenaTeam::NotifyStatsChanged() -{ - // this is called after a rated match ended - // updates arena team stats for every member of the team (not only the ones who participated!) - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - Player * plr = objmgr.GetPlayer(itr->guid); - if (plr) - Stats(plr->GetSession()); - } -} - -void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) -{ - ArenaTeamMember* member = GetMember(guid); - if (!member) - return; - - WorldPacket data(MSG_INSPECT_ARENA_TEAMS, 8+1+4*6); - data << uint64(guid); // player guid - data << uint8(GetSlot()); // slot (0...2) - data << uint32(GetId()); // arena team id - data << uint32(m_stats.rating); // rating - data << uint32(m_stats.games_season); // season played - data << uint32(m_stats.wins_season); // season wins - data << uint32(member->games_season); // played (count of all games, that the inspected member participated...) - data << uint32(member->personal_rating); // personal rating - session->SendPacket(&data); -} - -void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor) -{ - m_BackgroundColor = backgroundColor; - m_EmblemStyle = emblemStyle; - m_EmblemColor = emblemColor; - m_BorderStyle = borderStyle; - m_BorderColor = borderColor; - - CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_TeamId); -} - -void ArenaTeam::SetStats(uint32 stat_type, uint32 value) -{ - switch(stat_type) - { - case STAT_TYPE_RATING: - m_stats.rating = value; - CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u' WHERE arenateamid = '%u'", value, GetId()); - break; - case STAT_TYPE_GAMES_WEEK: - m_stats.games_week = value; - CharacterDatabase.PExecute("UPDATE arena_team_stats SET games = '%u' WHERE arenateamid = '%u'", value, GetId()); - break; - case STAT_TYPE_WINS_WEEK: - m_stats.wins_week = value; - CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins = '%u' WHERE arenateamid = '%u'", value, GetId()); - break; - case STAT_TYPE_GAMES_SEASON: - m_stats.games_season = value; - CharacterDatabase.PExecute("UPDATE arena_team_stats SET played = '%u' WHERE arenateamid = '%u'", value, GetId()); - break; - case STAT_TYPE_WINS_SEASON: - m_stats.wins_season = value; - CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins2 = '%u' WHERE arenateamid = '%u'", value, GetId()); - break; - case STAT_TYPE_RANK: - m_stats.rank = value; - CharacterDatabase.PExecute("UPDATE arena_team_stats SET rank = '%u' WHERE arenateamid = '%u'", value, GetId()); - break; - default: - sLog.outDebug("unknown stat type in ArenaTeam::SetStats() %u", stat_type); - break; - } -} - -void ArenaTeam::BroadcastPacket(WorldPacket *packet) -{ - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - Player *player = objmgr.GetPlayer(itr->guid); - if (player) - player->GetSession()->SendPacket(packet); - } -} - -void ArenaTeam::BroadcastEvent(ArenaTeamEvents event, uint64 guid, uint8 strCount, std::string str1, std::string str2, std::string str3) -{ - WorldPacket data(SMSG_ARENA_TEAM_EVENT, 1+1+1); - data << uint8(event); - data << uint8(strCount); - switch (strCount) - { - case 0: - break; - case 1: - data << str1; - break; - case 2: - data << str1 << str2; - break; - case 3: - data << str1 << str2 << str3; - break; - default: - sLog.outError("Unhandled strCount %u in ArenaTeam::BroadcastEvent", strCount); - return; - } - - if (guid) - data << uint64(guid); - - BroadcastPacket(&data); - - sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_EVENT"); -} - -uint8 ArenaTeam::GetSlotByType(uint32 type) -{ - switch(type) - { - case ARENA_TEAM_2v2: return 0; - case ARENA_TEAM_3v3: return 1; - case ARENA_TEAM_5v5: return 2; - default: - break; - } - sLog.outError("FATAL: Unknown arena team type %u for some arena team", type); - return 0xFF; -} - -bool ArenaTeam::HaveMember(const uint64& guid) const -{ - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (itr->guid == guid) - return true; - - return false; -} - -uint32 ArenaTeam::GetPoints(uint32 MemberRating) -{ - // returns how many points would be awarded with this team type with this rating - float points; - - uint32 rating = MemberRating + 150 < m_stats.rating ? MemberRating : m_stats.rating; - - if (rating <= 1500) - { - if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) < 6) - points = (float)rating * 0.22f + 14.0f; - else - points = 344; - } - else - points = 1511.26f / (1.0f + 1639.28f * exp(-0.00412f * (float)rating)); - - // type penalties for <5v5 teams - if (m_Type == ARENA_TEAM_2v2) - points *= 0.76f; - else if (m_Type == ARENA_TEAM_3v3) - points *= 0.88f; - - return (uint32) points; -} - -float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating) -{ - // returns the chance to win against a team with the given rating, used in the rating adjustment calculation - // ELO system - - if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) - if (enemy_rating < 1000) - enemy_rating = 1000; - return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f)); -} - -void ArenaTeam::FinishGame(int32 mod) -{ - if (int32(m_stats.rating) + mod < 0) - m_stats.rating = 0; - else - m_stats.rating += mod; - - m_stats.games_week += 1; - m_stats.games_season += 1; - // update team's rank - m_stats.rank = 1; - ObjectMgr::ArenaTeamMap::const_iterator i = objmgr.GetArenaTeamMapBegin(); - for (; i != objmgr.GetArenaTeamMapEnd(); ++i) - { - if (i->second->GetType() == m_Type && i->second->GetStats().rating > m_stats.rating) - ++m_stats.rank; - } -} - -int32 ArenaTeam::WonAgainst(uint32 againstRating) -{ - // called when the team has won - // 'chance' calculation - to beat the opponent - float chance = GetChanceAgainst(m_stats.rating, againstRating); - float K = (m_stats.rating < 1000) ? 48.0f : 32.0f; - // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) - int32 mod = (int32)floor(K* (1.0f - chance)); - - // modify the team stats accordingly - FinishGame(mod); - m_stats.wins_week += 1; - m_stats.wins_season += 1; - - // return the rating change, used to display it on the results screen - return mod; -} - -int32 ArenaTeam::LostAgainst(uint32 againstRating) -{ - // called when the team has lost - //'chance' calculation - to loose to the opponent - float chance = GetChanceAgainst(m_stats.rating, againstRating); - if (m_stats.rating < 1000) - { - FinishGame(0); - return 0; - } - - float K = 32.0f; - int32 mod = (int32)ceil(K * (0.0f - chance)); - // modify the team stats accordingly - FinishGame(mod); - - // return the rating change, used to display it on the results screen - return mod; -} - -void ArenaTeam::MemberLost(Player * plr, uint32 againstRating) -{ - // called for each participant of a match after losing - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - if (itr->guid == plr->GetGUID()) - { - // update personal rating - float chance = GetChanceAgainst(itr->personal_rating, againstRating); - float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f; - // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) - int32 mod = (int32)ceil(K * (0.0f - chance)); - itr->ModifyPersonalRating(plr, mod, GetSlot()); - // update personal played stats - itr->games_week +=1; - itr->games_season +=1; - // update the unit fields - plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->games_week); - plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->games_season); - return; - } - } -} - -void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating) -{ - // called for offline player after ending rated arena match! - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - if (itr->guid == guid) - { - // update personal rating - float chance = GetChanceAgainst(itr->personal_rating, againstRating); - float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f; - // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) - int32 mod = (int32)ceil(K * (0.0f - chance)); - if (int32(itr->personal_rating) + mod < 0) - itr->personal_rating = 0; - else - itr->personal_rating += mod; - // update personal played stats - itr->games_week +=1; - itr->games_season +=1; - return; - } - } -} - -void ArenaTeam::MemberWon(Player * plr, uint32 againstRating) -{ - // called for each participant after winning a match - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - if (itr->guid == plr->GetGUID()) - { - // update personal rating - float chance = GetChanceAgainst(itr->personal_rating, againstRating); - float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f; - // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) - int32 mod = (int32)floor(K* (1.0f - chance)); - itr->ModifyPersonalRating(plr, mod, GetSlot()); - // update personal stats - itr->games_week +=1; - itr->games_season +=1; - itr->wins_season += 1; - itr->wins_week += 1; - // update unit fields - plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->games_week); - plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->games_season); - return; - } - } -} - -void ArenaTeam::UpdateArenaPointsHelper(std::map& PlayerPoints) -{ - // called after a match has ended and the stats are already modified - // helper function for arena point distribution (this way, when distributing, no actual calculation is required, just a few comparisons) - // 10 played games per week is a minimum - if (m_stats.games_week < 10) - return; - // to get points, a player has to participate in at least 30% of the matches - uint32 min_plays = (uint32) ceil(m_stats.games_week * 0.3); - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - // the player participated in enough games, update his points - uint32 points_to_add = 0; - if (itr->games_week >= min_plays) - points_to_add = GetPoints(itr->personal_rating); - // OBSOLETE : CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", points_to_add, m_TeamId, itr->guid); - - std::map::iterator plr_itr = PlayerPoints.find(GUID_LOPART(itr->guid)); - if (plr_itr != PlayerPoints.end()) - { - //check if there is already more points - if (plr_itr->second < points_to_add) - PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add; - } - else - PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add; - } -} - -void ArenaTeam::SaveToDB() -{ - // save team and member stats to db - // called after a match has ended, or when calculating arena_points - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games = '%u',played = '%u',rank = '%u',wins = '%u',wins2 = '%u' WHERE arenateamid = '%u'", m_stats.rating, m_stats.games_week, m_stats.games_season, m_stats.rank, m_stats.wins_week, m_stats.wins_season, GetId()); - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, m_TeamId, GUID_LOPART(itr->guid)); - } - CharacterDatabase.CommitTransaction(); -} - -void ArenaTeam::FinishWeek() -{ - m_stats.games_week = 0; // played this week - m_stats.wins_week = 0; // wins this week - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - itr->games_week = 0; - itr->wins_week = 0; - } -} - -bool ArenaTeam::IsFighting() const -{ - for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (Player *p = objmgr.GetPlayer(itr->guid)) - if (p->GetMap()->IsBattleArena()) - return true; - return false; -} diff --git a/src/server/game/ArenaTeam.h b/src/server/game/ArenaTeam.h deleted file mode 100644 index e6f85cf8d9e..00000000000 --- a/src/server/game/ArenaTeam.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITYCORE_ARENATEAM_H -#define TRINITYCORE_ARENATEAM_H - -enum ArenaTeamCommandTypes -{ - ERR_ARENA_TEAM_CREATE_S = 0x00, - ERR_ARENA_TEAM_INVITE_SS = 0x01, - ERR_ARENA_TEAM_QUIT_S = 0x03, - ERR_ARENA_TEAM_FOUNDER_S = 0x0E -}; - -enum ArenaTeamCommandErrors -{ - ERR_ARENA_TEAM_INTERNAL = 0x01, - ERR_ALREADY_IN_ARENA_TEAM = 0x02, - ERR_ALREADY_IN_ARENA_TEAM_S = 0x03, - ERR_INVITED_TO_ARENA_TEAM = 0x04, - ERR_ALREADY_INVITED_TO_ARENA_TEAM_S = 0x05, - ERR_ARENA_TEAM_NAME_INVALID = 0x06, - ERR_ARENA_TEAM_NAME_EXISTS_S = 0x07, - ERR_ARENA_TEAM_LEADER_LEAVE_S = 0x08, - ERR_ARENA_TEAM_PERMISSIONS = 0x08, - ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM = 0x09, - ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS = 0x0A, - ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S = 0x0B, - ERR_ARENA_TEAM_NOT_ALLIED = 0x0C, - ERR_ARENA_TEAM_IGNORING_YOU_S = 0x13, - ERR_ARENA_TEAM_TARGET_TOO_LOW_S = 0x15, - ERR_ARENA_TEAM_TARGET_TOO_HIGH_S = 0x16, - ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S = 0x17, - ERR_ARENA_TEAM_NOT_FOUND = 0x1B, - ERR_ARENA_TEAMS_LOCKED = 0x1E -}; - -enum ArenaTeamEvents -{ - ERR_ARENA_TEAM_JOIN_SS = 3, // player name + arena team name - ERR_ARENA_TEAM_LEAVE_SS = 4, // player name + arena team name - ERR_ARENA_TEAM_REMOVE_SSS = 5, // player name + arena team name + captain name - ERR_ARENA_TEAM_LEADER_IS_SS = 6, // player name + arena team name - ERR_ARENA_TEAM_LEADER_CHANGED_SSS = 7, // old captain + new captain + arena team name - ERR_ARENA_TEAM_DISBANDED_S = 8 // captain name + arena team name -}; - -/* -need info how to send these ones: -ERR_ARENA_TEAM_YOU_JOIN_S - client show it automatically when accept invite -ERR_ARENA_TEAM_TARGET_TOO_LOW_S -ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S -ERR_ARENA_TEAM_LEVEL_TOO_LOW_I -*/ - -enum ArenaTeamStatTypes -{ - STAT_TYPE_RATING = 0, - STAT_TYPE_GAMES_WEEK = 1, - STAT_TYPE_WINS_WEEK = 2, - STAT_TYPE_GAMES_SEASON = 3, - STAT_TYPE_WINS_SEASON = 4, - STAT_TYPE_RANK = 5 -}; - -enum ArenaTeamTypes -{ - ARENA_TEAM_2v2 = 2, - ARENA_TEAM_3v3 = 3, - ARENA_TEAM_5v5 = 5 -}; - -struct ArenaTeamMember -{ - uint64 guid; - std::string name; - uint8 Class; - uint32 games_week; - uint32 wins_week; - uint32 games_season; - uint32 wins_season; - uint32 personal_rating; - - void ModifyPersonalRating(Player* plr, int32 mod, uint32 slot); -}; - -struct ArenaTeamStats -{ - uint32 rating; - uint32 games_week; - uint32 wins_week; - uint32 games_season; - uint32 wins_season; - uint32 rank; -}; - -#define MAX_ARENA_SLOT 3 // 0..2 slots - -class ArenaTeam -{ - public: - ArenaTeam(); - ~ArenaTeam(); - - bool Create(uint64 captainGuid, uint32 type, std::string ArenaTeamName); - void Disband(WorldSession *session); - - typedef std::list MemberList; - - uint32 GetId() const { return m_TeamId; } - uint32 GetType() const { return m_Type; } - uint8 GetSlot() const { return GetSlotByType(GetType()); } - static uint8 GetSlotByType(uint32 type); - const uint64& GetCaptain() const { return m_CaptainGuid; } - std::string GetName() const { return m_Name; } - const ArenaTeamStats& GetStats() const { return m_stats; } - void SetStats(uint32 stat_type, uint32 value); - uint32 GetRating() const { return m_stats.rating; } - - uint32 GetEmblemStyle() const { return m_EmblemStyle; } - uint32 GetEmblemColor() const { return m_EmblemColor; } - uint32 GetBorderStyle() const { return m_BorderStyle; } - uint32 GetBorderColor() const { return m_BorderColor; } - uint32 GetBackgroundColor() const { return m_BackgroundColor; } - - void SetCaptain(const uint64& guid); - bool AddMember(const uint64& PlayerGuid); - - // Shouldn't be const uint64& ed, because than can reference guid from members on Disband - // and this method removes given record from list. So invalid reference can happen. - void DelMember(uint64 guid); - - void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor); - - size_t GetMembersSize() const { return m_members.size(); } - bool Empty() const { return m_members.empty(); } - MemberList::iterator m_membersBegin() { return m_members.begin(); } - MemberList::iterator m_membersEnd() { return m_members.end(); } - bool HaveMember(const uint64& guid) const; - - ArenaTeamMember* GetMember(const uint64& guid) - { - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (itr->guid == guid) - return &(*itr); - - return NULL; - } - - ArenaTeamMember* GetMember(const std::string& name) - { - for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - if (itr->name == name) - return &(*itr); - - return NULL; - } - - bool IsFighting() const; - - bool LoadArenaTeamFromDB(QueryResult_AutoPtr arenaTeamDataResult); - bool LoadMembersFromDB(QueryResult_AutoPtr arenaTeamMembersResult); - void LoadStatsFromDB(uint32 ArenaTeamId); - - void SaveToDB(); - - void BroadcastPacket(WorldPacket *packet); - void BroadcastEvent(ArenaTeamEvents event, uint64 guid, uint8 strCount, std::string str1, std::string str2, std::string str3); - - void Roster(WorldSession *session); - void Query(WorldSession *session); - void Stats(WorldSession *session); - void InspectStats(WorldSession *session, uint64 guid); - - uint32 GetPoints(uint32 MemberRating); - float GetChanceAgainst(uint32 own_rating, uint32 enemy_rating); - int32 WonAgainst(uint32 againstRating); - void MemberWon(Player * plr, uint32 againstRating); - int32 LostAgainst(uint32 againstRating); - void MemberLost(Player * plr, uint32 againstRating); - void OfflineMemberLost(uint64 guid, uint32 againstRating); - - void UpdateArenaPointsHelper(std::map & PlayerPoints); - - void NotifyStatsChanged(); - - void FinishWeek(); - void FinishGame(int32 mod); - - protected: - - uint32 m_TeamId; - uint32 m_Type; - std::string m_Name; - uint64 m_CaptainGuid; - - uint32 m_BackgroundColor; // ARGB format - uint32 m_EmblemStyle; // icon id - uint32 m_EmblemColor; // ARGB format - uint32 m_BorderStyle; // border image id - uint32 m_BorderColor; // ARGB format - - MemberList m_members; - ArenaTeamStats m_stats; -}; -#endif - diff --git a/src/server/game/ArenaTeamHandler.cpp b/src/server/game/ArenaTeamHandler.cpp deleted file mode 100644 index 3a9a14524f9..00000000000 --- a/src/server/game/ArenaTeamHandler.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Player.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Database/DatabaseEnv.h" - -#include "ArenaTeam.h" -#include "Log.h" -#include "ObjectMgr.h" -#include "SocialMgr.h" - -void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("MSG_INSPECT_ARENA_TEAMS"); - - uint64 guid; - recv_data >> guid; - sLog.outDebug("Inspect Arena stats (GUID: %u TypeId: %u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); - - if (Player *plr = objmgr.GetPlayer(guid)) - { - for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) - { - if (uint32 a_id = plr->GetArenaTeamId(i)) - { - if (ArenaTeam *at = objmgr.GetArenaTeamById(a_id)) - at->InspectStats(this, plr->GetGUID()); - } - } - } -} - -void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("WORLD: Received CMSG_ARENA_TEAM_QUERY"); - - uint32 ArenaTeamId; - recv_data >> ArenaTeamId; - - if (ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId)) - { - arenateam->Query(this); - arenateam->Stats(this); - } -} - -void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("WORLD: Received CMSG_ARENA_TEAM_ROSTER"); - - uint32 ArenaTeamId; // arena team id - recv_data >> ArenaTeamId; - - if (ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId)) - arenateam->Roster(this); -} - -void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_INVITE"); - - uint32 ArenaTeamId; // arena team id - std::string Invitedname; - - Player * player = NULL; - - recv_data >> ArenaTeamId >> Invitedname; - - if (!Invitedname.empty()) - { - if (!normalizePlayerName(Invitedname)) - return; - - player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str()); - } - - if (!player) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); - return; - } - - if (player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S); - return; - } - - ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); - if (!arenateam) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM); - return; - } - - // OK result but not send invite - if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) - return; - - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); - return; - } - - if (player->GetArenaTeamId(arenateam->GetSlot())) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S); - return; - } - - if (player->GetArenaTeamIdInvited()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); - return; - } - - if (arenateam->GetMembersSize() >= arenateam->GetType() * 2) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, arenateam->GetName(), "", ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S); - return; - } - - sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str()); - - player->SetArenaTeamIdInvited(arenateam->GetId()); - - WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10)); - data << GetPlayer()->GetName(); - data << arenateam->GetName(); - player->GetSession()->SendPacket(&data); - - sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_INVITE"); -} - -void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("CMSG_ARENA_TEAM_ACCEPT"); // empty opcode - - ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited()); - if (!at) - return; - - if (_player->GetArenaTeamId(at->GetSlot())) - { - // already in arena team that size - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ALREADY_IN_ARENA_TEAM); - return; - } - - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain())) - { - // not let enemies sign petition - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED); - return; - } - - if (!at->AddMember(_player->GetGUID())) - { - // arena team not found - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_INTERNAL); - return; - } - - // event - at->BroadcastEvent(ERR_ARENA_TEAM_JOIN_SS, _player->GetGUID(), 2, _player->GetName(), at->GetName(), ""); -} - -void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("CMSG_ARENA_TEAM_DECLINE"); // empty opcode - - _player->SetArenaTeamIdInvited(0); // no more invited -} - -void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_LEAVE"); - - uint32 ArenaTeamId; // arena team id - recv_data >> ArenaTeamId; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if (!at) - return; - - if (_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1) - { - // check for correctness - SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); - return; - } - - // arena team has only one member (=captain) - if (_player->GetGUID() == at->GetCaptain()) - { - at->Disband(this); - delete at; - return; - } - - at->DelMember(_player->GetGUID()); - - // event - at->BroadcastEvent(ERR_ARENA_TEAM_LEAVE_SS, _player->GetGUID(), 2, _player->GetName(), at->GetName(), ""); - - // send you are no longer member of team - SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0); -} - -void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_DISBAND"); - - uint32 ArenaTeamId; // arena team id - recv_data >> ArenaTeamId; - - if (ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId)) - { - if (at->GetCaptain() != _player->GetGUID()) - return; - - if (at->IsFighting()) - return; - - at->Disband(this); - delete at; - } -} - -void WorldSession::HandleArenaTeamRemoveOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_REMOVE"); - - uint32 ArenaTeamId; - std::string name; - - recv_data >> ArenaTeamId; - recv_data >> name; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if (!at) // arena team not found - return; - - if (at->GetCaptain() != _player->GetGUID()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); - return; - } - - if (!normalizePlayerName(name)) - return; - - ArenaTeamMember* member = at->GetMember(name); - if (!member) // member not found - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); - return; - } - - if (at->GetCaptain() == member->guid) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); - return; - } - - at->DelMember(member->guid); - - // event - at->BroadcastEvent(ERR_ARENA_TEAM_REMOVE_SSS, 0, 3, name, at->GetName(), _player->GetName()); -} - -void WorldSession::HandleArenaTeamLeaderOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ARENA_TEAM_LEADER"); - - uint32 ArenaTeamId; - std::string name; - - recv_data >> ArenaTeamId; - recv_data >> name; - - ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); - if (!at) // arena team not found - return; - - if (at->GetCaptain() != _player->GetGUID()) - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); - return; - } - - if (!normalizePlayerName(name)) - return; - - ArenaTeamMember* member = at->GetMember(name); - if (!member) // member not found - { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); - return; - } - - if (at->GetCaptain() == member->guid) // target player already captain - return; - - at->SetCaptain(member->guid); - - // event - at->BroadcastEvent(ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 0, 3, _player->GetName(), name, at->GetName()); -} - -void WorldSession::SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id) -{ - WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4); - data << uint32(team_action); - data << team; - data << player; - data << uint32(error_id); - SendPacket(&data); -} - -void WorldSession::SendNotInArenaTeamPacket(uint8 type) -{ - WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team - uint32 unk = 0; - data << uint32(unk); // unk(0) - if (!unk) - data << uint8(type); // team type (2=2v2,3=3v3,5=5v5), can be used for custom types... - SendPacket(&data); -} - -/* -+ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team" - -+ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]." -+ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s" -+ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s" -ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]." - -+ERR_ARENA_TEAM_INTERNAL "Internal arena team error" -+ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size" -+ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size" -+ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team" -+ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team" -+ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name" -+ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\"" -+ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team" -+ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that" -+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size" -+ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s" -+ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found" -+ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance" - -+ERR_ARENA_TEAM_JOIN_SS "%s has joined %s" -+ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]." - -+ERR_ARENA_TEAM_LEAVE_SS "%s has left %s" - -+ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s" -+ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s" - -+ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s" - -+ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s" - -ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team" - -ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full" - -ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team" -*/ diff --git a/src/server/game/AuctionHouse/AuctionHouseHandler.cpp b/src/server/game/AuctionHouse/AuctionHouseHandler.cpp new file mode 100644 index 00000000000..8fec3b7df1d --- /dev/null +++ b/src/server/game/AuctionHouse/AuctionHouseHandler.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectMgr.h" +#include "Player.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +#include "AuctionHouseBot.h" +#include "AuctionHouseMgr.h" +#include "Log.h" +#include "Opcodes.h" +#include "UpdateMask.h" +#include "Util.h" + +//please DO NOT use iterator++, because it is slower than ++iterator!!! +//post-incrementation is always slower than pre-incrementation ! + +//void called when player click on auctioneer npc +void WorldSession::HandleAuctionHelloOpcode(WorldPacket & recv_data) +{ + uint64 guid; //NPC guid + recv_data >> guid; + + Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!unit) + { + sLog.outDebug("WORLD: HandleAuctionHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + SendAuctionHello(guid, unit); +} + +//this void causes that auction window is opened +void WorldSession::SendAuctionHello(uint64 guid, Creature* unit) +{ + if (GetPlayer()->getLevel() < sWorld.getConfig(CONFIG_AUCTION_LEVEL_REQ)) + { + SendNotification(GetTrinityString(LANG_AUCTION_REQ), sWorld.getConfig(CONFIG_AUCTION_LEVEL_REQ)); + return; + } + + AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->getFaction()); + if (!ahEntry) + return; + + WorldPacket data(MSG_AUCTION_HELLO, 12); + data << uint64(guid); + data << uint32(ahEntry->houseId); + data << uint8(1); // 3.3.3: 1 - AH enabled, 0 - AH disabled + SendPacket(&data); +} + +//call this method when player bids, creates, or deletes auction +void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError) +{ + WorldPacket data(SMSG_AUCTION_COMMAND_RESULT, 16); + data << auctionId; + data << Action; + data << ErrorCode; + if (!ErrorCode && Action) + data << bidError; //when bid, then send 0, once... + SendPacket(&data); +} + +//this function sends notification, if bidder is online +void WorldSession::SendAuctionBidderNotification(uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template) +{ + WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4)); + data << uint32(location); + data << uint32(auctionId); + data << uint64(bidder); + data << uint32(bidSum); + data << uint32(diff); + data << uint32(item_template); + data << uint32(0); + SendPacket(&data); +} + +//this void causes on client to display: "Your auction sold" +void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction) +{ + WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (7*4)); + data << auction->Id; + data << auction->bid; + data << (uint32) 0; //unk + data << (uint32) 0; //unk + data << (uint32) 0; //unk + data << auction->item_template; + data << (uint32) 0; //unk + SendPacket(&data); +} + +//this function sends mail to old bidder +void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice) +{ + uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder,0, HIGHGUID_PLAYER); + Player *oldBidder = objmgr.GetPlayer(oldBidder_guid); + + uint32 oldBidder_accId = 0; + if (!oldBidder) + oldBidder_accId = objmgr.GetPlayerAccountIdByGUID(oldBidder_guid); + + // old bidder exist + if (oldBidder || oldBidder_accId) + { + std::ostringstream msgAuctionOutbiddedSubject; + msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED << ":0:0"; + + if (oldBidder && !_player) + oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, auctionbot.GetAHBplayerGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); + + if (oldBidder && _player) + oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); + + MailDraft(msgAuctionOutbiddedSubject.str(), "") // TODO: fix body + .AddMoney(auction->bid) + .SendMailTo(MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED); + } +} + +//this function sends mail, when auction is cancelled to old bidder +void WorldSession::SendAuctionCancelledToBidderMail(AuctionEntry* auction) +{ + uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); + Player *bidder = objmgr.GetPlayer(bidder_guid); + + uint32 bidder_accId = 0; + if (!bidder) + bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid); + + // bidder exist + if (bidder || bidder_accId) + { + std::ostringstream msgAuctionCancelledSubject; + msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0"; + + MailDraft(msgAuctionCancelledSubject.str(), "") // TODO: fix body + .AddMoney(auction->bid) + .SendMailTo(MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED); + } +} + +//this void creates new auction and adds auction to some auctionhouse +void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data) +{ + uint64 auctioneer, item; + uint32 etime, bid, buyout; + recv_data >> auctioneer; + recv_data.read_skip(); // const 1? + recv_data >> item; + recv_data.read_skip(); // unk 3.2.2, const 1? + recv_data >> bid; + recv_data >> buyout; + recv_data >> etime; + + Player *pl = GetPlayer(); + + if (!item || !bid || !etime) + return; //check for cheaters + + Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); + return; + } + + AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(pCreature->getFaction()); + if (!auctionHouseEntry) + { + sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer))); + return; + } + + sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime); + + // client send time in minutes, convert to common used sec time + etime *= MINUTE; + + sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime); + + // client understand only 3 auction time + switch(etime) + { + case 1*MIN_AUCTION_TIME: + case 2*MIN_AUCTION_TIME: + case 4*MIN_AUCTION_TIME: + break; + default: + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + Item *it = pl->GetItemByGuid(item); + //do not allow to sell already auctioned items + if (auctionmgr.GetAItem(GUID_LOPART(item))) + { + sLog.outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", pl->GetName(), GUID_LOPART(item)); + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); + return; + } + // prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction) + if (!it) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND); + return; + } + + if (!it->CanBeTraded()) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); + return; + } + + if (it->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); + return; + } + + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); + + //we have to take deposit : + uint32 deposit = auctionmgr.GetAuctionDeposit(auctionHouseEntry, etime, it); + if (pl->GetMoney() < deposit) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY); + return; + } + + if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", + GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount()); + } + + pl->ModifyMoney(-int32(deposit)); + + uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME)); + + AuctionEntry *AH = new AuctionEntry; + AH->Id = objmgr.GenerateAuctionID(); + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + AH->auctioneer = 23442; + else + AH->auctioneer = GUID_LOPART(auctioneer); + AH->item_guidlow = GUID_LOPART(item); + AH->item_template = it->GetEntry(); + AH->owner = pl->GetGUIDLow(); + AH->startbid = bid; + AH->bidder = 0; + AH->bid = 0; + AH->buyout = buyout; + AH->expire_time = time(NULL) + auction_time; + AH->deposit = deposit; + AH->auctionHouseEntry = auctionHouseEntry; + + sLog.outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", GUID_LOPART(item), AH->auctioneer, bid, buyout, auction_time, AH->GetHouseId()); + auctionmgr.AddAItem(it); + auctionHouse->AddAuction(AH); + + pl->MoveItemFromInventory(it->GetBagSlot(), it->GetSlot(), true); + + CharacterDatabase.BeginTransaction(); + it->DeleteFromInventoryDB(); + it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone + AH->SaveToDB(); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + + SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK); + + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); +} + +//this function is called when client bids or buys out auction +void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data) +{ + uint64 auctioneer; + uint32 auctionId; + uint32 price; + recv_data >> auctioneer; + recv_data >> auctionId >> price; + + if (!auctionId || !price) + return; //check for cheaters + + Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + AuctionHouseObject *auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); + + AuctionEntry *auction = auctionHouse->GetAuction(auctionId); + Player *pl = GetPlayer(); + + if (!auction || auction->owner == pl->GetGUIDLow()) + { + //you cannot bid your own auction: + SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); + return; + } + + // impossible have online own another character (use this for speedup check in case online owner) + Player* auction_owner = objmgr.GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)); + if (!auction_owner && objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->GetSession()->GetAccountId()) + { + //you cannot bid your another character auction: + SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); + return; + } + + // cheating + if (price <= auction->bid || price < auction->startbid) + return; + + // price too low for next bid if not buyout + if ((price < auction->buyout || auction->buyout == 0) && + price < auction->bid + auction->GetAuctionOutBid()) + { + //auction has already higher bid, client tests it! + return; + } + + if (price > pl->GetMoney()) + { + //you don't have enought money!, client tests! + //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); + return; + } + + if (price < auction->buyout || auction->buyout == 0) + { + if (auction->bidder > 0) + { + if (auction->bidder == pl->GetGUIDLow()) + pl->ModifyMoney(-int32(price - auction->bid)); + else + { + // mail to last bidder and return money + SendAuctionOutbiddedMail(auction, price); + pl->ModifyMoney(-int32(price)); + } + } + else + pl->ModifyMoney(-int32(price)); + + auction->bidder = pl->GetGUIDLow(); + auction->bid = price; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); + + // after this update we should save player's money ... + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); + + SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0); + } + else + { + //buyout: + if (pl->GetGUIDLow() == auction->bidder) + pl->ModifyMoney(-int32(auction->buyout - auction->bid)); + else + { + pl->ModifyMoney(-int32(auction->buyout)); + if (auction->bidder) //buyout for bidded auction .. + SendAuctionOutbiddedMail(auction, auction->buyout); + } + auction->bidder = pl->GetGUIDLow(); + auction->bid = auction->buyout; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); + + auctionmgr.SendAuctionSalePendingMail(auction); + auctionmgr.SendAuctionSuccessfulMail(auction); + auctionmgr.SendAuctionWonMail(auction); + + SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK); + + CharacterDatabase.BeginTransaction(); + auction->DeleteFromDB(); + uint32 item_template = auction->item_template; + auctionmgr.RemoveAItem(auction->item_guidlow); + auctionHouse->RemoveAuction(auction, item_template); + } + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); +} + +//this void is called when auction_owner cancels his auction +void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data) +{ + uint64 auctioneer; + uint32 auctionId; + recv_data >> auctioneer; + recv_data >> auctionId; + //sLog.outDebug("Cancel AUCTION AuctionID: %u", auctionId); + + Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); + + AuctionEntry *auction = auctionHouse->GetAuction(auctionId); + Player *pl = GetPlayer(); + + if (auction && auction->owner == pl->GetGUIDLow()) + { + Item *pItem = auctionmgr.GetAItem(auction->item_guidlow); + if (pItem) + { + if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid + { + uint32 auctionCut = auction->GetAuctionCut(); + if (pl->GetMoney() < auctionCut) //player doesn't have enough money, maybe message needed + return; + //some auctionBidderNotification would be needed, but don't know that parts.. + SendAuctionCancelledToBidderMail(auction); + pl->ModifyMoney(-int32(auctionCut)); + } + // Return the item by mail + std::ostringstream msgAuctionCanceledOwner; + msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0"; + + // item will deleted or added to received mail list + MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body + .AddItem(pItem) + .SendMailTo(pl, auction, MAIL_CHECK_MASK_COPIED); + } + else + { + sLog.outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow); + SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR); + return; + } + } + else + { + SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR); + //this code isn't possible ... maybe there should be assert + sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId); + return; + } + + //inform player, that auction is removed + SendAuctionCommandResult(auction->Id, AUCTION_CANCEL, AUCTION_OK); + + // Now remove the auction + CharacterDatabase.BeginTransaction(); + pl->SaveInventoryAndGoldToDB(); + auction->DeleteFromDB(); + uint32 item_template = auction->item_template; + auctionmgr.RemoveAItem(auction->item_guidlow); + auctionHouse->RemoveAuction(auction, item_template); + CharacterDatabase.CommitTransaction(); +} + +//called when player lists his bids +void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data) +{ + uint64 guid; //NPC guid + uint32 listfrom; //page of auctions + uint32 outbiddedCount; //count of outbidded auctions + + recv_data >> guid; + recv_data >> listfrom; // not used in fact (this list not have page control in client) + recv_data >> outbiddedCount; + if (recv_data.size() != (16 + outbiddedCount * 4)) + { + sLog.outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(),(16 + outbiddedCount * 4)); + outbiddedCount = 0; + } + + Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandleAuctionListBidderItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); + + WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4)); + Player *pl = GetPlayer(); + data << (uint32) 0; //add 0 as count + uint32 count = 0; + uint32 totalcount = 0; + while (outbiddedCount > 0) //add all data, which client requires + { + --outbiddedCount; + uint32 outbiddedAuctionId; + recv_data >> outbiddedAuctionId; + AuctionEntry * auction = auctionHouse->GetAuction(outbiddedAuctionId); + if (auction && auction->BuildAuctionInfo(data)) + { + ++totalcount; + ++count; + } + } + + auctionHouse->BuildListBidderItems(data,pl,count,totalcount); + data.put(0, count); // add count to placeholder + data << totalcount; + data << (uint32)300; //unk 2.3.0 + SendPacket(&data); +} + +//this void sends player info about his auctions +void WorldSession::HandleAuctionListOwnerItems(WorldPacket & recv_data) +{ + uint32 listfrom; + uint64 guid; + + recv_data >> guid; + recv_data >> listfrom; // not used in fact (this list not have page control in client) + + Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandleAuctionListOwnerItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); + + WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4)); + data << (uint32) 0; // amount place holder + + uint32 count = 0; + uint32 totalcount = 0; + + auctionHouse->BuildListOwnerItems(data,_player,count,totalcount); + data.put(0, count); + data << (uint32) totalcount; + data << (uint32) 0; + SendPacket(&data); +} + +//this void is called when player clicks on search button +void WorldSession::HandleAuctionListItems(WorldPacket & recv_data) +{ + std::string searchedname; + uint8 levelmin, levelmax, usable; + uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality; + uint64 guid; + + recv_data >> guid; + recv_data >> listfrom; // start, used for page control listing by 50 elements + recv_data >> searchedname; + + recv_data >> levelmin >> levelmax; + recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; + recv_data >> quality >> usable; + + recv_data.read_skip(16); // unknown 16 bytes: 00 07 01 00 00 01 05 00 06 00 09 01 08 00 03 00 + + Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandleAuctionListItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); + return; + } + + // remove fake death + if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + + AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); + + //sLog.outDebug("Auctionhouse search (GUID: %u TypeId: %u)", , list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", + // GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable); + + WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4+4+4)); + uint32 count = 0; + uint32 totalcount = 0; + data << (uint32) 0; + + // converting string that we try to find to lower case + std::wstring wsearchedname; + if (!Utf8toWStr(searchedname,wsearchedname)) + return; + + wstrToLower(wsearchedname); + + auctionHouse->BuildListAuctionItems(data,_player, + wsearchedname, listfrom, levelmin, levelmax, usable, + auctionSlotID, auctionMainCategory, auctionSubCategory, quality, + count,totalcount); + + data.put(0, count); + data << (uint32) totalcount; + data << (uint32) 300; // unk 2.3.0 const? + SendPacket(&data); +} + +void WorldSession::HandleAuctionListPendingSales(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_AUCTION_LIST_PENDING_SALES"); + + recv_data.read_skip(); + + uint32 count = 0; + + WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4); + data << uint32(count); // count + /*for (uint32 i = 0; i < count; ++i) + { + data << ""; // string + data << ""; // string + data << uint32(0); + data << uint32(0); + data << float(0); + }*/ + SendPacket(&data); +} diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp new file mode 100644 index 00000000000..ddd44bbefa2 --- /dev/null +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -0,0 +1,750 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Database/DatabaseEnv.h" +#include "Database/SQLStorage.h" +#include "Policies/SingletonImp.h" +#include "DBCStores.h" + +#include "AccountMgr.h" +#include "AuctionHouseMgr.h" +#include "Item.h" +#include "Language.h" +#include "Log.h" +#include "ProgressBar.h" +#include + +INSTANTIATE_SINGLETON_1(AuctionHouseMgr); + +using namespace std; + +AuctionHouseMgr::AuctionHouseMgr() +{ +} + +AuctionHouseMgr::~AuctionHouseMgr() +{ + for (ItemMap::const_iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr) + delete itr->second; +} + +AuctionHouseObject * AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId) +{ + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + return &mNeutralAuctions; + + // team have linked auction houses + FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); + if (!u_entry) + return &mNeutralAuctions; + else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) + return &mAllianceAuctions; + else if (u_entry->ourMask & FACTION_MASK_HORDE) + return &mHordeAuctions; + else + return &mNeutralAuctions; +} + +uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem) +{ + uint32 MSV = pItem->GetProto()->SellPrice; + int32 deposit; + uint32 timeHr = (((time / 60) / 60) / 12); + + if (MSV > 0) + deposit = (int32)floor((double)MSV * (((double)(entry->depositPercent * 3) / 100.0f * (double)sWorld.getRate(RATE_AUCTION_DEPOSIT) * (double)pItem->GetCount()))) * timeHr; + else + deposit = 0; + + sLog.outDebug("Sellprice: %u / Depositpercent: %f / AT1: %u / AT2: %u / AT3: %u / Count: %u", MSV, ((double)entry->depositPercent / 100.0f), time, MIN_AUCTION_TIME, timeHr, pItem->GetCount() ); + if (deposit > 0) + { + sLog.outDebug("Deposit: %u", deposit); + return deposit; + } + else + { + sLog.outDebug("Deposit: 0"); + return 0; + } +} + +//does not clear ram +void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction) +{ + Item *pItem = GetAItem(auction->item_guidlow); + if (!pItem) + return; + + uint32 bidder_accId = 0; + uint32 bidder_security = 0; + uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); + Player *bidder = objmgr.GetPlayer(bidder_guid); + // data for gm.log + if (sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + std::string bidder_name; + if (bidder) + { + bidder_accId = bidder->GetSession()->GetAccountId(); + bidder_security = bidder->GetSession()->GetSecurity(); + bidder_name = bidder->GetName(); + } + else + { + bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid); + bidder_security = accmgr.GetSecurity(bidder_accId); + + if (bidder_security > SEC_PLAYER) // not do redundant DB requests + { + if (!objmgr.GetPlayerNameByGUID(bidder_guid,bidder_name)) + bidder_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN); + } + } + if (bidder_security > SEC_PLAYER) + { + std::string owner_name; + if (!objmgr.GetPlayerNameByGUID(auction->owner,owner_name)) + owner_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN); + + uint32 owner_accid = objmgr.GetPlayerAccountIdByGUID(auction->owner); + + sLog.outCommand(bidder_accId,"GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)", + bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid); + } + } + + // receiver exist + if (bidder || bidder_accId) + { + std::ostringstream msgAuctionWonSubject; + msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON; + + std::ostringstream msgAuctionWonBody; + msgAuctionWonBody.width(16); + msgAuctionWonBody << std::right << std::hex << auction->owner; + msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout; + sLog.outDebug("AuctionWon body string : %s", msgAuctionWonBody.str().c_str()); + + // set owner to bidder (to prevent delete item with sender char deleting) + // owner in `data` will set at mail receive and item extracting + CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow()); + + if (bidder) + { + bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template); + // FIXME: for offline player need also + bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); + } + + MailDraft(msgAuctionWonSubject.str(), msgAuctionWonBody.str()) + .AddItem(pItem) + .SendMailTo(MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_COPIED); + } +} + +void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction) +{ + uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER); + Player *owner = objmgr.GetPlayer(owner_guid); + uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid); + // owner exist (online or offline) + if (owner || owner_accId) + { + std::ostringstream msgAuctionSalePendingSubject; + msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING; + + std::ostringstream msgAuctionSalePendingBody; + uint32 auctionCut = auction->GetAuctionCut(); + + time_t distrTime = time(NULL) + sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY); + + msgAuctionSalePendingBody.width(16); + msgAuctionSalePendingBody << std::right << std::hex << auction->bidder; + msgAuctionSalePendingBody << std::dec << ":" << auction->bid << ":" << auction->buyout; + msgAuctionSalePendingBody << ":" << auction->deposit << ":" << auctionCut << ":0:"; + msgAuctionSalePendingBody << secsToTimeBitFields(distrTime); + + sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str()); + + MailDraft(msgAuctionSalePendingSubject.str(), msgAuctionSalePendingBody.str()) + .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED); + } +} + +//call this method to send mail to auction owner, when auction is successful, it does not clear ram +void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction) +{ + uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER); + Player *owner = objmgr.GetPlayer(owner_guid); + uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid); + // owner exist + if (owner || owner_accId) + { + std::ostringstream msgAuctionSuccessfulSubject; + msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL; + + std::ostringstream auctionSuccessfulBody; + uint32 auctionCut = auction->GetAuctionCut(); + + auctionSuccessfulBody.width(16); + auctionSuccessfulBody << std::right << std::hex << auction->bidder; + auctionSuccessfulBody << std::dec << ":" << auction->bid << ":" << auction->buyout; + auctionSuccessfulBody << ":" << auction->deposit << ":" << auctionCut; + + sLog.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody.str().c_str()); + + uint32 profit = auction->bid + auction->deposit - auctionCut; + + //FIXME: what do if owner offline + if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID()) + { + owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS, profit); + owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid); + //send auction owner notification, bidder must be current! + owner->GetSession()->SendAuctionOwnerNotification(auction); + } + MailDraft(msgAuctionSuccessfulSubject.str(), auctionSuccessfulBody.str()) + .AddMoney(profit) + .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED, sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY)); + } +} + +//does not clear ram +void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction) +{ //return an item in auction to its owner by mail + Item *pItem = GetAItem(auction->item_guidlow); + if (!pItem) + return; + + uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER); + Player *owner = objmgr.GetPlayer(owner_guid); + uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid); + // owner exist + if (owner || owner_accId) + { + std::ostringstream subject; + subject << auction->item_template << ":0:" << AUCTION_EXPIRED << ":0:0"; + + if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID()) + owner->GetSession()->SendAuctionOwnerNotification(auction); + + MailDraft(subject.str(), "") // TODO: fix body + .AddItem(pItem) + .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED); + } +} + +void AuctionHouseMgr::LoadAuctionItems() +{ + // data needs to be at first place for Item::LoadFromDB + QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT data, text, itemguid, item_template FROM auctionhouse JOIN item_instance ON itemguid = guid"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 auction items"); + return; + } + + barGoLink bar(result->GetRowCount()); + + uint32 count = 0; + + Field *fields; + do + { + bar.step(); + + fields = result->Fetch(); + uint32 item_guid = fields[2].GetUInt32(); + uint32 item_template = fields[3].GetUInt32(); + + ItemPrototype const *proto = objmgr.GetItemPrototype(item_template); + + if (!proto) + { + sLog.outError("AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template); + continue; + } + + Item *item = NewItemOrBag(proto); + + if (!item->LoadFromDB(item_guid,0, result)) + { + delete item; + continue; + } + AddAItem(item); + + ++count; + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u auction items", count); +} + +void AuctionHouseMgr::LoadAuctions() +{ + QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse"); + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); + return; + } + + Field *fields = result->Fetch(); + uint32 AuctionCount=fields[0].GetUInt32(); + + if (!AuctionCount) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); + return; + } + + result = CharacterDatabase.Query("SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit FROM auctionhouse"); + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); + return; + } + + barGoLink bar(AuctionCount); + + AuctionEntry *aItem; + + do + { + fields = result->Fetch(); + + bar.step(); + + aItem = new AuctionEntry; + aItem->Id = fields[0].GetUInt32(); + aItem->auctioneer = fields[1].GetUInt32(); + aItem->item_guidlow = fields[2].GetUInt32(); + aItem->item_template = fields[3].GetUInt32(); + aItem->owner = fields[4].GetUInt32(); + aItem->buyout = fields[5].GetUInt32(); + aItem->expire_time = fields[6].GetUInt32(); + aItem->bidder = fields[7].GetUInt32(); + aItem->bid = fields[8].GetUInt32(); + aItem->startbid = fields[9].GetUInt32(); + aItem->deposit = fields[10].GetUInt32(); + + CreatureData const* auctioneerData = objmgr.GetCreatureData(aItem->auctioneer); + if (!auctioneerData) + { + aItem->DeleteFromDB(); + sLog.outError("Auction %u has not a existing auctioneer (GUID : %u)", aItem->Id, aItem->auctioneer); + delete aItem; + continue; + } + + CreatureInfo const* auctioneerInfo = objmgr.GetCreatureTemplate(auctioneerData->id); + if (!auctioneerInfo) + { + aItem->DeleteFromDB(); + sLog.outError("Auction %u has not a existing auctioneer (GUID : %u Entry: %u)", aItem->Id, aItem->auctioneer,auctioneerData->id); + delete aItem; + continue; + } + + aItem->auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(auctioneerInfo->faction_A); + if (!aItem->auctionHouseEntry) + { + aItem->DeleteFromDB(); + sLog.outError("Auction %u has auctioneer (GUID : %u Entry: %u) with wrong faction %u", + aItem->Id, aItem->auctioneer,auctioneerData->id,auctioneerInfo->faction_A); + delete aItem; + continue; + } + + // check if sold item exists for guid + // and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems) + if (!GetAItem(aItem->item_guidlow)) + { + aItem->DeleteFromDB(); + sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow); + delete aItem; + continue; + } + + GetAuctionsMap(auctioneerInfo->faction_A)->AddAuction(aItem); + + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u auctions", AuctionCount); +} + +void AuctionHouseMgr::AddAItem(Item* it) +{ + ASSERT(it); + ASSERT(mAitems.find(it->GetGUIDLow()) == mAitems.end()); + mAitems[it->GetGUIDLow()] = it; +} + +bool AuctionHouseMgr::RemoveAItem(uint32 id) +{ + ItemMap::iterator i = mAitems.find(id); + if (i == mAitems.end()) + return false; + + mAitems.erase(i); + return true; +} + +void AuctionHouseMgr::Update() +{ + mHordeAuctions.Update(); + mAllianceAuctions.Update(); + mNeutralAuctions.Update(); +} + +AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId) +{ + uint32 houseid = 7; // goblin auction house + + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + { + //FIXME: found way for proper auctionhouse selection by another way + // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races. + // but no easy way convert creature faction to player race faction for specific city + switch(factionTemplateId) + { + case 12: houseid = 1; break; // human + case 29: houseid = 6; break; // orc, and generic for horde + case 55: houseid = 2; break; // dwarf, and generic for alliance + case 68: houseid = 4; break; // undead + case 80: houseid = 3; break; // n-elf + case 104: houseid = 5; break; // trolls + case 120: houseid = 7; break; // booty bay, neutral + case 474: houseid = 7; break; // gadgetzan, neutral + case 855: houseid = 7; break; // everlook, neutral + case 1604: houseid = 6; break; // b-elfs, + default: // for unknown case + { + FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); + if (!u_entry) + houseid = 7; // goblin auction house + else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) + houseid = 1; // human auction house + else if (u_entry->ourMask & FACTION_MASK_HORDE) + houseid = 6; // orc auction house + else + houseid = 7; // goblin auction house + break; + } + } + } + + return sAuctionHouseStore.LookupEntry(houseid); +} + void AuctionHouseObject::AddAuction(AuctionEntry *ah) + { + ASSERT(ah); + AuctionsMap[ah->Id] = ah; + auctionbot.IncrementItemCounts(ah); + } + + bool AuctionHouseObject::RemoveAuction(AuctionEntry *auction, uint32 item_template) + { + auctionbot.DecrementItemCounts(auction, item_template); + return AuctionsMap.erase(auction->Id) ? true : false; + } + +void AuctionHouseObject::Update() +{ + time_t curTime = sWorld.GetGameTime(); + ///- Handle expired auctions + + // If storage is empty, no need to update. next == NULL in this case. + if (AuctionsMap.empty()) + return; + + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT id FROM auctionhouse WHERE time <= %u ORDER BY TIME ASC", (uint32)curTime+60); + + if (!result) + return; + + if (result->GetRowCount() == 0) + return; + + vector expiredAuctions; + + do + { + uint32 tmpdata = result->Fetch()->GetUInt32(); + expiredAuctions.push_back(tmpdata); + } while (result->NextRow()); + + while (!expiredAuctions.empty()) + { + vector::iterator iter = expiredAuctions.begin(); + + // from auctionhousehandler.cpp, creates auction pointer & player pointer + AuctionEntry* auction = GetAuction(*iter); + + // Erase the auction from the vector. + expiredAuctions.erase(iter); + + if (!auction) + continue; + + ///- Either cancel the auction if there was no bidder + if (auction->bidder == 0) + auctionmgr.SendAuctionExpiredMail(auction); + ///- Or perform the transaction + else + { + //we should send an "item sold" message if the seller is online + //we send the item to the winner + //we send the money to the seller + auctionmgr.SendAuctionSuccessfulMail(auction); + auctionmgr.SendAuctionWonMail(auction); + } + + ///- In any case clear the auction + CharacterDatabase.BeginTransaction(); + auction->DeleteFromDB(); + uint32 item_template = auction->item_template; + auctionmgr.RemoveAItem(auction->item_guidlow); + RemoveAuction(auction, item_template); + CharacterDatabase.CommitTransaction(); + } +} + +void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) +{ + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + { + AuctionEntry *Aentry = itr->second; + if (Aentry && Aentry->bidder == player->GetGUIDLow()) + { + if (itr->second->BuildAuctionInfo(data)) + ++count; + + ++totalcount; + } + } +} + +void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) +{ + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + { + AuctionEntry *Aentry = itr->second; + if (Aentry && Aentry->owner == player->GetGUIDLow()) + { + if (Aentry->BuildAuctionInfo(data)) + ++count; + + ++totalcount; + } + } +} + +void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player, + std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, + uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, + uint32& count, uint32& totalcount) +{ + int loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); + int locdbc_idx = player->GetSession()->GetSessionDbcLocale(); + + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + { + AuctionEntry *Aentry = itr->second; + Item *item = auctionmgr.GetAItem(Aentry->item_guidlow); + if (!item) + continue; + + ItemPrototype const *proto = item->GetProto(); + + if (itemClass != 0xffffffff && proto->Class != itemClass) + continue; + + if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass) + continue; + + if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType) + continue; + + if (quality != 0xffffffff && proto->Quality != quality) + continue; + + if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax))) + continue; + + if (usable != 0x00 && player->CanUseItem(item) != EQUIP_ERR_OK) + continue; + + // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) + // No need to do any of this if no search term was entered + if (!wsearchedname.empty()) + { + std::string name = proto->Name1; + if (name.empty()) + continue; + + // local name + if (loc_idx >= 0) + { + ItemLocale const *il = objmgr.GetItemLocale(proto->ItemId); + if (il) + { + if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty()) + name = il->Name[loc_idx]; + } + } + + // DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result + // that matches the search but it may not equal item->GetItemRandomPropertyId() + // used in BuildAuctionInfo() which then causes wrong items to be listed + int32 propRefID = item->GetItemRandomPropertyId(); + + if (propRefID) + { + // Append the suffix to the name (ie: of the Monkey) if one exists + // These are found in ItemRandomProperties.dbc, not ItemRandomSuffix.dbc + // even though the DBC names seem misleading + const ItemRandomPropertiesEntry *itemRandProp = sItemRandomPropertiesStore.LookupEntry(propRefID); + + if (itemRandProp) + { + char* const* temp = itemRandProp->nameSuffix; + //char* temp = itemRandProp->nameSuffix; + + // dbc local name + if (temp) + { + if (locdbc_idx >= 0) + { + // Append the suffix (ie: of the Monkey) to the name using localization + name += " "; + name += temp[locdbc_idx]; + } + else + { + // Invalid localization? Append the suffix using default enUS + name += " "; + name += temp[LOCALE_enUS]; + } + } + } + } + + // Perform the search (with or without suffix) + if (!Utf8FitTo(name, wsearchedname)) + continue; + } + + // Add the item if no search term or if entered search term was found + if (count < 50 && totalcount >= listfrom) + { + ++count; + Aentry->BuildAuctionInfo(data); + } + ++totalcount; + } +} + +//this function inserts to WorldPacket auction's data +bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const +{ + Item *pItem = auctionmgr.GetAItem(item_guidlow); + if (!pItem) + { + sLog.outError("auction to item, that doesn't exist !!!!"); + return false; + } + data << uint32(Id); + data << uint32(pItem->GetEntry()); + + for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i) + { + data << uint32(pItem->GetEnchantmentId(EnchantmentSlot(i))); + data << uint32(pItem->GetEnchantmentDuration(EnchantmentSlot(i))); + data << uint32(pItem->GetEnchantmentCharges(EnchantmentSlot(i))); + } + + data << int32(pItem->GetItemRandomPropertyId()); //random item property id + data << uint32(pItem->GetItemSuffixFactor()); //SuffixFactor + data << uint32(pItem->GetCount()); //item->count + data << uint32(pItem->GetSpellCharges()); //item->charge FFFFFFF + data << uint32(0); //Unknown + data << uint64(owner); //Auction->owner + data << uint32(startbid); //Auction->startbid (not sure if useful) + data << uint32(bid ? GetAuctionOutBid() : 0); + //minimal outbid + data << uint32(buyout); //auction->buyout + data << uint32((expire_time-time(NULL))*IN_MILISECONDS);//time left + data << uint64(bidder) ; //auction->bidder current + data << uint32(bid); //current bid + return true; +} + +uint32 AuctionEntry::GetAuctionCut() const +{ + int32 cut = int32(((double)auctionHouseEntry->cutPercent / 100.0f) * (double)sWorld.getRate(RATE_AUCTION_CUT)) * bid; + if (cut > 0) + return cut; + else + return 0; +} + +/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c +uint32 AuctionEntry::GetAuctionOutBid() const +{ + uint32 outbid = (uint32)((double)bid / 100.0f) * 5; + if (!outbid) + outbid = 1; + return outbid; +} + +void AuctionEntry::DeleteFromDB() const +{ + //No SQL injection (Id is integer) + CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",Id); +} + +void AuctionEntry::SaveToDB() const +{ + //No SQL injection (no strings) + CharacterDatabase.PExecute("INSERT INTO auctionhouse (id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit) " + "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u')", + Id, auctioneer, item_guidlow, item_template, owner, buyout, (uint64)expire_time, bidder, bid, startbid, deposit); +} diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h new file mode 100644 index 00000000000..b92cec986c7 --- /dev/null +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUCTION_HOUSE_MGR_H +#define _AUCTION_HOUSE_MGR_H + +#include "Policies/Singleton.h" + +#include "SharedDefines.h" + +#include "AuctionHouseBot.h" + +class Item; +class Player; +class WorldPacket; + +#define MIN_AUCTION_TIME (12*HOUR) + +enum AuctionError +{ + AUCTION_OK = 0, + AUCTION_INTERNAL_ERROR = 2, + AUCTION_NOT_ENOUGHT_MONEY = 3, + AUCTION_ITEM_NOT_FOUND = 4, + CANNOT_BID_YOUR_AUCTION_ERROR = 10 +}; + +enum AuctionAction +{ + AUCTION_SELL_ITEM = 0, + AUCTION_CANCEL = 1, + AUCTION_PLACE_BID = 2 +}; + +struct AuctionEntry +{ + uint32 Id; + uint32 auctioneer; // creature low guid + uint32 item_guidlow; + uint32 item_template; + uint32 owner; + uint32 startbid; //maybe useless + uint32 bid; + uint32 buyout; + time_t expire_time; + uint32 bidder; + uint32 deposit; //deposit can be calculated only when creating auction + AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc + + // helpers + uint32 GetHouseId() const { return auctionHouseEntry->houseId; } + uint32 GetHouseFaction() const { return auctionHouseEntry->faction; } + uint32 GetAuctionCut() const; + uint32 GetAuctionOutBid() const; + bool BuildAuctionInfo(WorldPacket & data) const; + void DeleteFromDB() const; + void SaveToDB() const; +}; + +//this class is used as auctionhouse instance +class AuctionHouseObject +{ + public: + // Initialize storage + AuctionHouseObject() { next = AuctionsMap.begin(); } + ~AuctionHouseObject() + { + for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + delete itr->second; + } + + typedef std::map AuctionEntryMap; + + uint32 Getcount() { return AuctionsMap.size(); } + + AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();} + AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();} + + AuctionEntry* GetAuction(uint32 id) const + { + AuctionEntryMap::const_iterator itr = AuctionsMap.find(id); + return itr != AuctionsMap.end() ? itr->second : NULL; + } + + void AddAuction(AuctionEntry *ah); + + bool RemoveAuction(AuctionEntry *auction, uint32 item_template); + + void Update(); + + void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); + void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); + void BuildListAuctionItems(WorldPacket& data, Player* player, + std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, + uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, + uint32& count, uint32& totalcount); + + private: + AuctionEntryMap AuctionsMap; + + // storage for "next" auction item for next Update() + AuctionEntryMap::const_iterator next; +}; + +class AuctionHouseMgr +{ + public: + AuctionHouseMgr(); + ~AuctionHouseMgr(); + + typedef UNORDERED_MAP ItemMap; + + AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId); + AuctionHouseObject* GetBidsMap(uint32 factionTemplateId); + + Item* GetAItem(uint32 id) + { + ItemMap::const_iterator itr = mAitems.find(id); + if (itr != mAitems.end()) + { + return itr->second; + } + return NULL; + } + + //auction messages + void SendAuctionWonMail(AuctionEntry * auction); + void SendAuctionSalePendingMail(AuctionEntry * auction); + void SendAuctionSuccessfulMail(AuctionEntry * auction); + void SendAuctionExpiredMail(AuctionEntry * auction); + static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem); + static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId); + + public: + //load first auction items, because of check if item exists, when loading + void LoadAuctionItems(); + void LoadAuctions(); + + void AddAItem(Item* it); + bool RemoveAItem(uint32 id); + + void Update(); + + private: + AuctionHouseObject mHordeAuctions; + AuctionHouseObject mAllianceAuctions; + AuctionHouseObject mNeutralAuctions; + + ItemMap mAitems; +}; + +#define auctionmgr Trinity::Singleton::Instance() + +#endif diff --git a/src/server/game/AuctionHouseBot.cpp b/src/server/game/AuctionHouseBot.cpp deleted file mode 100644 index ebbf48e6476..00000000000 --- a/src/server/game/AuctionHouseBot.cpp +++ /dev/null @@ -1,1860 +0,0 @@ -#include "ObjectMgr.h" -#include "AuctionHouseMgr.h" -#include "AuctionHouseBot.h" -#include - -#include "Policies/SingletonImp.h" -INSTANTIATE_SINGLETON_1(AuctionHouseBot); - -using namespace std; -vector npcItems; -vector lootItems; -vector greyTradeGoodsBin; -vector whiteTradeGoodsBin; -vector greenTradeGoodsBin; -vector blueTradeGoodsBin; -vector purpleTradeGoodsBin; -vector orangeTradeGoodsBin; -vector yellowTradeGoodsBin; -vector greyItemsBin; -vector whiteItemsBin; -vector greenItemsBin; -vector blueItemsBin; -vector purpleItemsBin; -vector orangeItemsBin; -vector yellowItemsBin; -AuctionHouseBot::AuctionHouseBot() -{ - debug_Out = false; - debug_Out_Filters = false; - AHBSeller = false; - AHBBuyer = false; - - //Begin Filters - - Vendor_Items = false; - Loot_Items = false; - Other_Items = false; - Vendor_TGs = false; - Loot_TGs = false; - Other_TGs = false; - - No_Bind = false; - Bind_When_Picked_Up = false; - Bind_When_Equipped = false; - Bind_When_Use = false; - Bind_Quest_Item = false; - - DisableBeta_PTR_Unused = false; - DisablePermEnchant = false; - DisableConjured = false; - DisableGems = false; - DisableMoney = false; - DisableMoneyLoot = false; - DisableLootable = false; - DisableKeys = false; - DisableDuration = false; - DisableBOP_Or_Quest_NoReqLevel = false; - - DisableWarriorItems = false; - DisablePaladinItems = false; - DisableHunterItems = false; - DisableRogueItems = false; - DisablePriestItems = false; - DisableDKItems = false; - DisableShamanItems = false; - DisableMageItems = false; - DisableWarlockItems = false; - DisableUnusedClassItems = false; - DisableDruidItems = false; - - DisableItemsBelowLevel = 0; - DisableItemsAboveLevel = 0; - DisableTGsBelowLevel = 0; - DisableTGsAboveLevel = 0; - DisableItemsBelowGUID = 0; - DisableItemsAboveGUID = 0; - DisableTGsBelowGUID = 0; - DisableTGsAboveGUID = 0; - DisableItemsBelowReqLevel = 0; - DisableItemsAboveReqLevel = 0; - DisableTGsBelowReqLevel = 0; - DisableTGsAboveReqLevel = 0; - DisableItemsBelowReqSkillRank = 0; - DisableItemsAboveReqSkillRank = 0; - DisableTGsBelowReqSkillRank = 0; - DisableTGsAboveReqSkillRank = 0; - - //End Filters - - _lastrun_a = time(NULL); - _lastrun_h = time(NULL); - _lastrun_n = time(NULL); - - AllianceConfig = AHBConfig(2); - HordeConfig = AHBConfig(6); - NeutralConfig = AHBConfig(7); -} - -AuctionHouseBot::~AuctionHouseBot() -{ -} - -void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config) -{ - if (!AHBSeller) - { - if (debug_Out) sLog.outError("AHSeller: Disabled"); - return; - } - - uint32 minItems = config->GetMinItems(); - uint32 maxItems = config->GetMaxItems(); - - if (maxItems == 0) - { - //if (debug_Out) sLog.outString("AHSeller: Auctions disabled"); - return; - } - - AuctionHouseEntry const* ahEntry = auctionmgr.GetAuctionHouseEntry(config->GetAHFID()); - if (!ahEntry) - { - return; - } - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); - if (!auctionHouse) - { - return; - } - - uint32 auctions = auctionHouse->Getcount(); - - if (auctions >= minItems) - { - //if (debug_Out) sLog.outString("AHSeller: Auctions above minimum"); - return; - } - - if (auctions >= maxItems) - { - //if (debug_Out) sLog.outString("AHSeller: Auctions at or above maximum"); - return; - } - - uint32 items = 0; - if ((maxItems - auctions) >= ItemsPerCycle) - items = ItemsPerCycle; - else - items = (maxItems - auctions); - - if (debug_Out) sLog.outString("AHSeller: Adding %u Auctions", items); - - uint32 AuctioneerGUID = 0; - - switch (config->GetAHID()) - { - case 2: - AuctioneerGUID = 79707; //Human in stormwind. - break; - case 6: - AuctioneerGUID = 4656; //orc in Orgrimmar - break; - case 7: - AuctioneerGUID = 23442; //goblin in GZ - break; - default: - if (debug_Out) sLog.outError("AHSeller: GetAHID() - Default switch reached"); - AuctioneerGUID = 23442; //default to neutral 7 - break; - } - - if (debug_Out) sLog.outString("AHSeller: Current Auctineer GUID is %u", AuctioneerGUID); - - uint32 greyTGcount = config->GetPercents(AHB_GREY_TG); - uint32 whiteTGcount = config->GetPercents(AHB_WHITE_TG); - uint32 greenTGcount = config->GetPercents(AHB_GREEN_TG); - uint32 blueTGcount = config->GetPercents(AHB_BLUE_TG); - uint32 purpleTGcount = config->GetPercents(AHB_PURPLE_TG); - uint32 orangeTGcount = config->GetPercents(AHB_ORANGE_TG); - uint32 yellowTGcount = config->GetPercents(AHB_YELLOW_TG); - uint32 greyIcount = config->GetPercents(AHB_GREY_I); - uint32 whiteIcount = config->GetPercents(AHB_WHITE_I); - uint32 greenIcount = config->GetPercents(AHB_GREEN_I); - uint32 blueIcount = config->GetPercents(AHB_BLUE_I); - uint32 purpleIcount = config->GetPercents(AHB_PURPLE_I); - uint32 orangeIcount = config->GetPercents(AHB_ORANGE_I); - uint32 yellowIcount = config->GetPercents(AHB_YELLOW_I); -/* uint32 total = greyTGcount + whiteTGcount + greenTGcount + blueTGcount - + purpleTGcount + orangeTGcount + yellowTGcount - + whiteIcount + greenIcount + blueIcount + purpleIcount - + orangeIcount + yellowIcount; -*/ - uint32 greyTGoods = config->GetItemCounts(AHB_GREY_TG); - uint32 whiteTGoods = config->GetItemCounts(AHB_WHITE_TG); - uint32 greenTGoods = config->GetItemCounts(AHB_GREEN_TG); - uint32 blueTGoods = config->GetItemCounts(AHB_BLUE_TG); - uint32 purpleTGoods = config->GetItemCounts(AHB_PURPLE_TG); - uint32 orangeTGoods = config->GetItemCounts(AHB_ORANGE_TG); - uint32 yellowTGoods = config->GetItemCounts(AHB_YELLOW_TG); - - uint32 greyItems = config->GetItemCounts(AHB_GREY_I); - uint32 whiteItems = config->GetItemCounts(AHB_WHITE_I); - uint32 greenItems = config->GetItemCounts(AHB_GREEN_I); - uint32 blueItems = config->GetItemCounts(AHB_BLUE_I); - uint32 purpleItems = config->GetItemCounts(AHB_PURPLE_I); - uint32 orangeItems = config->GetItemCounts(AHB_ORANGE_I); - uint32 yellowItems = config->GetItemCounts(AHB_YELLOW_I); - if (debug_Out) sLog.outString("AHSeller: %u items", items); - - // only insert a few at a time, so as not to peg the processor - for (uint32 cnt = 1; cnt <= items; cnt++) - { - if (debug_Out) sLog.outString("AHSeller: %u count", cnt); - uint32 itemID = 0; - uint32 itemColor = 99; - uint32 loopbreaker = 0; - while (itemID == 0 && loopbreaker <= 50) - { - ++loopbreaker; - uint32 choice = urand(0, 13); - itemColor = choice; - switch (choice) - { - case 0: - { - if ((greyItemsBin.size() > 0) && (greyItems < greyIcount)) - itemID = greyItemsBin[urand(0, greyItemsBin.size() - 1)]; - else continue; - break; - } - case 1: - { - if ((whiteItemsBin.size() > 0) && (whiteItems < whiteIcount)) - itemID = whiteItemsBin[urand(0, whiteItemsBin.size() - 1)]; - else continue; - break; - } - case 2: - { - if ((greenItemsBin.size() > 0) && (greenItems < greenIcount)) - itemID = greenItemsBin[urand(0, greenItemsBin.size() - 1)]; - else continue; - break; - } - case 3: - { - if ((blueItemsBin.size() > 0) && (blueItems < blueIcount)) - itemID = blueItemsBin[urand(0, blueItemsBin.size() - 1)]; - else continue; - break; - } - case 4: - { - if ((purpleItemsBin.size() > 0) && (purpleItems < purpleIcount)) - itemID = purpleItemsBin[urand(0, purpleItemsBin.size() - 1)]; - else continue; - break; - } - case 5: - { - if ((orangeItemsBin.size() > 0) && (orangeItems < orangeIcount)) - itemID = orangeItemsBin[urand(0, orangeItemsBin.size() - 1)]; - else continue; - break; - } - case 6: - { - if ((yellowItemsBin.size() > 0) && (yellowItems < yellowIcount)) - itemID = yellowItemsBin[urand(0, yellowItemsBin.size() - 1)]; - else continue; - break; - } - case 7: - { - if ((greyTradeGoodsBin.size() > 0) && (greyTGoods < greyTGcount)) - itemID = greyTradeGoodsBin[urand(0, greyTradeGoodsBin.size() - 1)]; - else continue; - break; - } - case 8: - { - if ((whiteTradeGoodsBin.size() > 0) && (whiteTGoods < whiteTGcount)) - itemID = whiteTradeGoodsBin[urand(0, whiteTradeGoodsBin.size() - 1)]; - else continue; - break; - } - case 9: - { - if ((greenTradeGoodsBin.size() > 0) && (greenTGoods < greenTGcount)) - itemID = greenTradeGoodsBin[urand(0, greenTradeGoodsBin.size() - 1)]; - else continue; - break; - } - case 10: - { - if ((blueTradeGoodsBin.size() > 0) && (blueTGoods < blueTGcount)) - itemID = blueTradeGoodsBin[urand(0, blueTradeGoodsBin.size() - 1)]; - else continue; - break; - } - case 11: - { - if ((purpleTradeGoodsBin.size() > 0) && (purpleTGoods < purpleTGcount)) - itemID = purpleTradeGoodsBin[urand(0, purpleTradeGoodsBin.size() - 1)]; - else continue; - break; - } - case 12: - { - if ((orangeTradeGoodsBin.size() > 0) && (orangeTGoods < orangeTGcount)) - itemID = orangeTradeGoodsBin[urand(0, orangeTradeGoodsBin.size() - 1)]; - else continue; - break; - } - case 13: - { - if ((yellowTradeGoodsBin.size() > 0) && (yellowTGoods < yellowTGcount)) - itemID = yellowTradeGoodsBin[urand(0, yellowTradeGoodsBin.size() - 1)]; - else continue; - break; - } - default: - { - if (debug_Out) sLog.outError("AHSeller: itemID Switch - Default Reached"); - break; - } - } - - if (itemID == 0) - { - if (debug_Out) sLog.outError("AHSeller: Item::CreateItem() - ItemID is 0"); - continue; - } - - ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); - if (prototype == NULL) - { - if (debug_Out) sLog.outError("AHSeller: Huh?!?! prototype == NULL"); - continue; - } - - Item* item = Item::CreateItem(itemID, 1, AHBplayer); - if (item == NULL) - { - if (debug_Out) sLog.outError("AHSeller: Item::CreateItem() returned NULL"); - break; - } - item->AddToUpdateQueueOf(AHBplayer); - - uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID); - if (randomPropertyId != 0) - item->SetItemRandomProperties(randomPropertyId); - - uint64 buyoutPrice = 0; - uint64 bidPrice = 0; - uint32 stackCount = 1; - - switch (SellMethod) - { - case 0: - buyoutPrice = prototype->SellPrice; - break; - case 1: - buyoutPrice = prototype->BuyPrice; - break; - } - - if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY)) - { - if (config->GetMaxStack(prototype->Quality) > 1 && item->GetMaxStackCount() > 1) - stackCount = urand(1, minValue(item->GetMaxStackCount(), config->GetMaxStack(prototype->Quality))); - else if (config->GetMaxStack(prototype->Quality) == 0 && item->GetMaxStackCount() > 1) - stackCount = urand(1, item->GetMaxStackCount()); - else - stackCount = 1; - buyoutPrice *= urand(config->GetMinPrice(prototype->Quality), config->GetMaxPrice(prototype->Quality)); - buyoutPrice /= 100; - bidPrice = buyoutPrice * urand(config->GetMinBidPrice(prototype->Quality), config->GetMaxBidPrice(prototype->Quality)); - bidPrice /= 100; - } - else - { - // quality is something it shouldn't be, let's get out of here - if (debug_Out) sLog.outError("AHBuyer: Quality %u not Supported", prototype->Quality); - item->RemoveFromUpdateQueueOf(AHBplayer); - continue; - } - - uint32 etime = urand(1,3); - switch(etime) - { - case 1: - etime = 43200; - break; - case 2: - etime = 86400; - break; - case 3: - etime = 172800; - break; - default: - etime = 86400; - break; - } - item->SetCount(stackCount); - - uint32 dep = auctionmgr.GetAuctionDeposit(ahEntry, etime, item); - - AuctionEntry* auctionEntry = new AuctionEntry; - auctionEntry->Id = objmgr.GenerateAuctionID(); - auctionEntry->auctioneer = AuctioneerGUID; - auctionEntry->item_guidlow = item->GetGUIDLow(); - auctionEntry->item_template = item->GetEntry(); - auctionEntry->owner = AHBplayer->GetGUIDLow(); - auctionEntry->startbid = bidPrice * stackCount; - auctionEntry->buyout = buyoutPrice * stackCount; - auctionEntry->bidder = 0; - auctionEntry->bid = 0; - auctionEntry->deposit = dep; - auctionEntry->expire_time = (time_t) etime + time(NULL); - auctionEntry->auctionHouseEntry = ahEntry; - item->SaveToDB(); - item->RemoveFromUpdateQueueOf(AHBplayer); - auctionmgr.AddAItem(item); - auctionHouse->AddAuction(auctionEntry); - auctionEntry->SaveToDB(); - - switch(itemColor) - { - case 0: - ++greyItems; - break; - case 1: - ++whiteItems; - break; - case 2: - ++greenItems; - break; - case 3: - ++blueItems; - break; - case 4: - ++purpleItems; - break; - case 5: - ++orangeItems; - break; - case 6: - ++yellowItems; - break; - case 7: - ++greyTGoods; - break; - case 8: - ++whiteTGoods; - break; - case 9: - ++greenTGoods; - break; - case 10: - ++blueTGoods; - break; - case 11: - ++purpleTGoods; - break; - case 12: - ++orangeTGoods; - break; - case 13: - ++yellowTGoods; - break; - default: - break; - } - } - } -} -void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session) -{ - if (!AHBBuyer) - { - if (debug_Out) sLog.outError("AHBuyer: Disabled"); - return; - } - - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT id FROM auctionhouse WHERE itemowner<>%u AND buyguid<>%u", AHBplayerGUID, AHBplayerGUID); - - if (!result) - return; - - if (result->GetRowCount() == 0) - return; - - // Fetches content of selected AH - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); - vector possibleBids; - - do - { - uint32 tmpdata = result->Fetch()->GetUInt32(); - possibleBids.push_back(tmpdata); - }while (result->NextRow()); - - for (uint32 count = 1; count <= config->GetBidsPerInterval(); ++count) - { - // Do we have anything to bid? If not, stop here. - if (possibleBids.empty()) - { - //if (debug_Out) sLog.outString("AHBuyer: I have no items to bid on."); - count = config->GetBidsPerInterval(); - continue; - } - - // Choose random auction from possible auctions - uint32 vectorPos = urand(0, possibleBids.size() - 1); - vector::iterator iter = possibleBids.begin(); - advance(iter, vectorPos); - - // from auctionhousehandler.cpp, creates auction pointer & player pointer - AuctionEntry* auction = auctionHouse->GetAuction(*iter); - - // Erase the auction from the vector to prevent bidding on item in next iteration. - possibleBids.erase(iter); - - if (!auction) - continue; - - // get exact item information - Item *pItem = auctionmgr.GetAItem(auction->item_guidlow); - if (!pItem) - { - if (debug_Out) sLog.outError("AHBuyer: Item %u doesn't exist, perhaps bought already?", auction->item_guidlow); - continue; - } - - // get item prototype - ItemPrototype const* prototype = objmgr.GetItemPrototype(auction->item_template); - - // check which price we have to use, startbid or if it is bidded already - uint32 currentprice; - if (auction->bid) - currentprice = auction->bid; - else - currentprice = auction->startbid; - - // Prepare portion from maximum bid - double bidrate = static_cast(urand(1, 100)) / 100; - long double bidMax = 0; - - // check that bid has acceptable value and take bid based on vendorprice, stacksize and quality - switch (BuyMethod) - { - case 0: - { - if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY)) - { - if (currentprice < prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality)) - bidMax = prototype->SellPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality); - } - else - { - // quality is something it shouldn't be, let's get out of here - if (debug_Out) sLog.outError("AHBuyer: Quality %u not Supported", prototype->Quality); - continue; - } - break; - } - case 1: - { - if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY)) - { - if (currentprice < prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality)) - bidMax = prototype->BuyPrice * pItem->GetCount() * config->GetBuyerPrice(prototype->Quality); - } - else - { - // quality is something it shouldn't be, let's get out of here - if (debug_Out) sLog.outError("AHBuyer: Quality %u not Supported", prototype->Quality); - continue; - } - break; - } - } - - // check some special items, and do recalculating to their prices - switch (prototype->Class) - { - // ammo - case 6: - bidMax = 0; - break; - default: - break; - } - - if (bidMax == 0) - { - // quality check failed to get bidmax, let's get out of here - continue; - } - - // Calculate our bid - long double bidvalue = currentprice + ((bidMax - currentprice) * bidrate); - // Convert to uint32 - uint32 bidprice = static_cast(bidvalue); - - // Check our bid is high enough to be valid. If not, correct it to minimum. - if ((currentprice + auction->GetAuctionOutBid()) > bidprice) - bidprice = currentprice + auction->GetAuctionOutBid(); - - if (debug_Out) - { - sLog.outString("-------------------------------------------------"); - sLog.outString("AHBuyer: Info for Auction #%u:", auction->Id); - sLog.outString("AHBuyer: AuctionHouse: %u", auction->GetHouseId()); - sLog.outString("AHBuyer: Auctioneer: %u", auction->auctioneer); - sLog.outString("AHBuyer: Owner: %u", auction->owner); - sLog.outString("AHBuyer: Bidder: %u", auction->bidder); - sLog.outString("AHBuyer: Starting Bid: %u", auction->startbid); - sLog.outString("AHBuyer: Current Bid: %u", currentprice); - sLog.outString("AHBuyer: Buyout: %u", auction->buyout); - sLog.outString("AHBuyer: Deposit: %u", auction->deposit); - sLog.outString("AHBuyer: Expire Time: %u", auction->expire_time); - sLog.outString("AHBuyer: Bid Rate: %f", bidrate); - sLog.outString("AHBuyer: Bid Max: %f", bidMax); - sLog.outString("AHBuyer: Bid Value: %f", bidvalue); - sLog.outString("AHBuyer: Bid Price: %u", bidprice); - sLog.outString("AHBuyer: Item GUID: %u", auction->item_guidlow); - sLog.outString("AHBuyer: Item Template: %u", auction->item_template); - sLog.outString("AHBuyer: Item Info:"); - sLog.outString("AHBuyer: Item ID: %u", prototype->ItemId); - sLog.outString("AHBuyer: Buy Price: %u", prototype->BuyPrice); - sLog.outString("AHBuyer: Sell Price: %u", prototype->SellPrice); - sLog.outString("AHBuyer: Bonding: %u", prototype->Bonding); - sLog.outString("AHBuyer: Quality: %u", prototype->Quality); - sLog.outString("AHBuyer: Item Level: %u", prototype->ItemLevel); - sLog.outString("AHBuyer: Ammo Type: %u", prototype->AmmoType); - sLog.outString("-------------------------------------------------"); - } - - // Check whether we do normal bid, or buyout - if ((bidprice < auction->buyout) || (auction->buyout == 0)) - { - - if (auction->bidder > 0) - { - if (auction->bidder == AHBplayer->GetGUIDLow()) - { - //pl->ModifyMoney(-int32(price - auction->bid)); - } - else - { - // mail to last bidder and return money - session->SendAuctionOutbiddedMail(auction , bidprice); - //pl->ModifyMoney(-int32(price)); - } - } - - auction->bidder = AHBplayer->GetGUIDLow(); - auction->bid = bidprice; - - // Saving auction into database - CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); - } - else - { - //buyout - if ((auction->bidder) && (AHBplayer->GetGUIDLow() != auction->bidder)) - { - session->SendAuctionOutbiddedMail(auction, auction->buyout); - } - auction->bidder = AHBplayer->GetGUIDLow(); - auction->bid = auction->buyout; - - // Send mails to buyer & seller - auctionmgr.SendAuctionSalePendingMail(auction); - auctionmgr.SendAuctionSuccessfulMail(auction); - auctionmgr.SendAuctionWonMail(auction); - auction->DeleteFromDB(); - uint32 item_template = auction->item_template; - auctionmgr.RemoveAItem(auction->item_guidlow); - auctionHouse->RemoveAuction(auction, item_template); - } - } -} - -void AuctionHouseBot::Update() -{ - time_t _newrun = time(NULL); - if ((!AHBSeller) && (!AHBBuyer)) - return; - - WorldSession _session(AHBplayerAccount, NULL, SEC_PLAYER, true, 0, LOCALE_enUS); - Player _AHBplayer(&_session); - _AHBplayer.Initialize(AHBplayerGUID); - ObjectAccessor::Instance().AddObject(&_AHBplayer); - - // Add New Bids - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - { - addNewAuctions(&_AHBplayer, &AllianceConfig); - if (((_newrun - _lastrun_a) >= (AllianceConfig.GetBiddingInterval() * MINUTE)) && (AllianceConfig.GetBidsPerInterval() > 0)) - { - //if (debug_Out) sLog.outString("AHBuyer: %u seconds have passed since last bid", (_newrun - _lastrun_a)); - //if (debug_Out) sLog.outString("AHBuyer: Bidding on Alliance Auctions"); - addNewAuctionBuyerBotBid(&_AHBplayer, &AllianceConfig, &_session); - _lastrun_a = _newrun; - } - - addNewAuctions(&_AHBplayer, &HordeConfig); - if (((_newrun - _lastrun_h) >= (HordeConfig.GetBiddingInterval() * MINUTE)) && (HordeConfig.GetBidsPerInterval() > 0)) - { - //if (debug_Out) sLog.outString("AHBuyer: %u seconds have passed since last bid", (_newrun - _lastrun_h)); - //if (debug_Out) sLog.outString("AHBuyer: Bidding on Horde Auctions"); - addNewAuctionBuyerBotBid(&_AHBplayer, &HordeConfig, &_session); - _lastrun_h = _newrun; - } - } - - addNewAuctions(&_AHBplayer, &NeutralConfig); - if (((_newrun - _lastrun_n) >= (NeutralConfig.GetBiddingInterval() * MINUTE)) && (NeutralConfig.GetBidsPerInterval() > 0)) - { - //if (debug_Out) sLog.outString("AHBuyer: %u seconds have passed since last bid", (_newrun - _lastrun_n)); - //if (debug_Out) sLog.outString("AHBuyer: Bidding on Neutral Auctions"); - addNewAuctionBuyerBotBid(&_AHBplayer, &NeutralConfig, &_session); - _lastrun_n = _newrun; - } - ObjectAccessor::Instance().RemoveObject(&_AHBplayer); -} - -void AuctionHouseBot::Initialize() -{ - debug_Out = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG", false); - debug_Out_Filters = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG_FILTERS", false); - - AHBSeller = sConfig.GetBoolDefault("AuctionHouseBot.EnableSeller", false); - AHBBuyer = sConfig.GetBoolDefault("AuctionHouseBot.EnableBuyer", false); - SellMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForSeller", false); - BuyMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForBuyer", false); - - AHBplayerAccount = sConfig.GetIntDefault("AuctionHouseBot.Account", 0); - AHBplayerGUID = sConfig.GetIntDefault("AuctionHouseBot.GUID", 0); - ItemsPerCycle = sConfig.GetIntDefault("AuctionHouseBot.ItemsPerCycle", 200); - - //Begin Filters - - Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", false); - Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", true); - Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", false); - Vendor_TGs = sConfig.GetBoolDefault("AuctionHouseBot.VendorTradeGoods", false); - Loot_TGs = sConfig.GetBoolDefault("AuctionHouseBot.LootTradeGoods", true); - Other_TGs = sConfig.GetBoolDefault("AuctionHouseBot.OtherTradeGoods", false); - - No_Bind = sConfig.GetBoolDefault("AuctionHouseBot.No_Bind", true); - Bind_When_Picked_Up = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Picked_Up", false); - Bind_When_Equipped = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Equipped", true); - Bind_When_Use = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Use", true); - Bind_Quest_Item = sConfig.GetBoolDefault("AuctionHouseBot.Bind_Quest_Item", false); - - DisableBeta_PTR_Unused = sConfig.GetBoolDefault("AuctionHouseBot.DisableBeta_PTR_Unused", false); - DisablePermEnchant = sConfig.GetBoolDefault("AuctionHouseBot.DisablePermEnchant", false); - DisableConjured = sConfig.GetBoolDefault("AuctionHouseBot.DisableConjured", false); - DisableGems = sConfig.GetBoolDefault("AuctionHouseBot.DisableGems", false); - DisableMoney = sConfig.GetBoolDefault("AuctionHouseBot.DisableMoney", false); - DisableMoneyLoot = sConfig.GetBoolDefault("AuctionHouseBot.DisableMoneyLoot", false); - DisableLootable = sConfig.GetBoolDefault("AuctionHouseBot.DisableLootable", false); - DisableKeys = sConfig.GetBoolDefault("AuctionHouseBot.DisableKeys", false); - DisableDuration = sConfig.GetBoolDefault("AuctionHouseBot.DisableDuration", false); - DisableBOP_Or_Quest_NoReqLevel = sConfig.GetBoolDefault("AuctionHouseBot.DisableBOP_Or_Quest_NoReqLevel", false); - - DisableWarriorItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarriorItems", false); - DisablePaladinItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePaladinItems", false); - DisableHunterItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableHunterItems", false); - DisableRogueItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableRogueItems", false); - DisablePriestItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePriestItems", false); - DisableDKItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDKItems", false); - DisableShamanItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableShamanItems", false); - DisableMageItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableMageItems", false); - DisableWarlockItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarlockItems", false); - DisableUnusedClassItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableUnusedClassItems", false); - DisableDruidItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDruidItems", false); - - DisableItemsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowLevel", 0); - DisableItemsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveLevel", 0); - DisableTGsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowLevel", 0); - DisableTGsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveLevel", 0); - DisableItemsBelowGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowGUID", 0); - DisableItemsAboveGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveGUID", 0); - DisableTGsBelowGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowGUID", 0); - DisableTGsAboveGUID = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveGUID", 0); - DisableItemsBelowReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowReqLevel", 0); - DisableItemsAboveReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqLevel", 0); - DisableTGsBelowReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqLevel", 0); - DisableTGsAboveReqLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqLevel", 0); - DisableItemsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowReqSkillRank", 0); - DisableItemsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqSkillRank", 0); - DisableTGsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqSkillRank", 0); - DisableTGsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqSkillRank", 0); - - //End Filters - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - { - LoadValues(&AllianceConfig); - LoadValues(&HordeConfig); - } - LoadValues(&NeutralConfig); - - // - // check if the AHBot account/GUID in the config actually exists - // - - if ((AHBplayerAccount != 0) || (AHBplayerGUID != 0)) - { - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT 1 FROM characters WHERE account = %u AND guid = %u", AHBplayerAccount, AHBplayerGUID); - if (!result) - { - sLog.outError("AuctionHouseBot: The account/GUID-information set for your AHBot is incorrect (account: %u guid: %u)", AHBplayerAccount, AHBplayerGUID); - return; - } - } - - if (AHBSeller) - { - QueryResult_AutoPtr results = QueryResult_AutoPtr(NULL); - char npcQuery[] = "SELECT distinct item FROM npc_vendor"; - results = WorldDatabase.Query(npcQuery); - if (results != NULL) - { - do - { - Field* fields = results->Fetch(); - npcItems.push_back(fields[0].GetUInt32()); - - } while (results->NextRow()); - } - else - { - if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", npcQuery); - } - - char lootQuery[] = "SELECT item FROM creature_loot_template UNION " - "SELECT item FROM reference_loot_template UNION " - "SELECT item FROM disenchant_loot_template UNION " - "SELECT item FROM fishing_loot_template UNION " - "SELECT item FROM gameobject_loot_template UNION " - "SELECT item FROM item_loot_template UNION " - "SELECT item FROM milling_loot_template UNION " - "SELECT item FROM pickpocketing_loot_template UNION " - "SELECT item FROM prospecting_loot_template UNION " - "SELECT item FROM skinning_loot_template"; - - results = WorldDatabase.Query(lootQuery); - if (results != NULL) - { - do - { - Field* fields = results->Fetch(); - lootItems.push_back(fields[0].GetUInt32()); - - } while (results->NextRow()); - } - else - { - if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", lootQuery); - } - - for (uint32 itemID = 0; itemID < sItemStorage.MaxEntry; itemID++) - { - ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID); - - if (prototype == NULL) - continue; - - switch (prototype->Bonding) - { - case NO_BIND: - if (!No_Bind) - continue; - break; - case BIND_WHEN_PICKED_UP: - if (!Bind_When_Picked_Up) - continue; - break; - case BIND_WHEN_EQUIPED: - if (!Bind_When_Equipped) - continue; - break; - case BIND_WHEN_USE: - if (!Bind_When_Use) - continue; - break; - case BIND_QUEST_ITEM: - if (!Bind_Quest_Item) - continue; - break; - default: - continue; - break; - } - - switch (SellMethod) - { - case 0: - if (prototype->SellPrice == 0) - continue; - break; - case 1: - if (prototype->BuyPrice == 0) - continue; - break; - } - - if ((prototype->Quality < 0) || (prototype->Quality > 6)) - continue; - - if ((Vendor_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS)) - { - bool isVendorItem = false; - - for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++) - { - if (itemID == npcItems[i]) - isVendorItem = true; - } - - if (isVendorItem) - continue; - } - - if ((Vendor_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS)) - { - bool isVendorTG = false; - - for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorTG); i++) - { - if (itemID == npcItems[i]) - isVendorTG = true; - } - - if (isVendorTG) - continue; - } - - if ((Loot_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS)) - { - bool isLootItem = false; - - for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++) - { - if (itemID == lootItems[i]) - isLootItem = true; - } - - if (isLootItem) - continue; - } - - if ((Loot_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS)) - { - bool isLootTG = false; - - for (unsigned int i = 0; (i < lootItems.size()) && (!isLootTG); i++) - { - if (itemID == lootItems[i]) - isLootTG = true; - } - - if (isLootTG) - continue; - } - - if ((Other_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS)) - { - bool isVendorItem = false; - bool isLootItem = false; - - for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++) - { - if (itemID == npcItems[i]) - isVendorItem = true; - } - for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++) - { - if (itemID == lootItems[i]) - isLootItem = true; - } - if ((!isLootItem) && (!isVendorItem)) - continue; - } - - if ((Other_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS)) - { - bool isVendorTG = false; - bool isLootTG = false; - - for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorTG); i++) - { - if (itemID == npcItems[i]) - isVendorTG = true; - } - for (unsigned int i = 0; (i < lootItems.size()) && (!isLootTG); i++) - { - if (itemID == lootItems[i]) - isLootTG = true; - } - if ((!isLootTG) && (!isVendorTG)) - continue; - } - - //TODO:Make list of items and create a vector - // Disable PTR/Beta/Unused items - if ((DisableBeta_PTR_Unused) && ((prototype->ItemId == 21878) || (prototype->ItemId == 27774) || (prototype->ItemId == 27811) || (prototype->ItemId == 28117) || (prototype->ItemId == 28112))) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (PTR/Beta/Unused Item)", prototype->ItemId); - continue; - } - - // Disable permanent enchants items - if ((DisablePermEnchant) && (prototype->Class == ITEM_CLASS_PERMANENT)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Permanent Enchant Item)", prototype->ItemId); - continue; - } - - // Disable conjured items - if ((DisableConjured) && (prototype->IsConjuredConsumable())) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Conjured Consumable)", prototype->ItemId); - continue; - } - - // Disable gems - if ((DisableGems) && (prototype->Class == ITEM_CLASS_GEM)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Gem)", prototype->ItemId); - continue; - } - - // Disable money - if ((DisableMoney) && (prototype->Class == ITEM_CLASS_MONEY)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Money)", prototype->ItemId); - continue; - } - - // Disable moneyloot - if ((DisableMoneyLoot) && (prototype->MinMoneyLoot > 0)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (MoneyLoot)", prototype->ItemId); - continue; - } - - // Disable lootable items - if ((DisableLootable) && (prototype->Flags & 4)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Lootable Item)", prototype->ItemId); - continue; - } - - // Disable Keys - if ((DisableKeys) && (prototype->Class == ITEM_CLASS_KEY)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Quest Item)", prototype->ItemId); - continue; - } - - // Disable items with duration - if ((DisableDuration) && (prototype->Duration > 0)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Has a Duration)", prototype->ItemId); - continue; - } - - // Disable items which are BOP or Quest Items and have a required level lower than the item level - if ((DisableBOP_Or_Quest_NoReqLevel) && ((prototype->Bonding == BIND_WHEN_PICKED_UP || prototype->Bonding == BIND_QUEST_ITEM) && (prototype->RequiredLevel < prototype->ItemLevel))) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (BOP or BQI and Required Level is less than Item Level)", prototype->ItemId); - continue; - } - - // Disable items specifically for Warrior - if ((DisableWarriorItems) && (prototype->AllowableClass == 1)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Warrior Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Paladin - if ((DisablePaladinItems) && (prototype->AllowableClass == 2)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Paladin Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Hunter - if ((DisableHunterItems) && (prototype->AllowableClass == 4)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Hunter Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Rogue - if ((DisableRogueItems) && (prototype->AllowableClass == 8)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Rogue Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Priest - if ((DisablePriestItems) && (prototype->AllowableClass == 16)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Priest Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for DK - if ((DisableDKItems) && (prototype->AllowableClass == 32)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (DK Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Shaman - if ((DisableShamanItems) && (prototype->AllowableClass == 64)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Shaman Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Mage - if ((DisableMageItems) && (prototype->AllowableClass == 128)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Mage Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Warlock - if ((DisableWarlockItems) && (prototype->AllowableClass == 256)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Warlock Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Unused Class - if ((DisableUnusedClassItems) && (prototype->AllowableClass == 512)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Unused Item)", prototype->ItemId); - continue; - } - - // Disable items specifically for Druid - if ((DisableDruidItems) && (prototype->AllowableClass == 1024)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Druid Item)", prototype->ItemId); - continue; - } - - // Disable Items below level X - if ((DisableItemsBelowLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableItemsBelowLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Items above level X - if ((DisableItemsAboveLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableItemsAboveLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Trade Goods below level X - if ((DisableTGsBelowLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableTGsBelowLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Trade Goods above level X - if ((DisableTGsAboveLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableTGsAboveLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Items below GUID X - if ((DisableItemsBelowGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableItemsBelowGUID)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Items above GUID X - if ((DisableItemsAboveGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableItemsAboveGUID)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Trade Goods below GUID X - if ((DisableTGsBelowGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableTGsBelowGUID)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Trade Goods above GUID X - if ((DisableTGsAboveGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableTGsAboveGUID)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel); - continue; - } - - // Disable Items for level lower than X - if ((DisableItemsBelowReqLevel) && (prototype->RequiredLevel < DisableItemsBelowReqLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); - continue; - } - - // Disable Items for level higher than X - if ((DisableItemsAboveReqLevel) && (prototype->RequiredLevel > DisableItemsAboveReqLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); - continue; - } - - // Disable Trade Goods for level lower than X - if ((DisableTGsBelowReqLevel) && (prototype->RequiredLevel < DisableTGsBelowReqLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); - continue; - } - - // Disable Trade Goods for level higher than X - if ((DisableTGsAboveReqLevel) && (prototype->RequiredLevel > DisableTGsAboveReqLevel)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel); - continue; - } - - // Disable Items that require skill lower than X - if ((DisableItemsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableItemsBelowReqSkillRank)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); - continue; - } - - // Disable Items that require skill higher than X - if ((DisableItemsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableItemsAboveReqSkillRank)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); - continue; - } - - // Disable Trade Goods that require skill lower than X - if ((DisableTGsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableTGsBelowReqSkillRank)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); - continue; - } - - // Disable Trade Goods that require skill higher than X - if ((DisableTGsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableTGsAboveReqSkillRank)) - { - if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank); - continue; - } - - switch (prototype->Quality) - { - case AHB_GREY: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - greyTradeGoodsBin.push_back(itemID); - else - greyItemsBin.push_back(itemID); - break; - - case AHB_WHITE: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - whiteTradeGoodsBin.push_back(itemID); - else - whiteItemsBin.push_back(itemID); - break; - - case AHB_GREEN: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - greenTradeGoodsBin.push_back(itemID); - else - greenItemsBin.push_back(itemID); - break; - - case AHB_BLUE: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - blueTradeGoodsBin.push_back(itemID); - else - blueItemsBin.push_back(itemID); - break; - - case AHB_PURPLE: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - purpleTradeGoodsBin.push_back(itemID); - else - purpleItemsBin.push_back(itemID); - break; - - case AHB_ORANGE: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - orangeTradeGoodsBin.push_back(itemID); - else - orangeItemsBin.push_back(itemID); - break; - - case AHB_YELLOW: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - yellowTradeGoodsBin.push_back(itemID); - else - yellowItemsBin.push_back(itemID); - break; - } - } - - if ((greyTradeGoodsBin.size() == 0) && - (whiteTradeGoodsBin.size() == 0) && - (greenTradeGoodsBin.size() == 0) && - (blueTradeGoodsBin.size() == 0) && - (purpleTradeGoodsBin.size() == 0) && - (orangeTradeGoodsBin.size() == 0) && - (yellowTradeGoodsBin.size() == 0) && - (greyItemsBin.size() == 0) && - (whiteItemsBin.size() == 0) && - (greenItemsBin.size() == 0) && - (blueItemsBin.size() == 0) && - (purpleItemsBin.size() == 0) && - (orangeItemsBin.size() == 0) && - (yellowItemsBin.size() == 0)) - { - sLog.outError("AuctionHouseBot: No items"); - AHBSeller = 0; - } - - sLog.outString("AuctionHouseBot:"); - sLog.outString("loaded %u grey trade goods", greyTradeGoodsBin.size()); - sLog.outString("loaded %u white trade goods", whiteTradeGoodsBin.size()); - sLog.outString("loaded %u green trade goods", greenTradeGoodsBin.size()); - sLog.outString("loaded %u blue trade goods", blueTradeGoodsBin.size()); - sLog.outString("loaded %u purple trade goods", purpleTradeGoodsBin.size()); - sLog.outString("loaded %u orange trade goods", orangeTradeGoodsBin.size()); - sLog.outString("loaded %u yellow trade goods", yellowTradeGoodsBin.size()); - sLog.outString("loaded %u grey items", greyItemsBin.size()); - sLog.outString("loaded %u white items", whiteItemsBin.size()); - sLog.outString("loaded %u green items", greenItemsBin.size()); - sLog.outString("loaded %u blue items", blueItemsBin.size()); - sLog.outString("loaded %u purple items", purpleItemsBin.size()); - sLog.outString("loaded %u orange items", orangeItemsBin.size()); - sLog.outString("loaded %u yellow items", yellowItemsBin.size()); - } - sLog.outString("AuctionHouseBot and AuctionHouseBuyer have been loaded."); -} - -void AuctionHouseBot::IncrementItemCounts(AuctionEntry* ah) -{ - // from auctionhousehandler.cpp, creates auction pointer & player pointer - - // get exact item information - Item *pItem = auctionmgr.GetAItem(ah->item_guidlow); - if (!pItem) - { - if (debug_Out) sLog.outError("AHBot: Item %u doesn't exist, perhaps bought already?", ah->item_guidlow); - return; - } - - // get item prototype - ItemPrototype const* prototype = objmgr.GetItemPrototype(ah->item_template); - - AHBConfig *config; - - FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(ah->GetHouseFaction()); - if (!u_entry) - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); - config = &NeutralConfig; - } - else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Alliance", ah->GetHouseFaction()); - config = &AllianceConfig; - } - else if (u_entry->ourMask & FACTION_MASK_HORDE) - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Horde", ah->GetHouseFaction()); - config = &HordeConfig; - } - else - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); - config = &NeutralConfig; - } - - config->IncItemCounts(prototype->Class, prototype->Quality); -} - -void AuctionHouseBot::DecrementItemCounts(AuctionEntry* ah, uint32 item_template) -{ - // get item prototype - ItemPrototype const* prototype = objmgr.GetItemPrototype(item_template); - - AHBConfig *config; - - FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(ah->GetHouseFaction()); - if (!u_entry) - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); - config = &NeutralConfig; - } - else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Alliance", ah->GetHouseFaction()); - config = &AllianceConfig; - } - else if (u_entry->ourMask & FACTION_MASK_HORDE) - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Horde", ah->GetHouseFaction()); - config = &HordeConfig; - } - else - { - if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction()); - config = &NeutralConfig; - } - - config->DecItemCounts(prototype->Class, prototype->Quality); -} - -void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char* args) -{ - AHBConfig *config = NULL; - switch (ahMapID) - { - case 2: - config = &AllianceConfig; - break; - case 6: - config = &HordeConfig; - break; - case 7: - config = &NeutralConfig; - break; - } - std::string color; - switch (col) - { - case AHB_GREY: - color = "grey"; - break; - case AHB_WHITE: - color = "white"; - break; - case AHB_GREEN: - color = "green"; - break; - case AHB_BLUE: - color = "blue"; - break; - case AHB_PURPLE: - color = "purple"; - break; - case AHB_ORANGE: - color = "orange"; - break; - case AHB_YELLOW: - color = "yellow"; - break; - default: - break; - } - switch (command) - { - case 0: //ahexpire - { - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); - - AuctionHouseObject::AuctionEntryMap::iterator itr; - itr = auctionHouse->GetAuctionsBegin(); - - while (itr != auctionHouse->GetAuctionsEnd()) - { - if (itr->second->owner == AHBplayerGUID) - { - itr->second->expire_time = sWorld.GetGameTime(); - uint32 id = itr->second->Id; - uint32 expire_time = itr->second->expire_time; - CharacterDatabase.PExecute("UPDATE auctionhouse SET time = '%u' WHERE id = '%u'", expire_time, id); - } - ++itr; - } - } - break; - case 1: //min items - { - char * param1 = strtok(args, " "); - uint32 minItems = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET minitems = '%u' WHERE auctionhouse = '%u'", minItems, ahMapID); - config->SetMinItems(minItems); - } - break; - case 2: //max items - { - char * param1 = strtok(args, " "); - uint32 maxItems = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxitems = '%u' WHERE auctionhouse = '%u'", maxItems, ahMapID); - config->SetMaxItems(maxItems); - } - break; - case 3: //min time Deprecated (Place holder for future commands) - break; - case 4: //max time Deprecated (Place holder for future commands) - break; - case 5: //percentages - { - char * param1 = strtok(args, " "); - char * param2 = strtok(NULL, " "); - char * param3 = strtok(NULL, " "); - char * param4 = strtok(NULL, " "); - char * param5 = strtok(NULL, " "); - char * param6 = strtok(NULL, " "); - char * param7 = strtok(NULL, " "); - char * param8 = strtok(NULL, " "); - char * param9 = strtok(NULL, " "); - char * param10 = strtok(NULL, " "); - char * param11 = strtok(NULL, " "); - char * param12 = strtok(NULL, " "); - char * param13 = strtok(NULL, " "); - char * param14 = strtok(NULL, " "); - uint32 greytg = (uint32) strtoul(param1, NULL, 0); - uint32 whitetg = (uint32) strtoul(param2, NULL, 0); - uint32 greentg = (uint32) strtoul(param3, NULL, 0); - uint32 bluetg = (uint32) strtoul(param4, NULL, 0); - uint32 purpletg = (uint32) strtoul(param5, NULL, 0); - uint32 orangetg = (uint32) strtoul(param6, NULL, 0); - uint32 yellowtg = (uint32) strtoul(param7, NULL, 0); - uint32 greyi = (uint32) strtoul(param8, NULL, 0); - uint32 whitei = (uint32) strtoul(param9, NULL, 0); - uint32 greeni = (uint32) strtoul(param10, NULL, 0); - uint32 bluei = (uint32) strtoul(param11, NULL, 0); - uint32 purplei = (uint32) strtoul(param12, NULL, 0); - uint32 orangei = (uint32) strtoul(param13, NULL, 0); - uint32 yellowi = (uint32) strtoul(param14, NULL, 0); - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreytradegoods = '%u' WHERE auctionhouse = '%u'", greytg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentwhitetradegoods = '%u' WHERE auctionhouse = '%u'", whitetg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreentradegoods = '%u' WHERE auctionhouse = '%u'", greentg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentbluetradegoods = '%u' WHERE auctionhouse = '%u'", bluetg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentpurpletradegoods = '%u' WHERE auctionhouse = '%u'", purpletg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentorangetradegoods = '%u' WHERE auctionhouse = '%u'", orangetg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentyellowtradegoods = '%u' WHERE auctionhouse = '%u'", yellowtg, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreyitems = '%u' WHERE auctionhouse = '%u'", greyi, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentwhiteitems = '%u' WHERE auctionhouse = '%u'", whitei, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreenitems = '%u' WHERE auctionhouse = '%u'", greeni, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentblueitems = '%u' WHERE auctionhouse = '%u'", bluei, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentpurpleitems = '%u' WHERE auctionhouse = '%u'", purplei, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentorangeitems = '%u' WHERE auctionhouse = '%u'", orangei, ahMapID); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentyellowitems = '%u' WHERE auctionhouse = '%u'", yellowi, ahMapID); - CharacterDatabase.CommitTransaction(); - config->SetPercentages(greytg, whitetg, greentg, bluetg, purpletg, orangetg, yellowtg, greyi, whitei, greeni, bluei, purplei, orangei, yellowi); - } - break; - case 6: //min prices - { - char * param1 = strtok(args, " "); - uint32 minPrice = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET minprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), minPrice, ahMapID); - config->SetMinPrice(col, minPrice); - } - break; - case 7: //max prices - { - char * param1 = strtok(args, " "); - uint32 maxPrice = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxPrice, ahMapID); - config->SetMaxPrice(col, maxPrice); - } - break; - case 8: //min bid price - { - char * param1 = strtok(args, " "); - uint32 minBidPrice = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET minbidprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), minBidPrice, ahMapID); - config->SetMinBidPrice(col, minBidPrice); - } - break; - case 9: //max bid price - { - char * param1 = strtok(args, " "); - uint32 maxBidPrice = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxbidprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxBidPrice, ahMapID); - config->SetMaxBidPrice(col, maxBidPrice); - } - break; - case 10: //max stacks - { - char * param1 = strtok(args, " "); - uint32 maxStack = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET maxstack%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), maxStack, ahMapID); - config->SetMaxStack(col, maxStack); - } - break; - case 11: //buyer bid prices - { - char * param1 = strtok(args, " "); - uint32 buyerPrice = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerprice%s = '%u' WHERE auctionhouse = '%u'",color.c_str(), buyerPrice, ahMapID); - config->SetBuyerPrice(col, buyerPrice); - } - break; - case 12: //buyer bidding interval - { - char * param1 = strtok(args, " "); - uint32 bidInterval = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerbiddinginterval = '%u' WHERE auctionhouse = '%u'", bidInterval, ahMapID); - config->SetBiddingInterval(bidInterval); - } - break; - case 13: //buyer bids per interval - { - char * param1 = strtok(args, " "); - uint32 bidsPerInterval = (uint32) strtoul(param1, NULL, 0); - CharacterDatabase.PExecute("UPDATE auctionhousebot SET buyerbidsperinterval = '%u' WHERE auctionhouse = '%u'", bidsPerInterval, ahMapID); - config->SetBidsPerInterval(bidsPerInterval); - } - break; - default: - break; - } -} - -void AuctionHouseBot::LoadValues(AHBConfig *config) -{ - if (debug_Out) sLog.outString("Start Settings for %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString()); - if (AHBSeller) - { - //load min and max items - config->SetMinItems(CharacterDatabase.PQuery("SELECT minitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxItems(CharacterDatabase.PQuery("SELECT maxitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - //load percentages - uint32 greytg = CharacterDatabase.PQuery("SELECT percentgreytradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 whitetg = CharacterDatabase.PQuery("SELECT percentwhitetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 greentg = CharacterDatabase.PQuery("SELECT percentgreentradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 bluetg = CharacterDatabase.PQuery("SELECT percentbluetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 purpletg = CharacterDatabase.PQuery("SELECT percentpurpletradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 orangetg = CharacterDatabase.PQuery("SELECT percentorangetradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 yellowtg = CharacterDatabase.PQuery("SELECT percentyellowtradegoods FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 greyi = CharacterDatabase.PQuery("SELECT percentgreyitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 whitei = CharacterDatabase.PQuery("SELECT percentwhiteitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 greeni = CharacterDatabase.PQuery("SELECT percentgreenitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 bluei = CharacterDatabase.PQuery("SELECT percentblueitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 purplei = CharacterDatabase.PQuery("SELECT percentpurpleitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 orangei = CharacterDatabase.PQuery("SELECT percentorangeitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - uint32 yellowi = CharacterDatabase.PQuery("SELECT percentyellowitems FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32(); - config->SetPercentages(greytg, whitetg, greentg, bluetg, purpletg, orangetg, yellowtg, greyi, whitei, greeni, bluei, purplei, orangei, yellowi); - //load min and max prices - config->SetMinPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT minpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT maxpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT minpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT minpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT minpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT minpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT minpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT minpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - //load min and max bid prices - config->SetMinBidPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT minbidpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT maxbidpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinBidPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT minbidpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxbidpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinBidPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT minbidpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxbidpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinBidPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT minbidpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxbidpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinBidPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT minbidpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxbidpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinBidPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT minbidpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxbidpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMinBidPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT minbidpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxBidPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxbidpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - //load max stacks - config->SetMaxStack(AHB_GREY, CharacterDatabase.PQuery("SELECT maxstackgrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxStack(AHB_WHITE, CharacterDatabase.PQuery("SELECT maxstackwhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxStack(AHB_GREEN, CharacterDatabase.PQuery("SELECT maxstackgreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxStack(AHB_BLUE, CharacterDatabase.PQuery("SELECT maxstackblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxStack(AHB_PURPLE, CharacterDatabase.PQuery("SELECT maxstackpurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxStack(AHB_ORANGE, CharacterDatabase.PQuery("SELECT maxstackorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetMaxStack(AHB_YELLOW, CharacterDatabase.PQuery("SELECT maxstackyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if (debug_Out) - { - sLog.outString("minItems = %u", config->GetMinItems()); - sLog.outString("maxItems = %u", config->GetMaxItems()); - sLog.outString("percentGreyTradeGoods = %u", config->GetPercentages(AHB_GREY_TG)); - sLog.outString("percentWhiteTradeGoods = %u", config->GetPercentages(AHB_WHITE_TG)); - sLog.outString("percentGreenTradeGoods = %u", config->GetPercentages(AHB_GREEN_TG)); - sLog.outString("percentBlueTradeGoods = %u", config->GetPercentages(AHB_BLUE_TG)); - sLog.outString("percentPurpleTradeGoods = %u", config->GetPercentages(AHB_PURPLE_TG)); - sLog.outString("percentOrangeTradeGoods = %u", config->GetPercentages(AHB_ORANGE_TG)); - sLog.outString("percentYellowTradeGoods = %u", config->GetPercentages(AHB_YELLOW_TG)); - sLog.outString("percentGreyItems = %u", config->GetPercentages(AHB_GREY_I)); - sLog.outString("percentWhiteItems = %u", config->GetPercentages(AHB_WHITE_I)); - sLog.outString("percentGreenItems = %u", config->GetPercentages(AHB_GREEN_I)); - sLog.outString("percentBlueItems = %u", config->GetPercentages(AHB_BLUE_I)); - sLog.outString("percentPurpleItems = %u", config->GetPercentages(AHB_PURPLE_I)); - sLog.outString("percentOrangeItems = %u", config->GetPercentages(AHB_ORANGE_I)); - sLog.outString("percentYellowItems = %u", config->GetPercentages(AHB_YELLOW_I)); - sLog.outString("minPriceGrey = %u", config->GetMinPrice(AHB_GREY)); - sLog.outString("maxPriceGrey = %u", config->GetMaxPrice(AHB_GREY)); - sLog.outString("minPriceWhite = %u", config->GetMinPrice(AHB_WHITE)); - sLog.outString("maxPriceWhite = %u", config->GetMaxPrice(AHB_WHITE)); - sLog.outString("minPriceGreen = %u", config->GetMinPrice(AHB_GREEN)); - sLog.outString("maxPriceGreen = %u", config->GetMaxPrice(AHB_GREEN)); - sLog.outString("minPriceBlue = %u", config->GetMinPrice(AHB_BLUE)); - sLog.outString("maxPriceBlue = %u", config->GetMaxPrice(AHB_BLUE)); - sLog.outString("minPricePurple = %u", config->GetMinPrice(AHB_PURPLE)); - sLog.outString("maxPricePurple = %u", config->GetMaxPrice(AHB_PURPLE)); - sLog.outString("minPriceOrange = %u", config->GetMinPrice(AHB_ORANGE)); - sLog.outString("maxPriceOrange = %u", config->GetMaxPrice(AHB_ORANGE)); - sLog.outString("minPriceYellow = %u", config->GetMinPrice(AHB_YELLOW)); - sLog.outString("maxPriceYellow = %u", config->GetMaxPrice(AHB_YELLOW)); - sLog.outString("minBidPriceGrey = %u", config->GetMinBidPrice(AHB_GREY)); - sLog.outString("maxBidPriceGrey = %u", config->GetMaxBidPrice(AHB_GREY)); - sLog.outString("minBidPriceWhite = %u", config->GetMinBidPrice(AHB_WHITE)); - sLog.outString("maxBidPriceWhite = %u", config->GetMaxBidPrice(AHB_WHITE)); - sLog.outString("minBidPriceGreen = %u", config->GetMinBidPrice(AHB_GREEN)); - sLog.outString("maxBidPriceGreen = %u", config->GetMaxBidPrice(AHB_GREEN)); - sLog.outString("minBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE)); - sLog.outString("maxBidPriceBlue = %u", config->GetMinBidPrice(AHB_BLUE)); - sLog.outString("minBidPricePurple = %u", config->GetMinBidPrice(AHB_PURPLE)); - sLog.outString("maxBidPricePurple = %u", config->GetMaxBidPrice(AHB_PURPLE)); - sLog.outString("minBidPriceOrange = %u", config->GetMinBidPrice(AHB_ORANGE)); - sLog.outString("maxBidPriceOrange = %u", config->GetMaxBidPrice(AHB_ORANGE)); - sLog.outString("minBidPriceYellow = %u", config->GetMinBidPrice(AHB_YELLOW)); - sLog.outString("maxBidPriceYellow = %u", config->GetMaxBidPrice(AHB_YELLOW)); - sLog.outString("maxStackGrey = %u", config->GetMaxStack(AHB_GREY)); - sLog.outString("maxStackWhite = %u", config->GetMaxStack(AHB_WHITE)); - sLog.outString("maxStackGreen = %u", config->GetMaxStack(AHB_GREEN)); - sLog.outString("maxStackBlue = %u", config->GetMaxStack(AHB_BLUE)); - sLog.outString("maxStackPurple = %u", config->GetMaxStack(AHB_PURPLE)); - sLog.outString("maxStackOrange = %u", config->GetMaxStack(AHB_ORANGE)); - sLog.outString("maxStackYellow = %u", config->GetMaxStack(AHB_YELLOW)); - } - //AuctionHouseEntry const* ahEntry = auctionmgr.GetAuctionHouseEntry(config->GetAHFID()); - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID()); - - config->ResetItemCounts(); - uint32 auctions = auctionHouse->Getcount(); - - if (auctions) - { - for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin(); itr != auctionHouse->GetAuctionsEnd(); ++itr) - { - AuctionEntry *Aentry = itr->second; - Item *item = auctionmgr.GetAItem(Aentry->item_guidlow); - if (item) - { - ItemPrototype const *prototype = item->GetProto(); - if (prototype) - { - switch (prototype->Quality) - { - case 0: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_GREY_TG); - else - config->IncItemCounts(AHB_GREY_I); - break; - case 1: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_WHITE_TG); - else - config->IncItemCounts(AHB_WHITE_I); - break; - case 2: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_GREEN_TG); - else - config->IncItemCounts(AHB_GREEN_I); - break; - case 3: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_BLUE_TG); - else - config->IncItemCounts(AHB_BLUE_I); - break; - case 4: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_PURPLE_TG); - else - config->IncItemCounts(AHB_PURPLE_I); - break; - case 5: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_ORANGE_TG); - else - config->IncItemCounts(AHB_ORANGE_I); - break; - case 6: - if (prototype->Class == ITEM_CLASS_TRADE_GOODS) - config->IncItemCounts(AHB_YELLOW_TG); - else - config->IncItemCounts(AHB_YELLOW_I); - break; - } - } - } - } - } - if (debug_Out) - { - sLog.outString("Current Items in %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString()); - sLog.outString("Grey Trade Goods\t%u\tGrey Items\t%u", config->GetItemCounts(AHB_GREY_TG), config->GetItemCounts(AHB_GREY_I)); - sLog.outString("White Trade Goods\t%u\tWhite Items\t%u", config->GetItemCounts(AHB_WHITE_TG), config->GetItemCounts(AHB_WHITE_I)); - sLog.outString("Green Trade Goods\t%u\tGreen Items\t%u", config->GetItemCounts(AHB_GREEN_TG), config->GetItemCounts(AHB_GREEN_I)); - sLog.outString("Blue Trade Goods\t%u\tBlue Items\t%u", config->GetItemCounts(AHB_BLUE_TG), config->GetItemCounts(AHB_BLUE_I)); - sLog.outString("Purple Trade Goods\t%u\tPurple Items\t%u", config->GetItemCounts(AHB_PURPLE_TG), config->GetItemCounts(AHB_PURPLE_I)); - sLog.outString("Orange Trade Goods\t%u\tOrange Items\t%u", config->GetItemCounts(AHB_ORANGE_TG), config->GetItemCounts(AHB_ORANGE_I)); - sLog.outString("Yellow Trade Goods\t%u\tYellow Items\t%u", config->GetItemCounts(AHB_YELLOW_TG), config->GetItemCounts(AHB_YELLOW_I)); - } - } - if (AHBBuyer) - { - //load buyer bid prices - config->SetBuyerPrice(AHB_GREY, CharacterDatabase.PQuery("SELECT buyerpricegrey FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetBuyerPrice(AHB_WHITE, CharacterDatabase.PQuery("SELECT buyerpricewhite FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetBuyerPrice(AHB_GREEN, CharacterDatabase.PQuery("SELECT buyerpricegreen FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetBuyerPrice(AHB_BLUE, CharacterDatabase.PQuery("SELECT buyerpriceblue FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetBuyerPrice(AHB_PURPLE, CharacterDatabase.PQuery("SELECT buyerpricepurple FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetBuyerPrice(AHB_ORANGE, CharacterDatabase.PQuery("SELECT buyerpriceorange FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - config->SetBuyerPrice(AHB_YELLOW, CharacterDatabase.PQuery("SELECT buyerpriceyellow FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - //load bidding interval - config->SetBiddingInterval(CharacterDatabase.PQuery("SELECT buyerbiddinginterval FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - //load bids per interval - config->SetBidsPerInterval(CharacterDatabase.PQuery("SELECT buyerbidsperinterval FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetUInt32()); - if (debug_Out) - { - sLog.outString("buyerPriceGrey = %u", config->GetBuyerPrice(AHB_GREY)); - sLog.outString("buyerPriceWhite = %u", config->GetBuyerPrice(AHB_WHITE)); - sLog.outString("buyerPriceGreen = %u", config->GetBuyerPrice(AHB_GREEN)); - sLog.outString("buyerPriceBlue = %u", config->GetBuyerPrice(AHB_BLUE)); - sLog.outString("buyerPricePurple = %u", config->GetBuyerPrice(AHB_PURPLE)); - sLog.outString("buyerPriceOrange = %u", config->GetBuyerPrice(AHB_ORANGE)); - sLog.outString("buyerPriceYellow = %u", config->GetBuyerPrice(AHB_YELLOW)); - sLog.outString("buyerBiddingInterval = %u", config->GetBiddingInterval()); - sLog.outString("buyerBidsPerInterval = %u", config->GetBidsPerInterval()); - } - } - if (debug_Out) sLog.outString("End Settings for %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString()); -} diff --git a/src/server/game/AuctionHouseBot.h b/src/server/game/AuctionHouseBot.h deleted file mode 100644 index 208a09aa0b2..00000000000 --- a/src/server/game/AuctionHouseBot.h +++ /dev/null @@ -1,1225 +0,0 @@ -#ifndef AUCTION_HOUSE_BOT_H -#define AUCTION_HOUSE_BOT_H - -#include "World.h" -#include "Config/ConfigEnv.h" -#include "ItemPrototype.h" - -#define AHB_GREY 0 -#define AHB_WHITE 1 -#define AHB_GREEN 2 -#define AHB_BLUE 3 -#define AHB_PURPLE 4 -#define AHB_ORANGE 5 -#define AHB_YELLOW 6 -#define AHB_MAX_QUALITY 6 -#define AHB_GREY_TG 0 -#define AHB_WHITE_TG 1 -#define AHB_GREEN_TG 2 -#define AHB_BLUE_TG 3 -#define AHB_PURPLE_TG 4 -#define AHB_ORANGE_TG 5 -#define AHB_YELLOW_TG 6 -#define AHB_GREY_I 7 -#define AHB_WHITE_I 8 -#define AHB_GREEN_I 9 -#define AHB_BLUE_I 10 -#define AHB_PURPLE_I 11 -#define AHB_ORANGE_I 12 -#define AHB_YELLOW_I 13 - -class AHBConfig -{ -private: - uint32 AHID; - uint32 AHFID; - uint32 minItems; - uint32 maxItems; - uint32 percentGreyTradeGoods; - uint32 percentWhiteTradeGoods; - uint32 percentGreenTradeGoods; - uint32 percentBlueTradeGoods; - uint32 percentPurpleTradeGoods; - uint32 percentOrangeTradeGoods; - uint32 percentYellowTradeGoods; - uint32 percentGreyItems; - uint32 percentWhiteItems; - uint32 percentGreenItems; - uint32 percentBlueItems; - uint32 percentPurpleItems; - uint32 percentOrangeItems; - uint32 percentYellowItems; - uint32 minPriceGrey; - uint32 maxPriceGrey; - uint32 minBidPriceGrey; - uint32 maxBidPriceGrey; - uint32 maxStackGrey; - uint32 minPriceWhite; - uint32 maxPriceWhite; - uint32 minBidPriceWhite; - uint32 maxBidPriceWhite; - uint32 maxStackWhite; - uint32 minPriceGreen; - uint32 maxPriceGreen; - uint32 minBidPriceGreen; - uint32 maxBidPriceGreen; - uint32 maxStackGreen; - uint32 minPriceBlue; - uint32 maxPriceBlue; - uint32 minBidPriceBlue; - uint32 maxBidPriceBlue; - uint32 maxStackBlue; - uint32 minPricePurple; - uint32 maxPricePurple; - uint32 minBidPricePurple; - uint32 maxBidPricePurple; - uint32 maxStackPurple; - uint32 minPriceOrange; - uint32 maxPriceOrange; - uint32 minBidPriceOrange; - uint32 maxBidPriceOrange; - uint32 maxStackOrange; - uint32 minPriceYellow; - uint32 maxPriceYellow; - uint32 minBidPriceYellow; - uint32 maxBidPriceYellow; - uint32 maxStackYellow; - - uint32 buyerPriceGrey; - uint32 buyerPriceWhite; - uint32 buyerPriceGreen; - uint32 buyerPriceBlue; - uint32 buyerPricePurple; - uint32 buyerPriceOrange; - uint32 buyerPriceYellow; - uint32 buyerBiddingInterval; - uint32 buyerBidsPerInterval; - - uint32 greytgp; - uint32 whitetgp; - uint32 greentgp; - uint32 bluetgp; - uint32 purpletgp; - uint32 orangetgp; - uint32 yellowtgp; - uint32 greyip; - uint32 whiteip; - uint32 greenip; - uint32 blueip; - uint32 purpleip; - uint32 orangeip; - uint32 yellowip; - - uint32 greyTGoods; - uint32 whiteTGoods; - uint32 greenTGoods; - uint32 blueTGoods; - uint32 purpleTGoods; - uint32 orangeTGoods; - uint32 yellowTGoods; - - uint32 greyItems; - uint32 whiteItems; - uint32 greenItems; - uint32 blueItems; - uint32 purpleItems; - uint32 orangeItems; - uint32 yellowItems; - -public: - AHBConfig(uint32 ahid) - { - AHID = ahid; - switch(ahid) - { - case 2: - AHFID = 55; - break; - case 6: - AHFID = 29; - break; - case 7: - AHFID = 120; - break; - default: - AHFID = 120; - break; - } - } - AHBConfig() - { - } - uint32 GetAHID() - { - return AHID; - } - uint32 GetAHFID() - { - return AHFID; - } - void SetMinItems(uint32 value) - { - minItems = value; - } - uint32 GetMinItems() - { - if ((minItems == 0) && (maxItems)) - return maxItems; - else if ((maxItems) && (minItems > maxItems)) - return maxItems; - else - return minItems; - } - void SetMaxItems(uint32 value) - { - maxItems = value; - CalculatePercents(); - } - uint32 GetMaxItems() - { - return maxItems; - } - void SetPercentages(uint32 greytg, uint32 whitetg, uint32 greentg, uint32 bluetg, uint32 purpletg, uint32 orangetg, uint32 yellowtg, uint32 greyi, uint32 whitei, uint32 greeni, uint32 bluei, uint32 purplei, uint32 orangei, uint32 yellowi) - { - uint32 totalPercent = greytg + whitetg + greentg + bluetg + purpletg + orangetg + yellowtg + greyi + whitei + greeni + bluei + purplei + orangei + yellowi; - - if (totalPercent == 0) - { - maxItems = 0; - } - else if (totalPercent != 100) - { - greytg = 0; - whitetg = 27; - greentg = 12; - bluetg = 10; - purpletg = 1; - orangetg = 0; - yellowtg = 0; - greyi = 0; - whitei = 10; - greeni = 30; - bluei = 8; - purplei = 2; - orangei = 0; - yellowi = 0; - } - percentGreyTradeGoods = greytg; - percentWhiteTradeGoods = whitetg; - percentGreenTradeGoods = greentg; - percentBlueTradeGoods = bluetg; - percentPurpleTradeGoods = purpletg; - percentOrangeTradeGoods = orangetg; - percentYellowTradeGoods = yellowtg; - percentGreyItems = greyi; - percentWhiteItems = whitei; - percentGreenItems = greeni; - percentBlueItems = bluei; - percentPurpleItems = purplei; - percentOrangeItems = orangei; - percentYellowItems = yellowi; - CalculatePercents(); - } - uint32 GetPercentages(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: - return percentGreyTradeGoods; - break; - case AHB_WHITE_TG: - return percentWhiteTradeGoods; - break; - case AHB_GREEN_TG: - return percentGreenTradeGoods; - break; - case AHB_BLUE_TG: - return percentBlueTradeGoods; - break; - case AHB_PURPLE_TG: - return percentPurpleTradeGoods; - break; - case AHB_ORANGE_TG: - return percentOrangeTradeGoods; - break; - case AHB_YELLOW_TG: - return percentYellowTradeGoods; - break; - case AHB_GREY_I: - return percentGreyItems; - break; - case AHB_WHITE_I: - return percentWhiteItems; - break; - case AHB_GREEN_I: - return percentGreenItems; - break; - case AHB_BLUE_I: - return percentBlueItems; - break; - case AHB_PURPLE_I: - return percentPurpleItems; - break; - case AHB_ORANGE_I: - return percentOrangeItems; - break; - case AHB_YELLOW_I: - return percentYellowItems; - break; - default: - return 0; - break; - } - } - void SetMinPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: - minPriceGrey = value; - break; - case AHB_WHITE: - minPriceWhite = value; - break; - case AHB_GREEN: - minPriceGreen = value; - break; - case AHB_BLUE: - minPriceBlue = value; - break; - case AHB_PURPLE: - minPricePurple = value; - break; - case AHB_ORANGE: - minPriceOrange = value; - break; - case AHB_YELLOW: - minPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMinPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: - { - if (minPriceGrey == 0) - return 100; - else if (minPriceGrey > maxPriceGrey) - return maxPriceGrey; - else - return minPriceGrey; - break; - } - case AHB_WHITE: - { - if (minPriceWhite == 0) - return 150; - else if (minPriceWhite > maxPriceWhite) - return maxPriceWhite; - else - return minPriceWhite; - break; - } - case AHB_GREEN: - { - if (minPriceGreen == 0) - return 200; - else if (minPriceGreen > maxPriceGreen) - return maxPriceGreen; - else - return minPriceGreen; - break; - } - case AHB_BLUE: - { - if (minPriceBlue == 0) - return 250; - else if (minPriceBlue > maxPriceBlue) - return maxPriceBlue; - else - return minPriceBlue; - break; - } - case AHB_PURPLE: - { - if (minPricePurple == 0) - return 300; - else if (minPricePurple > maxPricePurple) - return maxPricePurple; - else - return minPricePurple; - break; - } - case AHB_ORANGE: - { - if (minPriceOrange == 0) - return 400; - else if (minPriceOrange > maxPriceOrange) - return maxPriceOrange; - else - return minPriceOrange; - break; - } - case AHB_YELLOW: - { - if (minPriceYellow == 0) - return 500; - else if (minPriceYellow > maxPriceYellow) - return maxPriceYellow; - else - return minPriceYellow; - break; - } - default: - { - return 0; - break; - } - } - } - void SetMaxPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: - maxPriceGrey = value; - break; - case AHB_WHITE: - maxPriceWhite = value; - break; - case AHB_GREEN: - maxPriceGreen = value; - break; - case AHB_BLUE: - maxPriceBlue = value; - break; - case AHB_PURPLE: - maxPricePurple = value; - break; - case AHB_ORANGE: - maxPriceOrange = value; - break; - case AHB_YELLOW: - maxPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMaxPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: - { - if (maxPriceGrey == 0) - return 150; - else - return maxPriceGrey; - break; - } - case AHB_WHITE: - { - if (maxPriceWhite == 0) - return 250; - else - return maxPriceWhite; - break; - } - case AHB_GREEN: - { - if (maxPriceGreen == 0) - return 300; - else - return maxPriceGreen; - break; - } - case AHB_BLUE: - { - if (maxPriceBlue == 0) - return 350; - else - return maxPriceBlue; - break; - } - case AHB_PURPLE: - { - if (maxPricePurple == 0) - return 450; - else - return maxPricePurple; - break; - } - case AHB_ORANGE: - { - if (maxPriceOrange == 0) - return 550; - else - return maxPriceOrange; - break; - } - case AHB_YELLOW: - { - if (maxPriceYellow == 0) - return 650; - else - return maxPriceYellow; - break; - } - default: - { - return 0; - break; - } - } - } - void SetMinBidPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: - minBidPriceGrey = value; - break; - case AHB_WHITE: - minBidPriceWhite = value; - break; - case AHB_GREEN: - minBidPriceGreen = value; - break; - case AHB_BLUE: - minBidPriceBlue = value; - break; - case AHB_PURPLE: - minBidPricePurple = value; - break; - case AHB_ORANGE: - minBidPriceOrange = value; - break; - case AHB_YELLOW: - minBidPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMinBidPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: - { - if (minBidPriceGrey > 100) - return 100; - else - return minBidPriceGrey; - break; - } - case AHB_WHITE: - { - if (minBidPriceWhite > 100) - return 100; - else - return minBidPriceWhite; - break; - } - case AHB_GREEN: - { - if (minBidPriceGreen > 100) - return 100; - else - return minBidPriceGreen; - break; - } - case AHB_BLUE: - { - if (minBidPriceBlue > 100) - return 100; - else - return minBidPriceBlue; - break; - } - case AHB_PURPLE: - { - if (minBidPricePurple > 100) - return 100; - else - return minBidPricePurple; - break; - } - case AHB_ORANGE: - { - if (minBidPriceOrange > 100) - return 100; - else - return minBidPriceOrange; - break; - } - case AHB_YELLOW: - { - if (minBidPriceYellow > 100) - return 100; - else - return minBidPriceYellow; - break; - } - default: - { - return 0; - break; - } - } - } - void SetMaxBidPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: - maxBidPriceGrey = value; - break; - case AHB_WHITE: - maxBidPriceWhite = value; - break; - case AHB_GREEN: - maxBidPriceGreen = value; - break; - case AHB_BLUE: - maxBidPriceBlue = value; - break; - case AHB_PURPLE: - maxBidPricePurple = value; - break; - case AHB_ORANGE: - maxBidPriceOrange = value; - break; - case AHB_YELLOW: - maxBidPriceYellow = value; - break; - default: - break; - } - } - uint32 GetMaxBidPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: - { - if (maxBidPriceGrey > 100) - return 100; - else - return maxBidPriceGrey; - break; - } - case AHB_WHITE: - { - if (maxBidPriceWhite > 100) - return 100; - else - return maxBidPriceWhite; - break; - } - case AHB_GREEN: - { - if (maxBidPriceGreen > 100) - return 100; - else - return maxBidPriceGreen; - break; - } - case AHB_BLUE: - { - if (maxBidPriceBlue > 100) - return 100; - else - return maxBidPriceBlue; - break; - } - case AHB_PURPLE: - { - if (maxBidPricePurple > 100) - return 100; - else - return maxBidPricePurple; - break; - } - case AHB_ORANGE: - { - if (maxBidPriceOrange > 100) - return 100; - else - return maxBidPriceOrange; - break; - } - case AHB_YELLOW: - { - if (maxBidPriceYellow > 100) - return 100; - else - return maxBidPriceYellow; - break; - } - default: - { - return 0; - break; - } - } - } - void SetMaxStack(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: - maxStackGrey = value; - break; - case AHB_WHITE: - maxStackWhite = value; - break; - case AHB_GREEN: - maxStackGreen = value; - break; - case AHB_BLUE: - maxStackBlue = value; - break; - case AHB_PURPLE: - maxStackPurple = value; - break; - case AHB_ORANGE: - maxStackOrange = value; - break; - case AHB_YELLOW: - maxStackYellow = value; - break; - default: - break; - } - } - uint32 GetMaxStack(uint32 color) - { - switch(color) - { - case AHB_GREY: - { - return maxStackGrey; - break; - } - case AHB_WHITE: - { - return maxStackWhite; - break; - } - case AHB_GREEN: - { - return maxStackGreen; - break; - } - case AHB_BLUE: - { - return maxStackBlue; - break; - } - case AHB_PURPLE: - { - return maxStackPurple; - break; - } - case AHB_ORANGE: - { - return maxStackOrange; - break; - } - case AHB_YELLOW: - { - return maxStackYellow; - break; - } - default: - { - return 0; - break; - } - } - } - void SetBuyerPrice(uint32 color, uint32 value) - { - switch(color) - { - case AHB_GREY: - buyerPriceGrey = value; - break; - case AHB_WHITE: - buyerPriceWhite = value; - break; - case AHB_GREEN: - buyerPriceGreen = value; - break; - case AHB_BLUE: - buyerPriceBlue = value; - break; - case AHB_PURPLE: - buyerPricePurple = value; - break; - case AHB_ORANGE: - buyerPriceOrange = value; - break; - case AHB_YELLOW: - buyerPriceYellow = value; - break; - default: - break; - } - } - uint32 GetBuyerPrice(uint32 color) - { - switch(color) - { - case AHB_GREY: - return buyerPriceGrey; - break; - case AHB_WHITE: - return buyerPriceWhite; - break; - case AHB_GREEN: - return buyerPriceGreen; - break; - case AHB_BLUE: - return buyerPriceBlue; - break; - case AHB_PURPLE: - return buyerPricePurple; - break; - case AHB_ORANGE: - return buyerPriceOrange; - break; - case AHB_YELLOW: - return buyerPriceYellow; - break; - default: - return 0; - break; - } - } - void SetBiddingInterval(uint32 value) - { - buyerBiddingInterval = value; - } - uint32 GetBiddingInterval() - { - return buyerBiddingInterval; - } - void CalculatePercents() - { - greytgp = (uint32) (((double)percentGreyTradeGoods / 100.0) * maxItems); - whitetgp = (uint32) (((double)percentWhiteTradeGoods / 100.0) * maxItems); - greentgp = (uint32) (((double)percentGreenTradeGoods / 100.0) * maxItems); - bluetgp = (uint32) (((double)percentBlueTradeGoods / 100.0) * maxItems); - purpletgp = (uint32) (((double)percentPurpleTradeGoods / 100.0) * maxItems); - orangetgp = (uint32) (((double)percentOrangeTradeGoods / 100.0) * maxItems); - yellowtgp = (uint32) (((double)percentYellowTradeGoods / 100.0) * maxItems); - greyip = (uint32) (((double)percentGreyItems / 100.0) * maxItems); - whiteip = (uint32) (((double)percentWhiteItems / 100.0) * maxItems); - greenip = (uint32) (((double)percentGreenItems / 100.0) * maxItems); - blueip = (uint32) (((double)percentBlueItems / 100.0) * maxItems); - purpleip = (uint32) (((double)percentPurpleItems / 100.0) * maxItems); - orangeip = (uint32) (((double)percentOrangeItems / 100.0) * maxItems); - yellowip = (uint32) (((double)percentYellowItems / 100.0) * maxItems); - uint32 total = greytgp + whitetgp + greentgp + bluetgp + purpletgp + orangetgp + yellowtgp + greyip + whiteip + greenip + blueip + purpleip + orangeip + yellowip; - int32 diff = (maxItems - total); - if (diff < 0) - { - if ((whiteip - diff) > 0) - whiteip -= diff; - else if ((greenip - diff) > 0) - greenip -= diff; - } - else if (diff < 0) - { - whiteip += diff; - } - } - uint32 GetPercents(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: - return greytgp; - break; - case AHB_WHITE_TG: - return whitetgp; - break; - case AHB_GREEN_TG: - return greentgp; - break; - case AHB_BLUE_TG: - return bluetgp; - break; - case AHB_PURPLE_TG: - return purpletgp; - break; - case AHB_ORANGE_TG: - return orangetgp; - break; - case AHB_YELLOW_TG: - return yellowtgp; - break; - case AHB_GREY_I: - return greyip; - break; - case AHB_WHITE_I: - return whiteip; - break; - case AHB_GREEN_I: - return greenip; - break; - case AHB_BLUE_I: - return blueip; - break; - case AHB_PURPLE_I: - return purpleip; - break; - case AHB_ORANGE_I: - return orangeip; - break; - case AHB_YELLOW_I: - return yellowip; - break; - default: - return 0; - break; - } - } - - void DecItemCounts(uint32 Class, uint32 Quality) - { - switch(Class) - { - case ITEM_CLASS_TRADE_GOODS: - DecItemCounts(Quality); - break; - default: - DecItemCounts(Quality + 7); - break; - } - } - - void DecItemCounts(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: - --greyTGoods; - break; - case AHB_WHITE_TG: - --whiteTGoods; - break; - case AHB_GREEN_TG: - --greenTGoods; - break; - case AHB_BLUE_TG: - --blueTGoods; - break; - case AHB_PURPLE_TG: - --purpleTGoods; - break; - case AHB_ORANGE_TG: - --orangeTGoods; - break; - case AHB_YELLOW_TG: - --yellowTGoods; - break; - case AHB_GREY_I: - --greyItems; - break; - case AHB_WHITE_I: - --whiteItems; - break; - case AHB_GREEN_I: - --greenItems; - break; - case AHB_BLUE_I: - --blueItems; - break; - case AHB_PURPLE_I: - --purpleItems; - break; - case AHB_ORANGE_I: - --orangeItems; - break; - case AHB_YELLOW_I: - --yellowItems; - break; - default: - break; - } - } - - void IncItemCounts(uint32 Class, uint32 Quality) - { - switch(Class) - { - case ITEM_CLASS_TRADE_GOODS: - IncItemCounts(Quality); - break; - default: - IncItemCounts(Quality + 7); - break; - } - } - - void IncItemCounts(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: - ++greyTGoods; - break; - case AHB_WHITE_TG: - ++whiteTGoods; - break; - case AHB_GREEN_TG: - ++greenTGoods; - break; - case AHB_BLUE_TG: - ++blueTGoods; - break; - case AHB_PURPLE_TG: - ++purpleTGoods; - break; - case AHB_ORANGE_TG: - ++orangeTGoods; - break; - case AHB_YELLOW_TG: - ++yellowTGoods; - break; - case AHB_GREY_I: - ++greyItems; - break; - case AHB_WHITE_I: - ++whiteItems; - break; - case AHB_GREEN_I: - ++greenItems; - break; - case AHB_BLUE_I: - ++blueItems; - break; - case AHB_PURPLE_I: - ++purpleItems; - break; - case AHB_ORANGE_I: - ++orangeItems; - break; - case AHB_YELLOW_I: - ++yellowItems; - break; - default: - break; - } - } - - void ResetItemCounts() - { - greyTGoods = 0; - whiteTGoods = 0; - greenTGoods = 0; - blueTGoods = 0; - purpleTGoods = 0; - orangeTGoods = 0; - yellowTGoods = 0; - - greyItems = 0; - whiteItems = 0; - greenItems = 0; - blueItems = 0; - purpleItems = 0; - orangeItems = 0; - yellowItems = 0; - } - - uint32 TotalItemCounts() - { - return( - greyTGoods + - whiteTGoods + - greenTGoods + - blueTGoods + - purpleTGoods + - orangeTGoods + - yellowTGoods + - - greyItems + - whiteItems + - greenItems + - blueItems + - purpleItems + - orangeItems + - yellowItems); - } - - uint32 GetItemCounts(uint32 color) - { - switch(color) - { - case AHB_GREY_TG: - return greyTGoods; - break; - case AHB_WHITE_TG: - return whiteTGoods; - break; - case AHB_GREEN_TG: - return greenTGoods; - break; - case AHB_BLUE_TG: - return blueTGoods; - break; - case AHB_PURPLE_TG: - return purpleTGoods; - break; - case AHB_ORANGE_TG: - return orangeTGoods; - break; - case AHB_YELLOW_TG: - return yellowTGoods; - break; - case AHB_GREY_I: - return greyItems; - break; - case AHB_WHITE_I: - return whiteItems; - break; - case AHB_GREEN_I: - return greenItems; - break; - case AHB_BLUE_I: - return blueItems; - break; - case AHB_PURPLE_I: - return purpleItems; - break; - case AHB_ORANGE_I: - return orangeItems; - break; - case AHB_YELLOW_I: - return yellowItems; - break; - default: - return 0; - break; - } - } - void SetBidsPerInterval(uint32 value) - { - buyerBidsPerInterval = value; - } - uint32 GetBidsPerInterval() - { - return buyerBidsPerInterval; - } - ~AHBConfig() - { - } -}; -class AuctionHouseBot -{ -private: - - bool debug_Out; - bool debug_Out_Filters; - - bool AHBSeller; - bool AHBBuyer; - bool BuyMethod; - bool SellMethod; - - uint32 AHBplayerAccount; - uint32 AHBplayerGUID; - uint32 ItemsPerCycle; - - //Begin Filters - - bool Vendor_Items; - bool Loot_Items; - bool Other_Items; - bool Vendor_TGs; - bool Loot_TGs; - bool Other_TGs; - - bool No_Bind; - bool Bind_When_Picked_Up; - bool Bind_When_Equipped; - bool Bind_When_Use; - bool Bind_Quest_Item; - - bool DisableBeta_PTR_Unused; - bool DisablePermEnchant; - bool DisableConjured; - bool DisableGems; - bool DisableMoney; - bool DisableMoneyLoot; - bool DisableLootable; - bool DisableKeys; - bool DisableDuration; - bool DisableBOP_Or_Quest_NoReqLevel; - - bool DisableWarriorItems; - bool DisablePaladinItems; - bool DisableHunterItems; - bool DisableRogueItems; - bool DisablePriestItems; - bool DisableDKItems; - bool DisableShamanItems; - bool DisableMageItems; - bool DisableWarlockItems; - bool DisableUnusedClassItems; - bool DisableDruidItems; - - uint32 DisableItemsBelowLevel; - uint32 DisableItemsAboveLevel; - uint32 DisableTGsBelowLevel; - uint32 DisableTGsAboveLevel; - uint32 DisableItemsBelowGUID; - uint32 DisableItemsAboveGUID; - uint32 DisableTGsBelowGUID; - uint32 DisableTGsAboveGUID; - uint32 DisableItemsBelowReqLevel; - uint32 DisableItemsAboveReqLevel; - uint32 DisableTGsBelowReqLevel; - uint32 DisableTGsAboveReqLevel; - uint32 DisableItemsBelowReqSkillRank; - uint32 DisableItemsAboveReqSkillRank; - uint32 DisableTGsBelowReqSkillRank; - uint32 DisableTGsAboveReqSkillRank; - - //End Filters - - AHBConfig AllianceConfig; - AHBConfig HordeConfig; - AHBConfig NeutralConfig; - - time_t _lastrun_a; - time_t _lastrun_h; - time_t _lastrun_n; - - inline uint32 minValue(uint32 a, uint32 b) { return a <= b ? a : b; }; - void addNewAuctions(Player *AHBplayer, AHBConfig *config); - void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session); - -public: - AuctionHouseBot(); - ~AuctionHouseBot(); - void Update(); - void Initialize(); - void LoadValues(AHBConfig*); - void DecrementItemCounts(AuctionEntry* ah, uint32 item_template); - void IncrementItemCounts(AuctionEntry* ah); - void Commands(uint32, uint32, uint32, char*); - uint32 GetAHBplayerGUID() { return AHBplayerGUID; }; -}; - -#define auctionbot Trinity::Singleton::Instance() - -#endif diff --git a/src/server/game/AuctionHouseHandler.cpp b/src/server/game/AuctionHouseHandler.cpp deleted file mode 100644 index 8fec3b7df1d..00000000000 --- a/src/server/game/AuctionHouseHandler.cpp +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ObjectMgr.h" -#include "Player.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" - -#include "AuctionHouseBot.h" -#include "AuctionHouseMgr.h" -#include "Log.h" -#include "Opcodes.h" -#include "UpdateMask.h" -#include "Util.h" - -//please DO NOT use iterator++, because it is slower than ++iterator!!! -//post-incrementation is always slower than pre-incrementation ! - -//void called when player click on auctioneer npc -void WorldSession::HandleAuctionHelloOpcode(WorldPacket & recv_data) -{ - uint64 guid; //NPC guid - recv_data >> guid; - - Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); - if (!unit) - { - sLog.outDebug("WORLD: HandleAuctionHelloOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - SendAuctionHello(guid, unit); -} - -//this void causes that auction window is opened -void WorldSession::SendAuctionHello(uint64 guid, Creature* unit) -{ - if (GetPlayer()->getLevel() < sWorld.getConfig(CONFIG_AUCTION_LEVEL_REQ)) - { - SendNotification(GetTrinityString(LANG_AUCTION_REQ), sWorld.getConfig(CONFIG_AUCTION_LEVEL_REQ)); - return; - } - - AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->getFaction()); - if (!ahEntry) - return; - - WorldPacket data(MSG_AUCTION_HELLO, 12); - data << uint64(guid); - data << uint32(ahEntry->houseId); - data << uint8(1); // 3.3.3: 1 - AH enabled, 0 - AH disabled - SendPacket(&data); -} - -//call this method when player bids, creates, or deletes auction -void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError) -{ - WorldPacket data(SMSG_AUCTION_COMMAND_RESULT, 16); - data << auctionId; - data << Action; - data << ErrorCode; - if (!ErrorCode && Action) - data << bidError; //when bid, then send 0, once... - SendPacket(&data); -} - -//this function sends notification, if bidder is online -void WorldSession::SendAuctionBidderNotification(uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template) -{ - WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4)); - data << uint32(location); - data << uint32(auctionId); - data << uint64(bidder); - data << uint32(bidSum); - data << uint32(diff); - data << uint32(item_template); - data << uint32(0); - SendPacket(&data); -} - -//this void causes on client to display: "Your auction sold" -void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction) -{ - WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (7*4)); - data << auction->Id; - data << auction->bid; - data << (uint32) 0; //unk - data << (uint32) 0; //unk - data << (uint32) 0; //unk - data << auction->item_template; - data << (uint32) 0; //unk - SendPacket(&data); -} - -//this function sends mail to old bidder -void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice) -{ - uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder,0, HIGHGUID_PLAYER); - Player *oldBidder = objmgr.GetPlayer(oldBidder_guid); - - uint32 oldBidder_accId = 0; - if (!oldBidder) - oldBidder_accId = objmgr.GetPlayerAccountIdByGUID(oldBidder_guid); - - // old bidder exist - if (oldBidder || oldBidder_accId) - { - std::ostringstream msgAuctionOutbiddedSubject; - msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED << ":0:0"; - - if (oldBidder && !_player) - oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, auctionbot.GetAHBplayerGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); - - if (oldBidder && _player) - oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); - - MailDraft(msgAuctionOutbiddedSubject.str(), "") // TODO: fix body - .AddMoney(auction->bid) - .SendMailTo(MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED); - } -} - -//this function sends mail, when auction is cancelled to old bidder -void WorldSession::SendAuctionCancelledToBidderMail(AuctionEntry* auction) -{ - uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); - Player *bidder = objmgr.GetPlayer(bidder_guid); - - uint32 bidder_accId = 0; - if (!bidder) - bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid); - - // bidder exist - if (bidder || bidder_accId) - { - std::ostringstream msgAuctionCancelledSubject; - msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0"; - - MailDraft(msgAuctionCancelledSubject.str(), "") // TODO: fix body - .AddMoney(auction->bid) - .SendMailTo(MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED); - } -} - -//this void creates new auction and adds auction to some auctionhouse -void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data) -{ - uint64 auctioneer, item; - uint32 etime, bid, buyout; - recv_data >> auctioneer; - recv_data.read_skip(); // const 1? - recv_data >> item; - recv_data.read_skip(); // unk 3.2.2, const 1? - recv_data >> bid; - recv_data >> buyout; - recv_data >> etime; - - Player *pl = GetPlayer(); - - if (!item || !bid || !etime) - return; //check for cheaters - - Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); - return; - } - - AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(pCreature->getFaction()); - if (!auctionHouseEntry) - { - sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer))); - return; - } - - sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime); - - // client send time in minutes, convert to common used sec time - etime *= MINUTE; - - sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime); - - // client understand only 3 auction time - switch(etime) - { - case 1*MIN_AUCTION_TIME: - case 2*MIN_AUCTION_TIME: - case 4*MIN_AUCTION_TIME: - break; - default: - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - Item *it = pl->GetItemByGuid(item); - //do not allow to sell already auctioned items - if (auctionmgr.GetAItem(GUID_LOPART(item))) - { - sLog.outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", pl->GetName(), GUID_LOPART(item)); - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); - return; - } - // prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction) - if (!it) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND); - return; - } - - if (!it->CanBeTraded()) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); - return; - } - - if (it->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); - return; - } - - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); - - //we have to take deposit : - uint32 deposit = auctionmgr.GetAuctionDeposit(auctionHouseEntry, etime, it); - if (pl->GetMoney() < deposit) - { - SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY); - return; - } - - if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", - GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount()); - } - - pl->ModifyMoney(-int32(deposit)); - - uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME)); - - AuctionEntry *AH = new AuctionEntry; - AH->Id = objmgr.GenerateAuctionID(); - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - AH->auctioneer = 23442; - else - AH->auctioneer = GUID_LOPART(auctioneer); - AH->item_guidlow = GUID_LOPART(item); - AH->item_template = it->GetEntry(); - AH->owner = pl->GetGUIDLow(); - AH->startbid = bid; - AH->bidder = 0; - AH->bid = 0; - AH->buyout = buyout; - AH->expire_time = time(NULL) + auction_time; - AH->deposit = deposit; - AH->auctionHouseEntry = auctionHouseEntry; - - sLog.outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", GUID_LOPART(item), AH->auctioneer, bid, buyout, auction_time, AH->GetHouseId()); - auctionmgr.AddAItem(it); - auctionHouse->AddAuction(AH); - - pl->MoveItemFromInventory(it->GetBagSlot(), it->GetSlot(), true); - - CharacterDatabase.BeginTransaction(); - it->DeleteFromInventoryDB(); - it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone - AH->SaveToDB(); - pl->SaveInventoryAndGoldToDB(); - CharacterDatabase.CommitTransaction(); - - SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK); - - GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); -} - -//this function is called when client bids or buys out auction -void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data) -{ - uint64 auctioneer; - uint32 auctionId; - uint32 price; - recv_data >> auctioneer; - recv_data >> auctionId >> price; - - if (!auctionId || !price) - return; //check for cheaters - - Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - AuctionHouseObject *auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); - - AuctionEntry *auction = auctionHouse->GetAuction(auctionId); - Player *pl = GetPlayer(); - - if (!auction || auction->owner == pl->GetGUIDLow()) - { - //you cannot bid your own auction: - SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); - return; - } - - // impossible have online own another character (use this for speedup check in case online owner) - Player* auction_owner = objmgr.GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)); - if (!auction_owner && objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->GetSession()->GetAccountId()) - { - //you cannot bid your another character auction: - SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); - return; - } - - // cheating - if (price <= auction->bid || price < auction->startbid) - return; - - // price too low for next bid if not buyout - if ((price < auction->buyout || auction->buyout == 0) && - price < auction->bid + auction->GetAuctionOutBid()) - { - //auction has already higher bid, client tests it! - return; - } - - if (price > pl->GetMoney()) - { - //you don't have enought money!, client tests! - //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); - return; - } - - if (price < auction->buyout || auction->buyout == 0) - { - if (auction->bidder > 0) - { - if (auction->bidder == pl->GetGUIDLow()) - pl->ModifyMoney(-int32(price - auction->bid)); - else - { - // mail to last bidder and return money - SendAuctionOutbiddedMail(auction, price); - pl->ModifyMoney(-int32(price)); - } - } - else - pl->ModifyMoney(-int32(price)); - - auction->bidder = pl->GetGUIDLow(); - auction->bid = price; - GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); - - // after this update we should save player's money ... - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); - - SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0); - } - else - { - //buyout: - if (pl->GetGUIDLow() == auction->bidder) - pl->ModifyMoney(-int32(auction->buyout - auction->bid)); - else - { - pl->ModifyMoney(-int32(auction->buyout)); - if (auction->bidder) //buyout for bidded auction .. - SendAuctionOutbiddedMail(auction, auction->buyout); - } - auction->bidder = pl->GetGUIDLow(); - auction->bid = auction->buyout; - GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); - - auctionmgr.SendAuctionSalePendingMail(auction); - auctionmgr.SendAuctionSuccessfulMail(auction); - auctionmgr.SendAuctionWonMail(auction); - - SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK); - - CharacterDatabase.BeginTransaction(); - auction->DeleteFromDB(); - uint32 item_template = auction->item_template; - auctionmgr.RemoveAItem(auction->item_guidlow); - auctionHouse->RemoveAuction(auction, item_template); - } - pl->SaveInventoryAndGoldToDB(); - CharacterDatabase.CommitTransaction(); -} - -//this void is called when auction_owner cancels his auction -void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data) -{ - uint64 auctioneer; - uint32 auctionId; - recv_data >> auctioneer; - recv_data >> auctionId; - //sLog.outDebug("Cancel AUCTION AuctionID: %u", auctionId); - - Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); - - AuctionEntry *auction = auctionHouse->GetAuction(auctionId); - Player *pl = GetPlayer(); - - if (auction && auction->owner == pl->GetGUIDLow()) - { - Item *pItem = auctionmgr.GetAItem(auction->item_guidlow); - if (pItem) - { - if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid - { - uint32 auctionCut = auction->GetAuctionCut(); - if (pl->GetMoney() < auctionCut) //player doesn't have enough money, maybe message needed - return; - //some auctionBidderNotification would be needed, but don't know that parts.. - SendAuctionCancelledToBidderMail(auction); - pl->ModifyMoney(-int32(auctionCut)); - } - // Return the item by mail - std::ostringstream msgAuctionCanceledOwner; - msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0"; - - // item will deleted or added to received mail list - MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body - .AddItem(pItem) - .SendMailTo(pl, auction, MAIL_CHECK_MASK_COPIED); - } - else - { - sLog.outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow); - SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR); - return; - } - } - else - { - SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR); - //this code isn't possible ... maybe there should be assert - sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId); - return; - } - - //inform player, that auction is removed - SendAuctionCommandResult(auction->Id, AUCTION_CANCEL, AUCTION_OK); - - // Now remove the auction - CharacterDatabase.BeginTransaction(); - pl->SaveInventoryAndGoldToDB(); - auction->DeleteFromDB(); - uint32 item_template = auction->item_template; - auctionmgr.RemoveAItem(auction->item_guidlow); - auctionHouse->RemoveAuction(auction, item_template); - CharacterDatabase.CommitTransaction(); -} - -//called when player lists his bids -void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data) -{ - uint64 guid; //NPC guid - uint32 listfrom; //page of auctions - uint32 outbiddedCount; //count of outbidded auctions - - recv_data >> guid; - recv_data >> listfrom; // not used in fact (this list not have page control in client) - recv_data >> outbiddedCount; - if (recv_data.size() != (16 + outbiddedCount * 4)) - { - sLog.outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(),(16 + outbiddedCount * 4)); - outbiddedCount = 0; - } - - Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandleAuctionListBidderItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); - - WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4)); - Player *pl = GetPlayer(); - data << (uint32) 0; //add 0 as count - uint32 count = 0; - uint32 totalcount = 0; - while (outbiddedCount > 0) //add all data, which client requires - { - --outbiddedCount; - uint32 outbiddedAuctionId; - recv_data >> outbiddedAuctionId; - AuctionEntry * auction = auctionHouse->GetAuction(outbiddedAuctionId); - if (auction && auction->BuildAuctionInfo(data)) - { - ++totalcount; - ++count; - } - } - - auctionHouse->BuildListBidderItems(data,pl,count,totalcount); - data.put(0, count); // add count to placeholder - data << totalcount; - data << (uint32)300; //unk 2.3.0 - SendPacket(&data); -} - -//this void sends player info about his auctions -void WorldSession::HandleAuctionListOwnerItems(WorldPacket & recv_data) -{ - uint32 listfrom; - uint64 guid; - - recv_data >> guid; - recv_data >> listfrom; // not used in fact (this list not have page control in client) - - Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandleAuctionListOwnerItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); - - WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4)); - data << (uint32) 0; // amount place holder - - uint32 count = 0; - uint32 totalcount = 0; - - auctionHouse->BuildListOwnerItems(data,_player,count,totalcount); - data.put(0, count); - data << (uint32) totalcount; - data << (uint32) 0; - SendPacket(&data); -} - -//this void is called when player clicks on search button -void WorldSession::HandleAuctionListItems(WorldPacket & recv_data) -{ - std::string searchedname; - uint8 levelmin, levelmax, usable; - uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality; - uint64 guid; - - recv_data >> guid; - recv_data >> listfrom; // start, used for page control listing by 50 elements - recv_data >> searchedname; - - recv_data >> levelmin >> levelmax; - recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; - recv_data >> quality >> usable; - - recv_data.read_skip(16); // unknown 16 bytes: 00 07 01 00 00 01 05 00 06 00 09 01 08 00 03 00 - - Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_AUCTIONEER); - if (!pCreature) - { - sLog.outDebug("WORLD: HandleAuctionListItems - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); - - //sLog.outDebug("Auctionhouse search (GUID: %u TypeId: %u)", , list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", - // GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable); - - WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4+4+4)); - uint32 count = 0; - uint32 totalcount = 0; - data << (uint32) 0; - - // converting string that we try to find to lower case - std::wstring wsearchedname; - if (!Utf8toWStr(searchedname,wsearchedname)) - return; - - wstrToLower(wsearchedname); - - auctionHouse->BuildListAuctionItems(data,_player, - wsearchedname, listfrom, levelmin, levelmax, usable, - auctionSlotID, auctionMainCategory, auctionSubCategory, quality, - count,totalcount); - - data.put(0, count); - data << (uint32) totalcount; - data << (uint32) 300; // unk 2.3.0 const? - SendPacket(&data); -} - -void WorldSession::HandleAuctionListPendingSales(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_AUCTION_LIST_PENDING_SALES"); - - recv_data.read_skip(); - - uint32 count = 0; - - WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4); - data << uint32(count); // count - /*for (uint32 i = 0; i < count; ++i) - { - data << ""; // string - data << ""; // string - data << uint32(0); - data << uint32(0); - data << float(0); - }*/ - SendPacket(&data); -} diff --git a/src/server/game/AuctionHouseMgr.cpp b/src/server/game/AuctionHouseMgr.cpp deleted file mode 100755 index ddd44bbefa2..00000000000 --- a/src/server/game/AuctionHouseMgr.cpp +++ /dev/null @@ -1,750 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Database/DatabaseEnv.h" -#include "Database/SQLStorage.h" -#include "Policies/SingletonImp.h" -#include "DBCStores.h" - -#include "AccountMgr.h" -#include "AuctionHouseMgr.h" -#include "Item.h" -#include "Language.h" -#include "Log.h" -#include "ProgressBar.h" -#include - -INSTANTIATE_SINGLETON_1(AuctionHouseMgr); - -using namespace std; - -AuctionHouseMgr::AuctionHouseMgr() -{ -} - -AuctionHouseMgr::~AuctionHouseMgr() -{ - for (ItemMap::const_iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr) - delete itr->second; -} - -AuctionHouseObject * AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId) -{ - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - return &mNeutralAuctions; - - // team have linked auction houses - FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); - if (!u_entry) - return &mNeutralAuctions; - else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) - return &mAllianceAuctions; - else if (u_entry->ourMask & FACTION_MASK_HORDE) - return &mHordeAuctions; - else - return &mNeutralAuctions; -} - -uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem) -{ - uint32 MSV = pItem->GetProto()->SellPrice; - int32 deposit; - uint32 timeHr = (((time / 60) / 60) / 12); - - if (MSV > 0) - deposit = (int32)floor((double)MSV * (((double)(entry->depositPercent * 3) / 100.0f * (double)sWorld.getRate(RATE_AUCTION_DEPOSIT) * (double)pItem->GetCount()))) * timeHr; - else - deposit = 0; - - sLog.outDebug("Sellprice: %u / Depositpercent: %f / AT1: %u / AT2: %u / AT3: %u / Count: %u", MSV, ((double)entry->depositPercent / 100.0f), time, MIN_AUCTION_TIME, timeHr, pItem->GetCount() ); - if (deposit > 0) - { - sLog.outDebug("Deposit: %u", deposit); - return deposit; - } - else - { - sLog.outDebug("Deposit: 0"); - return 0; - } -} - -//does not clear ram -void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction) -{ - Item *pItem = GetAItem(auction->item_guidlow); - if (!pItem) - return; - - uint32 bidder_accId = 0; - uint32 bidder_security = 0; - uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); - Player *bidder = objmgr.GetPlayer(bidder_guid); - // data for gm.log - if (sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - std::string bidder_name; - if (bidder) - { - bidder_accId = bidder->GetSession()->GetAccountId(); - bidder_security = bidder->GetSession()->GetSecurity(); - bidder_name = bidder->GetName(); - } - else - { - bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid); - bidder_security = accmgr.GetSecurity(bidder_accId); - - if (bidder_security > SEC_PLAYER) // not do redundant DB requests - { - if (!objmgr.GetPlayerNameByGUID(bidder_guid,bidder_name)) - bidder_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN); - } - } - if (bidder_security > SEC_PLAYER) - { - std::string owner_name; - if (!objmgr.GetPlayerNameByGUID(auction->owner,owner_name)) - owner_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN); - - uint32 owner_accid = objmgr.GetPlayerAccountIdByGUID(auction->owner); - - sLog.outCommand(bidder_accId,"GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)", - bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid); - } - } - - // receiver exist - if (bidder || bidder_accId) - { - std::ostringstream msgAuctionWonSubject; - msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON; - - std::ostringstream msgAuctionWonBody; - msgAuctionWonBody.width(16); - msgAuctionWonBody << std::right << std::hex << auction->owner; - msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout; - sLog.outDebug("AuctionWon body string : %s", msgAuctionWonBody.str().c_str()); - - // set owner to bidder (to prevent delete item with sender char deleting) - // owner in `data` will set at mail receive and item extracting - CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow()); - - if (bidder) - { - bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template); - // FIXME: for offline player need also - bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); - } - - MailDraft(msgAuctionWonSubject.str(), msgAuctionWonBody.str()) - .AddItem(pItem) - .SendMailTo(MailReceiver(bidder,auction->bidder), auction, MAIL_CHECK_MASK_COPIED); - } -} - -void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction) -{ - uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER); - Player *owner = objmgr.GetPlayer(owner_guid); - uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid); - // owner exist (online or offline) - if (owner || owner_accId) - { - std::ostringstream msgAuctionSalePendingSubject; - msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING; - - std::ostringstream msgAuctionSalePendingBody; - uint32 auctionCut = auction->GetAuctionCut(); - - time_t distrTime = time(NULL) + sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY); - - msgAuctionSalePendingBody.width(16); - msgAuctionSalePendingBody << std::right << std::hex << auction->bidder; - msgAuctionSalePendingBody << std::dec << ":" << auction->bid << ":" << auction->buyout; - msgAuctionSalePendingBody << ":" << auction->deposit << ":" << auctionCut << ":0:"; - msgAuctionSalePendingBody << secsToTimeBitFields(distrTime); - - sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str()); - - MailDraft(msgAuctionSalePendingSubject.str(), msgAuctionSalePendingBody.str()) - .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED); - } -} - -//call this method to send mail to auction owner, when auction is successful, it does not clear ram -void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction) -{ - uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER); - Player *owner = objmgr.GetPlayer(owner_guid); - uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid); - // owner exist - if (owner || owner_accId) - { - std::ostringstream msgAuctionSuccessfulSubject; - msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL; - - std::ostringstream auctionSuccessfulBody; - uint32 auctionCut = auction->GetAuctionCut(); - - auctionSuccessfulBody.width(16); - auctionSuccessfulBody << std::right << std::hex << auction->bidder; - auctionSuccessfulBody << std::dec << ":" << auction->bid << ":" << auction->buyout; - auctionSuccessfulBody << ":" << auction->deposit << ":" << auctionCut; - - sLog.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody.str().c_str()); - - uint32 profit = auction->bid + auction->deposit - auctionCut; - - //FIXME: what do if owner offline - if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID()) - { - owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS, profit); - owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid); - //send auction owner notification, bidder must be current! - owner->GetSession()->SendAuctionOwnerNotification(auction); - } - MailDraft(msgAuctionSuccessfulSubject.str(), auctionSuccessfulBody.str()) - .AddMoney(profit) - .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED, sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY)); - } -} - -//does not clear ram -void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction) -{ //return an item in auction to its owner by mail - Item *pItem = GetAItem(auction->item_guidlow); - if (!pItem) - return; - - uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER); - Player *owner = objmgr.GetPlayer(owner_guid); - uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid); - // owner exist - if (owner || owner_accId) - { - std::ostringstream subject; - subject << auction->item_template << ":0:" << AUCTION_EXPIRED << ":0:0"; - - if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID()) - owner->GetSession()->SendAuctionOwnerNotification(auction); - - MailDraft(subject.str(), "") // TODO: fix body - .AddItem(pItem) - .SendMailTo(MailReceiver(owner,auction->owner), auction, MAIL_CHECK_MASK_COPIED); - } -} - -void AuctionHouseMgr::LoadAuctionItems() -{ - // data needs to be at first place for Item::LoadFromDB - QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT data, text, itemguid, item_template FROM auctionhouse JOIN item_instance ON itemguid = guid"); - - if (!result) - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 auction items"); - return; - } - - barGoLink bar(result->GetRowCount()); - - uint32 count = 0; - - Field *fields; - do - { - bar.step(); - - fields = result->Fetch(); - uint32 item_guid = fields[2].GetUInt32(); - uint32 item_template = fields[3].GetUInt32(); - - ItemPrototype const *proto = objmgr.GetItemPrototype(item_template); - - if (!proto) - { - sLog.outError("AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template); - continue; - } - - Item *item = NewItemOrBag(proto); - - if (!item->LoadFromDB(item_guid,0, result)) - { - delete item; - continue; - } - AddAItem(item); - - ++count; - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u auction items", count); -} - -void AuctionHouseMgr::LoadAuctions() -{ - QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse"); - if (!result) - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); - return; - } - - Field *fields = result->Fetch(); - uint32 AuctionCount=fields[0].GetUInt32(); - - if (!AuctionCount) - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); - return; - } - - result = CharacterDatabase.Query("SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit FROM auctionhouse"); - if (!result) - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty."); - return; - } - - barGoLink bar(AuctionCount); - - AuctionEntry *aItem; - - do - { - fields = result->Fetch(); - - bar.step(); - - aItem = new AuctionEntry; - aItem->Id = fields[0].GetUInt32(); - aItem->auctioneer = fields[1].GetUInt32(); - aItem->item_guidlow = fields[2].GetUInt32(); - aItem->item_template = fields[3].GetUInt32(); - aItem->owner = fields[4].GetUInt32(); - aItem->buyout = fields[5].GetUInt32(); - aItem->expire_time = fields[6].GetUInt32(); - aItem->bidder = fields[7].GetUInt32(); - aItem->bid = fields[8].GetUInt32(); - aItem->startbid = fields[9].GetUInt32(); - aItem->deposit = fields[10].GetUInt32(); - - CreatureData const* auctioneerData = objmgr.GetCreatureData(aItem->auctioneer); - if (!auctioneerData) - { - aItem->DeleteFromDB(); - sLog.outError("Auction %u has not a existing auctioneer (GUID : %u)", aItem->Id, aItem->auctioneer); - delete aItem; - continue; - } - - CreatureInfo const* auctioneerInfo = objmgr.GetCreatureTemplate(auctioneerData->id); - if (!auctioneerInfo) - { - aItem->DeleteFromDB(); - sLog.outError("Auction %u has not a existing auctioneer (GUID : %u Entry: %u)", aItem->Id, aItem->auctioneer,auctioneerData->id); - delete aItem; - continue; - } - - aItem->auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(auctioneerInfo->faction_A); - if (!aItem->auctionHouseEntry) - { - aItem->DeleteFromDB(); - sLog.outError("Auction %u has auctioneer (GUID : %u Entry: %u) with wrong faction %u", - aItem->Id, aItem->auctioneer,auctioneerData->id,auctioneerInfo->faction_A); - delete aItem; - continue; - } - - // check if sold item exists for guid - // and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems) - if (!GetAItem(aItem->item_guidlow)) - { - aItem->DeleteFromDB(); - sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow); - delete aItem; - continue; - } - - GetAuctionsMap(auctioneerInfo->faction_A)->AddAuction(aItem); - - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u auctions", AuctionCount); -} - -void AuctionHouseMgr::AddAItem(Item* it) -{ - ASSERT(it); - ASSERT(mAitems.find(it->GetGUIDLow()) == mAitems.end()); - mAitems[it->GetGUIDLow()] = it; -} - -bool AuctionHouseMgr::RemoveAItem(uint32 id) -{ - ItemMap::iterator i = mAitems.find(id); - if (i == mAitems.end()) - return false; - - mAitems.erase(i); - return true; -} - -void AuctionHouseMgr::Update() -{ - mHordeAuctions.Update(); - mAllianceAuctions.Update(); - mNeutralAuctions.Update(); -} - -AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId) -{ - uint32 houseid = 7; // goblin auction house - - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - { - //FIXME: found way for proper auctionhouse selection by another way - // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races. - // but no easy way convert creature faction to player race faction for specific city - switch(factionTemplateId) - { - case 12: houseid = 1; break; // human - case 29: houseid = 6; break; // orc, and generic for horde - case 55: houseid = 2; break; // dwarf, and generic for alliance - case 68: houseid = 4; break; // undead - case 80: houseid = 3; break; // n-elf - case 104: houseid = 5; break; // trolls - case 120: houseid = 7; break; // booty bay, neutral - case 474: houseid = 7; break; // gadgetzan, neutral - case 855: houseid = 7; break; // everlook, neutral - case 1604: houseid = 6; break; // b-elfs, - default: // for unknown case - { - FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); - if (!u_entry) - houseid = 7; // goblin auction house - else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) - houseid = 1; // human auction house - else if (u_entry->ourMask & FACTION_MASK_HORDE) - houseid = 6; // orc auction house - else - houseid = 7; // goblin auction house - break; - } - } - } - - return sAuctionHouseStore.LookupEntry(houseid); -} - void AuctionHouseObject::AddAuction(AuctionEntry *ah) - { - ASSERT(ah); - AuctionsMap[ah->Id] = ah; - auctionbot.IncrementItemCounts(ah); - } - - bool AuctionHouseObject::RemoveAuction(AuctionEntry *auction, uint32 item_template) - { - auctionbot.DecrementItemCounts(auction, item_template); - return AuctionsMap.erase(auction->Id) ? true : false; - } - -void AuctionHouseObject::Update() -{ - time_t curTime = sWorld.GetGameTime(); - ///- Handle expired auctions - - // If storage is empty, no need to update. next == NULL in this case. - if (AuctionsMap.empty()) - return; - - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT id FROM auctionhouse WHERE time <= %u ORDER BY TIME ASC", (uint32)curTime+60); - - if (!result) - return; - - if (result->GetRowCount() == 0) - return; - - vector expiredAuctions; - - do - { - uint32 tmpdata = result->Fetch()->GetUInt32(); - expiredAuctions.push_back(tmpdata); - } while (result->NextRow()); - - while (!expiredAuctions.empty()) - { - vector::iterator iter = expiredAuctions.begin(); - - // from auctionhousehandler.cpp, creates auction pointer & player pointer - AuctionEntry* auction = GetAuction(*iter); - - // Erase the auction from the vector. - expiredAuctions.erase(iter); - - if (!auction) - continue; - - ///- Either cancel the auction if there was no bidder - if (auction->bidder == 0) - auctionmgr.SendAuctionExpiredMail(auction); - ///- Or perform the transaction - else - { - //we should send an "item sold" message if the seller is online - //we send the item to the winner - //we send the money to the seller - auctionmgr.SendAuctionSuccessfulMail(auction); - auctionmgr.SendAuctionWonMail(auction); - } - - ///- In any case clear the auction - CharacterDatabase.BeginTransaction(); - auction->DeleteFromDB(); - uint32 item_template = auction->item_template; - auctionmgr.RemoveAItem(auction->item_guidlow); - RemoveAuction(auction, item_template); - CharacterDatabase.CommitTransaction(); - } -} - -void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) -{ - for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) - { - AuctionEntry *Aentry = itr->second; - if (Aentry && Aentry->bidder == player->GetGUIDLow()) - { - if (itr->second->BuildAuctionInfo(data)) - ++count; - - ++totalcount; - } - } -} - -void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) -{ - for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) - { - AuctionEntry *Aentry = itr->second; - if (Aentry && Aentry->owner == player->GetGUIDLow()) - { - if (Aentry->BuildAuctionInfo(data)) - ++count; - - ++totalcount; - } - } -} - -void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player, - std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, - uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount) -{ - int loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); - int locdbc_idx = player->GetSession()->GetSessionDbcLocale(); - - for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) - { - AuctionEntry *Aentry = itr->second; - Item *item = auctionmgr.GetAItem(Aentry->item_guidlow); - if (!item) - continue; - - ItemPrototype const *proto = item->GetProto(); - - if (itemClass != 0xffffffff && proto->Class != itemClass) - continue; - - if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass) - continue; - - if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType) - continue; - - if (quality != 0xffffffff && proto->Quality != quality) - continue; - - if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax))) - continue; - - if (usable != 0x00 && player->CanUseItem(item) != EQUIP_ERR_OK) - continue; - - // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) - // No need to do any of this if no search term was entered - if (!wsearchedname.empty()) - { - std::string name = proto->Name1; - if (name.empty()) - continue; - - // local name - if (loc_idx >= 0) - { - ItemLocale const *il = objmgr.GetItemLocale(proto->ItemId); - if (il) - { - if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty()) - name = il->Name[loc_idx]; - } - } - - // DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result - // that matches the search but it may not equal item->GetItemRandomPropertyId() - // used in BuildAuctionInfo() which then causes wrong items to be listed - int32 propRefID = item->GetItemRandomPropertyId(); - - if (propRefID) - { - // Append the suffix to the name (ie: of the Monkey) if one exists - // These are found in ItemRandomProperties.dbc, not ItemRandomSuffix.dbc - // even though the DBC names seem misleading - const ItemRandomPropertiesEntry *itemRandProp = sItemRandomPropertiesStore.LookupEntry(propRefID); - - if (itemRandProp) - { - char* const* temp = itemRandProp->nameSuffix; - //char* temp = itemRandProp->nameSuffix; - - // dbc local name - if (temp) - { - if (locdbc_idx >= 0) - { - // Append the suffix (ie: of the Monkey) to the name using localization - name += " "; - name += temp[locdbc_idx]; - } - else - { - // Invalid localization? Append the suffix using default enUS - name += " "; - name += temp[LOCALE_enUS]; - } - } - } - } - - // Perform the search (with or without suffix) - if (!Utf8FitTo(name, wsearchedname)) - continue; - } - - // Add the item if no search term or if entered search term was found - if (count < 50 && totalcount >= listfrom) - { - ++count; - Aentry->BuildAuctionInfo(data); - } - ++totalcount; - } -} - -//this function inserts to WorldPacket auction's data -bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const -{ - Item *pItem = auctionmgr.GetAItem(item_guidlow); - if (!pItem) - { - sLog.outError("auction to item, that doesn't exist !!!!"); - return false; - } - data << uint32(Id); - data << uint32(pItem->GetEntry()); - - for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i) - { - data << uint32(pItem->GetEnchantmentId(EnchantmentSlot(i))); - data << uint32(pItem->GetEnchantmentDuration(EnchantmentSlot(i))); - data << uint32(pItem->GetEnchantmentCharges(EnchantmentSlot(i))); - } - - data << int32(pItem->GetItemRandomPropertyId()); //random item property id - data << uint32(pItem->GetItemSuffixFactor()); //SuffixFactor - data << uint32(pItem->GetCount()); //item->count - data << uint32(pItem->GetSpellCharges()); //item->charge FFFFFFF - data << uint32(0); //Unknown - data << uint64(owner); //Auction->owner - data << uint32(startbid); //Auction->startbid (not sure if useful) - data << uint32(bid ? GetAuctionOutBid() : 0); - //minimal outbid - data << uint32(buyout); //auction->buyout - data << uint32((expire_time-time(NULL))*IN_MILISECONDS);//time left - data << uint64(bidder) ; //auction->bidder current - data << uint32(bid); //current bid - return true; -} - -uint32 AuctionEntry::GetAuctionCut() const -{ - int32 cut = int32(((double)auctionHouseEntry->cutPercent / 100.0f) * (double)sWorld.getRate(RATE_AUCTION_CUT)) * bid; - if (cut > 0) - return cut; - else - return 0; -} - -/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c -uint32 AuctionEntry::GetAuctionOutBid() const -{ - uint32 outbid = (uint32)((double)bid / 100.0f) * 5; - if (!outbid) - outbid = 1; - return outbid; -} - -void AuctionEntry::DeleteFromDB() const -{ - //No SQL injection (Id is integer) - CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",Id); -} - -void AuctionEntry::SaveToDB() const -{ - //No SQL injection (no strings) - CharacterDatabase.PExecute("INSERT INTO auctionhouse (id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit) " - "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u')", - Id, auctioneer, item_guidlow, item_template, owner, buyout, (uint64)expire_time, bidder, bid, startbid, deposit); -} diff --git a/src/server/game/AuctionHouseMgr.h b/src/server/game/AuctionHouseMgr.h deleted file mode 100644 index b92cec986c7..00000000000 --- a/src/server/game/AuctionHouseMgr.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _AUCTION_HOUSE_MGR_H -#define _AUCTION_HOUSE_MGR_H - -#include "Policies/Singleton.h" - -#include "SharedDefines.h" - -#include "AuctionHouseBot.h" - -class Item; -class Player; -class WorldPacket; - -#define MIN_AUCTION_TIME (12*HOUR) - -enum AuctionError -{ - AUCTION_OK = 0, - AUCTION_INTERNAL_ERROR = 2, - AUCTION_NOT_ENOUGHT_MONEY = 3, - AUCTION_ITEM_NOT_FOUND = 4, - CANNOT_BID_YOUR_AUCTION_ERROR = 10 -}; - -enum AuctionAction -{ - AUCTION_SELL_ITEM = 0, - AUCTION_CANCEL = 1, - AUCTION_PLACE_BID = 2 -}; - -struct AuctionEntry -{ - uint32 Id; - uint32 auctioneer; // creature low guid - uint32 item_guidlow; - uint32 item_template; - uint32 owner; - uint32 startbid; //maybe useless - uint32 bid; - uint32 buyout; - time_t expire_time; - uint32 bidder; - uint32 deposit; //deposit can be calculated only when creating auction - AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc - - // helpers - uint32 GetHouseId() const { return auctionHouseEntry->houseId; } - uint32 GetHouseFaction() const { return auctionHouseEntry->faction; } - uint32 GetAuctionCut() const; - uint32 GetAuctionOutBid() const; - bool BuildAuctionInfo(WorldPacket & data) const; - void DeleteFromDB() const; - void SaveToDB() const; -}; - -//this class is used as auctionhouse instance -class AuctionHouseObject -{ - public: - // Initialize storage - AuctionHouseObject() { next = AuctionsMap.begin(); } - ~AuctionHouseObject() - { - for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) - delete itr->second; - } - - typedef std::map AuctionEntryMap; - - uint32 Getcount() { return AuctionsMap.size(); } - - AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();} - AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();} - - AuctionEntry* GetAuction(uint32 id) const - { - AuctionEntryMap::const_iterator itr = AuctionsMap.find(id); - return itr != AuctionsMap.end() ? itr->second : NULL; - } - - void AddAuction(AuctionEntry *ah); - - bool RemoveAuction(AuctionEntry *auction, uint32 item_template); - - void Update(); - - void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); - void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); - void BuildListAuctionItems(WorldPacket& data, Player* player, - std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, - uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount); - - private: - AuctionEntryMap AuctionsMap; - - // storage for "next" auction item for next Update() - AuctionEntryMap::const_iterator next; -}; - -class AuctionHouseMgr -{ - public: - AuctionHouseMgr(); - ~AuctionHouseMgr(); - - typedef UNORDERED_MAP ItemMap; - - AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId); - AuctionHouseObject* GetBidsMap(uint32 factionTemplateId); - - Item* GetAItem(uint32 id) - { - ItemMap::const_iterator itr = mAitems.find(id); - if (itr != mAitems.end()) - { - return itr->second; - } - return NULL; - } - - //auction messages - void SendAuctionWonMail(AuctionEntry * auction); - void SendAuctionSalePendingMail(AuctionEntry * auction); - void SendAuctionSuccessfulMail(AuctionEntry * auction); - void SendAuctionExpiredMail(AuctionEntry * auction); - static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem); - static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId); - - public: - //load first auction items, because of check if item exists, when loading - void LoadAuctionItems(); - void LoadAuctions(); - - void AddAItem(Item* it); - bool RemoveAItem(uint32 id); - - void Update(); - - private: - AuctionHouseObject mHordeAuctions; - AuctionHouseObject mAllianceAuctions; - AuctionHouseObject mNeutralAuctions; - - ItemMap mAitems; -}; - -#define auctionmgr Trinity::Singleton::Instance() - -#endif diff --git a/src/server/game/Bag.cpp b/src/server/game/Bag.cpp deleted file mode 100644 index aa78126b198..00000000000 --- a/src/server/game/Bag.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectMgr.h" -#include "Database/DatabaseEnv.h" - -#include "Bag.h" -#include "Log.h" -#include "UpdateData.h" - -Bag::Bag(): Item() -{ - m_objectType |= TYPEMASK_CONTAINER; - m_objectTypeId = TYPEID_CONTAINER; - - m_valuesCount = CONTAINER_END; - - memset(m_bagslot, 0, sizeof(Item *) * MAX_BAG_SIZE); -} - -Bag::~Bag() -{ - for (uint8 i = 0; i < MAX_BAG_SIZE; ++i) - if (Item *item = m_bagslot[i]) - { - if (item->IsInWorld()) - { - sLog.outCrash("Item %u (slot %u, bag slot %u) in bag %u (slot %u, bag slot %u, m_bagslot %u) is to be deleted but is still in world.", - item->GetEntry(), (uint32)item->GetSlot(), (uint32)item->GetBagSlot(), - GetEntry(), (uint32)GetSlot(), (uint32)GetBagSlot(), (uint32)i); - item->RemoveFromWorld(); - } - delete m_bagslot[i]; - } -} - -void Bag::AddToWorld() -{ - Item::AddToWorld(); - - for (uint32 i = 0; i < GetBagSize(); ++i) - if (m_bagslot[i]) - m_bagslot[i]->AddToWorld(); -} - -void Bag::RemoveFromWorld() -{ - for (uint32 i = 0; i < GetBagSize(); ++i) - if (m_bagslot[i]) - m_bagslot[i]->RemoveFromWorld(); - - Item::RemoveFromWorld(); -} - -bool Bag::Create(uint32 guidlow, uint32 itemid, Player const* owner) -{ - ItemPrototype const * itemProto = objmgr.GetItemPrototype(itemid); - - if (!itemProto || itemProto->ContainerSlots > MAX_BAG_SIZE) - return false; - - Object::_Create(guidlow, 0, HIGHGUID_CONTAINER); - - SetEntry(itemid); - SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f); - - SetUInt64Value(ITEM_FIELD_OWNER, owner ? owner->GetGUID() : 0); - SetUInt64Value(ITEM_FIELD_CONTAINED, owner ? owner->GetGUID() : 0); - - SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability); - SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability); - SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags); - SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1); - - // Setting the number of Slots the Container has - SetUInt32Value(CONTAINER_FIELD_NUM_SLOTS, itemProto->ContainerSlots); - - // Cleaning 20 slots - for (uint8 i = 0; i < MAX_BAG_SIZE; ++i) - { - SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (i*2), 0); - m_bagslot[i] = NULL; - } - - return true; -} - -void Bag::SaveToDB() -{ - Item::SaveToDB(); -} - -bool Bag::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult_AutoPtr result) -{ - if (!Item::LoadFromDB(guid, owner_guid, result)) - return false; - - // cleanup bag content related item value fields (its will be filled correctly from `character_inventory`) - for (uint8 i = 0; i < MAX_BAG_SIZE; ++i) - { - SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (i*2), 0); - if (m_bagslot[i]) - { - delete m_bagslot[i]; - m_bagslot[i] = NULL; - } - } - - return true; -} - -void Bag::DeleteFromDB() -{ - for (uint8 i = 0; i < MAX_BAG_SIZE; ++i) - if (m_bagslot[i]) - m_bagslot[i]->DeleteFromDB(); - - Item::DeleteFromDB(); -} - -uint32 Bag::GetFreeSlots() const -{ - uint32 slots = 0; - for (uint32 i=0; i < GetBagSize(); ++i) - if (!m_bagslot[i]) - ++slots; - - return slots; -} - -void Bag::RemoveItem(uint8 slot, bool /*update*/) -{ - assert(slot < MAX_BAG_SIZE); - - if (m_bagslot[slot]) - m_bagslot[slot]->SetContainer(NULL); - - m_bagslot[slot] = NULL; - SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (slot * 2), 0); -} - -void Bag::StoreItem(uint8 slot, Item *pItem, bool /*update*/) -{ - assert(slot < MAX_BAG_SIZE); - - if (pItem && pItem->GetGUID() != this->GetGUID()) - { - m_bagslot[slot] = pItem; - SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (slot * 2), pItem->GetGUID()); - pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, GetGUID()); - pItem->SetUInt64Value(ITEM_FIELD_OWNER, GetOwnerGUID()); - pItem->SetContainer(this); - pItem->SetSlot(slot); - } -} - -void Bag::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const -{ - Item::BuildCreateUpdateBlockForPlayer(data, target); - - for (uint32 i = 0; i < GetBagSize(); ++i) - if (m_bagslot[i]) - m_bagslot[i]->BuildCreateUpdateBlockForPlayer(data, target); -} - -// If the bag is empty returns true -bool Bag::IsEmpty() const -{ - for (uint32 i = 0; i < GetBagSize(); ++i) - if (m_bagslot[i]) - return false; - - return true; -} - -uint32 Bag::GetItemCount(uint32 item, Item* eItem) const -{ - Item *pItem; - uint32 count = 0; - for (uint32 i=0; i < GetBagSize(); ++i) - { - pItem = m_bagslot[i]; - if (pItem && pItem != eItem && pItem->GetEntry() == item) - count += pItem->GetCount(); - } - - if (eItem && eItem->GetProto()->GemProperties) - { - for (uint32 i=0; i < GetBagSize(); ++i) - { - pItem = m_bagslot[i]; - if (pItem && pItem != eItem && pItem->GetProto()->Socket[0].Color) - count += pItem->GetGemCountWithID(item); - } - } - - return count; -} - -uint8 Bag::GetSlotByItemGUID(uint64 guid) const -{ - for (uint32 i = 0; i < GetBagSize(); ++i) - if (m_bagslot[i] != 0) - if (m_bagslot[i]->GetGUID() == guid) - return i; - - return NULL_SLOT; -} - -Item* Bag::GetItemByPos(uint8 slot) const -{ - if (slot < GetBagSize()) - return m_bagslot[slot]; - - return NULL; -} - diff --git a/src/server/game/Bag.h b/src/server/game/Bag.h deleted file mode 100644 index 5bc2480fc47..00000000000 --- a/src/server/game/Bag.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_BAG_H -#define TRINITY_BAG_H - -// Maximum 36 Slots ((CONTAINER_END - CONTAINER_FIELD_SLOT_1)/2 -#define MAX_BAG_SIZE 36 // 2.0.12 - -#include "Item.h" -#include "ItemPrototype.h" - -class Bag : public Item -{ - public: - - Bag(); - ~Bag(); - - void AddToWorld(); - void RemoveFromWorld(); - - bool Create(uint32 guidlow, uint32 itemid, Player const* owner); - - void Clear(); - void StoreItem(uint8 slot, Item *pItem, bool update); - void RemoveItem(uint8 slot, bool update); - - Item* GetItemByPos(uint8 slot) const; - uint32 GetItemCount(uint32 item, Item* eItem = NULL) const; - - uint8 GetSlotByItemGUID(uint64 guid) const; - bool IsEmpty() const; - uint32 GetFreeSlots() const; - uint32 GetBagSize() const { return GetUInt32Value(CONTAINER_FIELD_NUM_SLOTS); } - - // DB operations - // overwrite virtual Item::SaveToDB - void SaveToDB(); - // overwrite virtual Item::LoadFromDB - bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult_AutoPtr result); - // overwrite virtual Item::DeleteFromDB - void DeleteFromDB(); - - void BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const; - - protected: - - // Bag Storage space - Item* m_bagslot[MAX_BAG_SIZE]; -}; - -inline Item* NewItemOrBag(ItemPrototype const * proto) -{ - return (proto->InventoryType == INVTYPE_BAG) ? new Bag : new Item; -} -#endif - diff --git a/src/server/game/BattleGround.cpp b/src/server/game/BattleGround.cpp deleted file mode 100644 index 5bfe2e139b6..00000000000 --- a/src/server/game/BattleGround.cpp +++ /dev/null @@ -1,1965 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Player.h" -#include "ObjectMgr.h" -#include "World.h" -#include "WorldPacket.h" - -#include "ArenaTeam.h" -#include "BattleGround.h" -#include "BattleGroundMgr.h" -#include "Creature.h" -#include "Formulas.h" -#include "GridNotifiersImpl.h" -#include "Group.h" -#include "Language.h" -#include "MapManager.h" -#include "Object.h" -#include "SpellAuras.h" -#include "SpellAuraEffects.h" -#include "Util.h" - -namespace Trinity -{ - class BattleGroundChatBuilder - { - public: - BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL) - : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {} - void operator()(WorldPacket& data, int32 loc_idx) - { - char const* text = objmgr.GetTrinityString(i_textId,loc_idx); - - if (i_args) - { - // we need copy va_list before use or original va_list will corrupted - va_list ap; - va_copy(ap,*i_args); - - char str [2048]; - vsnprintf(str,2048,text, ap); - va_end(ap); - - do_helper(data,&str[0]); - } - else - do_helper(data,text); - } - private: - void do_helper(WorldPacket& data, char const* text) - { - uint64 target_guid = i_source ? i_source ->GetGUID() : 0; - - data << uint8(i_msgtype); - data << uint32(LANG_UNIVERSAL); - data << uint64(target_guid); // there 0 for BG messages - data << uint32(0); // can be chat msg group or something - data << uint64(target_guid); - data << uint32(strlen(text)+1); - data << text; - data << uint8(i_source ? i_source->chatTag() : uint8(0)); - } - - ChatMsg i_msgtype; - int32 i_textId; - Player const* i_source; - va_list* i_args; - }; - - class BattleGround2ChatBuilder - { - public: - BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2) - : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {} - void operator()(WorldPacket& data, int32 loc_idx) - { - char const* text = objmgr.GetTrinityString(i_textId,loc_idx); - char const* arg1str = i_arg1 ? objmgr.GetTrinityString(i_arg1,loc_idx) : ""; - char const* arg2str = i_arg2 ? objmgr.GetTrinityString(i_arg2,loc_idx) : ""; - - char str [2048]; - snprintf(str,2048,text, arg1str, arg2str); - - uint64 target_guid = i_source ? i_source ->GetGUID() : 0; - - data << uint8(i_msgtype); - data << uint32(LANG_UNIVERSAL); - data << uint64(target_guid); // there 0 for BG messages - data << uint32(0); // can be chat msg group or something - data << uint64(target_guid); - data << uint32(strlen(str)+1); - data << str; - data << uint8(i_source ? i_source->chatTag() : uint8(0)); - } - private: - - ChatMsg i_msgtype; - int32 i_textId; - Player const* i_source; - int32 i_arg1; - int32 i_arg2; - }; -} // namespace Trinity - -template -void BattleGround::BroadcastWorker(Do& _do) -{ - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) - _do(plr); -} - -BattleGround::BattleGround() -{ - m_TypeID = BattleGroundTypeId(0); - m_RandomTypeID = BattleGroundTypeId(0); - m_InstanceID = 0; - m_Status = STATUS_NONE; - m_ClientInstanceID = 0; - m_EndTime = 0; - m_LastResurrectTime = 0; - m_BracketId = BG_BRACKET_ID_FIRST; - m_InvitedAlliance = 0; - m_InvitedHorde = 0; - m_ArenaType = 0; - m_IsArena = false; - m_Winner = 2; - m_StartTime = 0; - m_Events = 0; - m_IsRated = false; - m_BuffChange = false; - m_IsRandom = false; - m_Name = ""; - m_LevelMin = 0; - m_LevelMax = 0; - m_InBGFreeSlotQueue = false; - m_SetDeleteThis = false; - - m_MaxPlayersPerTeam = 0; - m_MaxPlayers = 0; - m_MinPlayersPerTeam = 0; - m_MinPlayers = 0; - - m_MapId = 0; - m_Map = NULL; - - m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocX[BG_TEAM_HORDE] = 0; - - m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocY[BG_TEAM_HORDE] = 0; - - m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocZ[BG_TEAM_HORDE] = 0; - - m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0; - m_TeamStartLocO[BG_TEAM_HORDE] = 0; - - m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0; - m_ArenaTeamIds[BG_TEAM_HORDE] = 0; - - m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0; - m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0; - - m_BgRaids[BG_TEAM_ALLIANCE] = NULL; - m_BgRaids[BG_TEAM_HORDE] = NULL; - - m_PlayersCount[BG_TEAM_ALLIANCE] = 0; - m_PlayersCount[BG_TEAM_HORDE] = 0; - - m_TeamScores[BG_TEAM_ALLIANCE] = 0; - m_TeamScores[BG_TEAM_HORDE] = 0; - - m_PrematureCountDown = false; - m_PrematureCountDown = 0; - - m_HonorMode = BG_NORMAL; - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set to some default existing values - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; -} - -BattleGround::~BattleGround() -{ - // remove objects and creatures - // (this is done automatically in mapmanager update, when the instance is reset after the reset time) - int size = m_BgCreatures.size(); - for (int i = 0; i < size; ++i) - DelCreature(i); - - size = m_BgObjects.size(); - for (int i = 0; i < size; ++i) - DelObject(i); - - if (GetInstanceID()) // not spam by useless queries in case BG templates - { - // delete creature and go respawn times - WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID()); - WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID()); - // delete instance from db - CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID()); - // remove from battlegrounds - } - - sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID()); - // unload map - if (m_Map) - m_Map->SetUnload(); - // remove from bg free slot queue - this->RemoveFromBGFreeSlotQueue(); - - for (BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr) - delete itr->second; -} - -void BattleGround::Update(uint32 diff) -{ - if (!GetPlayersSize()) - { - //BG is empty - // if there are no players invited, delete BG - // this will delete arena or bg object, where any player entered - // [[ but if you use battleground object again (more battles possible to be played on 1 instance) - // then this condition should be removed and code: - // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) - // this->AddToFreeBGObjectsQueue(); // not yet implemented - // should be used instead of current - // ]] - // BattleGround Template instance cannot be updated, because it would be deleted - if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) - m_SetDeleteThis = true; - return; - } - - // remove offline players from bg after 5 minutes - if (!m_OfflineQueue.empty()) - { - BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin())); - if (itr != m_Players.end()) - { - if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime()) - { - RemovePlayerAtLeave(itr->first, true, true);// remove player from BG - m_OfflineQueue.pop_front(); // remove from offline queue - //do not use itr for anything, because it is erased in RemovePlayerAtLeave() - } - } - } - - /*********************************************************/ - /*** BATTLEGROUND RESSURECTION SYSTEM ***/ - /*********************************************************/ - - //this should be handled by spell system - m_LastResurrectTime += diff; - if (m_LastResurrectTime >= RESURRECTION_INTERVAL) - { - if (GetReviveQueueSize()) - { - for (std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) - { - Creature *sh = NULL; - for (std::vector::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2) - { - Player *plr = objmgr.GetPlayer(*itr2); - if (!plr) - continue; - - if (!sh && plr->IsInWorld()) - { - sh = plr->GetMap()->GetCreature(itr->first); - // only for visual effect - if (sh) - // Spirit Heal, effect 117 - sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); - } - - // Resurrection visual - plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); - m_ResurrectQueue.push_back(*itr2); - } - (itr->second).clear(); - } - - m_ReviveQueue.clear(); - m_LastResurrectTime = 0; - } - else - // queue is clear and time passed, just update last resurrection time - m_LastResurrectTime = 0; - } - else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC - { - for (std::vector::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr) - { - Player *plr = objmgr.GetPlayer(*itr); - if (!plr) - continue; - plr->ResurrectPlayer(1.0f); - plr->CastSpell(plr, 6962, true); - plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true); - ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr); - } - m_ResurrectQueue.clear(); - } - - /*********************************************************/ - /*** BATTLEGROUND BALLANCE SYSTEM ***/ - /*********************************************************/ - - // if less then minimum players are in on one side, then start premature finish timer - if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam())) - { - if (!m_PrematureCountDown) - { - m_PrematureCountDown = true; - m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime(); - } - else if (m_PrematureCountDownTimer < diff) - { - // time's up! - uint32 winner = 0; - if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam()) - winner = ALLIANCE; - else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam()) - winner = HORDE; - - EndBattleGround(winner); - m_PrematureCountDown = false; - } - else if (!sBattleGroundMgr.isTesting()) - { - uint32 newtime = m_PrematureCountDownTimer - diff; - // announce every minute - if (newtime > (MINUTE * IN_MILISECONDS)) - { - if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)) - PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))); - } - else - { - //announce every 15 seconds - if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS)) - PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS)); - } - m_PrematureCountDownTimer = newtime; - } - } - else if (m_PrematureCountDown) - m_PrematureCountDown = false; - - /*********************************************************/ - /*** BATTLEGROUND STARTING SYSTEM ***/ - /*********************************************************/ - - if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) - { - ModifyStartDelayTime(diff); - - if (!(m_Events & BG_STARTING_EVENT_1)) - { - m_Events |= BG_STARTING_EVENT_1; - - // setup here, only when at least one player has ported to the map - if (!SetupBattleGround()) - { - EndNow(); - return; - } - - StartingEventCloseDoors(); - SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]); - //first start warning - 2 or 1 minute - SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - // After 1 minute or 30 seconds, warning is signalled - else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2)) - { - m_Events |= BG_STARTING_EVENT_2; - SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - // After 30 or 15 seconds, warning is signalled - else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3)) - { - m_Events |= BG_STARTING_EVENT_3; - SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - // delay expired (atfer 2 or 1 minute) - else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4)) - { - m_Events |= BG_STARTING_EVENT_4; - - StartingEventOpenDoors(); - - SendWarningToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH]); - SetStatus(STATUS_IN_PROGRESS); - SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]); - - //remove preparation - if (isArena()) - { - //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START); - - for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if (Player *plr = objmgr.GetPlayer(itr->first)) - { - plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); - // remove auras with duration lower than 30s - Unit::AuraApplicationMap & auraMap = plr->GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator iter = auraMap.begin(); iter != auraMap.end();) - { - AuraApplication * aurApp = iter->second; - Aura * aura = aurApp->GetBase(); - if (!aura->IsPermanent() - && aura->GetDuration() <= 30*IN_MILISECONDS - && aurApp->IsPositive() - && (!(aura->GetSpellProto()->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)) - && (!aura->HasEffectType(SPELL_AURA_MOD_INVISIBILITY))) - plr->RemoveAura(iter); - else - ++iter; - } - } - - CheckArenaWinConditions(); - } - else - { - - PlaySoundToAll(SOUND_BG_START); - - for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - if (Player* plr = objmgr.GetPlayer(itr->first)) - plr->RemoveAurasDueToSpell(SPELL_PREPARATION); - //Announce BG starting - if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) - { - sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel()); - } - } - } - } - - /*********************************************************/ - /*** BATTLEGROUND ENDING SYSTEM ***/ - /*********************************************************/ - - if (GetStatus() == STATUS_WAIT_LEAVE) - { - // remove all players from battleground after 2 minutes - m_EndTime -= diff; - if (m_EndTime <= 0) - { - m_EndTime = 0; - BattleGroundPlayerMap::iterator itr, next; - for (itr = m_Players.begin(); itr != m_Players.end(); itr = next) - { - next = itr; - ++next; - //itr is erased here! - RemovePlayerAtLeave(itr->first, true, true);// remove player from BG - // do not change any battleground's private variables - } - } - } - - //update start time - m_StartTime += diff; -} - -void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O) -{ - BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID); - m_TeamStartLocX[idx] = X; - m_TeamStartLocY[idx] = Y; - m_TeamStartLocZ[idx] = Z; - m_TeamStartLocO[idx] = O; -} - -void BattleGround::SendPacketToAll(WorldPacket *packet) -{ - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.OfflineRemoveTime) - continue; - Player *plr = objmgr.GetPlayer(itr->first); - if (plr) - plr->GetSession()->SendPacket(packet); - else - sLog.outError("BattleGround:SendPacketToAll: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - } -} - -void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self) -{ - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.OfflineRemoveTime) - continue; - Player *plr = objmgr.GetPlayer(itr->first); - if (!plr) - { - sLog.outError("BattleGround:SendPacketToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - continue; - } - - if (!self && sender == plr) - continue; - - uint32 team = itr->second.Team; - if (!team) team = plr->GetTeam(); - - if (team == TeamID) - plr->GetSession()->SendPacket(packet); - } -} - -void BattleGround::PlaySoundToAll(uint32 SoundID) -{ - WorldPacket data; - sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); - SendPacketToAll(&data); -} - -void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID) -{ - WorldPacket data; - - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.OfflineRemoveTime) - continue; - Player *plr = objmgr.GetPlayer(itr->first); - - if (!plr) - { - sLog.outError("BattleGround:PlaySoundToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - continue; - } - - uint32 team = itr->second.Team; - if (!team) team = plr->GetTeam(); - - if (team == TeamID) - { - sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); - plr->GetSession()->SendPacket(&data); - } - } -} - -void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID) -{ - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.OfflineRemoveTime) - continue; - Player *plr = objmgr.GetPlayer(itr->first); - - if (!plr) - { - sLog.outError("BattleGround:CastSpellOnTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - continue; - } - - uint32 team = itr->second.Team; - if (!team) team = plr->GetTeam(); - - if (team == TeamID) - plr->CastSpell(plr, SpellID, true); - } -} - -void BattleGround::YellToAll(Creature* creature, const char* text, uint32 language) -{ - for (std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - WorldPacket data(SMSG_MESSAGECHAT, 200); - Player *plr = objmgr.GetPlayer(itr->first); - if (!plr) - { - sLog.outError("BattleGround: Player " UI64FMTD " not found!", itr->first); - continue; - } - creature->BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,text,language,creature->GetName(),itr->first); - plr->GetSession()->SendPacket(&data); - } -} - -void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID) -{ - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.OfflineRemoveTime) - continue; - Player *plr = objmgr.GetPlayer(itr->first); - - if (!plr) - { - sLog.outError("BattleGround:RewardHonorToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - continue; - } - - uint32 team = itr->second.Team; - if (!team) team = plr->GetTeam(); - - if (team == TeamID) - UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor); - } -} - -void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID) -{ - FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); - - if (!factionEntry) - return; - - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.OfflineRemoveTime) - continue; - Player *plr = objmgr.GetPlayer(itr->first); - - if (!plr) - { - sLog.outError("BattleGround:RewardReputationToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - continue; - } - - uint32 team = itr->second.Team; - if (!team) team = plr->GetTeam(); - - if (team == TeamID) - plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation); - } -} - -void BattleGround::UpdateWorldState(uint32 Field, uint32 Value) -{ - WorldPacket data; - sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value); - SendPacketToAll(&data); -} - -void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source) -{ - WorldPacket data; - sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value); - Source->GetSession()->SendPacket(&data); -} - -void BattleGround::EndBattleGround(uint32 winner) -{ - this->RemoveFromBGFreeSlotQueue(); - - ArenaTeam * winner_arena_team = NULL; - ArenaTeam * loser_arena_team = NULL; - uint32 loser_rating = 0; - uint32 winner_rating = 0; - WorldPacket data; - int32 winmsg_id = 0; - - if (winner == ALLIANCE) - { - winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS; - - PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound - - SetWinner(WINNER_ALLIANCE); - } - else if (winner == HORDE) - { - winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS; - - PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound - - SetWinner(WINNER_HORDE); - } - else - { - SetWinner(3); - } - - SetStatus(STATUS_WAIT_LEAVE); - //we must set it this way, because end time is sent in packet! - m_EndTime = TIME_TO_AUTOREMOVE; - - // arena rating calculation - if (isArena() && isRated()) - { - winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner)); - loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner))); - if (winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) - { - loser_rating = loser_arena_team->GetStats().rating; - winner_rating = winner_arena_team->GetStats().rating; - int32 winner_change = winner_arena_team->WonAgainst(loser_rating); - int32 loser_change = loser_arena_team->LostAgainst(winner_rating); - sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change); - SetArenaTeamRatingChangeForTeam(winner, winner_change); - SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change); - sLog.outArena("Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. RatingChange: %i.", m_ArenaType, m_ArenaTeamIds[BG_TEAM_ALLIANCE], m_ArenaTeamIds[BG_TEAM_HORDE], winner_arena_team->GetId(), winner_change); - } - else - { - SetArenaTeamRatingChangeForTeam(ALLIANCE, 0); - SetArenaTeamRatingChangeForTeam(HORDE, 0); - } - } - - for (BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - uint32 team = itr->second.Team; - - if (itr->second.OfflineRemoveTime) - { - //if rated arena match - make member lost! - if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) - { - if (team == winner) - winner_arena_team->OfflineMemberLost(itr->first, loser_rating); - else - loser_arena_team->OfflineMemberLost(itr->first, winner_rating); - } - continue; - } - Player *plr = objmgr.GetPlayer(itr->first); - if (!plr) - { - sLog.outError("BattleGround:EndBattleGround Player (GUID: %u) not found!", GUID_LOPART(itr->first)); - continue; - } - - // should remove spirit of redemption - if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); - - if (!plr->isAlive()) - { - plr->ResurrectPlayer(1.0f); - plr->SpawnCorpseBones(); - } - else - { - //needed cause else in av some creatures will kill the players at the end - plr->CombatStop(); - plr->getHostileRefManager().deleteReferences(); - } - - //this line is obsolete - team is set ALWAYS - //if (!team) team = plr->GetTeam(); - - // per player calculation - if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) - { - if (team == winner) - { - // update achievement BEFORE personal rating update - ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID()); - if (member) - plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating); - - winner_arena_team->MemberWon(plr,loser_rating); - } - else - { - loser_arena_team->MemberLost(plr,winner_rating); - - // Arena lost => reset the win_rated_arena having the "no_loose" condition - plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE); - } - } - - uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; - uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; - uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; - - // Reward winner team - if (team == winner) - { - if (IsRandom() || BattleGroundMgr::IsBGWeekend(GetTypeID())) - { - UpdatePlayerScore(plr, SCORE_BONUS_HONOR, GetBonusHonorFromKill(win_kills)); - plr->ModifyArenaPoints(win_arena); - if (!plr->GetRandomWinner()) - plr->SetRandomWinner(true); - } - - plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1); - } - else - { - if (IsRandom() || BattleGroundMgr::IsBGWeekend(GetTypeID())) - UpdatePlayerScore(plr, SCORE_BONUS_HONOR, GetBonusHonorFromKill(loos_kills)); - } - - - plr->SetHealth(plr->GetMaxHealth()); - plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); - plr->CombatStopWithPets(true); - - BlockMovement(plr); - - sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); - plr->GetSession()->SendPacket(&data); - - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType()); - plr->GetSession()->SendPacket(&data); - plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1); - } - - if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) - { - // update arena points only after increasing the player's match count! - //obsolete: winner_arena_team->UpdateArenaPointsHelper(); - //obsolete: loser_arena_team->UpdateArenaPointsHelper(); - // save the stat changes - winner_arena_team->SaveToDB(); - loser_arena_team->SaveToDB(); - // send updated arena team stats to players - // this way all arena team members will get notified, not only the ones who participated in this match - winner_arena_team->NotifyStatsChanged(); - loser_arena_team->NotifyStatsChanged(); - } - - if (winmsg_id) - SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL); -} - -uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const -{ - //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill) - uint32 maxLevel = (GetMaxLevel()<80)?GetMaxLevel():80; - return Trinity::Honor::hk_honor_at_level(maxLevel, kills); -} - -uint32 BattleGround::GetBattlemasterEntry() const -{ - switch(GetTypeID(true)) - { - case BATTLEGROUND_AV: return 15972; - case BATTLEGROUND_WS: return 14623; - case BATTLEGROUND_AB: return 14879; - case BATTLEGROUND_EY: return 22516; - case BATTLEGROUND_NA: return 20200; - default: return 0; - } -} - -void BattleGround::BlockMovement(Player *plr) -{ - plr->SetClientControl(plr, 0); // movement disabled NOTE: the effect will be automatically removed by client when the player is teleported from the battleground, so no need to send with uint8(1) in RemovePlayerAtLeave() -} - -void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket) -{ - uint32 team = GetPlayerTeam(guid); - bool participant = false; - // Remove from lists/maps - BattleGroundPlayerMap::iterator itr = m_Players.find(guid); - if (itr != m_Players.end()) - { - UpdatePlayersCountByTeam(team, true); // -1 player - m_Players.erase(itr); - // check if the player was a participant of the match, or only entered through gm command (goname) - participant = true; - } - - BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid); - if (itr2 != m_PlayerScores.end()) - { - delete itr2->second; // delete player's score - m_PlayerScores.erase(itr2); - } - - RemovePlayerFromResurrectQueue(guid); - - Player *plr = objmgr.GetPlayer(guid); - - // should remove spirit of redemption - if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); - - if (plr && !plr->isAlive()) // resurrect on exit - { - plr->ResurrectPlayer(1.0f); - plr->SpawnCorpseBones(); - } - - RemovePlayer(plr, guid); // BG subclass specific code - - if (participant) // if the player was a match participant, remove auras, calc rating, update queue - { - BattleGroundTypeId bgTypeId = GetTypeID(); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); - if (plr) - { - plr->ClearAfkReports(); - - if (!team) team = plr->GetTeam(); - - // if arena, remove the specific arena auras - if (isArena()) - { - plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out - bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) - - // unsummon current and summon old pet if there was one and there isn't a current pet - plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT); - plr->ResummonPetTemporaryUnSummonedIfAny(); - - if (isRated() && GetStatus() == STATUS_IN_PROGRESS) - { - //left a rated match while the encounter was in progress, consider as loser - ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); - ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team)); - if (winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) - loser_arena_team->MemberLost(plr,winner_arena_team->GetRating()); - } - } - if (SendPacket) - { - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0); - plr->GetSession()->SendPacket(&data); - } - - // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg - plr->RemoveBattleGroundQueueId(bgQueueTypeId); - } - else - // removing offline participant - { - if (isRated() && GetStatus() == STATUS_IN_PROGRESS) - { - //left a rated match while the encounter was in progress, consider as loser - ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); - ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team)); - if (others_arena_team && players_arena_team) - players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating()); - } - } - - // remove from raid group if player is member - if (Group *group = GetBgRaid(team)) - { - if (!group->RemoveMember(guid, 0)) // group was disbanded - { - SetBgRaid(team, NULL); - delete group; - } - } - DecreaseInvitedCount(team); - //we should update battleground queue, but only if bg isn't ending - if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE) - { - // a player has left the battleground, so there are free slots -> add to queue - AddToBGFreeSlotQueue(); - sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, GetBracketId()); - } - // Let others know - WorldPacket data; - sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid); - SendPacketToTeam(team, &data, plr, false); - } - - if (plr) - { - // Do next only if found in battleground - plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG. - // reset destination bg team - plr->SetBGTeam(0); - - if (Transport) - plr->TeleportToBGEntryPoint(); - - sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName()); - } - - //battleground object will be deleted next BattleGround::Update() call -} - -// this method is called when no players remains in battleground -void BattleGround::Reset() -{ - SetWinner(WINNER_NONE); - SetStatus(STATUS_WAIT_QUEUE); - SetStartTime(0); - SetEndTime(0); - SetLastResurrectTime(0); - SetArenaType(0); - SetRated(false); - - m_Events = 0; - - if (m_InvitedAlliance > 0 || m_InvitedHorde > 0) - sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde); - - m_InvitedAlliance = 0; - m_InvitedHorde = 0; - m_InBGFreeSlotQueue = false; - - m_Players.clear(); - - for (BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr) - delete itr->second; - m_PlayerScores.clear(); - - ResetBGSubclass(); -} - -void BattleGround::StartBattleGround() -{ - SetStartTime(0); - SetLastResurrectTime(0); - // add BG to free slot queue - AddToBGFreeSlotQueue(); - - // add bg to update list - // This must be done here, because we need to have already invited some players when first BG::Update() method is executed - // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes - sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this); - if (m_IsRated) - sLog.outArena("Arena match type: %u for Team1Id: %u - Team2Id: %u started.", m_ArenaType, m_ArenaTeamIds[BG_TEAM_ALLIANCE], m_ArenaTeamIds[BG_TEAM_HORDE]); -} - -void BattleGround::AddPlayer(Player *plr) -{ - // remove afk from player - if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK)) - plr->ToggleAFK(); - - // score struct must be created in inherited class - - uint64 guid = plr->GetGUID(); - uint32 team = plr->GetBGTeam(); - - BattleGroundPlayer bp; - bp.OfflineRemoveTime = 0; - bp.Team = team; - - // Add to list/maps - m_Players[guid] = bp; - - UpdatePlayersCountByTeam(team, false); // +1 player - - WorldPacket data; - sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr); - SendPacketToTeam(team, &data, plr, false); - - // BG Status packet - WorldPacket status; - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(m_TypeID, GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType()); - plr->GetSession()->SendPacket(&status); - - plr->RemoveAurasByType(SPELL_AURA_MOUNTED); - - // add arena specific auras - if (isArena()) - { - plr->RemoveArenaSpellCooldowns(); - plr->RemoveArenaAuras(); - plr->RemoveArenaEnchantments(TEMP_ENCHANTMENT_SLOT); - if (team == ALLIANCE) // gold - { - if (plr->GetTeam() == HORDE) - plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true); - else - plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true); - } - else // green - { - if (plr->GetTeam() == HORDE) - plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true); - else - plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true); - } - - plr->DestroyConjuredItems(true); - plr->UnsummonPetTemporaryIfAny(); - - if (GetStatus() == STATUS_WAIT_JOIN) // not started yet - { - plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true); - - plr->SetHealth(plr->GetMaxHealth()); - plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); - } - } - else - { - if (GetStatus() == STATUS_WAIT_JOIN) // not started yet - plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells. - } - - plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId()); - plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId()); - - // setup BG group membership - PlayerAddedToBGCheckIfBGIsRunning(plr); - AddOrSetPlayerToCorrectBgGroup(plr, guid, team); - - // Log - sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName()); -} - -/* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */ -void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team) -{ - Group* group = GetBgRaid(team); - if (!group) // first player joined - { - group = new Group; - SetBgRaid(team, group); - group->Create(plr_guid, plr->GetName()); - } - else // raid already exist - { - if (group->IsMember(plr_guid)) - { - uint8 subgroup = group->GetMemberGroup(plr_guid); - plr->SetBattleGroundRaid(group, subgroup); - } - else - { - group->AddMember(plr_guid, plr->GetName()); - if (Group* originalGroup = plr->GetOriginalGroup()) - if (originalGroup->IsLeader(plr_guid)) - group->ChangeLeader(plr_guid); - } - } -} - -// This method should be called when player logs into running battleground -void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid) -{ - // player is correct pointer - for (std::deque::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr) - { - if (*itr == plr_guid) - { - m_OfflineQueue.erase(itr); - break; - } - } - m_Players[plr_guid].OfflineRemoveTime = 0; - PlayerAddedToBGCheckIfBGIsRunning(player); - // if battleground is starting, then add preparation aura - // we don't have to do that, because preparation aura isn't removed when player logs out -} - -// This method should be called when player logs out from running battleground -void BattleGround::EventPlayerLoggedOut(Player* player) -{ - // player is correct pointer, it is checked in WorldSession::LogoutPlayer() - m_OfflineQueue.push_back(player->GetGUID()); - m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME; - if (GetStatus() == STATUS_IN_PROGRESS) - { - // drop flag and handle other cleanups - RemovePlayer(player, player->GetGUID()); - - // 1 player is logging out, if it is the last, then end arena! - if (isArena()) - if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam()))) - EndBattleGround(GetOtherTeam(player->GetTeam())); - } - - player->LeaveBattleground(); -} - -/* This method should be called only once ... it adds pointer to queue */ -void BattleGround::AddToBGFreeSlotQueue() -{ - // make sure to add only once - if (!m_InBGFreeSlotQueue && isBattleGround()) - { - sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); - m_InBGFreeSlotQueue = true; - } -} - -/* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/ -void BattleGround::RemoveFromBGFreeSlotQueue() -{ - // set to be able to re-add if needed - m_InBGFreeSlotQueue = false; - // uncomment this code when battlegrounds will work like instances - for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) - { - if ((*itr)->GetInstanceID() == m_InstanceID) - { - sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); - return; - } - } -} - -// get the number of free slots for team -// returns the number how many players can join battleground to MaxPlayersPerTeam -uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const -{ - //if BG is starting ... invite anyone - if (GetStatus() == STATUS_WAIT_JOIN) - return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; - //if BG is already started .. do not allow to join too much players of one faction - uint32 otherTeam; - uint32 otherIn; - if (Team == ALLIANCE) - { - otherTeam = GetInvitedCount(HORDE); - otherIn = GetPlayersCountByTeam(HORDE); - } - else - { - otherTeam = GetInvitedCount(ALLIANCE); - otherIn = GetPlayersCountByTeam(ALLIANCE); - } - if (GetStatus() == STATUS_IN_PROGRESS) - { - // difference based on ppl invited (not necessarily entered battle) - // default: allow 0 - uint32 diff = 0; - // allow join one person if the sides are equal (to fill up bg to minplayersperteam) - if (otherTeam == GetInvitedCount(Team)) - diff = 1; - // allow join more ppl if the other side has more players - else if (otherTeam > GetInvitedCount(Team)) - diff = otherTeam - GetInvitedCount(Team); - - // difference based on max players per team (don't allow inviting more) - uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; - // difference based on players who already entered - // default: allow 0 - uint32 diff3 = 0; - // allow join one person if the sides are equal (to fill up bg minplayersperteam) - if (otherIn == GetPlayersCountByTeam(Team)) - diff3 = 1; - // allow join more ppl if the other side has more players - else if (otherIn > GetPlayersCountByTeam(Team)) - diff3 = otherIn - GetPlayersCountByTeam(Team); - // or other side has less than minPlayersPerTeam - else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam()) - diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1; - - // return the minimum of the 3 differences - - // min of diff and diff 2 - diff = diff < diff2 ? diff : diff2; - // min of diff, diff2 and diff3 - return diff < diff3 ? diff : diff3 ; - } - return 0; -} - -bool BattleGround::HasFreeSlots() const -{ - return GetPlayersSize() < GetMaxPlayers(); -} - -void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) -{ - //this procedure is called from virtual function implemented in bg subclass - BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID()); - - if (itr == m_PlayerScores.end()) // player not found... - return; - - switch(type) - { - case SCORE_KILLING_BLOWS: // Killing blows - itr->second->KillingBlows += value; - break; - case SCORE_DEATHS: // Deaths - itr->second->Deaths += value; - break; - case SCORE_HONORABLE_KILLS: // Honorable kills - itr->second->HonorableKills += value; - break; - case SCORE_BONUS_HONOR: // Honor bonus - // do not add honor in arenas - if (isBattleGround()) - { - // reward honor instantly - if (doAddHonor) - { - Source->RewardHonor(NULL, 1, value);//RewardHonor calls UpdatePlayerScore with doAddHonor = false - }else itr->second->BonusHonor += value; - } - break; - //used only in EY, but in MSG_PVP_LOG_DATA opcode - case SCORE_DAMAGE_DONE: // Damage Done - itr->second->DamageDone += value; - break; - case SCORE_HEALING_DONE: // Healing Done - itr->second->HealingDone += value; - break; - default: - sLog.outError("BattleGround: Unknown player score type %u", type); - break; - } -} - -void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid) -{ - m_ReviveQueue[npc_guid].push_back(player_guid); - - Player *plr = objmgr.GetPlayer(player_guid); - if (!plr) - return; - - plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true); -} - -void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid) -{ - for (std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) - { - for (std::vector::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2) - { - if (*itr2 == player_guid) - { - (itr->second).erase(itr2); - - Player *plr = objmgr.GetPlayer(player_guid); - if (!plr) - return; - - plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT); - - return; - } - } - } -} - -bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 /*respawnTime*/) -{ - Map *map = GetBgMap(); - if (!map) - return false; - // must be created this way, adding to godatamap would add it to the base map of the instance - // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created - // so we must create it specific for this instance - GameObject * go = new GameObject; - if (!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, GetBgMap(), - PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY)) - { - sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); - sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry); - delete go; - return false; - } -/* - uint32 guid = go->GetGUIDLow(); - - // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata - // iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed - GameObjectData& data = objmgr.NewGOData(guid); - - data.id = entry; - data.mapid = GetMapId(); - data.posX = x; - data.posY = y; - data.posZ = z; - data.orientation = o; - data.rotation0 = rotation0; - data.rotation1 = rotation1; - data.rotation2 = rotation2; - data.rotation3 = rotation3; - data.spawntimesecs = respawnTime; - data.spawnMask = 1; - data.animprogress = 100; - data.go_state = 1; -*/ - // add to world, so it can be later looked up from HashMapHolder - map->Add(go); - m_BgObjects[type] = go->GetGUID(); - return true; -} - -//some doors aren't despawned so we cannot handle their closing in gameobject::update() -//it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code -void BattleGround::DoorClose(uint32 type) -{ - GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); - if (obj) - { - //if doors are open, close it - if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY) - { - //change state to allow door to be closed - obj->SetLootState(GO_READY); - obj->UseDoorOrButton(RESPAWN_ONE_DAY); - } - } - else - { - sLog.outError("BattleGround: Door object not found (cannot close doors)"); - } -} - -void BattleGround::DoorOpen(uint32 type) -{ - GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); - if (obj) - { - //change state to be sure they will be opened - obj->SetLootState(GO_READY); - obj->UseDoorOrButton(RESPAWN_ONE_DAY); - } - else - { - sLog.outError("BattleGround: Door object not found! - doors will be closed."); - } -} - -GameObject* BattleGround::GetBGObject(uint32 type) -{ - GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); - if (!obj) - sLog.outError("couldn't get gameobject %i",type); - return obj; -} - -Creature* BattleGround::GetBGCreature(uint32 type) -{ - Creature *creature = GetBgMap()->GetCreature(m_BgCreatures[type]); - if (!creature) - sLog.outError("couldn't get creature %i",type); - return creature; -} - -void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) -{ - Map * map = GetBgMap(); - if (!map) - return; - if (respawntime == 0) - { - GameObject *obj = map->GetGameObject(m_BgObjects[type]); - if (obj) - { - //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again - if (obj->getLootState() == GO_JUST_DEACTIVATED) - obj->SetLootState(GO_READY); - obj->SetRespawnTime(0); - map->Add(obj); - } - } - else - { - GameObject *obj = map->GetGameObject(m_BgObjects[type]); - if (obj) - { - map->Add(obj); - obj->SetRespawnTime(respawntime); - obj->SetLootState(GO_JUST_DEACTIVATED); - } - } -} - -Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime) -{ - Map * map = GetBgMap(); - if (!map) - return NULL; - - Creature* pCreature = new Creature; - if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, teamval, x, y, z, o)) - { - sLog.outError("Can't create creature entry: %u",entry); - delete pCreature; - return NULL; - } - - pCreature->SetHomePosition(x, y, z, o); - - CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(entry); - if (!cinfo) - { - sLog.outErrorDb("BattleGround::AddCreature: entry %u does not exist.", entry); - return NULL; - } - //force using DB speeds - pCreature->SetSpeed(MOVE_WALK, cinfo->speed_walk); - pCreature->SetSpeed(MOVE_RUN, cinfo->speed_run); - - map->Add(pCreature); - m_BgCreatures[type] = pCreature->GetGUID(); - - if (respawntime) - pCreature->SetRespawnDelay(respawntime); - - return pCreature; -} -/* -void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) -{ - Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId()); - if (!map) - return false; - - if (respawntime == 0) - { - Creature *obj = HashMapHolder::Find(m_BgCreatures[type]); - if (obj) - { - //obj->Respawn(); // bugged - obj->SetRespawnTime(0); - objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); - map->Add(obj); - } - } - else - { - Creature *obj = HashMapHolder::Find(m_BgCreatures[type]); - if (obj) - { - obj->setDeathState(DEAD); - obj->SetRespawnTime(respawntime); - map->Add(obj); - } - } -} -*/ -bool BattleGround::DelCreature(uint32 type) -{ - if (!m_BgCreatures[type]) - return true; - - Creature *cr = GetBgMap()->GetCreature(m_BgCreatures[type]); - if (!cr) - { - sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type])); - return false; - } - cr->AddObjectToRemoveList(); - m_BgCreatures[type] = 0; - return true; -} - -bool BattleGround::DelObject(uint32 type) -{ - if (!m_BgObjects[type]) - return true; - - GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); - if (!obj) - { - sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type])); - return false; - } - obj->SetRespawnTime(0); // not save respawn time - obj->Delete(); - m_BgObjects[type] = 0; - return true; -} - -bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team) -{ - uint32 entry = 0; - - if (team == ALLIANCE) - entry = BG_CREATURE_ENTRY_A_SPIRITGUIDE; - else - entry = BG_CREATURE_ENTRY_H_SPIRITGUIDE; - - Creature* pCreature = AddCreature(entry,type,team,x,y,z,o); - if (!pCreature) - { - sLog.outError("Can't create Spirit guide. BattleGround not created!"); - EndNow(); - return false; - } - - pCreature->setDeathState(DEAD); - - pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID()); - // aura - //TODO: Fix display here - //pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL); - // casting visual effect - pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL); - // correct cast speed - pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); - - //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true); - - return true; -} - -void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source) -{ - Trinity::BattleGroundChatBuilder bg_builder(type, entry, source); - Trinity::LocalizedPacketDo bg_do(bg_builder); - BroadcastWorker(bg_do); -} - -void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...) -{ - va_list ap; - va_start(ap, source); - - Trinity::BattleGroundChatBuilder bg_builder(type, entry, source, &ap); - Trinity::LocalizedPacketDo bg_do(bg_builder); - BroadcastWorker(bg_do); - - va_end(ap); -} - -void BattleGround::SendWarningToAll(int32 entry, ...) -{ - const char *format = objmgr.GetTrinityStringForDBCLocale(entry); - va_list ap; - char str [1024]; - va_start(ap, entry); - vsnprintf(str,1024,format, ap); - va_end(ap); - std::string msg = (std::string)str; - - WorldPacket data(SMSG_MESSAGECHAT, 200); - - data << (uint8)CHAT_MSG_RAID_BOSS_EMOTE; - data << (uint32)LANG_UNIVERSAL; - data << (uint64)0; - data << (uint32)0; // 2.1.0 - data << (uint32)1; - data << (uint8)0; - data << (uint64)0; - data << (uint32)(strlen(msg.c_str())+1); - data << msg.c_str(); - data << (uint8)0; - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) - if (plr->GetSession()) - plr->GetSession()->SendPacket(&data); -} - -void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2) -{ - Trinity::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2); - Trinity::LocalizedPacketDo bg_do(bg_builder); - BroadcastWorker(bg_do); -} - -void BattleGround::EndNow() -{ - RemoveFromBGFreeSlotQueue(); - SetStatus(STATUS_WAIT_LEAVE); - SetEndTime(0); -} - -//to be removed -const char *BattleGround::GetTrinityString(int32 entry) -{ - // FIXME: now we have different DBC locales and need localized message for each target client - return objmgr.GetTrinityStringForDBCLocale(entry); -} - -/* -important notice: -buffs aren't spawned/despawned when players captures anything -buffs are in their positions when battleground starts -*/ -void BattleGround::HandleTriggerBuff(uint64 const& go_guid) -{ - GameObject *obj = GetBgMap()->GetGameObject(go_guid); - if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned()) - return; - - //change buff type, when buff is used: - int32 index = m_BgObjects.size() - 1; - while (index >= 0 && m_BgObjects[index] != go_guid) - index--; - if (index < 0) - { - sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(true),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType()); - return; - } - - //randomly select new buff - uint8 buff = urand(0, 2); - uint32 entry = obj->GetEntry(); - if (m_BuffChange && entry != Buff_Entries[buff]) - { - //despawn current buff - SpawnBGObject(index, RESPAWN_ONE_DAY); - //set index for new one - for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex) - if (entry == Buff_Entries[currBuffTypeIndex]) - { - index -= currBuffTypeIndex; - index += buff; - } - } - - SpawnBGObject(index, BUFF_RESPAWN_TIME); -} - -void BattleGround::HandleKillPlayer(Player *player, Player *killer) -{ - //keep in mind that for arena this will have to be changed a bit - - // add +1 deaths - UpdatePlayerScore(player, SCORE_DEATHS, 1); - // add +1 kills to group and +1 killing_blows to killer - if (killer) - { - UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1); - UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1); - - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - Player *plr = objmgr.GetPlayer(itr->first); - - if (!plr || plr == killer) - continue; - - if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player)) - UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1); - } - } - - // to be able to remove insignia -- ONLY IN BattleGrounds - // give xp only in BattleGrounds - if (!isArena()) - { - player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - RewardXPAtKill(killer, player); - } -} - -// return the player's team based on battlegroundplayer info -// used in same faction arena matches mainly -uint32 BattleGround::GetPlayerTeam(uint64 guid) -{ - BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid); - if (itr != m_Players.end()) - return itr->second.Team; - return 0; -} - -uint32 BattleGround::GetOtherTeam(uint32 teamId) -{ - return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0; -} - -bool BattleGround::IsPlayerInBattleGround(uint64 guid) -{ - BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid); - if (itr != m_Players.end()) - return true; - return false; -} - -void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr) -{ - if (GetStatus() != STATUS_WAIT_LEAVE) - return; - - WorldPacket data; - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); - - BlockMovement(plr); - - sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); - plr->GetSession()->SendPacket(&data); - - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType()); - plr->GetSession()->SendPacket(&data); -} - -uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const -{ - int count = 0; - for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (itr->second.Team == Team) - { - Player * pl = objmgr.GetPlayer(itr->first); - if (pl && pl->isAlive() && !pl->HasByteFlag(UNIT_FIELD_BYTES_2, 3, FORM_SPIRITOFREDEMPTION)) - ++count; - } - } - return count; -} - -void BattleGround::SetHoliday(bool is_holiday) -{ - if (is_holiday) - m_HonorMode = BG_HOLIDAY; - else - m_HonorMode = BG_NORMAL; -} - -int32 BattleGround::GetObjectType(uint64 guid) -{ - for (uint32 i = 0; i < m_BgObjects.size(); ++i) - if (m_BgObjects[i] == guid) - return i; - sLog.outError("BattleGround: cheating? a player used a gameobject which isnt supposed to be a usable object!"); - return -1; -} - -void BattleGround::HandleKillUnit(Creature * /*creature*/, Player * /*killer*/) -{ -} - -void BattleGround::CheckArenaWinConditions() -{ - if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) - EndBattleGround(HORDE); - else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) - EndBattleGround(ALLIANCE); -} - -void BattleGround::UpdateArenaWorldState() -{ - UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); - UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); -} - -void BattleGround::SetBgRaid(uint32 TeamID, Group *bg_raid) -{ - Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; - if (old_raid) old_raid->SetBattlegroundGroup(NULL); - if (bg_raid) bg_raid->SetBattlegroundGroup(this); - old_raid = bg_raid; -} - -WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard(Player* player) -{ - return objmgr.GetClosestGraveYard(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam()); -} - -bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const -{ - BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team); - uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]); - return score >= minScore && score <= maxScore; -} - -void BattleGround::SetBracket(PvPDifficultyEntry const* bracketEntry) -{ - m_BracketId = bracketEntry->GetBracketId(); - SetLevelRange(bracketEntry->minLevel,bracketEntry->maxLevel); -} - -void BattleGround::RewardXPAtKill(Player* plr, Player* victim) -{ - if (!sWorld.getConfig(CONFIG_BG_XP_FOR_KILL) || !plr || !victim) - return; - - uint32 xp = 0; - uint32 count = 0; - uint32 sum_level = 0; - Player* member_with_max_level = NULL; - Player* not_gray_member_with_max_level = NULL; - - if (Group *pGroup = plr->GetGroup())//should be always in a raid group while in any bg - { - for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* member = itr->getSource(); - if (!member || !member->isAlive()) // only for alive - continue; - - if (!member->IsAtGroupRewardDistance(victim)) // at req. distance - continue; - - ++count; - sum_level += member->getLevel(); - if (!member_with_max_level || member_with_max_level->getLevel() < member->getLevel()) - member_with_max_level = member; - - uint32 gray_level = Trinity::XP::GetGrayLevel(member->getLevel()); - if (victim->getLevel() > gray_level && (!not_gray_member_with_max_level - || not_gray_member_with_max_level->getLevel() < member->getLevel())) - not_gray_member_with_max_level = member; - } - - if (member_with_max_level) - { - xp = !not_gray_member_with_max_level ? 0 : Trinity::XP::Gain(not_gray_member_with_max_level, victim); - - if (!xp) - return; - - float group_rate = 1.0f; - - for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* pGroupGuy = itr->getSource(); - if (!pGroupGuy) - continue; - - if (!pGroupGuy->IsAtGroupRewardDistance(victim)) - continue; // member (alive or dead) or his corpse at req. distance - - float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level; - - // XP updated only for alive group member - if (pGroupGuy->isAlive() && not_gray_member_with_max_level && pGroupGuy->getLevel() <= not_gray_member_with_max_level->getLevel()) - { - uint32 itr_xp = (member_with_max_level == not_gray_member_with_max_level) ? uint32(xp*rate) : uint32((xp*rate/2)+1); - - // handle SPELL_AURA_MOD_XP_PCT auras - Unit::AuraEffectList const& ModXPPctAuras = plr->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT); - for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) - itr_xp = uint32(itr_xp*(1.0f + (*i)->GetAmount() / 100.0f)); - - pGroupGuy->GiveXP(itr_xp, victim); - if (Pet* pet = pGroupGuy->GetPet()) - pet->GivePetXP(itr_xp/2); - } - } - } - } - else//should be always in a raid group while in any BG, but you never know... - { - xp = Trinity::XP::Gain(plr, victim); - - if (!xp) - return; - - // handle SPELL_AURA_MOD_XP_PCT auras - Unit::AuraEffectList const& ModXPPctAuras = plr->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT); - for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) - xp = uint32(xp*(1.0f + (*i)->GetAmount() / 100.0f)); - - plr->GiveXP(xp, victim); - - if (Pet* pet = plr->GetPet()) - pet->GivePetXP(xp); - } -} \ No newline at end of file diff --git a/src/server/game/BattleGround.h b/src/server/game/BattleGround.h deleted file mode 100644 index 6f46b6f8f6d..00000000000 --- a/src/server/game/BattleGround.h +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUND_H -#define __BATTLEGROUND_H - -#include "Common.h" -#include "SharedDefines.h" -#include "DBCEnums.h" - -class Creature; -class GameObject; -class Group; -class Player; -class WorldPacket; -class BattleGroundMap; - -struct PvPDifficultyEntry; -struct WorldSafeLocsEntry; - -enum BattleGroundSounds -{ - SOUND_HORDE_WINS = 8454, - SOUND_ALLIANCE_WINS = 8455, - SOUND_BG_START = 3439, - SOUND_BG_START_L70ETC = 11803, -}; - -enum BattleGroundQuests -{ - SPELL_WS_QUEST_REWARD = 43483, - SPELL_AB_QUEST_REWARD = 43484, - SPELL_AV_QUEST_REWARD = 43475, - SPELL_AV_QUEST_KILLED_BOSS = 23658, - SPELL_EY_QUEST_REWARD = 43477, - SPELL_SA_QUEST_REWARD = 61213, - SPELL_AB_QUEST_REWARD_4_BASES = 24061, - SPELL_AB_QUEST_REWARD_5_BASES = 24064 -}; - -enum BattleGroundMarks -{ - SPELL_WS_MARK_LOSER = 24950, - SPELL_WS_MARK_WINNER = 24951, - SPELL_AB_MARK_LOSER = 24952, - SPELL_AB_MARK_WINNER = 24953, - SPELL_AV_MARK_LOSER = 24954, - SPELL_AV_MARK_WINNER = 24955, - SPELL_SA_MARK_WINNER = 61160, - SPELL_SA_MARK_LOSER = 61159, - ITEM_AV_MARK_OF_HONOR = 20560, - ITEM_WS_MARK_OF_HONOR = 20558, - ITEM_AB_MARK_OF_HONOR = 20559, - ITEM_EY_MARK_OF_HONOR = 29024, - ITEM_SA_MARK_OF_HONOR = 42425 -}; - -enum BattleGroundMarksCount -{ - ITEM_WINNER_COUNT = 3, - ITEM_LOSER_COUNT = 1 -}; - -enum BattleGroundCreatures -{ - BG_CREATURE_ENTRY_A_SPIRITGUIDE = 13116, // alliance - BG_CREATURE_ENTRY_H_SPIRITGUIDE = 13117, // horde -}; - -enum BattleGroundSpells -{ - SPELL_WAITING_FOR_RESURRECT = 2584, // Waiting to Resurrect - SPELL_SPIRIT_HEAL_CHANNEL = 22011, // Spirit Heal Channel - SPELL_SPIRIT_HEAL = 22012, // Spirit Heal - SPELL_RESURRECTION_VISUAL = 24171, // Resurrection Impact Visual - SPELL_ARENA_PREPARATION = 32727, // use this one, 32728 not correct - SPELL_ALLIANCE_GOLD_FLAG = 32724, - SPELL_ALLIANCE_GREEN_FLAG = 32725, - SPELL_HORDE_GOLD_FLAG = 35774, - SPELL_HORDE_GREEN_FLAG = 35775, - SPELL_PREPARATION = 44521, // Preparation - SPELL_SPIRIT_HEAL_MANA = 44535, // Spirit Heal - SPELL_RECENTLY_DROPPED_FLAG = 42792, // Recently Dropped Flag - SPELL_AURA_PLAYER_INACTIVE = 43681, // Inactive - SPELL_HONORABLE_DEFENDER_25Y = 68652, // +50% honor when standing at a capture point that you control, 25yards radius (added in 3.2) - SPELL_HONORABLE_DEFENDER_60Y = 66157 // +50% honor when standing at a capture point that you control, 60yards radius (added in 3.2), probably for 40+ player battlegrounds -}; - -enum BattleGroundTimeIntervals -{ - RESURRECTION_INTERVAL = 30000, // ms - //REMIND_INTERVAL = 10000, // ms - INVITATION_REMIND_TIME = 20000, // ms - INVITE_ACCEPT_WAIT_TIME = 40000, // ms - TIME_TO_AUTOREMOVE = 120000, // ms - MAX_OFFLINE_TIME = 300, // secs - RESPAWN_ONE_DAY = 86400, // secs - RESPAWN_IMMEDIATELY = 0, // secs - BUFF_RESPAWN_TIME = 180, // secs -}; - -enum BattleGroundStartTimeIntervals -{ - BG_START_DELAY_2M = 120000, // ms (2 minutes) - BG_START_DELAY_1M = 60000, // ms (1 minute) - BG_START_DELAY_30S = 30000, // ms (30 seconds) - BG_START_DELAY_15S = 15000, // ms (15 seconds) Used only in arena - BG_START_DELAY_NONE = 0, // ms -}; - -enum BattleGroundBuffObjects -{ - BG_OBJECTID_SPEEDBUFF_ENTRY = 179871, - BG_OBJECTID_REGENBUFF_ENTRY = 179904, - BG_OBJECTID_BERSERKERBUFF_ENTRY = 179905 -}; - -enum BattleGroundRandomRewards -{ - BG_REWARD_WINNER_HONOR_FIRST = 30, - BG_REWARD_WINNER_ARENA_FIRST = 25, - BG_REWARD_WINNER_HONOR_LAST = 15, - BG_REWARD_WINNER_ARENA_LAST = 0, - BG_REWARD_LOOSER_HONOR_FIRST = 5, - BG_REWARD_LOOSER_HONOR_LAST = 5 -}; - -const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENBUFF_ENTRY, BG_OBJECTID_BERSERKERBUFF_ENTRY }; - -enum BattleGroundStatus -{ - STATUS_NONE = 0, // first status, should mean bg is not instance - STATUS_WAIT_QUEUE = 1, // means bg is empty and waiting for queue - STATUS_WAIT_JOIN = 2, // this means, that BG has already started and it is waiting for more players - STATUS_IN_PROGRESS = 3, // means bg is running - STATUS_WAIT_LEAVE = 4 // means some faction has won BG and it is ending -}; - -struct BattleGroundPlayer -{ - time_t OfflineRemoveTime; // for tracking and removing offline players from queue after 5 minutes - uint32 Team; // Player's team -}; - -struct BattleGroundObjectInfo -{ - BattleGroundObjectInfo() : object(NULL), timer(0), spellid(0) {} - - GameObject *object; - int32 timer; - uint32 spellid; -}; - -// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time -enum BattleGroundQueueTypeId -{ - BATTLEGROUND_QUEUE_NONE = 0, - BATTLEGROUND_QUEUE_AV = 1, - BATTLEGROUND_QUEUE_WS = 2, - BATTLEGROUND_QUEUE_AB = 3, - BATTLEGROUND_QUEUE_EY = 4, - BATTLEGROUND_QUEUE_SA = 5, - BATTLEGROUND_QUEUE_IC = 6, - BATTLEGROUND_QUEUE_RB = 7, - BATTLEGROUND_QUEUE_2v2 = 8, - BATTLEGROUND_QUEUE_3v3 = 9, - BATTLEGROUND_QUEUE_5v5 = 10, - MAX_BATTLEGROUND_QUEUE_TYPES -}; - -enum ScoreType -{ - SCORE_KILLING_BLOWS = 1, - SCORE_DEATHS = 2, - SCORE_HONORABLE_KILLS = 3, - SCORE_BONUS_HONOR = 4, - //EY, but in MSG_PVP_LOG_DATA opcode! - SCORE_DAMAGE_DONE = 5, - SCORE_HEALING_DONE = 6, - //WS - SCORE_FLAG_CAPTURES = 7, - SCORE_FLAG_RETURNS = 8, - //AB - SCORE_BASES_ASSAULTED = 9, - SCORE_BASES_DEFENDED = 10, - //AV - SCORE_GRAVEYARDS_ASSAULTED = 11, - SCORE_GRAVEYARDS_DEFENDED = 12, - SCORE_TOWERS_ASSAULTED = 13, - SCORE_TOWERS_DEFENDED = 14, - SCORE_MINES_CAPTURED = 15, - SCORE_LEADERS_KILLED = 16, - SCORE_SECONDARY_OBJECTIVES = 17, - //SOTA - SCORE_DESTROYED_DEMOLISHER = 18, - SCORE_DESTROYED_WALL = 19 -}; - -enum ArenaType -{ - ARENA_TYPE_2v2 = 2, - ARENA_TYPE_3v3 = 3, - ARENA_TYPE_5v5 = 5 -}; - -enum BattleGroundType -{ - TYPE_BATTLEGROUND = 3, - TYPE_ARENA = 4 -}; - -enum BattleGroundWinner -{ - WINNER_HORDE = 0, - WINNER_ALLIANCE = 1, - WINNER_NONE = 2 -}; - -enum BattleGroundTeamId -{ - BG_TEAM_ALLIANCE = 0, - BG_TEAM_HORDE = 1 -}; -#define BG_TEAMS_COUNT 2 - -enum BattleGroundStartingEvents -{ - BG_STARTING_EVENT_NONE = 0x00, - BG_STARTING_EVENT_1 = 0x01, - BG_STARTING_EVENT_2 = 0x02, - BG_STARTING_EVENT_3 = 0x04, - BG_STARTING_EVENT_4 = 0x08 -}; - -enum BattleGroundStartingEventsIds -{ - BG_STARTING_EVENT_FIRST = 0, - BG_STARTING_EVENT_SECOND = 1, - BG_STARTING_EVENT_THIRD = 2, - BG_STARTING_EVENT_FOURTH = 3 -}; -#define BG_STARTING_EVENT_COUNT 4 - -enum BG_OBJECT_DMG_HIT_TYPE -{ - BG_OBJECT_DMG_HIT_TYPE_JUST_DAMAGED = 0, - BG_OBJECT_DMG_HIT_TYPE_DAMAGED = 1, - BG_OBJECT_DMG_HIT_TYPE_JUST_HIGH_DAMAGED = 2, - BG_OBJECT_DMG_HIT_TYPE_HIGH_DAMAGED = 3, - BG_OBJECT_DMG_HIT_TYPE_JUST_DESTROYED = 4 -}; - -enum GroupJoinBattlegroundResult -{ - // positive values are indexes in BattlemasterList.dbc - ERR_GROUP_JOIN_BATTLEGROUND_FAIL = 0, // Your group has joined a battleground queue, but you are not eligible (showed for non existing BattlemasterList.dbc indexes) - ERR_BATTLEGROUND_NONE = -1, // not show anything - ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS = -2, // You cannot join the battleground yet because you or one of your party members is flagged as a Deserter. - ERR_ARENA_TEAM_PARTY_SIZE = -3, // Incorrect party size for this arena. - ERR_BATTLEGROUND_TOO_MANY_QUEUES = -4, // You can only be queued for 2 battles at once - ERR_BATTLEGROUND_CANNOT_QUEUE_FOR_RATED = -5, // You cannot queue for a rated match while queued for other battles - ERR_BATTLEDGROUND_QUEUED_FOR_RATED = -6, // You cannot queue for another battle while queued for a rated arena match - ERR_BATTLEGROUND_TEAM_LEFT_QUEUE = -7, // Your team has left the arena queue - ERR_BATTLEGROUND_NOT_IN_BATTLEGROUND = -8, // You can't do that in a battleground. - ERR_BATTLEGROUND_JOIN_XP_GAIN = -9, // wtf, doesn't exist in client... - ERR_BATTLEGROUND_JOIN_RANGE_INDEX = -10, // Cannot join the queue unless all members of your party are in the same battleground level range. - ERR_BATTLEGROUND_JOIN_TIMED_OUT = -11, // %s was unavailable to join the queue. (uint64 guid exist in client cache) - ERR_BATTLEGROUND_JOIN_FAILED = -12, // Join as a group failed (uint64 guid doesn't exist in client cache) - ERR_LFG_CANT_USE_BATTLEGROUND = -13, // You cannot queue for a battleground or arena while using the dungeon system. - ERR_IN_RANDOM_BG = -14, // Can't do that while in a Random Battleground queue. - ERR_IN_NON_RANDOM_BG = -15, // Can't queue for Random Battleground while in another Battleground queue. -}; - -class BattleGroundScore -{ - public: - BattleGroundScore() : KillingBlows(0), Deaths(0), HonorableKills(0), - BonusHonor(0), DamageDone(0), HealingDone(0) - {} - virtual ~BattleGroundScore() {} //virtual destructor is used when deleting score from scores map - - uint32 KillingBlows; - uint32 Deaths; - uint32 HonorableKills; - uint32 BonusHonor; - uint32 DamageDone; - uint32 HealingDone; -}; - -enum BGHonorMode -{ - BG_NORMAL = 0, - BG_HOLIDAY, - BG_HONOR_MODE_NUM -}; - -/* -This class is used to: -1. Add player to battleground -2. Remove player from battleground -3. some certain cases, same for all battlegrounds -4. It has properties same for all battlegrounds -*/ -class BattleGround -{ - friend class BattleGroundMgr; - - public: - /* Construction */ - BattleGround(); - /*BattleGround(const BattleGround& bg);*/ - virtual ~BattleGround(); - virtual void Update(uint32 diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version - virtual bool SetupBattleGround() // must be implemented in BG subclass - { - return true; - } - virtual void Reset(); // resets all common properties for battlegrounds, must be implemented and called in BG subclass - virtual void StartingEventCloseDoors() {} - virtual void StartingEventOpenDoors() {} - virtual void ResetBGSubclass() // must be implemented in BG subclass - { - } - - virtual void DestroyGate(Player* pl, GameObject* go, uint32 destroyedEvent) {} - - /* achievement req. */ - virtual bool IsAllNodesConrolledByTeam(uint32 /*team*/) const { return false; } - bool IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const; - - /* Battleground */ - // Get methods: - char const* GetName() const { return m_Name; } - BattleGroundTypeId GetTypeID(bool GetRandom = false) const { return GetRandom ? m_RandomTypeID : m_TypeID; } - BattleGroundBracketId GetBracketId() const { return m_BracketId; } - uint32 GetInstanceID() const { return m_InstanceID; } - BattleGroundStatus GetStatus() const { return m_Status; } - uint32 GetClientInstanceID() const { return m_ClientInstanceID; } - uint32 GetStartTime() const { return m_StartTime; } - uint32 GetEndTime() const { return m_EndTime; } - uint32 GetLastResurrectTime() const { return m_LastResurrectTime; } - uint32 GetMaxPlayers() const { return m_MaxPlayers; } - uint32 GetMinPlayers() const { return m_MinPlayers; } - - uint32 GetMinLevel() const { return m_LevelMin; } - uint32 GetMaxLevel() const { return m_LevelMax; } - - uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; } - uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; } - - int32 GetStartDelayTime() const { return m_StartDelayTime; } - uint8 GetArenaType() const { return m_ArenaType; } - uint8 GetWinner() const { return m_Winner; } - uint32 GetBattlemasterEntry() const; - uint32 GetBonusHonorFromKill(uint32 kills) const; - bool IsRandom() { return m_IsRandom; } - - // Set methods: - void SetName(char const* Name) { m_Name = Name; } - void SetTypeID(BattleGroundTypeId TypeID) { m_TypeID = TypeID; } - void SetRandomTypeID(BattleGroundTypeId TypeID) { m_RandomTypeID = TypeID; } - //here we can count minlevel and maxlevel for players - void SetBracket(PvPDifficultyEntry const* bracketEntry); - void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; } - void SetStatus(BattleGroundStatus Status) { m_Status = Status; } - void SetClientInstanceID(uint32 InstanceID) { m_ClientInstanceID = InstanceID; } - void SetStartTime(uint32 Time) { m_StartTime = Time; } - void SetEndTime(uint32 Time) { m_EndTime = Time; } - void SetLastResurrectTime(uint32 Time) { m_LastResurrectTime = Time; } - void SetMaxPlayers(uint32 MaxPlayers) { m_MaxPlayers = MaxPlayers; } - void SetMinPlayers(uint32 MinPlayers) { m_MinPlayers = MinPlayers; } - void SetLevelRange(uint32 min, uint32 max) { m_LevelMin = min; m_LevelMax = max; } - void SetRated(bool state) { m_IsRated = state; } - void SetArenaType(uint8 type) { m_ArenaType = type; } - void SetArenaorBGType(bool _isArena) { m_IsArena = _isArena; } - void SetWinner(uint8 winner) { m_Winner = winner; } - - void ModifyStartDelayTime(int diff) { m_StartDelayTime -= diff; } - void SetStartDelayTime(int Time) { m_StartDelayTime = Time; } - - void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; } - void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; } - - void AddToBGFreeSlotQueue(); //this queue will be useful when more battlegrounds instances will be available - void RemoveFromBGFreeSlotQueue(); //this method could delete whole BG instance, if another free is available - - void DecreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? --m_InvitedAlliance : --m_InvitedHorde; } - void IncreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; } - - void SetRandom(bool isRandom) { m_IsRandom = isRandom; } - uint32 GetInvitedCount(uint32 team) const - { - if (team == ALLIANCE) - return m_InvitedAlliance; - else - return m_InvitedHorde; - } - bool HasFreeSlots() const; - uint32 GetFreeSlotsForTeam(uint32 Team) const; - - bool isArena() const { return m_IsArena; } - bool isBattleGround() const { return !m_IsArena; } - bool isRated() const { return m_IsRated; } - - typedef std::map BattleGroundPlayerMap; - BattleGroundPlayerMap const& GetPlayers() const { return m_Players; } - uint32 GetPlayersSize() const { return m_Players.size(); } - - typedef std::map BattleGroundScoreMap; - BattleGroundScoreMap::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); } - BattleGroundScoreMap::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); } - uint32 GetPlayerScoresSize() const { return m_PlayerScores.size(); } - - uint32 GetReviveQueueSize() const { return m_ReviveQueue.size(); } - - void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid); - void RemovePlayerFromResurrectQueue(uint64 player_guid); - - void StartBattleGround(); - - GameObject* GetBGObject(uint32 type); - Creature* GetBGCreature(uint32 type); - /* Location */ - void SetMapId(uint32 MapID) { m_MapId = MapID; } - uint32 GetMapId() const { return m_MapId; } - - /* Map pointers */ - void SetBgMap(BattleGroundMap* map) { m_Map = map; } - BattleGroundMap* GetBgMap() - { - ASSERT(m_Map); - return m_Map; - } - - void SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O); - void GetTeamStartLoc(uint32 TeamID, float &X, float &Y, float &Z, float &O) const - { - BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID); - X = m_TeamStartLocX[idx]; - Y = m_TeamStartLocY[idx]; - Z = m_TeamStartLocZ[idx]; - O = m_TeamStartLocO[idx]; - } - - /* Packet Transfer */ - // method that should fill worldpacket with actual world states (not yet implemented for all battlegrounds!) - virtual void FillInitialWorldStates(WorldPacket& /*data*/) {} - void SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender = NULL, bool self = true); - void SendPacketToAll(WorldPacket *packet); - void YellToAll(Creature* creature, const char* text, uint32 language); - - template - void BroadcastWorker(Do& _do); - - void PlaySoundToTeam(uint32 SoundID, uint32 TeamID); - void PlaySoundToAll(uint32 SoundID); - void CastSpellOnTeam(uint32 SpellID, uint32 TeamID); - void RewardHonorToTeam(uint32 Honor, uint32 TeamID); - void RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID); - void UpdateWorldState(uint32 Field, uint32 Value); - void UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source); - void EndBattleGround(uint32 winner); - void BlockMovement(Player *plr); - - void SendWarningToAll(int32 entry, ...); - void SendMessageToAll(int32 entry, ChatMsg type, Player const* source = NULL); - void PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...); - - // specialized version with 2 string id args - void SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 strId1 = 0, int32 strId2 = 0); - - /* Raid Group */ - Group *GetBgRaid(uint32 TeamID) const { return TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; } - void SetBgRaid(uint32 TeamID, Group *bg_raid); - - virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - - static BattleGroundTeamId GetTeamIndexByTeamId(uint32 Team) { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } - uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } - uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases - void UpdatePlayersCountByTeam(uint32 Team, bool remove) - { - if (remove) - --m_PlayersCount[GetTeamIndexByTeamId(Team)]; - else - ++m_PlayersCount[GetTeamIndexByTeamId(Team)]; - } - - // used for rated arena battles - void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; } - uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } - void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; } - int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } - void CheckArenaWinConditions(); - void UpdateArenaWorldState(); - - /* Triggers handle */ - // must be implemented in BG subclass - virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} - // must be implemented in BG subclass if need AND call base class generic code - virtual void HandleKillPlayer(Player *player, Player *killer); - virtual void HandleKillUnit(Creature* /*unit*/, Player* /*killer*/); - - /* Battleground events */ - virtual void EventPlayerDroppedFlag(Player* /*player*/) {} - virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {} - virtual void EventPlayerCapturedFlag(Player* /*player*/) {} - void EventPlayerLoggedIn(Player* player, uint64 plr_guid); - void EventPlayerLoggedOut(Player* player); - virtual void EventPlayerDamagedGO(Player* plr, GameObject* go, uint8 hitType, uint32 destroyedEvent) {} - virtual void EventPlayerUsedGO(Player* /*player*/, GameObject* /*go*/){} - - /* Death related */ - virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); - - virtual void AddPlayer(Player *plr); // must be implemented in BG subclass - - void AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team); - - virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); - // can be extended in in BG subclass - - void HandleTriggerBuff(uint64 const& go_guid); - void SetHoliday(bool is_holiday); - - // TODO: make this protected: - typedef std::vector BGObjects; - typedef std::vector BGCreatures; - BGObjects m_BgObjects; - BGCreatures m_BgCreatures; - void SpawnBGObject(uint32 type, uint32 respawntime); - bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime = 0); -// void SpawnBGCreature(uint32 type, uint32 respawntime); - Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime = 0); - bool DelCreature(uint32 type); - bool DelObject(uint32 type); - bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team); - int32 GetObjectType(uint64 guid); - - void DoorOpen(uint32 type); - void DoorClose(uint32 type); - //to be removed - const char *GetTrinityString(int32 entry); - - virtual bool HandlePlayerUnderMap(Player * /*plr*/) { return false; } - - // since arenas can be AvA or Hvh, we have to get the "temporary" team of a player - uint32 GetPlayerTeam(uint64 guid); - uint32 GetOtherTeam(uint32 teamId); - bool IsPlayerInBattleGround(uint64 guid); - - void SetDeleteThis() {m_SetDeleteThis = true;} - - /* virtual score-array - get's used in bg-subclasses */ - int32 m_TeamScores[BG_TEAMS_COUNT]; - - void RewardXPAtKill(Player* plr, Player* victim); - - protected: - //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround - void EndNow(); - void PlayerAddedToBGCheckIfBGIsRunning(Player* plr); - - /* Scorekeeping */ - - BattleGroundScoreMap m_PlayerScores; // Player scores - // must be implemented in BG subclass - virtual void RemovePlayer(Player * /*player*/, uint64 /*guid*/) {} - - /* Player lists, those need to be accessible by inherited classes */ - BattleGroundPlayerMap m_Players; - // Spirit Guide guid + Player list GUIDS - std::map > m_ReviveQueue; - - /* - these are important variables used for starting messages - */ - uint8 m_Events; - BattleGroundStartTimeIntervals m_StartDelayTimes[BG_STARTING_EVENT_COUNT]; - //this must be filled in constructors! - uint32 m_StartMessageIds[BG_STARTING_EVENT_COUNT]; - - bool m_BuffChange; - bool m_IsRandom; - - BGHonorMode m_HonorMode; - private: - /* Battleground */ - BattleGroundTypeId m_TypeID; - BattleGroundTypeId m_RandomTypeID; - uint32 m_InstanceID; //BattleGround Instance's GUID! - BattleGroundStatus m_Status; - uint32 m_ClientInstanceID; //the instance-id which is sent to the client and without any other internal use - uint32 m_StartTime; - int32 m_EndTime; // it is set to 120000 when bg is ending and it decreases itself - uint32 m_LastResurrectTime; - BattleGroundBracketId m_BracketId; - uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 - bool m_InBGFreeSlotQueue; // used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque - bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave - bool m_IsArena; - uint8 m_Winner; // 0=alliance, 1=horde, 2=none - int32 m_StartDelayTime; - bool m_IsRated; // is this battle rated? - bool m_PrematureCountDown; - uint32 m_PrematureCountDownTimer; - char const *m_Name; - - /* Player lists */ - std::vector m_ResurrectQueue; // Player GUID - std::deque m_OfflineQueue; // Player GUID - - /* Invited counters are useful for player invitation to BG - do not allow, if BG is started to one faction to have 2 more players than another faction */ - /* Invited counters will be changed only when removing already invited player from queue, removing player from battleground and inviting player to BG */ - /* Invited players counters*/ - uint32 m_InvitedAlliance; - uint32 m_InvitedHorde; - - /* Raid Group */ - Group *m_BgRaids[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde - - /* Players count by team */ - uint32 m_PlayersCount[BG_TEAMS_COUNT]; - - /* Arena team ids by team */ - uint32 m_ArenaTeamIds[BG_TEAMS_COUNT]; - - int32 m_ArenaTeamRatingChanges[BG_TEAMS_COUNT]; - - /* Limits */ - uint32 m_LevelMin; - uint32 m_LevelMax; - uint32 m_MaxPlayersPerTeam; - uint32 m_MaxPlayers; - uint32 m_MinPlayersPerTeam; - uint32 m_MinPlayers; - - /* Start location */ - uint32 m_MapId; - BattleGroundMap* m_Map; - float m_TeamStartLocX[BG_TEAMS_COUNT]; - float m_TeamStartLocY[BG_TEAMS_COUNT]; - float m_TeamStartLocZ[BG_TEAMS_COUNT]; - float m_TeamStartLocO[BG_TEAMS_COUNT]; -}; -#endif - diff --git a/src/server/game/BattleGroundAA.cpp b/src/server/game/BattleGroundAA.cpp deleted file mode 100644 index 56cf3ebed15..00000000000 --- a/src/server/game/BattleGroundAA.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundAA.h" -#include "Language.h" -#include "Player.h" - -BattleGroundAA::BattleGroundAA() -{ - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set messageIds - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; -} - -BattleGroundAA::~BattleGroundAA() -{ - -} - -void BattleGroundAA::Update(uint32 diff) -{ - BattleGround::Update(diff); -} - -void BattleGroundAA::StartingEventCloseDoors() -{ -} - -void BattleGroundAA::StartingEventOpenDoors() -{ -} - -void BattleGroundAA::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundAAScore* sc = new BattleGroundAAScore; - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundAA::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) -{ -} - -void BattleGroundAA::HandleKillPlayer(Player* player, Player* killer) -{ - BattleGround::HandleKillPlayer(player, killer); -} - -void BattleGroundAA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) -{ -} - -bool BattleGroundAA::SetupBattleGround() -{ - return true; -} - diff --git a/src/server/game/BattleGroundAA.h b/src/server/game/BattleGroundAA.h deleted file mode 100644 index a13833697cf..00000000000 --- a/src/server/game/BattleGroundAA.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDAA_H -#define __BATTLEGROUNDAA_H - -class BattleGround; - -class BattleGroundAAScore : public BattleGroundScore -{ - public: - BattleGroundAAScore() {}; - virtual ~BattleGroundAAScore() {}; - //TODO fix me -}; - -class BattleGroundAA : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundAA(); - ~BattleGroundAA(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - void HandleKillPlayer(Player* player, Player *killer); -}; -#endif - diff --git a/src/server/game/BattleGroundAB.cpp b/src/server/game/BattleGroundAB.cpp deleted file mode 100644 index 38671e85597..00000000000 --- a/src/server/game/BattleGroundAB.cpp +++ /dev/null @@ -1,715 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "World.h" -#include "WorldPacket.h" -#include "ObjectMgr.h" -#include "BattleGroundMgr.h" -#include "BattleGround.h" -#include "BattleGroundAB.h" -#include "Creature.h" -#include "Language.h" -#include "Object.h" -#include "Player.h" -#include "Util.h" - -// these variables aren't used outside of this file, so declare them only here -uint32 BG_AB_HonorScoreTicks[BG_HONOR_MODE_NUM] = { - 330, // normal honor - 200 // holiday -}; - -uint32 BG_AB_ReputationScoreTicks[BG_HONOR_MODE_NUM] = { - 200, // normal honor - 150 // holiday -}; - -BattleGroundAB::BattleGroundAB() -{ - m_BuffChange = true; - m_BgObjects.resize(BG_AB_OBJECT_MAX); - m_BgCreatures.resize(BG_AB_ALL_NODES_COUNT + 5);//+5 for aura triggers - - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AB_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AB_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AB_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AB_HAS_BEGUN; -} - -BattleGroundAB::~BattleGroundAB() -{ -} - -void BattleGroundAB::Update(uint32 diff) -{ - BattleGround::Update(diff); - - if (GetStatus() == STATUS_IN_PROGRESS) - { - int team_points[BG_TEAMS_COUNT] = { 0, 0 }; - - for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) - { - // 3 sec delay to spawn new banner instead previous despawned one - if (m_BannerTimers[node].timer) - { - if (m_BannerTimers[node].timer > diff) - m_BannerTimers[node].timer -= diff; - else - { - m_BannerTimers[node].timer = 0; - _CreateBanner(node, m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false); - } - } - - // 1-minute to occupy a node from contested state - if (m_NodeTimers[node]) - { - if (m_NodeTimers[node] > diff) - m_NodeTimers[node] -= diff; - else - { - m_NodeTimers[node] = 0; - // Change from contested to occupied ! - uint8 teamIndex = m_Nodes[node]-1; - m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] += 2; - // burn current contested banner - _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex); - // create new occupied banner - _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); - _SendNodeUpdate(node); - _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); - // Message to chatlog - - if (teamIndex == 0) - { - // FIXME: team and node names not localized - SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE,NULL,LANG_BG_AB_ALLY,_GetNodeNameId(node)); - PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); - } - else - { - // FIXME: team and node names not localized - SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE,NULL,LANG_BG_AB_HORDE,_GetNodeNameId(node)); - PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); - } - } - } - - for (int team = 0; team < BG_TEAMS_COUNT; ++team) - if (m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED) - ++team_points[team]; - } - - // Accumulate points - for (int team = 0; team < BG_TEAMS_COUNT; ++team) - { - int points = team_points[team]; - if (!points) - continue; - m_lastTick[team] += diff; - if (m_lastTick[team] > BG_AB_TickIntervals[points]) - { - m_lastTick[team] -= BG_AB_TickIntervals[points]; - m_TeamScores[team] += BG_AB_TickPoints[points]; - m_HonorScoreTics[team] += BG_AB_TickPoints[points]; - m_ReputationScoreTics[team] += BG_AB_TickPoints[points]; - if (m_ReputationScoreTics[team] >= m_ReputationTics) - { - (team == BG_TEAM_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE); - m_ReputationScoreTics[team] -= m_ReputationTics; - } - if (m_HonorScoreTics[team] >= m_HonorTics) - { - RewardHonorToTeam(GetBonusHonorFromKill(1), (team == BG_TEAM_ALLIANCE) ? ALLIANCE : HORDE); - m_HonorScoreTics[team] -= m_HonorTics; - } - if (!m_IsInformedNearVictory && m_TeamScores[team] > BG_AB_WARNING_NEAR_VICTORY_SCORE) - { - if (team == BG_TEAM_ALLIANCE) - SendMessageToAll(LANG_BG_AB_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); - else - SendMessageToAll(LANG_BG_AB_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); - PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY); - m_IsInformedNearVictory = true; - } - - if (m_TeamScores[team] > BG_AB_MAX_TEAM_SCORE) - m_TeamScores[team] = BG_AB_MAX_TEAM_SCORE; - if (team == BG_TEAM_ALLIANCE) - UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); - if (team == BG_TEAM_HORDE) - UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]); - // update achievement flags - // we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources - uint8 otherTeam = (team + 1) % BG_TEAMS_COUNT; - if (m_TeamScores[team] > m_TeamScores[otherTeam] + 500) - m_TeamScores500Disadvantage[otherTeam] = true; - } - } - - // Test win condition - if (m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE) - EndBattleGround(ALLIANCE); - if (m_TeamScores[BG_TEAM_HORDE] >= BG_AB_MAX_TEAM_SCORE) - EndBattleGround(HORDE); - } -} - -void BattleGroundAB::StartingEventCloseDoors() -{ - // despawn banners, auras and buffs - for (int obj = BG_AB_OBJECT_BANNER_NEUTRAL; obj < BG_AB_DYNAMIC_NODES_COUNT * 8; ++obj) - SpawnBGObject(obj, RESPAWN_ONE_DAY); - for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT * 3; ++i) - SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + i, RESPAWN_ONE_DAY); - - // Starting doors - DoorClose(BG_AB_OBJECT_GATE_A); - DoorClose(BG_AB_OBJECT_GATE_H); - SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY); - - // Starting base spirit guides - _NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE); - _NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE); -} - -void BattleGroundAB::StartingEventOpenDoors() -{ - // spawn neutral banners - for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i) - SpawnBGObject(banner, RESPAWN_IMMEDIATELY); - for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - { - //randomly select buff to spawn - uint8 buff = urand(0, 2); - SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + buff + i * 3, RESPAWN_IMMEDIATELY); - } - DoorOpen(BG_AB_OBJECT_GATE_A); - DoorOpen(BG_AB_OBJECT_GATE_H); -} - -void BattleGroundAB::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in the constructor - BattleGroundABScore* sc = new BattleGroundABScore; - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundAB::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) -{ - -} - -void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - switch(Trigger) - { - case 3948: // Arathi Basin Alliance Exit. - if (Source->GetTeam() != ALLIANCE) - Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal"); - else - Source->LeaveBattleground(); - break; - case 3949: // Arathi Basin Horde Exit. - if (Source->GetTeam() != HORDE) - Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal"); - else - Source->LeaveBattleground(); - break; - case 3866: // Stables - case 3869: // Gold Mine - case 3867: // Farm - case 3868: // Lumber Mill - case 3870: // Black Smith - case 4020: // Unk1 - case 4021: // Unk2 - //break; - default: - //sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - //Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } -} - -/* type: 0-neutral, 1-contested, 3-occupied - teamIndex: 0-ally, 1-horde */ -void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay) -{ - // Just put it into the queue - if (delay) - { - m_BannerTimers[node].timer = 2000; - m_BannerTimers[node].type = type; - m_BannerTimers[node].teamIndex = teamIndex; - return; - } - - uint8 obj = node*8 + type + teamIndex; - - SpawnBGObject(obj, RESPAWN_IMMEDIATELY); - - // handle aura with banner - if (!type) - return; - obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); - SpawnBGObject(obj, RESPAWN_IMMEDIATELY); -} - -void BattleGroundAB::_DelBanner(uint8 node, uint8 type, uint8 teamIndex) -{ - uint8 obj = node*8 + type + teamIndex; - SpawnBGObject(obj, RESPAWN_ONE_DAY); - - // handle aura with banner - if (!type) - return; - obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); - SpawnBGObject(obj, RESPAWN_ONE_DAY); -} - -int32 BattleGroundAB::_GetNodeNameId(uint8 node) -{ - switch (node) - { - case BG_AB_NODE_STABLES: return LANG_BG_AB_NODE_STABLES; - case BG_AB_NODE_BLACKSMITH: return LANG_BG_AB_NODE_BLACKSMITH; - case BG_AB_NODE_FARM: return LANG_BG_AB_NODE_FARM; - case BG_AB_NODE_LUMBER_MILL:return LANG_BG_AB_NODE_LUMBER_MILL; - case BG_AB_NODE_GOLD_MINE: return LANG_BG_AB_NODE_GOLD_MINE; - default: - ASSERT(0); - } - return 0; -} - -void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) -{ - const uint8 plusArray[] = {0, 2, 3, 0, 1}; - - // Node icons - for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) - data << uint32(BG_AB_OP_NODEICONS[node]) << uint32((m_Nodes[node] == 0)?1:0); - - // Node occupied states - for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) - for (uint8 i = 1; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - data << uint32(BG_AB_OP_NODESTATES[node] + plusArray[i]) << uint32((m_Nodes[node] == i)?1:0); - - // How many bases each team owns - uint8 ally = 0, horde = 0; - for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) - if (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) - ++ally; - else if (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) - ++horde; - - data << uint32(BG_AB_OP_OCCUPIED_BASES_ALLY) << uint32(ally); - data << uint32(BG_AB_OP_OCCUPIED_BASES_HORDE) << uint32(horde); - - // Team scores - data << uint32(BG_AB_OP_RESOURCES_MAX) << uint32(BG_AB_MAX_TEAM_SCORE); - data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_NEAR_VICTORY_SCORE); - data << uint32(BG_AB_OP_RESOURCES_ALLY) << uint32(m_TeamScores[BG_TEAM_ALLIANCE]); - data << uint32(BG_AB_OP_RESOURCES_HORDE) << uint32(m_TeamScores[BG_TEAM_HORDE]); - - // other unknown - data << uint32(0x745) << uint32(0x2); // 37 1861 unk -} - -void BattleGroundAB::_SendNodeUpdate(uint8 node) -{ - // Send node owner state update to refresh map icons on client - const uint8 plusArray[] = {0, 2, 3, 0, 1}; - - if (m_prevNodes[node]) - UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_prevNodes[node]], 0); - else - UpdateWorldState(BG_AB_OP_NODEICONS[node], 0); - - UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_Nodes[node]], 1); - - // How many bases each team owns - uint8 ally = 0, horde = 0; - for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - if (m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) - ++ally; - else if (m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) - ++horde; - - UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_ALLY, ally); - UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_HORDE, horde); -} - -void BattleGroundAB::_NodeOccupied(uint8 node,Team team) -{ - if (!AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team)) - sLog.outError("Failed to spawn spirit guide! point: %u, team: %u,", node, team); - - uint8 capturedNodes = 0; - for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - { - if (m_Nodes[node] == GetTeamIndexByTeamId(team) + BG_AB_NODE_TYPE_OCCUPIED && !m_NodeTimers[i]) - ++capturedNodes; - } - if (capturedNodes >= 5) - CastSpellOnTeam(SPELL_AB_QUEST_REWARD_5_BASES, team); - if (capturedNodes >= 4) - CastSpellOnTeam(SPELL_AB_QUEST_REWARD_4_BASES, team); - - if(node >= BG_AB_DYNAMIC_NODES_COUNT)//only dynamic nodes, no start points - return; - Creature* trigger = GetBGCreature(node+7);//0-6 spirit guides - if (!trigger) - trigger = AddCreature(WORLD_TRIGGER,node+7,team,BG_AB_NodePositions[node][0],BG_AB_NodePositions[node][1],BG_AB_NodePositions[node][2],BG_AB_NodePositions[node][3]); - - //add bonus honor aura trigger creature when node is accupied - //cast bonus aura (+50% honor in 25yards) - //aura should only apply to players who have accupied the node, set correct faction for trigger - if (trigger) - { - trigger->setFaction(team == ALLIANCE ? 84 : 83); - trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); - } -} - -void BattleGroundAB::_NodeDeOccupied(uint8 node) -{ - if (node >= BG_AB_DYNAMIC_NODES_COUNT) - return; - - //remove bonus honor aura trigger creature when node is lost - if(node < BG_AB_DYNAMIC_NODES_COUNT)//only dynamic nodes, no start points - DelCreature(node+7);//NULL checks are in DelCreature! 0-6 spirit guides - - // Those who are waiting to resurrect at this node are taken to the closest own node's graveyard - std::vector ghost_list = m_ReviveQueue[m_BgCreatures[node]]; - if (!ghost_list.empty()) - { - WorldSafeLocsEntry const *ClosestGrave = NULL; - for (std::vector::const_iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) - { - Player* plr = objmgr.GetPlayer(*itr); - if (!plr) - continue; - - if (!ClosestGrave) // cache - ClosestGrave = GetClosestGraveYard(plr); - - if (ClosestGrave) - plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); - } - } - - if (m_BgCreatures[node]) - DelCreature(node); - - // buff object isn't despawned -} - -/* Invoked if a player used a banner as a gameobject */ -void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - uint8 node = BG_AB_NODE_STABLES; - GameObject* obj=GetBgMap()->GetGameObject(m_BgObjects[node*8+7]); - while ((node < BG_AB_DYNAMIC_NODES_COUNT) && ((!obj) || (!source->IsWithinDistInMap(obj,10)))) - { - ++node; - obj=GetBgMap()->GetGameObject(m_BgObjects[node*8+BG_AB_OBJECT_AURA_CONTESTED]); - } - - if (node == BG_AB_DYNAMIC_NODES_COUNT) - { - // this means our player isn't close to any of banners - maybe cheater ?? - return; - } - - BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(source->GetTeam()); - - // Check if player really could use this banner, not cheated - if (!(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2)) - return; - - source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - uint32 sound = 0; - // If node is neutral, change to contested - if (m_Nodes[node] == BG_AB_NODE_TYPE_NEUTRAL) - { - UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); - m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = teamIndex + 1; - // burn current neutral banner - _DelBanner(node, BG_AB_NODE_TYPE_NEUTRAL, 0); - // create new contested banner - _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); - _SendNodeUpdate(node); - m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - - // FIXME: team and node names not localized - if (teamIndex == 0) - SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node), LANG_BG_AB_ALLY); - else - SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node), LANG_BG_AB_HORDE); - - sound = BG_AB_SOUND_NODE_CLAIMED; - } - // If node is contested - else if ((m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED)) - { - // If last state is NOT occupied, change node to enemy-contested - if (m_prevNodes[node] < BG_AB_NODE_TYPE_OCCUPIED) - { - UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); - m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED; - // burn current contested banner - _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex); - // create new contested banner - _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); - _SendNodeUpdate(node); - m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - - // FIXME: node names not localized - if (teamIndex == BG_TEAM_ALLIANCE) - SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); - else - SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); - } - // If contested, change back to occupied - else - { - UpdatePlayerScore(source, SCORE_BASES_DEFENDED, 1); - m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_OCCUPIED; - // burn current contested banner - _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex); - // create new occupied banner - _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); - _SendNodeUpdate(node); - m_NodeTimers[node] = 0; - _NodeOccupied(node,(teamIndex == BG_TEAM_ALLIANCE) ? ALLIANCE:HORDE); - - // FIXME: node names not localized - if (teamIndex == BG_TEAM_ALLIANCE) - SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); - else - SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); - } - sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; - } - // If node is occupied, change to enemy-contested - else - { - UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); - m_prevNodes[node] = m_Nodes[node]; - m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED; - // burn current occupied banner - _DelBanner(node, BG_AB_NODE_TYPE_OCCUPIED, !teamIndex); - // create new contested banner - _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); - _SendNodeUpdate(node); - _NodeDeOccupied(node); - m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; - - // FIXME: node names not localized - if (teamIndex == BG_TEAM_ALLIANCE) - SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); - else - SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); - - sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; - } - - // If node is occupied again, send "X has taken the Y" msg. - if (m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED) - { - // FIXME: team and node names not localized - if (teamIndex == BG_TEAM_ALLIANCE) - SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, LANG_BG_AB_ALLY, _GetNodeNameId(node)); - else - SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE, NULL, LANG_BG_AB_HORDE, _GetNodeNameId(node)); - } - PlaySoundToAll(sound); -} - -bool BattleGroundAB::SetupBattleGround() -{ - for (int i = 0 ; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - { - if (!AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + 8*i,BG_AB_OBJECTID_NODE_BANNER_0 + i,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_BANNER_CONT_A + 8*i,BG_AB_OBJECTID_BANNER_CONT_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_BANNER_CONT_H + 8*i,BG_AB_OBJECTID_BANNER_CONT_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_BANNER_ALLY + 8*i,BG_AB_OBJECTID_BANNER_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_BANNER_HORDE + 8*i,BG_AB_OBJECTID_BANNER_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_AURA_ALLY + 8*i,BG_AB_OBJECTID_AURA_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_AURA_HORDE + 8*i,BG_AB_OBJECTID_AURA_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_AURA_CONTESTED + 8*i,BG_AB_OBJECTID_AURA_C,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) -) - { - sLog.outErrorDb("BatteGroundAB: Failed to spawn some object BattleGround not created!"); - return false; - } - } - if (!AddObject(BG_AB_OBJECT_GATE_A,BG_AB_OBJECTID_GATE_A,BG_AB_DoorPositions[0][0],BG_AB_DoorPositions[0][1],BG_AB_DoorPositions[0][2],BG_AB_DoorPositions[0][3],BG_AB_DoorPositions[0][4],BG_AB_DoorPositions[0][5],BG_AB_DoorPositions[0][6],BG_AB_DoorPositions[0][7],RESPAWN_IMMEDIATELY) - || !AddObject(BG_AB_OBJECT_GATE_H,BG_AB_OBJECTID_GATE_H,BG_AB_DoorPositions[1][0],BG_AB_DoorPositions[1][1],BG_AB_DoorPositions[1][2],BG_AB_DoorPositions[1][3],BG_AB_DoorPositions[1][4],BG_AB_DoorPositions[1][5],BG_AB_DoorPositions[1][6],BG_AB_DoorPositions[1][7],RESPAWN_IMMEDIATELY) -) - { - sLog.outErrorDb("BatteGroundAB: Failed to spawn door object BattleGround not created!"); - return false; - } - //buffs - for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - { - if (!AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i, Buff_Entries[0], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 1, Buff_Entries[1], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) - || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 2, Buff_Entries[2], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) -) - sLog.outErrorDb("BatteGroundAB: Failed to spawn buff object!"); - } - - return true; -} - -void BattleGroundAB::Reset() -{ - //call parent's class reset - BattleGround::Reset(); - - m_TeamScores[BG_TEAM_ALLIANCE] = 0; - m_TeamScores[BG_TEAM_HORDE] = 0; - m_lastTick[BG_TEAM_ALLIANCE] = 0; - m_lastTick[BG_TEAM_HORDE] = 0; - m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0; - m_HonorScoreTics[BG_TEAM_HORDE] = 0; - m_ReputationScoreTics[BG_TEAM_ALLIANCE] = 0; - m_ReputationScoreTics[BG_TEAM_HORDE] = 0; - m_IsInformedNearVictory = false; - bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID()); - m_HonorTics = (isBGWeekend) ? BG_AB_ABBGWeekendHonorTicks : BG_AB_NotABBGWeekendHonorTicks; - m_ReputationTics = (isBGWeekend) ? BG_AB_ABBGWeekendReputationTicks : BG_AB_NotABBGWeekendReputationTicks; - m_TeamScores500Disadvantage[BG_TEAM_ALLIANCE] = false; - m_TeamScores500Disadvantage[BG_TEAM_HORDE] = false; - - for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - { - m_Nodes[i] = 0; - m_prevNodes[i] = 0; - m_NodeTimers[i] = 0; - m_BannerTimers[i].timer = 0; - } - - for (uint8 i = 0; i < BG_AB_ALL_NODES_COUNT + 5; ++i)//+5 for aura triggers - if (m_BgCreatures[i]) - DelCreature(i); -} - -void BattleGroundAB::EndBattleGround(uint32 winner) -{ - //win reward - if (winner == ALLIANCE) - RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); - if (winner == HORDE) - RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); - //complete map_end rewards (even if no team wins) - RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); - RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); - - BattleGround::EndBattleGround(winner); -} - -WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) -{ - BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(player->GetTeam()); - - // Is there any occupied node for this team? - std::vector nodes; - for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - if (m_Nodes[i] == teamIndex + 3) - nodes.push_back(i); - - WorldSafeLocsEntry const* good_entry = NULL; - // If so, select the closest node to place ghost on - if (!nodes.empty()) - { - float plr_x = player->GetPositionX(); - float plr_y = player->GetPositionY(); - - float mindist = 999999.0f; - for (uint8 i = 0; i < nodes.size(); ++i) - { - WorldSafeLocsEntry const*entry = sWorldSafeLocsStore.LookupEntry(BG_AB_GraveyardIds[nodes[i]]); - if (!entry) - continue; - float dist = (entry->x - plr_x)*(entry->x - plr_x)+(entry->y - plr_y)*(entry->y - plr_y); - if (mindist > dist) - { - mindist = dist; - good_entry = entry; - } - } - nodes.clear(); - } - // If not, place ghost on starting location - if (!good_entry) - good_entry = sWorldSafeLocsStore.LookupEntry(BG_AB_GraveyardIds[teamIndex+5]); - - return good_entry; -} - -void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) -{ - BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); - if (itr == m_PlayerScores.end()) // player not found... - return; - - switch(type) - { - case SCORE_BASES_ASSAULTED: - ((BattleGroundABScore*)itr->second)->BasesAssaulted += value; - break; - case SCORE_BASES_DEFENDED: - ((BattleGroundABScore*)itr->second)->BasesDefended += value; - break; - default: - BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); - break; - } -} - -bool BattleGroundAB::IsAllNodesConrolledByTeam(uint32 team) const -{ - uint32 count = 0; - for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) - if ((team == ALLIANCE && m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) || - (team == HORDE && m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED)) - ++count; - - return count == BG_AB_DYNAMIC_NODES_COUNT; -} diff --git a/src/server/game/BattleGroundAB.h b/src/server/game/BattleGroundAB.h deleted file mode 100644 index 3072f8beafd..00000000000 --- a/src/server/game/BattleGroundAB.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDAB_H -#define __BATTLEGROUNDAB_H - -class BattleGround; - -enum BG_AB_WorldStates -{ - BG_AB_OP_OCCUPIED_BASES_HORDE = 1778, - BG_AB_OP_OCCUPIED_BASES_ALLY = 1779, - BG_AB_OP_RESOURCES_ALLY = 1776, - BG_AB_OP_RESOURCES_HORDE = 1777, - BG_AB_OP_RESOURCES_MAX = 1780, - BG_AB_OP_RESOURCES_WARNING = 1955 -/* - BG_AB_OP_STABLE_ICON = 1842, //Stable map icon (NONE) - BG_AB_OP_STABLE_STATE_ALIENCE = 1767, //Stable map state (ALIENCE) - BG_AB_OP_STABLE_STATE_HORDE = 1768, //Stable map state (HORDE) - BG_AB_OP_STABLE_STATE_CON_ALI = 1769, //Stable map state (CON ALIENCE) - BG_AB_OP_STABLE_STATE_CON_HOR = 1770, //Stable map state (CON HORDE) - BG_AB_OP_FARM_ICON = 1845, //Farm map icon (NONE) - BG_AB_OP_FARM_STATE_ALIENCE = 1772, //Farm state (ALIENCE) - BG_AB_OP_FARM_STATE_HORDE = 1773, //Farm state (HORDE) - BG_AB_OP_FARM_STATE_CON_ALI = 1774, //Farm state (CON ALIENCE) - BG_AB_OP_FARM_STATE_CON_HOR = 1775, //Farm state (CON HORDE) - - BG_AB_OP_BLACKSMITH_ICON = 1846, //Blacksmith map icon (NONE) - BG_AB_OP_BLACKSMITH_STATE_ALIENCE = 1782, //Blacksmith map state (ALIENCE) - BG_AB_OP_BLACKSMITH_STATE_HORDE = 1783, //Blacksmith map state (HORDE) - BG_AB_OP_BLACKSMITH_STATE_CON_ALI = 1784, //Blacksmith map state (CON ALIENCE) - BG_AB_OP_BLACKSMITH_STATE_CON_HOR = 1785, //Blacksmith map state (CON HORDE) - BG_AB_OP_LUMBERMILL_ICON = 1844, //Lumber Mill map icon (NONE) - BG_AB_OP_LUMBERMILL_STATE_ALIENCE = 1792, //Lumber Mill map state (ALIENCE) - BG_AB_OP_LUMBERMILL_STATE_HORDE = 1793, //Lumber Mill map state (HORDE) - BG_AB_OP_LUMBERMILL_STATE_CON_ALI = 1794, //Lumber Mill map state (CON ALIENCE) - BG_AB_OP_LUMBERMILL_STATE_CON_HOR = 1795, //Lumber Mill map state (CON HORDE) - BG_AB_OP_GOLDMINE_ICON = 1843, //Gold Mine map icon (NONE) - BG_AB_OP_GOLDMINE_STATE_ALIENCE = 1787, //Gold Mine map state (ALIENCE) - BG_AB_OP_GOLDMINE_STATE_HORDE = 1788, //Gold Mine map state (HORDE) - BG_AB_OP_GOLDMINE_STATE_CON_ALI = 1789, //Gold Mine map state (CON ALIENCE - BG_AB_OP_GOLDMINE_STATE_CON_HOR = 1790, //Gold Mine map state (CON HORDE) -*/ -}; - -const uint32 BG_AB_OP_NODESTATES[5] = {1767, 1782, 1772, 1792, 1787}; - -const uint32 BG_AB_OP_NODEICONS[5] = {1842, 1846, 1845, 1844, 1843}; - -/* Note: code uses that these IDs follow each other */ -enum BG_AB_NodeObjectId -{ - BG_AB_OBJECTID_NODE_BANNER_0 = 180087, // Stables banner - BG_AB_OBJECTID_NODE_BANNER_1 = 180088, // Blacksmith banner - BG_AB_OBJECTID_NODE_BANNER_2 = 180089, // Farm banner - BG_AB_OBJECTID_NODE_BANNER_3 = 180090, // Lumber mill banner - BG_AB_OBJECTID_NODE_BANNER_4 = 180091 // Gold mine banner -}; - -enum BG_AB_ObjectType -{ - // for all 5 node points 8*5=40 objects - BG_AB_OBJECT_BANNER_NEUTRAL = 0, - BG_AB_OBJECT_BANNER_CONT_A = 1, - BG_AB_OBJECT_BANNER_CONT_H = 2, - BG_AB_OBJECT_BANNER_ALLY = 3, - BG_AB_OBJECT_BANNER_HORDE = 4, - BG_AB_OBJECT_AURA_ALLY = 5, - BG_AB_OBJECT_AURA_HORDE = 6, - BG_AB_OBJECT_AURA_CONTESTED = 7, - //gates - BG_AB_OBJECT_GATE_A = 40, - BG_AB_OBJECT_GATE_H = 41, - //buffs - BG_AB_OBJECT_SPEEDBUFF_STABLES = 42, - BG_AB_OBJECT_REGENBUFF_STABLES = 43, - BG_AB_OBJECT_BERSERKBUFF_STABLES = 44, - BG_AB_OBJECT_SPEEDBUFF_BLACKSMITH = 45, - BG_AB_OBJECT_REGENBUFF_BLACKSMITH = 46, - BG_AB_OBJECT_BERSERKBUFF_BLACKSMITH = 47, - BG_AB_OBJECT_SPEEDBUFF_FARM = 48, - BG_AB_OBJECT_REGENBUFF_FARM = 49, - BG_AB_OBJECT_BERSERKBUFF_FARM = 50, - BG_AB_OBJECT_SPEEDBUFF_LUMBER_MILL = 51, - BG_AB_OBJECT_REGENBUFF_LUMBER_MILL = 52, - BG_AB_OBJECT_BERSERKBUFF_LUMBER_MILL = 53, - BG_AB_OBJECT_SPEEDBUFF_GOLD_MINE = 54, - BG_AB_OBJECT_REGENBUFF_GOLD_MINE = 55, - BG_AB_OBJECT_BERSERKBUFF_GOLD_MINE = 56, - BG_AB_OBJECT_MAX = 57, -}; - -/* Object id templates from DB */ -enum BG_AB_ObjectTypes -{ - BG_AB_OBJECTID_BANNER_A = 180058, - BG_AB_OBJECTID_BANNER_CONT_A = 180059, - BG_AB_OBJECTID_BANNER_H = 180060, - BG_AB_OBJECTID_BANNER_CONT_H = 180061, - - BG_AB_OBJECTID_AURA_A = 180100, - BG_AB_OBJECTID_AURA_H = 180101, - BG_AB_OBJECTID_AURA_C = 180102, - - BG_AB_OBJECTID_GATE_A = 180255, - BG_AB_OBJECTID_GATE_H = 180256 -}; - -enum BG_AB_Timers -{ - BG_AB_FLAG_CAPTURING_TIME = 60000, -}; - -enum BG_AB_Score -{ - BG_AB_WARNING_NEAR_VICTORY_SCORE = 1400, - BG_AB_MAX_TEAM_SCORE = 1600 -}; - -/* do NOT change the order, else wrong behaviour */ -enum BG_AB_BattleGroundNodes -{ - BG_AB_NODE_STABLES = 0, - BG_AB_NODE_BLACKSMITH = 1, - BG_AB_NODE_FARM = 2, - BG_AB_NODE_LUMBER_MILL = 3, - BG_AB_NODE_GOLD_MINE = 4, - - BG_AB_DYNAMIC_NODES_COUNT = 5, // dynamic nodes that can be captured - - BG_AB_SPIRIT_ALIANCE = 5, - BG_AB_SPIRIT_HORDE = 6, - - BG_AB_ALL_NODES_COUNT = 7, // all nodes (dynamic and static) -}; - -enum BG_AB_NodeStatus -{ - BG_AB_NODE_TYPE_NEUTRAL = 0, - BG_AB_NODE_TYPE_CONTESTED = 1, - BG_AB_NODE_STATUS_ALLY_CONTESTED = 1, - BG_AB_NODE_STATUS_HORDE_CONTESTED = 2, - BG_AB_NODE_TYPE_OCCUPIED = 3, - BG_AB_NODE_STATUS_ALLY_OCCUPIED = 3, - BG_AB_NODE_STATUS_HORDE_OCCUPIED = 4 -}; - -enum BG_AB_Sounds -{ - BG_AB_SOUND_NODE_CLAIMED = 8192, - BG_AB_SOUND_NODE_CAPTURED_ALLIANCE = 8173, - BG_AB_SOUND_NODE_CAPTURED_HORDE = 8213, - BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE = 8212, - BG_AB_SOUND_NODE_ASSAULTED_HORDE = 8174, - BG_AB_SOUND_NEAR_VICTORY = 8456 -}; - -#define BG_AB_NotABBGWeekendHonorTicks 330 -#define BG_AB_ABBGWeekendHonorTicks 200 -#define BG_AB_NotABBGWeekendReputationTicks 200 -#define BG_AB_ABBGWeekendReputationTicks 150 - -// x, y, z, o -const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { - {1166.785f, 1200.132f, -56.70859f, 0.9075713f}, // stables - {977.0156f, 1046.616f, -44.80923f, -2.600541f}, // blacksmith - {806.1821f, 874.2723f, -55.99371f, -2.303835f}, // farm - {856.1419f, 1148.902f, 11.18469f, -2.303835f}, // lumber mill - {1146.923f, 848.1782f, -110.917f, -0.7330382f} // gold mine -}; - -// x, y, z, o, rot0, rot1, rot2, rot3 -const float BG_AB_DoorPositions[2][8] = { - {1284.597f, 1281.167f, -15.97792f, 0.7068594f, 0.012957f, -0.060288f, 0.344959f, 0.93659f}, - {708.0903f, 708.4479f, -17.8342f, -2.391099f, 0.050291f, 0.015127f, 0.929217f, -0.365784f} -}; - -// Tick intervals and given points: case 0,1,2,3,4,5 captured nodes -const uint32 BG_AB_TickIntervals[6] = {0, 12000, 9000, 6000, 3000, 1000}; -const uint32 BG_AB_TickPoints[6] = {0, 10, 10, 10, 10, 30}; - -// WorldSafeLocs ids for 5 nodes, and for ally, and horde starting location -const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 896, 898, 899}; - -// x, y, z, o -const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { - {1185.71f, 1185.24f, -56.36f, 2.56f}, // stables - {990.75f, 1008.18f, -42.60f, 2.43f}, // blacksmith - {817.66f, 843.34f, -56.54f, 3.01f}, // farm - {807.46f, 1189.16f, 11.92f, 5.44f}, // lumber mill - {1146.62f, 816.94f, -98.49f, 6.14f} // gold mine -}; - -// x, y, z, o -const float BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT][4] = { - {1200.03f, 1171.09f, -56.47f, 5.15f}, // stables - {1017.43f, 960.61f, -42.95f, 4.88f}, // blacksmith - {833.00f, 793.00f, -57.25f, 5.27f}, // farm - {775.17f, 1206.40f, 15.79f, 1.90f}, // lumber mill - {1207.48f, 787.00f, -83.36f, 5.51f}, // gold mine - {1354.05f, 1275.48f, -11.30f, 4.77f}, // alliance starting base - {714.61f, 646.15f, -10.87f, 4.34f} // horde starting base -}; - -struct BG_AB_BannerTimer -{ - uint32 timer; - uint8 type; - uint8 teamIndex; -}; - -class BattleGroundABScore : public BattleGroundScore -{ - public: - BattleGroundABScore(): BasesAssaulted(0), BasesDefended(0) {}; - virtual ~BattleGroundABScore() {}; - uint32 BasesAssaulted; - uint32 BasesDefended; -}; - -class BattleGroundAB : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundAB(); - ~BattleGroundAB(); - - void Update(uint32 diff); - void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - void RemovePlayer(Player *plr,uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - virtual bool SetupBattleGround(); - virtual void Reset(); - void EndBattleGround(uint32 winner); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); - - /* Scorekeeping */ - virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - - virtual void FillInitialWorldStates(WorldPacket& data); - - /* Nodes occupying */ - virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj); - - /* achievement req. */ - bool IsAllNodesConrolledByTeam(uint32 team) const; // overwrited - bool IsTeamScores500Disadvantage(uint32 team) const { return m_TeamScores500Disadvantage[GetTeamIndexByTeamId(team)]; } - private: - /* Gameobject spawning/despawning */ - void _CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay); - void _DelBanner(uint8 node, uint8 type, uint8 teamIndex); - void _SendNodeUpdate(uint8 node); - - /* Creature spawning/despawning */ - // TODO: working, scripted peons spawning - void _NodeOccupied(uint8 node,Team team); - void _NodeDeOccupied(uint8 node); - - int32 _GetNodeNameId(uint8 node); - - /* Nodes info: - 0: neutral - 1: ally contested - 2: horde contested - 3: ally occupied - 4: horde occupied */ - uint8 m_Nodes[BG_AB_DYNAMIC_NODES_COUNT]; - uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; - BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; - uint32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; - uint32 m_lastTick[BG_TEAMS_COUNT]; - uint32 m_HonorScoreTics[BG_TEAMS_COUNT]; - uint32 m_ReputationScoreTics[BG_TEAMS_COUNT]; - bool m_IsInformedNearVictory; - uint32 m_HonorTics; - uint32 m_ReputationTics; - // need for achievements - bool m_TeamScores500Disadvantage[BG_TEAMS_COUNT]; -}; -#endif - diff --git a/src/server/game/BattleGroundAV.cpp b/src/server/game/BattleGroundAV.cpp deleted file mode 100644 index 7f5482cbf16..00000000000 --- a/src/server/game/BattleGroundAV.cpp +++ /dev/null @@ -1,1490 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ObjectMgr.h" -#include "WorldPacket.h" - -#include "BattleGround.h" -#include "BattleGroundAV.h" -#include "Formulas.h" -#include "GameObject.h" -#include "Language.h" -#include "Player.h" -#include "SpellAuras.h" - -BattleGroundAV::BattleGroundAV() -{ - m_BgObjects.resize(BG_AV_OBJECT_MAX); - m_BgCreatures.resize(AV_CPLACE_MAX+AV_STATICCPLACE_MAX); - - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AV_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AV_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AV_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AV_HAS_BEGUN; -} - -BattleGroundAV::~BattleGroundAV() -{ -} - -const uint16 BattleGroundAV::GetBonusHonor(uint8 kills) //TODO: move this function to Battleground.cpp (needs to find a way to get m_MaxLevel) -{ - return Trinity::Honor::hk_honor_at_level(m_MaxLevel, kills); -} - -void BattleGroundAV::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - BattleGround::HandleKillPlayer(player, killer); - UpdateScore(player->GetTeam(),-1); -} - -void BattleGroundAV::HandleKillUnit(Creature *unit, Player *killer) -{ - sLog.outDebug("bg_av HandleKillUnit %i",unit->GetEntry()); - if (GetStatus() != STATUS_IN_PROGRESS) - return; - uint32 entry = unit->GetEntry(); - /* - uint32 triggerSpawnID = 0; - if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) - triggerSpawnID = AV_CPLACE_TRIGGER16; - else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_BOSS][0]) - triggerSpawnID = AV_CPLACE_TRIGGER17; - else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) - triggerSpawnID = AV_CPLACE_TRIGGER18; - else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_BOSS][0]) - triggerSpawnID = AV_CPLACE_TRIGGER19; - */ - if (entry == BG_AV_CreatureInfo[AV_NPC_A_BOSS][0]) - { - CastSpellOnTeam(23658,HORDE); //this is a spell which finishes a quest where a player has to kill the boss - RewardReputationToTeam(729,BG_AV_REP_BOSS,HORDE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_BOSS),HORDE); - EndBattleGround(HORDE); - DelCreature(AV_CPLACE_TRIGGER17); - } - else if (entry == BG_AV_CreatureInfo[AV_NPC_H_BOSS][0]) - { - CastSpellOnTeam(23658,ALLIANCE); //this is a spell which finishes a quest where a player has to kill the boss - RewardReputationToTeam(730,BG_AV_REP_BOSS,ALLIANCE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_BOSS),ALLIANCE); - EndBattleGround(ALLIANCE); - DelCreature(AV_CPLACE_TRIGGER19); - } - else if (entry == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) - { - if (!m_CaptainAlive[0]) - { - sLog.outError("Killed a Captain twice, please report this bug, if you haven't done \".respawn\""); - return; - } - m_CaptainAlive[0]=false; - RewardReputationToTeam(729,BG_AV_REP_CAPTAIN,HORDE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_CAPTAIN),HORDE); - UpdateScore(ALLIANCE,(-1)*BG_AV_RES_CAPTAIN); - //spawn destroyed aura - for (uint8 i=0; i <= 9; i++) - SpawnBGObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+i,RESPAWN_IMMEDIATELY); - Creature* creature = GetBGCreature(AV_CPLACE_HERALD); - if (creature) - YellToAll(creature,GetTrinityString(LANG_BG_AV_A_CAPTAIN_DEAD),LANG_UNIVERSAL); - DelCreature(AV_CPLACE_TRIGGER16); - } - else if (entry == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) - { - if (!m_CaptainAlive[1]) - { - sLog.outError("Killed a Captain twice, please report this bug, if you haven't done \".respawn\""); - return; - } - m_CaptainAlive[1]=false; - RewardReputationToTeam(730,BG_AV_REP_CAPTAIN,ALLIANCE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_CAPTAIN),ALLIANCE); - UpdateScore(HORDE,(-1)*BG_AV_RES_CAPTAIN); - //spawn destroyed aura - for (uint8 i=0; i <= 9; i++) - SpawnBGObject(BG_AV_OBJECT_BURN_BUILDING_HORDE+i,RESPAWN_IMMEDIATELY); - Creature* creature = GetBGCreature(AV_CPLACE_HERALD); - if (creature) - YellToAll(creature,GetTrinityString(LANG_BG_AV_H_CAPTAIN_DEAD),LANG_UNIVERSAL); - DelCreature(AV_CPLACE_TRIGGER18); - } - else if (entry == BG_AV_CreatureInfo[AV_NPC_N_MINE_N_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_N_MINE_A_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_N_MINE_H_4][0]) - ChangeMineOwner(AV_NORTH_MINE,killer->GetTeam()); - else if (entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_N_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_A_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_H_4][0]) - ChangeMineOwner(AV_SOUTH_MINE,killer->GetTeam()); -} - -void BattleGroundAV::HandleQuestComplete(uint32 questid, Player *player) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return;//maybe we should log this, cause this must be a cheater or a big bug - uint8 team = GetTeamIndexByTeamId(player->GetTeam()); - //TODO add reputation, events (including quest not available anymore, next quest availabe, go/npc de/spawning)and maybe honor - sLog.outDebug("BG_AV Quest %i completed",questid); - switch(questid) - { - case AV_QUEST_A_SCRAPS1: - case AV_QUEST_A_SCRAPS2: - case AV_QUEST_H_SCRAPS1: - case AV_QUEST_H_SCRAPS2: - m_Team_QuestStatus[team][0]+=20; - if (m_Team_QuestStatus[team][0] == 500 || m_Team_QuestStatus[team][0] == 1000 || m_Team_QuestStatus[team][0] == 1500) //25,50,75 turn ins - { - sLog.outDebug("BG_AV Quest %i completed starting with unit upgrading..",questid); - for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) - if (m_Nodes[i].Owner == player->GetTeam() && m_Nodes[i].State == POINT_CONTROLED) - { - DePopulateNode(i); - PopulateNode(i); - //maybe this is bad, because it will instantly respawn all creatures on every grave.. - } - } - break; - case AV_QUEST_A_COMMANDER1: - case AV_QUEST_H_COMMANDER1: - m_Team_QuestStatus[team][1]++; - RewardReputationToTeam(team,1,player->GetTeam()); - if (m_Team_QuestStatus[team][1] == 30) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - break; - case AV_QUEST_A_COMMANDER2: - case AV_QUEST_H_COMMANDER2: - m_Team_QuestStatus[team][2]++; - RewardReputationToTeam(team,1,player->GetTeam()); - if (m_Team_QuestStatus[team][2] == 60) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - break; - case AV_QUEST_A_COMMANDER3: - case AV_QUEST_H_COMMANDER3: - m_Team_QuestStatus[team][3]++; - RewardReputationToTeam(team,1,player->GetTeam()); - if (m_Team_QuestStatus[team][1] == 120) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - break; - case AV_QUEST_A_BOSS1: - case AV_QUEST_H_BOSS1: - m_Team_QuestStatus[team][4] += 9; //you can turn in 10 or 1 item.. - case AV_QUEST_A_BOSS2: - case AV_QUEST_H_BOSS2: - m_Team_QuestStatus[team][4]++; - if (m_Team_QuestStatus[team][4] >= 200) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - break; - case AV_QUEST_A_NEAR_MINE: - case AV_QUEST_H_NEAR_MINE: - m_Team_QuestStatus[team][5]++; - if (m_Team_QuestStatus[team][5] == 28) - { - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - if (m_Team_QuestStatus[team][6] == 7) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - ground assault ready",questid); - } - break; - case AV_QUEST_A_OTHER_MINE: - case AV_QUEST_H_OTHER_MINE: - m_Team_QuestStatus[team][6]++; - if (m_Team_QuestStatus[team][6] == 7) - { - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - if (m_Team_QuestStatus[team][5] == 20) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - ground assault ready",questid); - } - break; - case AV_QUEST_A_RIDER_HIDE: - case AV_QUEST_H_RIDER_HIDE: - m_Team_QuestStatus[team][7]++; - if (m_Team_QuestStatus[team][7] == 25) - { - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - if (m_Team_QuestStatus[team][8] == 25) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - rider assault ready",questid); - } - break; - case AV_QUEST_A_RIDER_TAME: - case AV_QUEST_H_RIDER_TAME: - m_Team_QuestStatus[team][8]++; - if (m_Team_QuestStatus[team][8] == 25) - { - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); - if (m_Team_QuestStatus[team][7] == 25) - sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - rider assault ready",questid); - } - break; - default: - sLog.outDebug("BG_AV Quest %i completed but is not interesting at all",questid); - return; //was no interesting quest at all - break; - } -} - -void BattleGroundAV::UpdateScore(uint16 team, int16 points) -{ //note: to remove reinforcementpoints points must be negative, for adding reinforcements points must be positive - assert(team == ALLIANCE || team == HORDE); - uint8 teamindex = GetTeamIndexByTeamId(team); //0=ally 1=horde - m_Team_Scores[teamindex] += points; - - UpdateWorldState(((teamindex == BG_TEAM_HORDE)?AV_Horde_Score:AV_Alliance_Score), m_Team_Scores[teamindex]); - if (points < 0) - { - if (m_Team_Scores[teamindex] < 1) - { - m_Team_Scores[teamindex]=0; - EndBattleGround(((teamindex == BG_TEAM_HORDE)?ALLIANCE:HORDE)); - } - else if (!m_IsInformedNearVictory[teamindex] && m_Team_Scores[teamindex] < SEND_MSG_NEAR_LOSE) - { - SendMessageToAll(teamindex == BG_TEAM_HORDE?LANG_BG_AV_H_NEAR_LOSE:LANG_BG_AV_A_NEAR_LOSE, teamindex == BG_TEAM_HORDE ? CHAT_MSG_BG_SYSTEM_HORDE : CHAT_MSG_BG_SYSTEM_ALLIANCE); - PlaySoundToAll(AV_SOUND_NEAR_VICTORY); - m_IsInformedNearVictory[teamindex] = true; - } - } -} - -Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type) -{ - uint8 level; - bool isStatic = false; - Creature* creature = NULL; - assert(type <= AV_CPLACE_MAX + AV_STATICCPLACE_MAX); - if (type >= AV_CPLACE_MAX) //static - { - type -= AV_CPLACE_MAX; - cinfoid=uint16(BG_AV_StaticCreaturePos[type][4]); - creature = AddCreature(BG_AV_StaticCreatureInfo[cinfoid][0],(type+AV_CPLACE_MAX),BG_AV_StaticCreatureInfo[cinfoid][1],BG_AV_StaticCreaturePos[type][0],BG_AV_StaticCreaturePos[type][1],BG_AV_StaticCreaturePos[type][2],BG_AV_StaticCreaturePos[type][3]); - level = (BG_AV_StaticCreatureInfo[cinfoid][2] == BG_AV_StaticCreatureInfo[cinfoid][3]) ? BG_AV_StaticCreatureInfo[cinfoid][2] : urand(BG_AV_StaticCreatureInfo[cinfoid][2],BG_AV_StaticCreatureInfo[cinfoid][3]); - isStatic = true; - } - else - { - creature = AddCreature(BG_AV_CreatureInfo[cinfoid][0],type,BG_AV_CreatureInfo[cinfoid][1],BG_AV_CreaturePos[type][0],BG_AV_CreaturePos[type][1],BG_AV_CreaturePos[type][2],BG_AV_CreaturePos[type][3]); - level = (BG_AV_CreatureInfo[cinfoid][2] == BG_AV_CreatureInfo[cinfoid][3]) ? BG_AV_CreatureInfo[cinfoid][2] : urand(BG_AV_CreatureInfo[cinfoid][2],BG_AV_CreatureInfo[cinfoid][3]); - } - if (!creature) - return NULL; - if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0] || creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) - creature->SetRespawnDelay(RESPAWN_ONE_DAY); // TODO: look if this can be done by database + also add this for the wingcommanders - - if ((isStatic && cinfoid >= 10 && cinfoid <= 14) || (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid <= AV_NPC_A_GRAVEDEFENSE3) || - (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3)))) - { - if (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid <= AV_NPC_A_GRAVEDEFENSE3) - || (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3))) - { - CreatureData &data = objmgr.NewOrExistCreatureData(creature->GetDBTableGUIDLow()); - data.spawndist = 5; - } - //else spawndist will be 15, so creatures move maximum=10 - //creature->SetDefaultMovementType(RANDOM_MOTION_TYPE); - creature->GetMotionMaster()->Initialize(); - creature->setDeathState(JUST_DIED); - creature->Respawn(); - //TODO: find a way to add a motionmaster without killing the creature (i - //just copied this code from a gm-command - } - - if (level != 0) - level += m_MaxLevel - 60; //maybe we can do this more generic for custom level-range.. actually it's blizzlike - creature->SetLevel(level); - - uint32 triggerSpawnID = 0; - uint32 newFaction = 0; - if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) - { - triggerSpawnID = AV_CPLACE_TRIGGER16; - newFaction = 84; - } - else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_BOSS][0]) - { - triggerSpawnID = AV_CPLACE_TRIGGER17; - newFaction = 84; - } - else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) - { - triggerSpawnID = AV_CPLACE_TRIGGER18; - newFaction = 83; - } - else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_BOSS][0]) - { - triggerSpawnID = AV_CPLACE_TRIGGER19; - newFaction = 83; - } - if (triggerSpawnID && newFaction) - { - if (Creature* trigger = AddCreature(WORLD_TRIGGER,triggerSpawnID,BG_AV_CreatureInfo[creature->GetEntry()][1],BG_AV_CreaturePos[triggerSpawnID][0],BG_AV_CreaturePos[triggerSpawnID][1],BG_AV_CreaturePos[triggerSpawnID][2],BG_AV_CreaturePos[triggerSpawnID][3])) - { - trigger->setFaction(newFaction); - trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); - } - } - - return creature; -} - -void BattleGroundAV::Update(uint32 diff) -{ - BattleGround::Update(diff); - - if (GetStatus() == STATUS_IN_PROGRESS) - { - for (uint8 i=0; i <= 1; i++)//0=alliance, 1=horde - { - if (!m_CaptainAlive[i]) - continue; - if (m_CaptainBuffTimer[i] > diff) - m_CaptainBuffTimer[i] -= diff; - else - { - if (i == 0) - { - CastSpellOnTeam(AV_BUFF_A_CAPTAIN,ALLIANCE); - Creature* creature = GetBGCreature(AV_CPLACE_MAX + 61); - if (creature) - YellToAll(creature,LANG_BG_AV_A_CAPTAIN_BUFF,LANG_COMMON); - } - else - { - CastSpellOnTeam(AV_BUFF_H_CAPTAIN,HORDE); - Creature* creature = GetBGCreature(AV_CPLACE_MAX + 59); //TODO: make the captains a dynamic creature - if (creature) - YellToAll(creature,LANG_BG_AV_H_CAPTAIN_BUFF,LANG_ORCISH); - } - m_CaptainBuffTimer[i] = 120000 + urand(0,4)* 60000; //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes TODO get the right times - } - } - //add points from mine owning, and look if he neutral team wanrts to reclaim the mine - m_Mine_Timer -=diff; - for (uint8 mine=0; mine <2; mine++) - { - if (m_Mine_Owner[mine] == ALLIANCE || m_Mine_Owner[mine] == HORDE) - { - if (m_Mine_Timer <= 0) - UpdateScore(m_Mine_Owner[mine],1); - - if (m_Mine_Reclaim_Timer[mine] > diff) - m_Mine_Reclaim_Timer[mine] -= diff; - else{ //we don't need to set this timer to 0 cause this codepart wont get called when this thing is 0 - ChangeMineOwner(mine,AV_NEUTRAL_TEAM); - } - } - } - if (m_Mine_Timer <= 0) - m_Mine_Timer=AV_MINE_TICK_TIMER; //this is at the end, cause we need to update both mines - - //looks for all timers of the nodes and destroy the building (for graveyards the building wont get destroyed, it goes just to the other team - for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i) - if (m_Nodes[i].State == POINT_ASSAULTED) //maybe remove this - { - if (m_Nodes[i].Timer > diff) - m_Nodes[i].Timer -= diff; - else - EventPlayerDestroyedPoint(i); - } - } -} - -void BattleGroundAV::StartingEventCloseDoors() -{ - DoorClose(BG_AV_OBJECT_DOOR_A); - DoorClose(BG_AV_OBJECT_DOOR_H); -} - -void BattleGroundAV::StartingEventOpenDoors() -{ - sLog.outDebug("BG_AV: start spawning mine stuff"); - for (uint16 i= BG_AV_OBJECT_MINE_SUPPLY_N_MIN; i <= BG_AV_OBJECT_MINE_SUPPLY_N_MAX; i++) - SpawnBGObject(i,RESPAWN_IMMEDIATELY); - for (uint16 i= BG_AV_OBJECT_MINE_SUPPLY_S_MIN; i <= BG_AV_OBJECT_MINE_SUPPLY_S_MAX; i++) - SpawnBGObject(i,RESPAWN_IMMEDIATELY); - for (uint8 mine = AV_NORTH_MINE; mine <= AV_SOUTH_MINE; mine++) //mine population - ChangeMineOwner(mine, AV_NEUTRAL_TEAM,true); - - UpdateWorldState(AV_SHOW_H_SCORE, 1); - UpdateWorldState(AV_SHOW_A_SCORE, 1); - - DoorOpen(BG_AV_OBJECT_DOOR_H); - DoorOpen(BG_AV_OBJECT_DOOR_A); -} - -void BattleGroundAV::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundAVScore* sc = new BattleGroundAVScore; - m_PlayerScores[plr->GetGUID()] = sc; - if (m_MaxLevel == 0) - m_MaxLevel=(plr->getLevel()%10 == 0)? plr->getLevel() : (plr->getLevel()-(plr->getLevel()%10))+10; //TODO: just look at the code \^_^/ --but queue-info should provide this information.. - -} - -void BattleGroundAV::EndBattleGround(uint32 winner) -{ - //calculate bonuskills for both teams: - //first towers: - uint8 kills[2]={0,0}; //0=ally 1=horde - uint8 rep[2]={0,0}; //0=ally 1=horde - for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) - { - if (m_Nodes[i].State == POINT_CONTROLED) - { - if (m_Nodes[i].Owner == ALLIANCE) - { - rep[0] += BG_AV_REP_SURVIVING_TOWER; - kills[0] += BG_AV_KILL_SURVIVING_TOWER; - } - else - { - rep[0] += BG_AV_KILL_SURVIVING_TOWER; - kills[1] += BG_AV_KILL_SURVIVING_TOWER; - } - } - } - - for (int i=0; i <= 1; i++) //0=ally 1=horde - { - if (m_CaptainAlive[i]) - { - kills[i] += BG_AV_KILL_SURVIVING_CAPTAIN; - rep[i] += BG_AV_REP_SURVIVING_CAPTAIN; - } - if (rep[i] != 0) - RewardReputationToTeam((i == 0)?730:729,rep[i],(i == 0)?ALLIANCE:HORDE); - if (kills[i] != 0) - RewardHonorToTeam(GetBonusHonor(kills[i]),(i == 0)?ALLIANCE:HORDE); - } - - //TODO add enterevademode for all attacking creatures - BattleGround::EndBattleGround(winner); -} - -void BattleGroundAV::RemovePlayer(Player* plr,uint64 /*guid*/) -{ - if (!plr) - { - sLog.outError("bg_AV no player at remove"); - return; - } - //TODO search more buffs - plr->RemoveAurasDueToSpell(AV_BUFF_ARMOR); - plr->RemoveAurasDueToSpell(AV_BUFF_A_CAPTAIN); - plr->RemoveAurasDueToSpell(AV_BUFF_H_CAPTAIN); -} - -void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - uint32 SpellId = 0; - switch(Trigger) - { - case 95: - case 2608: - if (Source->GetTeam() != ALLIANCE) - Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal"); - else - Source->LeaveBattleground(); - break; - case 2606: - if (Source->GetTeam() != HORDE) - Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal"); - else - Source->LeaveBattleground(); - break; - case 3326: - case 3327: - case 3328: - case 3329: - case 3330: - case 3331: - //Source->Unmount(); - break; - default: - sLog.outDebug("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); -// Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } - - if (SpellId) - Source->CastSpell(Source, SpellId, true); -} - -void BattleGroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - - BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); - if (itr == m_PlayerScores.end()) // player not found... - return; - - switch(type) - { - case SCORE_GRAVEYARDS_ASSAULTED: - ((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted += value; - break; - case SCORE_GRAVEYARDS_DEFENDED: - ((BattleGroundAVScore*)itr->second)->GraveyardsDefended += value; - break; - case SCORE_TOWERS_ASSAULTED: - ((BattleGroundAVScore*)itr->second)->TowersAssaulted += value; - break; - case SCORE_TOWERS_DEFENDED: - ((BattleGroundAVScore*)itr->second)->TowersDefended += value; - break; - case SCORE_MINES_CAPTURED: - ((BattleGroundAVScore*)itr->second)->MinesCaptured += value; - break; - case SCORE_LEADERS_KILLED: - ((BattleGroundAVScore*)itr->second)->LeadersKilled += value; - break; - case SCORE_SECONDARY_OBJECTIVES: - ((BattleGroundAVScore*)itr->second)->SecondaryObjectives += value; - break; - default: - BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); - break; - } -} - -void BattleGroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node) -{ - - uint32 object = GetObjectThroughNode(node); - sLog.outDebug("bg_av: player destroyed point node %i object %i",node,object); - - //despawn banner - SpawnBGObject(object, RESPAWN_ONE_DAY); - DestroyNode(node); - UpdateNodeWorldState(node); - - uint32 owner = m_Nodes[node].Owner; - if (IsTower(node)) - { - uint8 tmp = node-BG_AV_NODES_DUNBALDAR_SOUTH; - //despawn marshal - if (m_BgCreatures[AV_CPLACE_A_MARSHAL_SOUTH + tmp]) - DelCreature(AV_CPLACE_A_MARSHAL_SOUTH + tmp); - else - sLog.outError("BG_AV: playerdestroyedpoint: marshal %i doesn't exist",AV_CPLACE_A_MARSHAL_SOUTH + tmp); - //spawn destroyed aura - for (uint8 i=0; i <= 9; i++) - SpawnBGObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH + i + (tmp * 10),RESPAWN_IMMEDIATELY); - - UpdateScore((owner == ALLIANCE) ? HORDE : ALLIANCE, (-1)*BG_AV_RES_TOWER); - RewardReputationToTeam((owner == ALLIANCE)?730:729,BG_AV_REP_TOWER,owner); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_TOWER),owner); - - SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp),RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp),RESPAWN_ONE_DAY); - } - else - { - if (owner == ALLIANCE) - SpawnBGObject(object-11, RESPAWN_IMMEDIATELY); - else - SpawnBGObject(object+11, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+GetTeamIndexByTeamId(owner)+3*node,RESPAWN_IMMEDIATELY); - PopulateNode(node); - if (node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall eyecandy - { - for (uint8 i = 0; i < 4; i++) - { - SpawnBGObject(((owner == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_PA : BG_AV_OBJECT_SNOW_EYECANDY_PH)+i,RESPAWN_ONE_DAY); - SpawnBGObject(((owner == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_A : BG_AV_OBJECT_SNOW_EYECANDY_H)+i,RESPAWN_IMMEDIATELY); - } - } - } - //send a nice message to all :) - char buf[256]; - if (IsTower(node)) - sprintf(buf, GetTrinityString(LANG_BG_AV_TOWER_TAKEN) , GetNodeName(node),(owner == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); - else - sprintf(buf, GetTrinityString(LANG_BG_AV_GRAVE_TAKEN) , GetNodeName(node),(owner == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) :GetTrinityString(LANG_BG_AV_HORDE)); - - Creature* creature = GetBGCreature(AV_CPLACE_HERALD); - if (creature) - YellToAll(creature,buf,LANG_UNIVERSAL); -} - -void BattleGroundAV::ChangeMineOwner(uint8 mine, uint32 team, bool initial) -{ //mine=0 northmine mine=1 southmin -//changing the owner results in setting respawntim to infinite for current creatures, spawning new mine owners creatures and changing the chest-objects so that the current owning team can use them - assert(mine == AV_NORTH_MINE || mine == AV_SOUTH_MINE); - if (team != ALLIANCE && team != HORDE) - team = AV_NEUTRAL_TEAM; - else - PlaySoundToAll((team == ALLIANCE)?AV_SOUND_ALLIANCE_GOOD:AV_SOUND_HORDE_GOOD); - - if (m_Mine_Owner[mine] == team && !initial) - return; - m_Mine_PrevOwner[mine] = m_Mine_Owner[mine]; - m_Mine_Owner[mine] = team; - - if (!initial) - { - sLog.outDebug("bg_av depopulating mine %i (0=north,1=south)",mine); - if (mine == AV_SOUTH_MINE) - for (uint16 i=AV_CPLACE_MINE_S_S_MIN; i <= AV_CPLACE_MINE_S_S_MAX; i++) - if (m_BgCreatures[i]) - DelCreature(i); //TODO just set the respawntime to 999999 - for (uint16 i=((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_1_MIN:AV_CPLACE_MINE_S_1_MIN); i <= ((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_3:AV_CPLACE_MINE_S_3); i++) - if (m_BgCreatures[i]) - DelCreature(i); //TODO here also - } - SendMineWorldStates(mine); - - sLog.outDebug("bg_av populating mine %i (0=north,1=south)",mine); - uint16 miner; - //also neutral team exists.. after a big time, the neutral team tries to conquer the mine - if (mine == AV_NORTH_MINE) - { - if (team == ALLIANCE) - miner = AV_NPC_N_MINE_A_1; - else if (team == HORDE) - miner = AV_NPC_N_MINE_H_1; - else - miner = AV_NPC_N_MINE_N_1; - } - else - { - uint16 cinfo; - if (team == ALLIANCE) - miner = AV_NPC_S_MINE_A_1; - else if (team == HORDE) - miner = AV_NPC_S_MINE_H_1; - else - miner = AV_NPC_S_MINE_N_1; - //vermin - sLog.outDebug("spawning vermin"); - if (team == ALLIANCE) - cinfo = AV_NPC_S_MINE_A_3; - else if (team == HORDE) - cinfo = AV_NPC_S_MINE_H_3; - else - cinfo = AV_NPC_S_MINE_N_S; - for (uint16 i=AV_CPLACE_MINE_S_S_MIN; i <= AV_CPLACE_MINE_S_S_MAX; i++) - AddAVCreature(cinfo,i); - } - for (uint16 i=((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_1_MIN:AV_CPLACE_MINE_S_1_MIN); i <= ((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_1_MAX:AV_CPLACE_MINE_S_1_MAX); i++) - AddAVCreature(miner,i); - //the next chooses randomly between 2 cretures - for (uint16 i=((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_2_MIN:AV_CPLACE_MINE_S_2_MIN); i <= ((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_2_MAX:AV_CPLACE_MINE_S_2_MAX); i++) - AddAVCreature(miner+(urand(1,2)),i); - AddAVCreature(miner+3,(mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_3:AV_CPLACE_MINE_S_3); - //because the gameobjects in this mine have changed, update all surrounding players: -// for (uint16 i = ((mine == AV_NORTH_MINE)?BG_AV_OBJECT_MINE_SUPPLY_N_MIN:BG_AV_OBJECT_MINE_SUPPLY_N_MIN); i <= ((mine == AV_NORTH_MINE)?BG_AV_OBJECT_MINE_SUPPLY_N_MAX:BG_AV_OBJECT_MINE_SUPPLY_N_MAX); i++) -// { - //TODO: add gameobject-update code -// } - if (team == ALLIANCE || team == HORDE) - { - m_Mine_Reclaim_Timer[mine]=AV_MINE_RECLAIM_TIMER; - char buf[256]; - sprintf(buf, GetTrinityString(LANG_BG_AV_MINE_TAKEN), GetTrinityString((mine == AV_NORTH_MINE) ? LANG_BG_AV_MINE_NORTH : LANG_BG_AV_MINE_SOUTH), (team == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); - Creature* creature = GetBGCreature(AV_CPLACE_HERALD); - if (creature) - YellToAll(creature,buf,LANG_UNIVERSAL); - } - else - { - if (mine == AV_SOUTH_MINE) //i think this gets called all the time - { - Creature* creature = GetBGCreature(AV_CPLACE_MINE_S_3); - YellToAll(creature,LANG_BG_AV_S_MINE_BOSS_CLAIMS,LANG_UNIVERSAL); - } - } - return; -} - -bool BattleGroundAV::PlayerCanDoMineQuest(int32 GOId,uint32 team) -{ - if (GOId == BG_AV_OBJECTID_MINE_N) - return (m_Mine_Owner[AV_NORTH_MINE] == team); - if (GOId == BG_AV_OBJECTID_MINE_S) - return (m_Mine_Owner[AV_SOUTH_MINE] == team); - return true; //cause it's no mine'object it is ok if this is true -} - -void BattleGroundAV::PopulateNode(BG_AV_Nodes node) -{ - uint32 owner = m_Nodes[node].Owner; - assert(owner); - - uint32 c_place = AV_CPLACE_DEFENSE_STORM_AID + (4 * node); - uint32 creatureid; - if (IsTower(node)) - creatureid=(owner == ALLIANCE)?AV_NPC_A_TOWERDEFENSE:AV_NPC_H_TOWERDEFENSE; - else - { - uint8 team2 = GetTeamIndexByTeamId(owner); - if (m_Team_QuestStatus[team2][0] < 500) - creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE0 : AV_NPC_H_GRAVEDEFENSE0; - else if (m_Team_QuestStatus[team2][0] < 1000) - creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE1 : AV_NPC_H_GRAVEDEFENSE1; - else if (m_Team_QuestStatus[team2][0] < 1500) - creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE2 : AV_NPC_H_GRAVEDEFENSE2; - else - creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE3 : AV_NPC_H_GRAVEDEFENSE3; - //spiritguide - if (m_BgCreatures[node]) - DelCreature(node); - if (!AddSpiritGuide(node, BG_AV_CreaturePos[node][0], BG_AV_CreaturePos[node][1], BG_AV_CreaturePos[node][2], BG_AV_CreaturePos[node][3], owner)) - sLog.outError("AV: couldn't spawn spiritguide at node %i",node); - - } - for (uint8 i=0; i<4; i++) - AddAVCreature(creatureid,c_place+i); - - if (node >= BG_AV_NODES_MAX)//fail safe - return; - Creature* trigger = GetBGCreature(node + 302);//0-302 other creatures - if (!trigger) - trigger = AddCreature(WORLD_TRIGGER,node + 302,owner,BG_AV_CreaturePos[node + 302][0],BG_AV_CreaturePos[node + 302][1],BG_AV_CreaturePos[node + 302][2],BG_AV_CreaturePos[node + 302][3]); - - //add bonus honor aura trigger creature when node is accupied - //cast bonus aura (+50% honor in 25yards) - //aura should only apply to players who have accupied the node, set correct faction for trigger - if (trigger) - { - if (owner != ALLIANCE && owner != HORDE)//node can be neutral, remove trigger - { - DelCreature(node + 302); - return; - } - trigger->setFaction(owner == ALLIANCE ? 84 : 83); - trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); - } -} -void BattleGroundAV::DePopulateNode(BG_AV_Nodes node) -{ - uint32 c_place = AV_CPLACE_DEFENSE_STORM_AID + (4 * node); - for (uint8 i=0; i<4; i++) - if (m_BgCreatures[c_place+i]) - DelCreature(c_place+i); - //spiritguide - if (!IsTower(node) && m_BgCreatures[node]) - DelCreature(node); - - //remove bonus honor aura trigger creature when node is lost - if(node < BG_AV_NODES_MAX)//fail safe - DelCreature(node + 302);//NULL checks are in DelCreature! 0-302 spirit guides -} - -const BG_AV_Nodes BattleGroundAV::GetNodeThroughObject(uint32 object) -{ - sLog.outDebug("bg_AV getnodethroughobject %i",object); - if (object <= BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER) - return BG_AV_Nodes(object); - if (object <= BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_HUT) - return BG_AV_Nodes(object - 11); - if (object <= BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_WTOWER) - return BG_AV_Nodes(object - 7); - if (object <= BG_AV_OBJECT_FLAG_C_H_STONEHEART_BUNKER) - return BG_AV_Nodes(object -22); - if (object <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT) - return BG_AV_Nodes(object - 33); - if (object <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER) - return BG_AV_Nodes(object - 29); - if (object == BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE) - return BG_AV_NODES_SNOWFALL_GRAVE; - sLog.outError("BattleGroundAV: ERROR! GetPlace got a wrong object :("); - assert(false); - return BG_AV_Nodes(0); -} - -const uint32 BattleGroundAV::GetObjectThroughNode(BG_AV_Nodes node) -{ //this function is the counterpart to GetNodeThroughObject() - sLog.outDebug("bg_AV GetObjectThroughNode %i",node); - if (m_Nodes[node].Owner == ALLIANCE) - { - if (m_Nodes[node].State == POINT_ASSAULTED) - { - if (node <= BG_AV_NODES_FROSTWOLF_HUT) - return node+11; - if (node >= BG_AV_NODES_ICEBLOOD_TOWER && node <= BG_AV_NODES_FROSTWOLF_WTOWER) - return node+7; - } - else if (m_Nodes[node].State == POINT_CONTROLED) - if (node <= BG_AV_NODES_STONEHEART_BUNKER) - return node; - } - else if (m_Nodes[node].Owner == HORDE) - { - if (m_Nodes[node].State == POINT_ASSAULTED) - if (node <= BG_AV_NODES_STONEHEART_BUNKER) - return node+22; - else if (m_Nodes[node].State == POINT_CONTROLED) - { - if (node <= BG_AV_NODES_FROSTWOLF_HUT) - return node+33; - if (node >= BG_AV_NODES_ICEBLOOD_TOWER && node <= BG_AV_NODES_FROSTWOLF_WTOWER) - return node+29; - } - } - else if (m_Nodes[node].Owner == AV_NEUTRAL_TEAM) - return BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; - sLog.outError("BattleGroundAV: Error! GetPlaceNode couldn't resolve node %i",node); - assert(false); - return 0; -} - -//called when using banner - -void BattleGroundAV::EventPlayerClickedOnFlag(Player *source, GameObject* target_obj) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - int32 object = GetObjectType(target_obj->GetGUID()); - sLog.outDebug("BG_AV using gameobject %i with type %i",target_obj->GetEntry(),object); - if (object < 0) - return; - switch(target_obj->GetEntry()) - { - case BG_AV_OBJECTID_BANNER_A: - case BG_AV_OBJECTID_BANNER_A_B: - case BG_AV_OBJECTID_BANNER_H: - case BG_AV_OBJECTID_BANNER_H_B: - case BG_AV_OBJECTID_BANNER_SNOWFALL_N: - EventPlayerAssaultsPoint(source, object); - break; - case BG_AV_OBJECTID_BANNER_CONT_A: - case BG_AV_OBJECTID_BANNER_CONT_A_B: - case BG_AV_OBJECTID_BANNER_CONT_H: - case BG_AV_OBJECTID_BANNER_CONT_H_B: - EventPlayerDefendsPoint(source, object); - break; - default: - break; - } -} - -void BattleGroundAV::EventPlayerDefendsPoint(Player* player, uint32 object) -{ - assert(GetStatus() == STATUS_IN_PROGRESS); - BG_AV_Nodes node = GetNodeThroughObject(object); - - uint32 owner = m_Nodes[node].Owner; //maybe should name it prevowner - uint32 team = player->GetTeam(); - - if (owner == player->GetTeam() || m_Nodes[node].State != POINT_ASSAULTED) - return; - if (m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM) - { //until snowfall doesn't belong to anyone it is better handled in assault-code - assert(node == BG_AV_NODES_SNOWFALL_GRAVE); //currently the only neutral grave - EventPlayerAssaultsPoint(player,object); - return; - } - sLog.outDebug("player defends point object: %i node: %i",object,node); - if (m_Nodes[node].PrevOwner != team) - { - sLog.outError("BG_AV: player defends point which doesn't belong to his team %i",node); - return; - } - - //spawn new go :) - if (m_Nodes[node].Owner == ALLIANCE) - SpawnBGObject(object+22, RESPAWN_IMMEDIATELY); //spawn horde banner - else - SpawnBGObject(object-22, RESPAWN_IMMEDIATELY); //spawn alliance banner - - if (!IsTower(node)) - { - SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+GetTeamIndexByTeamId(team)+3*node,RESPAWN_IMMEDIATELY); - } - // despawn old go - SpawnBGObject(object, RESPAWN_ONE_DAY); - - DefendNode(node,team); - PopulateNode(node); - UpdateNodeWorldState(node); - - if (IsTower(node)) - { - //spawn big flag+aura on top of tower - SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - } - else if (node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall eyecandy - { - for (uint8 i = 0; i < 4; i++) - { - SpawnBGObject(((owner == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_PA : BG_AV_OBJECT_SNOW_EYECANDY_PH)+i,RESPAWN_ONE_DAY); - SpawnBGObject(((team == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_A : BG_AV_OBJECT_SNOW_EYECANDY_H)+i,RESPAWN_IMMEDIATELY); - } - } - //send a nice message to all :) - char buf[256]; - sprintf(buf, GetTrinityString((IsTower(node)) ? LANG_BG_AV_TOWER_DEFENDED : LANG_BG_AV_GRAVE_DEFENDED), GetNodeName(node),(team == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); - Creature* creature = GetBGCreature(AV_CPLACE_HERALD); - if (creature) - YellToAll(creature,buf,LANG_UNIVERSAL); - //update the statistic for the defending player - UpdatePlayerScore(player, (IsTower(node)) ? SCORE_TOWERS_DEFENDED : SCORE_GRAVEYARDS_DEFENDED, 1); - if (IsTower(node)) - PlaySoundToAll(AV_SOUND_BOTH_TOWER_DEFEND); - else - PlaySoundToAll((team == ALLIANCE)?AV_SOUND_ALLIANCE_GOOD:AV_SOUND_HORDE_GOOD); -} - -void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object) -{ - assert(GetStatus() == STATUS_IN_PROGRESS); - - BG_AV_Nodes node = GetNodeThroughObject(object); - uint32 owner = m_Nodes[node].Owner; //maybe name it prevowner - uint32 team = player->GetTeam(); - sLog.outDebug("bg_av: player assaults point object %i node %i",object,node); - if (owner == team || team == m_Nodes[node].TotalOwner) - return; //surely a gm used this object - - if (node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall is a bit special in capping + it gets eyecandy stuff - { - if (object == BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE) //initial capping - { - assert(owner == AV_NEUTRAL_TEAM && m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM); - if (team == ALLIANCE) - SpawnBGObject(BG_AV_OBJECT_FLAG_C_A_SNOWFALL_GRAVE, RESPAWN_IMMEDIATELY); - else - SpawnBGObject(BG_AV_OBJECT_FLAG_C_H_SNOWFALL_GRAVE, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_IMMEDIATELY); //neutral aura spawn - } - else if (m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM) //recapping, when no team owns this node realy - { - assert(m_Nodes[node].State != POINT_CONTROLED); - if (team == ALLIANCE) - SpawnBGObject(object-11, RESPAWN_IMMEDIATELY); - else - SpawnBGObject(object+11, RESPAWN_IMMEDIATELY); - } - //eyecandy - uint32 spawn,despawn; - if (team == ALLIANCE) - { - despawn = (m_Nodes[node].State == POINT_ASSAULTED)?BG_AV_OBJECT_SNOW_EYECANDY_PH : BG_AV_OBJECT_SNOW_EYECANDY_H; - spawn = BG_AV_OBJECT_SNOW_EYECANDY_PA; - } - else - { - despawn = (m_Nodes[node].State == POINT_ASSAULTED)?BG_AV_OBJECT_SNOW_EYECANDY_PA : BG_AV_OBJECT_SNOW_EYECANDY_A; - spawn = BG_AV_OBJECT_SNOW_EYECANDY_PH; - } - for (uint8 i = 0; i < 4; i++) - { - SpawnBGObject(despawn+i,RESPAWN_ONE_DAY); - SpawnBGObject(spawn+i,RESPAWN_IMMEDIATELY); - } - } - - //if snowfall gots capped it can be handled like all other graveyards - if (m_Nodes[node].TotalOwner != AV_NEUTRAL_TEAM) - { - assert(m_Nodes[node].Owner != AV_NEUTRAL_TEAM); - if (team == ALLIANCE) - SpawnBGObject(object-22, RESPAWN_IMMEDIATELY); - else - SpawnBGObject(object+22, RESPAWN_IMMEDIATELY); - if (IsTower(node)) - { //spawning/despawning of bigflag+aura - SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - SpawnBGObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); - } - else - { - //spawning/despawning of aura - SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_IMMEDIATELY); //neutral aura spawn - SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+GetTeamIndexByTeamId(owner)+3*node,RESPAWN_ONE_DAY); //teeamaura despawn - // Those who are waiting to resurrect at this object are taken to the closest own object's graveyard - std::vector ghost_list = m_ReviveQueue[m_BgCreatures[node]]; - if (!ghost_list.empty()) - { - Player *plr; - WorldSafeLocsEntry const *ClosestGrave = NULL; - for (std::vector::iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) - { - plr = objmgr.GetPlayer(*ghost_list.begin()); - if (!plr) - continue; - if (!ClosestGrave) - ClosestGrave = GetClosestGraveYard(plr); - else - plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); - } - m_ReviveQueue[m_BgCreatures[node]].clear(); - } - } - DePopulateNode(node); - } - - SpawnBGObject(object, RESPAWN_ONE_DAY); //delete old banner - AssaultNode(node,team); - UpdateNodeWorldState(node); - - //send a nice message to all :) - char buf[256]; - sprintf(buf, (IsTower(node)) ? GetTrinityString(LANG_BG_AV_TOWER_ASSAULTED) : GetTrinityString(LANG_BG_AV_GRAVE_ASSAULTED), GetNodeName(node), (team == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); - Creature* creature = GetBGCreature(AV_CPLACE_HERALD); - if (creature) - YellToAll(creature,buf,LANG_UNIVERSAL); - //update the statistic for the assaulting player - UpdatePlayerScore(player, (IsTower(node)) ? SCORE_TOWERS_ASSAULTED : SCORE_GRAVEYARDS_ASSAULTED, 1); - PlaySoundToAll((team == ALLIANCE)?AV_SOUND_ALLIANCE_ASSAULTS:AV_SOUND_HORDE_ASSAULTS); -} - -void BattleGroundAV::FillInitialWorldStates(WorldPacket& data) -{ - bool stateok; - //graveyards - for (uint8 i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; i++) - { - for (uint8 j =1; j <= 3; j+=2) - {//j=1=assaulted j=3=controled - stateok = (m_Nodes[i].State == j); - data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,ALLIANCE)]) << uint32((m_Nodes[i].Owner == ALLIANCE && stateok)?1:0); - data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,HORDE)]) << uint32((m_Nodes[i].Owner == HORDE && stateok)?1:0); - } - } - - //towers - for (uint8 i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_MAX; i++) - for (uint8 j =1; j <= 3; j+=2) - {//j=1=assaulted j=3=controled //i dont have j=2=destroyed cause destroyed is the same like enemy-team controll - stateok = (m_Nodes[i].State == j || (m_Nodes[i].State == POINT_DESTROYED && j == 3)); - data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,ALLIANCE)]) << uint32((m_Nodes[i].Owner == ALLIANCE && stateok)?1:0); - data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,HORDE)]) << uint32((m_Nodes[i].Owner == HORDE && stateok)?1:0); - } - if (m_Nodes[BG_AV_NODES_SNOWFALL_GRAVE].Owner == AV_NEUTRAL_TEAM) //cause neutral teams aren't handled generic - data << uint32(AV_SNOWFALL_N) << uint32(1); - data << uint32(AV_Alliance_Score) << uint32(m_Team_Scores[0]); - data << uint32(AV_Horde_Score) << uint32(m_Team_Scores[1]); - if (GetStatus() == STATUS_IN_PROGRESS){ //only if game started the teamscores are displayed - data << uint32(AV_SHOW_A_SCORE) << uint32(1); - data << uint32(AV_SHOW_H_SCORE) << uint32(1); - } - else - { - data << uint32(AV_SHOW_A_SCORE) << uint32(0); - data << uint32(AV_SHOW_H_SCORE) << uint32(0); - } - SendMineWorldStates(AV_NORTH_MINE); - SendMineWorldStates(AV_SOUTH_MINE); -} - -const uint8 BattleGroundAV::GetWorldStateType(uint8 state, uint16 team) //this is used for node worldstates and returns values which fit good into the worldstatesarray -{ - //neutral stuff cant get handled (currently its only snowfall) - assert(team != AV_NEUTRAL_TEAM); - //a_c a_a h_c h_a the positions in worldstate-array - if (team == ALLIANCE) - { - if (state == POINT_CONTROLED || state == POINT_DESTROYED) - return 0; - if (state == POINT_ASSAULTED) - return 1; - } - if (team == HORDE) - { - if (state == POINT_DESTROYED || state == POINT_CONTROLED) - return 2; - if (state == POINT_ASSAULTED) - return 3; - } - sLog.outError("BG_AV: should update a strange worldstate state:%i team:%i",state,team); - return 5; //this will crash the game, but i want to know if something is wrong here -} - -void BattleGroundAV::UpdateNodeWorldState(BG_AV_Nodes node) -{ - UpdateWorldState(BG_AV_NodeWorldStates[node][GetWorldStateType(m_Nodes[node].State,m_Nodes[node].Owner)],1); - if (m_Nodes[node].PrevOwner == AV_NEUTRAL_TEAM) //currently only snowfall is supported as neutral node (i don't want to make an extra row (neutral states) in worldstatesarray just for one node - UpdateWorldState(AV_SNOWFALL_N,0); - else - UpdateWorldState(BG_AV_NodeWorldStates[node][GetWorldStateType(m_Nodes[node].PrevState,m_Nodes[node].PrevOwner)],0); -} - -void BattleGroundAV::SendMineWorldStates(uint32 mine) -{ - assert(mine == AV_NORTH_MINE || mine == AV_SOUTH_MINE); -// currently i'm sure, that this works (: -// assert(m_Mine_PrevOwner[mine] == ALLIANCE || m_Mine_PrevOwner[mine] == HORDE || m_Mine_PrevOwner[mine] == AV_NEUTRAL_TEAM); -// assert(m_Mine_Owner[mine] == ALLIANCE || m_Mine_Owner[mine] == HORDE || m_Mine_Owner[mine] == AV_NEUTRAL_TEAM); - - uint8 owner,prevowner,mine2; //those variables are needed to access the right worldstate in the BG_AV_MineWorldStates array - mine2 = (mine == AV_NORTH_MINE)?0:1; - if (m_Mine_PrevOwner[mine] == ALLIANCE) - prevowner = 0; - else if (m_Mine_PrevOwner[mine] == HORDE) - prevowner = 2; - else - prevowner = 1; - if (m_Mine_Owner[mine] == ALLIANCE) - owner = 0; - else if (m_Mine_Owner[mine] == HORDE) - owner = 2; - else - owner = 1; - - UpdateWorldState(BG_AV_MineWorldStates[mine2][owner],1); - if (prevowner != owner) - UpdateWorldState(BG_AV_MineWorldStates[mine2][prevowner],0); -} - -WorldSafeLocsEntry const* BattleGroundAV::GetClosestGraveYard(Player* player) -{ - WorldSafeLocsEntry const* pGraveyard = NULL; - WorldSafeLocsEntry const* entry = NULL; - float dist = 0; - float minDist = 0; - float x, y; - - player->GetPosition(x, y); - - pGraveyard = sWorldSafeLocsStore.LookupEntry(BG_AV_GraveyardIds[GetTeamIndexByTeamId(player->GetTeam())+7]); - minDist = (pGraveyard->x - x)*(pGraveyard->x - x)+(pGraveyard->y - y)*(pGraveyard->y - y); - - for (uint8 i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) - if (m_Nodes[i].Owner == player->GetTeam() && m_Nodes[i].State == POINT_CONTROLED) - { - entry = sWorldSafeLocsStore.LookupEntry(BG_AV_GraveyardIds[i]); - if (entry) - { - dist = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y); - if (dist < minDist) - { - minDist = dist; - pGraveyard = entry; - } - } - } - return pGraveyard; -} - -bool BattleGroundAV::SetupBattleGround() -{ - // Create starting objects - if ( - // alliance gates - !AddObject(BG_AV_OBJECT_DOOR_A, BG_AV_OBJECTID_GATE_A, BG_AV_DoorPositons[0][0],BG_AV_DoorPositons[0][1],BG_AV_DoorPositons[0][2],BG_AV_DoorPositons[0][3],0,0,sin(BG_AV_DoorPositons[0][3]/2),cos(BG_AV_DoorPositons[0][3]/2),RESPAWN_IMMEDIATELY) - // horde gates - || !AddObject(BG_AV_OBJECT_DOOR_H, BG_AV_OBJECTID_GATE_H, BG_AV_DoorPositons[1][0],BG_AV_DoorPositons[1][1],BG_AV_DoorPositons[1][2],BG_AV_DoorPositons[1][3],0,0,sin(BG_AV_DoorPositons[1][3]/2),cos(BG_AV_DoorPositons[1][3]/2),RESPAWN_IMMEDIATELY)) - { - sLog.outErrorDb("BatteGroundAV: Failed to spawn some object BattleGround not created!1"); - return false; - } - - //spawn node-objects - for (uint8 i = BG_AV_NODES_FIRSTAID_STATION ; i < BG_AV_NODES_MAX; ++i) - { - if (i <= BG_AV_NODES_FROSTWOLF_HUT) - { - if (!AddObject(i,BG_AV_OBJECTID_BANNER_A_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(i+11,BG_AV_OBJECTID_BANNER_CONT_A_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(i+33,BG_AV_OBJECTID_BANNER_H_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(i+22,BG_AV_OBJECTID_BANNER_CONT_H_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - //aura - || !AddObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+i*3,BG_AV_OBJECTID_AURA_N,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+i*3,BG_AV_OBJECTID_AURA_A,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+i*3,BG_AV_OBJECTID_AURA_H,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!2"); - return false; - } - } - else //towers - { - if (i <= BG_AV_NODES_STONEHEART_BUNKER) //alliance towers - { - if (!AddObject(i,BG_AV_OBJECTID_BANNER_A,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(i+22,BG_AV_OBJECTID_BANNER_CONT_H,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_A,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_N,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_A,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_PH,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!3"); - return false; - } - } - else //horde towers - { - if (!AddObject(i+7,BG_AV_OBJECTID_BANNER_CONT_A,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(i+29,BG_AV_OBJECTID_BANNER_H,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_N,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_H,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_PA,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_H,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!4"); - return false; - } - } - for (uint8 j=0; j <= 9; j++) //burning aura - { - if (!AddObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j,BG_AV_OBJECTID_FIRE,BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][0],BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][1],BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][2],BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!5.%i",i); - return false; - } - } - } - } - for (uint8 i=0; i<2; i++) //burning aura for buildings - { - for (uint8 j=0; j <= 9; j++) - { - if (j<5) - { - if (!AddObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+(i*10)+j,BG_AV_OBJECTID_SMOKE,BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][0],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][1],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][2],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!6.%i",i); - return false; - } - } - else - { - if (!AddObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+(i*10)+j,BG_AV_OBJECTID_FIRE,BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][0],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][1],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][2],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!7.%i",i); - return false; - } - } - } - } - for (uint16 i= 0; i <= (BG_AV_OBJECT_MINE_SUPPLY_N_MAX-BG_AV_OBJECT_MINE_SUPPLY_N_MIN); i++) - { - if (!AddObject(BG_AV_OBJECT_MINE_SUPPLY_N_MIN+i,BG_AV_OBJECTID_MINE_N,BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][0],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][1],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][2],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some mine supplies BattleGround not created!7.5.%i",i); - return false; - } - } - for (uint16 i= 0 ; i <= (BG_AV_OBJECT_MINE_SUPPLY_S_MAX-BG_AV_OBJECT_MINE_SUPPLY_S_MIN); i++) - { - if (!AddObject(BG_AV_OBJECT_MINE_SUPPLY_S_MIN+i,BG_AV_OBJECTID_MINE_S,BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][0],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][1],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][2],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3]/2),RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some mine supplies BattleGround not created!7.6.%i",i); - return false; - } - } - - if (!AddObject(BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE, BG_AV_OBJECTID_BANNER_SNOWFALL_N ,BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][0],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][1],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][2],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3],0,0,sin(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), cos(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!8"); - return false; - } - for (uint8 i = 0; i < 4; i++) - { - if (!AddObject(BG_AV_OBJECT_SNOW_EYECANDY_A+i, BG_AV_OBJECTID_SNOWFALL_CANDY_A ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_PA+i, BG_AV_OBJECTID_SNOWFALL_CANDY_PA ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_H+i, BG_AV_OBJECTID_SNOWFALL_CANDY_H ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) - || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_PH+i, BG_AV_OBJECTID_SNOWFALL_CANDY_PH ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY)) - { - sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!9.%i",i); - return false; - } - } - - uint16 i; - sLog.outDebug("Alterac Valley: entering state STATUS_WAIT_JOIN ..."); - // Initial Nodes - for (i = 0; i < BG_AV_OBJECT_MAX; i++) - SpawnBGObject(i, RESPAWN_ONE_DAY); - for (i = BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE ; i++){ - SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+3*i,RESPAWN_IMMEDIATELY); - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - } - for (i = BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER ; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - for (i = BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE; i <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER ; i++){ - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - if (i <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT) - SpawnBGObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+3*GetNodeThroughObject(i),RESPAWN_IMMEDIATELY); - } - for (i = BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_TFLAG_A_STONEHEART_BUNKER; i+=2) - { - SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag - SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura - } - for (i = BG_AV_OBJECT_TFLAG_H_ICEBLOOD_TOWER; i <= BG_AV_OBJECT_TFLAG_H_FROSTWOLF_WTOWER; i+=2) - { - SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag - SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura - } - //snowfall and the doors - for (i = BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; i <= BG_AV_OBJECT_DOOR_A; i++) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE,RESPAWN_IMMEDIATELY); - - //creatures - sLog.outDebug("BG_AV start poputlating nodes"); - for (BG_AV_Nodes i= BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i) - { - if (m_Nodes[i].Owner) - PopulateNode(i); - } - //all creatures which don't get despawned through the script are static - sLog.outDebug("BG_AV: start spawning static creatures"); - for (i=0; i < AV_STATICCPLACE_MAX; i++) - AddAVCreature(0,i+AV_CPLACE_MAX); - //mainspiritguides: - sLog.outDebug("BG_AV: start spawning spiritguides creatures"); - AddSpiritGuide(7, BG_AV_CreaturePos[7][0], BG_AV_CreaturePos[7][1], BG_AV_CreaturePos[7][2], BG_AV_CreaturePos[7][3], ALLIANCE); - AddSpiritGuide(8, BG_AV_CreaturePos[8][0], BG_AV_CreaturePos[8][1], BG_AV_CreaturePos[8][2], BG_AV_CreaturePos[8][3], HORDE); - //spawn the marshals (those who get deleted, if a tower gets destroyed) - sLog.outDebug("BG_AV: start spawning marshal creatures"); - for (i=AV_NPC_A_MARSHAL_SOUTH; i <= AV_NPC_H_MARSHAL_WTOWER; i++) - AddAVCreature(i,AV_CPLACE_A_MARSHAL_SOUTH+(i-AV_NPC_A_MARSHAL_SOUTH)); - AddAVCreature(AV_NPC_HERALD,AV_CPLACE_HERALD); - return true; -} - -const char* BattleGroundAV::GetNodeName(BG_AV_Nodes node) -{ - switch (node) - { - case BG_AV_NODES_FIRSTAID_STATION: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_STORM_AID); - case BG_AV_NODES_DUNBALDAR_SOUTH: return GetTrinityString(LANG_BG_AV_NODE_TOWER_DUN_S); - case BG_AV_NODES_DUNBALDAR_NORTH: return GetTrinityString(LANG_BG_AV_NODE_TOWER_DUN_N); - case BG_AV_NODES_STORMPIKE_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_STORMPIKE); - case BG_AV_NODES_ICEWING_BUNKER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_ICEWING); - case BG_AV_NODES_STONEHEART_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_STONE); - case BG_AV_NODES_STONEHEART_BUNKER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_STONE); - case BG_AV_NODES_SNOWFALL_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_SNOW); - case BG_AV_NODES_ICEBLOOD_TOWER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_ICE); - case BG_AV_NODES_ICEBLOOD_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_ICE); - case BG_AV_NODES_TOWER_POINT: return GetTrinityString(LANG_BG_AV_NODE_TOWER_POINT); - case BG_AV_NODES_FROSTWOLF_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_FROST); - case BG_AV_NODES_FROSTWOLF_ETOWER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_FROST_E); - case BG_AV_NODES_FROSTWOLF_WTOWER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_FROST_W); - case BG_AV_NODES_FROSTWOLF_HUT: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_FROST_HUT); - default: - { - sLog.outError("tried to get name for node %u%",node); - return "Unknown"; - break; - } - } -} - -void BattleGroundAV::AssaultNode(BG_AV_Nodes node, uint16 team) -{ - if (m_Nodes[node].TotalOwner == team) - { - sLog.outCrash("Assaulting team is TotalOwner of node"); - assert (false); - } - if (m_Nodes[node].Owner == team) - { - sLog.outCrash("Assaulting team is owner of node"); - assert (false); - } - if (m_Nodes[node].State == POINT_DESTROYED) - { - sLog.outCrash("Destroyed node is being assaulted"); - assert (false); - } - if (m_Nodes[node].State == POINT_ASSAULTED && m_Nodes[node].TotalOwner) //only assault an assaulted node if no totalowner exists - { - sLog.outCrash("Assault on an not assaulted node with total owner"); - assert (false); - } - //the timer gets another time, if the previous owner was 0 == Neutral - m_Nodes[node].Timer = (m_Nodes[node].PrevOwner)? BG_AV_CAPTIME : BG_AV_SNOWFALL_FIRSTCAP; - m_Nodes[node].PrevOwner = m_Nodes[node].Owner; - m_Nodes[node].Owner = team; - m_Nodes[node].PrevState = m_Nodes[node].State; - m_Nodes[node].State = POINT_ASSAULTED; -} - -void BattleGroundAV::DestroyNode(BG_AV_Nodes node) -{ - assert(m_Nodes[node].State == POINT_ASSAULTED); - - m_Nodes[node].TotalOwner = m_Nodes[node].Owner; - m_Nodes[node].PrevOwner = m_Nodes[node].Owner; - m_Nodes[node].PrevState = m_Nodes[node].State; - m_Nodes[node].State = (m_Nodes[node].Tower)? POINT_DESTROYED : POINT_CONTROLED; - m_Nodes[node].Timer = 0; -} - -void BattleGroundAV::InitNode(BG_AV_Nodes node, uint16 team, bool tower) -{ - m_Nodes[node].TotalOwner = team; - m_Nodes[node].Owner = team; - m_Nodes[node].PrevOwner = 0; - m_Nodes[node].State = POINT_CONTROLED; - m_Nodes[node].PrevState = m_Nodes[node].State; - m_Nodes[node].State = POINT_CONTROLED; - m_Nodes[node].Timer = 0; - m_Nodes[node].Tower = tower; -} - -void BattleGroundAV::DefendNode(BG_AV_Nodes node, uint16 team) -{ - assert(m_Nodes[node].TotalOwner == team); - assert(m_Nodes[node].Owner != team); - assert(m_Nodes[node].State != POINT_CONTROLED && m_Nodes[node].State != POINT_DESTROYED); - m_Nodes[node].PrevOwner = m_Nodes[node].Owner; - m_Nodes[node].Owner = team; - m_Nodes[node].PrevState = m_Nodes[node].State; - m_Nodes[node].State = POINT_CONTROLED; - m_Nodes[node].Timer = 0; -} - -void BattleGroundAV::ResetBGSubclass() -{ - m_MaxLevel=0; - for (uint8 i=0; i<2; i++) //forloop for both teams (it just make 0 == alliance and 1 == horde also for both mines 0=north 1=south - { - for (uint8 j=0; j<9; j++) - m_Team_QuestStatus[i][j]=0; - m_Team_Scores[i]=BG_AV_SCORE_INITIAL_POINTS; - m_IsInformedNearVictory[i]=false; - m_CaptainAlive[i] = true; - m_CaptainBuffTimer[i] = 120000 + urand(0,4)* 60; //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes TODO get the right times - m_Mine_Owner[i] = AV_NEUTRAL_TEAM; - m_Mine_PrevOwner[i] = m_Mine_Owner[i]; - } - for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_STONEHEART_GRAVE; ++i) //alliance graves - InitNode(i,ALLIANCE,false); - for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_STONEHEART_BUNKER; ++i) //alliance towers - InitNode(i,ALLIANCE,true); - for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_GRAVE; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) //horde graves - InitNode(i,HORDE,false); - for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_TOWER; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) //horde towers - InitNode(i,HORDE,true); - InitNode(BG_AV_NODES_SNOWFALL_GRAVE,AV_NEUTRAL_TEAM,false); //give snowfall neutral owner - - m_Mine_Timer=AV_MINE_TICK_TIMER; - for (uint16 i = 0; i < AV_CPLACE_MAX+AV_STATICCPLACE_MAX; i++) - if (m_BgCreatures[i]) - DelCreature(i); - -} - - diff --git a/src/server/game/BattleGroundAV.h b/src/server/game/BattleGroundAV.h deleted file mode 100644 index 6d95c7bbc5d..00000000000 --- a/src/server/game/BattleGroundAV.h +++ /dev/null @@ -1,1614 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDAV_H -#define __BATTLEGROUNDAV_H - -class BattleGround; - -#define LANG_BG_AV_A_CAPTAIN_BUFF "Begone. Uncouth scum! The Alliance shall prevail in Alterac Valley!" -#define LANG_BG_AV_H_CAPTAIN_BUFF "Now is the time to attack! For the Horde!" -#define LANG_BG_AV_S_MINE_BOSS_CLAIMS "Snivvle is here! Snivvle claims the Coldtooth Mine!" - -#define BG_AV_CAPTIME 240000 //4:00 -#define BG_AV_SNOWFALL_FIRSTCAP 300000 //5:00 but i also have seen 4:05 - -#define BG_AV_SCORE_INITIAL_POINTS 600 -#define SEND_MSG_NEAR_LOSE 120 - -#define BG_AV_KILL_BOSS 4 -#define BG_AV_REP_BOSS 350 - -#define BG_AV_KILL_CAPTAIN 3 -#define BG_AV_REP_CAPTAIN 125 -#define BG_AV_RES_CAPTAIN 100 - -#define BG_AV_KILL_TOWER 3 -#define BG_AV_REP_TOWER 12 -#define BG_AV_RES_TOWER 75 - -#define BG_AV_GET_COMMANDER 1 //for a safely returned wingcommander -//bonushonor at the end -#define BG_AV_KILL_SURVIVING_TOWER 2 -#define BG_AV_REP_SURVIVING_TOWER 12 - -#define BG_AV_KILL_SURVIVING_CAPTAIN 2 -#define BG_AV_REP_SURVIVING_CAPTAIN 125 - -enum BG_AV_Sounds -{ //TODO: get out if there comes a sound when neutral team captures mine - -/* -8212: - alliance grave assault - alliance tower assault - drek "mlanzenabschaum! In meiner Burg?! Toetet sie all" - nicht immer der sound -8333: - galv "sterbt fuer euch ist kein platz hier" - -8332: - bal "Verschwinde, dreckiger Abschaum! Die Allianz wird im Alteractal " -8174: - horde tower assault - horde grave assault - van "es Sturmlanzenklans, euer General wird angegriffen! Ich fordere Unterst" -8173: - ally grave capture/defend - tower destroy - mine capture - ally wins -8192: - ally tower destroy(only iceblood - found a bug^^) - ally tower defend - horde tower defend -8213 -horde: - grave defend/capture - tower destroy - mine capture - horde wins - */ - - AV_SOUND_NEAR_VICTORY = 8456, //not confirmed yet - - AV_SOUND_ALLIANCE_ASSAULTS = 8212, //tower,grave + enemy boss if someone tries to attack him - AV_SOUND_HORDE_ASSAULTS = 8174, - AV_SOUND_ALLIANCE_GOOD = 8173, //if something good happens for the team: wins(maybe only through killing the boss), captures mine or grave, destroys tower and defends grave - AV_SOUND_HORDE_GOOD = 8213, - AV_SOUND_BOTH_TOWER_DEFEND = 8192, - - AV_SOUND_ALLIANCE_CAPTAIN = 8232, //gets called when someone attacks them and at the beginning after 3min+rand(x)*10sec (maybe buff) - AV_SOUND_HORDE_CAPTAIN = 8333, - -}; - -enum BG_AV_OTHER_VALUES -{ - AV_STATICCPLACE_MAX = 123, - AV_NORTH_MINE = 0, - AV_SOUTH_MINE = 1, - AV_MINE_TICK_TIMER = 45000, - AV_MINE_RECLAIM_TIMER = 1200000, //TODO: get the right value.. this is currently 20 minutes - AV_NEUTRAL_TEAM = 0 //this is the neutral owner of snowfall -}; -enum BG_AV_ObjectIds -{ - //cause the mangos-system is a bit different, we don't use the right go-ids for every node.. if we want to be 100% like another big server, we must take one object for every node - //snowfall 4flags as eyecandy 179424 (alliance neutral) - //Banners - stolen from battleground_AB.h ;-) - BG_AV_OBJECTID_BANNER_A = 178925, // can only be used by horde - BG_AV_OBJECTID_BANNER_H = 178943, // can only be used by alliance - BG_AV_OBJECTID_BANNER_CONT_A = 178940, // can only be used by horde - BG_AV_OBJECTID_BANNER_CONT_H = 179435, // can only be used by alliance - - BG_AV_OBJECTID_BANNER_A_B = 178365, - BG_AV_OBJECTID_BANNER_H_B = 178364, - BG_AV_OBJECTID_BANNER_CONT_A_B = 179286, - BG_AV_OBJECTID_BANNER_CONT_H_B = 179287, - BG_AV_OBJECTID_BANNER_SNOWFALL_N = 180418, - - //snowfall eyecandy banner: - BG_AV_OBJECTID_SNOWFALL_CANDY_A = 179044, - BG_AV_OBJECTID_SNOWFALL_CANDY_PA = 179424, - BG_AV_OBJECTID_SNOWFALL_CANDY_H = 179064, - BG_AV_OBJECTID_SNOWFALL_CANDY_PH = 179425, - - //banners on top of towers: - BG_AV_OBJECTID_TOWER_BANNER_A = 178927, //[PH] Alliance A1 Tower Banner BIG - BG_AV_OBJECTID_TOWER_BANNER_H = 178955, //[PH] Horde H1 Tower Banner BIG - BG_AV_OBJECTID_TOWER_BANNER_PA = 179446, //[PH] Alliance H1 Tower Pre-Banner BIG - BG_AV_OBJECTID_TOWER_BANNER_PH = 179436, //[PH] Horde A1 Tower Pre-Banner BIG - - //Auras - BG_AV_OBJECTID_AURA_A = 180421, - BG_AV_OBJECTID_AURA_H = 180422, - BG_AV_OBJECTID_AURA_N = 180423, - BG_AV_OBJECTID_AURA_A_S = 180100, - BG_AV_OBJECTID_AURA_H_S = 180101, - BG_AV_OBJECTID_AURA_N_S = 180102, - - BG_AV_OBJECTID_GATE_A = 180424, - BG_AV_OBJECTID_GATE_H = 180424, - - //mine supplies - BG_AV_OBJECTID_MINE_N = 178785, - BG_AV_OBJECTID_MINE_S = 178784, - - BG_AV_OBJECTID_FIRE = 179065, - BG_AV_OBJECTID_SMOKE = 179066 -}; - -enum BG_AV_Nodes -{ - BG_AV_NODES_FIRSTAID_STATION = 0, - BG_AV_NODES_STORMPIKE_GRAVE = 1, - BG_AV_NODES_STONEHEART_GRAVE = 2, - BG_AV_NODES_SNOWFALL_GRAVE = 3, - BG_AV_NODES_ICEBLOOD_GRAVE = 4, - BG_AV_NODES_FROSTWOLF_GRAVE = 5, - BG_AV_NODES_FROSTWOLF_HUT = 6, - BG_AV_NODES_DUNBALDAR_SOUTH = 7, - BG_AV_NODES_DUNBALDAR_NORTH = 8, - BG_AV_NODES_ICEWING_BUNKER = 9, - BG_AV_NODES_STONEHEART_BUNKER = 10, - BG_AV_NODES_ICEBLOOD_TOWER = 11, - BG_AV_NODES_TOWER_POINT = 12, - BG_AV_NODES_FROSTWOLF_ETOWER = 13, - BG_AV_NODES_FROSTWOLF_WTOWER = 14, - - BG_AV_NODES_MAX = 15 -}; - -enum BG_AV_ObjectTypes -{ - BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION = 0, - BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE = 1, - BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE = 2, - BG_AV_OBJECT_FLAG_A_SNOWFALL_GRAVE = 3, - BG_AV_OBJECT_FLAG_A_ICEBLOOD_GRAVE = 4, - BG_AV_OBJECT_FLAG_A_FROSTWOLF_GRAVE = 5, - BG_AV_OBJECT_FLAG_A_FROSTWOLF_HUT = 6, - BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH = 7, - BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH = 8, - BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER = 9, - BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER = 10, - - BG_AV_OBJECT_FLAG_C_A_FIRSTAID_STATION = 11, - BG_AV_OBJECT_FLAG_C_A_STORMPIKE_GRAVE = 12, - BG_AV_OBJECT_FLAG_C_A_STONEHEART_GRAVE = 13, - BG_AV_OBJECT_FLAG_C_A_SNOWFALL_GRAVE = 14, - BG_AV_OBJECT_FLAG_C_A_ICEBLOOD_GRAVE = 15, - BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_GRAVE = 16, - BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_HUT = 17, - BG_AV_OBJECT_FLAG_C_A_ICEBLOOD_TOWER = 18, - BG_AV_OBJECT_FLAG_C_A_TOWER_POINT = 19, - BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_ETOWER = 20, - BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_WTOWER = 21, - - BG_AV_OBJECT_FLAG_C_H_FIRSTAID_STATION = 22, - BG_AV_OBJECT_FLAG_C_H_STORMPIKE_GRAVE = 23, - BG_AV_OBJECT_FLAG_C_H_STONEHEART_GRAVE = 24, - BG_AV_OBJECT_FLAG_C_H_SNOWFALL_GRAVE = 25, - BG_AV_OBJECT_FLAG_C_H_ICEBLOOD_GRAVE = 26, - BG_AV_OBJECT_FLAG_C_H_FROSTWOLF_GRAVE = 27, - BG_AV_OBJECT_FLAG_C_H_FROSTWOLF_HUT = 28, - BG_AV_OBJECT_FLAG_C_H_DUNBALDAR_SOUTH = 29, - BG_AV_OBJECT_FLAG_C_H_DUNBALDAR_NORTH = 30, - BG_AV_OBJECT_FLAG_C_H_ICEWING_BUNKER = 31, - BG_AV_OBJECT_FLAG_C_H_STONEHEART_BUNKER = 32, - - BG_AV_OBJECT_FLAG_H_FIRSTAID_STATION = 33, - BG_AV_OBJECT_FLAG_H_STORMPIKE_GRAVE = 34, - BG_AV_OBJECT_FLAG_H_STONEHEART_GRAVE = 35, - BG_AV_OBJECT_FLAG_H_SNOWFALL_GRAVE = 36, - BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE = 37, - BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE = 38, - BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT = 39, - BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER = 40, - BG_AV_OBJECT_FLAG_H_TOWER_POINT = 41, - BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER = 42, - BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER = 43, - - BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE = 44, - - BG_AV_OBJECT_DOOR_H = 45, - BG_AV_OBJECT_DOOR_A = 46, -//auras for graveyards (3auras per graveyard neutral,alliance,horde) - BG_AV_OBJECT_AURA_N_FIRSTAID_STATION = 47, - BG_AV_OBJECT_AURA_A_FIRSTAID_STATION = 48, - BG_AV_OBJECT_AURA_H_FIRSTAID_STATION = 49, - BG_AV_OBJECT_AURA_N_STORMPIKE_GRAVE = 50, - BG_AV_OBJECT_AURA_A_STORMPIKE_GRAVE = 51, - BG_AV_OBJECT_AURA_H_STORMPIKE_GRAVE = 52, - BG_AV_OBJECT_AURA_N_STONEHEART_GRAVE = 53, - BG_AV_OBJECT_AURA_A_STONEHEART_GRAVE = 54, - BG_AV_OBJECT_AURA_H_STONEHEART_GRAVE = 55, - BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE = 56, - BG_AV_OBJECT_AURA_A_SNOWFALL_GRAVE = 57, - BG_AV_OBJECT_AURA_H_SNOWFALL_GRAVE = 58, - BG_AV_OBJECT_AURA_N_ICEBLOOD_GRAVE = 59, - BG_AV_OBJECT_AURA_A_ICEBLOOD_GRAVE = 60, - BG_AV_OBJECT_AURA_H_ICEBLOOD_GRAVE = 61, - BG_AV_OBJECT_AURA_N_FROSTWOLF_GRAVE = 62, - BG_AV_OBJECT_AURA_A_FROSTWOLF_GRAVE = 63, - BG_AV_OBJECT_AURA_H_FROSTWOLF_GRAVE = 64, - BG_AV_OBJECT_AURA_N_FROSTWOLF_HUT = 65, - BG_AV_OBJECT_AURA_A_FROSTWOLF_HUT = 66, - BG_AV_OBJECT_AURA_H_FROSTWOLF_HUT = 67, - - //big flags on top of towers 2 flags on each (contested,(alliance | horde)) + 2 auras - BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH = 67, - BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH = 68, - BG_AV_OBJECT_TFLAG_A_DUNBALDAR_NORTH = 69, - BG_AV_OBJECT_TFLAG_H_DUNBALDAR_NORTH = 70, - BG_AV_OBJECT_TFLAG_A_ICEWING_BUNKER = 71, - BG_AV_OBJECT_TFLAG_H_ICEWING_BUNKER = 72, - BG_AV_OBJECT_TFLAG_A_STONEHEART_BUNKER = 73, - BG_AV_OBJECT_TFLAG_H_STONEHEART_BUNKER = 74, - BG_AV_OBJECT_TFLAG_A_ICEBLOOD_TOWER = 75, - BG_AV_OBJECT_TFLAG_H_ICEBLOOD_TOWER = 76, - BG_AV_OBJECT_TFLAG_A_TOWER_POINT = 77, - BG_AV_OBJECT_TFLAG_H_TOWER_POINT = 78, - BG_AV_OBJECT_TFLAG_A_FROSTWOLF_ETOWER = 79, - BG_AV_OBJECT_TFLAG_H_FROSTWOLF_ETOWER = 80, - BG_AV_OBJECT_TFLAG_A_FROSTWOLF_WTOWER = 81, - BG_AV_OBJECT_TFLAG_H_FROSTWOLF_WTOWER = 82, - BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH = 83, - BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH = 84, - BG_AV_OBJECT_TAURA_A_DUNBALDAR_NORTH = 85, - BG_AV_OBJECT_TAURA_H_DUNBALDAR_NORTH = 86, - BG_AV_OBJECT_TAURA_A_ICEWING_BUNKER = 87, - BG_AV_OBJECT_TAURA_H_ICEWING_BUNKER = 88, - BG_AV_OBJECT_TAURA_A_STONEHEART_BUNKER = 89, - BG_AV_OBJECT_TAURA_H_STONEHEART_BUNKER = 90, - BG_AV_OBJECT_TAURA_A_ICEBLOOD_TOWER = 91, - BG_AV_OBJECT_TAURA_H_ICEBLOOD_TOWER = 92, - BG_AV_OBJECT_TAURA_A_TOWER_POINT = 93, - BG_AV_OBJECT_TAURA_H_TOWER_POINT = 94, - BG_AV_OBJECT_TAURA_A_FROSTWOLF_ETOWER = 95, - BG_AV_OBJECT_TAURA_H_FROSTWOLF_ETOWER = 96, - BG_AV_OBJECT_TAURA_A_FROSTWOLF_WTOWER = 97, - BG_AV_OBJECT_TAURA_H_FROSTWOLF_WTOWER = 98, - - BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH = 99, - BG_AV_OBJECT_BURN_DUNBALDAR_NORTH = 109, - BG_AV_OBJECT_BURN_ICEWING_BUNKER = 119, - BG_AV_OBJECT_BURN_STONEHEART_BUNKER = 129, - BG_AV_OBJECT_BURN_ICEBLOOD_TOWER = 139, - BG_AV_OBJECT_BURN_TOWER_POINT = 149, - BG_AV_OBJECT_BURN_FROSTWOLF_ETWOER = 159, - BG_AV_OBJECT_BURN_FROSTWOLF_WTOWER = 169, - BG_AV_OBJECT_BURN_BUILDING_ALLIANCE = 179, - BG_AV_OBJECT_BURN_BUILDING_HORDE = 189, - BG_AV_OBJECT_SNOW_EYECANDY_A = 199, - BG_AV_OBJECT_SNOW_EYECANDY_PA = 203, - BG_AV_OBJECT_SNOW_EYECANDY_H = 207, - BG_AV_OBJECT_SNOW_EYECANDY_PH = 211, - BG_AV_OBJECT_MINE_SUPPLY_N_MIN = 215, - BG_AV_OBJECT_MINE_SUPPLY_N_MAX = 224, - BG_AV_OBJECT_MINE_SUPPLY_S_MIN = 225, - BG_AV_OBJECT_MINE_SUPPLY_S_MAX = 236, - - BG_AV_OBJECT_MAX = 237 -}; - -enum BG_AV_OBJECTS -{ - AV_OPLACE_FIRSTAID_STATION = 0, - AV_OPLACE_STORMPIKE_GRAVE = 1, - AV_OPLACE_STONEHEART_GRAVE = 2, - AV_OPLACE_SNOWFALL_GRAVE = 3, - AV_OPLACE_ICEBLOOD_GRAVE = 4, - AV_OPLACE_FROSTWOLF_GRAVE = 5, - AV_OPLACE_FROSTWOLF_HUT = 6, - AV_OPLACE_DUNBALDAR_SOUTH = 7, - AV_OPLACE_DUNBALDAR_NORTH = 8, - AV_OPLACE_ICEWING_BUNKER = 9, - AV_OPLACE_STONEHEART_BUNKER = 10, - AV_OPLACE_ICEBLOOD_TOWER = 11, - AV_OPLACE_TOWER_POINT = 12, - AV_OPLACE_FROSTWOLF_ETOWER = 13, - AV_OPLACE_FROSTWOLF_WTOWER = 14, - AV_OPLACE_BIGBANNER_DUNBALDAR_SOUTH = 15, - AV_OPLACE_BIGBANNER_DUNBALDAR_NORTH = 16, - AV_OPLACE_BIGBANNER_ICEWING_BUNKER = 17, - AV_OPLACE_BIGBANNER_STONEHEART_BUNKER = 18, - AV_OPLACE_BIGBANNER_ICEBLOOD_TOWER = 19, - AV_OPLACE_BIGBANNER_TOWER_POINT = 20, - AV_OPLACE_BIGBANNER_FROSTWOLF_ETOWER = 21, - AV_OPLACE_BIGBANNER_FROSTWOLF_WTOWER = 22, - - AV_OPLACE_BURN_DUNBALDAR_SOUTH = 23, - AV_OPLACE_BURN_DUNBALDAR_NORTH = 33, - AV_OPLACE_BURN_ICEWING_BUNKER = 43, - AV_OPLACE_BURN_STONEHEART_BUNKER = 53, - AV_OPLACE_BURN_ICEBLOOD_TOWER = 63, - AV_OPLACE_BURN_TOWER_POINT = 73, - AV_OPLACE_BURN_FROSTWOLF_ETOWER = 83, - AV_OPLACE_BURN_FROSTWOLF_WTOWER = 93, - AV_OPLACE_BURN_BUILDING_A = 103, - AV_OPLACE_BURN_BUILDING_H = 113, - AV_OPLACE_SNOW_1 = 123, - AV_OPLACE_SNOW_2 = 124, - AV_OPLACE_SNOW_3 = 125, - AV_OPLACE_SNOW_4 = 126, - AV_OPLACE_MINE_SUPPLY_N_MIN = 127, - AV_OPLACE_MINE_SUPPLY_N_MAX = 136, - AV_OPLACE_MINE_SUPPLY_S_MIN = 137, - AV_OPLACE_MINE_SUPPLY_S_MAX = 148, - - AV_OPLACE_MAX = 149 -}; -const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = { - {638.592f,-32.422f,46.0608f,-1.62316f },//firstaid station - {669.007f,-294.078f,30.2909f,2.77507f },//stormpike - {77.8013f,-404.7f,46.7549f,-0.872665f },//stone grave - {-202.581f,-112.73f,78.4876f,-0.715585f },//snowfall - {-611.962f,-396.17f,60.8351f,2.53682f}, //iceblood grave - {-1082.45f,-346.823f,54.9219f,-1.53589f },//frostwolf grave - {-1402.21f,-307.431f,89.4424f,0.191986f },//frostwolf hut - {553.779f,-78.6566f,51.9378f,-1.22173f }, //dunnbaldar south - {674.001f,-143.125f,63.6615f,0.994838f }, //dunbaldar north - {203.281f,-360.366f,56.3869f,-0.925024f }, //icew - {-152.437f,-441.758f,40.3982f,-1.95477f }, //stone - {-571.88f,-262.777f,75.0087f,-0.802851f }, //ice tower - {-768.907f,-363.71f,90.8949f,1.07991f}, //tower point - {-1302.9f,-316.981f,113.867f,2.00713f }, //frostwolf etower - {-1297.5f,-266.767f,114.15f,3.31044f}, //frostwolf wtower - //bigbanner: - {555.848f,-84.4151f,64.4397f,3.12414f }, //duns - {679.339f,-136.468f,73.9626f,-2.16421f }, //dunn - {208.973f,-365.971f,66.7409f,-0.244346f }, //icew - {-155.832f,-449.401f,52.7306f,0.610865f }, //stone - {-572.329f,-262.476f,88.6496f,-0.575959f }, //icetower - {-768.199f,-363.105f,104.537f,0.10472f }, //towerp - {-1302.84f,-316.582f,127.516f,0.122173f }, //etower - {-1297.87f,-266.762f,127.796f,0.0698132f }, //wtower - //burning auras towers have 9*179065 captain-buildings have 5*179066+5*179065 - //dunns - {562.632f,-88.1815f,61.993f,0.383972f }, - {562.523f,-74.5028f,37.9474f,-0.0523599f }, - {558.097f,-70.9842f,52.4876f,0.820305f }, - {578.167f,-71.8191f,38.1514f,2.72271f }, - {556.028f,-94.9242f,44.8191f,3.05433f }, - {572.451f,-94.3655f,37.9443f,-1.72788f }, - {549.263f,-79.3645f,44.8191f,0.436332f }, - {543.513f,-94.4006f,52.4819f,0.0349066f }, - {572.149f,-93.7862f,52.5726f,0.541052f }, - {582.162f,-81.2375f,37.9216f,0.0872665f }, - //dunn - {664.797f,-143.65f,64.1784f,-0.453786f}, - {664.505f,-139.452f,49.6696f,-0.0349067f}, - {676.067f,-124.319f,49.6726f,-1.01229f}, - {693.004f,-144.025f,64.1755f,2.44346f}, - {661.175f,-117.691f,49.645f,1.91986f}, - {684.423f,-146.582f,63.6662f,0.994838f}, - {682.791f,-127.769f,62.4155f,1.09956f}, - {674.576f,-147.101f,56.5425f,-1.6057f}, - {655.719f,-126.673f,49.8138f,2.80998f}, - {0,0,0,0}, - //icew - {231.503f,-356.688f,42.3704f,0.296706f}, - {224.989f,-348.175f,42.5607f,1.50098f}, - {205.782f,-351.335f,56.8998f,1.01229f}, - {196.605f,-369.187f,56.3914f,2.46091f}, - {210.619f,-376.938f,49.2677f,2.86234f}, - {209.647f,-352.632f,42.3959f,-0.698132f}, - {220.65f,-368.132f,42.3978f,-0.2618f}, - {224.682f,-374.031f,57.0679f,0.541052f}, - {200.26f,-359.968f,49.2677f,-2.89725f}, - {196.619f,-378.016f,56.9131f,1.01229f}, - //stone - {-155.488f,-437.356f,33.2796f,2.60054f}, - {-163.441f,-454.188f,33.2796f,1.93732f}, - {-143.977f,-445.148f,26.4097f,-1.8675f}, - {-135.764f,-464.708f,26.3823f,2.25147f}, - {-154.076f,-466.929f,41.0636f,-1.8675f}, - {-149.908f,-460.332f,26.4083f,-2.09439f}, - {-151.638f,-439.521f,40.3797f,0.436332f}, - {-131.301f,-454.905f,26.5771f,2.93215f}, - {-171.291f,-444.684f,40.9211f,2.30383f}, - {-143.591f,-439.75f,40.9275f,-1.72788f}, - //iceblood - {-572.667f,-267.923f,56.8542f,2.35619f}, - {-561.021f,-262.689f,68.4589f,1.37881f}, - {-572.538f,-262.649f,88.6197f,1.8326f}, - {-574.77f,-251.45f,74.9422f,-1.18682f}, - {-578.625f,-267.571f,68.4696f,0.506145f}, - {-571.476f,-257.234f,63.3223f,3.10669f}, - {-566.035f,-273.907f,52.9582f,-0.890118f}, - {-580.948f,-259.77f,68.4696f,1.46608f}, - {-568.318f,-267.1f,75.0008f,1.01229f}, - {-559.621f,-268.597f,52.8986f,0.0523599f}, - //towerp - {-776.072f,-368.046f,84.3558f,2.63545f}, - {-777.564f,-368.521f,90.6701f,1.72788f}, - {-765.461f,-357.711f,90.888f,0.314159f}, - {-768.763f,-362.735f,104.612f,1.81514f}, - {-760.356f,-358.896f,84.3558f,2.1293f}, - {-771.967f,-352.838f,84.3484f,1.74533f}, - {-773.333f,-364.653f,79.2351f,-1.64061f}, - {-764.109f,-366.069f,70.0934f,0.383972f}, - {-767.103f,-350.737f,68.7933f,2.80998f}, - {-760.115f,-353.845f,68.8633f,1.79769f}, - //froste - {-1304.87f,-304.525f,91.8366f,-0.680679f}, - {-1301.77f,-310.974f,95.8252f,0.907571f}, - {-1305.58f,-320.625f,102.166f,-0.558505f}, - {-1294.27f,-323.468f,113.893f,-1.67552f}, - {-1302.65f,-317.192f,127.487f,2.30383f}, - {-1293.89f,-313.478f,107.328f,1.6057f}, - {-1312.41f,-312.999f,107.328f,1.5708f}, - {-1311.57f,-308.08f,91.7666f,-1.85005f}, - {-1314.7f,-322.131f,107.36f,0.645772f}, - {-1304.6f,-310.754f,113.859f,-0.401426f}, - //frostw - {-1308.24f,-273.26f,92.0514f,-0.139626f}, - {-1302.26f,-262.858f,95.9269f,0.418879f}, - {-1297.28f,-267.773f,126.756f,2.23402f}, - {-1299.08f,-256.89f,114.108f,-2.44346f}, - {-1303.41f,-268.237f,114.151f,-1.23918f}, - {-1304.43f,-273.682f,107.612f,0.244346f}, - {-1309.53f,-265.951f,92.1418f,-2.49582f}, - {-1295.55f,-263.865f,105.033f,0.925024f}, - {-1294.71f,-281.466f,107.664f,-1.50098f}, - {-1289.69f,-259.521f,107.612f,-2.19912f}, - - //the two buildings of the captains - //alliance - {-64.4987f,-289.33f,33.4616f,-2.82743f}, - {-5.98025f,-326.144f,38.8538f,0}, - {-2.67893f,-306.998f,33.4165f,0}, - {-60.25f,-309.232f,50.2408f,-1.46608f}, - {-48.7941f,-266.533f,47.7916f,2.44346f}, - {-3.40929f,-306.288f,33.34f,0}, - {-48.619f,-266.917f,47.8168f,0}, - {-62.9474f,-286.212f,66.7288f,0}, - {-5.05132f,-325.323f,38.8536f,0}, - {-64.2677f,-289.412f,33.469f,0}, -//horde - {-524.276f,-199.6f,82.8733f,-1.46608f}, - {-518.196f,-173.085f,102.43f,0}, - {-500.732f,-145.358f,88.5337f,2.44346f}, - {-501.084f,-150.784f,80.8506f,0}, - {-518.309f,-163.963f,102.521f,2.96706f}, - {-517.053f,-200.429f,80.759f,0}, - {-514.361f,-163.864f,104.163f,0}, - {-568.04f,-188.707f,81.55f,0}, - {-501.775f,-151.581f,81.2027f,0}, - {-509.975f,-191.652f,83.2978f,0}, - -//snowfall eyecandy - {-191.153f,-129.868f,78.5595f,-1.25664f }, - {-201.282f,-134.319f,78.6753f,-0.942478f }, - {-215.981f,-91.4101f,80.8702f,-1.74533f }, - {-200.465f,-96.418f,79.7587f,1.36136f }, - //mine supplies - //irondeep - {870.899f,-388.434f,61.6406f,-1.22173f}, - {825.214f,-320.174f,63.712f,-2.82743f}, - {837.117f,-452.556f,47.2331f,-3.12414f}, - {869.755f,-448.867f,52.5448f,-0.855212f}, - {949.877f,-458.198f,56.4874f,0.314159f}, - {900.35f,-479.024f,58.3553f,0.122173f}, - {854.449f,-442.255f,50.6589f,0.401426f}, - {886.685f,-442.358f,54.6962f,-1.22173f}, - {817.509f,-457.331f,48.4666f,2.07694f}, - {793.411f,-326.281f,63.1117f,-2.79253f}, - //coldtooth - {-934.212f,-57.3517f,80.277f,-0.0174535f}, - {-916.281f,-36.8579f,77.0227f,0.122173f}, - {-902.73f,-103.868f,75.4378f,-1.58825f}, - {-900.514f,-143.527f,75.9686f,1.8675f}, - {-862.882f,-0.353299f,72.1526f,-2.51327f}, - {-854.932f,-85.9184f,68.6056f,-2.04204f}, - {-851.833f,-118.959f,63.8672f,-0.0698131f}, - {-849.832f,-20.8421f,70.4672f,-1.81514f}, - {-844.25f,-60.0374f,72.1031f,-2.19912f}, - {-820.644f,-136.043f,63.1977f,2.40855f}, - {-947.642f,-208.807f,77.0101f,1.36136f}, - {-951.394f,-193.695f,67.634f,0.802851f} -}; - -const float BG_AV_DoorPositons[2][4] = { - {780.487f, -493.024f, 99.9553f, 3.0976f}, //alliance - {-1375.193f, -538.981f, 55.2824f, 0.72178f} //horde -}; - -//creaturestuff starts here -//is related to BG_AV_CreaturePos -enum BG_AV_CreaturePlace -{ - AV_CPLACE_SPIRIT_STORM_AID = 0, - AV_CPLACE_SPIRIT_STORM_GRAVE = 1, - AV_CPLACE_SPIRIT_STONE_GRAVE = 2, - AV_CPLACE_SPIRIT_SNOWFALL = 3, - AV_CPLACE_SPIRIT_ICE_GRAVE = 4, - AV_CPLACE_SPIRIT_FROSTWOLF = 5, - AV_CPLACE_SPIRIT_FROST_HUT = 6, - AV_CPLACE_SPIRIT_MAIN_ALLIANCE = 7, - AV_CPLACE_SPIRIT_MAIN_HORDE = 8, -//i don't will add for all 4 positions a variable.. i think one is enough to compute the rest - AV_CPLACE_DEFENSE_STORM_AID = 9, - AV_CPLACE_DEFEMSE_STORM_GRAVE = 13, - AV_CPLACE_DEFENSE_STONE_GRAVE = 17, - AV_CPLACE_DEFENSE_SNOWFALL = 21, - AV_CPLACE_DEFENSE_FROSTWOLF = 25, - AV_CPLACE_DEFENSE_ICE_GRAVE = 29, - AV_CPLACE_DEFENSE_FROST_HUT = 33, - - AV_CPLACE_DEFENSE_DUN_S = 37, - AV_CPLACE_DEFENSE_DUN_N = 41, - AV_CPLACE_DEFENSE_ICEWING = 45, - AV_CPLACE_DEFENSE_STONE_TOWER = 49, - AV_CPLACE_DEFENSE_ICE_TOWER = 53, - AV_CPLACE_DEFENSE_TOWERPOINT = 57, - AV_CPLACE_DEFENSE_FROST_E = 61, - AV_CPLACE_DEFENSE_FROST_t = 65, - - AV_CPLACE_A_MARSHAL_SOUTH = 69, - AV_CPLACE_A_MARSHAL_NORTH = 70, - AV_CPLACE_A_MARSHAL_ICE = 71, - AV_CPLACE_A_MARSHAL_STONE = 72, - AV_CPLACE_H_MARSHAL_ICE = 73, - AV_CPLACE_H_MARSHAL_TOWER = 74, - AV_CPLACE_H_MARSHAL_ETOWER = 75, - AV_CPLACE_H_MARSHAL_WTOWER = 76, - //irondeep - //miner: - AV_CPLACE_MINE_N_1_MIN = 77, - AV_CPLACE_MINE_N_1_MAX = 136, - //special types - AV_CPLACE_MINE_N_2_MIN = 137, - AV_CPLACE_MINE_N_2_MAX = 192, - //boss - AV_CPLACE_MINE_N_3 = 193, - //coldtooth - //miner: - AV_CPLACE_MINE_S_1_MIN = 194, - AV_CPLACE_MINE_S_1_MAX = 250, - //special types - AV_CPLACE_MINE_S_2_MIN = 251, - AV_CPLACE_MINE_S_2_MAX = 289, - //vermin - AV_CPLACE_MINE_S_S_MIN = 290, - AV_CPLACE_MINE_S_S_MAX = 299, - //boss - AV_CPLACE_MINE_S_3 = 300, - - //herald - AV_CPLACE_HERALD = 301, - - //node aura triggers - AV_CPLACE_TRIGGER01 = 302, - AV_CPLACE_TRIGGER02 = 303, - AV_CPLACE_TRIGGER03 = 304, - AV_CPLACE_TRIGGER04 = 305, - AV_CPLACE_TRIGGER05 = 306, - AV_CPLACE_TRIGGER06 = 307, - AV_CPLACE_TRIGGER07 = 308, - AV_CPLACE_TRIGGER08 = 309, - AV_CPLACE_TRIGGER09 = 310, - AV_CPLACE_TRIGGER10 = 311, - AV_CPLACE_TRIGGER11 = 312, - AV_CPLACE_TRIGGER12 = 313, - AV_CPLACE_TRIGGER13 = 314, - AV_CPLACE_TRIGGER14 = 315, - AV_CPLACE_TRIGGER15 = 316, - - //boss,captain triggers - AV_CPLACE_TRIGGER16 = 317, - AV_CPLACE_TRIGGER17 = 318, - AV_CPLACE_TRIGGER18 = 319, - AV_CPLACE_TRIGGER19 = 320, - - AV_CPLACE_MAX = 321 -}; - -//x, y, z, o -const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = { - //spiritguides - {643.000000f,44.000000f,69.740196f,-0.001854f}, - {676.000000f,-374.000000f,30.000000f,-0.001854f}, - {73.417755f,-496.433105f,48.731918f,-0.001854f}, - {-157.409195f,31.206272f,77.050598f,-0.001854f}, - {-531.217834f,-405.231384f,49.551376f,-0.001854f}, - {-1090.476807f,-253.308670f,57.672371f,-0.001854f}, - {-1496.065063f,-333.338409f,101.134804f,-0.001854f}, - {873.001770f,-491.283630f,96.541931f,-0.001854f}, - {-1437.670044f,-610.088989f,51.161900f,-0.001854f}, - //grave - //firstaid - {635.17f,-29.5594f,46.5056f,4.81711f}, - {642.488f,-32.9437f,46.365f,4.67748f}, - {642.326f,-27.9442f,46.9211f,4.59022f}, - {635.945f,-33.6171f,45.7164f,4.97419f}, - //stormpike - {669.272f,-297.304f,30.291f,4.66604f}, - {674.08f,-292.328f,30.4817f,0.0918785f}, - {667.01f,-288.532f,29.8809f,1.81583f}, - {664.153f,-294.042f,30.2851f,3.28531f}, - //stone - {81.7027f,-406.135f,47.7843f,0.598464f}, - {78.1431f,-409.215f,48.0401f,5.05953f}, - {73.4135f,-407.035f,46.7527f,3.34736f}, - {78.2258f,-401.859f,46.4202f,2.05852f}, - //snowfall - {-207.412f,-110.616f,78.7959f,2.43251f}, - {-197.95f,-112.205f,78.5686f,6.22441f}, - {-202.709f,-116.829f,78.4358f,5.13742f}, - {-202.059f,-108.314f,78.5783f,5.91968f}, - //ice - {-615.501f,-393.802f,60.4299f,3.06147f}, - {-608.513f,-392.717f,62.5724f,2.06323f}, - {-609.769f,-400.072f,60.7174f,5.22367f}, - {-616.093f,-398.293f,60.5628f,3.73613f}, - //frost - {-1077.7f,-340.21f,55.4682f,6.25569f}, - {-1082.74f,-333.821f,54.7962f,2.05459f}, - {-1090.66f,-341.267f,54.6768f,3.27746f}, - {-1081.58f,-344.63f,55.256f,4.75636f}, - //frost hut - {-1408.95f,-311.69f,89.2536f,4.49954f}, - {-1407.15f,-305.323f,89.1993f,2.86827f}, - {-1400.64f,-304.3f,89.7008f,1.0595f}, - {-1400.4f,-311.35f,89.3028f,4.99434f}, - //towers - //dun south - OK - {569.395f,-101.064f,52.8296f,2.34974f}, - {574.85f,-92.9842f,52.5869f,3.09325f}, - {575.411f,-83.597f,52.3626f,6.26573f}, - {571.352f,-75.6582f,52.479f,0.523599f}, - //dun north - OK - {668.60f,-122.53f,64.12f,2.34f}, //not 100% ok - {662.253f,-129.105f,64.1794f,2.77507f}, - {661.209f,-138.877f,64.2251f,3.38594f}, - {665.481f,-146.857f,64.1271f,3.75246f}, - //icewing - OK - {225.228f,-368.909f,56.9983f,6.23806f}, - {191.36f,-369.899f,57.1524f,3.24631f}, - {215.518f,-384.019f,56.9889f,5.09636f}, - {199.625f,-382.177f,56.8691f,4.08407f}, - //stone - {-172.851f,-452.366f,40.8725f,3.31829f}, - {-147.147f,-435.053f,40.8022f,0.599238f}, - {-169.456f,-440.325f,40.985f,2.59101f}, - {-163.494f,-434.904f,41.0725f,1.84174f}, - //ice - OK - {-573.522f,-271.854f,75.0078f,3.9619f}, - {-565.616f,-269.051f,74.9952f,5.02655f}, - {-562.825f,-261.087f,74.9898f,5.95157f}, - {-569.176f,-254.446f,74.8771f,0.820305f}, - //towerpoint - {-763.04f,-371.032f,90.7933f,5.25979f}, - {-759.764f,-358.264f,90.8681f,0.289795f}, - {-768.808f,-353.056f,90.8811f,1.52601f}, - {-775.944f,-362.639f,90.8949f,2.59573f}, - //frost etower - {-1294.13f,-313.045f,107.328f,0.270162f}, - {-1306.5f,-308.105f,113.767f,1.78755f}, - {-1294.78f,-319.966f,113.79f,5.94545f}, - {-1294.83f,-312.241f,113.799f,0.295293f}, - //frost wtower - {-1300.96f,-275.111f,114.058f,4.12804f}, - {-1302.41f,-259.256f,114.065f,1.67602f}, - {-1287.97f,-262.087f,114.165f,6.18264f}, - {-1291.59f,-271.166f,114.151f,5.28257f}, - - //alliance marshall - {721.104f,-7.64155f,50.7046f,3.45575f},// south - {723.058f,-14.1548f,50.7046f,3.40339f},// north - {715.691f,-4.72233f,50.2187f,3.47321f},// icewing - {720.046f,-19.9413f,50.2187f,3.36849f},// stone -//horde (coords not 100% ok) - {-1363.99f,-221.99f,98.4053f,4.93012f}, - {-1370.96f,-223.532f,98.4266f,4.93012f}, - {-1378.37f,-228.614f,99.3546f,5.38565f}, - {-1358.02f,-228.998f,98.868f,3.87768f}, - - //irondeep mine - //Irondeep Trogg - {971.671f,-442.657f,57.6951f,3.1765f}, - {969.979f,-457.148f,58.1119f,4.5204f}, - {958.692f,-333.477f,63.2276f,5.77704f}, - {957.113f,-325.92f,61.7589f,1.13446f}, - {948.25f,-448.268f,56.9009f,5.60251f}, - {934.727f,-385.802f,63.0344f,3.75246f}, - {931.751f,-403.458f,59.6737f,5.63741f}, - {931.146f,-359.666f,66.0294f,3.9619f}, - {929.702f,-412.401f,56.8776f,5.89921f}, - {926.849f,-379.074f,63.5286f,2.0944f}, - {921.972f,-358.597f,66.4313f,2.93215f}, - {921.449f,-341.981f,67.1264f,3.4383f}, - {921.1f,-395.812f,60.4615f,2.71695f}, - {919.274f,-394.986f,60.3478f,2.71696f}, - {916.852f,-393.891f,60.1726f,2.71695f}, - {914.568f,-326.21f,66.1733f,2.25147f}, - {913.064f,-395.773f,60.1364f,4.41568f}, - {909.246f,-474.576f,58.2067f,0.226893f}, - {909.246f,-474.576f,58.2901f,0.226893f}, - {907.209f,-428.267f,59.8065f,1.8675f}, - {905.973f,-459.528f,58.7594f,1.37189f}, - {905.067f,-396.074f,60.2085f,5.07891f}, - {901.809f,-457.709f,59.0116f,3.52557f}, - {900.962f,-427.44f,59.0842f,1.50098f}, - {897.929f,-471.742f,59.7729f,2.54818f}, - {893.376f,-343.171f,68.1499f,5.35816f}, - {890.584f,-406.049f,61.1925f,5.67232f}, - {888.208f,-332.564f,68.148f,1.93732f}, - {887.647f,-391.537f,61.8734f,1.37881f}, - {885.109f,-343.338f,67.0867f,3.78979f}, - {881.618f,-419.948f,53.5228f,0.593412f}, - {878.675f,-345.36f,66.1052f,3.45651f}, - {877.127f,-351.8f,66.5296f,5.74213f}, - {876.778f,-345.97f,65.7724f,3.45262f}, - {874.577f,-414.786f,52.7817f,1.67552f}, - {868.247f,-343.136f,64.9894f,1.6057f}, - {859.03f,-367.231f,47.4655f,0.0174533f}, - {857.513f,-351.817f,65.1867f,4.39823f}, - {852.632f,-372.416f,48.1657f,3.66519f}, - {849.86f,-340.944f,66.2447f,0.401426f}, - {847.99f,-386.287f,60.9277f,2.32374f}, - {847.601f,-423.072f,50.0852f,4.57276f}, - {847.135f,-411.307f,50.2106f,1.5708f}, - {835.077f,-379.418f,48.2755f,5.93412f}, - {834.87f,-453.304f,47.9075f,0.226893f}, - {834.634f,-365.981f,62.8801f,1.32645f}, - {834.354f,-355.526f,48.1491f,6.07375f}, - {833.702f,-327.506f,65.0439f,0.331613f}, - {833.151f,-374.228f,63.0938f,3.66519f}, - {831.711f,-346.785f,47.2975f,0.226893f}, - {827.874f,-413.624f,48.5818f,1.49241f}, - {827.728f,-415.483f,48.5593f,1.49238f}, - {827.016f,-424.543f,48.2856f,1.49236f}, - {823.222f,-334.283f,65.6306f,4.88692f}, - {821.892f,-464.723f,48.9451f,4.66003f}, - {821.006f,-387.635f,49.0728f,3.15905f}, - {817.26f,-447.432f,49.4308f,2.18166f}, - {805.399f,-320.146f,52.7712f,0.296706f}, - {801.405f,-328.055f,53.0195f,4.31096f}, - //irondeep skullthumber irondeep shaman - {955.812f,-440.302f,55.3411f,3.19395f}, - {937.378f,-377.816f,65.3919f,3.56047f}, - {925.059f,-331.347f,65.7564f,3.66519f}, - {922.918f,-396.634f,60.3942f,2.71695f}, - {909.99f,-462.154f,59.0811f,3.7001f}, - {907.893f,-388.787f,61.7923f,5.74213f}, - {898.801f,-437.105f,58.5266f,0.959931f}, - {884.237f,-407.597f,61.566f,0.820305f}, - {880.744f,-344.683f,66.4086f,3.4644f}, - {876.047f,-341.857f,65.8743f,4.45059f}, - {874.674f,-402.077f,61.7573f,0.26341f}, - {871.914f,-404.209f,62.1269f,6.06163f}, - {871.606f,-403.665f,62.0795f,0.765774f}, - {871.561f,-404.114f,62.1297f,0.00981727f}, - {871.528f,-404.248f,62.1455f,0.498032f}, - {871.493f,-404.122f,62.1331f,5.65727f}, - {871.282f,-403.843f,62.1108f,0.788382f}, - {868.294f,-392.395f,61.4772f,4.38685f}, - {868.256f,-392.363f,61.4803f,0.732738f}, - {867.804f,-392.51f,61.5089f,2.30167f}, - {867.612f,-392.371f,61.524f,2.86149f}, - {858.593f,-439.614f,50.2184f,0.872665f}, - {851.471f,-362.52f,47.314f,4.06662f}, - {846.939f,-347.279f,66.2876f,0.942478f}, - {842.08f,-421.775f,48.2659f,1.0821f}, - {838.358f,-371.212f,63.3299f,4.04916f}, - {827.57f,-417.483f,48.4538f,1.49237f}, - {827.012f,-457.397f,48.9331f,2.35619f}, - {825.535f,-322.373f,63.9357f,4.76475f}, - {867.635f,-443.605f,51.3347f,1.38626f}, - {957.293f,-455.039f,56.7395f,5.79449f}, - {950.077f,-326.672f,61.6552f,5.48033f}, - {936.692f,-356.78f,65.9835f,2.75762f}, - {926.475f,-419.345f,56.1833f,2.0944f}, - {924.729f,-397.453f,60.213f,2.71695f}, - {902.195f,-475.891f,58.312f,1.39626f}, - {897.464f,-338.758f,68.1715f,2.94961f}, - {884.237f,-407.597f,61.566f,0.820305f}, - {882.517f,-344.111f,66.7887f,3.46962f}, - {881.437f,-400.254f,61.2028f,0.263427f}, - {880.156f,-400.678f,61.3113f,3.41373f}, - {877.989f,-418.051f,52.9753f,4.46804f}, - {871.212f,-404.12f,62.1433f,3.6554f}, - {871.036f,-404.119f,62.2237f,4.50295f}, - {857.396f,-395.766f,61.263f,4.78684f}, - {857.276f,-395.395f,61.2418f,0.0845553f}, - {857.231f,-394.577f,61.2174f,1.96817f}, - {857.108f,-395.682f,61.2317f,4.87022f}, - {856.709f,-395.28f,61.1814f,2.54913f}, - {850.922f,-390.399f,60.8771f,2.85405f}, - {847.556f,-388.228f,60.9438f,2.56872f}, - {842.031f,-384.663f,61.6028f,2.56871f}, - {832.035f,-389.301f,47.5567f,2.11185f}, - {827.415f,-419.468f,48.3322f,1.49232f}, - {826.402f,-349.454f,47.2722f,1.51844f}, - {817.83f,-455.715f,48.4207f,0.925025f}, - {808.953f,-325.964f,52.4043f,3.01942f}, - // Morloch - {865.554f,-438.735f,50.7333f,2.12431f}, - //coldtooth mine - //miner/digger - {-917.648f,-46.8922f,77.0872f,5.27089f}, - {-912.689f,-45.4494f,76.2277f,4.60767f}, - {-905.455f,-84.5179f,75.3642f,3.29867f}, - {-904.332f,-111.509f,75.5925f,2.47837f}, - {-904.27f,-160.419f,61.9876f,3.61192f}, - {-904.023f,-90.4558f,75.3706f,3.40339f}, - {-978.678f,-37.3136f,75.8364f,2.84489f}, - {-973.076f,-36.5013f,77.5047f,1.0821f}, - {-963.951f,-87.734f,81.5555f,0.575959f}, - {-961.941f,-90.7252f,81.6629f,0.820305f}, - {-957.623f,-186.582f,66.6021f,1.95477f}, - {-952.476f,-179.778f,78.6771f,4.5204f}, - {-950.427f,-115.007f,79.6127f,3.68264f}, - {-950.25f,-151.95f,79.4598f,-1.81423f}, - {-950.169f,-188.099f,66.6184f,5.55015f}, - {-949.944f,-142.977f,80.5382f,2.70526f}, - {-947.854f,-170.5f,79.7618f,0.942478f}, - {-946.738f,-139.567f,80.0904f,2.3911f}, - {-945.503f,-65.0654f,79.7907f,5.02655f}, - {-943.678f,-110.986f,80.2557f,0.959931f}, - {-942.993f,-56.9881f,79.8915f,5.65487f}, - {-938.197f,-155.838f,61.3111f,1.65806f}, - {-930.488f,-214.524f,72.1431f,2.1236f}, - {-929.947f,-154.449f,61.5084f,1.67552f}, - {-927.412f,-135.313f,61.1987f,3.29867f}, - {-920.677f,-156.859f,62.8033f,3.15306f}, - {-916.75f,-136.094f,62.2357f,0.0698132f}, - {-915.319f,-132.718f,62.562f,1.16984f}, - {-913.589f,-146.794f,76.9366f,1.8675f}, - {-907.572f,-148.937f,76.6898f,4.76475f}, - {-902.02f,-64.6174f,73.9707f,1.19169f}, - {-899.489f,-61.7252f,73.2498f,5.09636f}, - {-894.792f,-127.141f,75.3834f,6.14356f}, - {-892.408f,-162.525f,64.1212f,2.69884f}, - {-892.326f,-123.158f,76.0318f,5.5676f}, - {-888.468f,-148.462f,61.8012f,1.65806f}, - {-883.268f,-159.738f,63.5311f,5.20108f}, - {-877.76f,-118.07f,65.215f,2.94961f}, - {-876.792f,-128.646f,64.1045f,3.40339f}, - {-874.901f,-36.6579f,69.4246f,2.00713f}, - {-874.856f,-151.351f,62.7537f,3.57875f}, - {-872.135f,-150.08f,62.7513f,3.57201f}, - {-870.288f,-149.217f,62.5413f,3.56624f}, - {-870.03f,-6.27443f,70.3867f,2.3911f}, - {-869.023f,-82.2118f,69.5848f,3.22886f}, - {-866.354f,-40.2455f,70.842f,0.0698132f}, - {-865.305f,-152.302f,63.5044f,4.86947f}, - {-861.926f,-79.0519f,71.4178f,0.20944f}, - {-857.292f,-152.277f,63.2114f,4.18879f}, - {-853.357f,-0.696194f,72.0655f,0.994838f}, - {-850.685f,-14.2596f,70.2298f,0.20944f}, - {-839.987f,-67.7695f,72.7916f,4.93928f}, - {-839.199f,-57.0558f,73.4891f,1.67552f}, - {-836.963f,-153.224f,63.3821f,4.46804f}, - {-832.721f,-67.7555f,72.9062f,4.99164f}, - {-821.496f,-143.095f,63.1292f,0.541052f}, - {-818.829f,-153.004f,62.1757f,6.12611f}, - //special - {-954.622f,-110.958f,80.7911f,6.24828f}, - {-951.477f,-53.9647f,80.0235f,5.32325f}, - {-946.812f,-126.04f,78.8601f,5.15265f}, - {-940.689f,-140.707f,79.9225f,2.79253f}, - {-933.954f,-159.632f,60.778f,2.56563f}, - {-922.537f,-130.291f,61.3756f,4.95674f}, - {-915.862f,-151.74f,76.9427f,0.942478f}, - {-888.321f,-159.831f,62.5303f,1.20428f}, - {-874.361f,-42.4751f,69.4316f,0.785398f}, - {-873.19f,-50.4899f,70.0568f,-2.41288f}, - {-868.511f,-148.386f,62.3547f,3.57875f}, - {-868.44f,-121.649f,64.5056f,3.33358f}, - {-868.324f,-77.7196f,71.4768f,5.41052f}, - {-859.846f,-19.6549f,70.7304f,1.97222f}, - {-828.05f,-150.508f,62.2019f,2.14675f}, - {-826.254f,-58.6911f,72.0041f,3.68264f}, - {-976.086f,-44.1775f,76.029f,1.46608f}, - {-971.864f,-87.4223f,81.4954f,5.8294f}, - {-966.551f,-74.1111f,80.0243f,4.2129f}, - {-958.509f,-173.652f,77.9013f,6.24828f}, - {-951.511f,-181.242f,65.529f,4.39823f}, - {-940.967f,-186.243f,77.698f,1.28164f}, - {-930.004f,-65.0898f,79.077f,0.0581657f}, - {-920.864f,-40.2009f,78.256f,5.16617f}, - {-919.089f,-148.021f,62.0317f,2.59327f}, - {-901.516f,-116.329f,75.6876f,0.471239f}, - {-897.864f,-84.4348f,74.083f,3.00197f}, - {-897.617f,-52.0457f,71.9503f,4.36332f}, - {-894.891f,-153.951f,61.6827f,3.23569f}, - {-893.933f,-111.625f,75.6591f,4.22536f}, - {-883.265f,-152.854f,61.8384f,0.0941087f}, - {-868.293f,-147.243f,62.1097f,3.2056f}, - {-867.501f,-11.8709f,70.018f,6.14356f}, - {-866.699f,-147.54f,62.1646f,3.57878f}, - {-866.566f,-91.1916f,67.4414f,4.56707f}, - {-857.272f,-141.142f,61.7356f,4.17134f}, - {-847.446f,-98.0061f,68.5131f,3.24631f}, - {-837.026f,-140.729f,62.5141f,5.51524f}, - {-824.204f,-65.053f,72.3381f,3.01942f}, - //vermin (s.th special for this mine) - {-951.955f,-197.5f,77.212f,5.63741f}, - {-944.837f,-199.608f,77.0737f,4.97419f}, - {-933.494f,-209.063f,73.7803f,5.88176f}, - {-929.666f,-201.308f,73.7032f,5.02655f}, - {-978.997f,-249.356f,65.4345f,5.05464f}, - {-974.565f,-224.828f,69.5858f,4.88846f}, - {-946.514f,-259.239f,66.0874f,3.78132f}, - {-918.402f,-250.439f,69.5271f,2.21352f}, - {-910.14f,-229.959f,72.9279f,0.27677f}, - {-851.563f,-88.6527f,68.5983f,3.61896f}, - //boss - {-848.902f,-92.931f,68.6325f,3.33350}, - //herald - {-48.459f,-288.802f,55.47f,1.0}, - //triggers - {637.083,-32.6603,45.9715,1.14353}, //firstaid_station - {669.007f,-294.078f,30.2909f,2.77507f}, //stormpike_grave - {77.8013f,-404.7f,46.7549f,-0.872665f}, //stoneheart_grave - {-202.581f,-112.73f,78.4876f,-0.715585f}, //snowfall_grave - {-611.962f,-396.17f,60.8351f,2.53682f}, //iceblood_grave - {-1082.45f,-346.823f,54.9219f,-1.53589f}, //frostwolf_grave - {-1402.21f,-307.431f,89.4424f,0.191986f}, //frostwolf_hut - {553.779f,-78.6566f,51.9378f,-1.22173f}, //dunbaldar_south - {674.001f,-143.125f,63.6615f,0.994838f}, //dunbaldar_north - {203.281f,-360.366f,56.3869f,-0.925024}, //icewing_bunker - {-152.437f,-441.758f,40.3982f,-1.95477f}, //stoneheart_bunker - {-571.88f,-262.777f,75.0087f,-0.802851f}, //iceblood_tower - {-768.907f,-363.71f,90.8949f,1.07991f}, //tower_point - {-1302.9f,-316.981f,113.867f,2.00713f}, //frostwolf_etower - {-1297.5f,-266.767f,114.15f,3.31044f}, //frostwolf_wtower - {-57.7891f,-286.597f,15.6479f,6.02139f}, //AV_NPC_A_CAPTAIN balinda - {722.43f,-10.9982f,50.7046f,3.42085f}, //AV_NPC_A_BOSS vanndar - {-545.23f,-165.35f,57.7886f,3.01145f}, //AV_NPC_H_CAPTAIN galvangar - {-1370.9f,-219.793f,98.4258f,5.04381f} //AV_NPC_H_BOSS drek thar -}; - -enum BG_AV_CreatureIds -{ - - AV_NPC_A_GRAVEDEFENSE0 = 0, // stormpike Defender - AV_NPC_A_GRAVEDEFENSE1 = 1, // seasoned defender - AV_NPC_A_GRAVEDEFENSE2 = 2, // veteran defender - AV_NPC_A_GRAVEDEFENSE3 = 3, // champion defender - AV_NPC_A_TOWERDEFENSE = 4, // stormpike bowman - AV_NPC_A_CAPTAIN = 5, // balinda - AV_NPC_A_BOSS = 6, // vanndar - - AV_NPC_H_GRAVEDEFENSE0 = 7, // frostwolf guardian - AV_NPC_H_GRAVEDEFENSE1 = 8, // seasoned guardian - AV_NPC_H_GRAVEDEFENSE2 = 9, // veteran guardian - AV_NPC_H_GRAVEDEFENSE3 = 10, // champion guardian - AV_NPC_H_TOWERDEFENSE = 11, // frostwolf bowman - AV_NPC_H_CAPTAIN = 12, // galvangar - AV_NPC_H_BOSS = 13, // drek thar - - AV_NPC_A_MARSHAL_SOUTH = 14, - AV_NPC_MARSHAL_NORTH = 15, - AV_NPC_A_MARSHAL_ICE = 16, - AV_NPC_A_MARSHAL_STONE = 17, - AV_NPC_H_MARSHAL_ICE = 18, - AV_NPC_H_MARSHAL_TOWER = 19, - AV_NPC_MARSHAL_ETOWER = 20, - AV_NPC_H_MARSHAL_WTOWER= 21, - AV_NPC_N_MINE_N_1 = 22, - AV_NPC_N_MINE_N_2 = 23, - AV_NPC_N_MINE_N_3 = 24, - AV_NPC_N_MINE_N_4 = 25, - AV_NPC_N_MINE_A_1 = 26, - AV_NPC_N_MINE_A_2 = 27, - AV_NPC_N_MINE_A_3 = 28, - AV_NPC_N_MINE_A_4 = 29, - AV_NPC_N_MINE_H_1 = 30, - AV_NPC_N_MINE_H_2 = 31, - AV_NPC_N_MINE_H_3 = 32, - AV_NPC_N_MINE_H_4 = 33, - AV_NPC_S_MINE_N_1 = 34, - AV_NPC_S_MINE_N_2 = 35, - AV_NPC_S_MINE_N_3 = 36, - AV_NPC_S_MINE_N_4 = 37, - AV_NPC_S_MINE_N_S = 38, - AV_NPC_S_MINE_A_1 = 39, - AV_NPC_S_MINE_A_2 = 40, - AV_NPC_S_MINE_A_3 = 41, - AV_NPC_S_MINE_A_4 = 42, - AV_NPC_S_MINE_H_1 = 43, - AV_NPC_S_MINE_H_2 = 44, - AV_NPC_S_MINE_H_3 = 45, - AV_NPC_S_MINE_H_4 = 46, - AV_NPC_HERALD = 47, - AV_NPC_INFO_MAX = 48 - -}; - -//entry, team, minlevel, maxlevel -//TODO this array should be removed, the only needed things are the entrys (for spawning(?) and handlekillunit) -const uint32 BG_AV_CreatureInfo[AV_NPC_INFO_MAX][4] = { - { 12050, 1216, 58, 58 }, //Stormpike Defender - { 13326, 1216, 59, 59 }, //Seasoned Defender - { 13331, 1216, 60, 60 }, //Veteran Defender - { 13422, 1216, 61, 61 }, //Champion Defender - { 13358, 1216, 59, 60 }, //Stormpike Bowman //i think its 60,61 and 69,70.. but this is until now not possible TODO look if this is ok - { 11949,469,0,0},//not spawned with this data, but used for handlekillunit - { 11948,469,0,0},//not spawned with this data, but used for handlekillunit - { 12053, 1214, 58, 58 }, //Frostwolf Guardian - { 13328, 1214, 59, 59 }, //Seasoned Guardian - { 13332, 1214, 60, 60 }, //Veteran Guardian - { 13421, 1214, 61, 61 }, //Champion Guardian - { 13359, 1214, 59, 60 }, //Frostwolf Bowman - { 11947,67,0,0}, //not spawned with this data, but used for handlekillunit - { 11946,67,0,0}, //not spawned with this data, but used for handlekillunit - { 14763, 1534, 60, 60 }, //Dun Baldar South Marshal - { 14762, 1534, 60, 60 }, //Dun Baldar North Marshal - { 14764, 1534, 60, 60 }, //Icewing Marshal - { 14765, 1534, 60, 60 }, //Stonehearth Marshal - - { 14773, 1214, 60, 60 }, //Iceblood Warmaster - { 14776, 1214, 60, 60 }, //Tower Point Warmaster - { 14772, 1214, 60, 60 }, //East Frostwolf Warmaster - { 14777, 1214, 60, 60 }, //West Frostwolf Warmaster - - { 10987, 59, 52, 53 }, //Irondeep Trogg - { 11600, 59, 53, 54 }, //Irondeep Shaman - { 11602, 59, 54, 55 }, //Irondeep Skullthumper - { 11657, 59, 58, 58 }, //Morloch - - {13396,469,52,53}, //irondeep alliance TODO: get the right ids - {13080,469,53,54}, - {13098,469,54,55}, - {13078,469,58,58}, - - {13397,67,52,53}, //irondeep horde - {13099,67,53,54}, - {13081,67,54,55}, - {13079,67,58,58}, - - { 11603, 59, 52, 53 }, //south mine neutral - { 11604, 59, 53, 54 }, - { 11605, 59, 54, 55 }, - { 11677, 59, 58, 58 }, - { 10982, 59, 52, 53 }, //vermin - - {13317,469,52,53}, //alliance - {13096,469,54,55}, //explorer - {13087,469,54,55}, //invader - {13086,469,58,58}, - - {13316,67,52,53}, //horde - {13097,67,54,55}, //surveypr - {13089,67,54,55}, //guard - {13088,67,58,58}, - {14848,67,58,58} //Herald - -}; - -//x,y,z,o,static_creature_info-id -const float BG_AV_StaticCreaturePos[AV_STATICCPLACE_MAX][5] = { //static creatures - {-1235.31f,-340.777f,60.5088f,3.31613f,0 },//2225 - Zora Guthrek - {-1244.02f,-323.795f,61.0485f,5.21853f,1 },//3343 - Grelkor - {-1235.16f,-332.302f,60.2985f,2.96706f,2 },//3625 - Rarck - {587.303f,-42.8257f,37.5615f,5.23599f,3 },//4255 - Brogus Thunderbrew - {643.635f,-58.3987f,41.7405f,4.72984f,4 },//4257 - Lana Thunderbrew - {591.464f,-44.452f,37.6166f,5.65487f,5 },//5134 - Jonivera Farmountain - {608.515f,-33.3935f,42.0003f,5.41052f,6 },//5135 - Svalbrad Farmountain - {617.656f,-32.0701f,42.7168f,4.06662f,7 },//5139 - Kurdrum Barleybeard - {-1183.76f,-268.295f,72.8233f,3.28122f,8 },//10364 - Yaelika Farclaw - {-1187.86f,-275.31f,73.0481f,3.63028f,9 },//10367 - Shrye Ragefist - {-1008.42f,-368.006f,55.3426f,5.95647f,10 },//10981 - Frostwolf - {-1091.92f,-424.28f,53.0139f,2.93958f,10 },//10981 - Frostwolf - {-558.455f,-198.768f,58.1755f,4.97946f,10 },//10981 - Frostwolf - {-861.247f,-312.51f,55.1427f,3.35382f,10 },//10981 - Frostwolf - {-1003.81f,-395.913f,50.4736f,2.85631f,10 },//10981 - Frostwolf - {-904.5f,-289.815f,65.1222f,5.7847f,10 },//10981 - Frostwolf - {-1064.41f,-438.839f,51.3614f,1.88857f,10 },//10981 - Frostwolf - {258.814f,76.2017f,18.6468f,6.19052f,11 },//10986 - Snowblind Harpy - {265.838f,-315.846f,-16.5429f,3.15917f,11 },//10986 - Snowblind Harpy - {426.485f,-51.1927f,-5.66286f,1.60347f,11 },//10986 - Snowblind Harpy - {452.044f,-33.9594f,-0.044651f,2.72815f,11 },//10986 - Snowblind Harpy - {266.032f,-315.639f,-16.5429f,4.67962f,11 },//10986 - Snowblind Harpy - {532.64f,-54.5863f,20.7024f,2.93215f,11 },//10986 - Snowblind Harpy - {295.183f,-299.908f,-34.6123f,0.135851f,12 },//10990 - Alterac Ram - {421.08f,-225.006f,-23.73f,0.166754f,12 },//10990 - Alterac Ram - {-55.7766f,-192.498f,20.4352f,6.12221f,12 },//10990 - Alterac Ram - {527.887f,-477.223f,62.3559f,0.170935f,12 },//10990 - Alterac Ram - {389.144f,-346.508f,-30.334f,4.14117f,12 },//10990 - Alterac Ram - {108.121f,-322.248f,37.5655f,4.46788f,12 },//10990 - Alterac Ram - {507.479f,-67.9403f,10.3571f,3.26304f,12 },//10990 - Alterac Ram - {329.071f,-185.016f,-29.1542f,0.356943f,12 },//10990 - Alterac Ram - {252.449f,-422.313f,35.1404f,4.53771f,12 },//10990 - Alterac Ram - {358.882f,-118.061f,-24.9119f,2.29257f,12 },//10990 - Alterac Ram - {487.151f,-174.229f,14.7558f,4.73192f,12 },//10990 - Alterac Ram - {449.652f,-123.561f,6.14273f,6.12029f,12 },//10990 - Alterac Ram - {272.419f,-261.802f,-41.8835f,3.66559f,12 },//10990 - Alterac Ram - {359.021f,-210.954f,-29.3483f,4.31339f,12 },//10990 - Alterac Ram - {450.598f,-318.048f,-37.7548f,0.655219f,12 },//10990 - Alterac Ram - {509.333f,-218.2f,3.05439f,3.66292f,12 },//10990 - Alterac Ram - {485.771f,-223.613f,-1.53f,2.04862f,12 },//10990 - Alterac Ram - {486.636f,-452.172f,39.6592f,2.3341f,12 },//10990 - Alterac Ram - {702.783f,-257.494f,25.9777f,1.68329f,12 },//10990 - Alterac Ram - {460.942f,-199.263f,-6.0149f,0.380506f,12 },//10990 - Alterac Ram - {483.108f,-115.307f,10.1056f,3.69701f,12 },//10990 - Alterac Ram - {471.601f,-154.174f,14.0702f,5.5807f,12 },//10990 - Alterac Ram - {213.938f,-420.793f,41.2549f,5.71394f,12 },//10990 - Alterac Ram - {289.387f,-294.685f,-33.9073f,0.555494f,12 },//10990 - Alterac Ram - {155.649f,-402.891f,43.3915f,5.94838f,12 },//10990 - Alterac Ram - {517.184f,-295.105f,-9.78195f,6.05668f,12 },//10990 - Alterac Ram - {102.334f,-332.165f,38.9812f,3.31445f,12 },//10990 - Alterac Ram - {320.244f,-107.793f,-42.6357f,-1.00311f,12 },//10990 - Alterac Ram - {217.976f,110.774f,15.7603f,4.56793f,13 },//11675 - Snowblind Windcaller - {269.872f,6.66684f,20.7592f,0.381212f,13 },//11675 - Snowblind Windcaller - {313.528f,-319.041f,-27.2373f,0.554098f,13 },//11675 - Snowblind Windcaller - {435.441f,-39.9289f,-0.169651f,0.549454f,13 },//11675 - Snowblind Windcaller - {315.115f,-317.62f,-29.1123f,0.90111f,13 },//11675 - Snowblind Windcaller - {428.091f,-122.731f,3.40332f,6.05901f,14 },//11678 - Snowblind Ambusher - {235.05f,85.5705f,18.3079f,-0.914255f,14 },//11678 - Snowblind Ambusher - {-1553.04f,-344.342f,64.4163f,6.09933f,15 },//11839 - Wildpaw Brute - {-545.23f,-165.35f,57.7886f,3.01145f,16 },//11947 - Captain Galvangar - {722.43f,-10.9982f,50.7046f,3.42085f,17 },//11948 - Vanndar Stormpike - {-57.7891f,-286.597f,15.6479f,6.02139f,18 },//11949 - Captain Balinda Stonehearth - {930.498f,-520.755f,93.7334f,1.8326f,19 },//11997 - Stormpike Herald - {-776.092f,-345.161f,67.4092f,1.89257f,20 },//12051 - Frostwolf Legionnaire - {-1224.63f,-308.144f,65.0087f,4.01139f,20 },//12051 - Frostwolf Legionnaire - {-713.039f,-442.515f,82.8638f,0.68724f,20 },//12051 - Frostwolf Legionnaire - {-711.783f,-444.061f,82.7039f,0.683494f,20 },//12051 - Frostwolf Legionnaire - {587.633f,-45.9816f,37.5438f,5.81195f,21 },//12096 - Stormpike Quartermaster - {-1293.79f,-194.407f,72.4398f,5.84685f,22 },//12097 - Frostwolf Quartermaster - {446.163f,-377.119f,-1.12725f,0.209526f,23 },//12127 - Stormpike Guardsman - {549.348f,-399.254f,53.3537f,3.24729f,23 },//12127 - Stormpike Guardsman - {549.801f,-401.217f,53.8305f,3.24729f,23 },//12127 - Stormpike Guardsman - {192.704f,-406.874f,42.9183f,6.10696f,23 },//12127 - Stormpike Guardsman - {441.305f,-435.765f,28.2385f,2.14472f,23 },//12127 - Stormpike Guardsman - {192.982f,-404.891f,43.0132f,6.1061f,23 },//12127 - Stormpike Guardsman - {355.342f,-391.989f,-0.486707f,3.00643f,23 },//12127 - Stormpike Guardsman - {446.035f,-375.104f,-1.12725f,0.21033f,23 },//12127 - Stormpike Guardsman - {697.864f,-433.238f,62.7914f,1.65776f,23 },//12127 - Stormpike Guardsman - {610.74f,-331.585f,30.8021f,5.14253f,23 },//12127 - Stormpike Guardsman - {609.815f,-329.775f,30.9271f,-2.38829f,23 },//12127 - Stormpike Guardsman - {695.874f,-433.434f,62.8543f,1.65776f,23 },//12127 - Stormpike Guardsman - {443.337f,-435.283f,28.6842f,2.13768f,23 },//12127 - Stormpike Guardsman - {-1251.5f,-316.327f,62.6565f,5.02655f,24 },//13176 - Smith Regzar - {-1332.0f,-331.243f,91.2631f,1.50098f,25 },//13179 - Wing Commander Guse - {569.983f,-94.9992f,38.0325f,1.39626f,26 },//13216 - Gaelden Hammersmith - {-1244.92f,-308.916f,63.2525f,1.62316f,27 },//13218 - Grunnda Wolfheart - {-1319.56f,-342.675f,60.3404f,1.20428f,28 },//13236 - Primalist Thurloga - {647.61f,-61.1548f,41.7405f,4.24115f,29 },//13257 - Murgot Deepforge - {-1321.64f,-343.73f,60.4833f,1.01229f,30 },//13284 - Frostwolf Shaman - {-1317.61f,-342.853f,60.3726f,2.47837f,30 },//13284 - Frostwolf Shaman - {-1319.31f,-344.475f,60.3825f,1.72788f,30 },//13284 - Frostwolf Shaman - {569.963f,-42.0218f,37.7581f,4.27606f,31 },//13438 - Wing Commander Slidore - {729.2f,-78.812f,51.6335f,3.97935f,32 },//13442 - Arch Druid Renferal - {729.118f,-82.8713f,51.6335f,2.53073f,33 },//13443 - Druid of the Grove - {725.554f,-79.4973f,51.6335f,5.27089f,33 },//13443 - Druid of the Grove - {724.768f,-84.1642f,51.6335f,0.733038f,33 },//13443 - Druid of the Grove - {596.68f,-83.0633f,39.0051f,6.24828f,34 },//13447 - Corporal Noreg Stormpike - {600.032f,-2.92475f,42.0788f,5.00909f,35 },//13577 - Stormpike Ram Rider Commander - {610.239f,-21.8454f,43.272f,4.90438f,36 },//13617 - Stormpike Stable Master - {613.422f,-150.764f,33.4517f,5.55015f,37 },//13797 - Mountaineer Boombellow - {-1213.91f,-370.619f,56.4455f,0.837758f,38 },//13798 - Jotek - {704.35f,-22.9071f,50.2187f,0.785398f,39 },//13816 - Prospector Stonehewer - {-1271.24f,-335.766f,62.3971f,5.75959f,40 },//14185 - Najak Hexxen - {-1268.64f,-332.688f,62.6171f,5.28835f,41 },//14186 - Ravak Grimtotem - {648.363f,-65.2233f,41.7405f,3.12414f,42 },//14187 - Athramanis - {648.238f,-67.8931f,41.7405f,2.60054f,43 },//14188 - Dirk Swindle - {-1223.44f,-309.833f,64.9331f,4.0131f,44 },//14282 - Frostwolf Bloodhound - {-1226.4f,-307.136f,64.9706f,4.0145f,44 },//14282 - Frostwolf Bloodhound - {356.001f,-389.969f,-0.438796f,3.0334f,45 },//14283 - Stormpike Owl - {355.835f,-394.005f,-0.60149f,3.02498f,45 },//14283 - Stormpike Owl - {882.266f,-496.378f,96.7707f,4.83248f,45 },//14283 - Stormpike Owl - {878.649f,-495.917f,96.6171f,4.67693f,45 },//14283 - Stormpike Owl - {932.851f,-511.017f,93.6748f,3.61004f,45 },//14283 - Stormpike Owl - {935.806f,-513.983f,93.7436f,3.61788f,45 },//14283 - Stormpike Owl - {947.412f,-509.982f,95.1098f,2.82743f,46 },//14284 - Stormpike Battleguard - {934.557f,-512.395f,93.662f,3.61004f,46 },//14284 - Stormpike Battleguard - {939.42f,-502.777f,94.5887f,5.14872f,46 },//14284 - Stormpike Battleguard - {854.276f,-494.241f,96.8017f,5.44543f,46 },//14284 - Stormpike Battleguard - {776.621f,-487.775f,99.4049f,3.50811f,46 },//14284 - Stormpike Battleguard - {880.169f,-495.699f,96.6204f,4.8325f,46 },//14284 - Stormpike Battleguard - {773.651f,-497.482f,99.0408f,2.11185f,46 },//14284 - Stormpike Battleguard - {949.1f,-506.913f,95.4237f,3.31613f,46 },//14284 - Stormpike Battleguard - {-1370.9f,-219.793f,98.4258f,5.04381f,47}, //drek thar - -}; - -const uint32 BG_AV_StaticCreatureInfo[51][4] = { - { 2225, 1215, 55, 55 }, //Zora Guthrek - { 3343, 1215, 55, 55 }, //Grelkor - { 3625, 1215, 55, 55 }, //Rarck - { 4255, 1217, 55, 55 }, //Brogus Thunderbrew - { 4257, 1217, 55, 55 }, //Lana Thunderbrew - { 5134, 1217, 55, 55 }, //Jonivera Farmountain - { 5135, 1217, 55, 55 }, //Svalbrad Farmountain - { 5139, 1217, 55, 55 }, //Kurdrum Barleybeard - { 10364, 1215, 55, 55 }, //Yaelika Farclaw - { 10367, 1215, 55, 55 }, //Shrye Ragefist - { 10981, 38, 50, 51 }, //Frostwolf - { 10986, 514, 52, 53 }, //Snowblind Harpy - { 10990, 1274, 50, 51 }, //Alterac Ram - { 11675, 514, 53, 53 }, //Snowblind Windcaller - { 11678, 14, 52, 53 }, //Snowblind Ambusher - { 11839, 39, 56, 56 }, //Wildpaw Brute - { 11947, 1214, 61, 61 }, //Captain Galvangar --TODO: doubled - { 11948, 1216, 63, 63 }, //Vanndar Stormpike - { 11949, 1216, 61, 61 }, //Captain Balinda Stonehearth - { 11997, 1334, 60, 60 }, //Stormpike Herald - { 12051, 1214, 57, 57 }, //Frostwolf Legionnaire - { 12096, 1217, 55, 55 }, //Stormpike Quartermaster - { 12097, 1215, 55, 55 }, //Frostwolf Quartermaster - { 12127, 1216, 57, 57 }, //Stormpike Guardsman - { 13176, 1215, 60, 60 }, //Smith Regzar - { 13179, 1215, 59, 59 }, //Wing Commander Guse - { 13216, 1217, 58, 58 }, //Gaelden Hammersmith - { 13218, 1215, 58, 58 }, //Grunnda Wolfheart - { 13236, 1214, 60, 60 }, //Primalist Thurloga - { 13257, 1216, 60, 60 }, //Murgot Deepforge - { 13284, 1214, 58, 58 }, //Frostwolf Shaman - { 13438, 1217, 58, 58 }, //Wing Commander Slidore - { 13442, 1216, 60, 60 }, //Arch Druid Renferal - { 13443, 1216, 60, 60 }, //Druid of the Grove - { 13447, 1216, 58, 58 }, //Corporal Noreg Stormpike - { 13577, 1216, 60, 60 }, //Stormpike Ram Rider Commander - { 13617, 1216, 60, 60 }, //Stormpike Stable Master - { 13797, 32, 60, 61 }, //Mountaineer Boombellow - { 13798, 1214, 60, 61 }, //Jotek - { 13816, 1216, 61, 61 }, //Prospector Stonehewer - { 14185, 877, 59, 59 }, //Najak Hexxen - { 14186, 105, 60, 60 }, //Ravak Grimtotem - { 14187, 1594, 60, 60 }, //Athramanis - { 14188, 57, 59, 59 }, //Dirk Swindle - { 14282, 1214, 53, 54 }, //Frostwolf Bloodhound - { 14283, 1216, 53, 54 }, //Stormpike Owl - { 14284, 1216, 61, 61 }, //Stormpike Battleguard - { 11946, 1214, 63, 63 }, //Drek'Thar //TODO: make the levels right (boss=0 maybe) - { 11948, 1216, 63, 63 }, //Vanndar Stormpike - { 11947, 1214, 61, 61 }, //Captain Galvangar - { 11949, 1216, 61, 61 } //Captain Balinda Stonehearth -}; - -enum BG_AV_Graveyards -{ - AV_GRAVE_STORM_AID = 751, - AV_GRAVE_STORM_GRAVE = 689, - AV_GRAVE_STONE_GRAVE = 729, - AV_GRAVE_SNOWFALL = 169, - AV_GRAVE_ICE_GRAVE = 749, - AV_GRAVE_FROSTWOLF = 690, - AV_GRAVE_FROST_HUT = 750, - AV_GRAVE_MAIN_ALLIANCE = 611, - AV_GRAVE_MAIN_HORDE = 610 -}; - -const uint32 BG_AV_GraveyardIds[9]= { - AV_GRAVE_STORM_AID, - AV_GRAVE_STORM_GRAVE, - AV_GRAVE_STONE_GRAVE, - AV_GRAVE_SNOWFALL, - AV_GRAVE_ICE_GRAVE, - AV_GRAVE_FROSTWOLF, - AV_GRAVE_FROST_HUT, - AV_GRAVE_MAIN_ALLIANCE, - AV_GRAVE_MAIN_HORDE -}; - -enum BG_AV_BUFF -{ //TODO add all other buffs here - AV_BUFF_ARMOR = 21163, - AV_BUFF_A_CAPTAIN = 23693, //the buff which the alliance captain does - AV_BUFF_H_CAPTAIN = 22751 //the buff which the horde captain does -}; -enum BG_AV_States -{ - POINT_NEUTRAL = 0, - POINT_ASSAULTED = 1, - POINT_DESTROYED = 2, - POINT_CONTROLED = 3 -}; - -enum BG_AV_WorldStates -{ - AV_Alliance_Score = 3127, - AV_Horde_Score = 3128, - AV_SHOW_H_SCORE = 3133, - AV_SHOW_A_SCORE = 3134, - -/* - //the comments behind the state shows which icon overlaps the other.. but is, until now, unused and maybe not a good solution (but give few performance (:) - -// Graves - - // Alliance - //Stormpike first aid station - AV_AID_A_C = 1325, - AV_AID_A_A = 1326, - AV_AID_H_C = 1327, - AV_AID_H_A = 1328, - //Stormpike Graveyard - AV_PIKEGRAVE_A_C = 1333, - AV_PIKEGRAVE_A_A = 1335, - AV_PIKEGRAVE_H_C = 1334, - AV_PIKEGRAVE_H_A = 1336, - //Stoneheart Grave - AV_STONEHEART_A_C = 1302, - AV_STONEHEART_A_A = 1304, //over hc - AV_STONEHEART_H_C = 1301, //over ac - AV_STONEHEART_H_A = 1303, //over aa - //Neutral - //Snowfall Grave -*/ - AV_SNOWFALL_N = 1966, //over aa -/* - AV_SNOWFALL_A_C = 1341, //over hc - AV_SNOWFALL_A_A = 1343, //over ha - AV_SNOWFALL_H_C = 1342, - AV_SNOWFALL_H_A = 1344, //over ac - //Horde - //Iceblood grave - AV_ICEBLOOD_A_C = 1346, //over hc - AV_ICEBLOOD_A_A = 1348, //over ac - AV_ICEBLOOD_H_C = 1347, - AV_ICEBLOOD_H_A = 1349, //over aa - //Frostwolf Grave - AV_FROSTWOLF_A_C = 1337, //over hc - AV_FROSTWOLF_A_A = 1339, //over ac - AV_FROSTWOLF_H_C = 1338, - AV_FROSTWOLF_H_A = 1340, //over aa - //Frostwolf Hut - AV_FROSTWOLFHUT_A_C = 1329, //over hc - AV_FROSTWOLFHUT_A_A = 1331, //over ha - AV_FROSTWOLFHUT_H_C = 1330, - AV_FROSTWOLFHUT_H_A = 1332, //over ac - -//Towers - //Alliance - //Dunbaldar South Bunker - AV_DUNS_CONTROLLED = 1361, - AV_DUNS_DESTROYED = 1370, - AV_DUNS_ASSAULTED = 1378, - //Dunbaldar North Bunker - AV_DUNN_CONTROLLED = 1362, - AV_DUNN_DESTROYED = 1371, - AV_DUNN_ASSAULTED = 1379, - //Icewing Bunker - AV_ICEWING_CONTROLLED = 1363, - AV_ICEWING_DESTROYED = 1372, - AV_ICEWING_ASSAULTED = 1380, - //Stoneheart Bunker - AV_STONEH_CONTROLLED = 1364, - AV_STONEH_DESTROYED = 1373, - AV_STONEH_ASSAULTED = 1381, - //Horde - //Iceblood Tower - AV_ICEBLOOD_CONTROLLED = 1385, - AV_ICEBLOOD_DESTROYED = 1368, - AV_ICEBLOOD_ASSAULTED = 1390, - //Tower Point - AV_TOWERPOINT_CONTROLLED = 1384, - AV_TOWERPOINT_DESTROYED = 1367, //goes over controlled - AV_TOWERPOINT_ASSAULTED = 1389, //goes over destroyed - //Frostwolf West - AV_FROSTWOLFW_CONTROLLED = 1382, - AV_FROSTWOLFW_DESTROYED = 1365, //over controlled - AV_FROSTWOLFW_ASSAULTED = 1387, //over destroyed - //Frostwolf East - AV_FROSTWOLFE_CONTROLLED = 1383, - AV_FROSTWOLFE_DESTROYED = 1366, - AV_FROSTWOLFE_ASSAULTED = 1388, - -//mines - - AV_N_MINE_N = 1360, - AV_N_MINE_A = 1358, - AV_N_MINE_H = 1359, - - AV_S_MINE_N = 1357, - AV_S_MINE_A = 1355, - AV_S_MINE_H = 1356, - -//towers assaulted by own team (unused) - AV_STONEH_UNUSED = 1377, - AV_ICEWING_UNUSED = 1376, - AV_DUNS_UNUSED = 1375, - AV_DUNN_UNUSED = 1374, - - AV_ICEBLOOD_UNUSED = 1395, - AV_TOWERPOINT_UNUSED = 1394, - AV_FROSTWOLFE_UNUSED = 1393, - AV_FROSTWOLFW_UNUSED = 1392 -*/ - -}; - -//alliance_control neutral_control horde_control -const uint32 BG_AV_MineWorldStates[2][3] = { - {1358, 1360,1359}, - {1355, 1357,1356} -}; - -//alliance_control alliance_assault h_control h_assault -const uint32 BG_AV_NodeWorldStates[16][4] = { - //Stormpike first aid station - {1325, 1326,1327,1328}, - //Stormpike Graveyard - {1333,1335,1334,1336}, - //Stoneheart Grave - {1302,1304,1301,1303}, - //Snowfall Grave - {1341,1343,1342,1344}, - //Iceblood grave - {1346,1348,1347,1349}, - //Frostwolf Grave - {1337,1339,1338,1340}, - //Frostwolf Hut - {1329,1331,1330,1332}, - //Dunbaldar South Bunker - {1361,1375,1370,1378}, - //Dunbaldar North Bunker - {1362,1374,1371,1379}, - //Icewing Bunker - {1363,1376,1372,1380}, - //Stoneheart Bunker - {1364,1377,1373,1381}, - //Iceblood Tower - {1368,1390,1385,1395}, - //Tower Point - {1367,1389,1384,1394}, - //Frostwolf East - {1366,1388,1383,1393}, - //Frostwolf West - {1365,1387,1382,1392}, -}; - -enum BG_AV_QuestIds -{ - AV_QUEST_A_SCRAPS1 = 7223, - AV_QUEST_A_SCRAPS2 = 6781, - AV_QUEST_H_SCRAPS1 = 7224, - AV_QUEST_H_SCRAPS2 = 6741, - AV_QUEST_A_COMMANDER1 = 6942, //soldier - AV_QUEST_H_COMMANDER1 = 6825, - AV_QUEST_A_COMMANDER2 = 6941, //leutnant - AV_QUEST_H_COMMANDER2 = 6826, - AV_QUEST_A_COMMANDER3 = 6943, //commander - AV_QUEST_H_COMMANDER3 = 6827, - AV_QUEST_A_BOSS1 = 7386, // 5 cristal/blood - AV_QUEST_H_BOSS1 = 7385, - AV_QUEST_A_BOSS2 = 6881, // 1 - AV_QUEST_H_BOSS2 = 6801, - AV_QUEST_A_NEAR_MINE = 5892, //the mine near start location of team - AV_QUEST_H_NEAR_MINE = 5893, - AV_QUEST_A_OTHER_MINE = 6982, //the other mine ;) - AV_QUEST_H_OTHER_MINE = 6985, - AV_QUEST_A_RIDER_HIDE = 7026, - AV_QUEST_H_RIDER_HIDE = 7002, - AV_QUEST_A_RIDER_TAME = 7027, - AV_QUEST_H_RIDER_TAME = 7001 -}; - -struct BG_AV_NodeInfo -{ - uint16 TotalOwner; - uint16 Owner; - uint16 PrevOwner; - BG_AV_States State; - BG_AV_States PrevState; - int Timer; - bool Tower; -}; - -inline BG_AV_Nodes &operator++(BG_AV_Nodes &i){ return i = BG_AV_Nodes(i + 1); } - -class BattleGroundAVScore : public BattleGroundScore -{ - public: - BattleGroundAVScore() : GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), TowersDefended(0), MinesCaptured(0), LeadersKilled(0), SecondaryObjectives(0) {}; - virtual ~BattleGroundAVScore() {}; - uint32 GraveyardsAssaulted; - uint32 GraveyardsDefended; - uint32 TowersAssaulted; - uint32 TowersDefended; - uint32 MinesCaptured; - uint32 LeadersKilled; - uint32 SecondaryObjectives; -}; - -class BattleGroundAV : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundAV(); - ~BattleGroundAV(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr,uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - virtual void ResetBGSubclass(); - - /*general stuff*/ - void UpdateScore(uint16 team, int16 points); - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - - /*handlestuff*/ //these are functions which get called from extern - virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj); - void HandleKillPlayer(Player* player, Player *killer); - void HandleKillUnit(Creature *unit, Player *killer); - void HandleQuestComplete(uint32 questid, Player *player); - bool PlayerCanDoMineQuest(int32 GOId,uint32 team); - - void EndBattleGround(uint32 winner); - - virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); - - private: - /* Nodes occupying */ - void EventPlayerAssaultsPoint(Player* player, uint32 object); - void EventPlayerDefendsPoint(Player* player, uint32 object); - void EventPlayerDestroyedPoint(BG_AV_Nodes node); - - void AssaultNode(BG_AV_Nodes node,uint16 team); - void DestroyNode(BG_AV_Nodes node); - void InitNode(BG_AV_Nodes node, uint16 team, bool tower); - void DefendNode(BG_AV_Nodes node, uint16 team); - - void PopulateNode(BG_AV_Nodes node); - void DePopulateNode(BG_AV_Nodes node); - - const BG_AV_Nodes GetNodeThroughObject(uint32 object); - const uint32 GetObjectThroughNode(BG_AV_Nodes node); - const char* GetNodeName(BG_AV_Nodes node); - const bool IsTower(BG_AV_Nodes node) { return m_Nodes[node].Tower; } - - /*mine*/ - void ChangeMineOwner(uint8 mine, uint32 team, bool initial=false); - - /*worldstates*/ - void FillInitialWorldStates(WorldPacket& data); - const uint8 GetWorldStateType(uint8 state, uint16 team); - void SendMineWorldStates(uint32 mine); - void UpdateNodeWorldState(BG_AV_Nodes node); - - /*general */ - Creature* AddAVCreature(uint16 cinfoid, uint16 type); - const uint16 GetBonusHonor(uint8 kills); //TODO remove this when the core handles this right - - /*variables */ - int32 m_Team_Scores[2]; - uint32 m_Team_QuestStatus[2][9]; //[x][y] x=team y=questcounter - - BG_AV_NodeInfo m_Nodes[BG_AV_NODES_MAX]; - - uint32 m_Mine_Owner[2]; - uint32 m_Mine_PrevOwner[2]; //only for worldstates needed - int32 m_Mine_Timer; //ticks for both teams - uint32 m_Mine_Reclaim_Timer[2]; - uint32 m_CaptainBuffTimer[2]; - bool m_CaptainAlive[2]; - - uint8 m_MaxLevel; //TODO remove this when battleground-getmaxlevel() returns something usefull - bool m_IsInformedNearVictory[2]; - -}; - -#endif - diff --git a/src/server/game/BattleGroundBE.cpp b/src/server/game/BattleGroundBE.cpp deleted file mode 100644 index d6debe45ae3..00000000000 --- a/src/server/game/BattleGroundBE.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundBE.h" -#include "Language.h" -#include "Object.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "WorldPacket.h" - -BattleGroundBE::BattleGroundBE() -{ - m_BgObjects.resize(BG_BE_OBJECT_MAX); - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set messageIds - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; -} - -BattleGroundBE::~BattleGroundBE() -{ - -} - -void BattleGroundBE::Update(uint32 diff) -{ - BattleGround::Update(diff); - - /*if (GetStatus() == STATUS_IN_PROGRESS) - { - // update something - }*/ -} - -void BattleGroundBE::StartingEventCloseDoors() -{ - for (uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - - for (uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; ++i) - SpawnBGObject(i, RESPAWN_ONE_DAY); -} - -void BattleGroundBE::StartingEventOpenDoors() -{ - for (uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_2; ++i) - DoorOpen(i); - - for (uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; ++i) - SpawnBGObject(i, 60); -} - -void BattleGroundBE::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundBEScore* sc = new BattleGroundBEScore; - - m_PlayerScores[plr->GetGUID()] = sc; - - UpdateArenaWorldState(); -} - -void BattleGroundBE::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) -{ - if (GetStatus() == STATUS_WAIT_LEAVE) - return; - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - if (!killer) - { - sLog.outError("Killer player not found"); - return; - } - - BattleGround::HandleKillPlayer(player,killer); - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -bool BattleGroundBE::HandlePlayerUnderMap(Player *player) -{ - player->TeleportTo(GetMapId(),6238.930176,262.963470,0.889519,player->GetOrientation(),false); - return true; -} - -void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - //uint32 SpellId = 0; - //uint64 buff_guid = 0; - switch(Trigger) - { - case 4538: // buff trigger? - //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_1]; - break; - case 4539: // buff trigger? - //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_2]; - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } - - //if (buff_guid) - // HandleTriggerBuff(buff_guid,Source); -} - -void BattleGroundBE::FillInitialWorldStates(WorldPacket &data) -{ - data << uint32(0x9f3) << uint32(1); // 9 - UpdateArenaWorldState(); -} - -void BattleGroundBE::Reset() -{ - //call parent's class reset - BattleGround::Reset(); -} - -bool BattleGroundBE::SetupBattleGround() -{ - // gates - if (!AddObject(BG_BE_OBJECT_DOOR_1, BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_BE_OBJECT_DOOR_2, BG_BE_OBJECT_TYPE_DOOR_2, 6189.546f, 241.7099f, 3.101481f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_BE_OBJECT_DOOR_3, BG_BE_OBJECT_TYPE_DOOR_3, 6299.116f, 296.5494f, 3.308032f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_BE_OBJECT_DOOR_4, BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) - // buffs - || !AddObject(BG_BE_OBJECT_BUFF_1, BG_BE_OBJECT_TYPE_BUFF_1, 6249.042f, 275.3239f, 11.22033f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120) - || !AddObject(BG_BE_OBJECT_BUFF_2, BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120)) - { - sLog.outErrorDb("BatteGroundBE: Failed to spawn some object!"); - return false; - } - - return true; -} - -void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - - BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); - if (itr == m_PlayerScores.end()) // player not found... - return; - - //there is nothing special in this score - BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); - -} - -/* -21:45:46 id:231310 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86 -0000: 32 02 00 00 76 0e 00 00 00 00 00 00 09 00 f3 09 | 2...v........... -0010: 00 00 01 00 00 00 f1 09 00 00 01 00 00 00 f0 09 | ................ -0020: 00 00 02 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................ -0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................ -0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................ -0050: 00 00 00 00 00 00 | ...... - -spell 32724 - Gold Team -spell 32725 - Green Team -35774 Gold Team -35775 Green Team -*/ diff --git a/src/server/game/BattleGroundBE.h b/src/server/game/BattleGroundBE.h deleted file mode 100644 index 760d4d278c9..00000000000 --- a/src/server/game/BattleGroundBE.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDBE_H -#define __BATTLEGROUNDBE_H - -class BattleGround; - -enum BattleGroundBEObjectTypes -{ - BG_BE_OBJECT_DOOR_1 = 0, - BG_BE_OBJECT_DOOR_2 = 1, - BG_BE_OBJECT_DOOR_3 = 2, - BG_BE_OBJECT_DOOR_4 = 3, - BG_BE_OBJECT_BUFF_1 = 4, - BG_BE_OBJECT_BUFF_2 = 5, - BG_BE_OBJECT_MAX = 6 -}; - -enum BattleGroundBEObjects -{ - BG_BE_OBJECT_TYPE_DOOR_1 = 183971, - BG_BE_OBJECT_TYPE_DOOR_2 = 183973, - BG_BE_OBJECT_TYPE_DOOR_3 = 183970, - BG_BE_OBJECT_TYPE_DOOR_4 = 183972, - BG_BE_OBJECT_TYPE_BUFF_1 = 184663, - BG_BE_OBJECT_TYPE_BUFF_2 = 184664 -}; - -class BattleGroundBEScore : public BattleGroundScore -{ - public: - BattleGroundBEScore() {}; - virtual ~BattleGroundBEScore() {}; -}; - -class BattleGroundBE : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundBE(); - ~BattleGroundBE(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - virtual void Reset(); - virtual void FillInitialWorldStates(WorldPacket &d); - void HandleKillPlayer(Player* player, Player *killer); - bool HandlePlayerUnderMap(Player * plr); - - /* Scorekeeping */ - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); -}; -#endif diff --git a/src/server/game/BattleGroundDS.cpp b/src/server/game/BattleGroundDS.cpp deleted file mode 100644 index 9036ef83f93..00000000000 --- a/src/server/game/BattleGroundDS.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundDS.h" -#include "Language.h" -#include "Player.h" -#include "Object.h" -#include "ObjectMgr.h" -#include "WorldPacket.h" - -BattleGroundDS::BattleGroundDS() -{ - m_BgObjects.resize(BG_DS_OBJECT_MAX); - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set messageIds - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; -} - -BattleGroundDS::~BattleGroundDS() -{ - -} - -void BattleGroundDS::Update(uint32 diff) -{ - BattleGround::Update(diff); - if (getWaterFallTimer() < diff) - { - if (isWaterFallActive()) - { - setWaterFallTimer(urand(BG_DS_WATERFALL_TIMER_MIN, BG_DS_WATERFALL_TIMER_MAX)); - for (uint32 i = BG_DS_OBJECT_WATER_1; i <= BG_DS_OBJECT_WATER_2; ++i) - SpawnBGObject(i, getWaterFallTimer()); - setWaterFallActive(false); - } - else - { - setWaterFallTimer(BG_DS_WATERFALL_DURATION); - for (uint32 i = BG_DS_OBJECT_WATER_1; i <= BG_DS_OBJECT_WATER_2; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - setWaterFallActive(true); - } - } - else - setWaterFallTimer(getWaterFallTimer() - diff); -} - -void BattleGroundDS::StartingEventCloseDoors() -{ - for (uint32 i = BG_DS_OBJECT_DOOR_1; i <= BG_DS_OBJECT_DOOR_2; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); -} - -void BattleGroundDS::StartingEventOpenDoors() -{ - for (uint32 i = BG_DS_OBJECT_DOOR_1; i <= BG_DS_OBJECT_DOOR_2; ++i) - DoorOpen(i); - - for (uint32 i = BG_DS_OBJECT_BUFF_1; i <= BG_DS_OBJECT_BUFF_2; ++i) - SpawnBGObject(i, 60); - - setWaterFallTimer(urand(BG_DS_WATERFALL_TIMER_MIN, BG_DS_WATERFALL_TIMER_MAX)); - setWaterFallActive(false); - - for (uint32 i = BG_DS_OBJECT_WATER_1; i <= BG_DS_OBJECT_WATER_2; ++i) - SpawnBGObject(i, getWaterFallTimer()); -} - -void BattleGroundDS::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundDSScore* sc = new BattleGroundDSScore; - - m_PlayerScores[plr->GetGUID()] = sc; - - UpdateArenaWorldState(); -} - -void BattleGroundDS::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) -{ - if (GetStatus() == STATUS_WAIT_LEAVE) - return; - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -void BattleGroundDS::HandleKillPlayer(Player* player, Player* killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - if (!killer) - { - sLog.outError("BattleGroundDS: Killer player not found"); - return; - } - - BattleGround::HandleKillPlayer(player,killer); - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -void BattleGroundDS::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - switch(Trigger) - { - case 5347: - case 5348: - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } -} - -bool BattleGroundDS::HandlePlayerUnderMap(Player *player) -{ - player->TeleportTo(GetMapId(), 1299.046, 784.825, 9.338, 2.422, false); - return true; -} - -void BattleGroundDS::FillInitialWorldStates(WorldPacket &data) -{ - data << uint32(3610) << uint32(1); // 9 show - UpdateArenaWorldState(); -} - -void BattleGroundDS::Reset() -{ - //call parent's class reset - BattleGround::Reset(); -} - - -bool BattleGroundDS::SetupBattleGround() -{ - // gates - if (!AddObject(BG_DS_OBJECT_DOOR_1, BG_DS_OBJECT_TYPE_DOOR_1, 1350.95, 817.2, 20.8096, 3.15, 0, 0, 0.99627, 0.0862864, RESPAWN_IMMEDIATELY) - || !AddObject(BG_DS_OBJECT_DOOR_2, BG_DS_OBJECT_TYPE_DOOR_2, 1232.65, 764.913, 20.0729, 6.3, 0, 0, 0.0310211, -0.999519, RESPAWN_IMMEDIATELY) - // water - || !AddObject(BG_DS_OBJECT_WATER_1, BG_DS_OBJECT_TYPE_WATER_1, 1291.56, 790.837, 7.1, 3.14238, 0, 0, 0.694215, -0.719768, 120) - || !AddObject(BG_DS_OBJECT_WATER_2, BG_DS_OBJECT_TYPE_WATER_2, 1291.56, 790.837, 7.1, 3.14238, 0, 0, 0.694215, -0.719768, 120) - // buffs - || !AddObject(BG_DS_OBJECT_BUFF_1, BG_DS_OBJECT_TYPE_BUFF_1, 1291.7, 813.424, 7.11472, 4.64562, 0, 0, 0.730314, -0.683111, 120) - || !AddObject(BG_DS_OBJECT_BUFF_2, BG_DS_OBJECT_TYPE_BUFF_2, 1291.7, 768.911, 7.11472, 1.55194, 0, 0, 0.700409, 0.713742, 120)) - { - sLog.outErrorDb("BatteGroundDS: Failed to spawn some object!"); - return false; - } - - return true; -} diff --git a/src/server/game/BattleGroundDS.h b/src/server/game/BattleGroundDS.h deleted file mode 100644 index 2ced5c88fd1..00000000000 --- a/src/server/game/BattleGroundDS.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDDS_H -#define __BATTLEGROUNDDS_H - -class BattleGround; - -enum BattleGroundDSObjectTypes -{ - BG_DS_OBJECT_DOOR_1 = 0, - BG_DS_OBJECT_DOOR_2 = 1, - BG_DS_OBJECT_WATER_1 = 2, - BG_DS_OBJECT_WATER_2 = 3, - BG_DS_OBJECT_BUFF_1 = 4, - BG_DS_OBJECT_BUFF_2 = 5, - BG_DS_OBJECT_MAX = 6 -}; - -enum BattleGroundDSObjects -{ - BG_DS_OBJECT_TYPE_DOOR_1 = 192642, - BG_DS_OBJECT_TYPE_DOOR_2 = 192643, - BG_DS_OBJECT_TYPE_WATER_1 = 194395, - BG_DS_OBJECT_TYPE_WATER_2 = 191877, - BG_DS_OBJECT_TYPE_BUFF_1 = 184663, - BG_DS_OBJECT_TYPE_BUFF_2 = 184664 -}; - -enum BattleGroundDSData -{ // These values are NOT blizzlike... need the correct data! - BG_DS_WATERFALL_TIMER_MIN = 30000, - BG_DS_WATERFALL_TIMER_MAX = 60000, - BG_DS_WATERFALL_DURATION = 10000, -}; - -class BattleGroundDSScore : public BattleGroundScore -{ - public: - BattleGroundDSScore() {}; - virtual ~BattleGroundDSScore() {}; - //TODO fix me -}; - -class BattleGroundDS : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundDS(); - ~BattleGroundDS(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - virtual void Reset(); - virtual void FillInitialWorldStates(WorldPacket &d); - void HandleKillPlayer(Player* player, Player *killer); - bool HandlePlayerUnderMap(Player * plr); - private: - uint32 m_waterTimer; - bool m_waterfallActive; - protected: - bool isWaterFallActive() { return m_waterfallActive; }; - void setWaterFallActive(bool active) { m_waterfallActive = active; }; - void setWaterFallTimer(uint32 timer) { m_waterTimer = timer; }; - uint32 getWaterFallTimer() { return m_waterTimer; }; -}; -#endif diff --git a/src/server/game/BattleGroundEY.cpp b/src/server/game/BattleGroundEY.cpp deleted file mode 100644 index 20f023e4c2a..00000000000 --- a/src/server/game/BattleGroundEY.cpp +++ /dev/null @@ -1,938 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ObjectMgr.h" -#include "World.h" -#include "WorldPacket.h" -#include "BattleGroundMgr.h" -#include "BattleGround.h" -#include "BattleGroundEY.h" -#include "Creature.h" -#include "Language.h" -#include "Object.h" -#include "Player.h" -#include "Util.h" - -// these variables aren't used outside of this file, so declare them only here -uint32 BG_EY_HonorScoreTicks[BG_HONOR_MODE_NUM] = { - 330, // normal honor - 200 // holiday -}; - -BattleGroundEY::BattleGroundEY() -{ - m_BuffChange = true; - m_BgObjects.resize(BG_EY_OBJECT_MAX); - m_BgCreatures.resize(BG_EY_CREATURES_MAX); - m_Points_Trigger[FEL_REALVER] = TR_FEL_REALVER_BUFF; - m_Points_Trigger[BLOOD_ELF] = TR_BLOOD_ELF_BUFF; - m_Points_Trigger[DRAENEI_RUINS] = TR_DRAENEI_RUINS_BUFF; - m_Points_Trigger[MAGE_TOWER] = TR_MAGE_TOWER_BUFF; - - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_EY_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_EY_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_EY_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_EY_HAS_BEGUN; -} - -BattleGroundEY::~BattleGroundEY() -{ -} - -void BattleGroundEY::Update(uint32 diff) -{ - BattleGround::Update(diff); - - if (GetStatus() == STATUS_IN_PROGRESS) - { - m_PointAddingTimer -= diff; - if (m_PointAddingTimer <= 0) - { - m_PointAddingTimer = BG_EY_FPOINTS_TICK_TIME; - if (m_TeamPointsCount[BG_TEAM_ALLIANCE] > 0) - AddPoints(ALLIANCE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_ALLIANCE] - 1]); - if (m_TeamPointsCount[BG_TEAM_HORDE] > 0) - AddPoints(HORDE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_HORDE] - 1]); - } - - if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN || m_FlagState == BG_EY_FLAG_STATE_ON_GROUND) - { - m_FlagsTimer -= diff; - - if (m_FlagsTimer < 0) - { - m_FlagsTimer = 0; - if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN) - RespawnFlag(true); - else - RespawnFlagAfterDrop(); - } - } - - m_TowerCapCheckTimer -= diff; - if (m_TowerCapCheckTimer <= 0) - { - //check if player joined point - /*I used this order of calls, because although we will check if one player is in gameobject's distance 2 times - but we can count of players on current point in CheckSomeoneLeftPoint - */ - this->CheckSomeoneJoinedPoint(); - //check if player left point - this->CheckSomeoneLeftPoint(); - this->UpdatePointStatuses(); - m_TowerCapCheckTimer = BG_EY_FPOINTS_TICK_TIME; - } - } -} - -void BattleGroundEY::StartingEventCloseDoors() -{ - SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY); - - for (uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i) - SpawnBGObject(i, RESPAWN_ONE_DAY); -} - -void BattleGroundEY::StartingEventOpenDoors() -{ - SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_ONE_DAY); - SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_ONE_DAY); - - for (uint32 i = BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER; i <= BG_EY_OBJECT_FLAG_NETHERSTORM; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - for (uint32 i = 0; i < EY_POINTS_MAX; ++i) - { - //randomly spawn buff - uint8 buff = urand(0, 2); - SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY); - } -} - -void BattleGroundEY::AddPoints(uint32 Team, uint32 Points) -{ - BattleGroundTeamId team_index = GetTeamIndexByTeamId(Team); - m_TeamScores[team_index] += Points; - m_HonorScoreTics[team_index] += Points; - if (m_HonorScoreTics[team_index] >= m_HonorTics) - { - RewardHonorToTeam(GetBonusHonorFromKill(1), Team); - m_HonorScoreTics[team_index] -= m_HonorTics; - } - UpdateTeamScore(Team); -} - -void BattleGroundEY::CheckSomeoneJoinedPoint() -{ - GameObject *obj = NULL; - for (uint8 i = 0; i < EY_POINTS_MAX; ++i) - { - obj = HashMapHolder::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); - if (obj) - { - uint8 j = 0; - while (j < m_PlayersNearPoint[EY_POINTS_MAX].size()) - { - Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]); - if (!plr) - { - sLog.outError("BattleGroundEY:CheckSomeoneJoinedPoint: Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[EY_POINTS_MAX][j])); - ++j; - continue; - } - if (plr->CanCaptureTowerPoint() && plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) - { - //player joined point! - //show progress bar - UpdateWorldStateForPlayer(PROGRESS_BAR_PERCENT_GREY, BG_EY_PROGRESS_BAR_PERCENT_GREY, plr); - UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[i], plr); - UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_SHOW, plr); - //add player to point - m_PlayersNearPoint[i].push_back(m_PlayersNearPoint[EY_POINTS_MAX][j]); - //remove player from "free space" - m_PlayersNearPoint[EY_POINTS_MAX].erase(m_PlayersNearPoint[EY_POINTS_MAX].begin() + j); - } - else - ++j; - } - } - } -} - -void BattleGroundEY::CheckSomeoneLeftPoint() -{ - //reset current point counts - for (uint8 i = 0; i < 2*EY_POINTS_MAX; ++i) - m_CurrentPointPlayersCount[i] = 0; - GameObject *obj = NULL; - for (uint8 i = 0; i < EY_POINTS_MAX; ++i) - { - obj = HashMapHolder::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); - if (obj) - { - uint8 j = 0; - while (j < m_PlayersNearPoint[i].size()) - { - Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[i][j]); - if (!plr) - { - sLog.outError("BattleGroundEY:CheckSomeoneLeftPoint Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[i][j])); - //move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug - m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); - m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); - ++j; - continue; - } - if (!plr->CanCaptureTowerPoint() || !plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) - //move player out of point (add him to players that are out of points - { - m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); - m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); - this->UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_DONT_SHOW, plr); - } - else - { - //player is neat flag, so update count: - m_CurrentPointPlayersCount[2 * i + GetTeamIndexByTeamId(plr->GetTeam())]++; - ++j; - } - } - } - } -} - -void BattleGroundEY::UpdatePointStatuses() -{ - for (uint8 point = 0; point < EY_POINTS_MAX; ++point) - { - if (m_PlayersNearPoint[point].empty()) - continue; - //count new point bar status: - m_PointBarStatus[point] += (m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] < BG_EY_POINT_MAX_CAPTURERS_COUNT) ? m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] : BG_EY_POINT_MAX_CAPTURERS_COUNT; - - if (m_PointBarStatus[point] > BG_EY_PROGRESS_BAR_ALI_CONTROLLED) - //point is fully alliance's - m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_ALI_CONTROLLED; - if (m_PointBarStatus[point] < BG_EY_PROGRESS_BAR_HORDE_CONTROLLED) - //point is fully horde's - m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_HORDE_CONTROLLED; - - uint32 pointOwnerTeamId = 0; - //find which team should own this point - if (m_PointBarStatus[point] <= BG_EY_PROGRESS_BAR_NEUTRAL_LOW) - pointOwnerTeamId = HORDE; - else if (m_PointBarStatus[point] >= BG_EY_PROGRESS_BAR_NEUTRAL_HIGH) - pointOwnerTeamId = ALLIANCE; - else - pointOwnerTeamId = EY_POINT_NO_OWNER; - - for (uint8 i = 0; i < m_PlayersNearPoint[point].size(); ++i) - { - Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[point][i]); - if (plr) - { - this->UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[point], plr); - //if point owner changed we must evoke event! - if (pointOwnerTeamId != m_PointOwnedByTeam[point]) - { - //point was uncontrolled and player is from team which captured point - if (m_PointState[point] == EY_POINT_STATE_UNCONTROLLED && plr->GetTeam() == pointOwnerTeamId) - this->EventTeamCapturedPoint(plr, point); - - //point was under control and player isn't from team which controlled it - if (m_PointState[point] == EY_POINT_UNDER_CONTROL && plr->GetTeam() != m_PointOwnedByTeam[point]) - this->EventTeamLostPoint(plr, point); - } - } - } - } -} - -void BattleGroundEY::UpdateTeamScore(uint32 Team) -{ - uint32 score = GetTeamScore(Team); - //TODO there should be some sound played when one team is near victory!! - and define variables - /*if (!m_IsInformedNearVictory && score >= BG_EY_WARNING_NEAR_VICTORY_SCORE) - { - if (Team == ALLIANCE) - SendMessageToAll(LANG_BG_EY_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); - else - SendMessageToAll(LANG_BG_EY_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); - PlaySoundToAll(BG_EY_SOUND_NEAR_VICTORY); - m_IsInformedNearVictory = true; - }*/ - - if (score >= BG_EY_MAX_TEAM_SCORE) - { - score = BG_EY_MAX_TEAM_SCORE; - EndBattleGround(Team); - } - - if (Team == ALLIANCE) - UpdateWorldState(EY_ALLIANCE_RESOURCES, score); - else - UpdateWorldState(EY_HORDE_RESOURCES, score); -} - -void BattleGroundEY::EndBattleGround(uint32 winner) -{ - //win reward - if (winner == ALLIANCE) - RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); - if (winner == HORDE) - RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); - //complete map reward - RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); - RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); - - BattleGround::EndBattleGround(winner); -} - -void BattleGroundEY::UpdatePointsCount(uint32 Team) -{ - if (Team == ALLIANCE) - UpdateWorldState(EY_ALLIANCE_BASE, m_TeamPointsCount[BG_TEAM_ALLIANCE]); - else - UpdateWorldState(EY_HORDE_BASE, m_TeamPointsCount[BG_TEAM_HORDE]); -} - -void BattleGroundEY::UpdatePointsIcons(uint32 Team, uint32 Point) -{ - //we MUST firstly send 0, after that we can send 1!!! - if (m_PointState[Point] == EY_POINT_UNDER_CONTROL) - { - UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 0); - if (Team == ALLIANCE) - UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 1); - else - UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 1); - } - else - { - if (Team == ALLIANCE) - UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 0); - else - UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 0); - UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 1); - } -} - -void BattleGroundEY::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map - BattleGroundEYScore* sc = new BattleGroundEYScore; - - m_PlayersNearPoint[EY_POINTS_MAX].push_back(plr->GetGUID()); - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid) -{ - // sometimes flag aura not removed :( - for (int j = EY_POINTS_MAX; j >= 0; --j) - { - for (size_t i = 0; i < m_PlayersNearPoint[j].size(); ++i) - if (m_PlayersNearPoint[j][i] == guid) - m_PlayersNearPoint[j].erase(m_PlayersNearPoint[j].begin() + i); - } - if (IsFlagPickedup()) - { - if (m_FlagKeeper == guid) - { - if (plr) - EventPlayerDroppedFlag(plr); - else - { - SetFlagPicker(0); - RespawnFlag(true); - } - } - } -} - -void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - if (!Source->isAlive()) //hack code, must be removed later - return; - - switch(Trigger) - { - case TR_BLOOD_ELF_POINT: - if (m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == Source->GetTeam()) - if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) - EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_BLOOD_ELF); - break; - case TR_FEL_REALVER_POINT: - if (m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[FEL_REALVER] == Source->GetTeam()) - if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) - EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_FEL_REALVER); - break; - case TR_MAGE_TOWER_POINT: - if (m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[MAGE_TOWER] == Source->GetTeam()) - if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) - EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_MAGE_TOWER); - break; - case TR_DRAENEI_RUINS_POINT: - if (m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[DRAENEI_RUINS] == Source->GetTeam()) - if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) - EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_DRAENEI_RUINS); - break; - case 4512: - case 4515: - case 4517: - case 4519: - case 4530: - case 4531: - case 4568: - case 4569: - case 4570: - case 4571: - case 5866: - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } -} - -bool BattleGroundEY::SetupBattleGround() -{ - // doors - if (!AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_EY_OBJECT_DOOR_H, BG_OBJECT_H_DOOR_EY_ENTRY, 1803.21f, 1539.49f, 1261.09f, 3.14159f, 0.173648f, 0, 0.984808f, 0, RESPAWN_IMMEDIATELY) - // banners (alliance) - || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) - // banners (horde) - || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) - // banners (natural) - || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) - // flags - || !AddObject(BG_EY_OBJECT_FLAG_NETHERSTORM, BG_OBJECT_FLAG2_EY_ENTRY, 2174.782227f, 1569.054688f, 1160.361938f, -1.448624f, 0, 0, 0.662620f, -0.748956f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_FLAG_FEL_REALVER, BG_OBJECT_FLAG1_EY_ENTRY, 2044.28f, 1729.68f, 1189.96f, -0.017453f, 0, 0, 0.008727f, -0.999962f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_FLAG_BLOOD_ELF, BG_OBJECT_FLAG1_EY_ENTRY, 2048.83f, 1393.65f, 1194.49f, 0.20944f, 0, 0, 0.104528f, 0.994522f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_FLAG_DRAENEI_RUINS, BG_OBJECT_FLAG1_EY_ENTRY, 2286.56f, 1402.36f, 1197.11f, 3.72381f, 0, 0, 0.957926f, -0.287016f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_FLAG_MAGE_TOWER, BG_OBJECT_FLAG1_EY_ENTRY, 2284.48f, 1731.23f, 1189.99f, 2.89725f, 0, 0, 0.992546f, 0.121869f, RESPAWN_ONE_DAY) - // tower cap - || !AddObject(BG_EY_OBJECT_TOWER_CAP_FEL_REALVER, BG_OBJECT_FR_TOWER_CAP_EY_ENTRY, 2024.600708f, 1742.819580f, 1195.157715f, 2.443461f, 0, 0, 0.939693f, 0.342020f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF, BG_OBJECT_BE_TOWER_CAP_EY_ENTRY, 2050.493164f, 1372.235962f, 1194.563477f, 1.710423f, 0, 0, 0.754710f, 0.656059f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS, BG_OBJECT_DR_TOWER_CAP_EY_ENTRY, 2301.010498f, 1386.931641f, 1197.183472f, 1.570796f, 0, 0, 0.707107f, 0.707107f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER, BG_OBJECT_HU_TOWER_CAP_EY_ENTRY, 2282.121582f, 1760.006958f, 1189.707153f, 1.919862f, 0, 0, 0.819152f, 0.573576f, RESPAWN_ONE_DAY) -) - { - sLog.outErrorDb("BatteGroundEY: Failed to spawn some object BattleGround not created!"); - return false; - } - - //buffs - for (int i = 0; i < EY_POINTS_MAX; ++i) - { - AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(m_Points_Trigger[i]); - if (!at) - { - sLog.outError("BattleGroundEY: Unknown trigger: %u", m_Points_Trigger[i]); - continue; - } - if (!AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3, Buff_Entries[0], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) - || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) -) - sLog.outError("BattleGroundEY: Cannot spawn buff"); - } - - WorldSafeLocsEntry const *sg = NULL; - sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); - if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) - { - sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); - return false; - } - - sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE); - if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) - { - sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); - return false; - } - - return true; -} - -void BattleGroundEY::Reset() -{ - //call parent's class reset - BattleGround::Reset(); - - m_TeamScores[BG_TEAM_ALLIANCE] = 0; - m_TeamScores[BG_TEAM_HORDE] = 0; - m_TeamPointsCount[BG_TEAM_ALLIANCE] = 0; - m_TeamPointsCount[BG_TEAM_HORDE] = 0; - m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0; - m_HonorScoreTics[BG_TEAM_HORDE] = 0; - m_FlagState = BG_EY_FLAG_STATE_ON_BASE; - m_FlagCapturedBgObjectType = 0; - m_FlagKeeper = 0; - m_DroppedFlagGUID = 0; - m_PointAddingTimer = 0; - m_TowerCapCheckTimer = 0; - bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID()); - m_HonorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks; - - for (uint8 i = 0; i < EY_POINTS_MAX; ++i) - { - m_PointOwnedByTeam[i] = EY_POINT_NO_OWNER; - m_PointState[i] = EY_POINT_STATE_UNCONTROLLED; - m_PointBarStatus[i] = BG_EY_PROGRESS_BAR_STATE_MIDDLE; - m_PlayersNearPoint[i].clear(); - m_PlayersNearPoint[i].reserve(15); //tip size - } - m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].clear(); - m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].reserve(30); -} - -void BattleGroundEY::RespawnFlag(bool send_message) -{ - if (m_FlagCapturedBgObjectType > 0) - SpawnBGObject(m_FlagCapturedBgObjectType, RESPAWN_ONE_DAY); - - m_FlagCapturedBgObjectType = 0; - m_FlagState = BG_EY_FLAG_STATE_ON_BASE; - SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_IMMEDIATELY); - - if (send_message) - { - SendMessageToAll(LANG_BG_EY_RESETED_FLAG, CHAT_MSG_BG_SYSTEM_NEUTRAL); - PlaySoundToAll(BG_EY_SOUND_FLAG_RESET); // flags respawned sound... - } - - UpdateWorldState(NETHERSTORM_FLAG, 1); -} - -void BattleGroundEY::RespawnFlagAfterDrop() -{ - RespawnFlag(true); - - GameObject *obj = HashMapHolder::Find(GetDroppedFlagGUID()); - if (obj) - obj->Delete(); - else - sLog.outError("BattleGroundEY: Unknown dropped flag guid: %u",GUID_LOPART(GetDroppedFlagGUID())); - - SetDroppedFlagGUID(0); -} - -void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - BattleGround::HandleKillPlayer(player, killer); - EventPlayerDroppedFlag(player); -} - -void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - { - // if not running, do not cast things at the dropper player, neither send unnecessary messages - // just take off the aura - if (IsFlagPickedup() && GetFlagPickerGUID() == Source->GetGUID()) - { - SetFlagPicker(0); - Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); - } - return; - } - - if (!IsFlagPickedup()) - return; - - if (GetFlagPickerGUID() != Source->GetGUID()) - return; - - SetFlagPicker(0); - Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); - m_FlagState = BG_EY_FLAG_STATE_ON_GROUND; - m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; - Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); - Source->CastSpell(Source, BG_EY_PLAYER_DROPPED_FLAG_SPELL, true); - //this does not work correctly :((it should remove flag carrier name) - UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_WAIT_RESPAWN); - UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN); - - if (Source->GetTeam() == ALLIANCE) - SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL); - else - SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL); -} - -void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) -{ - if (GetStatus() != STATUS_IN_PROGRESS || IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10)) - return; - - if (Source->GetTeam() == ALLIANCE) - { - UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER); - PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE); - } - else - { - UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER); - PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_HORDE); - } - - if (m_FlagState == BG_EY_FLAG_STATE_ON_BASE) - UpdateWorldState(NETHERSTORM_FLAG, 0); - m_FlagState = BG_EY_FLAG_STATE_ON_PLAYER; - - SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_ONE_DAY); - SetFlagPicker(Source->GetGUID()); - //get flag aura on player - Source->CastSpell(Source, BG_EY_NETHERSTORM_FLAG_SPELL, true); - Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - - if (Source->GetTeam() == ALLIANCE) - PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, Source->GetName()); - else - PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL, Source->GetName()); -} - -void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - //Natural point - uint32 Team = m_PointOwnedByTeam[Point]; - - if (!Team) - return; - - if (Team == ALLIANCE) - { - m_TeamPointsCount[BG_TEAM_ALLIANCE]--; - SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance, RESPAWN_ONE_DAY); - SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 1, RESPAWN_ONE_DAY); - SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 2, RESPAWN_ONE_DAY); - } - else - { - m_TeamPointsCount[BG_TEAM_HORDE]--; - SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde, RESPAWN_ONE_DAY); - SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 1, RESPAWN_ONE_DAY); - SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 2, RESPAWN_ONE_DAY); - } - - SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType, RESPAWN_IMMEDIATELY); - SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 1, RESPAWN_IMMEDIATELY); - SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 2, RESPAWN_IMMEDIATELY); - - //buff isn't despawned - - m_PointOwnedByTeam[Point] = EY_POINT_NO_OWNER; - m_PointState[Point] = EY_POINT_NO_OWNER; - - if (Team == ALLIANCE) - SendMessageToAll(m_LoosingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); - else - SendMessageToAll(m_LoosingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source); - - UpdatePointsIcons(Team, Point); - UpdatePointsCount(Team); - - //remove bonus honor aura trigger creature when node is lost - if (Point < EY_POINTS_MAX) - DelCreature(Point + 6);//NULL checks are in DelCreature! 0-5 spirit guides -} - -void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - uint32 Team = Source->GetTeam(); - - SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType, RESPAWN_ONE_DAY); - SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 1, RESPAWN_ONE_DAY); - SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 2, RESPAWN_ONE_DAY); - - if (Team == ALLIANCE) - { - m_TeamPointsCount[BG_TEAM_ALLIANCE]++; - SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance, RESPAWN_IMMEDIATELY); - SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 1, RESPAWN_IMMEDIATELY); - SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 2, RESPAWN_IMMEDIATELY); - } - else - { - m_TeamPointsCount[BG_TEAM_HORDE]++; - SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde, RESPAWN_IMMEDIATELY); - SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 1, RESPAWN_IMMEDIATELY); - SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 2, RESPAWN_IMMEDIATELY); - } - - //buff isn't respawned - - m_PointOwnedByTeam[Point] = Team; - m_PointState[Point] = EY_POINT_UNDER_CONTROL; - - if (Team == ALLIANCE) - SendMessageToAll(m_CapturingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); - else - SendMessageToAll(m_CapturingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source); - - if (m_BgCreatures[Point]) - DelCreature(Point); - - WorldSafeLocsEntry const *sg = NULL; - sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId); - if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, Team)) - sLog.outError("BatteGroundEY: Failed to spawn spirit guide! point: %u, team: %u, graveyard_id: %u", - Point, Team, m_CapturingPointTypes[Point].GraveYardId); - -// SpawnBGCreature(Point,RESPAWN_IMMEDIATELY); - - UpdatePointsIcons(Team, Point); - UpdatePointsCount(Team); - - if (Point >= EY_POINTS_MAX) - return; - - Creature* trigger = GetBGCreature(Point + 6);//0-5 spirit guides - if (!trigger) - trigger = AddCreature(WORLD_TRIGGER,Point+6,Team,BG_EY_TriggerPositions[Point][0],BG_EY_TriggerPositions[Point][1],BG_EY_TriggerPositions[Point][2],BG_EY_TriggerPositions[Point][3]); - - //add bonus honor aura trigger creature when node is accupied - //cast bonus aura (+50% honor in 25yards) - //aura should only apply to players who have accupied the node, set correct faction for trigger - if (trigger) - { - trigger->setFaction(Team == ALLIANCE ? 84 : 83); - trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); - } -} - -void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType) -{ - if (GetStatus() != STATUS_IN_PROGRESS || GetFlagPickerGUID() != Source->GetGUID()) - return; - - SetFlagPicker(0); - m_FlagState = BG_EY_FLAG_STATE_WAIT_RESPAWN; - Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); - - Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - - if (Source->GetTeam() == ALLIANCE) - PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE); - else - PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE); - - SpawnBGObject(BgObjectType, RESPAWN_IMMEDIATELY); - - m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; - m_FlagCapturedBgObjectType = BgObjectType; - - uint8 team_id = 0; - if (Source->GetTeam() == ALLIANCE) - { - team_id = BG_TEAM_ALLIANCE; - SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_A, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); - } - else - { - team_id = BG_TEAM_HORDE; - SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_H, CHAT_MSG_BG_SYSTEM_HORDE, Source); - } - - if (m_TeamPointsCount[team_id] > 0) - AddPoints(Source->GetTeam(), BG_EY_FlagPoints[m_TeamPointsCount[team_id] - 1]); - - UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); -} - -void BattleGroundEY::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) -{ - BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); - if (itr == m_PlayerScores.end()) // player not found - return; - - switch(type) - { - case SCORE_FLAG_CAPTURES: // flags captured - ((BattleGroundEYScore*)itr->second)->FlagCaptures += value; - break; - default: - BattleGround::UpdatePlayerScore(Source, type, value, doAddHonor); - break; - } -} - -void BattleGroundEY::FillInitialWorldStates(WorldPacket& data) -{ - data << uint32(EY_HORDE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_HORDE]); - data << uint32(EY_ALLIANCE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_ALLIANCE]); - data << uint32(0xab6) << uint32(0x0); - data << uint32(0xab5) << uint32(0x0); - data << uint32(0xab4) << uint32(0x0); - data << uint32(0xab3) << uint32(0x0); - data << uint32(0xab2) << uint32(0x0); - data << uint32(0xab1) << uint32(0x0); - data << uint32(0xab0) << uint32(0x0); - data << uint32(0xaaf) << uint32(0x0); - - data << uint32(DRAENEI_RUINS_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == HORDE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL); - - data << uint32(DRAENEI_RUINS_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == ALLIANCE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL); - - data << uint32(DRAENEI_RUINS_UNCONTROL) << uint32(m_PointState[DRAENEI_RUINS] != EY_POINT_UNDER_CONTROL); - - data << uint32(MAGE_TOWER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == ALLIANCE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL); - - data << uint32(MAGE_TOWER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == HORDE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL); - - data << uint32(MAGE_TOWER_UNCONTROL) << uint32(m_PointState[MAGE_TOWER] != EY_POINT_UNDER_CONTROL); - - data << uint32(FEL_REAVER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == HORDE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL); - - data << uint32(FEL_REAVER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == ALLIANCE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL); - - data << uint32(FEL_REAVER_UNCONTROL) << uint32(m_PointState[FEL_REALVER] != EY_POINT_UNDER_CONTROL); - - data << uint32(BLOOD_ELF_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == HORDE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL); - - data << uint32(BLOOD_ELF_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == ALLIANCE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL); - - data << uint32(BLOOD_ELF_UNCONTROL) << uint32(m_PointState[BLOOD_ELF] != EY_POINT_UNDER_CONTROL); - - data << uint32(NETHERSTORM_FLAG) << uint32(m_FlagState == BG_EY_FLAG_STATE_ON_BASE); - - data << uint32(0xad2) << uint32(0x1); - data << uint32(0xad1) << uint32(0x1); - data << uint32(0xabe) << uint32(GetTeamScore(HORDE)); - data << uint32(0xabd) << uint32(GetTeamScore(ALLIANCE)); - data << uint32(0xa05) << uint32(0x8e); - data << uint32(0xaa0) << uint32(0x0); - data << uint32(0xa9f) << uint32(0x0); - data << uint32(0xa9e) << uint32(0x0); - data << uint32(0xc0d) << uint32(0x17b); -} - -WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player) -{ - uint32 g_id = 0; - - switch(player->GetTeam()) - { - case ALLIANCE: g_id = EY_GRAVEYARD_MAIN_ALLIANCE; break; - case HORDE: g_id = EY_GRAVEYARD_MAIN_HORDE; break; - default: return NULL; - } - - float distance, nearestDistance; - - WorldSafeLocsEntry const* entry = NULL; - WorldSafeLocsEntry const* nearestEntry = NULL; - entry = sWorldSafeLocsStore.LookupEntry(g_id); - nearestEntry = entry; - - if (!entry) - { - sLog.outError("BattleGroundEY: Not found the main team graveyard. Graveyard system isn't working!"); - return NULL; - } - - float plr_x = player->GetPositionX(); - float plr_y = player->GetPositionY(); - float plr_z = player->GetPositionZ(); - - distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); - nearestDistance = distance; - - for (uint8 i = 0; i < EY_POINTS_MAX; ++i) - { - if (m_PointOwnedByTeam[i] == player->GetTeam() && m_PointState[i] == EY_POINT_UNDER_CONTROL) - { - entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId); - if (!entry) - sLog.outError("BattleGroundEY: Not found graveyard: %u",m_CapturingPointTypes[i].GraveYardId); - else - { - distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); - if (distance < nearestDistance) - { - nearestDistance = distance; - nearestEntry = entry; - } - } - } - } - - return nearestEntry; -} - -bool BattleGroundEY::IsAllNodesConrolledByTeam(uint32 team) const -{ - uint32 count = 0; - for (int i = 0; i < EY_POINTS_MAX; ++i) - if (m_PointOwnedByTeam[i] == team && m_PointState[i] == EY_POINT_UNDER_CONTROL) - ++count; - - return count == EY_POINTS_MAX; -} diff --git a/src/server/game/BattleGroundEY.h b/src/server/game/BattleGroundEY.h deleted file mode 100644 index 4fe23c4c821..00000000000 --- a/src/server/game/BattleGroundEY.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDEY_H -#define __BATTLEGROUNDEY_H - -#include "Language.h" - -class BattleGround; - -#define BG_EY_FLAG_RESPAWN_TIME (8*IN_MILISECONDS) //8 seconds -#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds - -enum BG_EY_WorldStates -{ - EY_ALLIANCE_RESOURCES = 2749, - EY_HORDE_RESOURCES = 2750, - EY_ALLIANCE_BASE = 2752, - EY_HORDE_BASE = 2753, - DRAENEI_RUINS_HORDE_CONTROL = 2733, - DRAENEI_RUINS_ALLIANCE_CONTROL = 2732, - DRAENEI_RUINS_UNCONTROL = 2731, - MAGE_TOWER_ALLIANCE_CONTROL = 2730, - MAGE_TOWER_HORDE_CONTROL = 2729, - MAGE_TOWER_UNCONTROL = 2728, - FEL_REAVER_HORDE_CONTROL = 2727, - FEL_REAVER_ALLIANCE_CONTROL = 2726, - FEL_REAVER_UNCONTROL = 2725, - BLOOD_ELF_HORDE_CONTROL = 2724, - BLOOD_ELF_ALLIANCE_CONTROL = 2723, - BLOOD_ELF_UNCONTROL = 2722, - PROGRESS_BAR_PERCENT_GREY = 2720, //100 = empty (only grey), 0 = blue|red (no grey) - PROGRESS_BAR_STATUS = 2719, //50 init!, 48 ... hordak bere .. 33 .. 0 = full 100% hordacky , 100 = full alliance - PROGRESS_BAR_SHOW = 2718, //1 init, 0 druhy send - bez messagu, 1 = controlled aliance - NETHERSTORM_FLAG = 2757, - //set to 2 when flag is picked up, and to 1 if it is dropped - NETHERSTORM_FLAG_STATE_ALLIANCE = 2769, - NETHERSTORM_FLAG_STATE_HORDE = 2770 -}; - -enum BG_EY_ProgressBarConsts -{ - BG_EY_POINT_MAX_CAPTURERS_COUNT = 5, - BG_EY_POINT_RADIUS = 70, - BG_EY_PROGRESS_BAR_DONT_SHOW = 0, - BG_EY_PROGRESS_BAR_SHOW = 1, - BG_EY_PROGRESS_BAR_PERCENT_GREY = 40, - BG_EY_PROGRESS_BAR_STATE_MIDDLE = 50, - BG_EY_PROGRESS_BAR_HORDE_CONTROLLED = 0, - BG_EY_PROGRESS_BAR_NEUTRAL_LOW = 30, - BG_EY_PROGRESS_BAR_NEUTRAL_HIGH = 70, - BG_EY_PROGRESS_BAR_ALI_CONTROLLED = 100 -}; - -enum BG_EY_Sounds -{ - //strange ids, but sure about them - BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, - BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, - BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, - BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, - BG_EY_SOUND_FLAG_RESET = 8192 -}; - -enum BG_EY_Spells -{ - BG_EY_NETHERSTORM_FLAG_SPELL = 34976, - BG_EY_PLAYER_DROPPED_FLAG_SPELL = 34991 -}; - -enum EYBattleGroundObjectEntry -{ - BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door - BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door - BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) - BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) - BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) - BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) - BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) - BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) - BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt - BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt - BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt - BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt -}; - -enum EYBattleGroundPointsTrigger -{ - TR_BLOOD_ELF_POINT = 4476, - TR_FEL_REALVER_POINT = 4514, - TR_MAGE_TOWER_POINT = 4516, - TR_DRAENEI_RUINS_POINT = 4518, - TR_BLOOD_ELF_BUFF = 4568, - TR_FEL_REALVER_BUFF = 4569, - TR_MAGE_TOWER_BUFF = 4570, - TR_DRAENEI_RUINS_BUFF = 4571 -}; - -enum EYBattleGroundGaveyards -{ - EY_GRAVEYARD_MAIN_ALLIANCE = 1103, - EY_GRAVEYARD_MAIN_HORDE = 1104, - EY_GRAVEYARD_FEL_REALVER = 1105, - EY_GRAVEYARD_BLOOD_ELF = 1106, - EY_GRAVEYARD_DRAENEI_RUINS = 1107, - EY_GRAVEYARD_MAGE_TOWER = 1108 -}; - -enum EYBattleGroundPoints -{ - FEL_REALVER = 0, - BLOOD_ELF = 1, - DRAENEI_RUINS = 2, - MAGE_TOWER = 3, - - EY_PLAYERS_OUT_OF_POINTS = 4, - EY_POINTS_MAX = 4 -}; - -enum EYBattleGroundCreaturesTypes -{ - EY_SPIRIT_FEL_REALVER = 0, - EY_SPIRIT_BLOOD_ELF = 1, - EY_SPIRIT_DRAENEI_RUINS = 2, - EY_SPIRIT_MAGE_TOWER = 3, - EY_SPIRIT_MAIN_ALLIANCE = 4, - EY_SPIRIT_MAIN_HORDE = 5, - - EY_TRIGGER_FEL_REALVER = 6, - EY_TRIGGER_BLOOD_ELF = 7, - EY_TRIGGER_DRAENEI_RUINS = 8, - EY_TRIGGER_MAGE_TOWER = 9, - - BG_EY_CREATURES_MAX = 10 -}; - -enum EYBattleGroundObjectTypes -{ - BG_EY_OBJECT_DOOR_A = 0, - BG_EY_OBJECT_DOOR_H = 1, - BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER = 2, - BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT = 3, - BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT = 4, - BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER = 5, - BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT = 6, - BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT = 7, - BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER = 8, - BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT = 9, - BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT = 10, - BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER = 11, - BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT = 12, - BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT = 13, - BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER = 14, - BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT = 15, - BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT = 16, - BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER = 17, - BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT = 18, - BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT = 19, - BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER = 20, - BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT = 21, - BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT = 22, - BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER = 23, - BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT = 24, - BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT = 25, - BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER = 26, - BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT = 27, - BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT = 28, - BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER = 29, - BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT = 30, - BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT = 31, - BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER = 32, - BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT = 33, - BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT = 34, - BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER = 35, - BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT = 36, - BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT = 37, - BG_EY_OBJECT_TOWER_CAP_FEL_REALVER = 38, - BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF = 39, - BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS = 40, - BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER = 41, - BG_EY_OBJECT_FLAG_NETHERSTORM = 42, - BG_EY_OBJECT_FLAG_FEL_REALVER = 43, - BG_EY_OBJECT_FLAG_BLOOD_ELF = 44, - BG_EY_OBJECT_FLAG_DRAENEI_RUINS = 45, - BG_EY_OBJECT_FLAG_MAGE_TOWER = 46, - //buffs - BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER = 47, - BG_EY_OBJECT_REGENBUFF_FEL_REALVER = 48, - BG_EY_OBJECT_BERSERKBUFF_FEL_REALVER = 49, - BG_EY_OBJECT_SPEEDBUFF_BLOOD_ELF = 50, - BG_EY_OBJECT_REGENBUFF_BLOOD_ELF = 51, - BG_EY_OBJECT_BERSERKBUFF_BLOOD_ELF = 52, - BG_EY_OBJECT_SPEEDBUFF_DRAENEI_RUINS = 53, - BG_EY_OBJECT_REGENBUFF_DRAENEI_RUINS = 54, - BG_EY_OBJECT_BERSERKBUFF_DRAENEI_RUINS = 55, - BG_EY_OBJECT_SPEEDBUFF_MAGE_TOWER = 56, - BG_EY_OBJECT_REGENBUFF_MAGE_TOWER = 57, - BG_EY_OBJECT_BERSERKBUFF_MAGE_TOWER = 58, - BG_EY_OBJECT_MAX = 59 -}; - -#define BG_EY_NotEYWeekendHonorTicks 330 -#define BG_EY_EYWeekendHonorTicks 200 - -enum BG_EY_Score -{ - BG_EY_WARNING_NEAR_VICTORY_SCORE = 1400, - BG_EY_MAX_TEAM_SCORE = 1600 -}; - -enum BG_EY_FlagState -{ - BG_EY_FLAG_STATE_ON_BASE = 0, - BG_EY_FLAG_STATE_WAIT_RESPAWN = 1, - BG_EY_FLAG_STATE_ON_PLAYER = 2, - BG_EY_FLAG_STATE_ON_GROUND = 3 -}; - -enum EYBattleGroundPointState -{ - EY_POINT_NO_OWNER = 0, - EY_POINT_STATE_UNCONTROLLED = 0, - EY_POINT_UNDER_CONTROL = 3 -}; - -struct BattleGroundEYPointIconsStruct -{ - BattleGroundEYPointIconsStruct(uint32 _WorldStateControlIndex, uint32 _WorldStateAllianceControlledIndex, uint32 _WorldStateHordeControlledIndex) - : WorldStateControlIndex(_WorldStateControlIndex), WorldStateAllianceControlledIndex(_WorldStateAllianceControlledIndex), WorldStateHordeControlledIndex(_WorldStateHordeControlledIndex) {} - uint32 WorldStateControlIndex; - uint32 WorldStateAllianceControlledIndex; - uint32 WorldStateHordeControlledIndex; -}; - -// x, y, z, o -const float BG_EY_TriggerPositions[EY_POINTS_MAX][4] = { - {2044.28f, 1729.68f, 1189.96f, -0.017453f}, // FEL_REALVER center - {2048.83f, 1393.65f, 1194.49f, 0.20944f}, // BLOOD_ELF center - {2286.56f, 1402.36f, 1197.11f, 3.72381f}, // DRAENEI_RUINS center - {2284.48f, 1731.23f, 1189.99f, 2.89725f} // MAGE_TOWER center -}; - -struct BattleGroundEYLoosingPointStruct -{ - BattleGroundEYLoosingPointStruct(uint32 _SpawnNeutralObjectType, uint32 _DespawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _DespawnObjectTypeHorde, uint32 _MessageIdHorde) - : SpawnNeutralObjectType(_SpawnNeutralObjectType), - DespawnObjectTypeAlliance(_DespawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), - DespawnObjectTypeHorde(_DespawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde) - {} - - uint32 SpawnNeutralObjectType; - uint32 DespawnObjectTypeAlliance; - uint32 MessageIdAlliance; - uint32 DespawnObjectTypeHorde; - uint32 MessageIdHorde; -}; - -struct BattleGroundEYCapturingPointStruct -{ - BattleGroundEYCapturingPointStruct(uint32 _DespawnNeutralObjectType, uint32 _SpawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _SpawnObjectTypeHorde, uint32 _MessageIdHorde, uint32 _GraveYardId) - : DespawnNeutralObjectType(_DespawnNeutralObjectType), - SpawnObjectTypeAlliance(_SpawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), - SpawnObjectTypeHorde(_SpawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde), - GraveYardId(_GraveYardId) - {} - - uint32 DespawnNeutralObjectType; - uint32 SpawnObjectTypeAlliance; - uint32 MessageIdAlliance; - uint32 SpawnObjectTypeHorde; - uint32 MessageIdHorde; - uint32 GraveYardId; -}; - -const uint8 BG_EY_TickPoints[EY_POINTS_MAX] = {1, 2, 5, 10}; -const uint32 BG_EY_FlagPoints[EY_POINTS_MAX] = {75, 85, 100, 500}; - -//constant arrays: -const BattleGroundEYPointIconsStruct m_PointsIconStruct[EY_POINTS_MAX] = -{ - BattleGroundEYPointIconsStruct(FEL_REAVER_UNCONTROL, FEL_REAVER_ALLIANCE_CONTROL, FEL_REAVER_HORDE_CONTROL), - BattleGroundEYPointIconsStruct(BLOOD_ELF_UNCONTROL, BLOOD_ELF_ALLIANCE_CONTROL, BLOOD_ELF_HORDE_CONTROL), - BattleGroundEYPointIconsStruct(DRAENEI_RUINS_UNCONTROL, DRAENEI_RUINS_ALLIANCE_CONTROL, DRAENEI_RUINS_HORDE_CONTROL), - BattleGroundEYPointIconsStruct(MAGE_TOWER_UNCONTROL, MAGE_TOWER_ALLIANCE_CONTROL, MAGE_TOWER_HORDE_CONTROL) -}; -const BattleGroundEYLoosingPointStruct m_LoosingPointTypes[EY_POINTS_MAX] = -{ - BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_H_F_RUINS), - BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_H_B_TOWER), - BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_H_D_RUINS), - BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_H_M_TOWER) -}; -const BattleGroundEYCapturingPointStruct m_CapturingPointTypes[EY_POINTS_MAX] = -{ - BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_H_F_RUINS, EY_GRAVEYARD_FEL_REALVER), - BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_H_B_TOWER, EY_GRAVEYARD_BLOOD_ELF), - BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_H_D_RUINS, EY_GRAVEYARD_DRAENEI_RUINS), - BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_H_M_TOWER, EY_GRAVEYARD_MAGE_TOWER) -}; - -class BattleGroundEYScore : public BattleGroundScore -{ - public: - BattleGroundEYScore () : FlagCaptures(0) {}; - virtual ~BattleGroundEYScore() {}; - uint32 FlagCaptures; -}; - -class BattleGroundEY : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundEY(); - ~BattleGroundEY(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - /* BG Flags */ - uint64 GetFlagPickerGUID() const { return m_FlagKeeper; } - void SetFlagPicker(uint64 guid) { m_FlagKeeper = guid; } - bool IsFlagPickedup() const { return m_FlagKeeper != 0; } - uint8 GetFlagState() const { return m_FlagState; } - void RespawnFlag(bool send_message); - void RespawnFlagAfterDrop(); - - void RemovePlayer(Player *plr,uint64 guid); - void HandleBuffUse(uint64 const& buff_guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - void HandleKillPlayer(Player *player, Player *killer); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); - virtual bool SetupBattleGround(); - virtual void Reset(); - void UpdateTeamScore(uint32 Team); - void EndBattleGround(uint32 winner); - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - virtual void FillInitialWorldStates(WorldPacket& data); - void SetDroppedFlagGUID(uint64 guid) { m_DroppedFlagGUID = guid;} - uint64 GetDroppedFlagGUID() const { return m_DroppedFlagGUID;} - - /* Battleground Events */ - virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); - virtual void EventPlayerDroppedFlag(Player *Source); - - /* achievement req. */ - bool IsAllNodesConrolledByTeam(uint32 team) const; - private: - void EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType); - void EventTeamCapturedPoint(Player *Source, uint32 Point); - void EventTeamLostPoint(Player *Source, uint32 Point); - void UpdatePointsCount(uint32 Team); - void UpdatePointsIcons(uint32 Team, uint32 Point); - - /* Point status updating procedures */ - void CheckSomeoneLeftPoint(); - void CheckSomeoneJoinedPoint(); - void UpdatePointStatuses(); - - /* Scorekeeping */ - uint32 GetTeamScore(uint32 Team) const { return m_TeamScores[GetTeamIndexByTeamId(Team)]; } - void AddPoints(uint32 Team, uint32 Points); - - void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } - void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } - - uint32 m_HonorScoreTics[2]; - uint32 m_TeamPointsCount[2]; - - uint32 m_Points_Trigger[EY_POINTS_MAX]; - - uint64 m_FlagKeeper; // keepers guid - uint64 m_DroppedFlagGUID; - uint32 m_FlagCapturedBgObjectType; // type that should be despawned when flag is captured - uint8 m_FlagState; // for checking flag state - int32 m_FlagsTimer; - int32 m_TowerCapCheckTimer; - - uint32 m_PointOwnedByTeam[EY_POINTS_MAX]; - uint8 m_PointState[EY_POINTS_MAX]; - int32 m_PointBarStatus[EY_POINTS_MAX]; - typedef std::vector PlayersNearPointType; - PlayersNearPointType m_PlayersNearPoint[EY_POINTS_MAX + 1]; - uint8 m_CurrentPointPlayersCount[2*EY_POINTS_MAX]; - - int32 m_PointAddingTimer; - uint32 m_HonorTics; -}; -#endif - diff --git a/src/server/game/BattleGroundHandler.cpp b/src/server/game/BattleGroundHandler.cpp deleted file mode 100644 index e779f2a8ab1..00000000000 --- a/src/server/game/BattleGroundHandler.cpp +++ /dev/null @@ -1,803 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectAccessor.h" -#include "ObjectMgr.h" -#include "WorldPacket.h" -#include "WorldSession.h" - -#include "ArenaTeam.h" -#include "BattleGroundMgr.h" -#include "BattleGroundWS.h" -#include "BattleGround.h" -#include "Chat.h" -#include "Language.h" -#include "Log.h" -#include "Player.h" -#include "Object.h" -#include "Opcodes.h" - -void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket & recv_data) -{ - uint64 guid; - recv_data >> guid; - sLog.outDebug("WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); - - Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); - if (!unit) - return; - - if (!unit->isBattleMaster()) // it's not battlemaster - return; - - // Stop the npc if moving - unit->StopMoving(); - - BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(unit->GetEntry()); - - if (!_player->GetBGAccessByLevel(bgTypeId)) - { - // temp, must be gossip message... - SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR); - return; - } - - SendBattlegGroundList(guid, bgTypeId); -} - -void WorldSession::SendBattlegGroundList(uint64 guid, BattleGroundTypeId bgTypeId) -{ - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId, 0); - SendPacket(&data); -} - -void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket & recv_data) -{ - uint64 guid; - uint32 bgTypeId_; - uint32 instanceId; - uint8 joinAsGroup; - bool isPremade = false; - Group * grp = NULL; - - recv_data >> guid; // battlemaster guid - recv_data >> bgTypeId_; // battleground type id (DBC id) - recv_data >> instanceId; // instance id, 0 if First Available selected - recv_data >> joinAsGroup; // join as group - - if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) - { - sLog.outError("Battleground: invalid bgtype (%u) received. possible cheater? player guid %u",bgTypeId_,_player->GetGUIDLow()); - return; - } - - BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); - - sLog.outDebug("WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid))); - - // can do this, since it's battleground, not arena - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, 0); - BattleGroundQueueTypeId bgQueueTypeIdRandom = BattleGroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0); - - // ignore if player is already in BG - if (_player->InBattleGround()) - return; - - // get bg instance or bg template if instance not found - BattleGround *bg = NULL; - if (instanceId) - bg = sBattleGroundMgr.GetBattleGroundThroughClientInstance(instanceId, bgTypeId); - - if (!bg) - bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if (!bg) - return; - - // expected bracket entry - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); - if (!bracketEntry) - return; - - GroupJoinBattlegroundResult err; - - // check queue conditions - if (!joinAsGroup) - { - // check Deserter debuff - if (!_player->CanJoinToBattleground()) - { - WorldPacket data; - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); - _player->GetSession()->SendPacket(&data); - return; - } - - if (_player->GetBattleGroundQueueIndex(bgQueueTypeIdRandom) < PLAYER_MAX_BATTLEGROUND_QUEUES) - { - //player is already in random queue - WorldPacket data; - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_RANDOM_BG); - _player->GetSession()->SendPacket(&data); - return; - } - - if (_player->InBattleGroundQueue() && bgTypeId == BATTLEGROUND_RB) - { - //player is already in queue, can't start random queue - WorldPacket data; - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_NON_RANDOM_BG); - _player->GetSession()->SendPacket(&data); - return; - } - - // check if already in queue - if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) - //player is already in this queue - return; - - // check if has free queue slots - if (!_player->HasFreeBattleGroundQueueId()) - { - WorldPacket data; - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_TOO_MANY_QUEUES); - _player->GetSession()->SendPacket(&data); - return; - } - - BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - - GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, 0, false, isPremade, 0); - uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); - // already checked if queueSlot is valid, now just get it - uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); - - WorldPacket data; - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); - SendPacket(&data); - sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); - } - else - { - grp = _player->GetGroup(); - // no group found, error - if (!grp) - return; - if (grp->GetLeaderGUID() != _player->GetGUID()) - return; - err = grp->CanJoinBattleGroundQueue(bg, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0); - isPremade = (grp->GetMembersCount() >= bg->GetMinPlayersPerTeam()); - - BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - GroupQueueInfo * ginfo = NULL; - uint32 avgTime = 0; - - if (err > 0) - { - sLog.outDebug("Battleground: the following players are joining as group:"); - ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, 0, false, isPremade, 0); - avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); - } - - for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if (!member) continue; // this should never happen - - WorldPacket data; - - if (err <= 0) - { - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); - member->GetSession()->SendPacket(&data); - continue; - } - - // add to queue - uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); - - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); - member->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); - member->GetSession()->SendPacket(&data); - sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); - } - sLog.outDebug("Battleground: group end"); - - } - sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); -} - -void WorldSession::HandleBattleGroundPlayerPositionsOpcode(WorldPacket & /*recv_data*/) -{ - // empty opcode - sLog.outDebug("WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message"); - - BattleGround *bg = _player->GetBattleGround(); - if (!bg) // can't be received if player not in battleground - return; - - switch(bg->GetTypeID(true)) - { - case BATTLEGROUND_WS: - { - uint32 count1 = 0; //always constant zero? - uint32 count2 = 0; //count of next fields - - Player *ali_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID()); - if (ali_plr) - ++count2; - - Player *horde_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID()); - if (horde_plr) - ++count2; - - WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2)); - data << count1; // alliance flag holders count - obsolete, now always 0 - /*for (uint8 i = 0; i < count1; ++i) - { - data << uint64(0); // guid - data << (float)0; // x - data << (float)0; // y - }*/ - data << count2; // horde flag holders count - obsolete, now count of next fields - if (ali_plr) - { - data << (uint64)ali_plr->GetGUID(); - data << (float)ali_plr->GetPositionX(); - data << (float)ali_plr->GetPositionY(); - } - if (horde_plr) - { - data << (uint64)horde_plr->GetGUID(); - data << (float)horde_plr->GetPositionX(); - data << (float)horde_plr->GetPositionY(); - } - - SendPacket(&data); - } - break; - case BATTLEGROUND_EY: - //TODO : fix me! - break; - case BATTLEGROUND_AB: - case BATTLEGROUND_AV: - { - //for other BG types - send default - WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4)); - data << uint32(0); - data << uint32(0); - SendPacket(&data); - } - break; - default: - //maybe it is sent also in arena - do nothing - break; - } -} - -void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("WORLD: Recvd MSG_PVP_LOG_DATA Message"); - - BattleGround *bg = _player->GetBattleGround(); - if (!bg) - return; - - WorldPacket data; - sBattleGroundMgr.BuildPvpLogDataPacket(&data, bg); - SendPacket(&data); - - sLog.outDebug("WORLD: Sent MSG_PVP_LOG_DATA Message"); -} - -void WorldSession::HandleBattlefieldListOpcode(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: Recvd CMSG_BATTLEFIELD_LIST Message"); - - uint32 bgTypeId; - recv_data >> bgTypeId; // id from DBC - - uint8 fromWhere; - recv_data >> fromWhere; // 0 - battlemaster (lua: ShowBattlefieldList), 1 - UI (lua: RequestBattlegroundInstanceInfo) - - uint8 unk1; - recv_data >> unk1; // Unknown 3.2.2 - - BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); - if (!bl) - { - sLog.outError("Battleground: invalid bgtype received."); - return; - } - - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundListPacket(&data, 0, _player, BattleGroundTypeId(bgTypeId), fromWhere); - SendPacket(&data); -} - -void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: Recvd CMSG_BATTLEFIELD_PORT Message"); - - uint8 type; // arenatype if arena - uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 - uint32 bgTypeId_; // type id from dbc - uint16 unk; // 0x1F90 constant? - uint8 action; // enter battle 0x1, leave queue 0x0 - - recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action; - - if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) - { - sLog.outError("BattlegroundHandler: invalid bgtype (%u) received.", bgTypeId_); - return; - } - if (!_player->InBattleGroundQueue()) - { - sLog.outError("BattlegroundHandler: Invalid CMSG_BATTLEFIELD_PORT received from player (%u), he is not in bg_queue.", _player->GetGUIDLow()); - return; - } - - //get GroupQueueInfo from BattleGroundQueue - BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type); - BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - //we must use temporary variable, because GroupQueueInfo pointer can be deleted in BattleGroundQueue::RemovePlayer() function - GroupQueueInfo ginfo; - if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo)) - { - sLog.outError("BattlegroundHandler: itrplayerstatus not found."); - return; - } - // if action == 1, then instanceId is required - if (!ginfo.IsInvitedToBGInstanceGUID && action == 1) - { - sLog.outError("BattlegroundHandler: instance not found."); - return; - } - - BattleGround *bg = sBattleGroundMgr.GetBattleGround(ginfo.IsInvitedToBGInstanceGUID, bgTypeId); - - // bg template might and must be used in case of leaving queue, when instance is not created yet - if (!bg && action == 0) - bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if (!bg) - { - sLog.outError("BattlegroundHandler: bg_template not found for type id %u.", bgTypeId); - return; - } - - // expected bracket entry - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); - if (!bracketEntry) - return; - - //some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it - if (action == 1 && ginfo.ArenaType == 0) - { - //if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue - if (!_player->CanJoinToBattleground()) - { - //send bg command result to show nice message - WorldPacket data2; - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data2, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); - _player->GetSession()->SendPacket(&data2); - action = 0; - sLog.outDebug("Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); - } - //if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue - if (_player->getLevel() > bg->GetMaxLevel()) - { - sLog.outError("Battleground: Player %s (%u) has level (%u) higher than maxlevel (%u) of battleground (%u)! Do not port him to battleground!", - _player->GetName(), _player->GetGUIDLow(), _player->getLevel(), bg->GetMaxLevel(), bg->GetTypeID()); - action = 0; - } - } - uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - WorldPacket data; - switch(action) - { - case 1: // port to battleground - if (!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) - return; // cheating? - - if (!_player->InBattleGround()) - _player->SetBattleGroundEntryPoint(); - - // resurrect the player - if (!_player->isAlive()) - { - _player->ResurrectPlayer(1.0f); - _player->SpawnCorpseBones(); - } - // stop taxi flight at port - if (_player->isInFlight()) - { - _player->GetMotionMaster()->MovementExpired(); - _player->CleanupAfterTaxiFlight(); - } - - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType()); - _player->GetSession()->SendPacket(&data); - // remove battleground queue status from BGmgr - bgQueue.RemovePlayer(_player->GetGUID(), false); - // this is still needed here if battleground "jumping" shouldn't add deserter debuff - // also this is required to prevent stuck at old battleground after SetBattleGroundId set to new - if (BattleGround *currentBg = _player->GetBattleGround()) - currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); - - // set the destination instance id - _player->SetBattleGroundId(bg->GetInstanceID(), bgTypeId); - // set the destination team - _player->SetBGTeam(ginfo.Team); - // bg->HandleBeforeTeleportToBattleGround(_player); - sBattleGroundMgr.SendToBattleGround(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId); - // add only in HandleMoveWorldPortAck() - // bg->AddPlayer(_player,team); - sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetInstanceID(), bg->GetTypeID(), bgQueueTypeId); - break; - case 0: // leave queue - // if player leaves rated arena match before match start, it is counted as he played but he lost - if (ginfo.IsRated) - { - ArenaTeam * at = objmgr.GetArenaTeamById(ginfo.Team); - if (at) - { - sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u, because he has left queue!", GUID_LOPART(_player->GetGUID()), ginfo.OpponentsTeamRating); - at->MemberLost(_player, ginfo.OpponentsTeamRating); - at->SaveToDB(); - } - } - _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); - bgQueue.RemovePlayer(_player->GetGUID(), true); - // player left queue, we should update it - do not update Arena Queue - if (!ginfo.ArenaType) - sBattleGroundMgr.ScheduleQueueUpdate(ginfo.ArenaTeamRating, ginfo.ArenaType, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); - SendPacket(&data); - sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetTypeID(), bgQueueTypeId); - break; - default: - sLog.outError("Battleground port: unknown action %u", action); - break; - } -} - -void WorldSession::HandleLeaveBattlefieldOpcode(WorldPacket& recv_data) -{ - sLog.outDebug("WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message"); - - recv_data.read_skip(); // unk1 - recv_data.read_skip(); // unk2 - recv_data.read_skip(); // BattleGroundTypeId - recv_data.read_skip(); // unk3 - - // not allow leave battleground in combat - if (_player->isInCombat()) - if (BattleGround* bg = _player->GetBattleGround()) - if (bg->GetStatus() != STATUS_WAIT_LEAVE) - return; - - _player->LeaveBattleground(); -} - -void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket & /*recv_data*/) -{ - // empty opcode - sLog.outDebug("WORLD: Battleground status"); - - WorldPacket data; - // we must update all queues here - BattleGround *bg = NULL; - for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - { - BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i); - if (!bgQueueTypeId) - continue; - BattleGroundTypeId bgTypeId = BattleGroundMgr::BGTemplateId(bgQueueTypeId); - uint8 arenaType = BattleGroundMgr::BGArenaType(bgQueueTypeId); - if (bgTypeId == _player->GetBattleGroundTypeId()) - { - bg = _player->GetBattleGround(); - //i cannot check any variable from player class because player class doesn't know if player is in 2v2 / 3v3 or 5v5 arena - //so i must use bg pointer to get that information - if (bg && bg->GetArenaType() == arenaType) - { - // this line is checked, i only don't know if GetStartTime is changing itself after bg end! - // send status in BattleGround - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType); - SendPacket(&data); - continue; - } - } - //we are sending update to player about queue - he can be invited there! - //get GroupQueueInfo for queue status - BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - GroupQueueInfo ginfo; - if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo)) - continue; - if (ginfo.IsInvitedToBGInstanceGUID) - { - bg = sBattleGroundMgr.GetBattleGround(ginfo.IsInvitedToBGInstanceGUID, bgTypeId); - if (!bg) - continue; - uint32 remainingTime = getMSTimeDiff(getMSTime(), ginfo.RemoveInviteTime); - // send status invited to BattleGround - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, arenaType); - SendPacket(&data); - } - else - { - bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if (!bg) - continue; - - // expected bracket entry - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); - if (!bracketEntry) - continue; - - uint32 avgTime = bgQueue.GetAverageQueueWaitTime(&ginfo, bracketEntry->GetBracketId()); - // send status in BattleGround Queue - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTimeDiff(ginfo.JoinTime, getMSTime()), arenaType); - SendPacket(&data); - } - } -} - -void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY"); - - BattleGround *bg = _player->GetBattleGround(); - - uint64 guid; - recv_data >> guid; - - Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); - if (!unit) - return; - - if (!unit->isSpiritService()) // it's not spirit service - return; - - if (bg) - sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, guid); -} - - -void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket & recv_data) -{ - sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE"); - - BattleGround *bg = _player->GetBattleGround(); - - uint64 guid; - recv_data >> guid; - - Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); - if (!unit) - return; - - if (!unit->isSpiritService()) // it's not spirit service - return; - - if (bg) - bg->AddPlayerToResurrectQueue(guid, _player->GetGUID()); -} - - -void WorldSession::HandleBattlemasterJoinArena(WorldPacket & recv_data) -{ - sLog.outDebug("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA"); - //recv_data.hexlike(); - - uint64 guid; // arena Battlemaster guid - uint8 arenaslot; // 2v2, 3v3 or 5v5 - uint8 asGroup; // asGroup - uint8 isRated; // isRated - Group * grp = NULL; - - recv_data >> guid >> arenaslot >> asGroup >> isRated; - - // ignore if we already in BG or BG queue - if (_player->InBattleGround()) - return; - - Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); - if (!unit) - return; - - if (!unit->isBattleMaster()) // it's not battle master - return; - - uint8 arenatype = 0; - uint32 arenaRating = 0; - - switch(arenaslot) - { - case 0: - arenatype = ARENA_TYPE_2v2; - break; - case 1: - arenatype = ARENA_TYPE_3v3; - break; - case 2: - arenatype = ARENA_TYPE_5v5; - break; - default: - sLog.outError("Unknown arena slot %u at HandleBattlemasterJoinArena()", arenaslot); - return; - } - - //check existance - BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA); - if (!bg) - { - sLog.outError("Battleground: template bg (all arenas) not found"); - return; - } - - BattleGroundTypeId bgTypeId = bg->GetTypeID(); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, arenatype); - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); - if (!bracketEntry) - return; - - GroupJoinBattlegroundResult err = ERR_GROUP_JOIN_BATTLEGROUND_FAIL; - - if (!asGroup) - { - // check if already in queue - if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) - //player is already in this queue - return; - // check if has free queue slots - if (!_player->HasFreeBattleGroundQueueId()) - return; - } - else - { - grp = _player->GetGroup(); - // no group found, error - if (!grp) - return; - if (grp->GetLeaderGUID() != _player->GetGUID()) - return; - err = grp->CanJoinBattleGroundQueue(bg, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot); - } - - uint32 ateamId = 0; - - if (isRated) - { - ateamId = _player->GetArenaTeamId(arenaslot); - // check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice) - ArenaTeam * at = objmgr.GetArenaTeamById(ateamId); - if (!at) - { - _player->GetSession()->SendNotInArenaTeamPacket(arenatype); - return; - } - // get the team rating for queueing - arenaRating = at->GetRating(); - // the arenateam id must match for everyone in the group - // get the personal ratings for queueing - uint32 avg_pers_rating = 0; - for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - - // calc avg personal rating - avg_pers_rating += member->GetArenaPersonalRating(arenaslot); - } - - if (arenatype) - avg_pers_rating /= arenatype; - - // if avg personal rating is more than 150 points below the teams rating, the team will be queued against an opponent matching or similar to the average personal rating - if (avg_pers_rating + 150 < arenaRating) - arenaRating = avg_pers_rating; - - if (arenaRating <= 0) - arenaRating = 1; - } - - BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - if (asGroup) - { - uint32 avgTime = 0; - - if (err > 0) - { - sLog.outDebug("Battleground: arena join as group start"); - if (isRated) - { - sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(arenaslot),_player->GetName(),arenaRating,arenatype); - bg->SetRated(true); - } - else - bg->SetRated(false); - - GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, ateamId); - avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); - } - - for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if (!member) - continue; - - WorldPacket data; - - if (err <= 0) - { - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); - member->GetSession()->SendPacket(&data); - continue; - } - - // add to queue - uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); - - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype); - member->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); - member->GetSession()->SendPacket(&data); - sLog.outDebug("Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); - } - } - else - { - GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, ateamId); - uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); - uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); - - WorldPacket data; - // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype); - SendPacket(&data); - sLog.outDebug("Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); - } - sBattleGroundMgr.ScheduleQueueUpdate(arenaRating, arenatype, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); -} - -void WorldSession::HandleReportPvPAFK(WorldPacket & recv_data) -{ - uint64 playerGuid; - recv_data >> playerGuid; - Player *reportedPlayer = objmgr.GetPlayer(playerGuid); - - if (!reportedPlayer) - { - sLog.outDebug("WorldSession::HandleReportPvPAFK: player not found"); - return; - } - - sLog.outDebug("WorldSession::HandleReportPvPAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName()); - - reportedPlayer->ReportedAfkBy(_player); -} diff --git a/src/server/game/BattleGroundIC.cpp b/src/server/game/BattleGroundIC.cpp deleted file mode 100644 index 8dbcc81e5c6..00000000000 --- a/src/server/game/BattleGroundIC.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Player.h" -#include "BattleGround.h" -#include "BattleGroundIC.h" -#include "Language.h" - -BattleGroundIC::BattleGroundIC() -{ - m_BgCreatures.resize(2); - m_BgObjects.resize(5); - //TODO FIX ME! - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; -} - -BattleGroundIC::~BattleGroundIC() -{ - -} - -void BattleGroundIC::Update(uint32 diff) -{ - BattleGround::Update(diff); -} - -void BattleGroundIC::StartingEventCloseDoors() -{ -} - -void BattleGroundIC::StartingEventOpenDoors() -{ -} - -void BattleGroundIC::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundICScore* sc = new BattleGroundICScore; - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundIC::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) -{ - -} - -void BattleGroundIC::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; -} - -void BattleGroundIC::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - - std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); - - if (itr == m_PlayerScores.end()) // player not found... - return; - - BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); -} - -bool BattleGroundIC::SetupBattleGround() -{ - AddObject(0, 195157, 459.72f, -419.93f, 42.55f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); - AddObject(1, 195158, 797.72f, -1009.48f, 138.52f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); - AddObject(2, 195338, 418.98f, -838.33f, 51.09f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); - AddObject(3, 195343, 1267.45f, -390.88f, 24.23f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); - AddObject(4, 195333, 769.27f, -833.53f, 9.57f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); - SpawnLeader(ALLIANCE); - SpawnLeader(HORDE); - return true; -} - -void BattleGroundIC::SpawnLeader(uint32 teamid) -{ - if (teamid == ALLIANCE) - AddCreature(34924, 0, ALLIANCE, 307.03f, -833.04f, 48.91f, 6.23f, 10*MINUTE); - else - AddCreature(34922, 1, HORDE, 1264.42f, -766.80f, 48.91f, 3.28f, 10*MINUTE); -} - -void BattleGroundIC::HandleKillUnit(Creature *unit, Player * /*killer*/) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - uint32 entry = unit->GetEntry(); - if (entry == 34924) - { - RewardHonorToTeam(500,HORDE); - EndBattleGround(HORDE); - } - else if (entry == 34922) - { - RewardHonorToTeam(500,ALLIANCE); - EndBattleGround(ALLIANCE); - } -} - -void BattleGroundIC::EndBattleGround(uint32 winner) -{ - BattleGround::EndBattleGround(winner); -} - -void BattleGroundIC::EventPlayerClickedOnFlag(Player * /*source*/, GameObject* /*target_obj*/) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; -} diff --git a/src/server/game/BattleGroundIC.h b/src/server/game/BattleGroundIC.h deleted file mode 100644 index e49ea01e850..00000000000 --- a/src/server/game/BattleGroundIC.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDIC_H -#define __BATTLEGROUNDIC_H - -class BattleGround; - -enum Buffs -{ - OIL_REFINERY = 68719, - QUARRY = 68720 -}; - -class BattleGroundICScore : public BattleGroundScore -{ - public: - BattleGroundICScore() {}; - virtual ~BattleGroundICScore() {}; -}; - -class BattleGroundIC : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundIC(); - ~BattleGroundIC(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr,uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - void SpawnLeader(uint32 teamid); - void HandleKillUnit(Creature *unit, Player *killer); - void EndBattleGround(uint32 winner); - void EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/); - - /* Scorekeeping */ - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - - private: -}; -#endif diff --git a/src/server/game/BattleGroundMgr.cpp b/src/server/game/BattleGroundMgr.cpp deleted file mode 100644 index 3bbe8c3cf31..00000000000 --- a/src/server/game/BattleGroundMgr.cpp +++ /dev/null @@ -1,2232 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectMgr.h" -#include "World.h" -#include "WorldPacket.h" -#include "Policies/SingletonImp.h" - -#include "ArenaTeam.h" -#include "BattleGroundMgr.h" -#include "BattleGroundAV.h" -#include "BattleGroundAB.h" -#include "BattleGroundEY.h" -#include "BattleGroundWS.h" -#include "BattleGroundNA.h" -#include "BattleGroundBE.h" -#include "BattleGroundAA.h" -#include "BattleGroundRL.h" -#include "BattleGroundSA.h" -#include "BattleGroundDS.h" -#include "BattleGroundRV.h" -#include "BattleGroundIC.h" -#include "BattleGroundRB.h" -#include "Chat.h" -#include "Map.h" -#include "MapInstanced.h" -#include "MapManager.h" -#include "Player.h" -#include "GameEventMgr.h" -#include "ProgressBar.h" -#include "SharedDefines.h" -#include "Formulas.h" - -INSTANTIATE_SINGLETON_1(BattleGroundMgr); - -/*********************************************************/ -/*** BATTLEGROUND QUEUE SYSTEM ***/ -/*********************************************************/ - -BattleGroundQueue::BattleGroundQueue() -{ - for (uint32 i = 0; i < BG_TEAMS_COUNT; ++i) - { - for (uint32 j = 0; j < MAX_BATTLEGROUND_BRACKETS; ++j) - { - m_SumOfWaitTimes[i][j] = 0; - m_WaitTimeLastPlayer[i][j] = 0; - for (uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++k) - m_WaitTimes[i][j][k] = 0; - } - } -} - -BattleGroundQueue::~BattleGroundQueue() -{ - m_QueuedPlayers.clear(); - for (int i = 0; i < MAX_BATTLEGROUND_BRACKETS; ++i) - { - for (uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; ++j) - { - for (GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr) - delete (*itr); - m_QueuedGroups[i][j].clear(); - } - } -} - -/*********************************************************/ -/*** BATTLEGROUND QUEUE SELECTION POOLS ***/ -/*********************************************************/ - -// selection pool initialization, used to clean up from prev selection -void BattleGroundQueue::SelectionPool::Init() -{ - SelectedGroups.clear(); - PlayerCount = 0; -} - -// remove group info from selection pool -// returns true when we need to try to add new group to selection pool -// returns false when selection pool is ok or when we kicked smaller group than we need to kick -// sometimes it can be called on empty selection pool -bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size) -{ - //find maxgroup or LAST group with size == size and kick it - bool found = false; - GroupsQueueType::iterator groupToKick = SelectedGroups.begin(); - for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr) - { - if (abs((int32)((*itr)->Players.size() - size)) <= 1) - { - groupToKick = itr; - found = true; - } - else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size()) - groupToKick = itr; - } - //if pool is empty, do nothing - if (GetPlayerCount()) - { - //update player count - GroupQueueInfo* ginfo = (*groupToKick); - SelectedGroups.erase(groupToKick); - PlayerCount -= ginfo->Players.size(); - //return false if we kicked smaller group or there are enough players in selection pool - if (ginfo->Players.size() <= size + 1) - return false; - } - return true; -} - -// add group to selection pool -// used when building selection pools -// returns true if we can invite more players, or when we added group to selection pool -// returns false when selection pool is full -bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount) -{ - //if group is larger than desired count - don't allow to add it to pool - if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size()) - { - SelectedGroups.push_back(ginfo); - // increase selected players count - PlayerCount += ginfo->Players.size(); - return true; - } - if (PlayerCount < desiredCount) - return true; - return false; -} - -/*********************************************************/ -/*** BATTLEGROUND QUEUES ***/ -/*********************************************************/ - -// add group or player (grp == NULL) to bg queue with the given leader and bg specifications -GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, Group* grp, BattleGroundTypeId BgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid) -{ - BattleGroundBracketId bracketId = backetEntry->GetBracketId(); - - // create new ginfo - GroupQueueInfo* ginfo = new GroupQueueInfo; - ginfo->BgTypeId = BgTypeId; - ginfo->ArenaType = ArenaType; - ginfo->ArenaTeamId = arenateamid; - ginfo->IsRated = isRated; - ginfo->IsInvitedToBGInstanceGUID = 0; - ginfo->JoinTime = getMSTime(); - ginfo->RemoveInviteTime = 0; - ginfo->Team = leader->GetTeam(); - ginfo->ArenaTeamRating = arenaRating; - ginfo->OpponentsTeamRating = 0; - - ginfo->Players.clear(); - - //compute index (if group is premade or joined a rated match) to queues - uint32 index = 0; - if (!isRated && !isPremade) - index += BG_TEAMS_COUNT; - if (ginfo->Team == HORDE) - index++; - sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, bracket_id : %u, index : %u", BgTypeId, bracketId, index); - - uint32 lastOnlineTime = getMSTime(); - - //announce world (this don't need mutex) - if (isRated && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) - { - ArenaTeam *Team = objmgr.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 - { - //ACE_Guard guard(m_Lock); - if (grp) - { - for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player *member = itr->getSource(); - if (!member) - continue; // this should never happen - PlayerQueueInfo& pl_info = m_QueuedPlayers[member->GetGUID()]; - pl_info.LastOnlineTime = lastOnlineTime; - pl_info.GroupInfo = ginfo; - // add the pinfo to ginfo's list - ginfo->Players[member->GetGUID()] = &pl_info; - } - } - else - { - PlayerQueueInfo& pl_info = m_QueuedPlayers[leader->GetGUID()]; - pl_info.LastOnlineTime = lastOnlineTime; - pl_info.GroupInfo = ginfo; - ginfo->Players[leader->GetGUID()] = &pl_info; - } - - //add GroupInfo to m_QueuedGroups - m_QueuedGroups[bracketId][index].push_back(ginfo); - - //announce to world, this code needs mutex - if (!isRated && !isPremade && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) - { - if (BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId)) - { - char const* bgName = bg->GetName(); - uint32 MinPlayers = bg->GetMinPlayersPerTeam(); - uint32 qHorde = 0; - uint32 qAlliance = 0; - uint32 q_min_level = backetEntry->minLevel; - uint32 q_max_level = backetEntry->maxLevel; - GroupsQueueType::const_iterator itr; - for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr) - if (!(*itr)->IsInvitedToBGInstanceGUID) - qAlliance += (*itr)->Players.size(); - for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].end(); ++itr) - if (!(*itr)->IsInvitedToBGInstanceGUID) - qHorde += (*itr)->Players.size(); - - // Show queue status to player only (when joining queue) - if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) - { - ChatHandler(leader).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bgName, q_min_level, q_max_level, - qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); - } - // System message - else - { - sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bgName, q_min_level, q_max_level, - qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); - } - } - } - //release mutex - } - - return ginfo; -} - -void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) -{ - uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); - uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! - if (!ginfo->ArenaType) - { - if (ginfo->Team == HORDE) - team_index = BG_TEAM_HORDE; - } - else - { - if (ginfo->IsRated) - team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE - } - - //store pointer to arrayindex of player that was added first - uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][bracket_id]); - //remove his time from sum - m_SumOfWaitTimes[team_index][bracket_id] -= m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)]; - //set average time to new - m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)] = timeInQueue; - //add new time to sum - m_SumOfWaitTimes[team_index][bracket_id] += timeInQueue; - //set index of last player added to next one - (*lastPlayerAddedPointer)++; - (*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; -} - -uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) -{ - uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! - if (!ginfo->ArenaType) - { - if (ginfo->Team == HORDE) - team_index = BG_TEAM_HORDE; - } - else - { - if (ginfo->IsRated) - team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE - } - //check if there is enought values(we always add values > 0) - if (m_WaitTimes[team_index][bracket_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1]) - return (m_SumOfWaitTimes[team_index][bracket_id] / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME); - else - //if there aren't enough values return 0 - not available - return 0; -} - -//remove player from queue and from group info, if group info is empty then remove it too -void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount) -{ - //Player *plr = objmgr.GetPlayer(guid); - - int32 bracket_id = -1; // signed for proper for-loop finish - QueuedPlayersMap::iterator itr; - - //remove player from map, if he's there - itr = m_QueuedPlayers.find(guid); - if (itr == m_QueuedPlayers.end()) - { - sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid)); - return; - } - - GroupQueueInfo* group = itr->second.GroupInfo; - GroupsQueueType::iterator group_itr, group_itr_tmp; - // mostly people with the highest levels are in battlegrounds, thats why - // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 - // variable index removes useless searching in other team's queue - uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE; - - for (int32 bracket_id_tmp = MAX_BATTLEGROUND_BRACKETS - 1; bracket_id_tmp >= 0 && bracket_id == -1; --bracket_id_tmp) - { - //we must check premade and normal team's queue - because when players from premade are joining bg, - //they leave groupinfo so we can't use its players size to find out index - for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT; j += BG_QUEUE_NORMAL_ALLIANCE) - { - for (group_itr_tmp = m_QueuedGroups[bracket_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[bracket_id_tmp][j].end(); ++group_itr_tmp) - { - if ((*group_itr_tmp) == group) - { - bracket_id = bracket_id_tmp; - group_itr = group_itr_tmp; - //we must store index to be able to erase iterator - index = j; - break; - } - } - } - } - //player can't be in queue without group, but just in case - if (bracket_id == -1) - { - sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid)); - return; - } - sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from bracket_id %u", GUID_LOPART(guid), (uint32)bracket_id); - - // ALL variables are correctly set - // We can ignore leveling up in queue - it should not cause crash - // remove player from group - // if only one player there, remove group - - // remove player queue info from group queue info - std::map::iterator pitr = group->Players.find(guid); - if (pitr != group->Players.end()) - group->Players.erase(pitr); - - // if invited to bg, and should decrease invited count, then do it - if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID) - { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId); - if (bg) - bg->DecreaseInvitedCount(group->Team); - } - - // remove player queue info - m_QueuedPlayers.erase(itr); - - // announce to world if arena team left queue for rated match, show only once - if (group->ArenaType && group->IsRated && group->Players.empty() && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) - { - ArenaTeam *Team = objmgr.GetArenaTeamById(group->ArenaTeamId); - if (Team) - 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 loose - if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount) - { - ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId); - if (at) - { - sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating); - Player *plr = objmgr.GetPlayer(guid); - if (plr) - at->MemberLost(plr, group->OpponentsTeamRating); - else - at->OfflineMemberLost(guid, group->OpponentsTeamRating); - at->SaveToDB(); - } - } - - // remove group queue info if needed - if (group->Players.empty()) - { - m_QueuedGroups[bracket_id][index].erase(group_itr); - delete group; - } - // if group wasn't empty, so it wasn't deleted, and player have left a rated - // queue -> everyone from the group should leave too - // don't remove recursively if already invited to bg! - else if (!group->IsInvitedToBGInstanceGUID && group->IsRated) - { - // remove next player, this is recursive - // first send removal information - if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) - { - BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType); - uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); - plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to - // queue->removeplayer, it causes bugs - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); - plr2->GetSession()->SendPacket(&data); - } - // then actually delete, this may delete the group as well! - RemovePlayer(group->Players.begin()->first, decreaseInvitedCount); - } -} - -//returns true when player pl_guid is in queue and is invited to bgInstanceGuid -bool BattleGroundQueue::IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime) -{ - QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(pl_guid); - return (qItr != m_QueuedPlayers.end() - && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == bgInstanceGuid - && qItr->second.GroupInfo->RemoveInviteTime == removeTime); -} - -bool BattleGroundQueue::GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo) -{ - QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(guid); - if (qItr == m_QueuedPlayers.end()) - return false; - *ginfo = *(qItr->second.GroupInfo); - return true; -} - -bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) -{ - // set side if needed - if (side) - ginfo->Team = side; - - if (!ginfo->IsInvitedToBGInstanceGUID) - { - // not yet invited - // set invitation - ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); - BattleGroundTypeId bgTypeId = bg->GetTypeID(); - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType()); - BattleGroundBracketId bracket_id = bg->GetBracketId(); - - // set ArenaTeamId for rated matches - if (bg->isArena() && bg->isRated()) - bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId); - - ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; - - // loop through the players - for (std::map::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) - { - // get the player - Player* plr = objmgr.GetPlayer(itr->first); - // if offline, skip him, this should not happen - player is removed from queue when he logs out - if (!plr) - continue; - - // invite the player - PlayerInvitedToBGUpdateAverageWaitTime(ginfo, bracket_id); - //sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team); - - // set invited player counters - bg->IncreaseInvitedCount(ginfo->Team); - - plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); - - // create remind invite events - BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime); - plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME)); - // create automatic remove events - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); - plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); - - WorldPacket data; - - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - - sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); - - // send status packet - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); - plr->GetSession()->SendPacket(&data); - } - return true; - } - - return false; -} - -/* -This function is inviting players to already running battlegrounds -Invitation type is based on config file -large groups are disadvantageous, because they will be kicked first if invitation type = 1 -*/ -void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id) -{ - int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE); - int32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE); - - //iterator for iterating through bg queue - GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); - //count of groups in queue - used to stop cycles - uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); - //index to queue which group is current - uint32 aliIndex = 0; - for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++) - ++Ali_itr; - //the same thing for horde - GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin(); - uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); - uint32 hordeIndex = 0; - for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++) - ++Horde_itr; - - //if ofc like BG queue invitation is set in config, then we are happy - if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) - return; - - /* - if we reached this code, then we have to solve NP - complete problem called Subset sum problem - So one solution is to check all possible invitation subgroups, or we can use these conditions: - 1. Last time when BattleGroundQueue::Update was executed we invited all possible players - so there is only small possibility - that we will invite now whole queue, because only 1 change has been made to queues from the last BattleGroundQueue::Update call - 2. Other thing we should consider is group order in queue - */ - - // At first we need to compare free space in bg and our selection pool - int32 diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); - int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); - while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0)) - { - //each cycle execution we need to kick at least 1 group - if (diffAli < diffHorde) - { - //kick alliance group, add to pool new group if needed - if (m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) - { - for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++) - ++Ali_itr; - } - //if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break; - if (!m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) - { - if (aliFree <= diffHorde + 1) - break; - m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); - } - } - else - { - //kick horde group, add to pool new group if needed - if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) - { - for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) - ++Horde_itr; - } - if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) - { - if (hordeFree <= diffAli + 1) - break; - m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); - } - } - //count diffs after small update - diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); - diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); - } -} - -// this method checks if premade versus premade battleground is possible -// then after 30 mins (default) in queue it moves premade group to normal queue -// it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players -bool BattleGroundQueue::CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam) -{ - //check match - if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) - { - //start premade match - //if groups aren't invited - GroupsQueueType::const_iterator ali_group, horde_group; - for (ali_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group) - if (!(*ali_group)->IsInvitedToBGInstanceGUID) - break; - for (horde_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group) - if (!(*horde_group)->IsInvitedToBGInstanceGUID) - break; - - if (ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end()) - { - m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); - m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); - //add groups/players from normal queue to size of bigger group - uint32 maxPlayers = std::min(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); - GroupsQueueType::const_iterator itr; - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - { - for (itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) - { - //if itr can join BG and player count is less that maxPlayers, then add group to selectionpool - if (!(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers)) - break; - } - } - //premade selection pools are set - return true; - } - } - // now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!! - // this could be 2 cycles but i'm checking only first team in queue - it can cause problem - - // if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg - // and when they click or after 80 seconds the queue info is removed from queue - uint32 time_before = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - { - if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty()) - { - GroupsQueueType::iterator itr = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin(); - if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam)) - { - //we must insert group to normal queue and erase pointer from premade queue - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); - } - } - } - //selection pools are not set - return false; -} - -// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam -bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) -{ - GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - { - itr_team[i] = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); - for (; itr_team[i] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i])) - { - if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID) - { - m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers); - if (m_SelectionPools[i].GetPlayerCount() >= minPlayers) - break; - } - } - } - //try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg - uint32 j = BG_TEAM_ALLIANCE; - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) - j = BG_TEAM_HORDE; - if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 - && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers) - { - //we will try to invite more groups to team with less players indexed by j - ++(itr_team[j]); //this will not cause a crash, because for cycle above reached break; - for (; itr_team[j] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j])) - { - if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID) - if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount())) - break; - } - // do not allow to start bg with more than 2 players more on 1 faction - if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) - return false; - } - //allow 1v0 if debug bg - if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())) - return true; - //return true if there are enough players in selection pools - enable to work .debug bg command correctly - return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers; -} - -// this method will check if we can invite players to same faction skirmish match -bool BattleGroundQueue::CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam) -{ - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) - return false; - uint32 teamIndex = BG_TEAM_ALLIANCE; - uint32 otherTeam = BG_TEAM_HORDE; - uint32 otherTeamId = HORDE; - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) - { - teamIndex = BG_TEAM_HORDE; - otherTeam = BG_TEAM_ALLIANCE; - otherTeamId = ALLIANCE; - } - //clear other team's selection - m_SelectionPools[otherTeam].Init(); - //store last ginfo pointer - GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back(); - //set itr_team to group that was added to selection pool latest - GroupsQueueType::iterator itr_team = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); - for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) - if (ginfo == *itr_team) - break; - if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end()) - return false; - GroupsQueueType::iterator itr_team2 = itr_team; - ++itr_team2; - //invite players to other selection pool - for (; itr_team2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team2) - { - //if selection pool is full then break; - if (!(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam)) - break; - } - if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam) - return false; - - //here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue - for (GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr) - { - //set correct team - (*itr)->Team = otherTeamId; - //add team to other queue - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + otherTeam].push_front(*itr); - //remove team from old queue - GroupsQueueType::iterator itr2 = itr_team; - ++itr2; - for (; itr2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr2) - { - if (*itr2 == *itr) - { - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); - break; - } - } - } - return true; -} - -/* -this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while (true) cycles to invite whole queue -it must be called after fully adding the members of a group to ensure group joining -should be called from BattleGround::RemovePlayer function in some cases -*/ -void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating) -{ - //if no players in queue - do nothing - if (m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty() && - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].empty() && - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].empty()) - return; - - //battleground with free slot for player should be always in the beggining of the queue - // maybe it would be better to create bgfreeslotqueue for each bracket_id - BGFreeSlotQueueType::iterator itr, next; - for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) - { - next = itr; - ++next; - // DO NOT allow queue manager to invite new player to arena - if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetBracketId() == bracket_id && - (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) - { - BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) - // and iterator is invalid - - // clear selection pools - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_HORDE].Init(); - - // call a function that does the job for us - FillPlayersToBG(bg, bracket_id); - - // now everything is set, invite players - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg, (*citr)->Team); - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg, (*citr)->Team); - - if (!bg->HasFreeSlots()) - { - // remove BG from BGFreeSlotQueue - bg->RemoveFromBGFreeSlotQueue(); - } - } - } - - // finished iterating through the bgs with free slots, maybe we need to create a new bg - - BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if (!bg_template) - { - sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); - return; - } - - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(),bracket_id); - if (!bracketEntry) - { - sLog.outError("Battleground: Update: bg bracket entry not found for map %u bracket id %u", bg_template->GetMapId(), bracket_id); - return; - } - - // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) - uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); - uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); - if (sBattleGroundMgr.isTesting()) - MinPlayersPerTeam = 1; - if (bg_template->isArena()) - { - if (sBattleGroundMgr.isArenaTesting()) - { - MaxPlayersPerTeam = 1; - MinPlayersPerTeam = 1; - } - else - { - //this switch can be much shorter - MaxPlayersPerTeam = arenaType; - MinPlayersPerTeam = arenaType; - /*switch(arenaType) - { - case ARENA_TYPE_2v2: - MaxPlayersPerTeam = 2; - MinPlayersPerTeam = 2; - break; - case ARENA_TYPE_3v3: - MaxPlayersPerTeam = 3; - MinPlayersPerTeam = 3; - break; - case ARENA_TYPE_5v5: - MaxPlayersPerTeam = 5; - MinPlayersPerTeam = 5; - break; - }*/ - } - } - - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_HORDE].Init(); - - if (bg_template->isBattleGround()) - { - //check if there is premade against premade match - if (CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)) - { - //create new battleground - BattleGround * bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, 0, false); - if (!bg2) - { - sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); - return; - } - //invite those selection pools - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg2, (*citr)->Team); - //start bg - bg2->StartBattleGround(); - //clear structures - m_SelectionPools[BG_TEAM_ALLIANCE].Init(); - m_SelectionPools[BG_TEAM_HORDE].Init(); - } - } - - // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena) - if (!isRated) - { - // if there are enough players in pools, start new battleground or non rated arena - if (CheckNormalMatch(bg_template, bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam) - || (bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam))) - { - // we successfully created a pool - BattleGround * bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, false); - if (!bg2) - { - sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); - return; - } - - // invite those selection pools - for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) - for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) - InviteGroupToBG((*citr), bg2, (*citr)->Team); - // start bg - bg2->StartBattleGround(); - } - } - else if (bg_template->isArena()) - { - // found out the minimum and maximum ratings the newly added team should battle against - // arenaRating is the rating of the latest joined team, or 0 - // 0 is on (automatic update call) and we must set it to team's with longest wait time - if (!arenaRating) - { - GroupQueueInfo* front1 = NULL; - GroupQueueInfo* front2 = NULL; - if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty()) - { - front1 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].front(); - arenaRating = front1->ArenaTeamRating; - } - if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) - { - front2 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].front(); - arenaRating = front2->ArenaTeamRating; - } - if (front1 && front2) - { - if (front1->JoinTime < front2->JoinTime) - arenaRating = front1->ArenaTeamRating; - } - else if (!front1 && !front2) - return; //queues are empty - } - - //set rating range - uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); - uint32 arenaMaxRating = arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); - // if max rating difference is set and the time past since server startup is greater than the rating discard time - // (after what time the ratings aren't taken into account when making teams) then - // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account - // else leave the discard time on 0, this way all ratings will be discarded - uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); - - // we need to find 2 teams which will play next game - - GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; - - //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups - - for (uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) - { - // take the group that joined first - itr_team[i] = m_QueuedGroups[bracket_id][i].begin(); - for (; itr_team[i] != m_QueuedGroups[bracket_id][i].end(); ++(itr_team[i])) - { - // if group match conditions, then add it to pool - if (!(*itr_team[i])->IsInvitedToBGInstanceGUID - && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[i])->JoinTime < discardTime)) - { - m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam); - // break for cycle to be able to start selecting another group from same faction queue - break; - } - } - } - // now we are done if we have 2 groups - ali vs horde! - // if we don't have, we must try to continue search in same queue - // tmp variables are correctly set - // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) - { - itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; - ++itr_team[BG_TEAM_ALLIANCE]; - for (; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) - { - if (!(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID - && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime)) - { - m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); - break; - } - } - } - // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue - if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) - { - itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; - ++itr_team[BG_TEAM_HORDE]; - for (; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) - { - if (!(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID - && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) - || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime)) - { - m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); - break; - } - } - } - - //if we have 2 teams, then start new arena and invite players! - if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) - { - BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, true); - if (!arena) - { - sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); - return; - } - - (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; - sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); - (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; - sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating); - // now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer - if ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE) - { - // add to alliance queue - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); - // erase from horde queue - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); - itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); - } - if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE) - { - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); - itr_team[BG_TEAM_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); - } - - InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); - InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); - - sLog.outDebug("Starting rated arena match!"); - - arena->StartBattleGround(); - } - } -} - -/*********************************************************/ -/*** BATTLEGROUND QUEUE EVENTS ***/ -/*********************************************************/ - -bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - Player* plr = objmgr.GetPlayer(m_PlayerGuid); - // player logged off (we should do nothing, he is correctly removed from queue in another procedure) - if (!plr) - return true; - - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); - //if battleground ended and its instance deleted - do nothing - if (!bg) - return true; - - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue or in battleground - { - // check if player is invited to this bg - BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; - if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) - { - WorldPacket data; - //we must send remaining time in queue - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, m_ArenaType); - plr->GetSession()->SendPacket(&data); - } - } - return true; //event will be deleted -} - -void BGQueueInviteEvent::Abort(uint64 /*e_time*/) -{ - //do nothing -} - -/* - this event has many possibilities when it is executed: - 1. player is in battleground (he clicked enter on invitation window) - 2. player left battleground queue and he isn't there any more - 3. player left battleground queue and he joined it again and IsInvitedToBGInstanceGUID = 0 - 4. player left queue and he joined again and he has been invited to same battleground again -> we should not remove him from queue yet - 5. player is invited to bg and he didn't choose what to do and timer expired - only in this condition we should call queue::RemovePlayer - we must remove player in the 5. case even if battleground object doesn't exist! -*/ -bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - Player* plr = objmgr.GetPlayer(m_PlayerGuid); - if (!plr) - // player logged off (we should do nothing, he is correctly removed from queue in another procedure) - return true; - - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); - //battleground can be deleted already when we are removing queue info - //bg pointer can be NULL! so use it carefully! - - uint32 queueSlot = plr->GetBattleGroundQueueIndex(m_BgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue, or in Battleground - { - // check if player is in queue for this BG and if we are removing his invite event - BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId]; - if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) - { - sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); - - plr->RemoveBattleGroundQueueId(m_BgQueueTypeId); - bgQueue.RemovePlayer(m_PlayerGuid, true); - //update queues if battleground isn't ended - if (bg && bg->isBattleGround() && bg->GetStatus() != STATUS_WAIT_LEAVE) - sBattleGroundMgr.ScheduleQueueUpdate(0, 0, m_BgQueueTypeId, m_BgTypeId, bg->GetBracketId()); - - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); - plr->GetSession()->SendPacket(&data); - } - } - - //event will be deleted - return true; -} - -void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) -{ - //do nothing -} - -/*********************************************************/ -/*** BATTLEGROUND MANAGER ***/ -/*********************************************************/ - -BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false) -{ - for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) - m_BattleGrounds[i].clear(); - m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); - m_Testing=false; -} - -BattleGroundMgr::~BattleGroundMgr() -{ - DeleteAllBattleGrounds(); -} - -void BattleGroundMgr::DeleteAllBattleGrounds() -{ - for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i) - { - for (BattleGroundSet::iterator itr = m_BattleGrounds[i].begin(); itr != m_BattleGrounds[i].end();) - { - BattleGround * bg = itr->second; - m_BattleGrounds[i].erase(itr++); - if (!m_ClientBattleGroundIds[i][bg->GetBracketId()].empty()) - m_ClientBattleGroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID()); - delete bg; - } - } - - // destroy template battlegrounds that listed only in queues (other already terminated) - for (uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId) - { - // ~BattleGround call unregistring BG from queue - while (!BGFreeSlotQueue[bgTypeId].empty()) - delete BGFreeSlotQueue[bgTypeId].front(); - } -} - -// used to update running battlegrounds, and delete finished ones -void BattleGroundMgr::Update(uint32 diff) -{ - BattleGroundSet::iterator itr, next; - for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i) - { - itr = m_BattleGrounds[i].begin(); - // skip updating battleground template - if (itr != m_BattleGrounds[i].end()) - ++itr; - for (; itr != m_BattleGrounds[i].end(); itr = next) - { - next = itr; - ++next; - itr->second->Update(diff); - // use the SetDeleteThis variable - // direct deletion caused crashes - if (itr->second->m_SetDeleteThis) - { - BattleGround * bg = itr->second; - m_BattleGrounds[i].erase(itr); - if (!m_ClientBattleGroundIds[i][bg->GetBracketId()].empty()) - m_ClientBattleGroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID()); - delete bg; - } - } - } - - // update scheduled queues - if (!m_QueueUpdateScheduler.empty()) - { - std::vector scheduled; - { - //copy vector and clear the other - scheduled = std::vector(m_QueueUpdateScheduler); - m_QueueUpdateScheduler.clear(); - //release lock - } - - for (uint8 i = 0; i < scheduled.size(); i++) - { - uint32 arenaRating = scheduled[i] >> 32; - uint8 arenaType = scheduled[i] >> 24 & 255; - BattleGroundQueueTypeId bgQueueTypeId = BattleGroundQueueTypeId(scheduled[i] >> 16 & 255); - BattleGroundTypeId bgTypeId = BattleGroundTypeId((scheduled[i] >> 8) & 255); - BattleGroundBracketId bracket_id = BattleGroundBracketId(scheduled[i] & 255); - m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, bracket_id, arenaType, arenaRating > 0, arenaRating); - } - } - - // if rating difference counts, maybe force-update queues - if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER)) - { - // it's time to force update - if (m_NextRatingDiscardUpdate < diff) - { - // forced update for rated arenas (scan all, but skipped non rated) - sLog.outDebug("BattleGroundMgr: UPDATING ARENA QUEUES"); - for (int qtype = BATTLEGROUND_QUEUE_2v2; qtype <= BATTLEGROUND_QUEUE_5v5; ++qtype) - for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) - m_BattleGroundQueues[qtype].Update( - BATTLEGROUND_AA, BattleGroundBracketId(bracket), - BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId(qtype)), true, 0); - - m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); - } - else - m_NextRatingDiscardUpdate -= diff; - } - if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) - { - if (m_AutoDistributionTimeChecker < diff) - { - if (time(NULL) > m_NextAutoDistributionTime) - { - DistributeArenaPoints(); - m_NextAutoDistributionTime = m_NextAutoDistributionTime + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); - sWorld.setWorldState(WS_ARENA_DISTRIBUTION_TIME, uint64(m_NextAutoDistributionTime)); - } - m_AutoDistributionTimeChecker = 600000; // check 10 minutes - } - else - m_AutoDistributionTimeChecker -= diff; - } -} - -void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype) -{ - // we can be in 2 queues in same time... - - if (StatusID == 0 || !bg) - { - data->Initialize(SMSG_BATTLEFIELD_STATUS, 4+8); - *data << uint32(QueueSlot); // queue id (0...1) - *data << uint64(0); - return; - } - - data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+8+1+1+4+1+4+4+4)); - *data << uint32(QueueSlot); // queue id (0...1) - player can be in 2 queues in time - // The following segment is read as uint64 in client but can be appended as their original type. - *data << uint8(arenatype); - sLog.outDebug("BattleGroundMgr::BuildBattleGroundStatusPacket: arenatype = %u for bg instanceID %u, TypeID %u.", arenatype, bg->GetClientInstanceID(), bg->GetTypeID()); - *data << uint8(bg->isArena() ? 0xC : 0x2); - *data << uint32(bg->GetTypeID()); - *data << uint16(0x1F90); - // End of uint64 segment, decomposed this way for simplicity - *data << uint8(0); // 3.3.0 - *data << uint8(0); // 3.3.0 - *data << uint32(bg->GetClientInstanceID()); - // alliance/horde for BG and skirmish/rated for Arenas - // following displays the minimap-icon 0 = faction icon 1 = arenaicon - *data << uint8(bg->isRated()); // 1 for rated match, 0 for bg or non rated match - - *data << uint32(StatusID); // status - switch(StatusID) - { - case STATUS_WAIT_QUEUE: // status_in_queue - *data << uint32(Time1); // average wait time, milliseconds - *data << uint32(Time2); // time in queue, updated every minute!, milliseconds - break; - case STATUS_WAIT_JOIN: // status_invite - *data << uint32(bg->GetMapId()); // map id - *data << uint32(Time1); // time to remove from queue, milliseconds - break; - case STATUS_IN_PROGRESS: // status_in_progress - *data << uint32(bg->GetMapId()); // map id - *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds - *data << uint32(Time2); // time from bg start, milliseconds - *data << uint8(/*bg->isArena() ? 0 :*/ 1); // unk, possibly 0 == preparation phase, 1 == battle - break; - default: - sLog.outError("Unknown BG status!"); - break; - } -} - -void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) -{ - uint8 type = (bg->isArena() ? 1 : 0); - // last check on 3.0.3 - data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); - *data << uint8(type); // type (battleground=0/arena=1) - - if (type) // arena - { - // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H - for (int i = 1; i >= 0; --i) - { - uint32 pointsLost = bg->m_ArenaTeamRatingChanges[i] < 0 ? abs(bg->m_ArenaTeamRatingChanges[i]) : 0; - uint32 pointsGained = bg->m_ArenaTeamRatingChanges[i] > 0 ? bg->m_ArenaTeamRatingChanges[i] : 0; - - *data << uint32(pointsLost); // Rating Lost - *data << uint32(pointsGained); // Rating gained - *data << uint32(0); // Matchmaking Value - sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); - } - for (int i = 1; i >= 0; --i) - { - uint32 at_id = bg->m_ArenaTeamIds[i]; - ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if (at) - *data << at->GetName(); - else - *data << (uint8)0; - } - } - - if (bg->GetStatus() != STATUS_WAIT_LEAVE) - { - *data << uint8(0); // bg not ended - } - else - { - *data << uint8(1); // bg ended - *data << uint8(bg->GetWinner()); // who win - } - - *data << (int32)(bg->GetPlayerScoresSize()); - - for (BattleGround::BattleGroundScoreMap::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr) - { - *data << (uint64)itr->first; - *data << (int32)itr->second->KillingBlows; - if (type == 0) - { - *data << (int32)itr->second->HonorableKills; - *data << (int32)itr->second->Deaths; - *data << (int32)(itr->second->BonusHonor); - } - else - { - Player *plr = objmgr.GetPlayer(itr->first); - uint32 team = bg->GetPlayerTeam(itr->first); - if (!team && plr) - team = plr->GetBGTeam(); - *data << uint8(team == ALLIANCE ? 1 : 0); // green or yellow - - } - *data << (int32)itr->second->DamageDone; // damage done - *data << (int32)itr->second->HealingDone; // healing done - switch(bg->GetTypeID(true)) // battleground specific things - { - case BATTLEGROUND_RB: - switch(bg->GetMapId()) - { - case 489: - *data << (uint32)0x00000002; // count of next fields - *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures - *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns - break; - case 566: - *data << (uint32)0x00000001; // count of next fields - *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures - break; - case 529: - *data << (uint32)0x00000002; // count of next fields - *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted - *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended - break; - case 30: - *data << (uint32)0x00000005; // count of next fields - *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted - *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended - *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted - *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended - *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured - break; - case 607: - *data << uint32(2); - *data << uint32(((BattleGroundSAScore*)itr->second)->demolishers_destroyed); - *data << uint32(((BattleGroundSAScore*)itr->second)->gates_destroyed); - break; - default: - *data << (int32)0; // 0 - break; - } - case BATTLEGROUND_AV: - *data << (uint32)0x00000005; // count of next fields - *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted - *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended - *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted - *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended - *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured - break; - case BATTLEGROUND_WS: - *data << (uint32)0x00000002; // count of next fields - *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures - *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns - break; - case BATTLEGROUND_AB: - *data << (uint32)0x00000002; // count of next fields - *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted - *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended - break; - case BATTLEGROUND_EY: - *data << (uint32)0x00000001; // count of next fields - *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures - break; - case BATTLEGROUND_NA: - case BATTLEGROUND_BE: - case BATTLEGROUND_AA: - case BATTLEGROUND_RL: - case BATTLEGROUND_SA: - *data << uint32(2); - *data << uint32(((BattleGroundSAScore*)itr->second)->demolishers_destroyed); - *data << uint32(((BattleGroundSAScore*)itr->second)->gates_destroyed); - break; - case BATTLEGROUND_DS: // wotlk - case BATTLEGROUND_RV: // wotlk - case BATTLEGROUND_IC: // wotlk - *data << (int32)0; // 0 - break; - default: - sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); - *data << (int32)0; - break; - } - } -} - -void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, GroupJoinBattlegroundResult result) -{ - data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - *data << int32(result); - if (result == ERR_BATTLEGROUND_JOIN_TIMED_OUT || result == ERR_BATTLEGROUND_JOIN_FAILED) - *data << uint64(0); // player guid -} - -void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value) -{ - data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4); - *data << uint32(field); - *data << uint32(value); -} - -void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid) -{ - data->Initialize(SMSG_PLAY_SOUND, 4); - *data << uint32(soundid); -} - -void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid) -{ - data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); - *data << uint64(guid); -} - -void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr) -{ - data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8); - *data << uint64(plr->GetGUID()); -} - -BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId) -{ - //cause at HandleBattleGroundJoinOpcode the clients sends the instanceid he gets from - //SMSG_BATTLEFIELD_LIST we need to find the battleground with this clientinstance-id - BattleGround* bg = GetBattleGroundTemplate(bgTypeId); - if (!bg) - return NULL; - - if (bg->isArena()) - return GetBattleGround(instanceId, bgTypeId); - - for (BattleGroundSet::iterator itr = m_BattleGrounds[bgTypeId].begin(); itr != m_BattleGrounds[bgTypeId].end(); ++itr) - { - if (itr->second->GetClientInstanceID() == instanceId) - return itr->second; - } - return NULL; -} - -BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId) -{ - if (!InstanceID) - return NULL; - //search if needed - BattleGroundSet::iterator itr; - if (bgTypeId == BATTLEGROUND_TYPE_NONE) - { - for (uint32 i = BATTLEGROUND_AV; i < MAX_BATTLEGROUND_TYPE_ID; i++) - { - itr = m_BattleGrounds[i].find(InstanceID); - if (itr != m_BattleGrounds[i].end()) - return itr->second; - } - return NULL; - } - itr = m_BattleGrounds[bgTypeId].find(InstanceID); - return ((itr != m_BattleGrounds[bgTypeId].end()) ? itr->second : NULL); -} - -BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId) -{ - //map is sorted and we can be sure that lowest instance id has only BG template - return m_BattleGrounds[bgTypeId].empty() ? NULL : m_BattleGrounds[bgTypeId].begin()->second; -} - -uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id) -{ - if (IsArenaType(bgTypeId)) - return 0; //arenas don't have client-instanceids - - // we create here an instanceid, which is just for - // displaying this to the client and without any other use.. - // the client-instanceIds are unique for each battleground-type - // the instance-id just needs to be as low as possible, beginning with 1 - // the following works, because std::set is default ordered with "<" - // the optimalization would be to use as bitmask std::vector - but that would only make code unreadable - uint32 lastId = 0; - for (std::set::iterator itr = m_ClientBattleGroundIds[bgTypeId][bracket_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][bracket_id].end();) - { - if ((++lastId) != *itr) //if there is a gap between the ids, we will break.. - break; - lastId = *itr; - } - m_ClientBattleGroundIds[bgTypeId][bracket_id].insert(lastId + 1); - return lastId + 1; -} - -// create a new battleground that will really be used to play -BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated) -{ - // get the template BG - BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); - BattleGroundTypeIdList *enabledBGsOrArenas = NULL; - - if (!bg_template) - { - sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); - return NULL; - } - bool isRandom = false; - - if (bg_template->isArena()) - enabledBGsOrArenas = &m_EnabledArenas; - else if (bgTypeId == BATTLEGROUND_RB) - { - enabledBGsOrArenas = &m_EnabledBGs; - isRandom = true; - } - - if (enabledBGsOrArenas) - { - if (!enabledBGsOrArenas->size()) - return NULL; - uint8 size = enabledBGsOrArenas->size() - 1; - bgTypeId = enabledBGsOrArenas->at(urand(0,size)); - bg_template = GetBattleGroundTemplate(bgTypeId); - if (!bg_template) - { - sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); - return NULL; - } - } - - BattleGround *bg = NULL; - // create a copy of the BG template - switch(bgTypeId) - { - case BATTLEGROUND_AV: - bg = new BattleGroundAV(*(BattleGroundAV*)bg_template); - break; - case BATTLEGROUND_WS: - bg = new BattleGroundWS(*(BattleGroundWS*)bg_template); - break; - case BATTLEGROUND_AB: - bg = new BattleGroundAB(*(BattleGroundAB*)bg_template); - break; - case BATTLEGROUND_NA: - bg = new BattleGroundNA(*(BattleGroundNA*)bg_template); - break; - case BATTLEGROUND_BE: - bg = new BattleGroundBE(*(BattleGroundBE*)bg_template); - break; - case BATTLEGROUND_AA: - bg = new BattleGroundAA(*(BattleGroundAA*)bg_template); - break; - case BATTLEGROUND_EY: - bg = new BattleGroundEY(*(BattleGroundEY*)bg_template); - break; - case BATTLEGROUND_RL: - bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); - break; - case BATTLEGROUND_SA: - bg = new BattleGroundSA(*(BattleGroundSA*)bg_template); - break; - case BATTLEGROUND_DS: - bg = new BattleGroundDS(*(BattleGroundDS*)bg_template); - break; - case BATTLEGROUND_RV: - bg = new BattleGroundRV(*(BattleGroundRV*)bg_template); - break; - case BATTLEGROUND_IC: - bg = new BattleGroundIC(*(BattleGroundIC*)bg_template); - break; - case BATTLEGROUND_RB: - bg = new BattleGroundRB(*(BattleGroundRB*)bg_template); - break; - default: - //error, but it is handled few lines above - return 0; - } - - // generate a new instance id - bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id - bg->SetClientInstanceID(CreateClientVisibleInstanceId(isRandom ? BATTLEGROUND_RB : bgTypeId, bracketEntry->GetBracketId())); - - // reset the new bg (set status to status_wait_queue from status_none) - bg->Reset(); - - // start the joining of the bg - bg->SetStatus(STATUS_WAIT_JOIN); - bg->SetBracket(bracketEntry); - bg->SetArenaType(arenaType); - bg->SetRated(isRated); - bg->SetRandom(isRandom); - bg->SetTypeID(isRandom ? BATTLEGROUND_RB : bgTypeId); - bg->SetRandomTypeID(bgTypeId); - - return bg; -} - -// used to create the BG templates -uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) -{ - // Create the BG - BattleGround *bg = NULL; - switch(bgTypeId) - { - case BATTLEGROUND_AV: bg = new BattleGroundAV; break; - case BATTLEGROUND_WS: bg = new BattleGroundWS; break; - case BATTLEGROUND_AB: bg = new BattleGroundAB; break; - case BATTLEGROUND_NA: bg = new BattleGroundNA; break; - case BATTLEGROUND_BE: bg = new BattleGroundBE; break; - case BATTLEGROUND_AA: bg = new BattleGroundAA; break; - case BATTLEGROUND_EY: bg = new BattleGroundEY; break; - case BATTLEGROUND_RL: bg = new BattleGroundRL; break; - case BATTLEGROUND_SA: bg = new BattleGroundSA; break; - case BATTLEGROUND_DS: bg = new BattleGroundDS; break; - case BATTLEGROUND_RV: bg = new BattleGroundRV; break; - case BATTLEGROUND_IC: bg = new BattleGroundIC; break; - case BATTLEGROUND_RB: bg = new BattleGroundRB; break; - default: - bg = new BattleGround; - break; - } - - bg->SetMapId(MapID); - bg->SetTypeID(bgTypeId); - bg->SetInstanceID(0); - bg->SetArenaorBGType(IsArena); - bg->SetMinPlayersPerTeam(MinPlayersPerTeam); - bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); - bg->SetMinPlayers(MinPlayersPerTeam * 2); - bg->SetMaxPlayers(MaxPlayersPerTeam * 2); - bg->SetName(BattleGroundName); - bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); - bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); - bg->SetLevelRange(LevelMin, LevelMax); - - // add bg to update list - AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg); - - // return some not-null value, bgTypeId is good enough for me - return bgTypeId; -} - -void BattleGroundMgr::CreateInitialBattleGrounds() -{ - float AStartLoc[4]; - float HStartLoc[4]; - uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2; - BattlemasterListEntry const *bl; - WorldSafeLocsEntry const *start; - bool IsArena; - - uint32 count = 0; - - // 0 1 2 3 4 5 6 7 8 - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template WHERE disable = 0"); - - if (!result) - { - barGoLink bar(1); - - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); - return; - } - - barGoLink bar(result->GetRowCount()); - - do - { - Field *fields = result->Fetch(); - bar.step(); - - uint32 bgTypeID_ = fields[0].GetUInt32(); - - // can be overwrite by values from DB - bl = sBattlemasterListStore.LookupEntry(bgTypeID_); - if (!bl) - { - sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_); - continue; - } - - BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_); - - IsArena = (bl->type == TYPE_ARENA); - MinPlayersPerTeam = fields[1].GetUInt32(); - MaxPlayersPerTeam = fields[2].GetUInt32(); - MinLvl = fields[3].GetUInt32(); - MaxLvl = fields[4].GetUInt32(); - //check values from DB - if (MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam) - { - MinPlayersPerTeam = 0; // by default now expected strong full bg requirement - MaxPlayersPerTeam = 40; - } - if (MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl) - { - //TO-DO: FIX ME - MinLvl = 0;//bl->minlvl; - MaxLvl = 80;//bl->maxlvl; - } - - start1 = fields[5].GetUInt32(); - - start = sWorldSafeLocsStore.LookupEntry(start1); - if (start) - { - AStartLoc[0] = start->x; - AStartLoc[1] = start->y; - AStartLoc[2] = start->z; - AStartLoc[3] = fields[6].GetFloat(); - } - else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_RB) - { - AStartLoc[0] = 0; - AStartLoc[1] = 0; - AStartLoc[2] = 0; - AStartLoc[3] = fields[6].GetFloat(); - } - else - { - sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1); - continue; - } - - start2 = fields[7].GetUInt32(); - - start = sWorldSafeLocsStore.LookupEntry(start2); - if (start) - { - HStartLoc[0] = start->x; - HStartLoc[1] = start->y; - HStartLoc[2] = start->z; - HStartLoc[3] = fields[8].GetFloat(); - } - else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_RB) - { - HStartLoc[0] = 0; - HStartLoc[1] = 0; - HStartLoc[2] = 0; - HStartLoc[3] = fields[8].GetFloat(); - } - else - { - sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2); - continue; - } - - //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl); - if (!CreateBattleGround(bgTypeID, IsArena, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) - continue; - - if (IsArena) - { - if (bgTypeID != BATTLEGROUND_AA) - m_EnabledArenas.push_back(bgTypeID); - } - else if (bgTypeID != BATTLEGROUND_RB) - m_EnabledBGs.push_back(bgTypeID); - ++count; - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u battlegrounds", count); -} - -void BattleGroundMgr::InitAutomaticArenaPointDistribution() -{ - if (!sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) - return; - - uint64 wstime = sWorld.getWorldState(WS_ARENA_DISTRIBUTION_TIME); - time_t curtime = time(NULL); - sLog.outDebug("Initializing Automatic Arena Point Distribution"); - if (wstime < curtime) - { - m_NextAutoDistributionTime = curtime; // reset will be called in the next update - sLog.outDebug("Battleground: Next arena point distribution time in the past, reseting it now."); - } - else - m_NextAutoDistributionTime = time_t(wstime); - sLog.outDebug("Automatic Arena Point Distribution initialized."); -} - -void BattleGroundMgr::DistributeArenaPoints() -{ - // used to distribute arena points based on last week's stats - sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START); - - sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START); - - //temporary structure for storing maximum points to add values for all players - std::map PlayerPoints; - - //at first update all points for all team members - for (ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr) - if (ArenaTeam * at = team_itr->second) - at->UpdateArenaPointsHelper(PlayerPoints); - - //cycle that gives points to all players - for (std::map::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr) - { - //update to database - CharacterDatabase.PExecute("UPDATE characters SET arenaPoints = arenaPoints + '%u' WHERE guid = '%u'", plr_itr->second, plr_itr->first); - - //add points to player if online - Player* pl = objmgr.GetPlayer(plr_itr->first); - if (pl) - pl->ModifyArenaPoints(plr_itr->second); - } - - PlayerPoints.clear(); - - sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END); - - sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START); - for (ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr) - { - if (ArenaTeam * at = titr->second) - { - at->FinishWeek(); // set played this week etc values to 0 in memory, too - at->SaveToDB(); // save changes - at->NotifyStatsChanged(); // notify the players of the changes - } - } - - sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END); - - sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END); -} - -void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId, uint8 fromWhere) -{ - if (!plr) - return; - - uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; - uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; - uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; - - win_kills = (uint32)Trinity::Honor::hk_honor_at_level(plr->getLevel(), win_kills); - loos_kills = (uint32)Trinity::Honor::hk_honor_at_level(plr->getLevel(), loos_kills); - - data->Initialize(SMSG_BATTLEFIELD_LIST); - *data << uint64(guid); // battlemaster guid - *data << uint8(fromWhere); // from where you joined - *data << uint32(bgTypeId); // battleground id - *data << uint8(0); // unk - *data << uint8(0); // unk - - // Rewards - *data << uint8(plr->GetRandomWinner()); // 3.3.3 hasWin - *data << uint32(win_kills); // 3.3.3 winHonor - *data << uint32(win_arena); // 3.3.3 winArena - *data << uint32(loos_kills); // 3.3.3 lossHonor - - uint8 isRandom = bgTypeId == BATTLEGROUND_RB; - - *data << uint8(isRandom); // 3.3.3 isRandom - if (isRandom) - { - // Rewards (random) - *data << uint8(plr->GetRandomWinner()); // 3.3.3 hasWin_Random - *data << uint32(win_kills); // 3.3.3 winHonor_Random - *data << uint32(win_arena); // 3.3.3 winArena_Random - *data << uint32(loos_kills); // 3.3.3 lossHonor_Random - } - - if (bgTypeId == BATTLEGROUND_AA) // arena - { - *data << uint32(0); // unk (count?) - } - else // battleground - { - size_t count_pos = data->wpos(); - uint32 count = 0; - *data << uint32(0); // number of bg instances - - if (BattleGround* bgTemplate = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId)) - { - // expected bracket entry - if (PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgTemplate->GetMapId(),plr->getLevel())) - { - BattleGroundBracketId bracketId = bracketEntry->GetBracketId(); - for (std::set::iterator itr = m_ClientBattleGroundIds[bgTypeId][bracketId].begin(); itr != m_ClientBattleGroundIds[bgTypeId][bracketId].end();++itr) - { - *data << uint32(*itr); - ++count; - } - data->put(count_pos , count); - } - } - } -} - -void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGroundTypeId bgTypeId) -{ - BattleGround *bg = GetBattleGround(instanceId, bgTypeId); - if (bg) - { - uint32 mapid = bg->GetMapId(); - float x, y, z, O; - uint32 team = pl->GetBGTeam(); - if (team == 0) - team = pl->GetTeam(); - bg->GetTeamStartLoc(team, x, y, z, O); - - sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O); - pl->TeleportTo(mapid, x, y, z, O); - } - else - { - sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId); - } -} - -void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid) -{ - WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); - uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds - if (time_ == uint32(-1)) - time_ = 0; - data << guid << time_; - pl->GetSession()->SendPacket(&data); -} - -bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId) -{ - return (bgTypeId == BATTLEGROUND_AA || - bgTypeId == BATTLEGROUND_BE || - bgTypeId == BATTLEGROUND_NA || - bgTypeId == BATTLEGROUND_DS || - bgTypeId == BATTLEGROUND_RV || - bgTypeId == BATTLEGROUND_RL || - bgTypeId == BATTLEGROUND_DS); -} - -BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType) -{ - switch(bgTypeId) - { - case BATTLEGROUND_WS: - return BATTLEGROUND_QUEUE_WS; - case BATTLEGROUND_AB: - return BATTLEGROUND_QUEUE_AB; - case BATTLEGROUND_AV: - return BATTLEGROUND_QUEUE_AV; - case BATTLEGROUND_EY: - return BATTLEGROUND_QUEUE_EY; - case BATTLEGROUND_SA: - return BATTLEGROUND_QUEUE_SA; - case BATTLEGROUND_IC: - return BATTLEGROUND_QUEUE_IC; - case BATTLEGROUND_RB: - return BATTLEGROUND_QUEUE_RB; - case BATTLEGROUND_AA: - case BATTLEGROUND_NA: - case BATTLEGROUND_RL: - case BATTLEGROUND_BE: - case BATTLEGROUND_DS: - case BATTLEGROUND_RV: - switch(arenaType) - { - case ARENA_TYPE_2v2: - return BATTLEGROUND_QUEUE_2v2; - case ARENA_TYPE_3v3: - return BATTLEGROUND_QUEUE_3v3; - case ARENA_TYPE_5v5: - return BATTLEGROUND_QUEUE_5v5; - default: - return BATTLEGROUND_QUEUE_NONE; - } - default: - return BATTLEGROUND_QUEUE_NONE; - } -} - -BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId) -{ - switch(bgQueueTypeId) - { - case BATTLEGROUND_QUEUE_WS: - return BATTLEGROUND_WS; - case BATTLEGROUND_QUEUE_AB: - return BATTLEGROUND_AB; - case BATTLEGROUND_QUEUE_AV: - return BATTLEGROUND_AV; - case BATTLEGROUND_QUEUE_EY: - return BATTLEGROUND_EY; - case BATTLEGROUND_QUEUE_SA: - return BATTLEGROUND_SA; - case BATTLEGROUND_QUEUE_IC: - return BATTLEGROUND_IC; - case BATTLEGROUND_QUEUE_RB: - return BATTLEGROUND_RB; - case BATTLEGROUND_QUEUE_2v2: - case BATTLEGROUND_QUEUE_3v3: - case BATTLEGROUND_QUEUE_5v5: - return BATTLEGROUND_AA; - default: - return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing) - } -} - -uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId) -{ - switch(bgQueueTypeId) - { - case BATTLEGROUND_QUEUE_2v2: - return ARENA_TYPE_2v2; - case BATTLEGROUND_QUEUE_3v3: - return ARENA_TYPE_3v3; - case BATTLEGROUND_QUEUE_5v5: - return ARENA_TYPE_5v5; - default: - return 0; - } -} - -void BattleGroundMgr::ToggleTesting() -{ - m_Testing = !m_Testing; - if (m_Testing) - sWorld.SendWorldText(LANG_DEBUG_BG_ON); - else - sWorld.SendWorldText(LANG_DEBUG_BG_OFF); -} - -void BattleGroundMgr::ToggleArenaTesting() -{ - m_ArenaTesting = !m_ArenaTesting; - if (m_ArenaTesting) - sWorld.SendWorldText(LANG_DEBUG_ARENA_ON); - else - sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF); -} - -void BattleGroundMgr::SetHolidayWeekends(uint32 mask) -{ - for (uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype) - { - if (BattleGround * bg = GetBattleGroundTemplate(BattleGroundTypeId(bgtype))) - { - bg->SetHoliday(mask & (1 << bgtype)); - } - } -} - -void BattleGroundMgr::ScheduleQueueUpdate(uint32 arenaRating, uint8 arenaType, BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id) -{ - //This method must be atomic, TODO add mutex - //we will use only 1 number created of bgTypeId and bracket_id - uint64 schedule_id = ((uint64)arenaRating << 32) | (arenaType << 24) | (bgQueueTypeId << 16) | (bgTypeId << 8) | bracket_id; - bool found = false; - for (uint8 i = 0; i < m_QueueUpdateScheduler.size(); i++) - { - if (m_QueueUpdateScheduler[i] == schedule_id) - { - found = true; - break; - } - } - if (!found) - m_QueueUpdateScheduler.push_back(schedule_id); -} - -uint32 BattleGroundMgr::GetMaxRatingDifference() const -{ - // this is for stupid people who can't use brain and set max rating difference to 0 - uint32 diff = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); - if (diff == 0) - diff = 5000; - return diff; -} - -uint32 BattleGroundMgr::GetRatingDiscardTimer() const -{ - return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); -} - -uint32 BattleGroundMgr::GetPrematureFinishTime() const -{ - return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); -} - -void BattleGroundMgr::LoadBattleMastersEntry() -{ - mBattleMastersMap.clear(); // need for reload case - - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,bg_template FROM battlemaster_entry"); - - uint32 count = 0; - - if (!result) - { - barGoLink bar(1); - bar.step(); - - sLog.outString(); - sLog.outString(">> Loaded 0 battlemaster entries - table is empty!"); - return; - } - - barGoLink bar(result->GetRowCount()); - - do - { - ++count; - bar.step(); - - Field *fields = result->Fetch(); - - uint32 entry = fields[0].GetUInt32(); - uint32 bgTypeId = fields[1].GetUInt32(); - if (!sBattlemasterListStore.LookupEntry(bgTypeId)) - { - sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId); - continue; - } - - mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId); - - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u battlemaster entries", count); -} - -HolidayIds BattleGroundMgr::BGTypeToWeekendHolidayId(BattleGroundTypeId bgTypeId) -{ - switch (bgTypeId) - { - case BATTLEGROUND_AV: return HOLIDAY_CALL_TO_ARMS_AV; - case BATTLEGROUND_EY: return HOLIDAY_CALL_TO_ARMS_EY; - case BATTLEGROUND_WS: return HOLIDAY_CALL_TO_ARMS_WS; - case BATTLEGROUND_SA: return HOLIDAY_CALL_TO_ARMS_SA; - default: return HOLIDAY_NONE; - } -} - -BattleGroundTypeId BattleGroundMgr::WeekendHolidayIdToBGType(HolidayIds holiday) -{ - switch (holiday) - { - case HOLIDAY_CALL_TO_ARMS_AV: return BATTLEGROUND_AV; - case HOLIDAY_CALL_TO_ARMS_EY: return BATTLEGROUND_EY; - case HOLIDAY_CALL_TO_ARMS_WS: return BATTLEGROUND_WS; - case HOLIDAY_CALL_TO_ARMS_SA: return BATTLEGROUND_SA; - default: return BATTLEGROUND_TYPE_NONE; - } -} - -bool BattleGroundMgr::IsBGWeekend(BattleGroundTypeId bgTypeId) -{ - return IsHolidayActive(BGTypeToWeekendHolidayId(bgTypeId)); -} - -void BattleGroundMgr::DoCompleteAchievement(uint32 achievement, Player * player) -{ - AchievementEntry const* AE = GetAchievementStore()->LookupEntry(achievement); - - if (!player) - { - //Map::PlayerList const &PlayerList = this->GetPlayers(); - //GroupsQueueType::iterator group = SelectedGroups.begin(); - - //if (!PlayerList.isEmpty()) - //for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - // for (GroupsQueueType::iterator itr = group; itr != SelectedGroups.end(); ++itr) - // if (Player *pPlayer = itr->getSource()) - // pPlayer->CompletedAchievement(AE); - } - else - { - player->CompletedAchievement(AE); - } -} diff --git a/src/server/game/BattleGroundMgr.h b/src/server/game/BattleGroundMgr.h deleted file mode 100644 index 569651a95b3..00000000000 --- a/src/server/game/BattleGroundMgr.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDMGR_H -#define __BATTLEGROUNDMGR_H - -#include "Common.h" -#include "Policies/Singleton.h" - -#include "DBCEnums.h" -#include "BattleGround.h" - -typedef std::map BattleGroundSet; - -//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears -typedef std::list BGFreeSlotQueueType; - -typedef UNORDERED_MAP BattleMastersMap; - -#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day -#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10 -#define WS_ARENA_DISTRIBUTION_TIME 20001 // Custom worldstate - -struct GroupQueueInfo; // type predefinition -struct PlayerQueueInfo // stores information for players in queue -{ - uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes - GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo -}; - -struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) -{ - std::map Players; // player queue info map - uint32 Team; // Player team (ALLIANCE/HORDE) - BattleGroundTypeId BgTypeId; // battleground type id - bool IsRated; // rated - uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG - uint32 ArenaTeamId; // team id if rated match - uint32 JoinTime; // time when group was added - uint32 RemoveInviteTime; // time when we will remove invite for players in group - uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG - uint32 ArenaTeamRating; // if rated match, inited to the rating of the team - uint32 OpponentsTeamRating; // for rated arena matches -}; - -enum BattleGroundQueueGroupTypes -{ - BG_QUEUE_PREMADE_ALLIANCE = 0, - BG_QUEUE_PREMADE_HORDE = 1, - BG_QUEUE_NORMAL_ALLIANCE = 2, - BG_QUEUE_NORMAL_HORDE = 3 -}; -#define BG_QUEUE_GROUP_TYPES_COUNT 4 - -class BattleGround; -class BattleGroundQueue -{ - public: - BattleGroundQueue(); - ~BattleGroundQueue(); - - void Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType = 0, bool isRated = false, uint32 minRating = 0); - - void FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id); - bool CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); - bool CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); - bool CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam); - GroupQueueInfo * AddGroup(Player* leader, Group* group, BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 ArenaRating, uint32 ArenaTeamId = 0); - void RemovePlayer(const uint64& guid, bool decreaseInvitedCount); - bool IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime); - bool GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo); - void PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); - uint32 GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); - - typedef std::map QueuedPlayersMap; - QueuedPlayersMap m_QueuedPlayers; - - //we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well - typedef std::list GroupsQueueType; - - /* - This two dimensional array is used to store All queued groups - First dimension specifies the bgTypeId - Second dimension specifies the player's group types - - BG_QUEUE_PREMADE_ALLIANCE is used for premade alliance groups and alliance rated arena teams - BG_QUEUE_PREMADE_HORDE is used for premade horde groups and horde rated arena teams - BG_QUEUE_NORMAL_ALLIANCE is used for normal (or small) alliance groups or non-rated arena matches - BG_QUEUE_NORMAL_HORDE is used for normal (or small) horde groups or non-rated arena matches - */ - GroupsQueueType m_QueuedGroups[MAX_BATTLEGROUND_BRACKETS][BG_QUEUE_GROUP_TYPES_COUNT]; - - // class to select and invite groups to bg - class SelectionPool - { - public: - void Init(); - bool AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount); - bool KickGroup(uint32 size); - uint32 GetPlayerCount() const {return PlayerCount;} - public: - GroupsQueueType SelectedGroups; - private: - uint32 PlayerCount; - }; - - //one selection pool for horde, other one for alliance - SelectionPool m_SelectionPools[BG_TEAMS_COUNT]; - - private: - - bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); - uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; - uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; - uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; -}; - -/* - 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 -{ - public: - BGQueueInviteEvent(const uint64& pl_guid, uint32 BgInstanceGUID, BattleGroundTypeId BgTypeId, uint8 arenaType, uint32 removeTime) : - m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID), m_BgTypeId(BgTypeId), m_ArenaType(arenaType), m_RemoveTime(removeTime) - { - }; - virtual ~BGQueueInviteEvent() {}; - - virtual bool Execute(uint64 e_time, uint32 p_time); - virtual void Abort(uint64 e_time); - private: - uint64 m_PlayerGuid; - uint32 m_BgInstanceGUID; - BattleGroundTypeId m_BgTypeId; - uint8 m_ArenaType; - uint32 m_RemoveTime; -}; - -/* - This class is used to remove player from BG queue after 1 minute 20 seconds from first invitation - 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 -{ - public: - BGQueueRemoveEvent(const uint64& pl_guid, uint32 bgInstanceGUID, BattleGroundTypeId BgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 removeTime) - : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId) - {} - - virtual ~BGQueueRemoveEvent() {} - - virtual bool Execute(uint64 e_time, uint32 p_time); - virtual void Abort(uint64 e_time); - private: - uint64 m_PlayerGuid; - uint32 m_BgInstanceGUID; - uint32 m_RemoveTime; - BattleGroundTypeId m_BgTypeId; - BattleGroundQueueTypeId m_BgQueueTypeId; -}; - -class BattleGroundMgr -{ - public: - /* Construction */ - BattleGroundMgr(); - ~BattleGroundMgr(); - void Update(uint32 diff); - - /* Packet Building */ - void BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr); - void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid); - void BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player *plr, BattleGroundTypeId bgTypeId, uint8 fromWhere); - void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, GroupJoinBattlegroundResult result); - void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); - void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); - void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype); - void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); - void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid); - - /* Battlegrounds */ - BattleGround* GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId); - BattleGround* GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId); //there must be uint32 because MAX_BATTLEGROUND_TYPE_ID means unknown - - BattleGround* GetBattleGroundTemplate(BattleGroundTypeId bgTypeId); - BattleGround* CreateNewBattleGround(BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated); - - uint32 CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO); - - void AddBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId, BattleGround* BG) { m_BattleGrounds[bgTypeId][InstanceID] = BG; }; - void RemoveBattleGround(uint32 instanceID, BattleGroundTypeId bgTypeId) { m_BattleGrounds[bgTypeId].erase(instanceID); } - uint32 CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id); - - void CreateInitialBattleGrounds(); - void DeleteAllBattleGrounds(); - - void SendToBattleGround(Player *pl, uint32 InstanceID, BattleGroundTypeId bgTypeId); - - /* Battleground queues */ - //these queues are instantiated when creating BattlegroundMrg - BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; // public, because we need to access them in BG handler code - - BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPE_ID]; - - void ScheduleQueueUpdate(uint32 arenaRating, uint8 arenaType, BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id); - uint32 GetMaxRatingDifference() const; - uint32 GetRatingDiscardTimer() const; - uint32 GetPrematureFinishTime() const; - - void InitAutomaticArenaPointDistribution(); - void DistributeArenaPoints(); - void ToggleArenaTesting(); - void ToggleTesting(); - - void SetHolidayWeekends(uint32 mask); - void LoadBattleMastersEntry(); - BattleGroundTypeId GetBattleMasterBG(uint32 entry) const - { - BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry); - if (itr != mBattleMastersMap.end()) - return itr->second; - return BATTLEGROUND_WS; - } - - bool isArenaTesting() const { return m_ArenaTesting; } - bool isTesting() const { return m_Testing; } - - static bool IsArenaType(BattleGroundTypeId bgTypeId); - static bool IsBattleGroundType(BattleGroundTypeId bgTypeId) { return !BattleGroundMgr::IsArenaType(bgTypeId); } - static BattleGroundQueueTypeId BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType); - static BattleGroundTypeId BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId); - static uint8 BGArenaType(BattleGroundQueueTypeId bgQueueTypeId); - - static HolidayIds BGTypeToWeekendHolidayId(BattleGroundTypeId bgTypeId); - static BattleGroundTypeId WeekendHolidayIdToBGType(HolidayIds holiday); - static bool IsBGWeekend(BattleGroundTypeId bgTypeId); - void DoCompleteAchievement(uint32 achievement, Player * player = NULL); - private: - BattleMastersMap mBattleMastersMap; - - typedef std::vector BattleGroundTypeIdList; - /* Battlegrounds */ - BattleGroundSet m_BattleGrounds[MAX_BATTLEGROUND_TYPE_ID]; - BattleGroundTypeIdList m_EnabledArenas; - BattleGroundTypeIdList m_EnabledBGs; - std::vector m_QueueUpdateScheduler; - std::set m_ClientBattleGroundIds[MAX_BATTLEGROUND_TYPE_ID][MAX_BATTLEGROUND_BRACKETS]; //the instanceids just visible for the client - uint32 m_NextRatingDiscardUpdate; - time_t m_NextAutoDistributionTime; - uint32 m_AutoDistributionTimeChecker; - bool m_ArenaTesting; - bool m_Testing; -}; - -#define sBattleGroundMgr Trinity::Singleton::Instance() -#endif - diff --git a/src/server/game/BattleGroundNA.cpp b/src/server/game/BattleGroundNA.cpp deleted file mode 100644 index 793cf13b3cb..00000000000 --- a/src/server/game/BattleGroundNA.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundNA.h" -#include "Language.h" -#include "Object.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "WorldPacket.h" - -BattleGroundNA::BattleGroundNA() -{ - m_BgObjects.resize(BG_NA_OBJECT_MAX); - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set messageIds - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; -} - -BattleGroundNA::~BattleGroundNA() -{ - -} - -void BattleGroundNA::Update(uint32 diff) -{ - BattleGround::Update(diff); - - /*if (GetStatus() == STATUS_IN_PROGRESS) - { - // update something - }*/ -} - -void BattleGroundNA::StartingEventCloseDoors() -{ - for (uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); -} - -void BattleGroundNA::StartingEventOpenDoors() -{ - for (uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; ++i) - DoorOpen(i); - - for (uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; ++i) - SpawnBGObject(i, 60); -} - -void BattleGroundNA::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundNAScore* sc = new BattleGroundNAScore; - - m_PlayerScores[plr->GetGUID()] = sc; - - UpdateArenaWorldState(); -} - -void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) -{ - if (GetStatus() == STATUS_WAIT_LEAVE) - return; - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - if (!killer) - { - sLog.outError("BattleGroundNA: Killer player not found"); - return; - } - - BattleGround::HandleKillPlayer(player,killer); - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -bool BattleGroundNA::HandlePlayerUnderMap(Player *player) -{ - player->TeleportTo(GetMapId(),4055.504395,2919.660645,13.611241,player->GetOrientation(),false); - return true; -} - -void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - //uint32 SpellId = 0; - //uint64 buff_guid = 0; - switch(Trigger) - { - case 4536: // buff trigger? - case 4537: // buff trigger? - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } - - //if (buff_guid) - // HandleTriggerBuff(buff_guid,Source); -} - -void BattleGroundNA::FillInitialWorldStates(WorldPacket &data) -{ - data << uint32(0xa11) << uint32(1); // 9 - UpdateArenaWorldState(); -} - -void BattleGroundNA::Reset() -{ - //call parent's class reset - BattleGround::Reset(); -} - -bool BattleGroundNA::SetupBattleGround() -{ - // gates - if (!AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854, 2966.833, 12.6462, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179, 2874.97, 12.39171, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709, 2981.777, 10.70117, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) - || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064, 2858.438, 10.23631, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) - // buffs - || !AddObject(BG_NA_OBJECT_BUFF_1, BG_NA_OBJECT_TYPE_BUFF_1, 4009.189941, 2895.250000, 13.052700, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) - || !AddObject(BG_NA_OBJECT_BUFF_2, BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078, 2946.350098, 13.051300, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) - { - sLog.outErrorDb("BatteGroundNA: Failed to spawn some object!"); - return false; - } - - return true; -} - -/* -20:12:14 id:036668 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86 -0000: 2f 02 00 00 72 0e 00 00 00 00 00 00 09 00 11 0a | /...r........... -0010: 00 00 01 00 00 00 0f 0a 00 00 00 00 00 00 10 0a | ................ -0020: 00 00 00 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................ -0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................ -0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................ -0050: 00 00 00 00 00 00 | ...... -*/ diff --git a/src/server/game/BattleGroundNA.h b/src/server/game/BattleGroundNA.h deleted file mode 100644 index a11d311515d..00000000000 --- a/src/server/game/BattleGroundNA.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDNA_H -#define __BATTLEGROUNDNA_H - -class BattleGround; - -enum BattleGroundNAObjectTypes -{ - BG_NA_OBJECT_DOOR_1 = 0, - BG_NA_OBJECT_DOOR_2 = 1, - BG_NA_OBJECT_DOOR_3 = 2, - BG_NA_OBJECT_DOOR_4 = 3, - BG_NA_OBJECT_BUFF_1 = 4, - BG_NA_OBJECT_BUFF_2 = 5, - BG_NA_OBJECT_MAX = 6 -}; - -enum BattleGroundNAObjects -{ - BG_NA_OBJECT_TYPE_DOOR_1 = 183978, - BG_NA_OBJECT_TYPE_DOOR_2 = 183980, - BG_NA_OBJECT_TYPE_DOOR_3 = 183977, - BG_NA_OBJECT_TYPE_DOOR_4 = 183979, - BG_NA_OBJECT_TYPE_BUFF_1 = 184663, - BG_NA_OBJECT_TYPE_BUFF_2 = 184664 -}; - -class BattleGroundNAScore : public BattleGroundScore -{ - public: - BattleGroundNAScore() {}; - virtual ~BattleGroundNAScore() {}; - //TODO fix me -}; - -class BattleGroundNA : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundNA(); - ~BattleGroundNA(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - virtual void Reset(); - virtual void FillInitialWorldStates(WorldPacket &d); - void HandleKillPlayer(Player* player, Player *killer); - bool HandlePlayerUnderMap(Player * plr); -}; -#endif diff --git a/src/server/game/BattleGroundRB.cpp b/src/server/game/BattleGroundRB.cpp deleted file mode 100644 index cf22154ed11..00000000000 --- a/src/server/game/BattleGroundRB.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Player.h" -#include "BattleGround.h" -#include "BattleGroundRB.h" -#include "Language.h" - -BattleGroundRB::BattleGroundRB() -{ - //TODO FIX ME! - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = 0; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; -} - -BattleGroundRB::~BattleGroundRB() -{ - -} - -void BattleGroundRB::Update(uint32 diff) -{ - BattleGround::Update(diff); -} - -void BattleGroundRB::StartingEventCloseDoors() -{ -} - -void BattleGroundRB::StartingEventOpenDoors() -{ -} - -void BattleGroundRB::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundRBScore* sc = new BattleGroundRBScore; - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundRB::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) -{ -} - -void BattleGroundRB::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; -} - -void BattleGroundRB::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); - - if (itr == m_PlayerScores.end()) // player not found... - return; - - BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); -} diff --git a/src/server/game/BattleGroundRB.h b/src/server/game/BattleGroundRB.h deleted file mode 100644 index a40ade5adfe..00000000000 --- a/src/server/game/BattleGroundRB.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDRB_H -#define __BATTLEGROUNDRB_H - -class BattleGround; - -class BattleGroundRBScore : public BattleGroundScore -{ - public: - BattleGroundRBScore() {}; - virtual ~BattleGroundRBScore() {}; -}; - -class BattleGroundRB : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundRB(); - ~BattleGroundRB(); - void Update(uint32 diff); - - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr,uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - - /* Scorekeeping */ - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - - private: -}; -#endif diff --git a/src/server/game/BattleGroundRL.cpp b/src/server/game/BattleGroundRL.cpp deleted file mode 100644 index ef2ec3cfa94..00000000000 --- a/src/server/game/BattleGroundRL.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundRL.h" -#include "Language.h" -#include "Object.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "WorldPacket.h" - -BattleGroundRL::BattleGroundRL() -{ - m_BgObjects.resize(BG_RL_OBJECT_MAX); - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set messageIds - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; -} - -BattleGroundRL::~BattleGroundRL() -{ - -} - -void BattleGroundRL::Update(uint32 diff) -{ - BattleGround::Update(diff); - - /*if (GetStatus() == STATUS_IN_PROGRESS) - { - // update something - }*/ -} - -void BattleGroundRL::StartingEventCloseDoors() -{ - for (uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); -} - -void BattleGroundRL::StartingEventOpenDoors() -{ - for (uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; ++i) - DoorOpen(i); - - for (uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; ++i) - SpawnBGObject(i, 60); -} - -void BattleGroundRL::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundRLScore* sc = new BattleGroundRLScore; - - m_PlayerScores[plr->GetGUID()] = sc; - - UpdateArenaWorldState(); -} - -void BattleGroundRL::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) -{ - if (GetStatus() == STATUS_WAIT_LEAVE) - return; - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - if (!killer) - { - sLog.outError("Killer player not found"); - return; - } - - BattleGround::HandleKillPlayer(player,killer); - - UpdateArenaWorldState(); - CheckArenaWinConditions(); -} - -bool BattleGroundRL::HandlePlayerUnderMap(Player *player) -{ - player->TeleportTo(GetMapId(),1285.810547,1667.896851,39.957642,player->GetOrientation(),false); - return true; -} - -void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - //uint32 SpellId = 0; - //uint64 buff_guid = 0; - switch(Trigger) - { - case 4696: // buff trigger? - case 4697: // buff trigger? - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } - - //if (buff_guid) - // HandleTriggerBuff(buff_guid,Source); -} - -void BattleGroundRL::FillInitialWorldStates(WorldPacket &data) -{ - data << uint32(0xbba) << uint32(1); // 9 - UpdateArenaWorldState(); -} - -void BattleGroundRL::Reset() -{ - //call parent's reset - BattleGround::Reset(); -} - -bool BattleGroundRL::SetupBattleGround() -{ - // gates - if (!AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561, 1601.938, 31.60557, -1.457349, 0, 0, -0.6658813, 0.7460576, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648, 1730.557, 31.60557, 1.684245, 0, 0, 0.7460582, 0.6658807, RESPAWN_IMMEDIATELY) - // buffs - || !AddObject(BG_RL_OBJECT_BUFF_1, BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971, 1632.719971, 36.730400, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) - || !AddObject(BG_RL_OBJECT_BUFF_2, BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049, 1699.170044, 34.872601, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) - { - sLog.outErrorDb("BatteGroundRL: Failed to spawn some object!"); - return false; - } - - return true; -} - -/* -Packet S->C, id 600, SMSG_INIT_WORLD_STATES (706), len 86 -0000: 3C 02 00 00 80 0F 00 00 00 00 00 00 09 00 BA 0B | <............... -0010: 00 00 01 00 00 00 B9 0B 00 00 02 00 00 00 B8 0B | ................ -0020: 00 00 00 00 00 00 D8 08 00 00 00 00 00 00 D7 08 | ................ -0030: 00 00 00 00 00 00 D6 08 00 00 00 00 00 00 D5 08 | ................ -0040: 00 00 00 00 00 00 D3 08 00 00 00 00 00 00 D4 08 | ................ -0050: 00 00 00 00 00 00 | ...... -*/ diff --git a/src/server/game/BattleGroundRL.h b/src/server/game/BattleGroundRL.h deleted file mode 100644 index 3b4c10a7c9a..00000000000 --- a/src/server/game/BattleGroundRL.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDRL_H -#define __BATTLEGROUNDRL_H - -class BattleGround; - -enum BattleGroundRLObjectTypes -{ - BG_RL_OBJECT_DOOR_1 = 0, - BG_RL_OBJECT_DOOR_2 = 1, - BG_RL_OBJECT_BUFF_1 = 2, - BG_RL_OBJECT_BUFF_2 = 3, - BG_RL_OBJECT_MAX = 4 -}; - -enum BattleGroundRLObjects -{ - BG_RL_OBJECT_TYPE_DOOR_1 = 185918, - BG_RL_OBJECT_TYPE_DOOR_2 = 185917, - BG_RL_OBJECT_TYPE_BUFF_1 = 184663, - BG_RL_OBJECT_TYPE_BUFF_2 = 184664 -}; - -class BattleGroundRLScore : public BattleGroundScore -{ - public: - BattleGroundRLScore() {}; - virtual ~BattleGroundRLScore() {}; - //TODO fix me -}; - -class BattleGroundRL : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundRL(); - ~BattleGroundRL(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void Reset(); - virtual void FillInitialWorldStates(WorldPacket &d); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - void HandleKillPlayer(Player* player, Player *killer); - bool HandlePlayerUnderMap(Player * plr); -}; -#endif diff --git a/src/server/game/BattleGroundRV.cpp b/src/server/game/BattleGroundRV.cpp deleted file mode 100644 index fcc53dbbcf9..00000000000 --- a/src/server/game/BattleGroundRV.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundRV.h" -#include "ObjectAccessor.h" -#include "Language.h" -#include "Player.h" -#include "WorldPacket.h" -#include "GameObject.h" - -BattleGroundRV::BattleGroundRV() -{ - m_BgObjects.resize(BG_RV_OBJECT_MAX); - - m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; - m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; - m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; - m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; - //we must set messageIds - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; -} - -BattleGroundRV::~BattleGroundRV() -{ - -} - -void BattleGroundRV::Update(uint32 diff) -{ - BattleGround::Update(diff); - - if (getTimer() < diff) - { - uint32 i; - switch(getState()) - { - case BG_RV_STATE_OPEN_FENCES: - { - setTimer(BG_RV_PILAR_TO_FIRE_TIMER); - setState(BG_RV_STATE_CLOSE_FIRE); - break; - } - case BG_RV_STATE_CLOSE_FIRE: - for (i = BG_RV_OBJECT_FIRE_1; i <= BG_RV_OBJECT_FIREDOOR_2; ++i) - DoorClose(i); - setTimer(BG_RV_FIRE_TO_PILAR_TIMER); - setState(BG_RV_STATE_OPEN_PILARS); - break; - case BG_RV_STATE_OPEN_PILARS: - for (i = BG_RV_OBJECT_PILAR_1; i <= BG_RV_OBJECT_PULLEY_2; ++i) - DoorOpen(i); - setTimer(BG_RV_PILAR_TO_FIRE_TIMER); - setState(BG_RV_STATE_OPEN_FIRE); - break; - case BG_RV_STATE_OPEN_FIRE: - for (i = BG_RV_OBJECT_FIRE_1; i <= BG_RV_OBJECT_FIREDOOR_2; ++i) - DoorOpen(i); - setTimer(BG_RV_FIRE_TO_PILAR_TIMER); - setState(BG_RV_STATE_CLOSE_PILARS); - break; - case BG_RV_STATE_CLOSE_PILARS: - uint32 i; - for (i = BG_RV_OBJECT_PILAR_1; i <= BG_RV_OBJECT_PULLEY_2; ++i) - DoorOpen(i); - setTimer(BG_RV_PILAR_TO_FIRE_TIMER); - setState(BG_RV_STATE_CLOSE_FIRE); - break; - } - } - else - setTimer(getTimer() - diff); -} - -void BattleGroundRV::StartingEventCloseDoors() -{ -} - -void BattleGroundRV::StartingEventOpenDoors() -{ - // Buff respawn - SpawnBGObject(BG_RV_OBJECT_BUFF_1, 90); - SpawnBGObject(BG_RV_OBJECT_BUFF_2, 90); - // Open fences - DoorOpen(BG_RV_OBJECT_FENCE_1); - DoorOpen(BG_RV_OBJECT_FENCE_2); - // Elevators - DoorOpen(BG_RV_OBJECT_ELEVATOR_1); - DoorOpen(BG_RV_OBJECT_ELEVATOR_2); - - setState(BG_RV_STATE_OPEN_FENCES); - setTimer(BG_RV_FIRST_TIMER); -} - -void BattleGroundRV::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundRVScore* sc = new BattleGroundRVScore; - - m_PlayerScores[plr->GetGUID()] = sc; - - UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); - UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); -} - -void BattleGroundRV::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) -{ - if (GetStatus() == STATUS_WAIT_LEAVE) - return; - - UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); - UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); - - CheckArenaWinConditions(); -} - -void BattleGroundRV::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - if (!killer) - { - sLog.outError("BattleGroundRV: Killer player not found"); - return; - } - - BattleGround::HandleKillPlayer(player, killer); - - UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); - UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); - - CheckArenaWinConditions(); -} - -bool BattleGroundRV::HandlePlayerUnderMap(Player *player) -{ - player->TeleportTo(GetMapId(), 763.5, -284, 28.276, 2.422, false); - return true; -} - - -void BattleGroundRV::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - switch(Trigger) - { - case 5224: - case 5226: - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } -} - -void BattleGroundRV::FillInitialWorldStates(WorldPacket &data) -{ - data << uint32(BG_RV_WORLD_STATE_A) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); - data << uint32(BG_RV_WORLD_STATE_H) << uint32(GetAlivePlayersCountByTeam(HORDE)); - data << uint32(BG_RV_WORLD_STATE) << uint32(1); -} - -void BattleGroundRV::Reset() -{ - //call parent's class reset - BattleGround::Reset(); -} - -bool BattleGroundRV::SetupBattleGround() -{ - // Fence - if (!AddObject(BG_RV_OBJECT_FENCE_1, BG_RV_OBJECT_TYPE_FENCE_1, 763.432373, -274.058197, 28.276695, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_FENCE_2, BG_RV_OBJECT_TYPE_FENCE_2, 763.432373, -294.419464, 28.276684, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - // elevators - || !AddObject(BG_RV_OBJECT_ELEVATOR_1, BG_RV_OBJECT_TYPE_ELEVATOR_1, 763.536377, -294.535767, 0.505383, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_ELEVATOR_2, BG_RV_OBJECT_TYPE_ELEVATOR_2, 763.506348, -273.873352, 0.505383, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - // buffs - || !AddObject(BG_RV_OBJECT_BUFF_1, BG_RV_OBJECT_TYPE_BUFF_1, 735.551819, -284.794678, 28.276682, 0.034906, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_BUFF_2, BG_RV_OBJECT_TYPE_BUFF_2, 791.224487, -284.794464, 28.276682, 2.600535, 0, 0, 0, RESPAWN_IMMEDIATELY) - // fire - || !AddObject(BG_RV_OBJECT_FIRE_1, BG_RV_OBJECT_TYPE_FIRE_1, 743.543457, -283.799469, 28.286655, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_FIRE_2, BG_RV_OBJECT_TYPE_FIRE_2, 782.971802, -283.799469, 28.286655, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_FIREDOOR_1, BG_RV_OBJECT_TYPE_FIREDOOR_1, 743.711060, -284.099609, 27.542587, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_FIREDOOR_2, BG_RV_OBJECT_TYPE_FIREDOOR_2, 783.221252, -284.133362, 27.535686, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - // Gear - || !AddObject(BG_RV_OBJECT_GEAR_1, BG_RV_OBJECT_TYPE_GEAR_1, 763.664551, -261.872986, 26.686588, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_GEAR_2, BG_RV_OBJECT_TYPE_GEAR_2, 763.578979, -306.146149, 26.665222, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - // Pulley - || !AddObject(BG_RV_OBJECT_PULLEY_1, BG_RV_OBJECT_TYPE_PULLEY_1, 700.722290, -283.990662, 39.517582, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PULLEY_2, BG_RV_OBJECT_TYPE_PULLEY_2, 826.303833, -283.996429, 39.517582, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - // Pilars - || !AddObject(BG_RV_OBJECT_PILAR_1, BG_RV_OBJECT_TYPE_PILAR_1, 763.632385, -306.162384, 25.909504, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PILAR_2, BG_RV_OBJECT_TYPE_PILAR_2, 723.644287, -284.493256, 24.648525, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PILAR_3, BG_RV_OBJECT_TYPE_PILAR_3, 763.611145, -261.856750, 25.909504, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PILAR_4, BG_RV_OBJECT_TYPE_PILAR_4, 802.211609, -284.493256, 24.648525, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) -/* - // Pilars Collision - Fixme: Use the collision pilars - should make u break LoS - || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_1, BG_RV_OBJECT_TYPE_PILAR_COLLISION_1, 763.632385, -306.162384, 30.639660, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_2, BG_RV_OBJECT_TYPE_PILAR_COLLISION_2, 723.644287, -284.493256, 32.382710, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_3, BG_RV_OBJECT_TYPE_PILAR_COLLISION_3, 763.611145, -261.856750, 30.639660, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) - || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_4, BG_RV_OBJECT_TYPE_PILAR_COLLISION_4, 802.211609, -284.493256, 32.382710, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) -*/ -) - { - sLog.outErrorDb("BatteGroundRV: Failed to spawn some object!"); - return false; - } - return true; -} diff --git a/src/server/game/BattleGroundRV.h b/src/server/game/BattleGroundRV.h deleted file mode 100644 index bf06478d364..00000000000 --- a/src/server/game/BattleGroundRV.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __BATTLEGROUNDRV_H -#define __BATTLEGROUNDRV_H - -class BattleGround; - -enum BattleGroundRVObjectTypes -{ - BG_RV_OBJECT_BUFF_1, - BG_RV_OBJECT_BUFF_2, - BG_RV_OBJECT_FIRE_1, - BG_RV_OBJECT_FIRE_2, - BG_RV_OBJECT_FIREDOOR_1, - BG_RV_OBJECT_FIREDOOR_2, - - BG_RV_OBJECT_PILAR_1, - BG_RV_OBJECT_PILAR_3, - BG_RV_OBJECT_GEAR_1, - BG_RV_OBJECT_GEAR_2, - - BG_RV_OBJECT_PILAR_2, - BG_RV_OBJECT_PILAR_4, - BG_RV_OBJECT_PULLEY_1, - BG_RV_OBJECT_PULLEY_2, -/* - BG_RV_OBJECT_PILAR_COLLISION_1, - BG_RV_OBJECT_PILAR_COLLISION_2, - BG_RV_OBJECT_PILAR_COLLISION_3, - BG_RV_OBJECT_PILAR_COLLISION_4, -*/ - BG_RV_OBJECT_ELEVATOR_1, - BG_RV_OBJECT_ELEVATOR_2, - BG_RV_OBJECT_FENCE_1, - BG_RV_OBJECT_FENCE_2, - BG_RV_OBJECT_MAX, -}; - -enum BattleGroundRVObjects -{ - BG_RV_OBJECT_TYPE_BUFF_1 = 184663, - BG_RV_OBJECT_TYPE_BUFF_2 = 184664, - BG_RV_OBJECT_TYPE_FIRE_1 = 192704, - BG_RV_OBJECT_TYPE_FIRE_2 = 192705, - - BG_RV_OBJECT_TYPE_FIREDOOR_2 = 192387, - BG_RV_OBJECT_TYPE_FIREDOOR_1 = 192388, - BG_RV_OBJECT_TYPE_PULLEY_1 = 192389, - BG_RV_OBJECT_TYPE_PULLEY_2 = 192390, - BG_RV_OBJECT_TYPE_FENCE_1 = 192391, - BG_RV_OBJECT_TYPE_FENCE_2 = 192392, - BG_RV_OBJECT_TYPE_GEAR_1 = 192393, - BG_RV_OBJECT_TYPE_GEAR_2 = 192394, - BG_RV_OBJECT_TYPE_ELEVATOR_1 = 194582, - BG_RV_OBJECT_TYPE_ELEVATOR_2 = 194586, -/* - BG_RV_OBJECT_TYPE_PILAR_COLLISION_1 = 194580, // axe - BG_RV_OBJECT_TYPE_PILAR_COLLISION_2 = 194579, // arena - BG_RV_OBJECT_TYPE_PILAR_COLLISION_3 = 194581, // lightning - BG_RV_OBJECT_TYPE_PILAR_COLLISION_4 = 194578, // ivory -*/ - BG_RV_OBJECT_TYPE_PILAR_1 = 194583, // axe - BG_RV_OBJECT_TYPE_PILAR_2 = 194584, // arena - BG_RV_OBJECT_TYPE_PILAR_3 = 194585, // lightning - BG_RV_OBJECT_TYPE_PILAR_4 = 194587, // ivory -}; - -enum BattleGroundRVData -{ - BG_RV_STATE_OPEN_FENCES, - BG_RV_STATE_OPEN_PILARS, - BG_RV_STATE_CLOSE_PILARS, - BG_RV_STATE_OPEN_FIRE, - BG_RV_STATE_CLOSE_FIRE, - BG_RV_FIRE_TO_PILAR_TIMER = 20000, - BG_RV_PILAR_TO_FIRE_TIMER = 5000, - BG_RV_FIRST_TIMER = 20133, - BG_RV_WORLD_STATE_A = 0xe10, - BG_RV_WORLD_STATE_H = 0xe11, - BG_RV_WORLD_STATE = 0xe1a, -}; - -class BattleGroundRVScore : public BattleGroundScore -{ - public: - BattleGroundRVScore() {}; - virtual ~BattleGroundRVScore() {}; -}; - -class BattleGroundRV : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundRV(); - ~BattleGroundRV(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - virtual void Reset(); - virtual void FillInitialWorldStates(WorldPacket &d); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - bool SetupBattleGround(); - void HandleKillPlayer(Player* player, Player *killer); - bool HandlePlayerUnderMap(Player * plr); - - private: - uint32 Timer; - uint32 State; - - protected: - uint32 getTimer() { return Timer; }; - void setTimer(uint32 timer) { Timer = timer; }; - - uint32 getState() { return State; }; - void setState(uint32 state) { State = state; }; -}; -#endif diff --git a/src/server/game/BattleGroundSA.cpp b/src/server/game/BattleGroundSA.cpp deleted file mode 100644 index ccde43ce948..00000000000 --- a/src/server/game/BattleGroundSA.cpp +++ /dev/null @@ -1,822 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundSA.h" -#include "Language.h" -#include "Player.h" -#include "GameObject.h" -#include "ObjectMgr.h" -#include "WorldPacket.h" - - -BattleGroundSA::BattleGroundSA() -{ - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_SA_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_SA_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_SA_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_SA_HAS_BEGUN; - m_BgObjects.resize(BG_SA_MAXOBJ); - m_BgCreatures.resize(BG_SA_MAXNPC + BG_SA_MAX_GY); - TimerEnabled = false; - UpdateWaitTimer = 0; - SignaledRoundTwo = false; - SignaledRoundTwoHalfMin = false; - InitSecondRound = false; -} - -BattleGroundSA::~BattleGroundSA() -{ -} - -void BattleGroundSA::Reset() -{ - TotalTime = 0; - attackers = ((urand(0,1)) ? TEAM_ALLIANCE : TEAM_HORDE); - for (uint8 i = 0; i <= 5; i++) - GateStatus[i] = BG_SA_GATE_OK; - ShipsStarted = false; - status = BG_SA_WARMUP; -} - -bool BattleGroundSA::SetupBattleGround() -{ - return ResetObjs(); -} - -bool BattleGroundSA::ResetObjs() -{ - uint32 atF = BG_SA_Factions[attackers]; - uint32 defF = BG_SA_Factions[attackers ? TEAM_ALLIANCE : TEAM_HORDE]; - - - for (uint8 i = 0; i SetUInt32Value(GAMEOBJECT_FACTION, defF); - } - - GetBGObject(BG_SA_TITAN_RELIC)->SetUInt32Value(GAMEOBJECT_FACTION, atF); - GetBGObject(BG_SA_TITAN_RELIC)->Refresh(); - - for (uint8 i = 0; i <= 5; i++) - GateStatus[i] = BG_SA_GATE_OK; - - // MAD props for Kiper for discovering those values - 4 hours of his work. - GetBGObject(BG_SA_BOAT_ONE)->UpdateRotationFields(1.0f, 0.0002f); - GetBGObject(BG_SA_BOAT_TWO)->UpdateRotationFields(1.0f, 0.00001f); - SpawnBGObject(BG_SA_BOAT_ONE, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_SA_BOAT_TWO, RESPAWN_IMMEDIATELY); - - TotalTime = 0; - ShipsStarted = false; - - //Graveyards! - for (uint8 i = 0;i < BG_SA_MAX_GY; i++) - { - WorldSafeLocsEntry const *sg = NULL; - sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); - - if (!sg) - { - sLog.outError("SOTA: Can't find GY entry %u",BG_SA_GYEntries[i]); - return false; - } - - if (i == BG_SA_BEACH_GY) - { - GraveyardStatus[i] = attackers; - AddSpiritGuide(i + BG_SA_MAXNPC, sg->x, sg->y, sg->z, BG_SA_GYOrientation[i], ((attackers == TEAM_HORDE)? HORDE : ALLIANCE)); - } - else - { - GraveyardStatus[i] = ((attackers == TEAM_HORDE)? TEAM_ALLIANCE : TEAM_HORDE); - if (!AddSpiritGuide(i + BG_SA_MAXNPC, sg->x, sg->y, sg->z, BG_SA_GYOrientation[i], ((attackers == TEAM_HORDE)? ALLIANCE : HORDE))) - sLog.outError("SOTA: couldn't spawn GY: %u",i); - } - } - - //GY capture points - for (uint8 i = BG_SA_CENTRAL_FLAG; i < BG_SA_MAXOBJ; i++) - { - AddObject(i, BG_SA_ObjEntries[(i + (attackers == TEAM_ALLIANCE ? 3:0))], - BG_SA_ObjSpawnlocs[i][0], BG_SA_ObjSpawnlocs[i][1], - BG_SA_ObjSpawnlocs[i][2], BG_SA_ObjSpawnlocs[i][3], - 0,0,0,0,RESPAWN_ONE_DAY); - GetBGObject(i)->SetUInt32Value(GAMEOBJECT_FACTION, atF); - } - - //Player may enter BEFORE we set up bG - lets update his worldstates anyway... - UpdateWorldState(BG_SA_RIGHT_GY_HORDE , GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_HORDE?1:0); - UpdateWorldState(BG_SA_LEFT_GY_HORDE , GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_HORDE?1:0); - UpdateWorldState(BG_SA_CENTER_GY_HORDE , GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_HORDE?1:0); - - UpdateWorldState(BG_SA_RIGHT_GY_ALLIANCE , GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); - UpdateWorldState(BG_SA_LEFT_GY_ALLIANCE , GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); - UpdateWorldState(BG_SA_CENTER_GY_ALLIANCE , GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); - - if (attackers == TEAM_ALLIANCE) - { - UpdateWorldState(BG_SA_ALLY_ATTACKS, 1); - UpdateWorldState(BG_SA_HORDE_ATTACKS, 0); - - UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_ALL, 1); - UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_ALL, 1); - UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_HRD, 0); - UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_HRD, 0); - - UpdateWorldState(BG_SA_HORDE_DEFENCE_TOKEN,1); - UpdateWorldState(BG_SA_ALLIANCE_DEFENCE_TOKEN,0); - } - else - { - UpdateWorldState(BG_SA_HORDE_ATTACKS, 1); - UpdateWorldState(BG_SA_ALLY_ATTACKS, 0); - - UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_ALL, 0); - UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_ALL, 0); - UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_HRD, 1); - UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_HRD, 1); - - UpdateWorldState(BG_SA_HORDE_DEFENCE_TOKEN,0); - UpdateWorldState(BG_SA_ALLIANCE_DEFENCE_TOKEN,1); - } - - UpdateWorldState(BG_SA_PURPLE_GATEWS, 1); - UpdateWorldState(BG_SA_RED_GATEWS, 1); - UpdateWorldState(BG_SA_BLUE_GATEWS, 1); - UpdateWorldState(BG_SA_GREEN_GATEWS, 1); - UpdateWorldState(BG_SA_YELLOW_GATEWS, 1); - UpdateWorldState(BG_SA_ANCIENT_GATEWS, 1); - - TeleportPlayers(); - return true; -} - -void BattleGroundSA::StartShips() -{ - if (ShipsStarted) - return; - - DoorOpen(BG_SA_BOAT_ONE); - DoorOpen(BG_SA_BOAT_TWO); - - for (int i = BG_SA_BOAT_ONE; i <= BG_SA_BOAT_TWO; i++) - { - for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end();itr++) - { - if (Player* p = objmgr.GetPlayer(itr->first)) - { - if (p->GetTeamId() != attackers) - continue; - - UpdateData data; - WorldPacket pkt; - GetBGObject(i)->BuildValuesUpdateBlockForPlayer(&data, p); - data.BuildPacket(&pkt); - p->GetSession()->SendPacket(&pkt); - } - } - } - ShipsStarted = true; -} - -void BattleGroundSA::Update(uint32 diff) -{ - if (InitSecondRound) - { - if (UpdateWaitTimer < diff) - { - if (!SignaledRoundTwo) - { - SignaledRoundTwo = true; - InitSecondRound = false; - SendMessageToAll(LANG_BG_SA_ROUND_TWO_ONE_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - }else - { - UpdateWaitTimer -= diff; - return; - } - } - BattleGround::Update(diff); - TotalTime += diff; - - if (status == BG_SA_WARMUP ) - { - BG_SA_ENDROUNDTIME = BG_SA_ROUNDLENGTH; - if (TotalTime >= BG_SA_WARMUPLENGTH) - { - TotalTime = 0; - ToggleTimer(); - DemolisherStartState(false); - status = BG_SA_ROUND_ONE; - } - if (TotalTime >= BG_SA_BOAT_START) - StartShips(); - return; - } - else if (status == BG_SA_SECOND_WARMUP) - { - if (RoundScores[0].time= 60000) - { - SendWarningToAll(LANG_BG_SA_HAS_BEGUN); - TotalTime = 0; - ToggleTimer(); - DemolisherStartState(false); - status = BG_SA_ROUND_TWO; - } - if (TotalTime >= 30000) - { - if (!SignaledRoundTwoHalfMin) - { - SignaledRoundTwoHalfMin = true; - SendMessageToAll(LANG_BG_SA_ROUND_TWO_START_HALF_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - } - StartShips(); - return; - } - else if (GetStatus() == STATUS_IN_PROGRESS) - { - if (status == BG_SA_ROUND_ONE) - { - if (TotalTime >= BG_SA_ROUNDLENGTH) - { - RoundScores[0].winner = attackers; - RoundScores[0].time = BG_SA_ROUNDLENGTH; - TotalTime = 0; - status = BG_SA_SECOND_WARMUP; - attackers = (attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; - status = BG_SA_SECOND_WARMUP; - ToggleTimer(); - ResetObjs(); - return; - } - } - else if (status == BG_SA_ROUND_TWO) - { - if (TotalTime >= BG_SA_ENDROUNDTIME) - { - RoundScores[1].time = BG_SA_ROUNDLENGTH; - RoundScores[1].winner = (attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; - - if (RoundScores[0].time == RoundScores[1].time) - EndBattleGround(NULL); - else if (RoundScores[0].time < RoundScores[1].time) - EndBattleGround(RoundScores[0].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); - else - EndBattleGround(RoundScores[1].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); - return; - } - } - if (status == BG_SA_ROUND_ONE || status == BG_SA_ROUND_TWO) - { - SendTime(); - UpdateDemolisherSpawns(); - } - } -} - -void BattleGroundSA::StartingEventCloseDoors() -{ -} - -void BattleGroundSA::StartingEventOpenDoors() -{ -} - -void BattleGroundSA::FillInitialWorldStates(WorldPacket& data) -{ - uint32 ally_attacks = uint32(attackers == TEAM_ALLIANCE ? 1 : 0); - uint32 horde_attacks = uint32(attackers == TEAM_HORDE ? 1 : 0); - - data << uint32(BG_SA_ANCIENT_GATEWS) << uint32(GateStatus[BG_SA_ANCIENT_GATE]); - data << uint32(BG_SA_YELLOW_GATEWS) << uint32(GateStatus[BG_SA_YELLOW_GATE]); - data << uint32(BG_SA_GREEN_GATEWS) << uint32(GateStatus[BG_SA_GREEN_GATE]); - data << uint32(BG_SA_BLUE_GATEWS) << uint32(GateStatus[BG_SA_BLUE_GATE]); - data << uint32(BG_SA_RED_GATEWS) << uint32(GateStatus[BG_SA_RED_GATE]); - data << uint32(BG_SA_PURPLE_GATEWS) << uint32(GateStatus[BG_SA_PURPLE_GATE]); - - data << uint32(BG_SA_BONUS_TIMER) << uint32(0); - - data << uint32(BG_SA_HORDE_ATTACKS)<< horde_attacks; - data << uint32(BG_SA_ALLY_ATTACKS) << ally_attacks; - - //Time will be sent on first update... - data << uint32(BG_SA_ENABLE_TIMER) << ((TimerEnabled) ? uint32(1) : uint32(0)); - data << uint32(BG_SA_TIMER_MINS) << uint32(0); - data << uint32(BG_SA_TIMER_SEC_TENS) << uint32(0); - data << uint32(BG_SA_TIMER_SEC_DECS) << uint32(0); - - data << uint32(BG_SA_RIGHT_GY_HORDE) << uint32(GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_HORDE?1:0); - data << uint32(BG_SA_LEFT_GY_HORDE) << uint32(GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_HORDE?1:0); - data << uint32(BG_SA_CENTER_GY_HORDE) << uint32(GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_HORDE?1:0); - - data << uint32(BG_SA_RIGHT_GY_ALLIANCE) << uint32(GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); - data << uint32(BG_SA_LEFT_GY_ALLIANCE) << uint32(GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); - data << uint32(BG_SA_CENTER_GY_ALLIANCE) << uint32(GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); - - data << uint32(BG_SA_HORDE_DEFENCE_TOKEN) << ally_attacks; - data << uint32(BG_SA_ALLIANCE_DEFENCE_TOKEN) << horde_attacks; - - data << uint32(BG_SA_LEFT_ATT_TOKEN_HRD) << horde_attacks; - data << uint32(BG_SA_RIGHT_ATT_TOKEN_HRD) << horde_attacks; - data << uint32(BG_SA_RIGHT_ATT_TOKEN_ALL) << ally_attacks; - data << uint32(BG_SA_LEFT_ATT_TOKEN_ALL) << ally_attacks; -} - -void BattleGroundSA::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundSAScore* sc = new BattleGroundSAScore; - - if (!ShipsStarted) - { - if (plr->GetTeamId() == attackers) - { - plr->CastSpell(plr,12438,true);//Without this player falls before boat loads... - - if (urand(0,1)) - plr->TeleportTo(607, 2682.936f, -830.368f, 50.0f, 2.895f, 0); - else - plr->TeleportTo(607, 2577.003f, 980.261f, 50.0f, 0.807f, 0); - - } - else - plr->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); - } - else - { - if (plr->GetTeamId() == attackers) - plr->TeleportTo(607, 1600.381f, -106.263f, 8.8745f, 3.78f, 0); - else - plr->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); - } - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundSA::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) -{ -} - -void BattleGroundSA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; -} - -void BattleGroundSA::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); - if (itr == m_PlayerScores.end()) // player not found... - return; - - if (type == SCORE_DESTROYED_DEMOLISHER) - ((BattleGroundSAScore*)itr->second)->demolishers_destroyed += value; - else if (type == SCORE_DESTROYED_WALL) - ((BattleGroundSAScore*)itr->second)->gates_destroyed += value; - else - BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); -} - -void BattleGroundSA::TeleportPlayers() -{ - for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) - { - if (Player *plr = objmgr.GetPlayer(itr->first)) - { - // should remove spirit of redemption - if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); - - if (!plr->isAlive()) - { - plr->ResurrectPlayer(1.0f); - plr->SpawnCorpseBones(); - } - - plr->SetHealth(plr->GetMaxHealth()); - plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); - plr->CombatStopWithPets(true); - - if (plr->GetTeamId() == attackers) - { - plr->CastSpell(plr,12438,true); //Without this player falls before boat loads... - - if (urand(0,1)) - plr->TeleportTo(607, 2682.936f, -830.368f, 50.0f, 2.895f, 0); - else - plr->TeleportTo(607, 2577.003f, 980.261f, 50.0f, 0.807f, 0); - } - else - plr->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); - } - } -} - -void BattleGroundSA::EventPlayerDamagedGO(Player* plr, GameObject* go, uint8 hitType, uint32 destroyedEvent) -{ - if (!go || !go->GetGOInfo()) - return; - - switch(hitType) - { - case BG_OBJECT_DMG_HIT_TYPE_JUST_DAMAGED://under attack - SendWarningToAll(LANG_BG_SA_IS_UNDER_ATTACK, go->GetGOInfo()->name); - break; - case BG_OBJECT_DMG_HIT_TYPE_DAMAGED: - break; - case BG_OBJECT_DMG_HIT_TYPE_JUST_HIGH_DAMAGED: - { - uint32 i = GetGateIDFromDestroyEventID(destroyedEvent); - GateStatus[i] = BG_SA_GATE_DAMAGED; - uint32 uws = GetWorldStateFromGateID(i); - if (uws) - UpdateWorldState(uws, GateStatus[i]); - break; - } - case BG_OBJECT_DMG_HIT_TYPE_HIGH_DAMAGED: - break; - case BG_OBJECT_DMG_HIT_TYPE_JUST_DESTROYED://handled at DestroyGate() - if (destroyedEvent == 19837) - SendWarningToAll(LANG_BG_SA_CHAMBER_BREACHED); - else - SendWarningToAll(LANG_BG_SA_WAS_DESTROYED, go->GetGOInfo()->name); - break; - } -} - -void BattleGroundSA::HandleKillUnit(Creature* unit, Player* killer) -{ - if (!unit) - return; - - if (unit->GetEntry() == 28781) //Demolisher - UpdatePlayerScore(killer, SCORE_DESTROYED_DEMOLISHER, 1); -} - -/* - You may ask what the fuck does it do? - Prevents owner overwriting guns faction with own. - */ -void BattleGroundSA::OverrideGunFaction() -{ - if (!m_BgCreatures[0]) - return; - - for (uint8 i = BG_SA_GUN_1; i <= BG_SA_GUN_10;i++) - { - if (Creature* gun = GetBGCreature(i)) - gun->setFaction(BG_SA_Factions[attackers? TEAM_ALLIANCE : TEAM_HORDE]); - } - - for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4;i++) - { - if (Creature* dem = GetBGCreature(i)) - dem->setFaction(BG_SA_Factions[attackers]); - } -} - -void BattleGroundSA::DemolisherStartState(bool start) -{ - if (!m_BgCreatures[0]) - return; - - for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4; i++) - { - if (Creature* dem = GetBGCreature(i)) - { - if (start) - dem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - else - dem->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - } - } -} - -void BattleGroundSA::DestroyGate(Player* pl, GameObject* /*go*/, uint32 destroyedEvent) -{ - uint32 i = GetGateIDFromDestroyEventID(destroyedEvent); - if (!GateStatus[i]) - return; - - if (GameObject* g = GetBGObject(i)) - { - if (g->GetGOValue()->building.health == 0) - { - GateStatus[i] = BG_SA_GATE_DESTROYED; - uint32 uws = GetWorldStateFromGateID(i); - if (uws) - UpdateWorldState(uws, GateStatus[i]); - bool rewardHonor = true; - switch(i) - { - case BG_SA_GREEN_GATE: - if (GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) - rewardHonor = false; - break; - case BG_SA_BLUE_GATE: - if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED) - rewardHonor = false; - break; - case BG_SA_RED_GATE: - if (GateStatus[BG_SA_PURPLE_GATE] == BG_SA_GATE_DESTROYED) - rewardHonor = false; - break; - case BG_SA_PURPLE_GATE: - if (GateStatus[BG_SA_RED_GATE] == BG_SA_GATE_DESTROYED) - rewardHonor = false; - break; - } - - if (i < 5) - DelObject(i+9); - UpdatePlayerScore(pl,SCORE_DESTROYED_WALL, 1); - if (rewardHonor) - UpdatePlayerScore(pl,SCORE_BONUS_HONOR,(GetBonusHonorFromKill(1))); - } - } -} - -WorldSafeLocsEntry const* BattleGroundSA::GetClosestGraveYard(Player* player) -{ - uint32 safeloc = 0; - WorldSafeLocsEntry const* ret; - WorldSafeLocsEntry const* closest; - float dist, nearest; - float x,y,z; - - player->GetPosition(x,y,z); - - if (player->GetTeamId() == attackers) - safeloc = BG_SA_GYEntries[BG_SA_BEACH_GY]; - else - safeloc = BG_SA_GYEntries[BG_SA_DEFENDER_LAST_GY]; - - closest = sWorldSafeLocsStore.LookupEntry(safeloc); - nearest = sqrt((closest->x - x)*(closest->x - x) + (closest->y - y)*(closest->y - y)+(closest->z - z)*(closest->z - z)); - - for (uint8 i = BG_SA_RIGHT_CAPTURABLE_GY; i < BG_SA_MAX_GY; i++) - { - if (GraveyardStatus[i] != player->GetTeamId()) - continue; - - ret = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); - dist = sqrt((ret->x - x)*(ret->x - x) + (ret->y - y)*(ret->y - y)+(ret->z - z)*(ret->z - z)); - if (dist < nearest) - { - closest = ret; - nearest = dist; - } - } - - return closest; -} - -void BattleGroundSA::SendTime() -{ - uint32 end_of_round = (BG_SA_ENDROUNDTIME - TotalTime); - UpdateWorldState(BG_SA_TIMER_MINS, end_of_round/60000); - UpdateWorldState(BG_SA_TIMER_SEC_TENS, (end_of_round%60000)/10000); - UpdateWorldState(BG_SA_TIMER_SEC_DECS, ((end_of_round%60000)%10000)/1000); -} - -void BattleGroundSA::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) -{ - switch(target_obj->GetEntry()) - { - case 191307: - case 191308: - if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) - CaptureGraveyard(BG_SA_LEFT_CAPTURABLE_GY, Source); - break; - case 191305: - case 191306: - if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) - CaptureGraveyard(BG_SA_RIGHT_CAPTURABLE_GY, Source); - break; - case 191310: - case 191309: - if ((GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) && (GateStatus[BG_SA_RED_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_PURPLE_GATE] == BG_SA_GATE_DESTROYED)) - CaptureGraveyard(BG_SA_CENTRAL_CAPTURABLE_GY, Source); - break; - default: - return; - }; -} - -void BattleGroundSA::CaptureGraveyard(BG_SA_Graveyards i, Player *Source) -{ - DelCreature(BG_SA_MAXNPC + i); - GraveyardStatus[i] = Source->GetTeamId(); - WorldSafeLocsEntry const *sg = NULL; - sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); - AddSpiritGuide(i + BG_SA_MAXNPC, sg->x, sg->y, sg->z, BG_SA_GYOrientation[i], (GraveyardStatus[i] == TEAM_ALLIANCE? ALLIANCE : HORDE)); - uint32 npc = 0; - uint32 flag = 0; - - switch(i) - { - case BG_SA_LEFT_CAPTURABLE_GY: - flag = BG_SA_LEFT_FLAG; - DelObject(flag); - AddObject(flag,BG_SA_ObjEntries[(flag + (Source->GetTeamId() == TEAM_ALLIANCE ? 0:3))], - BG_SA_ObjSpawnlocs[flag][0],BG_SA_ObjSpawnlocs[flag][1], - BG_SA_ObjSpawnlocs[flag][2],BG_SA_ObjSpawnlocs[flag][3],0,0,0,0,RESPAWN_ONE_DAY); - - npc = BG_SA_NPC_RIGSPARK; - AddCreature(BG_SA_NpcEntries[npc], npc, attackers, - BG_SA_NpcSpawnlocs[npc][0], BG_SA_NpcSpawnlocs[npc][1], - BG_SA_NpcSpawnlocs[npc][2], BG_SA_NpcSpawnlocs[npc][3]); - - UpdateWorldState(BG_SA_LEFT_GY_ALLIANCE, (GraveyardStatus[i] == TEAM_ALLIANCE? 1:0)); - UpdateWorldState(BG_SA_LEFT_GY_HORDE, (GraveyardStatus[i] == TEAM_ALLIANCE? 0:1)); - if (Source->GetTeamId() == TEAM_ALLIANCE) - SendWarningToAll(LANG_BG_SA_A_GY_WEST); - else - SendWarningToAll(LANG_BG_SA_H_GY_WEST); - break; - case BG_SA_RIGHT_CAPTURABLE_GY: - flag = BG_SA_RIGHT_FLAG; - DelObject(flag); - AddObject(flag,BG_SA_ObjEntries[(flag + (Source->GetTeamId() == TEAM_ALLIANCE ? 0:3))], - BG_SA_ObjSpawnlocs[flag][0],BG_SA_ObjSpawnlocs[flag][1], - BG_SA_ObjSpawnlocs[flag][2],BG_SA_ObjSpawnlocs[flag][3],0,0,0,0,RESPAWN_ONE_DAY); - - npc = BG_SA_NPC_SPARKLIGHT; - AddCreature(BG_SA_NpcEntries[npc], npc, attackers, - BG_SA_NpcSpawnlocs[npc][0], BG_SA_NpcSpawnlocs[npc][1], - BG_SA_NpcSpawnlocs[npc][2], BG_SA_NpcSpawnlocs[npc][3]); - - UpdateWorldState(BG_SA_RIGHT_GY_ALLIANCE, (GraveyardStatus[i] == TEAM_ALLIANCE? 1:0)); - UpdateWorldState(BG_SA_RIGHT_GY_HORDE, (GraveyardStatus[i] == TEAM_ALLIANCE? 0:1)); - if (Source->GetTeamId() == TEAM_ALLIANCE) - SendWarningToAll(LANG_BG_SA_A_GY_EAST); - else - SendWarningToAll(LANG_BG_SA_H_GY_EAST); - break; - case BG_SA_CENTRAL_CAPTURABLE_GY: - flag = BG_SA_CENTRAL_FLAG; - DelObject(flag); - AddObject(flag,BG_SA_ObjEntries[(flag + (Source->GetTeamId() == TEAM_ALLIANCE ? 0:3))], - BG_SA_ObjSpawnlocs[flag][0],BG_SA_ObjSpawnlocs[flag][1], - BG_SA_ObjSpawnlocs[flag][2],BG_SA_ObjSpawnlocs[flag][3],0,0,0,0,RESPAWN_ONE_DAY); - - UpdateWorldState(BG_SA_CENTER_GY_ALLIANCE, (GraveyardStatus[i] == TEAM_ALLIANCE? 1:0)); - UpdateWorldState(BG_SA_CENTER_GY_HORDE, (GraveyardStatus[i] == TEAM_ALLIANCE? 0:1)); - if (Source->GetTeamId() == TEAM_ALLIANCE) - SendWarningToAll(LANG_BG_SA_A_GY_SOUTH); - else - SendWarningToAll(LANG_BG_SA_H_GY_SOUTH); - break; - default: - ASSERT(0); - break; - }; -} - -void BattleGroundSA::EventPlayerUsedGO(Player* Source, GameObject* object) -{ - if (object->GetEntry() == BG_SA_ObjEntries[BG_SA_TITAN_RELIC] && GateStatus[BG_SA_ANCIENT_GATE] == BG_SA_GATE_DESTROYED) - { - if (Source->GetTeamId() == attackers) - { - if (Source->GetTeamId() == ALLIANCE) - SendMessageToAll(LANG_BG_SA_ALLIANCE_CAPTURED_RELIC, CHAT_MSG_BG_SYSTEM_NEUTRAL); - else SendMessageToAll(LANG_BG_SA_HORDE_CAPTURED_RELIC, CHAT_MSG_BG_SYSTEM_NEUTRAL); - - if (status == BG_SA_ROUND_ONE) - { - RoundScores[0].winner = attackers; - RoundScores[0].time = TotalTime; - attackers = (attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; - status = BG_SA_SECOND_WARMUP; - TotalTime = 0; - ToggleTimer(); - SendWarningToAll(LANG_BG_SA_ROUND_ONE_END); - UpdateWaitTimer = 5000; - SignaledRoundTwo = false; - SignaledRoundTwoHalfMin = false; - InitSecondRound = true; - ResetObjs(); - } - else if (status == BG_SA_ROUND_TWO) - { - RoundScores[1].winner = attackers; - RoundScores[1].time = TotalTime;ToggleTimer(); - if (RoundScores[0].time == RoundScores[1].time) - EndBattleGround(NULL); - else if (RoundScores[0].time < RoundScores[1].time) - EndBattleGround(RoundScores[0].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); - else - EndBattleGround(RoundScores[1].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); - } - } - } -} - -void BattleGroundSA::ToggleTimer() -{ - TimerEnabled = !TimerEnabled; - UpdateWorldState(BG_SA_ENABLE_TIMER, (TimerEnabled) ? 1 : 0); -} - -void BattleGroundSA::EndBattleGround(uint32 winner) -{ - //honor reward for winning - if (winner == ALLIANCE) - RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); - else if (winner == HORDE) - RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); - - //complete map_end rewards (even if no team wins) - RewardHonorToTeam(GetBonusHonorFromKill(2), ALLIANCE); - RewardHonorToTeam(GetBonusHonorFromKill(2), HORDE); - - BattleGround::EndBattleGround(winner); -} - -void BattleGroundSA::UpdateDemolisherSpawns() -{ - for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4; i++) - { - if (m_BgCreatures[i]) - { - if (Creature *Demolisher = GetBGCreature(i)) - { - if (Demolisher->isDead()) - { - uint8 gy = (i >= BG_SA_DEMOLISHER_3 ? 3 : 2); - if (GraveyardStatus[gy] == attackers) - Demolisher->Relocate(BG_SA_NpcSpawnlocs[i + 6][0], BG_SA_NpcSpawnlocs[i + 6][1], - BG_SA_NpcSpawnlocs[i + 6][2], BG_SA_NpcSpawnlocs[i + 6][3]); - else - Demolisher->Relocate(BG_SA_NpcSpawnlocs[i][0], BG_SA_NpcSpawnlocs[i][1], - BG_SA_NpcSpawnlocs[i][2], BG_SA_NpcSpawnlocs[i][3]); - - Demolisher->Respawn(); - } - } - } - } -} - - diff --git a/src/server/game/BattleGroundSA.h b/src/server/game/BattleGroundSA.h deleted file mode 100644 index 760be3ca02e..00000000000 --- a/src/server/game/BattleGroundSA.h +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDSA_H -#define __BATTLEGROUNDSA_H - -class BattleGround; - -class BattleGroundSAScore : public BattleGroundScore -{ - public: - BattleGroundSAScore(): demolishers_destroyed(0), gates_destroyed(0) {}; - virtual ~BattleGroundSAScore() {}; - uint8 demolishers_destroyed; - uint8 gates_destroyed; -}; - -#define BG_SA_FLAG_AMOUNT 3 -#define BG_SA_DEMOLISHER_AMOUNT 4 - -enum BG_SA_Status - { - BG_SA_NOTSTARTED = 0, - BG_SA_WARMUP, - BG_SA_ROUND_ONE, - BG_SA_SECOND_WARMUP, - BG_SA_ROUND_TWO, - BG_SA_BONUS_ROUND - }; - -enum BG_SA_GateState - { - BG_SA_GATE_OK = 1, - BG_SA_GATE_DAMAGED = 2, - BG_SA_GATE_DESTROYED = 3 - }; - -enum BG_SA_Timers - { - BG_SA_BOAT_START = 60000, - BG_SA_WARMUPLENGTH = 120000, - BG_SA_ROUNDLENGTH = 600000 - }; - -enum BG_SA_WorldStates - { - BG_SA_TIMER_MINS = 3559, - BG_SA_TIMER_SEC_TENS = 3560, - BG_SA_TIMER_SEC_DECS = 3561, - BG_SA_ALLY_ATTACKS = 4352, - BG_SA_HORDE_ATTACKS = 4353, - - BG_SA_PURPLE_GATEWS = 3614, - BG_SA_RED_GATEWS = 3617, - BG_SA_BLUE_GATEWS = 3620, - BG_SA_GREEN_GATEWS = 3623, - BG_SA_YELLOW_GATEWS = 3638, - BG_SA_ANCIENT_GATEWS = 3849, - - - BG_SA_LEFT_GY_ALLIANCE = 3635, - BG_SA_RIGHT_GY_ALLIANCE = 3636, - BG_SA_CENTER_GY_ALLIANCE = 3637, - - BG_SA_RIGHT_ATT_TOKEN_ALL = 3627, - BG_SA_LEFT_ATT_TOKEN_ALL = 3626, - - BG_SA_LEFT_ATT_TOKEN_HRD = 3629, - BG_SA_RIGHT_ATT_TOKEN_HRD = 3628, - - BG_SA_HORDE_DEFENCE_TOKEN = 3631, - BG_SA_ALLIANCE_DEFENCE_TOKEN = 3630, - - BG_SA_RIGHT_GY_HORDE = 3632, - BG_SA_LEFT_GY_HORDE = 3633, - BG_SA_CENTER_GY_HORDE = 3634, - - BG_SA_BONUS_TIMER = 0xdf3, - BG_SA_ENABLE_TIMER = 3564, - }; - -enum BG_SA_NPCs - { - BG_SA_GUN_1 = 0, - BG_SA_GUN_2, - BG_SA_GUN_3, - BG_SA_GUN_4, - BG_SA_GUN_5, - BG_SA_GUN_6, - BG_SA_GUN_7, - BG_SA_GUN_8, - BG_SA_GUN_9, - BG_SA_GUN_10, - BG_SA_DEMOLISHER_1, - BG_SA_DEMOLISHER_2, - BG_SA_DEMOLISHER_3, - BG_SA_DEMOLISHER_4, - BG_SA_NPC_SPARKLIGHT, - BG_SA_NPC_RIGSPARK, - BG_SA_MAXNPC - }; - -const uint32 BG_SA_NpcEntries[BG_SA_MAXNPC] = - { - 27894, - 27894, - 27894, - 27894, - 27894, - 27894, - 27894, - 27894, - 27894, - 27894, - //4 beach demolishers - 28781, - 28781, - 28781, - 28781, - //Fizzle Sparklight, or whatever his name was - 29260, - 29262, - }; - -const float BG_SA_NpcSpawnlocs[BG_SA_MAXNPC + BG_SA_DEMOLISHER_AMOUNT][4] = - { - //Cannons - { 1436.429f, 110.05f, 41.407f, 5.4f }, - { 1404.9023f, 84.758f, 41.183f, 5.46f }, - { 1068.693f, -86.951f, 93.81f, 0.02f }, - { 1068.83f, -127.56f, 96.45f, 0.0912f }, - { 1422.115f, -196.433f, 42.1825f, 1.0222f }, - { 1454.887f, -220.454f, 41.956f, 0.9627f }, - { 1232.345f, -187.517f, 66.945f, 0.45f }, - { 1249.634f, -224.189f, 66.72f, 0.635f }, - { 1236.213f, 92.287f, 64.965f, 5.751f }, - { 1215.11f, 57.772f, 64.739f, 5.78f } , - //Demolishers - { 1611.597656,-117.270073,8.719355,2.513274}, - { 1575.562500,-158.421875,5.024450,2.129302}, - { 1618.047729,61.424641,7.248210,3.979351}, - { 1575.103149,98.873344,2.830360,3.752458}, - //Npcs - { 1348.644165, -298.786469, 31.080130, 1.710423}, - { 1358.191040, 195.527786, 31.018187, 4.171337}, - //Demolishers2 - { 1371.055786, -317.071136, 35.007359, 1.947460}, - { 1424.034912, -260.195190, 31.084425, 2.820013}, - { 1353.139893, 223.745438, 35.265411, 4.343684}, - { 1404.809570, 197.027237, 32.046032, 3.605401}, - }; - -enum BG_SA_Objects - { - BG_SA_GREEN_GATE = 0, - BG_SA_YELLOW_GATE, - BG_SA_BLUE_GATE, - BG_SA_RED_GATE, - BG_SA_PURPLE_GATE, - BG_SA_ANCIENT_GATE, - BG_SA_TITAN_RELIC, - BG_SA_BOAT_ONE, - BG_SA_BOAT_TWO, - BG_SA_SIGIL_1, - BG_SA_SIGIL_2, - BG_SA_SIGIL_3, - BG_SA_SIGIL_4, - BG_SA_SIGIL_5, - BG_SA_CENTRAL_FLAGPOLE, - BG_SA_RIGHT_FLAGPOLE, - BG_SA_LEFT_FLAGPOLE, - BG_SA_CENTRAL_FLAG, - BG_SA_RIGHT_FLAG, - BG_SA_LEFT_FLAG, - BG_SA_MAXOBJ - }; - -const float BG_SA_ObjSpawnlocs[BG_SA_MAXOBJ][4] = - { - { 1411.57f, 108.163f, 28.692f, 5.441f }, - { 1055.452f, -108.1f, 82.134f, 0.034f }, - { 1431.3413f, -219.437f, 30.893f, 0.9736f }, - { 1227.667f, -212.555f, 55.372f, 0.5023f }, - { 1214.681f, 81.21f, 53.413f, 5.745f }, - { 878.555f, -108.989f, 119.835f, 0.0565f }, - { 836.5f, -108.8f, 120.219f, 0.0f }, - //Ships - { 2679.696777, -826.891235, 3.712860, 5.78367f}, //rot2 1 rot3 0.0002 - { 2574.003662, 981.261475, 2.603424, 0.807696}, - //Sigils - { 1414.054f, 106.72f, 41.442f, 5.441f }, - { 1060.63f, -107.8f, 94.7f, 0.034f }, - { 1433.383f, -216.4f, 43.642f, 0.9736f }, - { 1230.75f, -210.724f, 67.611f, 0.5023f }, - { 1217.8f, 79.532f, 66.58f, 5.745f }, - //Flagpoles - { 1215.114258,-65.711861,70.084267,-3.124123}, - {1338.863892,-153.336533,30.895121,-2.530723}, - {1309.124268,9.410645,30.893402,-1.623156}, - //Flags - { 1215.108032,-65.715767,70.084267,-3.124123}, - { 1338.859253,-153.327316,30.895077,-2.530723}, - { 1309.192017,9.416233,30.893402,1.518436}, - }; - -/* Ships: - * 193182 - ally - * 193183 - horde - * 193184 - horde - * 193185 - ally - * Banners: - * 191308 - left one, - * 191306 - right one, - * 191310 - central, - * Ally ones, substract 1 - * to get horde ones. - */ - -const uint32 BG_SA_ObjEntries[BG_SA_MAXOBJ + BG_SA_FLAG_AMOUNT] = - { - 190722, - 190727, - 190724, - 190726, - 190723, - 192549, - 192834, - 193182, - 193185, - 192687, - 192685, - 192689, - 192690, - 192691, - 191311, - 191311, - 191311, - 191310, - 191306, - 191308, - 191309, - 191305, - 191307, - }; - -const uint32 BG_SA_Factions[2] = - { - 1732, - 1735, - }; - -enum BG_SA_Graveyards - { - BG_SA_BEACH_GY = 0, - BG_SA_DEFENDER_LAST_GY, - BG_SA_RIGHT_CAPTURABLE_GY, - BG_SA_LEFT_CAPTURABLE_GY, - BG_SA_CENTRAL_CAPTURABLE_GY, - BG_SA_MAX_GY - }; - -const uint32 BG_SA_GYEntries[BG_SA_MAX_GY] = - { - 1350, - 1349, - 1347, - 1346, - 1348, - }; - -const float BG_SA_GYOrientation[BG_SA_MAX_GY] = - { - 6.202f, - 1.926f, //right capturable GY - 3.917f, //left capturable GY - 3.104f, //center, capturable - 6.148f, //defender last GY - }; - -struct BG_SA_RoundScore -{ - TeamId winner; - uint32 time; -}; - -class BattleGroundSA : public BattleGround -{ - friend class BattleGroundMgr; - - public: - BattleGroundSA(); - ~BattleGroundSA(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - virtual bool SetupBattleGround(); - virtual void Reset(); - virtual void FillInitialWorldStates(WorldPacket& data); - virtual void EventPlayerDamagedGO(Player* plr, GameObject* go, uint8 hitType, uint32 destroyedEvent); - virtual void HandleKillUnit(Creature* unit, Player* killer); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); - virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); - virtual void EventPlayerUsedGO(Player* Source, GameObject* object); - uint32 GetGateIDFromDestroyEventID(uint32 id) - { - uint32 i = 0; - switch(id) - { - case 19046: i = BG_SA_GREEN_GATE; break; //Green gate destroyed - case 19045: i = BG_SA_BLUE_GATE; break; //blue gate - case 19047: i = BG_SA_RED_GATE; break; //red gate - case 19048: i = BG_SA_PURPLE_GATE; break; //purple gate - case 19049: i = BG_SA_YELLOW_GATE; break; //yellow gate - case 19837: i = BG_SA_ANCIENT_GATE; break; //ancient gate - } - return i; - } - uint32 GetWorldStateFromGateID(uint32 id) - { - uint32 uws = 0; - switch(id) - { - case BG_SA_GREEN_GATE: uws = BG_SA_GREEN_GATEWS; break; - case BG_SA_YELLOW_GATE: uws = BG_SA_YELLOW_GATEWS; break; - case BG_SA_BLUE_GATE: uws = BG_SA_BLUE_GATEWS; break; - case BG_SA_RED_GATE: uws = BG_SA_RED_GATEWS; break; - case BG_SA_PURPLE_GATE: uws = BG_SA_PURPLE_GATEWS; break; - case BG_SA_ANCIENT_GATE: uws = BG_SA_ANCIENT_GATEWS; break; - } - return uws; - } - void EndBattleGround(uint32 winner); - - void RemovePlayer(Player *plr,uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - - - /* Scorekeeping */ - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - - private: - bool ResetObjs(); - void StartShips(); - void TeleportPlayers(); - void OverrideGunFaction(); - void DemolisherStartState(bool start); - void DestroyGate(Player* pl, GameObject* /*go*/, uint32 destroyedEvent); - void SendTime(); - void CaptureGraveyard(BG_SA_Graveyards i, Player *Source); - void ToggleTimer(); - void UpdateDemolisherSpawns(); - TeamId attackers; - uint32 TotalTime; - uint32 BG_SA_ENDROUNDTIME; - bool ShipsStarted; - BG_SA_GateState GateStatus[6]; - BG_SA_Status status; - TeamId GraveyardStatus[BG_SA_MAX_GY]; - BG_SA_RoundScore RoundScores[2]; - bool TimerEnabled; - uint32 UpdateWaitTimer;//5secs before starting the 1min countdown for second round - bool SignaledRoundTwo; - bool SignaledRoundTwoHalfMin; - bool InitSecondRound; -}; -#endif diff --git a/src/server/game/BattleGroundWS.cpp b/src/server/game/BattleGroundWS.cpp deleted file mode 100644 index 71872511274..00000000000 --- a/src/server/game/BattleGroundWS.cpp +++ /dev/null @@ -1,841 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "BattleGround.h" -#include "BattleGroundWS.h" -#include "Creature.h" -#include "GameObject.h" -#include "Language.h" -#include "Object.h" -#include "ObjectMgr.h" -#include "BattleGroundMgr.h" -#include "Player.h" -#include "World.h" -#include "WorldPacket.h" - -// these variables aren't used outside of this file, so declare them only here -enum BG_WSG_Rewards -{ - BG_WSG_WIN = 0, - BG_WSG_FLAG_CAP, - BG_WSG_MAP_COMPLETE, - BG_WSG_REWARD_NUM -}; - -uint32 BG_WSG_Honor[BG_HONOR_MODE_NUM][BG_WSG_REWARD_NUM] = { - {20,40,40}, // normal honor - {60,40,80} // holiday -}; - -uint32 BG_WSG_Reputation[BG_HONOR_MODE_NUM][BG_WSG_REWARD_NUM] = { - {0,35,0}, // normal honor - {0,45,0} // holiday -}; - -BattleGroundWS::BattleGroundWS() -{ - m_BgObjects.resize(BG_WS_OBJECT_MAX); - m_BgCreatures.resize(BG_CREATURES_MAX_WS); - - m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; - m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; - m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; -} - -BattleGroundWS::~BattleGroundWS() -{ -} - -void BattleGroundWS::Update(uint32 diff) -{ - BattleGround::Update(diff); - - if (GetStatus() == STATUS_IN_PROGRESS) - { - if (GetStartTime() >= 25*MINUTE*IN_MILISECONDS) // Òàéìåð òèêàòü íà÷èíàåò ïîñëå 25 ìèíóò - { - if (GetTeamScore(ALLIANCE) == 0) - { - if (GetTeamScore(HORDE) == 0) // No one scored - result is tie - EndBattleGround(NULL); - else // Horde has more points and thus wins - EndBattleGround(HORDE); - } - - else if (GetTeamScore(HORDE) == 0) - EndBattleGround(ALLIANCE); // Alliance has > 0, Horde has 0, alliance wins - - else if (GetTeamScore(HORDE) == GetTeamScore(ALLIANCE)) // Team score equal, winner is team that scored the first flag - EndBattleGround(m_FirstFlagCaptureTeam); - - else if (GetTeamScore(HORDE) > GetTeamScore(ALLIANCE)) // Last but not least, check who has the higher score - EndBattleGround(HORDE); - else - EndBattleGround(ALLIANCE); - } - else if (GetStartTime() > m_minutesElapsed*MINUTE*IN_MILISECONDS) - { - ++m_minutesElapsed; - UpdateWorldState(BG_WS_STATE_TIMER, 25-m_minutesElapsed); - } - - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) - { - m_FlagsTimer[BG_TEAM_ALLIANCE] -= diff; - - if (m_FlagsTimer[BG_TEAM_ALLIANCE] < 0) - { - m_FlagsTimer[BG_TEAM_ALLIANCE] = 0; - RespawnFlag(ALLIANCE, true); - } - } - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) - { - m_FlagsDropTimer[BG_TEAM_ALLIANCE] -= diff; - - if (m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0) - { - m_FlagsDropTimer[BG_TEAM_ALLIANCE] = 0; - RespawnFlagAfterDrop(ALLIANCE); - m_BothFlagsKept = false; - } - } - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) - { - m_FlagsTimer[BG_TEAM_HORDE] -= diff; - - if (m_FlagsTimer[BG_TEAM_HORDE] < 0) - { - m_FlagsTimer[BG_TEAM_HORDE] = 0; - RespawnFlag(HORDE, true); - } - } - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) - { - m_FlagsDropTimer[BG_TEAM_HORDE] -= diff; - - if (m_FlagsDropTimer[BG_TEAM_HORDE] < 0) - { - m_FlagsDropTimer[BG_TEAM_HORDE] = 0; - RespawnFlagAfterDrop(HORDE); - m_BothFlagsKept = false; - } - } - if (m_BothFlagsKept) - { - m_FlagSpellForceTimer += diff; - if (m_FlagDebuffState == 0 && m_FlagSpellForceTimer >= 600000) //10 minutes - { - if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[0])) - plr->CastSpell(plr,WS_SPELL_FOCUSED_ASSAULT,true); - if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[1])) - plr->CastSpell(plr,WS_SPELL_FOCUSED_ASSAULT,true); - m_FlagDebuffState = 1; - } - else if (m_FlagDebuffState == 1 && m_FlagSpellForceTimer >= 900000) //15 minutes - { - if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[0])) - { - plr->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); - plr->CastSpell(plr,WS_SPELL_BRUTAL_ASSAULT,true); - } - if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[1])) - { - plr->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); - plr->CastSpell(plr,WS_SPELL_BRUTAL_ASSAULT,true); - } - m_FlagDebuffState = 2; - } - } - else - { - m_FlagSpellForceTimer = 0; //reset timer. - m_FlagDebuffState = 0; - } - } -} - -void BattleGroundWS::StartingEventCloseDoors() -{ - for (uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; ++i) - { - DoorClose(i); - SpawnBGObject(i, RESPAWN_IMMEDIATELY); - } - for (uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; ++i) - SpawnBGObject(i, RESPAWN_ONE_DAY); - - UpdateWorldState(BG_WS_STATE_TIMER_ACTIVE, 1); - UpdateWorldState(BG_WS_STATE_TIMER, 25); -} - -void BattleGroundWS::StartingEventOpenDoors() -{ - for (uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_A_4; ++i) - DoorOpen(i); - for (uint32 i = BG_WS_OBJECT_DOOR_H_1; i <= BG_WS_OBJECT_DOOR_H_2; ++i) - DoorOpen(i); - - SpawnBGObject(BG_WS_OBJECT_DOOR_A_5, RESPAWN_ONE_DAY); - SpawnBGObject(BG_WS_OBJECT_DOOR_A_6, RESPAWN_ONE_DAY); - SpawnBGObject(BG_WS_OBJECT_DOOR_H_3, RESPAWN_ONE_DAY); - SpawnBGObject(BG_WS_OBJECT_DOOR_H_4, RESPAWN_ONE_DAY); - - for (uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; ++i) - SpawnBGObject(i, RESPAWN_IMMEDIATELY); -} - -void BattleGroundWS::AddPlayer(Player *plr) -{ - BattleGround::AddPlayer(plr); - //create score and add it to map, default values are set in constructor - BattleGroundWGScore* sc = new BattleGroundWGScore; - - m_PlayerScores[plr->GetGUID()] = sc; -} - -void BattleGroundWS::RespawnFlag(uint32 Team, bool captured) -{ - if (Team == ALLIANCE) - { - sLog.outDebug("Respawn Alliance flag"); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; - } - else - { - sLog.outDebug("Respawn Horde flag"); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; - } - - if (captured) - { - //when map_update will be allowed for battlegrounds this code will be useless - SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); - SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); - SendMessageToAll(LANG_BG_WS_F_PLACED, CHAT_MSG_BG_SYSTEM_NEUTRAL); - PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); // flag respawned sound... - } - m_BothFlagsKept = false; -} - -void BattleGroundWS::RespawnFlagAfterDrop(uint32 team) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - RespawnFlag(team,false); - if (team == ALLIANCE) - { - SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); - SendMessageToAll(LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - else - { - SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); - SendMessageToAll(LANG_BG_WS_HORDE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL); - } - - PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); - - GameObject *obj = GetBgMap()->GetGameObject(GetDroppedFlagGUID(team)); - if (obj) - obj->Delete(); - else - sLog.outError("unknown droped flag bg, guid: %u",GUID_LOPART(GetDroppedFlagGUID(team))); - - SetDroppedFlagGUID(0,team); - m_BothFlagsKept = false; -} - -void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - uint32 winner = 0; - - Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); - if (Source->GetTeam() == ALLIANCE) - { - if (!this->IsHordeFlagPickedup()) - return; - SetHordeFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time - // horde flag in base (but not respawned yet) - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; - // Drop Horde Flag from Player - Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); - if (m_FlagDebuffState == 1) - Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); - if (m_FlagDebuffState == 2) - Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); - if (GetTeamScore(ALLIANCE) < BG_WS_MAX_TEAM_SCORE) - AddPoint(ALLIANCE, 1); - PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE); - RewardReputationToTeam(890, m_ReputationCapture, ALLIANCE); - } - else - { - if (!this->IsAllianceFlagPickedup()) - return; - SetAllianceFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time - // alliance flag in base (but not respawned yet) - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; - // Drop Alliance Flag from Player - Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); - if (m_FlagDebuffState == 1) - Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); - if (m_FlagDebuffState == 2) - Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); - if (GetTeamScore(HORDE) < BG_WS_MAX_TEAM_SCORE) - AddPoint(HORDE, 1); - PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_HORDE); - RewardReputationToTeam(889, m_ReputationCapture, HORDE); - } - //for flag capture is reward 2 honorable kills - RewardHonorToTeam(GetBonusHonorFromKill(2), Source->GetTeam()); - - SpawnBGObject(BG_WS_OBJECT_H_FLAG, BG_WS_FLAG_RESPAWN_TIME); - SpawnBGObject(BG_WS_OBJECT_A_FLAG, BG_WS_FLAG_RESPAWN_TIME); - - if (Source->GetTeam() == ALLIANCE) - SendMessageToAll(LANG_BG_WS_CAPTURED_HF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); - else - SendMessageToAll(LANG_BG_WS_CAPTURED_AF, CHAT_MSG_BG_SYSTEM_HORDE, Source); - - UpdateFlagState(Source->GetTeam(), 1); // flag state none - UpdateTeamScore(Source->GetTeam()); - // only flag capture should be updated - UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures - - if (!m_FirstFlagCaptureTeam) - SetFirstFlagCapture(Source->GetTeam()); - - if (GetTeamScore(ALLIANCE) == BG_WS_MAX_TEAM_SCORE) - winner = ALLIANCE; - - if (GetTeamScore(HORDE) == BG_WS_MAX_TEAM_SCORE) - winner = HORDE; - - if (winner) - { - UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 0); - UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 0); - UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, 1); - UpdateWorldState(BG_WS_FLAG_STATE_HORDE, 1); - UpdateWorldState(BG_WS_STATE_TIMER_ACTIVE, 0); - - RewardHonorToTeam(BG_WSG_Honor[m_HonorMode][BG_WSG_WIN], winner); - EndBattleGround(winner); - } - else - { - m_FlagsTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_RESPAWN_TIME; - } -} - -void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - { - // if not running, do not cast things at the dropper player (prevent spawning the "dropped" flag), neither send unnecessary messages - // just take off the aura - if (Source->GetTeam() == ALLIANCE) - { - if (!this->IsHordeFlagPickedup()) - return; - if (GetHordeFlagPickerGUID() == Source->GetGUID()) - { - SetHordeFlagPicker(0); - Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); - } - } - else - { - if (!this->IsAllianceFlagPickedup()) - return; - if (GetAllianceFlagPickerGUID() == Source->GetGUID()) - { - SetAllianceFlagPicker(0); - Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); - } - } - return; - } - - bool set = false; - - if (Source->GetTeam() == ALLIANCE) - { - if (!IsHordeFlagPickedup()) - return; - if (GetHordeFlagPickerGUID() == Source->GetGUID()) - { - SetHordeFlagPicker(0); - Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); - if (m_FlagDebuffState == 1) - Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); - if (m_FlagDebuffState == 2) - Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_GROUND; - Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG_DROPPED, true); - set = true; - } - } - else - { - if (!IsAllianceFlagPickedup()) - return; - if (GetAllianceFlagPickerGUID() == Source->GetGUID()) - { - SetAllianceFlagPicker(0); - Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); - if (m_FlagDebuffState == 1) - Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); - if (m_FlagDebuffState == 2) - Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND; - Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG_DROPPED, true); - set = true; - } - } - - if (set) - { - Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); - UpdateFlagState(Source->GetTeam(), 1); - - if (Source->GetTeam() == ALLIANCE) - { - SendMessageToAll(LANG_BG_WS_DROPPED_HF, CHAT_MSG_BG_SYSTEM_HORDE, Source); - UpdateWorldState(BG_WS_FLAG_UNK_HORDE, uint32(-1)); - } - else - { - SendMessageToAll(LANG_BG_WS_DROPPED_AF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); - UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, uint32(-1)); - } - - m_FlagsDropTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_DROP_TIME; - } -} - -void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - int32 message_id = 0; - ChatMsg type = CHAT_MSG_BG_SYSTEM_NEUTRAL; - - //alliance flag picked up from base - if (Source->GetTeam() == HORDE && this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_BASE - && this->m_BgObjects[BG_WS_OBJECT_A_FLAG] == target_obj->GetGUID()) - { - message_id = LANG_BG_WS_PICKEDUP_AF; - type = CHAT_MSG_BG_SYSTEM_HORDE; - PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); - SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); - SetAllianceFlagPicker(Source->GetGUID()); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; - //update world state to show correct flag carrier - UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); - UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); - Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true); - if (m_FlagState[1] == BG_WS_FLAG_STATE_ON_PLAYER) - m_BothFlagsKept = true; - } - - //horde flag picked up from base - if (Source->GetTeam() == ALLIANCE && this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_BASE - && this->m_BgObjects[BG_WS_OBJECT_H_FLAG] == target_obj->GetGUID()) - { - message_id = LANG_BG_WS_PICKEDUP_HF; - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); - SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); - SetHordeFlagPicker(Source->GetGUID()); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; - //update world state to show correct flag carrier - UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); - UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); - Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true); - if (m_FlagState[0] == BG_WS_FLAG_STATE_ON_PLAYER) - m_BothFlagsKept = true; - } - - //Alliance flag on ground(not in base) (returned or picked up again from ground!) - if (GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) - { - if (Source->GetTeam() == ALLIANCE) - { - message_id = LANG_BG_WS_RETURNED_AF; - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - UpdateFlagState(HORDE, BG_WS_FLAG_STATE_WAIT_RESPAWN); - RespawnFlag(ALLIANCE, false); - SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); - PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED); - UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1); - m_BothFlagsKept = false; - } - else - { - message_id = LANG_BG_WS_PICKEDUP_AF; - type = CHAT_MSG_BG_SYSTEM_HORDE; - PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); - SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); - SetAllianceFlagPicker(Source->GetGUID()); - Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true); - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; - UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); - if (m_FlagDebuffState == 1) - Source->CastSpell(Source,WS_SPELL_FOCUSED_ASSAULT,true); - if (m_FlagDebuffState == 2) - Source->CastSpell(Source,WS_SPELL_BRUTAL_ASSAULT,true); - UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); - } - //called in HandleGameObjectUseOpcode: - //target_obj->Delete(); - } - - //Horde flag on ground(not in base) (returned or picked up again) - if (GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) - { - if (Source->GetTeam() == HORDE) - { - message_id = LANG_BG_WS_RETURNED_HF; - type = CHAT_MSG_BG_SYSTEM_HORDE; - UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_WAIT_RESPAWN); - RespawnFlag(HORDE, false); - SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); - PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED); - UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1); - m_BothFlagsKept = false; - } - else - { - message_id = LANG_BG_WS_PICKEDUP_HF; - type = CHAT_MSG_BG_SYSTEM_ALLIANCE; - PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); - SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); - SetHordeFlagPicker(Source->GetGUID()); - Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true); - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; - UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); - if (m_FlagDebuffState == 1) - Source->CastSpell(Source,WS_SPELL_FOCUSED_ASSAULT,true); - if (m_FlagDebuffState == 2) - Source->CastSpell(Source,WS_SPELL_BRUTAL_ASSAULT,true); - UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); - } - //called in HandleGameObjectUseOpcode: - //target_obj->Delete(); - } - - if (!message_id) - return; - - SendMessageToAll(message_id, type, Source); - Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); -} - -void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid) -{ - // sometimes flag aura not removed :( - if (IsAllianceFlagPickedup() && m_FlagKeepers[BG_TEAM_ALLIANCE] == guid) - { - if (!plr) - { - sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); - this->SetAllianceFlagPicker(0); - this->RespawnFlag(ALLIANCE, false); - } - else - this->EventPlayerDroppedFlag(plr); - } - if (IsHordeFlagPickedup() && m_FlagKeepers[BG_TEAM_HORDE] == guid) - { - if (!plr) - { - sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); - this->SetHordeFlagPicker(0); - this->RespawnFlag(HORDE, false); - } - else - this->EventPlayerDroppedFlag(plr); - } -} - -void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value) -{ - if (team == ALLIANCE) - UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, value); - else - UpdateWorldState(BG_WS_FLAG_STATE_HORDE, value); -} - -void BattleGroundWS::UpdateTeamScore(uint32 team) -{ - if (team == ALLIANCE) - UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, GetTeamScore(team)); - else - UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, GetTeamScore(team)); -} - -void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger) -{ - // this is wrong way to implement these things. On official it done by gameobject spell cast. - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - //uint32 SpellId = 0; - //uint64 buff_guid = 0; - switch(Trigger) - { - case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). - //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_1]; - break; - case 3687: // Horde elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). - //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_2]; - break; - case 3706: // Alliance elixir of regeneration spawn - //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_1]; - break; - case 3708: // Horde elixir of regeneration spawn - //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_2]; - break; - case 3707: // Alliance elixir of berserk spawn - //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_1]; - break; - case 3709: // Horde elixir of berserk spawn - //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_2]; - break; - case 3646: // Alliance Flag spawn - if (m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE]) - if (GetHordeFlagPickerGUID() == Source->GetGUID()) - EventPlayerCapturedFlag(Source); - break; - case 3647: // Horde Flag spawn - if (m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE]) - if (GetAllianceFlagPickerGUID() == Source->GetGUID()) - EventPlayerCapturedFlag(Source); - break; - case 3649: // unk1 - case 3688: // unk2 - case 4628: // unk3 - case 4629: // unk4 - break; - default: - sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); - Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); - break; - } - - //if (buff_guid) - // HandleTriggerBuff(buff_guid,Source); -} - -bool BattleGroundWS::SetupBattleGround() -{ - // flags - if (!AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, BG_WS_FLAG_RESPAWN_TIME/1000) - || !AddObject(BG_WS_OBJECT_H_FLAG, BG_OBJECT_H_FLAG_WS_ENTRY, 916.0226f, 1434.405f, 345.413f, 0.01745329f, 0, 0, 0.008726535f, 0.9999619f, BG_WS_FLAG_RESPAWN_TIME/1000) - // buffs - || !AddObject(BG_WS_OBJECT_SPEEDBUFF_1, BG_OBJECTID_SPEEDBUFF_ENTRY, 1449.93f, 1470.71f, 342.6346f, -1.64061f, 0, 0, 0.7313537f, -0.6819983f, BUFF_RESPAWN_TIME) - || !AddObject(BG_WS_OBJECT_SPEEDBUFF_2, BG_OBJECTID_SPEEDBUFF_ENTRY, 1005.171f, 1447.946f, 335.9032f, 1.64061f, 0, 0, 0.7313537f, 0.6819984f, BUFF_RESPAWN_TIME) - || !AddObject(BG_WS_OBJECT_REGENBUFF_1, BG_OBJECTID_REGENBUFF_ENTRY, 1317.506f, 1550.851f, 313.2344f, -0.2617996f, 0, 0, 0.1305263f, -0.9914448f, BUFF_RESPAWN_TIME) - || !AddObject(BG_WS_OBJECT_REGENBUFF_2, BG_OBJECTID_REGENBUFF_ENTRY, 1110.451f, 1353.656f, 316.5181f, -0.6806787f, 0, 0, 0.333807f, -0.9426414f, BUFF_RESPAWN_TIME) - || !AddObject(BG_WS_OBJECT_BERSERKBUFF_1, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1320.09f, 1378.79f, 314.7532f, 1.186824f, 0, 0, 0.5591929f, 0.8290376f, BUFF_RESPAWN_TIME) - || !AddObject(BG_WS_OBJECT_BERSERKBUFF_2, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1139.688f, 1560.288f, 306.8432f, -2.443461f, 0, 0, 0.9396926f, -0.3420201f, BUFF_RESPAWN_TIME) - // alliance gates - || !AddObject(BG_WS_OBJECT_DOOR_A_1, BG_OBJECT_DOOR_A_1_WS_ENTRY, 1503.335f, 1493.466f, 352.1888f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_A_2, BG_OBJECT_DOOR_A_2_WS_ENTRY, 1492.478f, 1457.912f, 342.9689f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_A_3, BG_OBJECT_DOOR_A_3_WS_ENTRY, 1468.503f, 1494.357f, 351.8618f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_A_4, BG_OBJECT_DOOR_A_4_WS_ENTRY, 1471.555f, 1458.778f, 362.6332f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_A_5, BG_OBJECT_DOOR_A_5_WS_ENTRY, 1492.347f, 1458.34f, 342.3712f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_A_6, BG_OBJECT_DOOR_A_6_WS_ENTRY, 1503.466f, 1493.367f, 351.7352f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY) - // horde gates - || !AddObject(BG_WS_OBJECT_DOOR_H_1, BG_OBJECT_DOOR_H_1_WS_ENTRY, 949.1663f, 1423.772f, 345.6241f, -0.5756807f, -0.01673368f, -0.004956111f, -0.2839723f, 0.9586737f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_H_2, BG_OBJECT_DOOR_H_2_WS_ENTRY, 953.0507f, 1459.842f, 340.6526f, -1.99662f, -0.1971825f, 0.1575096f, -0.8239487f, 0.5073641f, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_H_3, BG_OBJECT_DOOR_H_3_WS_ENTRY, 949.9523f, 1422.751f, 344.9273f, 0.0f, 0, 0, 0, 1, RESPAWN_IMMEDIATELY) - || !AddObject(BG_WS_OBJECT_DOOR_H_4, BG_OBJECT_DOOR_H_4_WS_ENTRY, 950.7952f, 1459.583f, 342.1523f, 0.05235988f, 0, 0, 0.02617695f, 0.9996573f, RESPAWN_IMMEDIATELY) -) - { - sLog.outErrorDb("BatteGroundWS: Failed to spawn some object BattleGround not created!"); - return false; - } - - WorldSafeLocsEntry const *sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); - if (!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) - { - sLog.outErrorDb("BatteGroundWS: Failed to spawn Alliance spirit guide! BattleGround not created!"); - return false; - } - - sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); - if (!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) - { - sLog.outErrorDb("BatteGroundWS: Failed to spawn Horde spirit guide! BattleGround not created!"); - return false; - } - - sLog.outDebug("BatteGroundWS: BG objects and spirit guides spawned"); - - return true; -} - -void BattleGroundWS::Reset() -{ - //call parent's class reset - BattleGround::Reset(); - - m_FlagKeepers[BG_TEAM_ALLIANCE] = 0; - m_FlagKeepers[BG_TEAM_HORDE] = 0; - m_DroppedFlagGUID[BG_TEAM_ALLIANCE] = 0; - m_DroppedFlagGUID[BG_TEAM_HORDE] = 0; - m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; - m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; - m_TeamScores[BG_TEAM_ALLIANCE] = 0; - m_TeamScores[BG_TEAM_HORDE] = 0; - bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID()); - m_ReputationCapture = (isBGWeekend) ? 45 : 35; - m_HonorWinKills = (isBGWeekend) ? 3 : 1; - m_HonorEndKills = (isBGWeekend) ? 4 : 2; - // For WorldState - m_minutesElapsed = 0; - m_FirstFlagCaptureTeam = 0; - - /* Spirit nodes is static at this BG and then not required deleting at BG reset. - if (m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE]) - DelCreature(WS_SPIRIT_MAIN_ALLIANCE); - if (m_BgCreatures[WS_SPIRIT_MAIN_HORDE]) - DelCreature(WS_SPIRIT_MAIN_HORDE); - */ -} - -void BattleGroundWS::EndBattleGround(uint32 winner) -{ - //win reward - if (winner == ALLIANCE) - RewardHonorToTeam(GetBonusHonorFromKill(m_HonorWinKills), ALLIANCE); - if (winner == HORDE) - RewardHonorToTeam(GetBonusHonorFromKill(m_HonorWinKills), HORDE); - //complete map_end rewards (even if no team wins) - RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), ALLIANCE); - RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), HORDE); - - BattleGround::EndBattleGround(winner); -} - -void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer) -{ - if (GetStatus() != STATUS_IN_PROGRESS) - return; - - EventPlayerDroppedFlag(player); - - BattleGround::HandleKillPlayer(player, killer); -} - -void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) -{ - - BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); - if (itr == m_PlayerScores.end()) // player not found - return; - - switch(type) - { - case SCORE_FLAG_CAPTURES: // flags captured - ((BattleGroundWGScore*)itr->second)->FlagCaptures += value; - break; - case SCORE_FLAG_RETURNS: // flags returned - ((BattleGroundWGScore*)itr->second)->FlagReturns += value; - break; - default: - BattleGround::UpdatePlayerScore(Source, type, value, doAddHonor); - break; - } -} - -WorldSafeLocsEntry const* BattleGroundWS::GetClosestGraveYard(Player* player) -{ - //if status in progress, it returns main graveyards with spiritguides - //else it will return the graveyard in the flagroom - this is especially good - //if a player dies in preparation phase - then the player can't cheat - //and teleport to the graveyard outside the flagroom - //and start running around, while the doors are still closed - if (player->GetTeam() == ALLIANCE) - { - if (GetStatus() == STATUS_IN_PROGRESS) - return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); - else - return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_ALLIANCE); - } - else - { - if (GetStatus() == STATUS_IN_PROGRESS) - return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); - else - return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_HORDE); - } -} - -void BattleGroundWS::FillInitialWorldStates(WorldPacket& data) -{ - data << uint32(BG_WS_FLAG_CAPTURES_ALLIANCE) << uint32(GetTeamScore(ALLIANCE)); - data << uint32(BG_WS_FLAG_CAPTURES_HORDE) << uint32(GetTeamScore(HORDE)); - - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) - data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(-1); - else if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) - data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(1); - else - data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(0); - - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) - data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(-1); - else if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) - data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(1); - else - data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(0); - - data << uint32(BG_WS_FLAG_CAPTURES_MAX) << uint32(BG_WS_MAX_TEAM_SCORE); - - if (GetStatus() == STATUS_IN_PROGRESS) - { - data << uint32(BG_WS_STATE_TIMER_ACTIVE) << uint32(1); - data << uint32(BG_WS_STATE_TIMER) << uint32(25-m_minutesElapsed); - } - else - data << uint32(BG_WS_STATE_TIMER_ACTIVE) << uint32(0); - - if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) - data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(2); - else - data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(1); - - if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) - data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(2); - else - data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(1); - -} - diff --git a/src/server/game/BattleGroundWS.h b/src/server/game/BattleGroundWS.h deleted file mode 100644 index 1a733c14570..00000000000 --- a/src/server/game/BattleGroundWS.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __BATTLEGROUNDWS_H -#define __BATTLEGROUNDWS_H - -#include "BattleGround.h" - -enum BG_WS_TimerOrScore -{ - BG_WS_MAX_TEAM_SCORE = 3, - BG_WS_FLAG_RESPAWN_TIME = 23000, - BG_WS_FLAG_DROP_TIME = 10000, - BG_WS_SPELL_FORCE_TIME = 600000, - BG_WS_SPELL_BRUTAL_TIME = 900000 -}; - -enum BG_WS_Sound -{ - BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, - BG_WS_SOUND_FLAG_CAPTURED_HORDE = 8213, - BG_WS_SOUND_FLAG_PLACED = 8232, - BG_WS_SOUND_FLAG_RETURNED = 8192, - BG_WS_SOUND_HORDE_FLAG_PICKED_UP = 8212, - BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP = 8174, - BG_WS_SOUND_FLAGS_RESPAWNED = 8232 -}; - -enum BG_WS_SpellId -{ - BG_WS_SPELL_WARSONG_FLAG = 23333, - BG_WS_SPELL_WARSONG_FLAG_DROPPED = 23334, - BG_WS_SPELL_SILVERWING_FLAG = 23335, - BG_WS_SPELL_SILVERWING_FLAG_DROPPED = 23336, - BG_WS_SPELL_FOCUSED_ASSAULT = 46392, - BG_WS_SPELL_BRUTAL_ASSAULT = 46393 -}; - -enum BG_WS_WorldStates -{ - BG_WS_FLAG_UNK_ALLIANCE = 1545, - BG_WS_FLAG_UNK_HORDE = 1546, -// FLAG_UNK = 1547, - BG_WS_FLAG_CAPTURES_ALLIANCE = 1581, - BG_WS_FLAG_CAPTURES_HORDE = 1582, - BG_WS_FLAG_CAPTURES_MAX = 1601, - BG_WS_FLAG_STATE_HORDE = 2338, - BG_WS_FLAG_STATE_ALLIANCE = 2339, - BG_WS_STATE_TIMER = 4248, - BG_WS_STATE_TIMER_ACTIVE = 4247 -}; - -enum BG_WS_ObjectTypes -{ - BG_WS_OBJECT_DOOR_A_1 = 0, - BG_WS_OBJECT_DOOR_A_2 = 1, - BG_WS_OBJECT_DOOR_A_3 = 2, - BG_WS_OBJECT_DOOR_A_4 = 3, - BG_WS_OBJECT_DOOR_A_5 = 4, - BG_WS_OBJECT_DOOR_A_6 = 5, - BG_WS_OBJECT_DOOR_H_1 = 6, - BG_WS_OBJECT_DOOR_H_2 = 7, - BG_WS_OBJECT_DOOR_H_3 = 8, - BG_WS_OBJECT_DOOR_H_4 = 9, - BG_WS_OBJECT_A_FLAG = 10, - BG_WS_OBJECT_H_FLAG = 11, - BG_WS_OBJECT_SPEEDBUFF_1 = 12, - BG_WS_OBJECT_SPEEDBUFF_2 = 13, - BG_WS_OBJECT_REGENBUFF_1 = 14, - BG_WS_OBJECT_REGENBUFF_2 = 15, - BG_WS_OBJECT_BERSERKBUFF_1 = 16, - BG_WS_OBJECT_BERSERKBUFF_2 = 17, - BG_WS_OBJECT_MAX = 18 -}; - -enum BG_WS_ObjectEntry -{ - BG_OBJECT_DOOR_A_1_WS_ENTRY = 179918, - BG_OBJECT_DOOR_A_2_WS_ENTRY = 179919, - BG_OBJECT_DOOR_A_3_WS_ENTRY = 179920, - BG_OBJECT_DOOR_A_4_WS_ENTRY = 179921, - BG_OBJECT_DOOR_A_5_WS_ENTRY = 180322, - BG_OBJECT_DOOR_A_6_WS_ENTRY = 180322, - BG_OBJECT_DOOR_H_1_WS_ENTRY = 179916, - BG_OBJECT_DOOR_H_2_WS_ENTRY = 179917, - BG_OBJECT_DOOR_H_3_WS_ENTRY = 180322, - BG_OBJECT_DOOR_H_4_WS_ENTRY = 180322, - BG_OBJECT_A_FLAG_WS_ENTRY = 179830, - BG_OBJECT_H_FLAG_WS_ENTRY = 179831, - BG_OBJECT_A_FLAG_GROUND_WS_ENTRY = 179785, - BG_OBJECT_H_FLAG_GROUND_WS_ENTRY = 179786 -}; - -enum BG_WS_FlagState -{ - BG_WS_FLAG_STATE_ON_BASE = 0, - BG_WS_FLAG_STATE_WAIT_RESPAWN = 1, - BG_WS_FLAG_STATE_ON_PLAYER = 2, - BG_WS_FLAG_STATE_ON_GROUND = 3 -}; - -enum BG_WS_Graveyards -{ - WS_GRAVEYARD_FLAGROOM_ALLIANCE = 769, - WS_GRAVEYARD_FLAGROOM_HORDE = 770, - WS_GRAVEYARD_MAIN_ALLIANCE = 771, - WS_GRAVEYARD_MAIN_HORDE = 772 -}; - -enum BG_WS_CreatureTypes -{ - WS_SPIRIT_MAIN_ALLIANCE = 0, - WS_SPIRIT_MAIN_HORDE = 1, - - BG_CREATURES_MAX_WS = 2 -}; - -enum BG_WS_CarrierDebuffs -{ - WS_SPELL_FOCUSED_ASSAULT = 46392, - WS_SPELL_BRUTAL_ASSAULT = 46393 -}; - -class BattleGroundWGScore : public BattleGroundScore -{ - public: - BattleGroundWGScore() : FlagCaptures(0), FlagReturns(0) {}; - virtual ~BattleGroundWGScore() {}; - uint32 FlagCaptures; - uint32 FlagReturns; -}; - -class BattleGroundWS : public BattleGround -{ - friend class BattleGroundMgr; - - public: - /* Construction */ - BattleGroundWS(); - ~BattleGroundWS(); - void Update(uint32 diff); - - /* inherited from BattlegroundClass */ - virtual void AddPlayer(Player *plr); - virtual void StartingEventCloseDoors(); - virtual void StartingEventOpenDoors(); - - /* BG Flags */ - uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; } - uint64 GetHordeFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_HORDE]; } - void SetAllianceFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_ALLIANCE] = guid; } - void SetHordeFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_HORDE] = guid; } - bool IsAllianceFlagPickedup() const { return m_FlagKeepers[BG_TEAM_ALLIANCE] != 0; } - bool IsHordeFlagPickedup() const { return m_FlagKeepers[BG_TEAM_HORDE] != 0; } - void RespawnFlag(uint32 Team, bool captured); - void RespawnFlagAfterDrop(uint32 Team); - uint8 GetFlagState(uint32 team) { return m_FlagState[GetTeamIndexByTeamId(team)]; } - void AddTimedAura(uint32 aura); - void RemoveTimedAura(uint32 aura); - bool IsBrutalTimerDone; - bool IsForceTimerDone; - - /* Battleground Events */ - virtual void EventPlayerDroppedFlag(Player *Source); - virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); - virtual void EventPlayerCapturedFlag(Player *Source); - - void RemovePlayer(Player *plr, uint64 guid); - void HandleAreaTrigger(Player *Source, uint32 Trigger); - void HandleKillPlayer(Player *player, Player *killer); - bool SetupBattleGround(); - virtual void Reset(); - void EndBattleGround(uint32 winner); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); - - void UpdateFlagState(uint32 team, uint32 value); - void SetFirstFlagCapture(uint32 team) { m_FirstFlagCaptureTeam = team; } - void UpdateTeamScore(uint32 team); - void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); - void SetDroppedFlagGUID(uint64 guid, uint32 TeamID) { m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)] = guid;} - uint64 GetDroppedFlagGUID(uint32 TeamID) { return m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)];} - virtual void FillInitialWorldStates(WorldPacket& data); - - /* Scorekeeping */ - uint32 GetTeamScore(uint32 TeamID) const { return m_TeamScores[GetTeamIndexByTeamId(TeamID)]; } - void AddPoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] += Points; } - void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } - void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } - private: - uint64 m_FlagKeepers[2]; // 0 - alliance, 1 - horde - uint64 m_DroppedFlagGUID[2]; - uint8 m_FlagState[2]; // for checking flag state - int32 m_FlagsTimer[2]; - int32 m_FlagsDropTimer[2]; - uint32 m_FirstFlagCaptureTeam; // Winner is based on this if score is equal - - uint32 m_ReputationCapture; - uint32 m_HonorWinKills; - uint32 m_HonorEndKills; - int32 m_FlagSpellForceTimer; - bool m_BothFlagsKept; - uint8 m_FlagDebuffState; // 0 - no debuffs, 1 - focused assault, 2 - brutal assault - uint8 m_minutesElapsed; -}; -#endif - diff --git a/src/server/game/BattleGrounds/ArenaTeam.cpp b/src/server/game/BattleGrounds/ArenaTeam.cpp new file mode 100644 index 00000000000..4224ff6357a --- /dev/null +++ b/src/server/game/BattleGrounds/ArenaTeam.cpp @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectMgr.h" +#include "WorldPacket.h" + +#include "ArenaTeam.h" +#include "World.h" + +void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot) +{ + if (int32(personal_rating) + mod < 0) + personal_rating = 0; + else + personal_rating += mod; + if (plr) + plr->SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING, personal_rating); +} + +ArenaTeam::ArenaTeam() +{ + m_TeamId = 0; + m_Type = 0; + m_Name = ""; + m_CaptainGuid = 0; + m_BackgroundColor = 0; // background + m_EmblemStyle = 0; // icon + m_EmblemColor = 0; // icon color + m_BorderStyle = 0; // border + m_BorderColor = 0; // border color + m_stats.games_week = 0; + m_stats.games_season = 0; + m_stats.rank = 0; + if (sWorld.getConfig(CONFIG_ARENA_START_RATING) >= 0) + m_stats.rating = sWorld.getConfig(CONFIG_ARENA_START_RATING); + else if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) + m_stats.rating = 0; + else + m_stats.rating = 1500; + m_stats.wins_week = 0; + m_stats.wins_season = 0; +} + +ArenaTeam::~ArenaTeam() +{ +} + +bool ArenaTeam::Create(uint64 captainGuid, uint32 type, std::string ArenaTeamName) +{ + if (!objmgr.GetPlayer(captainGuid)) // player not exist + return false; + if (objmgr.GetArenaTeamByName(ArenaTeamName)) // arena team with this name already exist + return false; + + sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(captainGuid)); + + m_CaptainGuid = captainGuid; + m_Name = ArenaTeamName; + m_Type = type; + + m_TeamId = objmgr.GenerateArenaTeamId(); + + // ArenaTeamName already assigned to ArenaTeam::name, use it to encode string for DB + CharacterDatabase.escape_string(ArenaTeamName); + + CharacterDatabase.BeginTransaction(); + // CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid='%u'", m_TeamId); - MAX(arenateam)+1 not exist + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", m_TeamId); + CharacterDatabase.PExecute("INSERT INTO arena_team (arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor) " + "VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')", + m_TeamId, ArenaTeamName.c_str(), GUID_LOPART(m_CaptainGuid), m_Type, m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor); + CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games, wins, played, wins2, rank) VALUES " + "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", m_TeamId, m_stats.rating, m_stats.games_week, m_stats.wins_week, m_stats.games_season, m_stats.wins_season, m_stats.rank); + + CharacterDatabase.CommitTransaction(); + + AddMember(m_CaptainGuid); + sLog.outArena("New ArenaTeam created [Id: %u] [Type: %u] [Captain GUID: %u]", GetId(), GetType(), GetCaptain()); + return true; +} + +bool ArenaTeam::AddMember(const uint64& PlayerGuid) +{ + std::string plName; + uint8 plClass; + + // arena team is full (can't have more than type * 2 players!) + if (GetMembersSize() >= GetType() * 2) + return false; + + Player *pl = objmgr.GetPlayer(PlayerGuid); + if (pl) + { + if (pl->GetArenaTeamId(GetSlot())) + { + sLog.outError("Arena::AddMember() : player already in this sized team"); + return false; + } + + plClass = pl->getClass(); + plName = pl->GetName(); + } + else + { + // 0 1 + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT name, class FROM characters WHERE guid='%u'", GUID_LOPART(PlayerGuid)); + if (!result) + return false; + + plName = (*result)[0].GetCppString(); + plClass = (*result)[1].GetUInt8(); + + // check if player already in arenateam of that size + if (Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0) + { + sLog.outError("Arena::AddMember() : player already in this sized team"); + return false; + } + } + + // remove all player signs from another petitions + // this will be prevent attempt joining player to many arenateams and corrupt arena team data integrity + Player::RemovePetitionsAndSigns(PlayerGuid, GetType()); + + ArenaTeamMember newmember; + newmember.name = plName; + newmember.guid = PlayerGuid; + newmember.Class = plClass; + newmember.games_season = 0; + newmember.games_week = 0; + newmember.wins_season = 0; + newmember.wins_week = 0; + newmember.personal_rating = 0; + + if (sWorld.getConfig(CONFIG_ARENA_START_PERSONAL_RATING) > 0) + newmember.personal_rating = sWorld.getConfig(CONFIG_ARENA_START_PERSONAL_RATING); + else + { + if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) < 6) + newmember.personal_rating = 1500; + else + if (GetRating() >= 1000) + newmember.personal_rating = 1000; + } + + m_members.push_back(newmember); + + CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", m_TeamId, GUID_LOPART(newmember.guid), newmember.personal_rating); + + if (pl) + { + pl->SetInArenaTeam(m_TeamId, GetSlot(), GetType()); + pl->SetArenaTeamIdInvited(0); + pl->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_PERSONAL_RATING, newmember.personal_rating); + + // hide promote/remove buttons + if (m_CaptainGuid != PlayerGuid) + pl->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1); + sLog.outArena("Player: %s [GUID: %u] joined arena team type: %u [Id: %u].", pl->GetName(), pl->GetGUIDLow(), GetType(), GetId()); + } + return true; +} + +bool ArenaTeam::LoadArenaTeamFromDB(QueryResult_AutoPtr arenaTeamDataResult) +{ + if (!arenaTeamDataResult) + return false; + + Field *fields = arenaTeamDataResult->Fetch(); + + m_TeamId = fields[0].GetUInt32(); + m_Name = fields[1].GetCppString(); + m_CaptainGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); + m_Type = fields[3].GetUInt32(); + m_BackgroundColor = fields[4].GetUInt32(); + m_EmblemStyle = fields[5].GetUInt32(); + m_EmblemColor = fields[6].GetUInt32(); + m_BorderStyle = fields[7].GetUInt32(); + m_BorderColor = fields[8].GetUInt32(); + //load team stats + m_stats.rating = fields[9].GetUInt32(); + m_stats.games_week = fields[10].GetUInt32(); + m_stats.wins_week = fields[11].GetUInt32(); + m_stats.games_season = fields[12].GetUInt32(); + m_stats.wins_season = fields[13].GetUInt32(); + m_stats.rank = fields[14].GetUInt32(); + + return true; +} + +bool ArenaTeam::LoadMembersFromDB(QueryResult_AutoPtr arenaTeamMembersResult) +{ + if (!arenaTeamMembersResult) + return false; + + bool captainPresentInTeam = false; + + do + { + Field *fields = arenaTeamMembersResult->Fetch(); + //prevent crash if db records are broken, when all members in result are already processed and current team hasn't got any members + if (!fields) + break; + uint32 arenaTeamId = fields[0].GetUInt32(); + if (arenaTeamId < m_TeamId) + { + //there is in table arena_team_member record which doesn't have arenateamid in arena_team table, report error + sLog.outErrorDb("ArenaTeam %u does not exist but it has record in arena_team_member table, deleting it!", arenaTeamId); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", arenaTeamId); + continue; + } + + if (arenaTeamId > m_TeamId) + //we loaded all members for this arena_team already, break cycle + break; + + ArenaTeamMember newmember; + newmember.guid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_PLAYER); + newmember.games_week = fields[2].GetUInt32(); + newmember.wins_week = fields[3].GetUInt32(); + newmember.games_season = fields[4].GetUInt32(); + newmember.wins_season = fields[5].GetUInt32(); + newmember.personal_rating = fields[6].GetUInt32(); + newmember.name = fields[7].GetCppString(); + newmember.Class = fields[8].GetUInt8(); + + //check if member exists in characters table + if (newmember.name.empty()) + { + sLog.outErrorDb("ArenaTeam %u has member with empty name - probably player %u doesn't exist, deleting him from memberlist!", arenaTeamId, GUID_LOPART(newmember.guid)); + this->DelMember(newmember.guid); + continue; + } + + if (newmember.guid == GetCaptain()) + captainPresentInTeam = true; + + m_members.push_back(newmember); + }while (arenaTeamMembersResult->NextRow()); + + if (Empty() || !captainPresentInTeam) + { + // arena team is empty or captain is not in team, delete from db + sLog.outErrorDb("ArenaTeam %u does not have any members or its captain is not in team, disbanding it...", m_TeamId); + return false; + } + + return true; +} + +void ArenaTeam::SetCaptain(const uint64& guid) +{ + // disable remove/promote buttons + Player *oldcaptain = objmgr.GetPlayer(GetCaptain()); + if (oldcaptain) + oldcaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1); + + // set new captain + m_CaptainGuid = guid; + + // update database + CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), GetId()); + + // enable remove/promote buttons + Player *newcaptain = objmgr.GetPlayer(guid); + if (newcaptain) + { + newcaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 0); + sLog.outArena("Player: %s [GUID: %u] promoted player: %s [GUID: %u] to leader of arena team [Id: %u] [Type: %u].", oldcaptain->GetName(), oldcaptain->GetGUIDLow(), newcaptain->GetName(), newcaptain->GetGUIDLow(), GetId(), GetType()); + } +} + +void ArenaTeam::DelMember(uint64 guid) +{ + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + if (itr->guid == guid) + { + m_members.erase(itr); + break; + } + + if (Player *player = objmgr.GetPlayer(guid)) + { + player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0); + // delete all info regarding this team + for (uint32 i = 0; i < ARENA_TEAM_END; ++i) + player->SetArenaTeamInfoField(GetSlot(), ArenaTeamInfoType(i), 0); + sLog.outArena("Player: %s [GUID: %u] left arena team type: %u [Id: %u].", player->GetName(), player->GetGUIDLow(), GetType(), GetId()); + } + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u' AND guid = '%u'", GetId(), GUID_LOPART(guid)); +} + +void ArenaTeam::Disband(WorldSession *session) +{ + // event + if (session) + // probably only 1 string required... + BroadcastEvent(ERR_ARENA_TEAM_DISBANDED_S, 0, 2, session->GetPlayerName(), GetName(), ""); + + while (!m_members.empty()) + // Removing from members is done in DelMember. + DelMember(m_members.front().guid); + + if (session) + if (Player *player = session->GetPlayer()) + sLog.outArena("Player: %s [GUID: %u] disbanded arena team type: %u [Id: %u].", player->GetName(), player->GetGUIDLow(), GetType(), GetId()); + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", m_TeamId); + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", m_TeamId); //< this should be alredy done by calling DelMember(memberGuids[j]); for each member + CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", m_TeamId); + CharacterDatabase.CommitTransaction(); + objmgr.RemoveArenaTeam(m_TeamId); +} + +void ArenaTeam::Roster(WorldSession *session) +{ + Player *pl = NULL; + + uint8 unk308 = 0; + + WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100); + data << uint32(GetId()); // team id + data << uint8(unk308); // 308 unknown value but affect packet structure + data << uint32(GetMembersSize()); // members count + data << uint32(GetType()); // arena team type? + + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + pl = objmgr.GetPlayer(itr->guid); + + data << uint64(itr->guid); // guid + data << uint8((pl ? 1 : 0)); // online flag + data << itr->name; // member name + data << uint32((itr->guid == GetCaptain() ? 0 : 1));// captain flag 0 captain 1 member + data << uint8((pl ? pl->getLevel() : 0)); // unknown, level? + data << uint8(itr->Class); // class + data << uint32(itr->games_week); // played this week + data << uint32(itr->wins_week); // wins this week + data << uint32(itr->games_season); // played this season + data << uint32(itr->wins_season); // wins this season + data << uint32(itr->personal_rating); // personal rating + if (unk308) + { + data << float(0.0); // 308 unk + data << float(0.0); // 308 unk + } + } + + session->SendPacket(&data); + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER"); +} + +void ArenaTeam::Query(WorldSession *session) +{ + WorldPacket data(SMSG_ARENA_TEAM_QUERY_RESPONSE, 4*7+GetName().size()+1); + data << uint32(GetId()); // team id + data << GetName(); // team name + data << uint32(GetType()); // arena team type (2=2x2, 3=3x3 or 5=5x5) + data << uint32(m_BackgroundColor); // background color + data << uint32(m_EmblemStyle); // emblem style + data << uint32(m_EmblemColor); // emblem color + data << uint32(m_BorderStyle); // border style + data << uint32(m_BorderColor); // border color + session->SendPacket(&data); + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE"); +} + +void ArenaTeam::Stats(WorldSession *session) +{ + WorldPacket data(SMSG_ARENA_TEAM_STATS, 4*7); + data << uint32(GetId()); // team id + data << uint32(m_stats.rating); // rating + data << uint32(m_stats.games_week); // games this week + data << uint32(m_stats.wins_week); // wins this week + data << uint32(m_stats.games_season); // played this season + data << uint32(m_stats.wins_season); // wins this season + data << uint32(m_stats.rank); // rank + session->SendPacket(&data); +} + +void ArenaTeam::NotifyStatsChanged() +{ + // this is called after a rated match ended + // updates arena team stats for every member of the team (not only the ones who participated!) + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + Player * plr = objmgr.GetPlayer(itr->guid); + if (plr) + Stats(plr->GetSession()); + } +} + +void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) +{ + ArenaTeamMember* member = GetMember(guid); + if (!member) + return; + + WorldPacket data(MSG_INSPECT_ARENA_TEAMS, 8+1+4*6); + data << uint64(guid); // player guid + data << uint8(GetSlot()); // slot (0...2) + data << uint32(GetId()); // arena team id + data << uint32(m_stats.rating); // rating + data << uint32(m_stats.games_season); // season played + data << uint32(m_stats.wins_season); // season wins + data << uint32(member->games_season); // played (count of all games, that the inspected member participated...) + data << uint32(member->personal_rating); // personal rating + session->SendPacket(&data); +} + +void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor) +{ + m_BackgroundColor = backgroundColor; + m_EmblemStyle = emblemStyle; + m_EmblemColor = emblemColor; + m_BorderStyle = borderStyle; + m_BorderColor = borderColor; + + CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_TeamId); +} + +void ArenaTeam::SetStats(uint32 stat_type, uint32 value) +{ + switch(stat_type) + { + case STAT_TYPE_RATING: + m_stats.rating = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_GAMES_WEEK: + m_stats.games_week = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET games = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_WINS_WEEK: + m_stats.wins_week = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_GAMES_SEASON: + m_stats.games_season = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET played = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_WINS_SEASON: + m_stats.wins_season = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins2 = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_RANK: + m_stats.rank = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rank = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + default: + sLog.outDebug("unknown stat type in ArenaTeam::SetStats() %u", stat_type); + break; + } +} + +void ArenaTeam::BroadcastPacket(WorldPacket *packet) +{ + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + Player *player = objmgr.GetPlayer(itr->guid); + if (player) + player->GetSession()->SendPacket(packet); + } +} + +void ArenaTeam::BroadcastEvent(ArenaTeamEvents event, uint64 guid, uint8 strCount, std::string str1, std::string str2, std::string str3) +{ + WorldPacket data(SMSG_ARENA_TEAM_EVENT, 1+1+1); + data << uint8(event); + data << uint8(strCount); + switch (strCount) + { + case 0: + break; + case 1: + data << str1; + break; + case 2: + data << str1 << str2; + break; + case 3: + data << str1 << str2 << str3; + break; + default: + sLog.outError("Unhandled strCount %u in ArenaTeam::BroadcastEvent", strCount); + return; + } + + if (guid) + data << uint64(guid); + + BroadcastPacket(&data); + + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_EVENT"); +} + +uint8 ArenaTeam::GetSlotByType(uint32 type) +{ + switch(type) + { + case ARENA_TEAM_2v2: return 0; + case ARENA_TEAM_3v3: return 1; + case ARENA_TEAM_5v5: return 2; + default: + break; + } + sLog.outError("FATAL: Unknown arena team type %u for some arena team", type); + return 0xFF; +} + +bool ArenaTeam::HaveMember(const uint64& guid) const +{ + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + if (itr->guid == guid) + return true; + + return false; +} + +uint32 ArenaTeam::GetPoints(uint32 MemberRating) +{ + // returns how many points would be awarded with this team type with this rating + float points; + + uint32 rating = MemberRating + 150 < m_stats.rating ? MemberRating : m_stats.rating; + + if (rating <= 1500) + { + if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) < 6) + points = (float)rating * 0.22f + 14.0f; + else + points = 344; + } + else + points = 1511.26f / (1.0f + 1639.28f * exp(-0.00412f * (float)rating)); + + // type penalties for <5v5 teams + if (m_Type == ARENA_TEAM_2v2) + points *= 0.76f; + else if (m_Type == ARENA_TEAM_3v3) + points *= 0.88f; + + return (uint32) points; +} + +float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating) +{ + // returns the chance to win against a team with the given rating, used in the rating adjustment calculation + // ELO system + + if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6) + if (enemy_rating < 1000) + enemy_rating = 1000; + return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f)); +} + +void ArenaTeam::FinishGame(int32 mod) +{ + if (int32(m_stats.rating) + mod < 0) + m_stats.rating = 0; + else + m_stats.rating += mod; + + m_stats.games_week += 1; + m_stats.games_season += 1; + // update team's rank + m_stats.rank = 1; + ObjectMgr::ArenaTeamMap::const_iterator i = objmgr.GetArenaTeamMapBegin(); + for (; i != objmgr.GetArenaTeamMapEnd(); ++i) + { + if (i->second->GetType() == m_Type && i->second->GetStats().rating > m_stats.rating) + ++m_stats.rank; + } +} + +int32 ArenaTeam::WonAgainst(uint32 againstRating) +{ + // called when the team has won + // 'chance' calculation - to beat the opponent + float chance = GetChanceAgainst(m_stats.rating, againstRating); + float K = (m_stats.rating < 1000) ? 48.0f : 32.0f; + // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) + int32 mod = (int32)floor(K* (1.0f - chance)); + + // modify the team stats accordingly + FinishGame(mod); + m_stats.wins_week += 1; + m_stats.wins_season += 1; + + // return the rating change, used to display it on the results screen + return mod; +} + +int32 ArenaTeam::LostAgainst(uint32 againstRating) +{ + // called when the team has lost + //'chance' calculation - to loose to the opponent + float chance = GetChanceAgainst(m_stats.rating, againstRating); + if (m_stats.rating < 1000) + { + FinishGame(0); + return 0; + } + + float K = 32.0f; + int32 mod = (int32)ceil(K * (0.0f - chance)); + // modify the team stats accordingly + FinishGame(mod); + + // return the rating change, used to display it on the results screen + return mod; +} + +void ArenaTeam::MemberLost(Player * plr, uint32 againstRating) +{ + // called for each participant of a match after losing + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + if (itr->guid == plr->GetGUID()) + { + // update personal rating + float chance = GetChanceAgainst(itr->personal_rating, againstRating); + float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f; + // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) + int32 mod = (int32)ceil(K * (0.0f - chance)); + itr->ModifyPersonalRating(plr, mod, GetSlot()); + // update personal played stats + itr->games_week +=1; + itr->games_season +=1; + // update the unit fields + plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->games_week); + plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->games_season); + return; + } + } +} + +void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating) +{ + // called for offline player after ending rated arena match! + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + if (itr->guid == guid) + { + // update personal rating + float chance = GetChanceAgainst(itr->personal_rating, againstRating); + float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f; + // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) + int32 mod = (int32)ceil(K * (0.0f - chance)); + if (int32(itr->personal_rating) + mod < 0) + itr->personal_rating = 0; + else + itr->personal_rating += mod; + // update personal played stats + itr->games_week +=1; + itr->games_season +=1; + return; + } + } +} + +void ArenaTeam::MemberWon(Player * plr, uint32 againstRating) +{ + // called for each participant after winning a match + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + if (itr->guid == plr->GetGUID()) + { + // update personal rating + float chance = GetChanceAgainst(itr->personal_rating, againstRating); + float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f; + // calculate the rating modification (ELO system with k=32 or k=48 if rating<1000) + int32 mod = (int32)floor(K* (1.0f - chance)); + itr->ModifyPersonalRating(plr, mod, GetSlot()); + // update personal stats + itr->games_week +=1; + itr->games_season +=1; + itr->wins_season += 1; + itr->wins_week += 1; + // update unit fields + plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->games_week); + plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->games_season); + return; + } + } +} + +void ArenaTeam::UpdateArenaPointsHelper(std::map& PlayerPoints) +{ + // called after a match has ended and the stats are already modified + // helper function for arena point distribution (this way, when distributing, no actual calculation is required, just a few comparisons) + // 10 played games per week is a minimum + if (m_stats.games_week < 10) + return; + // to get points, a player has to participate in at least 30% of the matches + uint32 min_plays = (uint32) ceil(m_stats.games_week * 0.3); + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + // the player participated in enough games, update his points + uint32 points_to_add = 0; + if (itr->games_week >= min_plays) + points_to_add = GetPoints(itr->personal_rating); + // OBSOLETE : CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", points_to_add, m_TeamId, itr->guid); + + std::map::iterator plr_itr = PlayerPoints.find(GUID_LOPART(itr->guid)); + if (plr_itr != PlayerPoints.end()) + { + //check if there is already more points + if (plr_itr->second < points_to_add) + PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add; + } + else + PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add; + } +} + +void ArenaTeam::SaveToDB() +{ + // save team and member stats to db + // called after a match has ended, or when calculating arena_points + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games = '%u',played = '%u',rank = '%u',wins = '%u',wins2 = '%u' WHERE arenateamid = '%u'", m_stats.rating, m_stats.games_week, m_stats.games_season, m_stats.rank, m_stats.wins_week, m_stats.wins_season, GetId()); + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, m_TeamId, GUID_LOPART(itr->guid)); + } + CharacterDatabase.CommitTransaction(); +} + +void ArenaTeam::FinishWeek() +{ + m_stats.games_week = 0; // played this week + m_stats.wins_week = 0; // wins this week + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + { + itr->games_week = 0; + itr->wins_week = 0; + } +} + +bool ArenaTeam::IsFighting() const +{ + for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + if (Player *p = objmgr.GetPlayer(itr->guid)) + if (p->GetMap()->IsBattleArena()) + return true; + return false; +} diff --git a/src/server/game/BattleGrounds/ArenaTeam.h b/src/server/game/BattleGrounds/ArenaTeam.h new file mode 100644 index 00000000000..e6f85cf8d9e --- /dev/null +++ b/src/server/game/BattleGrounds/ArenaTeam.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITYCORE_ARENATEAM_H +#define TRINITYCORE_ARENATEAM_H + +enum ArenaTeamCommandTypes +{ + ERR_ARENA_TEAM_CREATE_S = 0x00, + ERR_ARENA_TEAM_INVITE_SS = 0x01, + ERR_ARENA_TEAM_QUIT_S = 0x03, + ERR_ARENA_TEAM_FOUNDER_S = 0x0E +}; + +enum ArenaTeamCommandErrors +{ + ERR_ARENA_TEAM_INTERNAL = 0x01, + ERR_ALREADY_IN_ARENA_TEAM = 0x02, + ERR_ALREADY_IN_ARENA_TEAM_S = 0x03, + ERR_INVITED_TO_ARENA_TEAM = 0x04, + ERR_ALREADY_INVITED_TO_ARENA_TEAM_S = 0x05, + ERR_ARENA_TEAM_NAME_INVALID = 0x06, + ERR_ARENA_TEAM_NAME_EXISTS_S = 0x07, + ERR_ARENA_TEAM_LEADER_LEAVE_S = 0x08, + ERR_ARENA_TEAM_PERMISSIONS = 0x08, + ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM = 0x09, + ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS = 0x0A, + ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S = 0x0B, + ERR_ARENA_TEAM_NOT_ALLIED = 0x0C, + ERR_ARENA_TEAM_IGNORING_YOU_S = 0x13, + ERR_ARENA_TEAM_TARGET_TOO_LOW_S = 0x15, + ERR_ARENA_TEAM_TARGET_TOO_HIGH_S = 0x16, + ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S = 0x17, + ERR_ARENA_TEAM_NOT_FOUND = 0x1B, + ERR_ARENA_TEAMS_LOCKED = 0x1E +}; + +enum ArenaTeamEvents +{ + ERR_ARENA_TEAM_JOIN_SS = 3, // player name + arena team name + ERR_ARENA_TEAM_LEAVE_SS = 4, // player name + arena team name + ERR_ARENA_TEAM_REMOVE_SSS = 5, // player name + arena team name + captain name + ERR_ARENA_TEAM_LEADER_IS_SS = 6, // player name + arena team name + ERR_ARENA_TEAM_LEADER_CHANGED_SSS = 7, // old captain + new captain + arena team name + ERR_ARENA_TEAM_DISBANDED_S = 8 // captain name + arena team name +}; + +/* +need info how to send these ones: +ERR_ARENA_TEAM_YOU_JOIN_S - client show it automatically when accept invite +ERR_ARENA_TEAM_TARGET_TOO_LOW_S +ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S +ERR_ARENA_TEAM_LEVEL_TOO_LOW_I +*/ + +enum ArenaTeamStatTypes +{ + STAT_TYPE_RATING = 0, + STAT_TYPE_GAMES_WEEK = 1, + STAT_TYPE_WINS_WEEK = 2, + STAT_TYPE_GAMES_SEASON = 3, + STAT_TYPE_WINS_SEASON = 4, + STAT_TYPE_RANK = 5 +}; + +enum ArenaTeamTypes +{ + ARENA_TEAM_2v2 = 2, + ARENA_TEAM_3v3 = 3, + ARENA_TEAM_5v5 = 5 +}; + +struct ArenaTeamMember +{ + uint64 guid; + std::string name; + uint8 Class; + uint32 games_week; + uint32 wins_week; + uint32 games_season; + uint32 wins_season; + uint32 personal_rating; + + void ModifyPersonalRating(Player* plr, int32 mod, uint32 slot); +}; + +struct ArenaTeamStats +{ + uint32 rating; + uint32 games_week; + uint32 wins_week; + uint32 games_season; + uint32 wins_season; + uint32 rank; +}; + +#define MAX_ARENA_SLOT 3 // 0..2 slots + +class ArenaTeam +{ + public: + ArenaTeam(); + ~ArenaTeam(); + + bool Create(uint64 captainGuid, uint32 type, std::string ArenaTeamName); + void Disband(WorldSession *session); + + typedef std::list MemberList; + + uint32 GetId() const { return m_TeamId; } + uint32 GetType() const { return m_Type; } + uint8 GetSlot() const { return GetSlotByType(GetType()); } + static uint8 GetSlotByType(uint32 type); + const uint64& GetCaptain() const { return m_CaptainGuid; } + std::string GetName() const { return m_Name; } + const ArenaTeamStats& GetStats() const { return m_stats; } + void SetStats(uint32 stat_type, uint32 value); + uint32 GetRating() const { return m_stats.rating; } + + uint32 GetEmblemStyle() const { return m_EmblemStyle; } + uint32 GetEmblemColor() const { return m_EmblemColor; } + uint32 GetBorderStyle() const { return m_BorderStyle; } + uint32 GetBorderColor() const { return m_BorderColor; } + uint32 GetBackgroundColor() const { return m_BackgroundColor; } + + void SetCaptain(const uint64& guid); + bool AddMember(const uint64& PlayerGuid); + + // Shouldn't be const uint64& ed, because than can reference guid from members on Disband + // and this method removes given record from list. So invalid reference can happen. + void DelMember(uint64 guid); + + void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor); + + size_t GetMembersSize() const { return m_members.size(); } + bool Empty() const { return m_members.empty(); } + MemberList::iterator m_membersBegin() { return m_members.begin(); } + MemberList::iterator m_membersEnd() { return m_members.end(); } + bool HaveMember(const uint64& guid) const; + + ArenaTeamMember* GetMember(const uint64& guid) + { + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + if (itr->guid == guid) + return &(*itr); + + return NULL; + } + + ArenaTeamMember* GetMember(const std::string& name) + { + for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) + if (itr->name == name) + return &(*itr); + + return NULL; + } + + bool IsFighting() const; + + bool LoadArenaTeamFromDB(QueryResult_AutoPtr arenaTeamDataResult); + bool LoadMembersFromDB(QueryResult_AutoPtr arenaTeamMembersResult); + void LoadStatsFromDB(uint32 ArenaTeamId); + + void SaveToDB(); + + void BroadcastPacket(WorldPacket *packet); + void BroadcastEvent(ArenaTeamEvents event, uint64 guid, uint8 strCount, std::string str1, std::string str2, std::string str3); + + void Roster(WorldSession *session); + void Query(WorldSession *session); + void Stats(WorldSession *session); + void InspectStats(WorldSession *session, uint64 guid); + + uint32 GetPoints(uint32 MemberRating); + float GetChanceAgainst(uint32 own_rating, uint32 enemy_rating); + int32 WonAgainst(uint32 againstRating); + void MemberWon(Player * plr, uint32 againstRating); + int32 LostAgainst(uint32 againstRating); + void MemberLost(Player * plr, uint32 againstRating); + void OfflineMemberLost(uint64 guid, uint32 againstRating); + + void UpdateArenaPointsHelper(std::map & PlayerPoints); + + void NotifyStatsChanged(); + + void FinishWeek(); + void FinishGame(int32 mod); + + protected: + + uint32 m_TeamId; + uint32 m_Type; + std::string m_Name; + uint64 m_CaptainGuid; + + uint32 m_BackgroundColor; // ARGB format + uint32 m_EmblemStyle; // icon id + uint32 m_EmblemColor; // ARGB format + uint32 m_BorderStyle; // border image id + uint32 m_BorderColor; // ARGB format + + MemberList m_members; + ArenaTeamStats m_stats; +}; +#endif + diff --git a/src/server/game/BattleGrounds/ArenaTeamHandler.cpp b/src/server/game/BattleGrounds/ArenaTeamHandler.cpp new file mode 100644 index 00000000000..3a9a14524f9 --- /dev/null +++ b/src/server/game/BattleGrounds/ArenaTeamHandler.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Database/DatabaseEnv.h" + +#include "ArenaTeam.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "SocialMgr.h" + +void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("MSG_INSPECT_ARENA_TEAMS"); + + uint64 guid; + recv_data >> guid; + sLog.outDebug("Inspect Arena stats (GUID: %u TypeId: %u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); + + if (Player *plr = objmgr.GetPlayer(guid)) + { + for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i) + { + if (uint32 a_id = plr->GetArenaTeamId(i)) + { + if (ArenaTeam *at = objmgr.GetArenaTeamById(a_id)) + at->InspectStats(this, plr->GetGUID()); + } + } + } +} + +void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("WORLD: Received CMSG_ARENA_TEAM_QUERY"); + + uint32 ArenaTeamId; + recv_data >> ArenaTeamId; + + if (ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId)) + { + arenateam->Query(this); + arenateam->Stats(this); + } +} + +void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("WORLD: Received CMSG_ARENA_TEAM_ROSTER"); + + uint32 ArenaTeamId; // arena team id + recv_data >> ArenaTeamId; + + if (ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId)) + arenateam->Roster(this); +} + +void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_INVITE"); + + uint32 ArenaTeamId; // arena team id + std::string Invitedname; + + Player * player = NULL; + + recv_data >> ArenaTeamId >> Invitedname; + + if (!Invitedname.empty()) + { + if (!normalizePlayerName(Invitedname)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str()); + } + + if (!player) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); + return; + } + + if (player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S); + return; + } + + ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); + if (!arenateam) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM); + return; + } + + // OK result but not send invite + if (player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) + return; + + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + return; + } + + if (player->GetArenaTeamId(arenateam->GetSlot())) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S); + return; + } + + if (player->GetArenaTeamIdInvited()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + return; + } + + if (arenateam->GetMembersSize() >= arenateam->GetType() * 2) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, arenateam->GetName(), "", ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S); + return; + } + + sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str()); + + player->SetArenaTeamIdInvited(arenateam->GetId()); + + WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10)); + data << GetPlayer()->GetName(); + data << arenateam->GetName(); + player->GetSession()->SendPacket(&data); + + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_INVITE"); +} + +void WorldSession::HandleArenaTeamAcceptOpcode(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("CMSG_ARENA_TEAM_ACCEPT"); // empty opcode + + ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited()); + if (!at) + return; + + if (_player->GetArenaTeamId(at->GetSlot())) + { + // already in arena team that size + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ALREADY_IN_ARENA_TEAM); + return; + } + + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain())) + { + // not let enemies sign petition + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + return; + } + + if (!at->AddMember(_player->GetGUID())) + { + // arena team not found + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_INTERNAL); + return; + } + + // event + at->BroadcastEvent(ERR_ARENA_TEAM_JOIN_SS, _player->GetGUID(), 2, _player->GetName(), at->GetName(), ""); +} + +void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("CMSG_ARENA_TEAM_DECLINE"); // empty opcode + + _player->SetArenaTeamIdInvited(0); // no more invited +} + +void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_LEAVE"); + + uint32 ArenaTeamId; // arena team id + recv_data >> ArenaTeamId; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if (!at) + return; + + if (_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1) + { + // check for correctness + SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); + return; + } + + // arena team has only one member (=captain) + if (_player->GetGUID() == at->GetCaptain()) + { + at->Disband(this); + delete at; + return; + } + + at->DelMember(_player->GetGUID()); + + // event + at->BroadcastEvent(ERR_ARENA_TEAM_LEAVE_SS, _player->GetGUID(), 2, _player->GetName(), at->GetName(), ""); + + // send you are no longer member of team + SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0); +} + +void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_DISBAND"); + + uint32 ArenaTeamId; // arena team id + recv_data >> ArenaTeamId; + + if (ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId)) + { + if (at->GetCaptain() != _player->GetGUID()) + return; + + if (at->IsFighting()) + return; + + at->Disband(this); + delete at; + } +} + +void WorldSession::HandleArenaTeamRemoveOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_REMOVE"); + + uint32 ArenaTeamId; + std::string name; + + recv_data >> ArenaTeamId; + recv_data >> name; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if (!at) // arena team not found + return; + + if (at->GetCaptain() != _player->GetGUID()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); + return; + } + + if (!normalizePlayerName(name)) + return; + + ArenaTeamMember* member = at->GetMember(name); + if (!member) // member not found + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); + return; + } + + if (at->GetCaptain() == member->guid) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); + return; + } + + at->DelMember(member->guid); + + // event + at->BroadcastEvent(ERR_ARENA_TEAM_REMOVE_SSS, 0, 3, name, at->GetName(), _player->GetName()); +} + +void WorldSession::HandleArenaTeamLeaderOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_LEADER"); + + uint32 ArenaTeamId; + std::string name; + + recv_data >> ArenaTeamId; + recv_data >> name; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if (!at) // arena team not found + return; + + if (at->GetCaptain() != _player->GetGUID()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); + return; + } + + if (!normalizePlayerName(name)) + return; + + ArenaTeamMember* member = at->GetMember(name); + if (!member) // member not found + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); + return; + } + + if (at->GetCaptain() == member->guid) // target player already captain + return; + + at->SetCaptain(member->guid); + + // event + at->BroadcastEvent(ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 0, 3, _player->GetName(), name, at->GetName()); +} + +void WorldSession::SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id) +{ + WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4); + data << uint32(team_action); + data << team; + data << player; + data << uint32(error_id); + SendPacket(&data); +} + +void WorldSession::SendNotInArenaTeamPacket(uint8 type) +{ + WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team + uint32 unk = 0; + data << uint32(unk); // unk(0) + if (!unk) + data << uint8(type); // team type (2=2v2,3=3v3,5=5v5), can be used for custom types... + SendPacket(&data); +} + +/* ++ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team" + ++ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]." ++ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s" ++ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s" +ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]." + ++ERR_ARENA_TEAM_INTERNAL "Internal arena team error" ++ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size" ++ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size" ++ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team" ++ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team" ++ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name" ++ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\"" ++ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team" ++ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that" ++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size" ++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s" ++ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found" ++ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance" + ++ERR_ARENA_TEAM_JOIN_SS "%s has joined %s" ++ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]." + ++ERR_ARENA_TEAM_LEAVE_SS "%s has left %s" + ++ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s" ++ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s" + ++ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s" + ++ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s" + +ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team" + +ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full" + +ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team" +*/ diff --git a/src/server/game/BattleGrounds/BattleGround.cpp b/src/server/game/BattleGrounds/BattleGround.cpp new file mode 100644 index 00000000000..5bfe2e139b6 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGround.cpp @@ -0,0 +1,1965 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" + +#include "ArenaTeam.h" +#include "BattleGround.h" +#include "BattleGroundMgr.h" +#include "Creature.h" +#include "Formulas.h" +#include "GridNotifiersImpl.h" +#include "Group.h" +#include "Language.h" +#include "MapManager.h" +#include "Object.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" +#include "Util.h" + +namespace Trinity +{ + class BattleGroundChatBuilder + { + public: + BattleGroundChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, va_list* args = NULL) + : i_msgtype(msgtype), i_textId(textId), i_source(source), i_args(args) {} + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* text = objmgr.GetTrinityString(i_textId,loc_idx); + + if (i_args) + { + // we need copy va_list before use or original va_list will corrupted + va_list ap; + va_copy(ap,*i_args); + + char str [2048]; + vsnprintf(str,2048,text, ap); + va_end(ap); + + do_helper(data,&str[0]); + } + else + do_helper(data,text); + } + private: + void do_helper(WorldPacket& data, char const* text) + { + uint64 target_guid = i_source ? i_source ->GetGUID() : 0; + + data << uint8(i_msgtype); + data << uint32(LANG_UNIVERSAL); + data << uint64(target_guid); // there 0 for BG messages + data << uint32(0); // can be chat msg group or something + data << uint64(target_guid); + data << uint32(strlen(text)+1); + data << text; + data << uint8(i_source ? i_source->chatTag() : uint8(0)); + } + + ChatMsg i_msgtype; + int32 i_textId; + Player const* i_source; + va_list* i_args; + }; + + class BattleGround2ChatBuilder + { + public: + BattleGround2ChatBuilder(ChatMsg msgtype, int32 textId, Player const* source, int32 arg1, int32 arg2) + : i_msgtype(msgtype), i_textId(textId), i_source(source), i_arg1(arg1), i_arg2(arg2) {} + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* text = objmgr.GetTrinityString(i_textId,loc_idx); + char const* arg1str = i_arg1 ? objmgr.GetTrinityString(i_arg1,loc_idx) : ""; + char const* arg2str = i_arg2 ? objmgr.GetTrinityString(i_arg2,loc_idx) : ""; + + char str [2048]; + snprintf(str,2048,text, arg1str, arg2str); + + uint64 target_guid = i_source ? i_source ->GetGUID() : 0; + + data << uint8(i_msgtype); + data << uint32(LANG_UNIVERSAL); + data << uint64(target_guid); // there 0 for BG messages + data << uint32(0); // can be chat msg group or something + data << uint64(target_guid); + data << uint32(strlen(str)+1); + data << str; + data << uint8(i_source ? i_source->chatTag() : uint8(0)); + } + private: + + ChatMsg i_msgtype; + int32 i_textId; + Player const* i_source; + int32 i_arg1; + int32 i_arg2; + }; +} // namespace Trinity + +template +void BattleGround::BroadcastWorker(Do& _do) +{ + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) + _do(plr); +} + +BattleGround::BattleGround() +{ + m_TypeID = BattleGroundTypeId(0); + m_RandomTypeID = BattleGroundTypeId(0); + m_InstanceID = 0; + m_Status = STATUS_NONE; + m_ClientInstanceID = 0; + m_EndTime = 0; + m_LastResurrectTime = 0; + m_BracketId = BG_BRACKET_ID_FIRST; + m_InvitedAlliance = 0; + m_InvitedHorde = 0; + m_ArenaType = 0; + m_IsArena = false; + m_Winner = 2; + m_StartTime = 0; + m_Events = 0; + m_IsRated = false; + m_BuffChange = false; + m_IsRandom = false; + m_Name = ""; + m_LevelMin = 0; + m_LevelMax = 0; + m_InBGFreeSlotQueue = false; + m_SetDeleteThis = false; + + m_MaxPlayersPerTeam = 0; + m_MaxPlayers = 0; + m_MinPlayersPerTeam = 0; + m_MinPlayers = 0; + + m_MapId = 0; + m_Map = NULL; + + m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocX[BG_TEAM_HORDE] = 0; + + m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocY[BG_TEAM_HORDE] = 0; + + m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocZ[BG_TEAM_HORDE] = 0; + + m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocO[BG_TEAM_HORDE] = 0; + + m_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0; + m_ArenaTeamIds[BG_TEAM_HORDE] = 0; + + m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0; + m_ArenaTeamRatingChanges[BG_TEAM_HORDE] = 0; + + m_BgRaids[BG_TEAM_ALLIANCE] = NULL; + m_BgRaids[BG_TEAM_HORDE] = NULL; + + m_PlayersCount[BG_TEAM_ALLIANCE] = 0; + m_PlayersCount[BG_TEAM_HORDE] = 0; + + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + + m_PrematureCountDown = false; + m_PrematureCountDown = 0; + + m_HonorMode = BG_NORMAL; + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set to some default existing values + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; +} + +BattleGround::~BattleGround() +{ + // remove objects and creatures + // (this is done automatically in mapmanager update, when the instance is reset after the reset time) + int size = m_BgCreatures.size(); + for (int i = 0; i < size; ++i) + DelCreature(i); + + size = m_BgObjects.size(); + for (int i = 0; i < size; ++i) + DelObject(i); + + if (GetInstanceID()) // not spam by useless queries in case BG templates + { + // delete creature and go respawn times + WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'",GetInstanceID()); + WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'",GetInstanceID()); + // delete instance from db + CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID()); + // remove from battlegrounds + } + + sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID()); + // unload map + if (m_Map) + m_Map->SetUnload(); + // remove from bg free slot queue + this->RemoveFromBGFreeSlotQueue(); + + for (BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr) + delete itr->second; +} + +void BattleGround::Update(uint32 diff) +{ + if (!GetPlayersSize()) + { + //BG is empty + // if there are no players invited, delete BG + // this will delete arena or bg object, where any player entered + // [[ but if you use battleground object again (more battles possible to be played on 1 instance) + // then this condition should be removed and code: + // if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) + // this->AddToFreeBGObjectsQueue(); // not yet implemented + // should be used instead of current + // ]] + // BattleGround Template instance cannot be updated, because it would be deleted + if (!GetInvitedCount(HORDE) && !GetInvitedCount(ALLIANCE)) + m_SetDeleteThis = true; + return; + } + + // remove offline players from bg after 5 minutes + if (!m_OfflineQueue.empty()) + { + BattleGroundPlayerMap::iterator itr = m_Players.find(*(m_OfflineQueue.begin())); + if (itr != m_Players.end()) + { + if (itr->second.OfflineRemoveTime <= sWorld.GetGameTime()) + { + RemovePlayerAtLeave(itr->first, true, true);// remove player from BG + m_OfflineQueue.pop_front(); // remove from offline queue + //do not use itr for anything, because it is erased in RemovePlayerAtLeave() + } + } + } + + /*********************************************************/ + /*** BATTLEGROUND RESSURECTION SYSTEM ***/ + /*********************************************************/ + + //this should be handled by spell system + m_LastResurrectTime += diff; + if (m_LastResurrectTime >= RESURRECTION_INTERVAL) + { + if (GetReviveQueueSize()) + { + for (std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) + { + Creature *sh = NULL; + for (std::vector::const_iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2) + { + Player *plr = objmgr.GetPlayer(*itr2); + if (!plr) + continue; + + if (!sh && plr->IsInWorld()) + { + sh = plr->GetMap()->GetCreature(itr->first); + // only for visual effect + if (sh) + // Spirit Heal, effect 117 + sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); + } + + // Resurrection visual + plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); + m_ResurrectQueue.push_back(*itr2); + } + (itr->second).clear(); + } + + m_ReviveQueue.clear(); + m_LastResurrectTime = 0; + } + else + // queue is clear and time passed, just update last resurrection time + m_LastResurrectTime = 0; + } + else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC + { + for (std::vector::const_iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(*itr); + if (!plr) + continue; + plr->ResurrectPlayer(1.0f); + plr->CastSpell(plr, 6962, true); + plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true); + ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr); + } + m_ResurrectQueue.clear(); + } + + /*********************************************************/ + /*** BATTLEGROUND BALLANCE SYSTEM ***/ + /*********************************************************/ + + // if less then minimum players are in on one side, then start premature finish timer + if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam())) + { + if (!m_PrematureCountDown) + { + m_PrematureCountDown = true; + m_PrematureCountDownTimer = sBattleGroundMgr.GetPrematureFinishTime(); + } + else if (m_PrematureCountDownTimer < diff) + { + // time's up! + uint32 winner = 0; + if (GetPlayersCountByTeam(ALLIANCE) >= GetMinPlayersPerTeam()) + winner = ALLIANCE; + else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam()) + winner = HORDE; + + EndBattleGround(winner); + m_PrematureCountDown = false; + } + else if (!sBattleGroundMgr.isTesting()) + { + uint32 newtime = m_PrematureCountDownTimer - diff; + // announce every minute + if (newtime > (MINUTE * IN_MILISECONDS)) + { + if (newtime / (MINUTE * IN_MILISECONDS) != m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS)) + PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / (MINUTE * IN_MILISECONDS))); + } + else + { + //announce every 15 seconds + if (newtime / (15 * IN_MILISECONDS) != m_PrematureCountDownTimer / (15 * IN_MILISECONDS)) + PSendMessageToAll(LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS, CHAT_MSG_SYSTEM, NULL, (uint32)(m_PrematureCountDownTimer / IN_MILISECONDS)); + } + m_PrematureCountDownTimer = newtime; + } + } + else if (m_PrematureCountDown) + m_PrematureCountDown = false; + + /*********************************************************/ + /*** BATTLEGROUND STARTING SYSTEM ***/ + /*********************************************************/ + + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if (!(m_Events & BG_STARTING_EVENT_1)) + { + m_Events |= BG_STARTING_EVENT_1; + + // setup here, only when at least one player has ported to the map + if (!SetupBattleGround()) + { + EndNow(); + return; + } + + StartingEventCloseDoors(); + SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]); + //first start warning - 2 or 1 minute + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + // After 1 minute or 30 seconds, warning is signalled + else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2)) + { + m_Events |= BG_STARTING_EVENT_2; + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + // After 30 or 15 seconds, warning is signalled + else if (GetStartDelayTime() <= m_StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3)) + { + m_Events |= BG_STARTING_EVENT_3; + SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + // delay expired (atfer 2 or 1 minute) + else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4)) + { + m_Events |= BG_STARTING_EVENT_4; + + StartingEventOpenDoors(); + + SendWarningToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH]); + SetStatus(STATUS_IN_PROGRESS); + SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]); + + //remove preparation + if (isArena()) + { + //TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START); + + for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (Player *plr = objmgr.GetPlayer(itr->first)) + { + plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + // remove auras with duration lower than 30s + Unit::AuraApplicationMap & auraMap = plr->GetAppliedAuras(); + for (Unit::AuraApplicationMap::iterator iter = auraMap.begin(); iter != auraMap.end();) + { + AuraApplication * aurApp = iter->second; + Aura * aura = aurApp->GetBase(); + if (!aura->IsPermanent() + && aura->GetDuration() <= 30*IN_MILISECONDS + && aurApp->IsPositive() + && (!(aura->GetSpellProto()->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)) + && (!aura->HasEffectType(SPELL_AURA_MOD_INVISIBILITY))) + plr->RemoveAura(iter); + else + ++iter; + } + } + + CheckArenaWinConditions(); + } + else + { + + PlaySoundToAll(SOUND_BG_START); + + for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (Player* plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_PREPARATION); + //Announce BG starting + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) + { + sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, GetName(), GetMinLevel(), GetMaxLevel()); + } + } + } + } + + /*********************************************************/ + /*** BATTLEGROUND ENDING SYSTEM ***/ + /*********************************************************/ + + if (GetStatus() == STATUS_WAIT_LEAVE) + { + // remove all players from battleground after 2 minutes + m_EndTime -= diff; + if (m_EndTime <= 0) + { + m_EndTime = 0; + BattleGroundPlayerMap::iterator itr, next; + for (itr = m_Players.begin(); itr != m_Players.end(); itr = next) + { + next = itr; + ++next; + //itr is erased here! + RemovePlayerAtLeave(itr->first, true, true);// remove player from BG + // do not change any battleground's private variables + } + } + } + + //update start time + m_StartTime += diff; +} + +void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O) +{ + BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID); + m_TeamStartLocX[idx] = X; + m_TeamStartLocY[idx] = Y; + m_TeamStartLocZ[idx] = Z; + m_TeamStartLocO[idx] = O; +} + +void BattleGround::SendPacketToAll(WorldPacket *packet) +{ + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.OfflineRemoveTime) + continue; + Player *plr = objmgr.GetPlayer(itr->first); + if (plr) + plr->GetSession()->SendPacket(packet); + else + sLog.outError("BattleGround:SendPacketToAll: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + } +} + +void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self) +{ + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.OfflineRemoveTime) + continue; + Player *plr = objmgr.GetPlayer(itr->first); + if (!plr) + { + sLog.outError("BattleGround:SendPacketToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + continue; + } + + if (!self && sender == plr) + continue; + + uint32 team = itr->second.Team; + if (!team) team = plr->GetTeam(); + + if (team == TeamID) + plr->GetSession()->SendPacket(packet); + } +} + +void BattleGround::PlaySoundToAll(uint32 SoundID) +{ + WorldPacket data; + sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); + SendPacketToAll(&data); +} + +void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID) +{ + WorldPacket data; + + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.OfflineRemoveTime) + continue; + Player *plr = objmgr.GetPlayer(itr->first); + + if (!plr) + { + sLog.outError("BattleGround:PlaySoundToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + continue; + } + + uint32 team = itr->second.Team; + if (!team) team = plr->GetTeam(); + + if (team == TeamID) + { + sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); + plr->GetSession()->SendPacket(&data); + } + } +} + +void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID) +{ + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.OfflineRemoveTime) + continue; + Player *plr = objmgr.GetPlayer(itr->first); + + if (!plr) + { + sLog.outError("BattleGround:CastSpellOnTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + continue; + } + + uint32 team = itr->second.Team; + if (!team) team = plr->GetTeam(); + + if (team == TeamID) + plr->CastSpell(plr, SpellID, true); + } +} + +void BattleGround::YellToAll(Creature* creature, const char* text, uint32 language) +{ + for (std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + WorldPacket data(SMSG_MESSAGECHAT, 200); + Player *plr = objmgr.GetPlayer(itr->first); + if (!plr) + { + sLog.outError("BattleGround: Player " UI64FMTD " not found!", itr->first); + continue; + } + creature->BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,text,language,creature->GetName(),itr->first); + plr->GetSession()->SendPacket(&data); + } +} + +void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID) +{ + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.OfflineRemoveTime) + continue; + Player *plr = objmgr.GetPlayer(itr->first); + + if (!plr) + { + sLog.outError("BattleGround:RewardHonorToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + continue; + } + + uint32 team = itr->second.Team; + if (!team) team = plr->GetTeam(); + + if (team == TeamID) + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor); + } +} + +void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID) +{ + FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); + + if (!factionEntry) + return; + + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.OfflineRemoveTime) + continue; + Player *plr = objmgr.GetPlayer(itr->first); + + if (!plr) + { + sLog.outError("BattleGround:RewardReputationToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + continue; + } + + uint32 team = itr->second.Team; + if (!team) team = plr->GetTeam(); + + if (team == TeamID) + plr->GetReputationMgr().ModifyReputation(factionEntry, Reputation); + } +} + +void BattleGround::UpdateWorldState(uint32 Field, uint32 Value) +{ + WorldPacket data; + sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value); + SendPacketToAll(&data); +} + +void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source) +{ + WorldPacket data; + sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value); + Source->GetSession()->SendPacket(&data); +} + +void BattleGround::EndBattleGround(uint32 winner) +{ + this->RemoveFromBGFreeSlotQueue(); + + ArenaTeam * winner_arena_team = NULL; + ArenaTeam * loser_arena_team = NULL; + uint32 loser_rating = 0; + uint32 winner_rating = 0; + WorldPacket data; + int32 winmsg_id = 0; + + if (winner == ALLIANCE) + { + winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS; + + PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound + + SetWinner(WINNER_ALLIANCE); + } + else if (winner == HORDE) + { + winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS; + + PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound + + SetWinner(WINNER_HORDE); + } + else + { + SetWinner(3); + } + + SetStatus(STATUS_WAIT_LEAVE); + //we must set it this way, because end time is sent in packet! + m_EndTime = TIME_TO_AUTOREMOVE; + + // arena rating calculation + if (isArena() && isRated()) + { + winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(winner)); + loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(winner))); + if (winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) + { + loser_rating = loser_arena_team->GetStats().rating; + winner_rating = winner_arena_team->GetStats().rating; + int32 winner_change = winner_arena_team->WonAgainst(loser_rating); + int32 loser_change = loser_arena_team->LostAgainst(winner_rating); + sLog.outDebug("--- Winner rating: %u, Loser rating: %u, Winner change: %u, Losser change: %u ---", winner_rating, loser_rating, winner_change, loser_change); + SetArenaTeamRatingChangeForTeam(winner, winner_change); + SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loser_change); + sLog.outArena("Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. RatingChange: %i.", m_ArenaType, m_ArenaTeamIds[BG_TEAM_ALLIANCE], m_ArenaTeamIds[BG_TEAM_HORDE], winner_arena_team->GetId(), winner_change); + } + else + { + SetArenaTeamRatingChangeForTeam(ALLIANCE, 0); + SetArenaTeamRatingChangeForTeam(HORDE, 0); + } + } + + for (BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + uint32 team = itr->second.Team; + + if (itr->second.OfflineRemoveTime) + { + //if rated arena match - make member lost! + if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) + { + if (team == winner) + winner_arena_team->OfflineMemberLost(itr->first, loser_rating); + else + loser_arena_team->OfflineMemberLost(itr->first, winner_rating); + } + continue; + } + Player *plr = objmgr.GetPlayer(itr->first); + if (!plr) + { + sLog.outError("BattleGround:EndBattleGround Player (GUID: %u) not found!", GUID_LOPART(itr->first)); + continue; + } + + // should remove spirit of redemption + if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + + if (!plr->isAlive()) + { + plr->ResurrectPlayer(1.0f); + plr->SpawnCorpseBones(); + } + else + { + //needed cause else in av some creatures will kill the players at the end + plr->CombatStop(); + plr->getHostileRefManager().deleteReferences(); + } + + //this line is obsolete - team is set ALWAYS + //if (!team) team = plr->GetTeam(); + + // per player calculation + if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) + { + if (team == winner) + { + // update achievement BEFORE personal rating update + ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID()); + if (member) + plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating); + + winner_arena_team->MemberWon(plr,loser_rating); + } + else + { + loser_arena_team->MemberLost(plr,winner_rating); + + // Arena lost => reset the win_rated_arena having the "no_loose" condition + plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE); + } + } + + uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; + uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; + uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; + + // Reward winner team + if (team == winner) + { + if (IsRandom() || BattleGroundMgr::IsBGWeekend(GetTypeID())) + { + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, GetBonusHonorFromKill(win_kills)); + plr->ModifyArenaPoints(win_arena); + if (!plr->GetRandomWinner()) + plr->SetRandomWinner(true); + } + + plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_BG, 1); + } + else + { + if (IsRandom() || BattleGroundMgr::IsBGWeekend(GetTypeID())) + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, GetBonusHonorFromKill(loos_kills)); + } + + + plr->SetHealth(plr->GetMaxHealth()); + plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); + plr->CombatStopWithPets(true); + + BlockMovement(plr); + + sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); + plr->GetSession()->SendPacket(&data); + + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType()); + plr->GetSession()->SendPacket(&data); + plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1); + } + + if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) + { + // update arena points only after increasing the player's match count! + //obsolete: winner_arena_team->UpdateArenaPointsHelper(); + //obsolete: loser_arena_team->UpdateArenaPointsHelper(); + // save the stat changes + winner_arena_team->SaveToDB(); + loser_arena_team->SaveToDB(); + // send updated arena team stats to players + // this way all arena team members will get notified, not only the ones who participated in this match + winner_arena_team->NotifyStatsChanged(); + loser_arena_team->NotifyStatsChanged(); + } + + if (winmsg_id) + SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL); +} + +uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const +{ + //variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill) + uint32 maxLevel = (GetMaxLevel()<80)?GetMaxLevel():80; + return Trinity::Honor::hk_honor_at_level(maxLevel, kills); +} + +uint32 BattleGround::GetBattlemasterEntry() const +{ + switch(GetTypeID(true)) + { + case BATTLEGROUND_AV: return 15972; + case BATTLEGROUND_WS: return 14623; + case BATTLEGROUND_AB: return 14879; + case BATTLEGROUND_EY: return 22516; + case BATTLEGROUND_NA: return 20200; + default: return 0; + } +} + +void BattleGround::BlockMovement(Player *plr) +{ + plr->SetClientControl(plr, 0); // movement disabled NOTE: the effect will be automatically removed by client when the player is teleported from the battleground, so no need to send with uint8(1) in RemovePlayerAtLeave() +} + +void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket) +{ + uint32 team = GetPlayerTeam(guid); + bool participant = false; + // Remove from lists/maps + BattleGroundPlayerMap::iterator itr = m_Players.find(guid); + if (itr != m_Players.end()) + { + UpdatePlayersCountByTeam(team, true); // -1 player + m_Players.erase(itr); + // check if the player was a participant of the match, or only entered through gm command (goname) + participant = true; + } + + BattleGroundScoreMap::iterator itr2 = m_PlayerScores.find(guid); + if (itr2 != m_PlayerScores.end()) + { + delete itr2->second; // delete player's score + m_PlayerScores.erase(itr2); + } + + RemovePlayerFromResurrectQueue(guid); + + Player *plr = objmgr.GetPlayer(guid); + + // should remove spirit of redemption + if (plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + + if (plr && !plr->isAlive()) // resurrect on exit + { + plr->ResurrectPlayer(1.0f); + plr->SpawnCorpseBones(); + } + + RemovePlayer(plr, guid); // BG subclass specific code + + if (participant) // if the player was a match participant, remove auras, calc rating, update queue + { + BattleGroundTypeId bgTypeId = GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); + if (plr) + { + plr->ClearAfkReports(); + + if (!team) team = plr->GetTeam(); + + // if arena, remove the specific arena auras + if (isArena()) + { + plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out + bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) + + // unsummon current and summon old pet if there was one and there isn't a current pet + plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT); + plr->ResummonPetTemporaryUnSummonedIfAny(); + + if (isRated() && GetStatus() == STATUS_IN_PROGRESS) + { + //left a rated match while the encounter was in progress, consider as loser + ArenaTeam * winner_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); + ArenaTeam * loser_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team)); + if (winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team) + loser_arena_team->MemberLost(plr,winner_arena_team->GetRating()); + } + } + if (SendPacket) + { + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0); + plr->GetSession()->SendPacket(&data); + } + + // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg + plr->RemoveBattleGroundQueueId(bgQueueTypeId); + } + else + // removing offline participant + { + if (isRated() && GetStatus() == STATUS_IN_PROGRESS) + { + //left a rated match while the encounter was in progress, consider as loser + ArenaTeam * others_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(GetOtherTeam(team))); + ArenaTeam * players_arena_team = objmgr.GetArenaTeamById(GetArenaTeamIdForTeam(team)); + if (others_arena_team && players_arena_team) + players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating()); + } + } + + // remove from raid group if player is member + if (Group *group = GetBgRaid(team)) + { + if (!group->RemoveMember(guid, 0)) // group was disbanded + { + SetBgRaid(team, NULL); + delete group; + } + } + DecreaseInvitedCount(team); + //we should update battleground queue, but only if bg isn't ending + if (isBattleGround() && GetStatus() < STATUS_WAIT_LEAVE) + { + // a player has left the battleground, so there are free slots -> add to queue + AddToBGFreeSlotQueue(); + sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, GetBracketId()); + } + // Let others know + WorldPacket data; + sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid); + SendPacketToTeam(team, &data, plr, false); + } + + if (plr) + { + // Do next only if found in battleground + plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG. + // reset destination bg team + plr->SetBGTeam(0); + + if (Transport) + plr->TeleportToBGEntryPoint(); + + sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName()); + } + + //battleground object will be deleted next BattleGround::Update() call +} + +// this method is called when no players remains in battleground +void BattleGround::Reset() +{ + SetWinner(WINNER_NONE); + SetStatus(STATUS_WAIT_QUEUE); + SetStartTime(0); + SetEndTime(0); + SetLastResurrectTime(0); + SetArenaType(0); + SetRated(false); + + m_Events = 0; + + if (m_InvitedAlliance > 0 || m_InvitedHorde > 0) + sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde); + + m_InvitedAlliance = 0; + m_InvitedHorde = 0; + m_InBGFreeSlotQueue = false; + + m_Players.clear(); + + for (BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr) + delete itr->second; + m_PlayerScores.clear(); + + ResetBGSubclass(); +} + +void BattleGround::StartBattleGround() +{ + SetStartTime(0); + SetLastResurrectTime(0); + // add BG to free slot queue + AddToBGFreeSlotQueue(); + + // add bg to update list + // This must be done here, because we need to have already invited some players when first BG::Update() method is executed + // and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes + sBattleGroundMgr.AddBattleGround(GetInstanceID(), GetTypeID(), this); + if (m_IsRated) + sLog.outArena("Arena match type: %u for Team1Id: %u - Team2Id: %u started.", m_ArenaType, m_ArenaTeamIds[BG_TEAM_ALLIANCE], m_ArenaTeamIds[BG_TEAM_HORDE]); +} + +void BattleGround::AddPlayer(Player *plr) +{ + // remove afk from player + if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK)) + plr->ToggleAFK(); + + // score struct must be created in inherited class + + uint64 guid = plr->GetGUID(); + uint32 team = plr->GetBGTeam(); + + BattleGroundPlayer bp; + bp.OfflineRemoveTime = 0; + bp.Team = team; + + // Add to list/maps + m_Players[guid] = bp; + + UpdatePlayersCountByTeam(team, false); // +1 player + + WorldPacket data; + sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr); + SendPacketToTeam(team, &data, plr, false); + + // BG Status packet + WorldPacket status; + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(m_TypeID, GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType()); + plr->GetSession()->SendPacket(&status); + + plr->RemoveAurasByType(SPELL_AURA_MOUNTED); + + // add arena specific auras + if (isArena()) + { + plr->RemoveArenaSpellCooldowns(); + plr->RemoveArenaAuras(); + plr->RemoveArenaEnchantments(TEMP_ENCHANTMENT_SLOT); + if (team == ALLIANCE) // gold + { + if (plr->GetTeam() == HORDE) + plr->CastSpell(plr, SPELL_HORDE_GOLD_FLAG,true); + else + plr->CastSpell(plr, SPELL_ALLIANCE_GOLD_FLAG,true); + } + else // green + { + if (plr->GetTeam() == HORDE) + plr->CastSpell(plr, SPELL_HORDE_GREEN_FLAG,true); + else + plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true); + } + + plr->DestroyConjuredItems(true); + plr->UnsummonPetTemporaryIfAny(); + + if (GetStatus() == STATUS_WAIT_JOIN) // not started yet + { + plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true); + + plr->SetHealth(plr->GetMaxHealth()); + plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); + } + } + else + { + if (GetStatus() == STATUS_WAIT_JOIN) // not started yet + plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells. + } + + plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId()); + plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId()); + + // setup BG group membership + PlayerAddedToBGCheckIfBGIsRunning(plr); + AddOrSetPlayerToCorrectBgGroup(plr, guid, team); + + // Log + sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName()); +} + +/* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */ +void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team) +{ + Group* group = GetBgRaid(team); + if (!group) // first player joined + { + group = new Group; + SetBgRaid(team, group); + group->Create(plr_guid, plr->GetName()); + } + else // raid already exist + { + if (group->IsMember(plr_guid)) + { + uint8 subgroup = group->GetMemberGroup(plr_guid); + plr->SetBattleGroundRaid(group, subgroup); + } + else + { + group->AddMember(plr_guid, plr->GetName()); + if (Group* originalGroup = plr->GetOriginalGroup()) + if (originalGroup->IsLeader(plr_guid)) + group->ChangeLeader(plr_guid); + } + } +} + +// This method should be called when player logs into running battleground +void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid) +{ + // player is correct pointer + for (std::deque::iterator itr = m_OfflineQueue.begin(); itr != m_OfflineQueue.end(); ++itr) + { + if (*itr == plr_guid) + { + m_OfflineQueue.erase(itr); + break; + } + } + m_Players[plr_guid].OfflineRemoveTime = 0; + PlayerAddedToBGCheckIfBGIsRunning(player); + // if battleground is starting, then add preparation aura + // we don't have to do that, because preparation aura isn't removed when player logs out +} + +// This method should be called when player logs out from running battleground +void BattleGround::EventPlayerLoggedOut(Player* player) +{ + // player is correct pointer, it is checked in WorldSession::LogoutPlayer() + m_OfflineQueue.push_back(player->GetGUID()); + m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME; + if (GetStatus() == STATUS_IN_PROGRESS) + { + // drop flag and handle other cleanups + RemovePlayer(player, player->GetGUID()); + + // 1 player is logging out, if it is the last, then end arena! + if (isArena()) + if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam()))) + EndBattleGround(GetOtherTeam(player->GetTeam())); + } + + player->LeaveBattleground(); +} + +/* This method should be called only once ... it adds pointer to queue */ +void BattleGround::AddToBGFreeSlotQueue() +{ + // make sure to add only once + if (!m_InBGFreeSlotQueue && isBattleGround()) + { + sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); + m_InBGFreeSlotQueue = true; + } +} + +/* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/ +void BattleGround::RemoveFromBGFreeSlotQueue() +{ + // set to be able to re-add if needed + m_InBGFreeSlotQueue = false; + // uncomment this code when battlegrounds will work like instances + for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) + { + if ((*itr)->GetInstanceID() == m_InstanceID) + { + sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); + return; + } + } +} + +// get the number of free slots for team +// returns the number how many players can join battleground to MaxPlayersPerTeam +uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const +{ + //if BG is starting ... invite anyone + if (GetStatus() == STATUS_WAIT_JOIN) + return (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; + //if BG is already started .. do not allow to join too much players of one faction + uint32 otherTeam; + uint32 otherIn; + if (Team == ALLIANCE) + { + otherTeam = GetInvitedCount(HORDE); + otherIn = GetPlayersCountByTeam(HORDE); + } + else + { + otherTeam = GetInvitedCount(ALLIANCE); + otherIn = GetPlayersCountByTeam(ALLIANCE); + } + if (GetStatus() == STATUS_IN_PROGRESS) + { + // difference based on ppl invited (not necessarily entered battle) + // default: allow 0 + uint32 diff = 0; + // allow join one person if the sides are equal (to fill up bg to minplayersperteam) + if (otherTeam == GetInvitedCount(Team)) + diff = 1; + // allow join more ppl if the other side has more players + else if (otherTeam > GetInvitedCount(Team)) + diff = otherTeam - GetInvitedCount(Team); + + // difference based on max players per team (don't allow inviting more) + uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0; + // difference based on players who already entered + // default: allow 0 + uint32 diff3 = 0; + // allow join one person if the sides are equal (to fill up bg minplayersperteam) + if (otherIn == GetPlayersCountByTeam(Team)) + diff3 = 1; + // allow join more ppl if the other side has more players + else if (otherIn > GetPlayersCountByTeam(Team)) + diff3 = otherIn - GetPlayersCountByTeam(Team); + // or other side has less than minPlayersPerTeam + else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam()) + diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1; + + // return the minimum of the 3 differences + + // min of diff and diff 2 + diff = diff < diff2 ? diff : diff2; + // min of diff, diff2 and diff3 + return diff < diff3 ? diff : diff3 ; + } + return 0; +} + +bool BattleGround::HasFreeSlots() const +{ + return GetPlayersSize() < GetMaxPlayers(); +} + +void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) +{ + //this procedure is called from virtual function implemented in bg subclass + BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if (itr == m_PlayerScores.end()) // player not found... + return; + + switch(type) + { + case SCORE_KILLING_BLOWS: // Killing blows + itr->second->KillingBlows += value; + break; + case SCORE_DEATHS: // Deaths + itr->second->Deaths += value; + break; + case SCORE_HONORABLE_KILLS: // Honorable kills + itr->second->HonorableKills += value; + break; + case SCORE_BONUS_HONOR: // Honor bonus + // do not add honor in arenas + if (isBattleGround()) + { + // reward honor instantly + if (doAddHonor) + { + Source->RewardHonor(NULL, 1, value);//RewardHonor calls UpdatePlayerScore with doAddHonor = false + }else itr->second->BonusHonor += value; + } + break; + //used only in EY, but in MSG_PVP_LOG_DATA opcode + case SCORE_DAMAGE_DONE: // Damage Done + itr->second->DamageDone += value; + break; + case SCORE_HEALING_DONE: // Healing Done + itr->second->HealingDone += value; + break; + default: + sLog.outError("BattleGround: Unknown player score type %u", type); + break; + } +} + +void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid) +{ + m_ReviveQueue[npc_guid].push_back(player_guid); + + Player *plr = objmgr.GetPlayer(player_guid); + if (!plr) + return; + + plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true); +} + +void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid) +{ + for (std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) + { + for (std::vector::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2) + { + if (*itr2 == player_guid) + { + (itr->second).erase(itr2); + + Player *plr = objmgr.GetPlayer(player_guid); + if (!plr) + return; + + plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT); + + return; + } + } + } +} + +bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 /*respawnTime*/) +{ + Map *map = GetBgMap(); + if (!map) + return false; + // must be created this way, adding to godatamap would add it to the base map of the instance + // and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created + // so we must create it specific for this instance + GameObject * go = new GameObject; + if (!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, GetBgMap(), + PHASEMASK_NORMAL, x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY)) + { + sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); + sLog.outError("Cannot create gameobject template %u! BattleGround not created!", entry); + delete go; + return false; + } +/* + uint32 guid = go->GetGUIDLow(); + + // without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata + // iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed + GameObjectData& data = objmgr.NewGOData(guid); + + data.id = entry; + data.mapid = GetMapId(); + data.posX = x; + data.posY = y; + data.posZ = z; + data.orientation = o; + data.rotation0 = rotation0; + data.rotation1 = rotation1; + data.rotation2 = rotation2; + data.rotation3 = rotation3; + data.spawntimesecs = respawnTime; + data.spawnMask = 1; + data.animprogress = 100; + data.go_state = 1; +*/ + // add to world, so it can be later looked up from HashMapHolder + map->Add(go); + m_BgObjects[type] = go->GetGUID(); + return true; +} + +//some doors aren't despawned so we cannot handle their closing in gameobject::update() +//it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code +void BattleGround::DoorClose(uint32 type) +{ + GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); + if (obj) + { + //if doors are open, close it + if (obj->getLootState() == GO_ACTIVATED && obj->GetGoState() != GO_STATE_READY) + { + //change state to allow door to be closed + obj->SetLootState(GO_READY); + obj->UseDoorOrButton(RESPAWN_ONE_DAY); + } + } + else + { + sLog.outError("BattleGround: Door object not found (cannot close doors)"); + } +} + +void BattleGround::DoorOpen(uint32 type) +{ + GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); + if (obj) + { + //change state to be sure they will be opened + obj->SetLootState(GO_READY); + obj->UseDoorOrButton(RESPAWN_ONE_DAY); + } + else + { + sLog.outError("BattleGround: Door object not found! - doors will be closed."); + } +} + +GameObject* BattleGround::GetBGObject(uint32 type) +{ + GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); + if (!obj) + sLog.outError("couldn't get gameobject %i",type); + return obj; +} + +Creature* BattleGround::GetBGCreature(uint32 type) +{ + Creature *creature = GetBgMap()->GetCreature(m_BgCreatures[type]); + if (!creature) + sLog.outError("couldn't get creature %i",type); + return creature; +} + +void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) +{ + Map * map = GetBgMap(); + if (!map) + return; + if (respawntime == 0) + { + GameObject *obj = map->GetGameObject(m_BgObjects[type]); + if (obj) + { + //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again + if (obj->getLootState() == GO_JUST_DEACTIVATED) + obj->SetLootState(GO_READY); + obj->SetRespawnTime(0); + map->Add(obj); + } + } + else + { + GameObject *obj = map->GetGameObject(m_BgObjects[type]); + if (obj) + { + map->Add(obj); + obj->SetRespawnTime(respawntime); + obj->SetLootState(GO_JUST_DEACTIVATED); + } + } +} + +Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime) +{ + Map * map = GetBgMap(); + if (!map) + return NULL; + + Creature* pCreature = new Creature; + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, teamval, x, y, z, o)) + { + sLog.outError("Can't create creature entry: %u",entry); + delete pCreature; + return NULL; + } + + pCreature->SetHomePosition(x, y, z, o); + + CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(entry); + if (!cinfo) + { + sLog.outErrorDb("BattleGround::AddCreature: entry %u does not exist.", entry); + return NULL; + } + //force using DB speeds + pCreature->SetSpeed(MOVE_WALK, cinfo->speed_walk); + pCreature->SetSpeed(MOVE_RUN, cinfo->speed_run); + + map->Add(pCreature); + m_BgCreatures[type] = pCreature->GetGUID(); + + if (respawntime) + pCreature->SetRespawnDelay(respawntime); + + return pCreature; +} +/* +void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime) +{ + Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId()); + if (!map) + return false; + + if (respawntime == 0) + { + Creature *obj = HashMapHolder::Find(m_BgCreatures[type]); + if (obj) + { + //obj->Respawn(); // bugged + obj->SetRespawnTime(0); + objmgr.SaveCreatureRespawnTime(obj->GetGUIDLow(), GetInstanceID(), 0); + map->Add(obj); + } + } + else + { + Creature *obj = HashMapHolder::Find(m_BgCreatures[type]); + if (obj) + { + obj->setDeathState(DEAD); + obj->SetRespawnTime(respawntime); + map->Add(obj); + } + } +} +*/ +bool BattleGround::DelCreature(uint32 type) +{ + if (!m_BgCreatures[type]) + return true; + + Creature *cr = GetBgMap()->GetCreature(m_BgCreatures[type]); + if (!cr) + { + sLog.outError("Can't find creature guid: %u",GUID_LOPART(m_BgCreatures[type])); + return false; + } + cr->AddObjectToRemoveList(); + m_BgCreatures[type] = 0; + return true; +} + +bool BattleGround::DelObject(uint32 type) +{ + if (!m_BgObjects[type]) + return true; + + GameObject *obj = GetBgMap()->GetGameObject(m_BgObjects[type]); + if (!obj) + { + sLog.outError("Can't find gobject guid: %u",GUID_LOPART(m_BgObjects[type])); + return false; + } + obj->SetRespawnTime(0); // not save respawn time + obj->Delete(); + m_BgObjects[type] = 0; + return true; +} + +bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team) +{ + uint32 entry = 0; + + if (team == ALLIANCE) + entry = BG_CREATURE_ENTRY_A_SPIRITGUIDE; + else + entry = BG_CREATURE_ENTRY_H_SPIRITGUIDE; + + Creature* pCreature = AddCreature(entry,type,team,x,y,z,o); + if (!pCreature) + { + sLog.outError("Can't create Spirit guide. BattleGround not created!"); + EndNow(); + return false; + } + + pCreature->setDeathState(DEAD); + + pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID()); + // aura + //TODO: Fix display here + //pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL); + // casting visual effect + pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL); + // correct cast speed + pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); + + //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true); + + return true; +} + +void BattleGround::SendMessageToAll(int32 entry, ChatMsg type, Player const* source) +{ + Trinity::BattleGroundChatBuilder bg_builder(type, entry, source); + Trinity::LocalizedPacketDo bg_do(bg_builder); + BroadcastWorker(bg_do); +} + +void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...) +{ + va_list ap; + va_start(ap, source); + + Trinity::BattleGroundChatBuilder bg_builder(type, entry, source, &ap); + Trinity::LocalizedPacketDo bg_do(bg_builder); + BroadcastWorker(bg_do); + + va_end(ap); +} + +void BattleGround::SendWarningToAll(int32 entry, ...) +{ + const char *format = objmgr.GetTrinityStringForDBCLocale(entry); + va_list ap; + char str [1024]; + va_start(ap, entry); + vsnprintf(str,1024,format, ap); + va_end(ap); + std::string msg = (std::string)str; + + WorldPacket data(SMSG_MESSAGECHAT, 200); + + data << (uint8)CHAT_MSG_RAID_BOSS_EMOTE; + data << (uint32)LANG_UNIVERSAL; + data << (uint64)0; + data << (uint32)0; // 2.1.0 + data << (uint32)1; + data << (uint8)0; + data << (uint64)0; + data << (uint32)(strlen(msg.c_str())+1); + data << msg.c_str(); + data << (uint8)0; + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) + if (plr->GetSession()) + plr->GetSession()->SendPacket(&data); +} + +void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2) +{ + Trinity::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2); + Trinity::LocalizedPacketDo bg_do(bg_builder); + BroadcastWorker(bg_do); +} + +void BattleGround::EndNow() +{ + RemoveFromBGFreeSlotQueue(); + SetStatus(STATUS_WAIT_LEAVE); + SetEndTime(0); +} + +//to be removed +const char *BattleGround::GetTrinityString(int32 entry) +{ + // FIXME: now we have different DBC locales and need localized message for each target client + return objmgr.GetTrinityStringForDBCLocale(entry); +} + +/* +important notice: +buffs aren't spawned/despawned when players captures anything +buffs are in their positions when battleground starts +*/ +void BattleGround::HandleTriggerBuff(uint64 const& go_guid) +{ + GameObject *obj = GetBgMap()->GetGameObject(go_guid); + if (!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned()) + return; + + //change buff type, when buff is used: + int32 index = m_BgObjects.size() - 1; + while (index >= 0 && m_BgObjects[index] != go_guid) + index--; + if (index < 0) + { + sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(true),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType()); + return; + } + + //randomly select new buff + uint8 buff = urand(0, 2); + uint32 entry = obj->GetEntry(); + if (m_BuffChange && entry != Buff_Entries[buff]) + { + //despawn current buff + SpawnBGObject(index, RESPAWN_ONE_DAY); + //set index for new one + for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex) + if (entry == Buff_Entries[currBuffTypeIndex]) + { + index -= currBuffTypeIndex; + index += buff; + } + } + + SpawnBGObject(index, BUFF_RESPAWN_TIME); +} + +void BattleGround::HandleKillPlayer(Player *player, Player *killer) +{ + //keep in mind that for arena this will have to be changed a bit + + // add +1 deaths + UpdatePlayerScore(player, SCORE_DEATHS, 1); + // add +1 kills to group and +1 killing_blows to killer + if (killer) + { + UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1); + UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1); + + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if (!plr || plr == killer) + continue; + + if (plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player)) + UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1); + } + } + + // to be able to remove insignia -- ONLY IN BattleGrounds + // give xp only in BattleGrounds + if (!isArena()) + { + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + RewardXPAtKill(killer, player); + } +} + +// return the player's team based on battlegroundplayer info +// used in same faction arena matches mainly +uint32 BattleGround::GetPlayerTeam(uint64 guid) +{ + BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid); + if (itr != m_Players.end()) + return itr->second.Team; + return 0; +} + +uint32 BattleGround::GetOtherTeam(uint32 teamId) +{ + return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0; +} + +bool BattleGround::IsPlayerInBattleGround(uint64 guid) +{ + BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid); + if (itr != m_Players.end()) + return true; + return false; +} + +void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr) +{ + if (GetStatus() != STATUS_WAIT_LEAVE) + return; + + WorldPacket data; + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); + + BlockMovement(plr); + + sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); + plr->GetSession()->SendPacket(&data); + + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType()); + plr->GetSession()->SendPacket(&data); +} + +uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const +{ + int count = 0; + for (BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (itr->second.Team == Team) + { + Player * pl = objmgr.GetPlayer(itr->first); + if (pl && pl->isAlive() && !pl->HasByteFlag(UNIT_FIELD_BYTES_2, 3, FORM_SPIRITOFREDEMPTION)) + ++count; + } + } + return count; +} + +void BattleGround::SetHoliday(bool is_holiday) +{ + if (is_holiday) + m_HonorMode = BG_HOLIDAY; + else + m_HonorMode = BG_NORMAL; +} + +int32 BattleGround::GetObjectType(uint64 guid) +{ + for (uint32 i = 0; i < m_BgObjects.size(); ++i) + if (m_BgObjects[i] == guid) + return i; + sLog.outError("BattleGround: cheating? a player used a gameobject which isnt supposed to be a usable object!"); + return -1; +} + +void BattleGround::HandleKillUnit(Creature * /*creature*/, Player * /*killer*/) +{ +} + +void BattleGround::CheckArenaWinConditions() +{ + if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE)) + EndBattleGround(HORDE); + else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE)) + EndBattleGround(ALLIANCE); +} + +void BattleGround::UpdateArenaWorldState() +{ + UpdateWorldState(0xe10, GetAlivePlayersCountByTeam(HORDE)); + UpdateWorldState(0xe11, GetAlivePlayersCountByTeam(ALLIANCE)); +} + +void BattleGround::SetBgRaid(uint32 TeamID, Group *bg_raid) +{ + Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; + if (old_raid) old_raid->SetBattlegroundGroup(NULL); + if (bg_raid) bg_raid->SetBattlegroundGroup(this); + old_raid = bg_raid; +} + +WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard(Player* player) +{ + return objmgr.GetClosestGraveYard(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam()); +} + +bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const +{ + BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team); + uint32 score = (m_TeamScores[team_idx] < 0) ? 0 : uint32(m_TeamScores[team_idx]); + return score >= minScore && score <= maxScore; +} + +void BattleGround::SetBracket(PvPDifficultyEntry const* bracketEntry) +{ + m_BracketId = bracketEntry->GetBracketId(); + SetLevelRange(bracketEntry->minLevel,bracketEntry->maxLevel); +} + +void BattleGround::RewardXPAtKill(Player* plr, Player* victim) +{ + if (!sWorld.getConfig(CONFIG_BG_XP_FOR_KILL) || !plr || !victim) + return; + + uint32 xp = 0; + uint32 count = 0; + uint32 sum_level = 0; + Player* member_with_max_level = NULL; + Player* not_gray_member_with_max_level = NULL; + + if (Group *pGroup = plr->GetGroup())//should be always in a raid group while in any bg + { + for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* member = itr->getSource(); + if (!member || !member->isAlive()) // only for alive + continue; + + if (!member->IsAtGroupRewardDistance(victim)) // at req. distance + continue; + + ++count; + sum_level += member->getLevel(); + if (!member_with_max_level || member_with_max_level->getLevel() < member->getLevel()) + member_with_max_level = member; + + uint32 gray_level = Trinity::XP::GetGrayLevel(member->getLevel()); + if (victim->getLevel() > gray_level && (!not_gray_member_with_max_level + || not_gray_member_with_max_level->getLevel() < member->getLevel())) + not_gray_member_with_max_level = member; + } + + if (member_with_max_level) + { + xp = !not_gray_member_with_max_level ? 0 : Trinity::XP::Gain(not_gray_member_with_max_level, victim); + + if (!xp) + return; + + float group_rate = 1.0f; + + for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* pGroupGuy = itr->getSource(); + if (!pGroupGuy) + continue; + + if (!pGroupGuy->IsAtGroupRewardDistance(victim)) + continue; // member (alive or dead) or his corpse at req. distance + + float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level; + + // XP updated only for alive group member + if (pGroupGuy->isAlive() && not_gray_member_with_max_level && pGroupGuy->getLevel() <= not_gray_member_with_max_level->getLevel()) + { + uint32 itr_xp = (member_with_max_level == not_gray_member_with_max_level) ? uint32(xp*rate) : uint32((xp*rate/2)+1); + + // handle SPELL_AURA_MOD_XP_PCT auras + Unit::AuraEffectList const& ModXPPctAuras = plr->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT); + for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) + itr_xp = uint32(itr_xp*(1.0f + (*i)->GetAmount() / 100.0f)); + + pGroupGuy->GiveXP(itr_xp, victim); + if (Pet* pet = pGroupGuy->GetPet()) + pet->GivePetXP(itr_xp/2); + } + } + } + } + else//should be always in a raid group while in any BG, but you never know... + { + xp = Trinity::XP::Gain(plr, victim); + + if (!xp) + return; + + // handle SPELL_AURA_MOD_XP_PCT auras + Unit::AuraEffectList const& ModXPPctAuras = plr->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT); + for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i) + xp = uint32(xp*(1.0f + (*i)->GetAmount() / 100.0f)); + + plr->GiveXP(xp, victim); + + if (Pet* pet = plr->GetPet()) + pet->GivePetXP(xp); + } +} \ No newline at end of file diff --git a/src/server/game/BattleGrounds/BattleGround.h b/src/server/game/BattleGrounds/BattleGround.h new file mode 100644 index 00000000000..6f46b6f8f6d --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGround.h @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUND_H +#define __BATTLEGROUND_H + +#include "Common.h" +#include "SharedDefines.h" +#include "DBCEnums.h" + +class Creature; +class GameObject; +class Group; +class Player; +class WorldPacket; +class BattleGroundMap; + +struct PvPDifficultyEntry; +struct WorldSafeLocsEntry; + +enum BattleGroundSounds +{ + SOUND_HORDE_WINS = 8454, + SOUND_ALLIANCE_WINS = 8455, + SOUND_BG_START = 3439, + SOUND_BG_START_L70ETC = 11803, +}; + +enum BattleGroundQuests +{ + SPELL_WS_QUEST_REWARD = 43483, + SPELL_AB_QUEST_REWARD = 43484, + SPELL_AV_QUEST_REWARD = 43475, + SPELL_AV_QUEST_KILLED_BOSS = 23658, + SPELL_EY_QUEST_REWARD = 43477, + SPELL_SA_QUEST_REWARD = 61213, + SPELL_AB_QUEST_REWARD_4_BASES = 24061, + SPELL_AB_QUEST_REWARD_5_BASES = 24064 +}; + +enum BattleGroundMarks +{ + SPELL_WS_MARK_LOSER = 24950, + SPELL_WS_MARK_WINNER = 24951, + SPELL_AB_MARK_LOSER = 24952, + SPELL_AB_MARK_WINNER = 24953, + SPELL_AV_MARK_LOSER = 24954, + SPELL_AV_MARK_WINNER = 24955, + SPELL_SA_MARK_WINNER = 61160, + SPELL_SA_MARK_LOSER = 61159, + ITEM_AV_MARK_OF_HONOR = 20560, + ITEM_WS_MARK_OF_HONOR = 20558, + ITEM_AB_MARK_OF_HONOR = 20559, + ITEM_EY_MARK_OF_HONOR = 29024, + ITEM_SA_MARK_OF_HONOR = 42425 +}; + +enum BattleGroundMarksCount +{ + ITEM_WINNER_COUNT = 3, + ITEM_LOSER_COUNT = 1 +}; + +enum BattleGroundCreatures +{ + BG_CREATURE_ENTRY_A_SPIRITGUIDE = 13116, // alliance + BG_CREATURE_ENTRY_H_SPIRITGUIDE = 13117, // horde +}; + +enum BattleGroundSpells +{ + SPELL_WAITING_FOR_RESURRECT = 2584, // Waiting to Resurrect + SPELL_SPIRIT_HEAL_CHANNEL = 22011, // Spirit Heal Channel + SPELL_SPIRIT_HEAL = 22012, // Spirit Heal + SPELL_RESURRECTION_VISUAL = 24171, // Resurrection Impact Visual + SPELL_ARENA_PREPARATION = 32727, // use this one, 32728 not correct + SPELL_ALLIANCE_GOLD_FLAG = 32724, + SPELL_ALLIANCE_GREEN_FLAG = 32725, + SPELL_HORDE_GOLD_FLAG = 35774, + SPELL_HORDE_GREEN_FLAG = 35775, + SPELL_PREPARATION = 44521, // Preparation + SPELL_SPIRIT_HEAL_MANA = 44535, // Spirit Heal + SPELL_RECENTLY_DROPPED_FLAG = 42792, // Recently Dropped Flag + SPELL_AURA_PLAYER_INACTIVE = 43681, // Inactive + SPELL_HONORABLE_DEFENDER_25Y = 68652, // +50% honor when standing at a capture point that you control, 25yards radius (added in 3.2) + SPELL_HONORABLE_DEFENDER_60Y = 66157 // +50% honor when standing at a capture point that you control, 60yards radius (added in 3.2), probably for 40+ player battlegrounds +}; + +enum BattleGroundTimeIntervals +{ + RESURRECTION_INTERVAL = 30000, // ms + //REMIND_INTERVAL = 10000, // ms + INVITATION_REMIND_TIME = 20000, // ms + INVITE_ACCEPT_WAIT_TIME = 40000, // ms + TIME_TO_AUTOREMOVE = 120000, // ms + MAX_OFFLINE_TIME = 300, // secs + RESPAWN_ONE_DAY = 86400, // secs + RESPAWN_IMMEDIATELY = 0, // secs + BUFF_RESPAWN_TIME = 180, // secs +}; + +enum BattleGroundStartTimeIntervals +{ + BG_START_DELAY_2M = 120000, // ms (2 minutes) + BG_START_DELAY_1M = 60000, // ms (1 minute) + BG_START_DELAY_30S = 30000, // ms (30 seconds) + BG_START_DELAY_15S = 15000, // ms (15 seconds) Used only in arena + BG_START_DELAY_NONE = 0, // ms +}; + +enum BattleGroundBuffObjects +{ + BG_OBJECTID_SPEEDBUFF_ENTRY = 179871, + BG_OBJECTID_REGENBUFF_ENTRY = 179904, + BG_OBJECTID_BERSERKERBUFF_ENTRY = 179905 +}; + +enum BattleGroundRandomRewards +{ + BG_REWARD_WINNER_HONOR_FIRST = 30, + BG_REWARD_WINNER_ARENA_FIRST = 25, + BG_REWARD_WINNER_HONOR_LAST = 15, + BG_REWARD_WINNER_ARENA_LAST = 0, + BG_REWARD_LOOSER_HONOR_FIRST = 5, + BG_REWARD_LOOSER_HONOR_LAST = 5 +}; + +const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENBUFF_ENTRY, BG_OBJECTID_BERSERKERBUFF_ENTRY }; + +enum BattleGroundStatus +{ + STATUS_NONE = 0, // first status, should mean bg is not instance + STATUS_WAIT_QUEUE = 1, // means bg is empty and waiting for queue + STATUS_WAIT_JOIN = 2, // this means, that BG has already started and it is waiting for more players + STATUS_IN_PROGRESS = 3, // means bg is running + STATUS_WAIT_LEAVE = 4 // means some faction has won BG and it is ending +}; + +struct BattleGroundPlayer +{ + time_t OfflineRemoveTime; // for tracking and removing offline players from queue after 5 minutes + uint32 Team; // Player's team +}; + +struct BattleGroundObjectInfo +{ + BattleGroundObjectInfo() : object(NULL), timer(0), spellid(0) {} + + GameObject *object; + int32 timer; + uint32 spellid; +}; + +// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time +enum BattleGroundQueueTypeId +{ + BATTLEGROUND_QUEUE_NONE = 0, + BATTLEGROUND_QUEUE_AV = 1, + BATTLEGROUND_QUEUE_WS = 2, + BATTLEGROUND_QUEUE_AB = 3, + BATTLEGROUND_QUEUE_EY = 4, + BATTLEGROUND_QUEUE_SA = 5, + BATTLEGROUND_QUEUE_IC = 6, + BATTLEGROUND_QUEUE_RB = 7, + BATTLEGROUND_QUEUE_2v2 = 8, + BATTLEGROUND_QUEUE_3v3 = 9, + BATTLEGROUND_QUEUE_5v5 = 10, + MAX_BATTLEGROUND_QUEUE_TYPES +}; + +enum ScoreType +{ + SCORE_KILLING_BLOWS = 1, + SCORE_DEATHS = 2, + SCORE_HONORABLE_KILLS = 3, + SCORE_BONUS_HONOR = 4, + //EY, but in MSG_PVP_LOG_DATA opcode! + SCORE_DAMAGE_DONE = 5, + SCORE_HEALING_DONE = 6, + //WS + SCORE_FLAG_CAPTURES = 7, + SCORE_FLAG_RETURNS = 8, + //AB + SCORE_BASES_ASSAULTED = 9, + SCORE_BASES_DEFENDED = 10, + //AV + SCORE_GRAVEYARDS_ASSAULTED = 11, + SCORE_GRAVEYARDS_DEFENDED = 12, + SCORE_TOWERS_ASSAULTED = 13, + SCORE_TOWERS_DEFENDED = 14, + SCORE_MINES_CAPTURED = 15, + SCORE_LEADERS_KILLED = 16, + SCORE_SECONDARY_OBJECTIVES = 17, + //SOTA + SCORE_DESTROYED_DEMOLISHER = 18, + SCORE_DESTROYED_WALL = 19 +}; + +enum ArenaType +{ + ARENA_TYPE_2v2 = 2, + ARENA_TYPE_3v3 = 3, + ARENA_TYPE_5v5 = 5 +}; + +enum BattleGroundType +{ + TYPE_BATTLEGROUND = 3, + TYPE_ARENA = 4 +}; + +enum BattleGroundWinner +{ + WINNER_HORDE = 0, + WINNER_ALLIANCE = 1, + WINNER_NONE = 2 +}; + +enum BattleGroundTeamId +{ + BG_TEAM_ALLIANCE = 0, + BG_TEAM_HORDE = 1 +}; +#define BG_TEAMS_COUNT 2 + +enum BattleGroundStartingEvents +{ + BG_STARTING_EVENT_NONE = 0x00, + BG_STARTING_EVENT_1 = 0x01, + BG_STARTING_EVENT_2 = 0x02, + BG_STARTING_EVENT_3 = 0x04, + BG_STARTING_EVENT_4 = 0x08 +}; + +enum BattleGroundStartingEventsIds +{ + BG_STARTING_EVENT_FIRST = 0, + BG_STARTING_EVENT_SECOND = 1, + BG_STARTING_EVENT_THIRD = 2, + BG_STARTING_EVENT_FOURTH = 3 +}; +#define BG_STARTING_EVENT_COUNT 4 + +enum BG_OBJECT_DMG_HIT_TYPE +{ + BG_OBJECT_DMG_HIT_TYPE_JUST_DAMAGED = 0, + BG_OBJECT_DMG_HIT_TYPE_DAMAGED = 1, + BG_OBJECT_DMG_HIT_TYPE_JUST_HIGH_DAMAGED = 2, + BG_OBJECT_DMG_HIT_TYPE_HIGH_DAMAGED = 3, + BG_OBJECT_DMG_HIT_TYPE_JUST_DESTROYED = 4 +}; + +enum GroupJoinBattlegroundResult +{ + // positive values are indexes in BattlemasterList.dbc + ERR_GROUP_JOIN_BATTLEGROUND_FAIL = 0, // Your group has joined a battleground queue, but you are not eligible (showed for non existing BattlemasterList.dbc indexes) + ERR_BATTLEGROUND_NONE = -1, // not show anything + ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS = -2, // You cannot join the battleground yet because you or one of your party members is flagged as a Deserter. + ERR_ARENA_TEAM_PARTY_SIZE = -3, // Incorrect party size for this arena. + ERR_BATTLEGROUND_TOO_MANY_QUEUES = -4, // You can only be queued for 2 battles at once + ERR_BATTLEGROUND_CANNOT_QUEUE_FOR_RATED = -5, // You cannot queue for a rated match while queued for other battles + ERR_BATTLEDGROUND_QUEUED_FOR_RATED = -6, // You cannot queue for another battle while queued for a rated arena match + ERR_BATTLEGROUND_TEAM_LEFT_QUEUE = -7, // Your team has left the arena queue + ERR_BATTLEGROUND_NOT_IN_BATTLEGROUND = -8, // You can't do that in a battleground. + ERR_BATTLEGROUND_JOIN_XP_GAIN = -9, // wtf, doesn't exist in client... + ERR_BATTLEGROUND_JOIN_RANGE_INDEX = -10, // Cannot join the queue unless all members of your party are in the same battleground level range. + ERR_BATTLEGROUND_JOIN_TIMED_OUT = -11, // %s was unavailable to join the queue. (uint64 guid exist in client cache) + ERR_BATTLEGROUND_JOIN_FAILED = -12, // Join as a group failed (uint64 guid doesn't exist in client cache) + ERR_LFG_CANT_USE_BATTLEGROUND = -13, // You cannot queue for a battleground or arena while using the dungeon system. + ERR_IN_RANDOM_BG = -14, // Can't do that while in a Random Battleground queue. + ERR_IN_NON_RANDOM_BG = -15, // Can't queue for Random Battleground while in another Battleground queue. +}; + +class BattleGroundScore +{ + public: + BattleGroundScore() : KillingBlows(0), Deaths(0), HonorableKills(0), + BonusHonor(0), DamageDone(0), HealingDone(0) + {} + virtual ~BattleGroundScore() {} //virtual destructor is used when deleting score from scores map + + uint32 KillingBlows; + uint32 Deaths; + uint32 HonorableKills; + uint32 BonusHonor; + uint32 DamageDone; + uint32 HealingDone; +}; + +enum BGHonorMode +{ + BG_NORMAL = 0, + BG_HOLIDAY, + BG_HONOR_MODE_NUM +}; + +/* +This class is used to: +1. Add player to battleground +2. Remove player from battleground +3. some certain cases, same for all battlegrounds +4. It has properties same for all battlegrounds +*/ +class BattleGround +{ + friend class BattleGroundMgr; + + public: + /* Construction */ + BattleGround(); + /*BattleGround(const BattleGround& bg);*/ + virtual ~BattleGround(); + virtual void Update(uint32 diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version + virtual bool SetupBattleGround() // must be implemented in BG subclass + { + return true; + } + virtual void Reset(); // resets all common properties for battlegrounds, must be implemented and called in BG subclass + virtual void StartingEventCloseDoors() {} + virtual void StartingEventOpenDoors() {} + virtual void ResetBGSubclass() // must be implemented in BG subclass + { + } + + virtual void DestroyGate(Player* pl, GameObject* go, uint32 destroyedEvent) {} + + /* achievement req. */ + virtual bool IsAllNodesConrolledByTeam(uint32 /*team*/) const { return false; } + bool IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const; + + /* Battleground */ + // Get methods: + char const* GetName() const { return m_Name; } + BattleGroundTypeId GetTypeID(bool GetRandom = false) const { return GetRandom ? m_RandomTypeID : m_TypeID; } + BattleGroundBracketId GetBracketId() const { return m_BracketId; } + uint32 GetInstanceID() const { return m_InstanceID; } + BattleGroundStatus GetStatus() const { return m_Status; } + uint32 GetClientInstanceID() const { return m_ClientInstanceID; } + uint32 GetStartTime() const { return m_StartTime; } + uint32 GetEndTime() const { return m_EndTime; } + uint32 GetLastResurrectTime() const { return m_LastResurrectTime; } + uint32 GetMaxPlayers() const { return m_MaxPlayers; } + uint32 GetMinPlayers() const { return m_MinPlayers; } + + uint32 GetMinLevel() const { return m_LevelMin; } + uint32 GetMaxLevel() const { return m_LevelMax; } + + uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; } + uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; } + + int32 GetStartDelayTime() const { return m_StartDelayTime; } + uint8 GetArenaType() const { return m_ArenaType; } + uint8 GetWinner() const { return m_Winner; } + uint32 GetBattlemasterEntry() const; + uint32 GetBonusHonorFromKill(uint32 kills) const; + bool IsRandom() { return m_IsRandom; } + + // Set methods: + void SetName(char const* Name) { m_Name = Name; } + void SetTypeID(BattleGroundTypeId TypeID) { m_TypeID = TypeID; } + void SetRandomTypeID(BattleGroundTypeId TypeID) { m_RandomTypeID = TypeID; } + //here we can count minlevel and maxlevel for players + void SetBracket(PvPDifficultyEntry const* bracketEntry); + void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; } + void SetStatus(BattleGroundStatus Status) { m_Status = Status; } + void SetClientInstanceID(uint32 InstanceID) { m_ClientInstanceID = InstanceID; } + void SetStartTime(uint32 Time) { m_StartTime = Time; } + void SetEndTime(uint32 Time) { m_EndTime = Time; } + void SetLastResurrectTime(uint32 Time) { m_LastResurrectTime = Time; } + void SetMaxPlayers(uint32 MaxPlayers) { m_MaxPlayers = MaxPlayers; } + void SetMinPlayers(uint32 MinPlayers) { m_MinPlayers = MinPlayers; } + void SetLevelRange(uint32 min, uint32 max) { m_LevelMin = min; m_LevelMax = max; } + void SetRated(bool state) { m_IsRated = state; } + void SetArenaType(uint8 type) { m_ArenaType = type; } + void SetArenaorBGType(bool _isArena) { m_IsArena = _isArena; } + void SetWinner(uint8 winner) { m_Winner = winner; } + + void ModifyStartDelayTime(int diff) { m_StartDelayTime -= diff; } + void SetStartDelayTime(int Time) { m_StartDelayTime = Time; } + + void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; } + void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; } + + void AddToBGFreeSlotQueue(); //this queue will be useful when more battlegrounds instances will be available + void RemoveFromBGFreeSlotQueue(); //this method could delete whole BG instance, if another free is available + + void DecreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? --m_InvitedAlliance : --m_InvitedHorde; } + void IncreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; } + + void SetRandom(bool isRandom) { m_IsRandom = isRandom; } + uint32 GetInvitedCount(uint32 team) const + { + if (team == ALLIANCE) + return m_InvitedAlliance; + else + return m_InvitedHorde; + } + bool HasFreeSlots() const; + uint32 GetFreeSlotsForTeam(uint32 Team) const; + + bool isArena() const { return m_IsArena; } + bool isBattleGround() const { return !m_IsArena; } + bool isRated() const { return m_IsRated; } + + typedef std::map BattleGroundPlayerMap; + BattleGroundPlayerMap const& GetPlayers() const { return m_Players; } + uint32 GetPlayersSize() const { return m_Players.size(); } + + typedef std::map BattleGroundScoreMap; + BattleGroundScoreMap::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); } + BattleGroundScoreMap::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); } + uint32 GetPlayerScoresSize() const { return m_PlayerScores.size(); } + + uint32 GetReviveQueueSize() const { return m_ReviveQueue.size(); } + + void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid); + void RemovePlayerFromResurrectQueue(uint64 player_guid); + + void StartBattleGround(); + + GameObject* GetBGObject(uint32 type); + Creature* GetBGCreature(uint32 type); + /* Location */ + void SetMapId(uint32 MapID) { m_MapId = MapID; } + uint32 GetMapId() const { return m_MapId; } + + /* Map pointers */ + void SetBgMap(BattleGroundMap* map) { m_Map = map; } + BattleGroundMap* GetBgMap() + { + ASSERT(m_Map); + return m_Map; + } + + void SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O); + void GetTeamStartLoc(uint32 TeamID, float &X, float &Y, float &Z, float &O) const + { + BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID); + X = m_TeamStartLocX[idx]; + Y = m_TeamStartLocY[idx]; + Z = m_TeamStartLocZ[idx]; + O = m_TeamStartLocO[idx]; + } + + /* Packet Transfer */ + // method that should fill worldpacket with actual world states (not yet implemented for all battlegrounds!) + virtual void FillInitialWorldStates(WorldPacket& /*data*/) {} + void SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender = NULL, bool self = true); + void SendPacketToAll(WorldPacket *packet); + void YellToAll(Creature* creature, const char* text, uint32 language); + + template + void BroadcastWorker(Do& _do); + + void PlaySoundToTeam(uint32 SoundID, uint32 TeamID); + void PlaySoundToAll(uint32 SoundID); + void CastSpellOnTeam(uint32 SpellID, uint32 TeamID); + void RewardHonorToTeam(uint32 Honor, uint32 TeamID); + void RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID); + void UpdateWorldState(uint32 Field, uint32 Value); + void UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source); + void EndBattleGround(uint32 winner); + void BlockMovement(Player *plr); + + void SendWarningToAll(int32 entry, ...); + void SendMessageToAll(int32 entry, ChatMsg type, Player const* source = NULL); + void PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...); + + // specialized version with 2 string id args + void SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 strId1 = 0, int32 strId2 = 0); + + /* Raid Group */ + Group *GetBgRaid(uint32 TeamID) const { return TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; } + void SetBgRaid(uint32 TeamID, Group *bg_raid); + + virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + + static BattleGroundTeamId GetTeamIndexByTeamId(uint32 Team) { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } + uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } + uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases + void UpdatePlayersCountByTeam(uint32 Team, bool remove) + { + if (remove) + --m_PlayersCount[GetTeamIndexByTeamId(Team)]; + else + ++m_PlayersCount[GetTeamIndexByTeamId(Team)]; + } + + // used for rated arena battles + void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; } + uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } + void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; } + int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } + void CheckArenaWinConditions(); + void UpdateArenaWorldState(); + + /* Triggers handle */ + // must be implemented in BG subclass + virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} + // must be implemented in BG subclass if need AND call base class generic code + virtual void HandleKillPlayer(Player *player, Player *killer); + virtual void HandleKillUnit(Creature* /*unit*/, Player* /*killer*/); + + /* Battleground events */ + virtual void EventPlayerDroppedFlag(Player* /*player*/) {} + virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {} + virtual void EventPlayerCapturedFlag(Player* /*player*/) {} + void EventPlayerLoggedIn(Player* player, uint64 plr_guid); + void EventPlayerLoggedOut(Player* player); + virtual void EventPlayerDamagedGO(Player* plr, GameObject* go, uint8 hitType, uint32 destroyedEvent) {} + virtual void EventPlayerUsedGO(Player* /*player*/, GameObject* /*go*/){} + + /* Death related */ + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); + + virtual void AddPlayer(Player *plr); // must be implemented in BG subclass + + void AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team); + + virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); + // can be extended in in BG subclass + + void HandleTriggerBuff(uint64 const& go_guid); + void SetHoliday(bool is_holiday); + + // TODO: make this protected: + typedef std::vector BGObjects; + typedef std::vector BGCreatures; + BGObjects m_BgObjects; + BGCreatures m_BgCreatures; + void SpawnBGObject(uint32 type, uint32 respawntime); + bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime = 0); +// void SpawnBGCreature(uint32 type, uint32 respawntime); + Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime = 0); + bool DelCreature(uint32 type); + bool DelObject(uint32 type); + bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team); + int32 GetObjectType(uint64 guid); + + void DoorOpen(uint32 type); + void DoorClose(uint32 type); + //to be removed + const char *GetTrinityString(int32 entry); + + virtual bool HandlePlayerUnderMap(Player * /*plr*/) { return false; } + + // since arenas can be AvA or Hvh, we have to get the "temporary" team of a player + uint32 GetPlayerTeam(uint64 guid); + uint32 GetOtherTeam(uint32 teamId); + bool IsPlayerInBattleGround(uint64 guid); + + void SetDeleteThis() {m_SetDeleteThis = true;} + + /* virtual score-array - get's used in bg-subclasses */ + int32 m_TeamScores[BG_TEAMS_COUNT]; + + void RewardXPAtKill(Player* plr, Player* victim); + + protected: + //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround + void EndNow(); + void PlayerAddedToBGCheckIfBGIsRunning(Player* plr); + + /* Scorekeeping */ + + BattleGroundScoreMap m_PlayerScores; // Player scores + // must be implemented in BG subclass + virtual void RemovePlayer(Player * /*player*/, uint64 /*guid*/) {} + + /* Player lists, those need to be accessible by inherited classes */ + BattleGroundPlayerMap m_Players; + // Spirit Guide guid + Player list GUIDS + std::map > m_ReviveQueue; + + /* + these are important variables used for starting messages + */ + uint8 m_Events; + BattleGroundStartTimeIntervals m_StartDelayTimes[BG_STARTING_EVENT_COUNT]; + //this must be filled in constructors! + uint32 m_StartMessageIds[BG_STARTING_EVENT_COUNT]; + + bool m_BuffChange; + bool m_IsRandom; + + BGHonorMode m_HonorMode; + private: + /* Battleground */ + BattleGroundTypeId m_TypeID; + BattleGroundTypeId m_RandomTypeID; + uint32 m_InstanceID; //BattleGround Instance's GUID! + BattleGroundStatus m_Status; + uint32 m_ClientInstanceID; //the instance-id which is sent to the client and without any other internal use + uint32 m_StartTime; + int32 m_EndTime; // it is set to 120000 when bg is ending and it decreases itself + uint32 m_LastResurrectTime; + BattleGroundBracketId m_BracketId; + uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 + bool m_InBGFreeSlotQueue; // used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque + bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave + bool m_IsArena; + uint8 m_Winner; // 0=alliance, 1=horde, 2=none + int32 m_StartDelayTime; + bool m_IsRated; // is this battle rated? + bool m_PrematureCountDown; + uint32 m_PrematureCountDownTimer; + char const *m_Name; + + /* Player lists */ + std::vector m_ResurrectQueue; // Player GUID + std::deque m_OfflineQueue; // Player GUID + + /* Invited counters are useful for player invitation to BG - do not allow, if BG is started to one faction to have 2 more players than another faction */ + /* Invited counters will be changed only when removing already invited player from queue, removing player from battleground and inviting player to BG */ + /* Invited players counters*/ + uint32 m_InvitedAlliance; + uint32 m_InvitedHorde; + + /* Raid Group */ + Group *m_BgRaids[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde + + /* Players count by team */ + uint32 m_PlayersCount[BG_TEAMS_COUNT]; + + /* Arena team ids by team */ + uint32 m_ArenaTeamIds[BG_TEAMS_COUNT]; + + int32 m_ArenaTeamRatingChanges[BG_TEAMS_COUNT]; + + /* Limits */ + uint32 m_LevelMin; + uint32 m_LevelMax; + uint32 m_MaxPlayersPerTeam; + uint32 m_MaxPlayers; + uint32 m_MinPlayersPerTeam; + uint32 m_MinPlayers; + + /* Start location */ + uint32 m_MapId; + BattleGroundMap* m_Map; + float m_TeamStartLocX[BG_TEAMS_COUNT]; + float m_TeamStartLocY[BG_TEAMS_COUNT]; + float m_TeamStartLocZ[BG_TEAMS_COUNT]; + float m_TeamStartLocO[BG_TEAMS_COUNT]; +}; +#endif + diff --git a/src/server/game/BattleGrounds/BattleGroundAA.cpp b/src/server/game/BattleGrounds/BattleGroundAA.cpp new file mode 100644 index 00000000000..56cf3ebed15 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundAA.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundAA.h" +#include "Language.h" +#include "Player.h" + +BattleGroundAA::BattleGroundAA() +{ + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundAA::~BattleGroundAA() +{ + +} + +void BattleGroundAA::Update(uint32 diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundAA::StartingEventCloseDoors() +{ +} + +void BattleGroundAA::StartingEventOpenDoors() +{ +} + +void BattleGroundAA::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundAAScore* sc = new BattleGroundAAScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundAA::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ +} + +void BattleGroundAA::HandleKillPlayer(Player* player, Player* killer) +{ + BattleGround::HandleKillPlayer(player, killer); +} + +void BattleGroundAA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ +} + +bool BattleGroundAA::SetupBattleGround() +{ + return true; +} + diff --git a/src/server/game/BattleGrounds/BattleGroundAA.h b/src/server/game/BattleGrounds/BattleGroundAA.h new file mode 100644 index 00000000000..a13833697cf --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundAA.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDAA_H +#define __BATTLEGROUNDAA_H + +class BattleGround; + +class BattleGroundAAScore : public BattleGroundScore +{ + public: + BattleGroundAAScore() {}; + virtual ~BattleGroundAAScore() {}; + //TODO fix me +}; + +class BattleGroundAA : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundAA(); + ~BattleGroundAA(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void HandleKillPlayer(Player* player, Player *killer); +}; +#endif + diff --git a/src/server/game/BattleGrounds/BattleGroundAB.cpp b/src/server/game/BattleGrounds/BattleGroundAB.cpp new file mode 100644 index 00000000000..38671e85597 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundAB.cpp @@ -0,0 +1,715 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "World.h" +#include "WorldPacket.h" +#include "ObjectMgr.h" +#include "BattleGroundMgr.h" +#include "BattleGround.h" +#include "BattleGroundAB.h" +#include "Creature.h" +#include "Language.h" +#include "Object.h" +#include "Player.h" +#include "Util.h" + +// these variables aren't used outside of this file, so declare them only here +uint32 BG_AB_HonorScoreTicks[BG_HONOR_MODE_NUM] = { + 330, // normal honor + 200 // holiday +}; + +uint32 BG_AB_ReputationScoreTicks[BG_HONOR_MODE_NUM] = { + 200, // normal honor + 150 // holiday +}; + +BattleGroundAB::BattleGroundAB() +{ + m_BuffChange = true; + m_BgObjects.resize(BG_AB_OBJECT_MAX); + m_BgCreatures.resize(BG_AB_ALL_NODES_COUNT + 5);//+5 for aura triggers + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AB_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AB_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AB_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AB_HAS_BEGUN; +} + +BattleGroundAB::~BattleGroundAB() +{ +} + +void BattleGroundAB::Update(uint32 diff) +{ + BattleGround::Update(diff); + + if (GetStatus() == STATUS_IN_PROGRESS) + { + int team_points[BG_TEAMS_COUNT] = { 0, 0 }; + + for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + { + // 3 sec delay to spawn new banner instead previous despawned one + if (m_BannerTimers[node].timer) + { + if (m_BannerTimers[node].timer > diff) + m_BannerTimers[node].timer -= diff; + else + { + m_BannerTimers[node].timer = 0; + _CreateBanner(node, m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false); + } + } + + // 1-minute to occupy a node from contested state + if (m_NodeTimers[node]) + { + if (m_NodeTimers[node] > diff) + m_NodeTimers[node] -= diff; + else + { + m_NodeTimers[node] = 0; + // Change from contested to occupied ! + uint8 teamIndex = m_Nodes[node]-1; + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] += 2; + // burn current contested banner + _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex); + // create new occupied banner + _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); + _SendNodeUpdate(node); + _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); + // Message to chatlog + + if (teamIndex == 0) + { + // FIXME: team and node names not localized + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE,NULL,LANG_BG_AB_ALLY,_GetNodeNameId(node)); + PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); + } + else + { + // FIXME: team and node names not localized + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE,NULL,LANG_BG_AB_HORDE,_GetNodeNameId(node)); + PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); + } + } + } + + for (int team = 0; team < BG_TEAMS_COUNT; ++team) + if (m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED) + ++team_points[team]; + } + + // Accumulate points + for (int team = 0; team < BG_TEAMS_COUNT; ++team) + { + int points = team_points[team]; + if (!points) + continue; + m_lastTick[team] += diff; + if (m_lastTick[team] > BG_AB_TickIntervals[points]) + { + m_lastTick[team] -= BG_AB_TickIntervals[points]; + m_TeamScores[team] += BG_AB_TickPoints[points]; + m_HonorScoreTics[team] += BG_AB_TickPoints[points]; + m_ReputationScoreTics[team] += BG_AB_TickPoints[points]; + if (m_ReputationScoreTics[team] >= m_ReputationTics) + { + (team == BG_TEAM_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE); + m_ReputationScoreTics[team] -= m_ReputationTics; + } + if (m_HonorScoreTics[team] >= m_HonorTics) + { + RewardHonorToTeam(GetBonusHonorFromKill(1), (team == BG_TEAM_ALLIANCE) ? ALLIANCE : HORDE); + m_HonorScoreTics[team] -= m_HonorTics; + } + if (!m_IsInformedNearVictory && m_TeamScores[team] > BG_AB_WARNING_NEAR_VICTORY_SCORE) + { + if (team == BG_TEAM_ALLIANCE) + SendMessageToAll(LANG_BG_AB_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + else + SendMessageToAll(LANG_BG_AB_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY); + m_IsInformedNearVictory = true; + } + + if (m_TeamScores[team] > BG_AB_MAX_TEAM_SCORE) + m_TeamScores[team] = BG_AB_MAX_TEAM_SCORE; + if (team == BG_TEAM_ALLIANCE) + UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); + if (team == BG_TEAM_HORDE) + UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]); + // update achievement flags + // we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources + uint8 otherTeam = (team + 1) % BG_TEAMS_COUNT; + if (m_TeamScores[team] > m_TeamScores[otherTeam] + 500) + m_TeamScores500Disadvantage[otherTeam] = true; + } + } + + // Test win condition + if (m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE) + EndBattleGround(ALLIANCE); + if (m_TeamScores[BG_TEAM_HORDE] >= BG_AB_MAX_TEAM_SCORE) + EndBattleGround(HORDE); + } +} + +void BattleGroundAB::StartingEventCloseDoors() +{ + // despawn banners, auras and buffs + for (int obj = BG_AB_OBJECT_BANNER_NEUTRAL; obj < BG_AB_DYNAMIC_NODES_COUNT * 8; ++obj) + SpawnBGObject(obj, RESPAWN_ONE_DAY); + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT * 3; ++i) + SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + i, RESPAWN_ONE_DAY); + + // Starting doors + DoorClose(BG_AB_OBJECT_GATE_A); + DoorClose(BG_AB_OBJECT_GATE_H); + SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY); + + // Starting base spirit guides + _NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE); + _NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE); +} + +void BattleGroundAB::StartingEventOpenDoors() +{ + // spawn neutral banners + for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i) + SpawnBGObject(banner, RESPAWN_IMMEDIATELY); + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + //randomly select buff to spawn + uint8 buff = urand(0, 2); + SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + buff + i * 3, RESPAWN_IMMEDIATELY); + } + DoorOpen(BG_AB_OBJECT_GATE_A); + DoorOpen(BG_AB_OBJECT_GATE_H); +} + +void BattleGroundAB::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in the constructor + BattleGroundABScore* sc = new BattleGroundABScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundAB::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ + +} + +void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + switch(Trigger) + { + case 3948: // Arathi Basin Alliance Exit. + if (Source->GetTeam() != ALLIANCE) + Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal"); + else + Source->LeaveBattleground(); + break; + case 3949: // Arathi Basin Horde Exit. + if (Source->GetTeam() != HORDE) + Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal"); + else + Source->LeaveBattleground(); + break; + case 3866: // Stables + case 3869: // Gold Mine + case 3867: // Farm + case 3868: // Lumber Mill + case 3870: // Black Smith + case 4020: // Unk1 + case 4021: // Unk2 + //break; + default: + //sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + //Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +/* type: 0-neutral, 1-contested, 3-occupied + teamIndex: 0-ally, 1-horde */ +void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay) +{ + // Just put it into the queue + if (delay) + { + m_BannerTimers[node].timer = 2000; + m_BannerTimers[node].type = type; + m_BannerTimers[node].teamIndex = teamIndex; + return; + } + + uint8 obj = node*8 + type + teamIndex; + + SpawnBGObject(obj, RESPAWN_IMMEDIATELY); + + // handle aura with banner + if (!type) + return; + obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); + SpawnBGObject(obj, RESPAWN_IMMEDIATELY); +} + +void BattleGroundAB::_DelBanner(uint8 node, uint8 type, uint8 teamIndex) +{ + uint8 obj = node*8 + type + teamIndex; + SpawnBGObject(obj, RESPAWN_ONE_DAY); + + // handle aura with banner + if (!type) + return; + obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); + SpawnBGObject(obj, RESPAWN_ONE_DAY); +} + +int32 BattleGroundAB::_GetNodeNameId(uint8 node) +{ + switch (node) + { + case BG_AB_NODE_STABLES: return LANG_BG_AB_NODE_STABLES; + case BG_AB_NODE_BLACKSMITH: return LANG_BG_AB_NODE_BLACKSMITH; + case BG_AB_NODE_FARM: return LANG_BG_AB_NODE_FARM; + case BG_AB_NODE_LUMBER_MILL:return LANG_BG_AB_NODE_LUMBER_MILL; + case BG_AB_NODE_GOLD_MINE: return LANG_BG_AB_NODE_GOLD_MINE; + default: + ASSERT(0); + } + return 0; +} + +void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) +{ + const uint8 plusArray[] = {0, 2, 3, 0, 1}; + + // Node icons + for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + data << uint32(BG_AB_OP_NODEICONS[node]) << uint32((m_Nodes[node] == 0)?1:0); + + // Node occupied states + for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + for (uint8 i = 1; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + data << uint32(BG_AB_OP_NODESTATES[node] + plusArray[i]) << uint32((m_Nodes[node] == i)?1:0); + + // How many bases each team owns + uint8 ally = 0, horde = 0; + for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + if (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) + ++ally; + else if (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) + ++horde; + + data << uint32(BG_AB_OP_OCCUPIED_BASES_ALLY) << uint32(ally); + data << uint32(BG_AB_OP_OCCUPIED_BASES_HORDE) << uint32(horde); + + // Team scores + data << uint32(BG_AB_OP_RESOURCES_MAX) << uint32(BG_AB_MAX_TEAM_SCORE); + data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_NEAR_VICTORY_SCORE); + data << uint32(BG_AB_OP_RESOURCES_ALLY) << uint32(m_TeamScores[BG_TEAM_ALLIANCE]); + data << uint32(BG_AB_OP_RESOURCES_HORDE) << uint32(m_TeamScores[BG_TEAM_HORDE]); + + // other unknown + data << uint32(0x745) << uint32(0x2); // 37 1861 unk +} + +void BattleGroundAB::_SendNodeUpdate(uint8 node) +{ + // Send node owner state update to refresh map icons on client + const uint8 plusArray[] = {0, 2, 3, 0, 1}; + + if (m_prevNodes[node]) + UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_prevNodes[node]], 0); + else + UpdateWorldState(BG_AB_OP_NODEICONS[node], 0); + + UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_Nodes[node]], 1); + + // How many bases each team owns + uint8 ally = 0, horde = 0; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + if (m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) + ++ally; + else if (m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED) + ++horde; + + UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_ALLY, ally); + UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_HORDE, horde); +} + +void BattleGroundAB::_NodeOccupied(uint8 node,Team team) +{ + if (!AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team)) + sLog.outError("Failed to spawn spirit guide! point: %u, team: %u,", node, team); + + uint8 capturedNodes = 0; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + if (m_Nodes[node] == GetTeamIndexByTeamId(team) + BG_AB_NODE_TYPE_OCCUPIED && !m_NodeTimers[i]) + ++capturedNodes; + } + if (capturedNodes >= 5) + CastSpellOnTeam(SPELL_AB_QUEST_REWARD_5_BASES, team); + if (capturedNodes >= 4) + CastSpellOnTeam(SPELL_AB_QUEST_REWARD_4_BASES, team); + + if(node >= BG_AB_DYNAMIC_NODES_COUNT)//only dynamic nodes, no start points + return; + Creature* trigger = GetBGCreature(node+7);//0-6 spirit guides + if (!trigger) + trigger = AddCreature(WORLD_TRIGGER,node+7,team,BG_AB_NodePositions[node][0],BG_AB_NodePositions[node][1],BG_AB_NodePositions[node][2],BG_AB_NodePositions[node][3]); + + //add bonus honor aura trigger creature when node is accupied + //cast bonus aura (+50% honor in 25yards) + //aura should only apply to players who have accupied the node, set correct faction for trigger + if (trigger) + { + trigger->setFaction(team == ALLIANCE ? 84 : 83); + trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); + } +} + +void BattleGroundAB::_NodeDeOccupied(uint8 node) +{ + if (node >= BG_AB_DYNAMIC_NODES_COUNT) + return; + + //remove bonus honor aura trigger creature when node is lost + if(node < BG_AB_DYNAMIC_NODES_COUNT)//only dynamic nodes, no start points + DelCreature(node+7);//NULL checks are in DelCreature! 0-6 spirit guides + + // Those who are waiting to resurrect at this node are taken to the closest own node's graveyard + std::vector ghost_list = m_ReviveQueue[m_BgCreatures[node]]; + if (!ghost_list.empty()) + { + WorldSafeLocsEntry const *ClosestGrave = NULL; + for (std::vector::const_iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) + { + Player* plr = objmgr.GetPlayer(*itr); + if (!plr) + continue; + + if (!ClosestGrave) // cache + ClosestGrave = GetClosestGraveYard(plr); + + if (ClosestGrave) + plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); + } + } + + if (m_BgCreatures[node]) + DelCreature(node); + + // buff object isn't despawned +} + +/* Invoked if a player used a banner as a gameobject */ +void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + uint8 node = BG_AB_NODE_STABLES; + GameObject* obj=GetBgMap()->GetGameObject(m_BgObjects[node*8+7]); + while ((node < BG_AB_DYNAMIC_NODES_COUNT) && ((!obj) || (!source->IsWithinDistInMap(obj,10)))) + { + ++node; + obj=GetBgMap()->GetGameObject(m_BgObjects[node*8+BG_AB_OBJECT_AURA_CONTESTED]); + } + + if (node == BG_AB_DYNAMIC_NODES_COUNT) + { + // this means our player isn't close to any of banners - maybe cheater ?? + return; + } + + BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(source->GetTeam()); + + // Check if player really could use this banner, not cheated + if (!(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2)) + return; + + source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + uint32 sound = 0; + // If node is neutral, change to contested + if (m_Nodes[node] == BG_AB_NODE_TYPE_NEUTRAL) + { + UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + 1; + // burn current neutral banner + _DelBanner(node, BG_AB_NODE_TYPE_NEUTRAL, 0); + // create new contested banner + _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); + _SendNodeUpdate(node); + m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; + + // FIXME: team and node names not localized + if (teamIndex == 0) + SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node), LANG_BG_AB_ALLY); + else + SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node), LANG_BG_AB_HORDE); + + sound = BG_AB_SOUND_NODE_CLAIMED; + } + // If node is contested + else if ((m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED)) + { + // If last state is NOT occupied, change node to enemy-contested + if (m_prevNodes[node] < BG_AB_NODE_TYPE_OCCUPIED) + { + UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED; + // burn current contested banner + _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex); + // create new contested banner + _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); + _SendNodeUpdate(node); + m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; + + // FIXME: node names not localized + if (teamIndex == BG_TEAM_ALLIANCE) + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); + } + // If contested, change back to occupied + else + { + UpdatePlayerScore(source, SCORE_BASES_DEFENDED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_OCCUPIED; + // burn current contested banner + _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex); + // create new occupied banner + _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); + _SendNodeUpdate(node); + m_NodeTimers[node] = 0; + _NodeOccupied(node,(teamIndex == BG_TEAM_ALLIANCE) ? ALLIANCE:HORDE); + + // FIXME: node names not localized + if (teamIndex == BG_TEAM_ALLIANCE) + SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); + } + sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; + } + // If node is occupied, change to enemy-contested + else + { + UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED; + // burn current occupied banner + _DelBanner(node, BG_AB_NODE_TYPE_OCCUPIED, !teamIndex); + // create new contested banner + _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); + _SendNodeUpdate(node); + _NodeDeOccupied(node); + m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; + + // FIXME: node names not localized + if (teamIndex == BG_TEAM_ALLIANCE) + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); + + sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; + } + + // If node is occupied again, send "X has taken the Y" msg. + if (m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED) + { + // FIXME: team and node names not localized + if (teamIndex == BG_TEAM_ALLIANCE) + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, LANG_BG_AB_ALLY, _GetNodeNameId(node)); + else + SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE, NULL, LANG_BG_AB_HORDE, _GetNodeNameId(node)); + } + PlaySoundToAll(sound); +} + +bool BattleGroundAB::SetupBattleGround() +{ + for (int i = 0 ; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + if (!AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + 8*i,BG_AB_OBJECTID_NODE_BANNER_0 + i,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_CONT_A + 8*i,BG_AB_OBJECTID_BANNER_CONT_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_CONT_H + 8*i,BG_AB_OBJECTID_BANNER_CONT_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_ALLY + 8*i,BG_AB_OBJECTID_BANNER_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_HORDE + 8*i,BG_AB_OBJECTID_BANNER_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_AURA_ALLY + 8*i,BG_AB_OBJECTID_AURA_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_AURA_HORDE + 8*i,BG_AB_OBJECTID_AURA_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_AURA_CONTESTED + 8*i,BG_AB_OBJECTID_AURA_C,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) +) + { + sLog.outErrorDb("BatteGroundAB: Failed to spawn some object BattleGround not created!"); + return false; + } + } + if (!AddObject(BG_AB_OBJECT_GATE_A,BG_AB_OBJECTID_GATE_A,BG_AB_DoorPositions[0][0],BG_AB_DoorPositions[0][1],BG_AB_DoorPositions[0][2],BG_AB_DoorPositions[0][3],BG_AB_DoorPositions[0][4],BG_AB_DoorPositions[0][5],BG_AB_DoorPositions[0][6],BG_AB_DoorPositions[0][7],RESPAWN_IMMEDIATELY) + || !AddObject(BG_AB_OBJECT_GATE_H,BG_AB_OBJECTID_GATE_H,BG_AB_DoorPositions[1][0],BG_AB_DoorPositions[1][1],BG_AB_DoorPositions[1][2],BG_AB_DoorPositions[1][3],BG_AB_DoorPositions[1][4],BG_AB_DoorPositions[1][5],BG_AB_DoorPositions[1][6],BG_AB_DoorPositions[1][7],RESPAWN_IMMEDIATELY) +) + { + sLog.outErrorDb("BatteGroundAB: Failed to spawn door object BattleGround not created!"); + return false; + } + //buffs + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + if (!AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i, Buff_Entries[0], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 1, Buff_Entries[1], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 2, Buff_Entries[2], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) +) + sLog.outErrorDb("BatteGroundAB: Failed to spawn buff object!"); + } + + return true; +} + +void BattleGroundAB::Reset() +{ + //call parent's class reset + BattleGround::Reset(); + + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + m_lastTick[BG_TEAM_ALLIANCE] = 0; + m_lastTick[BG_TEAM_HORDE] = 0; + m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0; + m_HonorScoreTics[BG_TEAM_HORDE] = 0; + m_ReputationScoreTics[BG_TEAM_ALLIANCE] = 0; + m_ReputationScoreTics[BG_TEAM_HORDE] = 0; + m_IsInformedNearVictory = false; + bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID()); + m_HonorTics = (isBGWeekend) ? BG_AB_ABBGWeekendHonorTicks : BG_AB_NotABBGWeekendHonorTicks; + m_ReputationTics = (isBGWeekend) ? BG_AB_ABBGWeekendReputationTicks : BG_AB_NotABBGWeekendReputationTicks; + m_TeamScores500Disadvantage[BG_TEAM_ALLIANCE] = false; + m_TeamScores500Disadvantage[BG_TEAM_HORDE] = false; + + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + m_Nodes[i] = 0; + m_prevNodes[i] = 0; + m_NodeTimers[i] = 0; + m_BannerTimers[i].timer = 0; + } + + for (uint8 i = 0; i < BG_AB_ALL_NODES_COUNT + 5; ++i)//+5 for aura triggers + if (m_BgCreatures[i]) + DelCreature(i); +} + +void BattleGroundAB::EndBattleGround(uint32 winner) +{ + //win reward + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + //complete map_end rewards (even if no team wins) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + + BattleGround::EndBattleGround(winner); +} + +WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) +{ + BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(player->GetTeam()); + + // Is there any occupied node for this team? + std::vector nodes; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + if (m_Nodes[i] == teamIndex + 3) + nodes.push_back(i); + + WorldSafeLocsEntry const* good_entry = NULL; + // If so, select the closest node to place ghost on + if (!nodes.empty()) + { + float plr_x = player->GetPositionX(); + float plr_y = player->GetPositionY(); + + float mindist = 999999.0f; + for (uint8 i = 0; i < nodes.size(); ++i) + { + WorldSafeLocsEntry const*entry = sWorldSafeLocsStore.LookupEntry(BG_AB_GraveyardIds[nodes[i]]); + if (!entry) + continue; + float dist = (entry->x - plr_x)*(entry->x - plr_x)+(entry->y - plr_y)*(entry->y - plr_y); + if (mindist > dist) + { + mindist = dist; + good_entry = entry; + } + } + nodes.clear(); + } + // If not, place ghost on starting location + if (!good_entry) + good_entry = sWorldSafeLocsStore.LookupEntry(BG_AB_GraveyardIds[teamIndex+5]); + + return good_entry; +} + +void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) +{ + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); + if (itr == m_PlayerScores.end()) // player not found... + return; + + switch(type) + { + case SCORE_BASES_ASSAULTED: + ((BattleGroundABScore*)itr->second)->BasesAssaulted += value; + break; + case SCORE_BASES_DEFENDED: + ((BattleGroundABScore*)itr->second)->BasesDefended += value; + break; + default: + BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); + break; + } +} + +bool BattleGroundAB::IsAllNodesConrolledByTeam(uint32 team) const +{ + uint32 count = 0; + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + if ((team == ALLIANCE && m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) || + (team == HORDE && m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED)) + ++count; + + return count == BG_AB_DYNAMIC_NODES_COUNT; +} diff --git a/src/server/game/BattleGrounds/BattleGroundAB.h b/src/server/game/BattleGrounds/BattleGroundAB.h new file mode 100644 index 00000000000..3072f8beafd --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundAB.h @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDAB_H +#define __BATTLEGROUNDAB_H + +class BattleGround; + +enum BG_AB_WorldStates +{ + BG_AB_OP_OCCUPIED_BASES_HORDE = 1778, + BG_AB_OP_OCCUPIED_BASES_ALLY = 1779, + BG_AB_OP_RESOURCES_ALLY = 1776, + BG_AB_OP_RESOURCES_HORDE = 1777, + BG_AB_OP_RESOURCES_MAX = 1780, + BG_AB_OP_RESOURCES_WARNING = 1955 +/* + BG_AB_OP_STABLE_ICON = 1842, //Stable map icon (NONE) + BG_AB_OP_STABLE_STATE_ALIENCE = 1767, //Stable map state (ALIENCE) + BG_AB_OP_STABLE_STATE_HORDE = 1768, //Stable map state (HORDE) + BG_AB_OP_STABLE_STATE_CON_ALI = 1769, //Stable map state (CON ALIENCE) + BG_AB_OP_STABLE_STATE_CON_HOR = 1770, //Stable map state (CON HORDE) + BG_AB_OP_FARM_ICON = 1845, //Farm map icon (NONE) + BG_AB_OP_FARM_STATE_ALIENCE = 1772, //Farm state (ALIENCE) + BG_AB_OP_FARM_STATE_HORDE = 1773, //Farm state (HORDE) + BG_AB_OP_FARM_STATE_CON_ALI = 1774, //Farm state (CON ALIENCE) + BG_AB_OP_FARM_STATE_CON_HOR = 1775, //Farm state (CON HORDE) + + BG_AB_OP_BLACKSMITH_ICON = 1846, //Blacksmith map icon (NONE) + BG_AB_OP_BLACKSMITH_STATE_ALIENCE = 1782, //Blacksmith map state (ALIENCE) + BG_AB_OP_BLACKSMITH_STATE_HORDE = 1783, //Blacksmith map state (HORDE) + BG_AB_OP_BLACKSMITH_STATE_CON_ALI = 1784, //Blacksmith map state (CON ALIENCE) + BG_AB_OP_BLACKSMITH_STATE_CON_HOR = 1785, //Blacksmith map state (CON HORDE) + BG_AB_OP_LUMBERMILL_ICON = 1844, //Lumber Mill map icon (NONE) + BG_AB_OP_LUMBERMILL_STATE_ALIENCE = 1792, //Lumber Mill map state (ALIENCE) + BG_AB_OP_LUMBERMILL_STATE_HORDE = 1793, //Lumber Mill map state (HORDE) + BG_AB_OP_LUMBERMILL_STATE_CON_ALI = 1794, //Lumber Mill map state (CON ALIENCE) + BG_AB_OP_LUMBERMILL_STATE_CON_HOR = 1795, //Lumber Mill map state (CON HORDE) + BG_AB_OP_GOLDMINE_ICON = 1843, //Gold Mine map icon (NONE) + BG_AB_OP_GOLDMINE_STATE_ALIENCE = 1787, //Gold Mine map state (ALIENCE) + BG_AB_OP_GOLDMINE_STATE_HORDE = 1788, //Gold Mine map state (HORDE) + BG_AB_OP_GOLDMINE_STATE_CON_ALI = 1789, //Gold Mine map state (CON ALIENCE + BG_AB_OP_GOLDMINE_STATE_CON_HOR = 1790, //Gold Mine map state (CON HORDE) +*/ +}; + +const uint32 BG_AB_OP_NODESTATES[5] = {1767, 1782, 1772, 1792, 1787}; + +const uint32 BG_AB_OP_NODEICONS[5] = {1842, 1846, 1845, 1844, 1843}; + +/* Note: code uses that these IDs follow each other */ +enum BG_AB_NodeObjectId +{ + BG_AB_OBJECTID_NODE_BANNER_0 = 180087, // Stables banner + BG_AB_OBJECTID_NODE_BANNER_1 = 180088, // Blacksmith banner + BG_AB_OBJECTID_NODE_BANNER_2 = 180089, // Farm banner + BG_AB_OBJECTID_NODE_BANNER_3 = 180090, // Lumber mill banner + BG_AB_OBJECTID_NODE_BANNER_4 = 180091 // Gold mine banner +}; + +enum BG_AB_ObjectType +{ + // for all 5 node points 8*5=40 objects + BG_AB_OBJECT_BANNER_NEUTRAL = 0, + BG_AB_OBJECT_BANNER_CONT_A = 1, + BG_AB_OBJECT_BANNER_CONT_H = 2, + BG_AB_OBJECT_BANNER_ALLY = 3, + BG_AB_OBJECT_BANNER_HORDE = 4, + BG_AB_OBJECT_AURA_ALLY = 5, + BG_AB_OBJECT_AURA_HORDE = 6, + BG_AB_OBJECT_AURA_CONTESTED = 7, + //gates + BG_AB_OBJECT_GATE_A = 40, + BG_AB_OBJECT_GATE_H = 41, + //buffs + BG_AB_OBJECT_SPEEDBUFF_STABLES = 42, + BG_AB_OBJECT_REGENBUFF_STABLES = 43, + BG_AB_OBJECT_BERSERKBUFF_STABLES = 44, + BG_AB_OBJECT_SPEEDBUFF_BLACKSMITH = 45, + BG_AB_OBJECT_REGENBUFF_BLACKSMITH = 46, + BG_AB_OBJECT_BERSERKBUFF_BLACKSMITH = 47, + BG_AB_OBJECT_SPEEDBUFF_FARM = 48, + BG_AB_OBJECT_REGENBUFF_FARM = 49, + BG_AB_OBJECT_BERSERKBUFF_FARM = 50, + BG_AB_OBJECT_SPEEDBUFF_LUMBER_MILL = 51, + BG_AB_OBJECT_REGENBUFF_LUMBER_MILL = 52, + BG_AB_OBJECT_BERSERKBUFF_LUMBER_MILL = 53, + BG_AB_OBJECT_SPEEDBUFF_GOLD_MINE = 54, + BG_AB_OBJECT_REGENBUFF_GOLD_MINE = 55, + BG_AB_OBJECT_BERSERKBUFF_GOLD_MINE = 56, + BG_AB_OBJECT_MAX = 57, +}; + +/* Object id templates from DB */ +enum BG_AB_ObjectTypes +{ + BG_AB_OBJECTID_BANNER_A = 180058, + BG_AB_OBJECTID_BANNER_CONT_A = 180059, + BG_AB_OBJECTID_BANNER_H = 180060, + BG_AB_OBJECTID_BANNER_CONT_H = 180061, + + BG_AB_OBJECTID_AURA_A = 180100, + BG_AB_OBJECTID_AURA_H = 180101, + BG_AB_OBJECTID_AURA_C = 180102, + + BG_AB_OBJECTID_GATE_A = 180255, + BG_AB_OBJECTID_GATE_H = 180256 +}; + +enum BG_AB_Timers +{ + BG_AB_FLAG_CAPTURING_TIME = 60000, +}; + +enum BG_AB_Score +{ + BG_AB_WARNING_NEAR_VICTORY_SCORE = 1400, + BG_AB_MAX_TEAM_SCORE = 1600 +}; + +/* do NOT change the order, else wrong behaviour */ +enum BG_AB_BattleGroundNodes +{ + BG_AB_NODE_STABLES = 0, + BG_AB_NODE_BLACKSMITH = 1, + BG_AB_NODE_FARM = 2, + BG_AB_NODE_LUMBER_MILL = 3, + BG_AB_NODE_GOLD_MINE = 4, + + BG_AB_DYNAMIC_NODES_COUNT = 5, // dynamic nodes that can be captured + + BG_AB_SPIRIT_ALIANCE = 5, + BG_AB_SPIRIT_HORDE = 6, + + BG_AB_ALL_NODES_COUNT = 7, // all nodes (dynamic and static) +}; + +enum BG_AB_NodeStatus +{ + BG_AB_NODE_TYPE_NEUTRAL = 0, + BG_AB_NODE_TYPE_CONTESTED = 1, + BG_AB_NODE_STATUS_ALLY_CONTESTED = 1, + BG_AB_NODE_STATUS_HORDE_CONTESTED = 2, + BG_AB_NODE_TYPE_OCCUPIED = 3, + BG_AB_NODE_STATUS_ALLY_OCCUPIED = 3, + BG_AB_NODE_STATUS_HORDE_OCCUPIED = 4 +}; + +enum BG_AB_Sounds +{ + BG_AB_SOUND_NODE_CLAIMED = 8192, + BG_AB_SOUND_NODE_CAPTURED_ALLIANCE = 8173, + BG_AB_SOUND_NODE_CAPTURED_HORDE = 8213, + BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE = 8212, + BG_AB_SOUND_NODE_ASSAULTED_HORDE = 8174, + BG_AB_SOUND_NEAR_VICTORY = 8456 +}; + +#define BG_AB_NotABBGWeekendHonorTicks 330 +#define BG_AB_ABBGWeekendHonorTicks 200 +#define BG_AB_NotABBGWeekendReputationTicks 200 +#define BG_AB_ABBGWeekendReputationTicks 150 + +// x, y, z, o +const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { + {1166.785f, 1200.132f, -56.70859f, 0.9075713f}, // stables + {977.0156f, 1046.616f, -44.80923f, -2.600541f}, // blacksmith + {806.1821f, 874.2723f, -55.99371f, -2.303835f}, // farm + {856.1419f, 1148.902f, 11.18469f, -2.303835f}, // lumber mill + {1146.923f, 848.1782f, -110.917f, -0.7330382f} // gold mine +}; + +// x, y, z, o, rot0, rot1, rot2, rot3 +const float BG_AB_DoorPositions[2][8] = { + {1284.597f, 1281.167f, -15.97792f, 0.7068594f, 0.012957f, -0.060288f, 0.344959f, 0.93659f}, + {708.0903f, 708.4479f, -17.8342f, -2.391099f, 0.050291f, 0.015127f, 0.929217f, -0.365784f} +}; + +// Tick intervals and given points: case 0,1,2,3,4,5 captured nodes +const uint32 BG_AB_TickIntervals[6] = {0, 12000, 9000, 6000, 3000, 1000}; +const uint32 BG_AB_TickPoints[6] = {0, 10, 10, 10, 10, 30}; + +// WorldSafeLocs ids for 5 nodes, and for ally, and horde starting location +const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 896, 898, 899}; + +// x, y, z, o +const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { + {1185.71f, 1185.24f, -56.36f, 2.56f}, // stables + {990.75f, 1008.18f, -42.60f, 2.43f}, // blacksmith + {817.66f, 843.34f, -56.54f, 3.01f}, // farm + {807.46f, 1189.16f, 11.92f, 5.44f}, // lumber mill + {1146.62f, 816.94f, -98.49f, 6.14f} // gold mine +}; + +// x, y, z, o +const float BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT][4] = { + {1200.03f, 1171.09f, -56.47f, 5.15f}, // stables + {1017.43f, 960.61f, -42.95f, 4.88f}, // blacksmith + {833.00f, 793.00f, -57.25f, 5.27f}, // farm + {775.17f, 1206.40f, 15.79f, 1.90f}, // lumber mill + {1207.48f, 787.00f, -83.36f, 5.51f}, // gold mine + {1354.05f, 1275.48f, -11.30f, 4.77f}, // alliance starting base + {714.61f, 646.15f, -10.87f, 4.34f} // horde starting base +}; + +struct BG_AB_BannerTimer +{ + uint32 timer; + uint8 type; + uint8 teamIndex; +}; + +class BattleGroundABScore : public BattleGroundScore +{ + public: + BattleGroundABScore(): BasesAssaulted(0), BasesDefended(0) {}; + virtual ~BattleGroundABScore() {}; + uint32 BasesAssaulted; + uint32 BasesDefended; +}; + +class BattleGroundAB : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundAB(); + ~BattleGroundAB(); + + void Update(uint32 diff); + void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + virtual bool SetupBattleGround(); + virtual void Reset(); + void EndBattleGround(uint32 winner); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); + + /* Scorekeeping */ + virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + + virtual void FillInitialWorldStates(WorldPacket& data); + + /* Nodes occupying */ + virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj); + + /* achievement req. */ + bool IsAllNodesConrolledByTeam(uint32 team) const; // overwrited + bool IsTeamScores500Disadvantage(uint32 team) const { return m_TeamScores500Disadvantage[GetTeamIndexByTeamId(team)]; } + private: + /* Gameobject spawning/despawning */ + void _CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay); + void _DelBanner(uint8 node, uint8 type, uint8 teamIndex); + void _SendNodeUpdate(uint8 node); + + /* Creature spawning/despawning */ + // TODO: working, scripted peons spawning + void _NodeOccupied(uint8 node,Team team); + void _NodeDeOccupied(uint8 node); + + int32 _GetNodeNameId(uint8 node); + + /* Nodes info: + 0: neutral + 1: ally contested + 2: horde contested + 3: ally occupied + 4: horde occupied */ + uint8 m_Nodes[BG_AB_DYNAMIC_NODES_COUNT]; + uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; + BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; + uint32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; + uint32 m_lastTick[BG_TEAMS_COUNT]; + uint32 m_HonorScoreTics[BG_TEAMS_COUNT]; + uint32 m_ReputationScoreTics[BG_TEAMS_COUNT]; + bool m_IsInformedNearVictory; + uint32 m_HonorTics; + uint32 m_ReputationTics; + // need for achievements + bool m_TeamScores500Disadvantage[BG_TEAMS_COUNT]; +}; +#endif + diff --git a/src/server/game/BattleGrounds/BattleGroundAV.cpp b/src/server/game/BattleGrounds/BattleGroundAV.cpp new file mode 100644 index 00000000000..7f5482cbf16 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundAV.cpp @@ -0,0 +1,1490 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectMgr.h" +#include "WorldPacket.h" + +#include "BattleGround.h" +#include "BattleGroundAV.h" +#include "Formulas.h" +#include "GameObject.h" +#include "Language.h" +#include "Player.h" +#include "SpellAuras.h" + +BattleGroundAV::BattleGroundAV() +{ + m_BgObjects.resize(BG_AV_OBJECT_MAX); + m_BgCreatures.resize(AV_CPLACE_MAX+AV_STATICCPLACE_MAX); + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AV_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AV_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AV_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AV_HAS_BEGUN; +} + +BattleGroundAV::~BattleGroundAV() +{ +} + +const uint16 BattleGroundAV::GetBonusHonor(uint8 kills) //TODO: move this function to Battleground.cpp (needs to find a way to get m_MaxLevel) +{ + return Trinity::Honor::hk_honor_at_level(m_MaxLevel, kills); +} + +void BattleGroundAV::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + BattleGround::HandleKillPlayer(player, killer); + UpdateScore(player->GetTeam(),-1); +} + +void BattleGroundAV::HandleKillUnit(Creature *unit, Player *killer) +{ + sLog.outDebug("bg_av HandleKillUnit %i",unit->GetEntry()); + if (GetStatus() != STATUS_IN_PROGRESS) + return; + uint32 entry = unit->GetEntry(); + /* + uint32 triggerSpawnID = 0; + if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) + triggerSpawnID = AV_CPLACE_TRIGGER16; + else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_BOSS][0]) + triggerSpawnID = AV_CPLACE_TRIGGER17; + else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) + triggerSpawnID = AV_CPLACE_TRIGGER18; + else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_BOSS][0]) + triggerSpawnID = AV_CPLACE_TRIGGER19; + */ + if (entry == BG_AV_CreatureInfo[AV_NPC_A_BOSS][0]) + { + CastSpellOnTeam(23658,HORDE); //this is a spell which finishes a quest where a player has to kill the boss + RewardReputationToTeam(729,BG_AV_REP_BOSS,HORDE); + RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_BOSS),HORDE); + EndBattleGround(HORDE); + DelCreature(AV_CPLACE_TRIGGER17); + } + else if (entry == BG_AV_CreatureInfo[AV_NPC_H_BOSS][0]) + { + CastSpellOnTeam(23658,ALLIANCE); //this is a spell which finishes a quest where a player has to kill the boss + RewardReputationToTeam(730,BG_AV_REP_BOSS,ALLIANCE); + RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_BOSS),ALLIANCE); + EndBattleGround(ALLIANCE); + DelCreature(AV_CPLACE_TRIGGER19); + } + else if (entry == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) + { + if (!m_CaptainAlive[0]) + { + sLog.outError("Killed a Captain twice, please report this bug, if you haven't done \".respawn\""); + return; + } + m_CaptainAlive[0]=false; + RewardReputationToTeam(729,BG_AV_REP_CAPTAIN,HORDE); + RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_CAPTAIN),HORDE); + UpdateScore(ALLIANCE,(-1)*BG_AV_RES_CAPTAIN); + //spawn destroyed aura + for (uint8 i=0; i <= 9; i++) + SpawnBGObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+i,RESPAWN_IMMEDIATELY); + Creature* creature = GetBGCreature(AV_CPLACE_HERALD); + if (creature) + YellToAll(creature,GetTrinityString(LANG_BG_AV_A_CAPTAIN_DEAD),LANG_UNIVERSAL); + DelCreature(AV_CPLACE_TRIGGER16); + } + else if (entry == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) + { + if (!m_CaptainAlive[1]) + { + sLog.outError("Killed a Captain twice, please report this bug, if you haven't done \".respawn\""); + return; + } + m_CaptainAlive[1]=false; + RewardReputationToTeam(730,BG_AV_REP_CAPTAIN,ALLIANCE); + RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_CAPTAIN),ALLIANCE); + UpdateScore(HORDE,(-1)*BG_AV_RES_CAPTAIN); + //spawn destroyed aura + for (uint8 i=0; i <= 9; i++) + SpawnBGObject(BG_AV_OBJECT_BURN_BUILDING_HORDE+i,RESPAWN_IMMEDIATELY); + Creature* creature = GetBGCreature(AV_CPLACE_HERALD); + if (creature) + YellToAll(creature,GetTrinityString(LANG_BG_AV_H_CAPTAIN_DEAD),LANG_UNIVERSAL); + DelCreature(AV_CPLACE_TRIGGER18); + } + else if (entry == BG_AV_CreatureInfo[AV_NPC_N_MINE_N_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_N_MINE_A_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_N_MINE_H_4][0]) + ChangeMineOwner(AV_NORTH_MINE,killer->GetTeam()); + else if (entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_N_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_A_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_H_4][0]) + ChangeMineOwner(AV_SOUTH_MINE,killer->GetTeam()); +} + +void BattleGroundAV::HandleQuestComplete(uint32 questid, Player *player) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return;//maybe we should log this, cause this must be a cheater or a big bug + uint8 team = GetTeamIndexByTeamId(player->GetTeam()); + //TODO add reputation, events (including quest not available anymore, next quest availabe, go/npc de/spawning)and maybe honor + sLog.outDebug("BG_AV Quest %i completed",questid); + switch(questid) + { + case AV_QUEST_A_SCRAPS1: + case AV_QUEST_A_SCRAPS2: + case AV_QUEST_H_SCRAPS1: + case AV_QUEST_H_SCRAPS2: + m_Team_QuestStatus[team][0]+=20; + if (m_Team_QuestStatus[team][0] == 500 || m_Team_QuestStatus[team][0] == 1000 || m_Team_QuestStatus[team][0] == 1500) //25,50,75 turn ins + { + sLog.outDebug("BG_AV Quest %i completed starting with unit upgrading..",questid); + for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) + if (m_Nodes[i].Owner == player->GetTeam() && m_Nodes[i].State == POINT_CONTROLED) + { + DePopulateNode(i); + PopulateNode(i); + //maybe this is bad, because it will instantly respawn all creatures on every grave.. + } + } + break; + case AV_QUEST_A_COMMANDER1: + case AV_QUEST_H_COMMANDER1: + m_Team_QuestStatus[team][1]++; + RewardReputationToTeam(team,1,player->GetTeam()); + if (m_Team_QuestStatus[team][1] == 30) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + break; + case AV_QUEST_A_COMMANDER2: + case AV_QUEST_H_COMMANDER2: + m_Team_QuestStatus[team][2]++; + RewardReputationToTeam(team,1,player->GetTeam()); + if (m_Team_QuestStatus[team][2] == 60) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + break; + case AV_QUEST_A_COMMANDER3: + case AV_QUEST_H_COMMANDER3: + m_Team_QuestStatus[team][3]++; + RewardReputationToTeam(team,1,player->GetTeam()); + if (m_Team_QuestStatus[team][1] == 120) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + break; + case AV_QUEST_A_BOSS1: + case AV_QUEST_H_BOSS1: + m_Team_QuestStatus[team][4] += 9; //you can turn in 10 or 1 item.. + case AV_QUEST_A_BOSS2: + case AV_QUEST_H_BOSS2: + m_Team_QuestStatus[team][4]++; + if (m_Team_QuestStatus[team][4] >= 200) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + break; + case AV_QUEST_A_NEAR_MINE: + case AV_QUEST_H_NEAR_MINE: + m_Team_QuestStatus[team][5]++; + if (m_Team_QuestStatus[team][5] == 28) + { + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + if (m_Team_QuestStatus[team][6] == 7) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - ground assault ready",questid); + } + break; + case AV_QUEST_A_OTHER_MINE: + case AV_QUEST_H_OTHER_MINE: + m_Team_QuestStatus[team][6]++; + if (m_Team_QuestStatus[team][6] == 7) + { + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + if (m_Team_QuestStatus[team][5] == 20) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - ground assault ready",questid); + } + break; + case AV_QUEST_A_RIDER_HIDE: + case AV_QUEST_H_RIDER_HIDE: + m_Team_QuestStatus[team][7]++; + if (m_Team_QuestStatus[team][7] == 25) + { + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + if (m_Team_QuestStatus[team][8] == 25) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - rider assault ready",questid); + } + break; + case AV_QUEST_A_RIDER_TAME: + case AV_QUEST_H_RIDER_TAME: + m_Team_QuestStatus[team][8]++; + if (m_Team_QuestStatus[team][8] == 25) + { + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here",questid); + if (m_Team_QuestStatus[team][7] == 25) + sLog.outDebug("BG_AV Quest %i completed (need to implement some events here - rider assault ready",questid); + } + break; + default: + sLog.outDebug("BG_AV Quest %i completed but is not interesting at all",questid); + return; //was no interesting quest at all + break; + } +} + +void BattleGroundAV::UpdateScore(uint16 team, int16 points) +{ //note: to remove reinforcementpoints points must be negative, for adding reinforcements points must be positive + assert(team == ALLIANCE || team == HORDE); + uint8 teamindex = GetTeamIndexByTeamId(team); //0=ally 1=horde + m_Team_Scores[teamindex] += points; + + UpdateWorldState(((teamindex == BG_TEAM_HORDE)?AV_Horde_Score:AV_Alliance_Score), m_Team_Scores[teamindex]); + if (points < 0) + { + if (m_Team_Scores[teamindex] < 1) + { + m_Team_Scores[teamindex]=0; + EndBattleGround(((teamindex == BG_TEAM_HORDE)?ALLIANCE:HORDE)); + } + else if (!m_IsInformedNearVictory[teamindex] && m_Team_Scores[teamindex] < SEND_MSG_NEAR_LOSE) + { + SendMessageToAll(teamindex == BG_TEAM_HORDE?LANG_BG_AV_H_NEAR_LOSE:LANG_BG_AV_A_NEAR_LOSE, teamindex == BG_TEAM_HORDE ? CHAT_MSG_BG_SYSTEM_HORDE : CHAT_MSG_BG_SYSTEM_ALLIANCE); + PlaySoundToAll(AV_SOUND_NEAR_VICTORY); + m_IsInformedNearVictory[teamindex] = true; + } + } +} + +Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type) +{ + uint8 level; + bool isStatic = false; + Creature* creature = NULL; + assert(type <= AV_CPLACE_MAX + AV_STATICCPLACE_MAX); + if (type >= AV_CPLACE_MAX) //static + { + type -= AV_CPLACE_MAX; + cinfoid=uint16(BG_AV_StaticCreaturePos[type][4]); + creature = AddCreature(BG_AV_StaticCreatureInfo[cinfoid][0],(type+AV_CPLACE_MAX),BG_AV_StaticCreatureInfo[cinfoid][1],BG_AV_StaticCreaturePos[type][0],BG_AV_StaticCreaturePos[type][1],BG_AV_StaticCreaturePos[type][2],BG_AV_StaticCreaturePos[type][3]); + level = (BG_AV_StaticCreatureInfo[cinfoid][2] == BG_AV_StaticCreatureInfo[cinfoid][3]) ? BG_AV_StaticCreatureInfo[cinfoid][2] : urand(BG_AV_StaticCreatureInfo[cinfoid][2],BG_AV_StaticCreatureInfo[cinfoid][3]); + isStatic = true; + } + else + { + creature = AddCreature(BG_AV_CreatureInfo[cinfoid][0],type,BG_AV_CreatureInfo[cinfoid][1],BG_AV_CreaturePos[type][0],BG_AV_CreaturePos[type][1],BG_AV_CreaturePos[type][2],BG_AV_CreaturePos[type][3]); + level = (BG_AV_CreatureInfo[cinfoid][2] == BG_AV_CreatureInfo[cinfoid][3]) ? BG_AV_CreatureInfo[cinfoid][2] : urand(BG_AV_CreatureInfo[cinfoid][2],BG_AV_CreatureInfo[cinfoid][3]); + } + if (!creature) + return NULL; + if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0] || creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) + creature->SetRespawnDelay(RESPAWN_ONE_DAY); // TODO: look if this can be done by database + also add this for the wingcommanders + + if ((isStatic && cinfoid >= 10 && cinfoid <= 14) || (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid <= AV_NPC_A_GRAVEDEFENSE3) || + (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3)))) + { + if (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid <= AV_NPC_A_GRAVEDEFENSE3) + || (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3))) + { + CreatureData &data = objmgr.NewOrExistCreatureData(creature->GetDBTableGUIDLow()); + data.spawndist = 5; + } + //else spawndist will be 15, so creatures move maximum=10 + //creature->SetDefaultMovementType(RANDOM_MOTION_TYPE); + creature->GetMotionMaster()->Initialize(); + creature->setDeathState(JUST_DIED); + creature->Respawn(); + //TODO: find a way to add a motionmaster without killing the creature (i + //just copied this code from a gm-command + } + + if (level != 0) + level += m_MaxLevel - 60; //maybe we can do this more generic for custom level-range.. actually it's blizzlike + creature->SetLevel(level); + + uint32 triggerSpawnID = 0; + uint32 newFaction = 0; + if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) + { + triggerSpawnID = AV_CPLACE_TRIGGER16; + newFaction = 84; + } + else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_BOSS][0]) + { + triggerSpawnID = AV_CPLACE_TRIGGER17; + newFaction = 84; + } + else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0]) + { + triggerSpawnID = AV_CPLACE_TRIGGER18; + newFaction = 83; + } + else if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_BOSS][0]) + { + triggerSpawnID = AV_CPLACE_TRIGGER19; + newFaction = 83; + } + if (triggerSpawnID && newFaction) + { + if (Creature* trigger = AddCreature(WORLD_TRIGGER,triggerSpawnID,BG_AV_CreatureInfo[creature->GetEntry()][1],BG_AV_CreaturePos[triggerSpawnID][0],BG_AV_CreaturePos[triggerSpawnID][1],BG_AV_CreaturePos[triggerSpawnID][2],BG_AV_CreaturePos[triggerSpawnID][3])) + { + trigger->setFaction(newFaction); + trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); + } + } + + return creature; +} + +void BattleGroundAV::Update(uint32 diff) +{ + BattleGround::Update(diff); + + if (GetStatus() == STATUS_IN_PROGRESS) + { + for (uint8 i=0; i <= 1; i++)//0=alliance, 1=horde + { + if (!m_CaptainAlive[i]) + continue; + if (m_CaptainBuffTimer[i] > diff) + m_CaptainBuffTimer[i] -= diff; + else + { + if (i == 0) + { + CastSpellOnTeam(AV_BUFF_A_CAPTAIN,ALLIANCE); + Creature* creature = GetBGCreature(AV_CPLACE_MAX + 61); + if (creature) + YellToAll(creature,LANG_BG_AV_A_CAPTAIN_BUFF,LANG_COMMON); + } + else + { + CastSpellOnTeam(AV_BUFF_H_CAPTAIN,HORDE); + Creature* creature = GetBGCreature(AV_CPLACE_MAX + 59); //TODO: make the captains a dynamic creature + if (creature) + YellToAll(creature,LANG_BG_AV_H_CAPTAIN_BUFF,LANG_ORCISH); + } + m_CaptainBuffTimer[i] = 120000 + urand(0,4)* 60000; //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes TODO get the right times + } + } + //add points from mine owning, and look if he neutral team wanrts to reclaim the mine + m_Mine_Timer -=diff; + for (uint8 mine=0; mine <2; mine++) + { + if (m_Mine_Owner[mine] == ALLIANCE || m_Mine_Owner[mine] == HORDE) + { + if (m_Mine_Timer <= 0) + UpdateScore(m_Mine_Owner[mine],1); + + if (m_Mine_Reclaim_Timer[mine] > diff) + m_Mine_Reclaim_Timer[mine] -= diff; + else{ //we don't need to set this timer to 0 cause this codepart wont get called when this thing is 0 + ChangeMineOwner(mine,AV_NEUTRAL_TEAM); + } + } + } + if (m_Mine_Timer <= 0) + m_Mine_Timer=AV_MINE_TICK_TIMER; //this is at the end, cause we need to update both mines + + //looks for all timers of the nodes and destroy the building (for graveyards the building wont get destroyed, it goes just to the other team + for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i) + if (m_Nodes[i].State == POINT_ASSAULTED) //maybe remove this + { + if (m_Nodes[i].Timer > diff) + m_Nodes[i].Timer -= diff; + else + EventPlayerDestroyedPoint(i); + } + } +} + +void BattleGroundAV::StartingEventCloseDoors() +{ + DoorClose(BG_AV_OBJECT_DOOR_A); + DoorClose(BG_AV_OBJECT_DOOR_H); +} + +void BattleGroundAV::StartingEventOpenDoors() +{ + sLog.outDebug("BG_AV: start spawning mine stuff"); + for (uint16 i= BG_AV_OBJECT_MINE_SUPPLY_N_MIN; i <= BG_AV_OBJECT_MINE_SUPPLY_N_MAX; i++) + SpawnBGObject(i,RESPAWN_IMMEDIATELY); + for (uint16 i= BG_AV_OBJECT_MINE_SUPPLY_S_MIN; i <= BG_AV_OBJECT_MINE_SUPPLY_S_MAX; i++) + SpawnBGObject(i,RESPAWN_IMMEDIATELY); + for (uint8 mine = AV_NORTH_MINE; mine <= AV_SOUTH_MINE; mine++) //mine population + ChangeMineOwner(mine, AV_NEUTRAL_TEAM,true); + + UpdateWorldState(AV_SHOW_H_SCORE, 1); + UpdateWorldState(AV_SHOW_A_SCORE, 1); + + DoorOpen(BG_AV_OBJECT_DOOR_H); + DoorOpen(BG_AV_OBJECT_DOOR_A); +} + +void BattleGroundAV::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundAVScore* sc = new BattleGroundAVScore; + m_PlayerScores[plr->GetGUID()] = sc; + if (m_MaxLevel == 0) + m_MaxLevel=(plr->getLevel()%10 == 0)? plr->getLevel() : (plr->getLevel()-(plr->getLevel()%10))+10; //TODO: just look at the code \^_^/ --but queue-info should provide this information.. + +} + +void BattleGroundAV::EndBattleGround(uint32 winner) +{ + //calculate bonuskills for both teams: + //first towers: + uint8 kills[2]={0,0}; //0=ally 1=horde + uint8 rep[2]={0,0}; //0=ally 1=horde + for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) + { + if (m_Nodes[i].State == POINT_CONTROLED) + { + if (m_Nodes[i].Owner == ALLIANCE) + { + rep[0] += BG_AV_REP_SURVIVING_TOWER; + kills[0] += BG_AV_KILL_SURVIVING_TOWER; + } + else + { + rep[0] += BG_AV_KILL_SURVIVING_TOWER; + kills[1] += BG_AV_KILL_SURVIVING_TOWER; + } + } + } + + for (int i=0; i <= 1; i++) //0=ally 1=horde + { + if (m_CaptainAlive[i]) + { + kills[i] += BG_AV_KILL_SURVIVING_CAPTAIN; + rep[i] += BG_AV_REP_SURVIVING_CAPTAIN; + } + if (rep[i] != 0) + RewardReputationToTeam((i == 0)?730:729,rep[i],(i == 0)?ALLIANCE:HORDE); + if (kills[i] != 0) + RewardHonorToTeam(GetBonusHonor(kills[i]),(i == 0)?ALLIANCE:HORDE); + } + + //TODO add enterevademode for all attacking creatures + BattleGround::EndBattleGround(winner); +} + +void BattleGroundAV::RemovePlayer(Player* plr,uint64 /*guid*/) +{ + if (!plr) + { + sLog.outError("bg_AV no player at remove"); + return; + } + //TODO search more buffs + plr->RemoveAurasDueToSpell(AV_BUFF_ARMOR); + plr->RemoveAurasDueToSpell(AV_BUFF_A_CAPTAIN); + plr->RemoveAurasDueToSpell(AV_BUFF_H_CAPTAIN); +} + +void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + uint32 SpellId = 0; + switch(Trigger) + { + case 95: + case 2608: + if (Source->GetTeam() != ALLIANCE) + Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal"); + else + Source->LeaveBattleground(); + break; + case 2606: + if (Source->GetTeam() != HORDE) + Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal"); + else + Source->LeaveBattleground(); + break; + case 3326: + case 3327: + case 3328: + case 3329: + case 3330: + case 3331: + //Source->Unmount(); + break; + default: + sLog.outDebug("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); +// Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + if (SpellId) + Source->CastSpell(Source, SpellId, true); +} + +void BattleGroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +{ + + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); + if (itr == m_PlayerScores.end()) // player not found... + return; + + switch(type) + { + case SCORE_GRAVEYARDS_ASSAULTED: + ((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted += value; + break; + case SCORE_GRAVEYARDS_DEFENDED: + ((BattleGroundAVScore*)itr->second)->GraveyardsDefended += value; + break; + case SCORE_TOWERS_ASSAULTED: + ((BattleGroundAVScore*)itr->second)->TowersAssaulted += value; + break; + case SCORE_TOWERS_DEFENDED: + ((BattleGroundAVScore*)itr->second)->TowersDefended += value; + break; + case SCORE_MINES_CAPTURED: + ((BattleGroundAVScore*)itr->second)->MinesCaptured += value; + break; + case SCORE_LEADERS_KILLED: + ((BattleGroundAVScore*)itr->second)->LeadersKilled += value; + break; + case SCORE_SECONDARY_OBJECTIVES: + ((BattleGroundAVScore*)itr->second)->SecondaryObjectives += value; + break; + default: + BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); + break; + } +} + +void BattleGroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node) +{ + + uint32 object = GetObjectThroughNode(node); + sLog.outDebug("bg_av: player destroyed point node %i object %i",node,object); + + //despawn banner + SpawnBGObject(object, RESPAWN_ONE_DAY); + DestroyNode(node); + UpdateNodeWorldState(node); + + uint32 owner = m_Nodes[node].Owner; + if (IsTower(node)) + { + uint8 tmp = node-BG_AV_NODES_DUNBALDAR_SOUTH; + //despawn marshal + if (m_BgCreatures[AV_CPLACE_A_MARSHAL_SOUTH + tmp]) + DelCreature(AV_CPLACE_A_MARSHAL_SOUTH + tmp); + else + sLog.outError("BG_AV: playerdestroyedpoint: marshal %i doesn't exist",AV_CPLACE_A_MARSHAL_SOUTH + tmp); + //spawn destroyed aura + for (uint8 i=0; i <= 9; i++) + SpawnBGObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH + i + (tmp * 10),RESPAWN_IMMEDIATELY); + + UpdateScore((owner == ALLIANCE) ? HORDE : ALLIANCE, (-1)*BG_AV_RES_TOWER); + RewardReputationToTeam((owner == ALLIANCE)?730:729,BG_AV_REP_TOWER,owner); + RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_TOWER),owner); + + SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp),RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp),RESPAWN_ONE_DAY); + } + else + { + if (owner == ALLIANCE) + SpawnBGObject(object-11, RESPAWN_IMMEDIATELY); + else + SpawnBGObject(object+11, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+GetTeamIndexByTeamId(owner)+3*node,RESPAWN_IMMEDIATELY); + PopulateNode(node); + if (node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall eyecandy + { + for (uint8 i = 0; i < 4; i++) + { + SpawnBGObject(((owner == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_PA : BG_AV_OBJECT_SNOW_EYECANDY_PH)+i,RESPAWN_ONE_DAY); + SpawnBGObject(((owner == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_A : BG_AV_OBJECT_SNOW_EYECANDY_H)+i,RESPAWN_IMMEDIATELY); + } + } + } + //send a nice message to all :) + char buf[256]; + if (IsTower(node)) + sprintf(buf, GetTrinityString(LANG_BG_AV_TOWER_TAKEN) , GetNodeName(node),(owner == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); + else + sprintf(buf, GetTrinityString(LANG_BG_AV_GRAVE_TAKEN) , GetNodeName(node),(owner == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) :GetTrinityString(LANG_BG_AV_HORDE)); + + Creature* creature = GetBGCreature(AV_CPLACE_HERALD); + if (creature) + YellToAll(creature,buf,LANG_UNIVERSAL); +} + +void BattleGroundAV::ChangeMineOwner(uint8 mine, uint32 team, bool initial) +{ //mine=0 northmine mine=1 southmin +//changing the owner results in setting respawntim to infinite for current creatures, spawning new mine owners creatures and changing the chest-objects so that the current owning team can use them + assert(mine == AV_NORTH_MINE || mine == AV_SOUTH_MINE); + if (team != ALLIANCE && team != HORDE) + team = AV_NEUTRAL_TEAM; + else + PlaySoundToAll((team == ALLIANCE)?AV_SOUND_ALLIANCE_GOOD:AV_SOUND_HORDE_GOOD); + + if (m_Mine_Owner[mine] == team && !initial) + return; + m_Mine_PrevOwner[mine] = m_Mine_Owner[mine]; + m_Mine_Owner[mine] = team; + + if (!initial) + { + sLog.outDebug("bg_av depopulating mine %i (0=north,1=south)",mine); + if (mine == AV_SOUTH_MINE) + for (uint16 i=AV_CPLACE_MINE_S_S_MIN; i <= AV_CPLACE_MINE_S_S_MAX; i++) + if (m_BgCreatures[i]) + DelCreature(i); //TODO just set the respawntime to 999999 + for (uint16 i=((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_1_MIN:AV_CPLACE_MINE_S_1_MIN); i <= ((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_3:AV_CPLACE_MINE_S_3); i++) + if (m_BgCreatures[i]) + DelCreature(i); //TODO here also + } + SendMineWorldStates(mine); + + sLog.outDebug("bg_av populating mine %i (0=north,1=south)",mine); + uint16 miner; + //also neutral team exists.. after a big time, the neutral team tries to conquer the mine + if (mine == AV_NORTH_MINE) + { + if (team == ALLIANCE) + miner = AV_NPC_N_MINE_A_1; + else if (team == HORDE) + miner = AV_NPC_N_MINE_H_1; + else + miner = AV_NPC_N_MINE_N_1; + } + else + { + uint16 cinfo; + if (team == ALLIANCE) + miner = AV_NPC_S_MINE_A_1; + else if (team == HORDE) + miner = AV_NPC_S_MINE_H_1; + else + miner = AV_NPC_S_MINE_N_1; + //vermin + sLog.outDebug("spawning vermin"); + if (team == ALLIANCE) + cinfo = AV_NPC_S_MINE_A_3; + else if (team == HORDE) + cinfo = AV_NPC_S_MINE_H_3; + else + cinfo = AV_NPC_S_MINE_N_S; + for (uint16 i=AV_CPLACE_MINE_S_S_MIN; i <= AV_CPLACE_MINE_S_S_MAX; i++) + AddAVCreature(cinfo,i); + } + for (uint16 i=((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_1_MIN:AV_CPLACE_MINE_S_1_MIN); i <= ((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_1_MAX:AV_CPLACE_MINE_S_1_MAX); i++) + AddAVCreature(miner,i); + //the next chooses randomly between 2 cretures + for (uint16 i=((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_2_MIN:AV_CPLACE_MINE_S_2_MIN); i <= ((mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_2_MAX:AV_CPLACE_MINE_S_2_MAX); i++) + AddAVCreature(miner+(urand(1,2)),i); + AddAVCreature(miner+3,(mine == AV_NORTH_MINE)?AV_CPLACE_MINE_N_3:AV_CPLACE_MINE_S_3); + //because the gameobjects in this mine have changed, update all surrounding players: +// for (uint16 i = ((mine == AV_NORTH_MINE)?BG_AV_OBJECT_MINE_SUPPLY_N_MIN:BG_AV_OBJECT_MINE_SUPPLY_N_MIN); i <= ((mine == AV_NORTH_MINE)?BG_AV_OBJECT_MINE_SUPPLY_N_MAX:BG_AV_OBJECT_MINE_SUPPLY_N_MAX); i++) +// { + //TODO: add gameobject-update code +// } + if (team == ALLIANCE || team == HORDE) + { + m_Mine_Reclaim_Timer[mine]=AV_MINE_RECLAIM_TIMER; + char buf[256]; + sprintf(buf, GetTrinityString(LANG_BG_AV_MINE_TAKEN), GetTrinityString((mine == AV_NORTH_MINE) ? LANG_BG_AV_MINE_NORTH : LANG_BG_AV_MINE_SOUTH), (team == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); + Creature* creature = GetBGCreature(AV_CPLACE_HERALD); + if (creature) + YellToAll(creature,buf,LANG_UNIVERSAL); + } + else + { + if (mine == AV_SOUTH_MINE) //i think this gets called all the time + { + Creature* creature = GetBGCreature(AV_CPLACE_MINE_S_3); + YellToAll(creature,LANG_BG_AV_S_MINE_BOSS_CLAIMS,LANG_UNIVERSAL); + } + } + return; +} + +bool BattleGroundAV::PlayerCanDoMineQuest(int32 GOId,uint32 team) +{ + if (GOId == BG_AV_OBJECTID_MINE_N) + return (m_Mine_Owner[AV_NORTH_MINE] == team); + if (GOId == BG_AV_OBJECTID_MINE_S) + return (m_Mine_Owner[AV_SOUTH_MINE] == team); + return true; //cause it's no mine'object it is ok if this is true +} + +void BattleGroundAV::PopulateNode(BG_AV_Nodes node) +{ + uint32 owner = m_Nodes[node].Owner; + assert(owner); + + uint32 c_place = AV_CPLACE_DEFENSE_STORM_AID + (4 * node); + uint32 creatureid; + if (IsTower(node)) + creatureid=(owner == ALLIANCE)?AV_NPC_A_TOWERDEFENSE:AV_NPC_H_TOWERDEFENSE; + else + { + uint8 team2 = GetTeamIndexByTeamId(owner); + if (m_Team_QuestStatus[team2][0] < 500) + creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE0 : AV_NPC_H_GRAVEDEFENSE0; + else if (m_Team_QuestStatus[team2][0] < 1000) + creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE1 : AV_NPC_H_GRAVEDEFENSE1; + else if (m_Team_QuestStatus[team2][0] < 1500) + creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE2 : AV_NPC_H_GRAVEDEFENSE2; + else + creatureid = (owner == ALLIANCE)? AV_NPC_A_GRAVEDEFENSE3 : AV_NPC_H_GRAVEDEFENSE3; + //spiritguide + if (m_BgCreatures[node]) + DelCreature(node); + if (!AddSpiritGuide(node, BG_AV_CreaturePos[node][0], BG_AV_CreaturePos[node][1], BG_AV_CreaturePos[node][2], BG_AV_CreaturePos[node][3], owner)) + sLog.outError("AV: couldn't spawn spiritguide at node %i",node); + + } + for (uint8 i=0; i<4; i++) + AddAVCreature(creatureid,c_place+i); + + if (node >= BG_AV_NODES_MAX)//fail safe + return; + Creature* trigger = GetBGCreature(node + 302);//0-302 other creatures + if (!trigger) + trigger = AddCreature(WORLD_TRIGGER,node + 302,owner,BG_AV_CreaturePos[node + 302][0],BG_AV_CreaturePos[node + 302][1],BG_AV_CreaturePos[node + 302][2],BG_AV_CreaturePos[node + 302][3]); + + //add bonus honor aura trigger creature when node is accupied + //cast bonus aura (+50% honor in 25yards) + //aura should only apply to players who have accupied the node, set correct faction for trigger + if (trigger) + { + if (owner != ALLIANCE && owner != HORDE)//node can be neutral, remove trigger + { + DelCreature(node + 302); + return; + } + trigger->setFaction(owner == ALLIANCE ? 84 : 83); + trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); + } +} +void BattleGroundAV::DePopulateNode(BG_AV_Nodes node) +{ + uint32 c_place = AV_CPLACE_DEFENSE_STORM_AID + (4 * node); + for (uint8 i=0; i<4; i++) + if (m_BgCreatures[c_place+i]) + DelCreature(c_place+i); + //spiritguide + if (!IsTower(node) && m_BgCreatures[node]) + DelCreature(node); + + //remove bonus honor aura trigger creature when node is lost + if(node < BG_AV_NODES_MAX)//fail safe + DelCreature(node + 302);//NULL checks are in DelCreature! 0-302 spirit guides +} + +const BG_AV_Nodes BattleGroundAV::GetNodeThroughObject(uint32 object) +{ + sLog.outDebug("bg_AV getnodethroughobject %i",object); + if (object <= BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER) + return BG_AV_Nodes(object); + if (object <= BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_HUT) + return BG_AV_Nodes(object - 11); + if (object <= BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_WTOWER) + return BG_AV_Nodes(object - 7); + if (object <= BG_AV_OBJECT_FLAG_C_H_STONEHEART_BUNKER) + return BG_AV_Nodes(object -22); + if (object <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT) + return BG_AV_Nodes(object - 33); + if (object <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER) + return BG_AV_Nodes(object - 29); + if (object == BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE) + return BG_AV_NODES_SNOWFALL_GRAVE; + sLog.outError("BattleGroundAV: ERROR! GetPlace got a wrong object :("); + assert(false); + return BG_AV_Nodes(0); +} + +const uint32 BattleGroundAV::GetObjectThroughNode(BG_AV_Nodes node) +{ //this function is the counterpart to GetNodeThroughObject() + sLog.outDebug("bg_AV GetObjectThroughNode %i",node); + if (m_Nodes[node].Owner == ALLIANCE) + { + if (m_Nodes[node].State == POINT_ASSAULTED) + { + if (node <= BG_AV_NODES_FROSTWOLF_HUT) + return node+11; + if (node >= BG_AV_NODES_ICEBLOOD_TOWER && node <= BG_AV_NODES_FROSTWOLF_WTOWER) + return node+7; + } + else if (m_Nodes[node].State == POINT_CONTROLED) + if (node <= BG_AV_NODES_STONEHEART_BUNKER) + return node; + } + else if (m_Nodes[node].Owner == HORDE) + { + if (m_Nodes[node].State == POINT_ASSAULTED) + if (node <= BG_AV_NODES_STONEHEART_BUNKER) + return node+22; + else if (m_Nodes[node].State == POINT_CONTROLED) + { + if (node <= BG_AV_NODES_FROSTWOLF_HUT) + return node+33; + if (node >= BG_AV_NODES_ICEBLOOD_TOWER && node <= BG_AV_NODES_FROSTWOLF_WTOWER) + return node+29; + } + } + else if (m_Nodes[node].Owner == AV_NEUTRAL_TEAM) + return BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; + sLog.outError("BattleGroundAV: Error! GetPlaceNode couldn't resolve node %i",node); + assert(false); + return 0; +} + +//called when using banner + +void BattleGroundAV::EventPlayerClickedOnFlag(Player *source, GameObject* target_obj) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + int32 object = GetObjectType(target_obj->GetGUID()); + sLog.outDebug("BG_AV using gameobject %i with type %i",target_obj->GetEntry(),object); + if (object < 0) + return; + switch(target_obj->GetEntry()) + { + case BG_AV_OBJECTID_BANNER_A: + case BG_AV_OBJECTID_BANNER_A_B: + case BG_AV_OBJECTID_BANNER_H: + case BG_AV_OBJECTID_BANNER_H_B: + case BG_AV_OBJECTID_BANNER_SNOWFALL_N: + EventPlayerAssaultsPoint(source, object); + break; + case BG_AV_OBJECTID_BANNER_CONT_A: + case BG_AV_OBJECTID_BANNER_CONT_A_B: + case BG_AV_OBJECTID_BANNER_CONT_H: + case BG_AV_OBJECTID_BANNER_CONT_H_B: + EventPlayerDefendsPoint(source, object); + break; + default: + break; + } +} + +void BattleGroundAV::EventPlayerDefendsPoint(Player* player, uint32 object) +{ + assert(GetStatus() == STATUS_IN_PROGRESS); + BG_AV_Nodes node = GetNodeThroughObject(object); + + uint32 owner = m_Nodes[node].Owner; //maybe should name it prevowner + uint32 team = player->GetTeam(); + + if (owner == player->GetTeam() || m_Nodes[node].State != POINT_ASSAULTED) + return; + if (m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM) + { //until snowfall doesn't belong to anyone it is better handled in assault-code + assert(node == BG_AV_NODES_SNOWFALL_GRAVE); //currently the only neutral grave + EventPlayerAssaultsPoint(player,object); + return; + } + sLog.outDebug("player defends point object: %i node: %i",object,node); + if (m_Nodes[node].PrevOwner != team) + { + sLog.outError("BG_AV: player defends point which doesn't belong to his team %i",node); + return; + } + + //spawn new go :) + if (m_Nodes[node].Owner == ALLIANCE) + SpawnBGObject(object+22, RESPAWN_IMMEDIATELY); //spawn horde banner + else + SpawnBGObject(object-22, RESPAWN_IMMEDIATELY); //spawn alliance banner + + if (!IsTower(node)) + { + SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+GetTeamIndexByTeamId(team)+3*node,RESPAWN_IMMEDIATELY); + } + // despawn old go + SpawnBGObject(object, RESPAWN_ONE_DAY); + + DefendNode(node,team); + PopulateNode(node); + UpdateNodeWorldState(node); + + if (IsTower(node)) + { + //spawn big flag+aura on top of tower + SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + } + else if (node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall eyecandy + { + for (uint8 i = 0; i < 4; i++) + { + SpawnBGObject(((owner == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_PA : BG_AV_OBJECT_SNOW_EYECANDY_PH)+i,RESPAWN_ONE_DAY); + SpawnBGObject(((team == ALLIANCE)?BG_AV_OBJECT_SNOW_EYECANDY_A : BG_AV_OBJECT_SNOW_EYECANDY_H)+i,RESPAWN_IMMEDIATELY); + } + } + //send a nice message to all :) + char buf[256]; + sprintf(buf, GetTrinityString((IsTower(node)) ? LANG_BG_AV_TOWER_DEFENDED : LANG_BG_AV_GRAVE_DEFENDED), GetNodeName(node),(team == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); + Creature* creature = GetBGCreature(AV_CPLACE_HERALD); + if (creature) + YellToAll(creature,buf,LANG_UNIVERSAL); + //update the statistic for the defending player + UpdatePlayerScore(player, (IsTower(node)) ? SCORE_TOWERS_DEFENDED : SCORE_GRAVEYARDS_DEFENDED, 1); + if (IsTower(node)) + PlaySoundToAll(AV_SOUND_BOTH_TOWER_DEFEND); + else + PlaySoundToAll((team == ALLIANCE)?AV_SOUND_ALLIANCE_GOOD:AV_SOUND_HORDE_GOOD); +} + +void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object) +{ + assert(GetStatus() == STATUS_IN_PROGRESS); + + BG_AV_Nodes node = GetNodeThroughObject(object); + uint32 owner = m_Nodes[node].Owner; //maybe name it prevowner + uint32 team = player->GetTeam(); + sLog.outDebug("bg_av: player assaults point object %i node %i",object,node); + if (owner == team || team == m_Nodes[node].TotalOwner) + return; //surely a gm used this object + + if (node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall is a bit special in capping + it gets eyecandy stuff + { + if (object == BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE) //initial capping + { + assert(owner == AV_NEUTRAL_TEAM && m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM); + if (team == ALLIANCE) + SpawnBGObject(BG_AV_OBJECT_FLAG_C_A_SNOWFALL_GRAVE, RESPAWN_IMMEDIATELY); + else + SpawnBGObject(BG_AV_OBJECT_FLAG_C_H_SNOWFALL_GRAVE, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_IMMEDIATELY); //neutral aura spawn + } + else if (m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM) //recapping, when no team owns this node realy + { + assert(m_Nodes[node].State != POINT_CONTROLED); + if (team == ALLIANCE) + SpawnBGObject(object-11, RESPAWN_IMMEDIATELY); + else + SpawnBGObject(object+11, RESPAWN_IMMEDIATELY); + } + //eyecandy + uint32 spawn,despawn; + if (team == ALLIANCE) + { + despawn = (m_Nodes[node].State == POINT_ASSAULTED)?BG_AV_OBJECT_SNOW_EYECANDY_PH : BG_AV_OBJECT_SNOW_EYECANDY_H; + spawn = BG_AV_OBJECT_SNOW_EYECANDY_PA; + } + else + { + despawn = (m_Nodes[node].State == POINT_ASSAULTED)?BG_AV_OBJECT_SNOW_EYECANDY_PA : BG_AV_OBJECT_SNOW_EYECANDY_A; + spawn = BG_AV_OBJECT_SNOW_EYECANDY_PH; + } + for (uint8 i = 0; i < 4; i++) + { + SpawnBGObject(despawn+i,RESPAWN_ONE_DAY); + SpawnBGObject(spawn+i,RESPAWN_IMMEDIATELY); + } + } + + //if snowfall gots capped it can be handled like all other graveyards + if (m_Nodes[node].TotalOwner != AV_NEUTRAL_TEAM) + { + assert(m_Nodes[node].Owner != AV_NEUTRAL_TEAM); + if (team == ALLIANCE) + SpawnBGObject(object-22, RESPAWN_IMMEDIATELY); + else + SpawnBGObject(object+22, RESPAWN_IMMEDIATELY); + if (IsTower(node)) + { //spawning/despawning of bigflag+aura + SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == ALLIANCE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + SpawnBGObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(node-BG_AV_NODES_DUNBALDAR_SOUTH)),(team == HORDE)? RESPAWN_IMMEDIATELY : RESPAWN_ONE_DAY); + } + else + { + //spawning/despawning of aura + SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_IMMEDIATELY); //neutral aura spawn + SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+GetTeamIndexByTeamId(owner)+3*node,RESPAWN_ONE_DAY); //teeamaura despawn + // Those who are waiting to resurrect at this object are taken to the closest own object's graveyard + std::vector ghost_list = m_ReviveQueue[m_BgCreatures[node]]; + if (!ghost_list.empty()) + { + Player *plr; + WorldSafeLocsEntry const *ClosestGrave = NULL; + for (std::vector::iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) + { + plr = objmgr.GetPlayer(*ghost_list.begin()); + if (!plr) + continue; + if (!ClosestGrave) + ClosestGrave = GetClosestGraveYard(plr); + else + plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); + } + m_ReviveQueue[m_BgCreatures[node]].clear(); + } + } + DePopulateNode(node); + } + + SpawnBGObject(object, RESPAWN_ONE_DAY); //delete old banner + AssaultNode(node,team); + UpdateNodeWorldState(node); + + //send a nice message to all :) + char buf[256]; + sprintf(buf, (IsTower(node)) ? GetTrinityString(LANG_BG_AV_TOWER_ASSAULTED) : GetTrinityString(LANG_BG_AV_GRAVE_ASSAULTED), GetNodeName(node), (team == ALLIANCE) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE)); + Creature* creature = GetBGCreature(AV_CPLACE_HERALD); + if (creature) + YellToAll(creature,buf,LANG_UNIVERSAL); + //update the statistic for the assaulting player + UpdatePlayerScore(player, (IsTower(node)) ? SCORE_TOWERS_ASSAULTED : SCORE_GRAVEYARDS_ASSAULTED, 1); + PlaySoundToAll((team == ALLIANCE)?AV_SOUND_ALLIANCE_ASSAULTS:AV_SOUND_HORDE_ASSAULTS); +} + +void BattleGroundAV::FillInitialWorldStates(WorldPacket& data) +{ + bool stateok; + //graveyards + for (uint8 i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; i++) + { + for (uint8 j =1; j <= 3; j+=2) + {//j=1=assaulted j=3=controled + stateok = (m_Nodes[i].State == j); + data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,ALLIANCE)]) << uint32((m_Nodes[i].Owner == ALLIANCE && stateok)?1:0); + data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,HORDE)]) << uint32((m_Nodes[i].Owner == HORDE && stateok)?1:0); + } + } + + //towers + for (uint8 i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_MAX; i++) + for (uint8 j =1; j <= 3; j+=2) + {//j=1=assaulted j=3=controled //i dont have j=2=destroyed cause destroyed is the same like enemy-team controll + stateok = (m_Nodes[i].State == j || (m_Nodes[i].State == POINT_DESTROYED && j == 3)); + data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,ALLIANCE)]) << uint32((m_Nodes[i].Owner == ALLIANCE && stateok)?1:0); + data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,HORDE)]) << uint32((m_Nodes[i].Owner == HORDE && stateok)?1:0); + } + if (m_Nodes[BG_AV_NODES_SNOWFALL_GRAVE].Owner == AV_NEUTRAL_TEAM) //cause neutral teams aren't handled generic + data << uint32(AV_SNOWFALL_N) << uint32(1); + data << uint32(AV_Alliance_Score) << uint32(m_Team_Scores[0]); + data << uint32(AV_Horde_Score) << uint32(m_Team_Scores[1]); + if (GetStatus() == STATUS_IN_PROGRESS){ //only if game started the teamscores are displayed + data << uint32(AV_SHOW_A_SCORE) << uint32(1); + data << uint32(AV_SHOW_H_SCORE) << uint32(1); + } + else + { + data << uint32(AV_SHOW_A_SCORE) << uint32(0); + data << uint32(AV_SHOW_H_SCORE) << uint32(0); + } + SendMineWorldStates(AV_NORTH_MINE); + SendMineWorldStates(AV_SOUTH_MINE); +} + +const uint8 BattleGroundAV::GetWorldStateType(uint8 state, uint16 team) //this is used for node worldstates and returns values which fit good into the worldstatesarray +{ + //neutral stuff cant get handled (currently its only snowfall) + assert(team != AV_NEUTRAL_TEAM); + //a_c a_a h_c h_a the positions in worldstate-array + if (team == ALLIANCE) + { + if (state == POINT_CONTROLED || state == POINT_DESTROYED) + return 0; + if (state == POINT_ASSAULTED) + return 1; + } + if (team == HORDE) + { + if (state == POINT_DESTROYED || state == POINT_CONTROLED) + return 2; + if (state == POINT_ASSAULTED) + return 3; + } + sLog.outError("BG_AV: should update a strange worldstate state:%i team:%i",state,team); + return 5; //this will crash the game, but i want to know if something is wrong here +} + +void BattleGroundAV::UpdateNodeWorldState(BG_AV_Nodes node) +{ + UpdateWorldState(BG_AV_NodeWorldStates[node][GetWorldStateType(m_Nodes[node].State,m_Nodes[node].Owner)],1); + if (m_Nodes[node].PrevOwner == AV_NEUTRAL_TEAM) //currently only snowfall is supported as neutral node (i don't want to make an extra row (neutral states) in worldstatesarray just for one node + UpdateWorldState(AV_SNOWFALL_N,0); + else + UpdateWorldState(BG_AV_NodeWorldStates[node][GetWorldStateType(m_Nodes[node].PrevState,m_Nodes[node].PrevOwner)],0); +} + +void BattleGroundAV::SendMineWorldStates(uint32 mine) +{ + assert(mine == AV_NORTH_MINE || mine == AV_SOUTH_MINE); +// currently i'm sure, that this works (: +// assert(m_Mine_PrevOwner[mine] == ALLIANCE || m_Mine_PrevOwner[mine] == HORDE || m_Mine_PrevOwner[mine] == AV_NEUTRAL_TEAM); +// assert(m_Mine_Owner[mine] == ALLIANCE || m_Mine_Owner[mine] == HORDE || m_Mine_Owner[mine] == AV_NEUTRAL_TEAM); + + uint8 owner,prevowner,mine2; //those variables are needed to access the right worldstate in the BG_AV_MineWorldStates array + mine2 = (mine == AV_NORTH_MINE)?0:1; + if (m_Mine_PrevOwner[mine] == ALLIANCE) + prevowner = 0; + else if (m_Mine_PrevOwner[mine] == HORDE) + prevowner = 2; + else + prevowner = 1; + if (m_Mine_Owner[mine] == ALLIANCE) + owner = 0; + else if (m_Mine_Owner[mine] == HORDE) + owner = 2; + else + owner = 1; + + UpdateWorldState(BG_AV_MineWorldStates[mine2][owner],1); + if (prevowner != owner) + UpdateWorldState(BG_AV_MineWorldStates[mine2][prevowner],0); +} + +WorldSafeLocsEntry const* BattleGroundAV::GetClosestGraveYard(Player* player) +{ + WorldSafeLocsEntry const* pGraveyard = NULL; + WorldSafeLocsEntry const* entry = NULL; + float dist = 0; + float minDist = 0; + float x, y; + + player->GetPosition(x, y); + + pGraveyard = sWorldSafeLocsStore.LookupEntry(BG_AV_GraveyardIds[GetTeamIndexByTeamId(player->GetTeam())+7]); + minDist = (pGraveyard->x - x)*(pGraveyard->x - x)+(pGraveyard->y - y)*(pGraveyard->y - y); + + for (uint8 i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) + if (m_Nodes[i].Owner == player->GetTeam() && m_Nodes[i].State == POINT_CONTROLED) + { + entry = sWorldSafeLocsStore.LookupEntry(BG_AV_GraveyardIds[i]); + if (entry) + { + dist = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y); + if (dist < minDist) + { + minDist = dist; + pGraveyard = entry; + } + } + } + return pGraveyard; +} + +bool BattleGroundAV::SetupBattleGround() +{ + // Create starting objects + if ( + // alliance gates + !AddObject(BG_AV_OBJECT_DOOR_A, BG_AV_OBJECTID_GATE_A, BG_AV_DoorPositons[0][0],BG_AV_DoorPositons[0][1],BG_AV_DoorPositons[0][2],BG_AV_DoorPositons[0][3],0,0,sin(BG_AV_DoorPositons[0][3]/2),cos(BG_AV_DoorPositons[0][3]/2),RESPAWN_IMMEDIATELY) + // horde gates + || !AddObject(BG_AV_OBJECT_DOOR_H, BG_AV_OBJECTID_GATE_H, BG_AV_DoorPositons[1][0],BG_AV_DoorPositons[1][1],BG_AV_DoorPositons[1][2],BG_AV_DoorPositons[1][3],0,0,sin(BG_AV_DoorPositons[1][3]/2),cos(BG_AV_DoorPositons[1][3]/2),RESPAWN_IMMEDIATELY)) + { + sLog.outErrorDb("BatteGroundAV: Failed to spawn some object BattleGround not created!1"); + return false; + } + + //spawn node-objects + for (uint8 i = BG_AV_NODES_FIRSTAID_STATION ; i < BG_AV_NODES_MAX; ++i) + { + if (i <= BG_AV_NODES_FROSTWOLF_HUT) + { + if (!AddObject(i,BG_AV_OBJECTID_BANNER_A_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(i+11,BG_AV_OBJECTID_BANNER_CONT_A_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(i+33,BG_AV_OBJECTID_BANNER_H_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(i+22,BG_AV_OBJECTID_BANNER_CONT_H_B,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + //aura + || !AddObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+i*3,BG_AV_OBJECTID_AURA_N,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+i*3,BG_AV_OBJECTID_AURA_A,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+i*3,BG_AV_OBJECTID_AURA_H,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!2"); + return false; + } + } + else //towers + { + if (i <= BG_AV_NODES_STONEHEART_BUNKER) //alliance towers + { + if (!AddObject(i,BG_AV_OBJECTID_BANNER_A,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(i+22,BG_AV_OBJECTID_BANNER_CONT_H,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_A,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_N,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_A,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_PH,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!3"); + return false; + } + } + else //horde towers + { + if (!AddObject(i+7,BG_AV_OBJECTID_BANNER_CONT_A,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(i+29,BG_AV_OBJECTID_BANNER_H,BG_AV_ObjectPos[i][0],BG_AV_ObjectPos[i][1],BG_AV_ObjectPos[i][2],BG_AV_ObjectPos[i][3], 0, 0, sin(BG_AV_ObjectPos[i][3]/2), cos(BG_AV_ObjectPos[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_N,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_AURA_H,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_PA,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)),BG_AV_OBJECTID_TOWER_BANNER_H,BG_AV_ObjectPos[i+8][0],BG_AV_ObjectPos[i+8][1],BG_AV_ObjectPos[i+8][2],BG_AV_ObjectPos[i+8][3], 0, 0, sin(BG_AV_ObjectPos[i+8][3]/2), cos(BG_AV_ObjectPos[i+8][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!4"); + return false; + } + } + for (uint8 j=0; j <= 9; j++) //burning aura + { + if (!AddObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j,BG_AV_OBJECTID_FIRE,BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][0],BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][1],BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][2],BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!5.%i",i); + return false; + } + } + } + } + for (uint8 i=0; i<2; i++) //burning aura for buildings + { + for (uint8 j=0; j <= 9; j++) + { + if (j<5) + { + if (!AddObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+(i*10)+j,BG_AV_OBJECTID_SMOKE,BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][0],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][1],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][2],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!6.%i",i); + return false; + } + } + else + { + if (!AddObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+(i*10)+j,BG_AV_OBJECTID_FIRE,BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][0],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][1],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][2],BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!7.%i",i); + return false; + } + } + } + } + for (uint16 i= 0; i <= (BG_AV_OBJECT_MINE_SUPPLY_N_MAX-BG_AV_OBJECT_MINE_SUPPLY_N_MIN); i++) + { + if (!AddObject(BG_AV_OBJECT_MINE_SUPPLY_N_MIN+i,BG_AV_OBJECTID_MINE_N,BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][0],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][1],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][2],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some mine supplies BattleGround not created!7.5.%i",i); + return false; + } + } + for (uint16 i= 0 ; i <= (BG_AV_OBJECT_MINE_SUPPLY_S_MAX-BG_AV_OBJECT_MINE_SUPPLY_S_MIN); i++) + { + if (!AddObject(BG_AV_OBJECT_MINE_SUPPLY_S_MIN+i,BG_AV_OBJECTID_MINE_S,BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][0],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][1],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][2],BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3], 0, 0, sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3]/2),RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some mine supplies BattleGround not created!7.6.%i",i); + return false; + } + } + + if (!AddObject(BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE, BG_AV_OBJECTID_BANNER_SNOWFALL_N ,BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][0],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][1],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][2],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3],0,0,sin(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), cos(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!8"); + return false; + } + for (uint8 i = 0; i < 4; i++) + { + if (!AddObject(BG_AV_OBJECT_SNOW_EYECANDY_A+i, BG_AV_OBJECTID_SNOWFALL_CANDY_A ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_PA+i, BG_AV_OBJECTID_SNOWFALL_CANDY_PA ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_H+i, BG_AV_OBJECTID_SNOWFALL_CANDY_H ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_PH+i, BG_AV_OBJECTID_SNOWFALL_CANDY_PH ,BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2],BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3],0,0,sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY)) + { + sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!9.%i",i); + return false; + } + } + + uint16 i; + sLog.outDebug("Alterac Valley: entering state STATUS_WAIT_JOIN ..."); + // Initial Nodes + for (i = 0; i < BG_AV_OBJECT_MAX; i++) + SpawnBGObject(i, RESPAWN_ONE_DAY); + for (i = BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE ; i++){ + SpawnBGObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+3*i,RESPAWN_IMMEDIATELY); + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + } + for (i = BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER ; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + for (i = BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE; i <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER ; i++){ + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + if (i <= BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT) + SpawnBGObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+3*GetNodeThroughObject(i),RESPAWN_IMMEDIATELY); + } + for (i = BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH; i <= BG_AV_OBJECT_TFLAG_A_STONEHEART_BUNKER; i+=2) + { + SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag + SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura + } + for (i = BG_AV_OBJECT_TFLAG_H_ICEBLOOD_TOWER; i <= BG_AV_OBJECT_TFLAG_H_FROSTWOLF_WTOWER; i+=2) + { + SpawnBGObject(i, RESPAWN_IMMEDIATELY); //flag + SpawnBGObject(i+16, RESPAWN_IMMEDIATELY); //aura + } + //snowfall and the doors + for (i = BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; i <= BG_AV_OBJECT_DOOR_A; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE,RESPAWN_IMMEDIATELY); + + //creatures + sLog.outDebug("BG_AV start poputlating nodes"); + for (BG_AV_Nodes i= BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i) + { + if (m_Nodes[i].Owner) + PopulateNode(i); + } + //all creatures which don't get despawned through the script are static + sLog.outDebug("BG_AV: start spawning static creatures"); + for (i=0; i < AV_STATICCPLACE_MAX; i++) + AddAVCreature(0,i+AV_CPLACE_MAX); + //mainspiritguides: + sLog.outDebug("BG_AV: start spawning spiritguides creatures"); + AddSpiritGuide(7, BG_AV_CreaturePos[7][0], BG_AV_CreaturePos[7][1], BG_AV_CreaturePos[7][2], BG_AV_CreaturePos[7][3], ALLIANCE); + AddSpiritGuide(8, BG_AV_CreaturePos[8][0], BG_AV_CreaturePos[8][1], BG_AV_CreaturePos[8][2], BG_AV_CreaturePos[8][3], HORDE); + //spawn the marshals (those who get deleted, if a tower gets destroyed) + sLog.outDebug("BG_AV: start spawning marshal creatures"); + for (i=AV_NPC_A_MARSHAL_SOUTH; i <= AV_NPC_H_MARSHAL_WTOWER; i++) + AddAVCreature(i,AV_CPLACE_A_MARSHAL_SOUTH+(i-AV_NPC_A_MARSHAL_SOUTH)); + AddAVCreature(AV_NPC_HERALD,AV_CPLACE_HERALD); + return true; +} + +const char* BattleGroundAV::GetNodeName(BG_AV_Nodes node) +{ + switch (node) + { + case BG_AV_NODES_FIRSTAID_STATION: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_STORM_AID); + case BG_AV_NODES_DUNBALDAR_SOUTH: return GetTrinityString(LANG_BG_AV_NODE_TOWER_DUN_S); + case BG_AV_NODES_DUNBALDAR_NORTH: return GetTrinityString(LANG_BG_AV_NODE_TOWER_DUN_N); + case BG_AV_NODES_STORMPIKE_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_STORMPIKE); + case BG_AV_NODES_ICEWING_BUNKER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_ICEWING); + case BG_AV_NODES_STONEHEART_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_STONE); + case BG_AV_NODES_STONEHEART_BUNKER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_STONE); + case BG_AV_NODES_SNOWFALL_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_SNOW); + case BG_AV_NODES_ICEBLOOD_TOWER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_ICE); + case BG_AV_NODES_ICEBLOOD_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_ICE); + case BG_AV_NODES_TOWER_POINT: return GetTrinityString(LANG_BG_AV_NODE_TOWER_POINT); + case BG_AV_NODES_FROSTWOLF_GRAVE: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_FROST); + case BG_AV_NODES_FROSTWOLF_ETOWER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_FROST_E); + case BG_AV_NODES_FROSTWOLF_WTOWER: return GetTrinityString(LANG_BG_AV_NODE_TOWER_FROST_W); + case BG_AV_NODES_FROSTWOLF_HUT: return GetTrinityString(LANG_BG_AV_NODE_GRAVE_FROST_HUT); + default: + { + sLog.outError("tried to get name for node %u%",node); + return "Unknown"; + break; + } + } +} + +void BattleGroundAV::AssaultNode(BG_AV_Nodes node, uint16 team) +{ + if (m_Nodes[node].TotalOwner == team) + { + sLog.outCrash("Assaulting team is TotalOwner of node"); + assert (false); + } + if (m_Nodes[node].Owner == team) + { + sLog.outCrash("Assaulting team is owner of node"); + assert (false); + } + if (m_Nodes[node].State == POINT_DESTROYED) + { + sLog.outCrash("Destroyed node is being assaulted"); + assert (false); + } + if (m_Nodes[node].State == POINT_ASSAULTED && m_Nodes[node].TotalOwner) //only assault an assaulted node if no totalowner exists + { + sLog.outCrash("Assault on an not assaulted node with total owner"); + assert (false); + } + //the timer gets another time, if the previous owner was 0 == Neutral + m_Nodes[node].Timer = (m_Nodes[node].PrevOwner)? BG_AV_CAPTIME : BG_AV_SNOWFALL_FIRSTCAP; + m_Nodes[node].PrevOwner = m_Nodes[node].Owner; + m_Nodes[node].Owner = team; + m_Nodes[node].PrevState = m_Nodes[node].State; + m_Nodes[node].State = POINT_ASSAULTED; +} + +void BattleGroundAV::DestroyNode(BG_AV_Nodes node) +{ + assert(m_Nodes[node].State == POINT_ASSAULTED); + + m_Nodes[node].TotalOwner = m_Nodes[node].Owner; + m_Nodes[node].PrevOwner = m_Nodes[node].Owner; + m_Nodes[node].PrevState = m_Nodes[node].State; + m_Nodes[node].State = (m_Nodes[node].Tower)? POINT_DESTROYED : POINT_CONTROLED; + m_Nodes[node].Timer = 0; +} + +void BattleGroundAV::InitNode(BG_AV_Nodes node, uint16 team, bool tower) +{ + m_Nodes[node].TotalOwner = team; + m_Nodes[node].Owner = team; + m_Nodes[node].PrevOwner = 0; + m_Nodes[node].State = POINT_CONTROLED; + m_Nodes[node].PrevState = m_Nodes[node].State; + m_Nodes[node].State = POINT_CONTROLED; + m_Nodes[node].Timer = 0; + m_Nodes[node].Tower = tower; +} + +void BattleGroundAV::DefendNode(BG_AV_Nodes node, uint16 team) +{ + assert(m_Nodes[node].TotalOwner == team); + assert(m_Nodes[node].Owner != team); + assert(m_Nodes[node].State != POINT_CONTROLED && m_Nodes[node].State != POINT_DESTROYED); + m_Nodes[node].PrevOwner = m_Nodes[node].Owner; + m_Nodes[node].Owner = team; + m_Nodes[node].PrevState = m_Nodes[node].State; + m_Nodes[node].State = POINT_CONTROLED; + m_Nodes[node].Timer = 0; +} + +void BattleGroundAV::ResetBGSubclass() +{ + m_MaxLevel=0; + for (uint8 i=0; i<2; i++) //forloop for both teams (it just make 0 == alliance and 1 == horde also for both mines 0=north 1=south + { + for (uint8 j=0; j<9; j++) + m_Team_QuestStatus[i][j]=0; + m_Team_Scores[i]=BG_AV_SCORE_INITIAL_POINTS; + m_IsInformedNearVictory[i]=false; + m_CaptainAlive[i] = true; + m_CaptainBuffTimer[i] = 120000 + urand(0,4)* 60; //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes TODO get the right times + m_Mine_Owner[i] = AV_NEUTRAL_TEAM; + m_Mine_PrevOwner[i] = m_Mine_Owner[i]; + } + for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_STONEHEART_GRAVE; ++i) //alliance graves + InitNode(i,ALLIANCE,false); + for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_STONEHEART_BUNKER; ++i) //alliance towers + InitNode(i,ALLIANCE,true); + for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_GRAVE; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i) //horde graves + InitNode(i,HORDE,false); + for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_TOWER; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) //horde towers + InitNode(i,HORDE,true); + InitNode(BG_AV_NODES_SNOWFALL_GRAVE,AV_NEUTRAL_TEAM,false); //give snowfall neutral owner + + m_Mine_Timer=AV_MINE_TICK_TIMER; + for (uint16 i = 0; i < AV_CPLACE_MAX+AV_STATICCPLACE_MAX; i++) + if (m_BgCreatures[i]) + DelCreature(i); + +} + + diff --git a/src/server/game/BattleGrounds/BattleGroundAV.h b/src/server/game/BattleGrounds/BattleGroundAV.h new file mode 100644 index 00000000000..6d95c7bbc5d --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundAV.h @@ -0,0 +1,1614 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDAV_H +#define __BATTLEGROUNDAV_H + +class BattleGround; + +#define LANG_BG_AV_A_CAPTAIN_BUFF "Begone. Uncouth scum! The Alliance shall prevail in Alterac Valley!" +#define LANG_BG_AV_H_CAPTAIN_BUFF "Now is the time to attack! For the Horde!" +#define LANG_BG_AV_S_MINE_BOSS_CLAIMS "Snivvle is here! Snivvle claims the Coldtooth Mine!" + +#define BG_AV_CAPTIME 240000 //4:00 +#define BG_AV_SNOWFALL_FIRSTCAP 300000 //5:00 but i also have seen 4:05 + +#define BG_AV_SCORE_INITIAL_POINTS 600 +#define SEND_MSG_NEAR_LOSE 120 + +#define BG_AV_KILL_BOSS 4 +#define BG_AV_REP_BOSS 350 + +#define BG_AV_KILL_CAPTAIN 3 +#define BG_AV_REP_CAPTAIN 125 +#define BG_AV_RES_CAPTAIN 100 + +#define BG_AV_KILL_TOWER 3 +#define BG_AV_REP_TOWER 12 +#define BG_AV_RES_TOWER 75 + +#define BG_AV_GET_COMMANDER 1 //for a safely returned wingcommander +//bonushonor at the end +#define BG_AV_KILL_SURVIVING_TOWER 2 +#define BG_AV_REP_SURVIVING_TOWER 12 + +#define BG_AV_KILL_SURVIVING_CAPTAIN 2 +#define BG_AV_REP_SURVIVING_CAPTAIN 125 + +enum BG_AV_Sounds +{ //TODO: get out if there comes a sound when neutral team captures mine + +/* +8212: + alliance grave assault + alliance tower assault + drek "mlanzenabschaum! In meiner Burg?! Toetet sie all" - nicht immer der sound +8333: + galv "sterbt fuer euch ist kein platz hier" + +8332: + bal "Verschwinde, dreckiger Abschaum! Die Allianz wird im Alteractal " +8174: + horde tower assault + horde grave assault + van "es Sturmlanzenklans, euer General wird angegriffen! Ich fordere Unterst" +8173: + ally grave capture/defend + tower destroy + mine capture + ally wins +8192: + ally tower destroy(only iceblood - found a bug^^) + ally tower defend + horde tower defend +8213 +horde: + grave defend/capture + tower destroy + mine capture + horde wins + */ + + AV_SOUND_NEAR_VICTORY = 8456, //not confirmed yet + + AV_SOUND_ALLIANCE_ASSAULTS = 8212, //tower,grave + enemy boss if someone tries to attack him + AV_SOUND_HORDE_ASSAULTS = 8174, + AV_SOUND_ALLIANCE_GOOD = 8173, //if something good happens for the team: wins(maybe only through killing the boss), captures mine or grave, destroys tower and defends grave + AV_SOUND_HORDE_GOOD = 8213, + AV_SOUND_BOTH_TOWER_DEFEND = 8192, + + AV_SOUND_ALLIANCE_CAPTAIN = 8232, //gets called when someone attacks them and at the beginning after 3min+rand(x)*10sec (maybe buff) + AV_SOUND_HORDE_CAPTAIN = 8333, + +}; + +enum BG_AV_OTHER_VALUES +{ + AV_STATICCPLACE_MAX = 123, + AV_NORTH_MINE = 0, + AV_SOUTH_MINE = 1, + AV_MINE_TICK_TIMER = 45000, + AV_MINE_RECLAIM_TIMER = 1200000, //TODO: get the right value.. this is currently 20 minutes + AV_NEUTRAL_TEAM = 0 //this is the neutral owner of snowfall +}; +enum BG_AV_ObjectIds +{ + //cause the mangos-system is a bit different, we don't use the right go-ids for every node.. if we want to be 100% like another big server, we must take one object for every node + //snowfall 4flags as eyecandy 179424 (alliance neutral) + //Banners - stolen from battleground_AB.h ;-) + BG_AV_OBJECTID_BANNER_A = 178925, // can only be used by horde + BG_AV_OBJECTID_BANNER_H = 178943, // can only be used by alliance + BG_AV_OBJECTID_BANNER_CONT_A = 178940, // can only be used by horde + BG_AV_OBJECTID_BANNER_CONT_H = 179435, // can only be used by alliance + + BG_AV_OBJECTID_BANNER_A_B = 178365, + BG_AV_OBJECTID_BANNER_H_B = 178364, + BG_AV_OBJECTID_BANNER_CONT_A_B = 179286, + BG_AV_OBJECTID_BANNER_CONT_H_B = 179287, + BG_AV_OBJECTID_BANNER_SNOWFALL_N = 180418, + + //snowfall eyecandy banner: + BG_AV_OBJECTID_SNOWFALL_CANDY_A = 179044, + BG_AV_OBJECTID_SNOWFALL_CANDY_PA = 179424, + BG_AV_OBJECTID_SNOWFALL_CANDY_H = 179064, + BG_AV_OBJECTID_SNOWFALL_CANDY_PH = 179425, + + //banners on top of towers: + BG_AV_OBJECTID_TOWER_BANNER_A = 178927, //[PH] Alliance A1 Tower Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_H = 178955, //[PH] Horde H1 Tower Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_PA = 179446, //[PH] Alliance H1 Tower Pre-Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_PH = 179436, //[PH] Horde A1 Tower Pre-Banner BIG + + //Auras + BG_AV_OBJECTID_AURA_A = 180421, + BG_AV_OBJECTID_AURA_H = 180422, + BG_AV_OBJECTID_AURA_N = 180423, + BG_AV_OBJECTID_AURA_A_S = 180100, + BG_AV_OBJECTID_AURA_H_S = 180101, + BG_AV_OBJECTID_AURA_N_S = 180102, + + BG_AV_OBJECTID_GATE_A = 180424, + BG_AV_OBJECTID_GATE_H = 180424, + + //mine supplies + BG_AV_OBJECTID_MINE_N = 178785, + BG_AV_OBJECTID_MINE_S = 178784, + + BG_AV_OBJECTID_FIRE = 179065, + BG_AV_OBJECTID_SMOKE = 179066 +}; + +enum BG_AV_Nodes +{ + BG_AV_NODES_FIRSTAID_STATION = 0, + BG_AV_NODES_STORMPIKE_GRAVE = 1, + BG_AV_NODES_STONEHEART_GRAVE = 2, + BG_AV_NODES_SNOWFALL_GRAVE = 3, + BG_AV_NODES_ICEBLOOD_GRAVE = 4, + BG_AV_NODES_FROSTWOLF_GRAVE = 5, + BG_AV_NODES_FROSTWOLF_HUT = 6, + BG_AV_NODES_DUNBALDAR_SOUTH = 7, + BG_AV_NODES_DUNBALDAR_NORTH = 8, + BG_AV_NODES_ICEWING_BUNKER = 9, + BG_AV_NODES_STONEHEART_BUNKER = 10, + BG_AV_NODES_ICEBLOOD_TOWER = 11, + BG_AV_NODES_TOWER_POINT = 12, + BG_AV_NODES_FROSTWOLF_ETOWER = 13, + BG_AV_NODES_FROSTWOLF_WTOWER = 14, + + BG_AV_NODES_MAX = 15 +}; + +enum BG_AV_ObjectTypes +{ + BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION = 0, + BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE = 1, + BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE = 2, + BG_AV_OBJECT_FLAG_A_SNOWFALL_GRAVE = 3, + BG_AV_OBJECT_FLAG_A_ICEBLOOD_GRAVE = 4, + BG_AV_OBJECT_FLAG_A_FROSTWOLF_GRAVE = 5, + BG_AV_OBJECT_FLAG_A_FROSTWOLF_HUT = 6, + BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH = 7, + BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH = 8, + BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER = 9, + BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER = 10, + + BG_AV_OBJECT_FLAG_C_A_FIRSTAID_STATION = 11, + BG_AV_OBJECT_FLAG_C_A_STORMPIKE_GRAVE = 12, + BG_AV_OBJECT_FLAG_C_A_STONEHEART_GRAVE = 13, + BG_AV_OBJECT_FLAG_C_A_SNOWFALL_GRAVE = 14, + BG_AV_OBJECT_FLAG_C_A_ICEBLOOD_GRAVE = 15, + BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_GRAVE = 16, + BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_HUT = 17, + BG_AV_OBJECT_FLAG_C_A_ICEBLOOD_TOWER = 18, + BG_AV_OBJECT_FLAG_C_A_TOWER_POINT = 19, + BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_ETOWER = 20, + BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_WTOWER = 21, + + BG_AV_OBJECT_FLAG_C_H_FIRSTAID_STATION = 22, + BG_AV_OBJECT_FLAG_C_H_STORMPIKE_GRAVE = 23, + BG_AV_OBJECT_FLAG_C_H_STONEHEART_GRAVE = 24, + BG_AV_OBJECT_FLAG_C_H_SNOWFALL_GRAVE = 25, + BG_AV_OBJECT_FLAG_C_H_ICEBLOOD_GRAVE = 26, + BG_AV_OBJECT_FLAG_C_H_FROSTWOLF_GRAVE = 27, + BG_AV_OBJECT_FLAG_C_H_FROSTWOLF_HUT = 28, + BG_AV_OBJECT_FLAG_C_H_DUNBALDAR_SOUTH = 29, + BG_AV_OBJECT_FLAG_C_H_DUNBALDAR_NORTH = 30, + BG_AV_OBJECT_FLAG_C_H_ICEWING_BUNKER = 31, + BG_AV_OBJECT_FLAG_C_H_STONEHEART_BUNKER = 32, + + BG_AV_OBJECT_FLAG_H_FIRSTAID_STATION = 33, + BG_AV_OBJECT_FLAG_H_STORMPIKE_GRAVE = 34, + BG_AV_OBJECT_FLAG_H_STONEHEART_GRAVE = 35, + BG_AV_OBJECT_FLAG_H_SNOWFALL_GRAVE = 36, + BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE = 37, + BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE = 38, + BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT = 39, + BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER = 40, + BG_AV_OBJECT_FLAG_H_TOWER_POINT = 41, + BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER = 42, + BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER = 43, + + BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE = 44, + + BG_AV_OBJECT_DOOR_H = 45, + BG_AV_OBJECT_DOOR_A = 46, +//auras for graveyards (3auras per graveyard neutral,alliance,horde) + BG_AV_OBJECT_AURA_N_FIRSTAID_STATION = 47, + BG_AV_OBJECT_AURA_A_FIRSTAID_STATION = 48, + BG_AV_OBJECT_AURA_H_FIRSTAID_STATION = 49, + BG_AV_OBJECT_AURA_N_STORMPIKE_GRAVE = 50, + BG_AV_OBJECT_AURA_A_STORMPIKE_GRAVE = 51, + BG_AV_OBJECT_AURA_H_STORMPIKE_GRAVE = 52, + BG_AV_OBJECT_AURA_N_STONEHEART_GRAVE = 53, + BG_AV_OBJECT_AURA_A_STONEHEART_GRAVE = 54, + BG_AV_OBJECT_AURA_H_STONEHEART_GRAVE = 55, + BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE = 56, + BG_AV_OBJECT_AURA_A_SNOWFALL_GRAVE = 57, + BG_AV_OBJECT_AURA_H_SNOWFALL_GRAVE = 58, + BG_AV_OBJECT_AURA_N_ICEBLOOD_GRAVE = 59, + BG_AV_OBJECT_AURA_A_ICEBLOOD_GRAVE = 60, + BG_AV_OBJECT_AURA_H_ICEBLOOD_GRAVE = 61, + BG_AV_OBJECT_AURA_N_FROSTWOLF_GRAVE = 62, + BG_AV_OBJECT_AURA_A_FROSTWOLF_GRAVE = 63, + BG_AV_OBJECT_AURA_H_FROSTWOLF_GRAVE = 64, + BG_AV_OBJECT_AURA_N_FROSTWOLF_HUT = 65, + BG_AV_OBJECT_AURA_A_FROSTWOLF_HUT = 66, + BG_AV_OBJECT_AURA_H_FROSTWOLF_HUT = 67, + + //big flags on top of towers 2 flags on each (contested,(alliance | horde)) + 2 auras + BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH = 67, + BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH = 68, + BG_AV_OBJECT_TFLAG_A_DUNBALDAR_NORTH = 69, + BG_AV_OBJECT_TFLAG_H_DUNBALDAR_NORTH = 70, + BG_AV_OBJECT_TFLAG_A_ICEWING_BUNKER = 71, + BG_AV_OBJECT_TFLAG_H_ICEWING_BUNKER = 72, + BG_AV_OBJECT_TFLAG_A_STONEHEART_BUNKER = 73, + BG_AV_OBJECT_TFLAG_H_STONEHEART_BUNKER = 74, + BG_AV_OBJECT_TFLAG_A_ICEBLOOD_TOWER = 75, + BG_AV_OBJECT_TFLAG_H_ICEBLOOD_TOWER = 76, + BG_AV_OBJECT_TFLAG_A_TOWER_POINT = 77, + BG_AV_OBJECT_TFLAG_H_TOWER_POINT = 78, + BG_AV_OBJECT_TFLAG_A_FROSTWOLF_ETOWER = 79, + BG_AV_OBJECT_TFLAG_H_FROSTWOLF_ETOWER = 80, + BG_AV_OBJECT_TFLAG_A_FROSTWOLF_WTOWER = 81, + BG_AV_OBJECT_TFLAG_H_FROSTWOLF_WTOWER = 82, + BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH = 83, + BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH = 84, + BG_AV_OBJECT_TAURA_A_DUNBALDAR_NORTH = 85, + BG_AV_OBJECT_TAURA_H_DUNBALDAR_NORTH = 86, + BG_AV_OBJECT_TAURA_A_ICEWING_BUNKER = 87, + BG_AV_OBJECT_TAURA_H_ICEWING_BUNKER = 88, + BG_AV_OBJECT_TAURA_A_STONEHEART_BUNKER = 89, + BG_AV_OBJECT_TAURA_H_STONEHEART_BUNKER = 90, + BG_AV_OBJECT_TAURA_A_ICEBLOOD_TOWER = 91, + BG_AV_OBJECT_TAURA_H_ICEBLOOD_TOWER = 92, + BG_AV_OBJECT_TAURA_A_TOWER_POINT = 93, + BG_AV_OBJECT_TAURA_H_TOWER_POINT = 94, + BG_AV_OBJECT_TAURA_A_FROSTWOLF_ETOWER = 95, + BG_AV_OBJECT_TAURA_H_FROSTWOLF_ETOWER = 96, + BG_AV_OBJECT_TAURA_A_FROSTWOLF_WTOWER = 97, + BG_AV_OBJECT_TAURA_H_FROSTWOLF_WTOWER = 98, + + BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH = 99, + BG_AV_OBJECT_BURN_DUNBALDAR_NORTH = 109, + BG_AV_OBJECT_BURN_ICEWING_BUNKER = 119, + BG_AV_OBJECT_BURN_STONEHEART_BUNKER = 129, + BG_AV_OBJECT_BURN_ICEBLOOD_TOWER = 139, + BG_AV_OBJECT_BURN_TOWER_POINT = 149, + BG_AV_OBJECT_BURN_FROSTWOLF_ETWOER = 159, + BG_AV_OBJECT_BURN_FROSTWOLF_WTOWER = 169, + BG_AV_OBJECT_BURN_BUILDING_ALLIANCE = 179, + BG_AV_OBJECT_BURN_BUILDING_HORDE = 189, + BG_AV_OBJECT_SNOW_EYECANDY_A = 199, + BG_AV_OBJECT_SNOW_EYECANDY_PA = 203, + BG_AV_OBJECT_SNOW_EYECANDY_H = 207, + BG_AV_OBJECT_SNOW_EYECANDY_PH = 211, + BG_AV_OBJECT_MINE_SUPPLY_N_MIN = 215, + BG_AV_OBJECT_MINE_SUPPLY_N_MAX = 224, + BG_AV_OBJECT_MINE_SUPPLY_S_MIN = 225, + BG_AV_OBJECT_MINE_SUPPLY_S_MAX = 236, + + BG_AV_OBJECT_MAX = 237 +}; + +enum BG_AV_OBJECTS +{ + AV_OPLACE_FIRSTAID_STATION = 0, + AV_OPLACE_STORMPIKE_GRAVE = 1, + AV_OPLACE_STONEHEART_GRAVE = 2, + AV_OPLACE_SNOWFALL_GRAVE = 3, + AV_OPLACE_ICEBLOOD_GRAVE = 4, + AV_OPLACE_FROSTWOLF_GRAVE = 5, + AV_OPLACE_FROSTWOLF_HUT = 6, + AV_OPLACE_DUNBALDAR_SOUTH = 7, + AV_OPLACE_DUNBALDAR_NORTH = 8, + AV_OPLACE_ICEWING_BUNKER = 9, + AV_OPLACE_STONEHEART_BUNKER = 10, + AV_OPLACE_ICEBLOOD_TOWER = 11, + AV_OPLACE_TOWER_POINT = 12, + AV_OPLACE_FROSTWOLF_ETOWER = 13, + AV_OPLACE_FROSTWOLF_WTOWER = 14, + AV_OPLACE_BIGBANNER_DUNBALDAR_SOUTH = 15, + AV_OPLACE_BIGBANNER_DUNBALDAR_NORTH = 16, + AV_OPLACE_BIGBANNER_ICEWING_BUNKER = 17, + AV_OPLACE_BIGBANNER_STONEHEART_BUNKER = 18, + AV_OPLACE_BIGBANNER_ICEBLOOD_TOWER = 19, + AV_OPLACE_BIGBANNER_TOWER_POINT = 20, + AV_OPLACE_BIGBANNER_FROSTWOLF_ETOWER = 21, + AV_OPLACE_BIGBANNER_FROSTWOLF_WTOWER = 22, + + AV_OPLACE_BURN_DUNBALDAR_SOUTH = 23, + AV_OPLACE_BURN_DUNBALDAR_NORTH = 33, + AV_OPLACE_BURN_ICEWING_BUNKER = 43, + AV_OPLACE_BURN_STONEHEART_BUNKER = 53, + AV_OPLACE_BURN_ICEBLOOD_TOWER = 63, + AV_OPLACE_BURN_TOWER_POINT = 73, + AV_OPLACE_BURN_FROSTWOLF_ETOWER = 83, + AV_OPLACE_BURN_FROSTWOLF_WTOWER = 93, + AV_OPLACE_BURN_BUILDING_A = 103, + AV_OPLACE_BURN_BUILDING_H = 113, + AV_OPLACE_SNOW_1 = 123, + AV_OPLACE_SNOW_2 = 124, + AV_OPLACE_SNOW_3 = 125, + AV_OPLACE_SNOW_4 = 126, + AV_OPLACE_MINE_SUPPLY_N_MIN = 127, + AV_OPLACE_MINE_SUPPLY_N_MAX = 136, + AV_OPLACE_MINE_SUPPLY_S_MIN = 137, + AV_OPLACE_MINE_SUPPLY_S_MAX = 148, + + AV_OPLACE_MAX = 149 +}; +const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = { + {638.592f,-32.422f,46.0608f,-1.62316f },//firstaid station + {669.007f,-294.078f,30.2909f,2.77507f },//stormpike + {77.8013f,-404.7f,46.7549f,-0.872665f },//stone grave + {-202.581f,-112.73f,78.4876f,-0.715585f },//snowfall + {-611.962f,-396.17f,60.8351f,2.53682f}, //iceblood grave + {-1082.45f,-346.823f,54.9219f,-1.53589f },//frostwolf grave + {-1402.21f,-307.431f,89.4424f,0.191986f },//frostwolf hut + {553.779f,-78.6566f,51.9378f,-1.22173f }, //dunnbaldar south + {674.001f,-143.125f,63.6615f,0.994838f }, //dunbaldar north + {203.281f,-360.366f,56.3869f,-0.925024f }, //icew + {-152.437f,-441.758f,40.3982f,-1.95477f }, //stone + {-571.88f,-262.777f,75.0087f,-0.802851f }, //ice tower + {-768.907f,-363.71f,90.8949f,1.07991f}, //tower point + {-1302.9f,-316.981f,113.867f,2.00713f }, //frostwolf etower + {-1297.5f,-266.767f,114.15f,3.31044f}, //frostwolf wtower + //bigbanner: + {555.848f,-84.4151f,64.4397f,3.12414f }, //duns + {679.339f,-136.468f,73.9626f,-2.16421f }, //dunn + {208.973f,-365.971f,66.7409f,-0.244346f }, //icew + {-155.832f,-449.401f,52.7306f,0.610865f }, //stone + {-572.329f,-262.476f,88.6496f,-0.575959f }, //icetower + {-768.199f,-363.105f,104.537f,0.10472f }, //towerp + {-1302.84f,-316.582f,127.516f,0.122173f }, //etower + {-1297.87f,-266.762f,127.796f,0.0698132f }, //wtower + //burning auras towers have 9*179065 captain-buildings have 5*179066+5*179065 + //dunns + {562.632f,-88.1815f,61.993f,0.383972f }, + {562.523f,-74.5028f,37.9474f,-0.0523599f }, + {558.097f,-70.9842f,52.4876f,0.820305f }, + {578.167f,-71.8191f,38.1514f,2.72271f }, + {556.028f,-94.9242f,44.8191f,3.05433f }, + {572.451f,-94.3655f,37.9443f,-1.72788f }, + {549.263f,-79.3645f,44.8191f,0.436332f }, + {543.513f,-94.4006f,52.4819f,0.0349066f }, + {572.149f,-93.7862f,52.5726f,0.541052f }, + {582.162f,-81.2375f,37.9216f,0.0872665f }, + //dunn + {664.797f,-143.65f,64.1784f,-0.453786f}, + {664.505f,-139.452f,49.6696f,-0.0349067f}, + {676.067f,-124.319f,49.6726f,-1.01229f}, + {693.004f,-144.025f,64.1755f,2.44346f}, + {661.175f,-117.691f,49.645f,1.91986f}, + {684.423f,-146.582f,63.6662f,0.994838f}, + {682.791f,-127.769f,62.4155f,1.09956f}, + {674.576f,-147.101f,56.5425f,-1.6057f}, + {655.719f,-126.673f,49.8138f,2.80998f}, + {0,0,0,0}, + //icew + {231.503f,-356.688f,42.3704f,0.296706f}, + {224.989f,-348.175f,42.5607f,1.50098f}, + {205.782f,-351.335f,56.8998f,1.01229f}, + {196.605f,-369.187f,56.3914f,2.46091f}, + {210.619f,-376.938f,49.2677f,2.86234f}, + {209.647f,-352.632f,42.3959f,-0.698132f}, + {220.65f,-368.132f,42.3978f,-0.2618f}, + {224.682f,-374.031f,57.0679f,0.541052f}, + {200.26f,-359.968f,49.2677f,-2.89725f}, + {196.619f,-378.016f,56.9131f,1.01229f}, + //stone + {-155.488f,-437.356f,33.2796f,2.60054f}, + {-163.441f,-454.188f,33.2796f,1.93732f}, + {-143.977f,-445.148f,26.4097f,-1.8675f}, + {-135.764f,-464.708f,26.3823f,2.25147f}, + {-154.076f,-466.929f,41.0636f,-1.8675f}, + {-149.908f,-460.332f,26.4083f,-2.09439f}, + {-151.638f,-439.521f,40.3797f,0.436332f}, + {-131.301f,-454.905f,26.5771f,2.93215f}, + {-171.291f,-444.684f,40.9211f,2.30383f}, + {-143.591f,-439.75f,40.9275f,-1.72788f}, + //iceblood + {-572.667f,-267.923f,56.8542f,2.35619f}, + {-561.021f,-262.689f,68.4589f,1.37881f}, + {-572.538f,-262.649f,88.6197f,1.8326f}, + {-574.77f,-251.45f,74.9422f,-1.18682f}, + {-578.625f,-267.571f,68.4696f,0.506145f}, + {-571.476f,-257.234f,63.3223f,3.10669f}, + {-566.035f,-273.907f,52.9582f,-0.890118f}, + {-580.948f,-259.77f,68.4696f,1.46608f}, + {-568.318f,-267.1f,75.0008f,1.01229f}, + {-559.621f,-268.597f,52.8986f,0.0523599f}, + //towerp + {-776.072f,-368.046f,84.3558f,2.63545f}, + {-777.564f,-368.521f,90.6701f,1.72788f}, + {-765.461f,-357.711f,90.888f,0.314159f}, + {-768.763f,-362.735f,104.612f,1.81514f}, + {-760.356f,-358.896f,84.3558f,2.1293f}, + {-771.967f,-352.838f,84.3484f,1.74533f}, + {-773.333f,-364.653f,79.2351f,-1.64061f}, + {-764.109f,-366.069f,70.0934f,0.383972f}, + {-767.103f,-350.737f,68.7933f,2.80998f}, + {-760.115f,-353.845f,68.8633f,1.79769f}, + //froste + {-1304.87f,-304.525f,91.8366f,-0.680679f}, + {-1301.77f,-310.974f,95.8252f,0.907571f}, + {-1305.58f,-320.625f,102.166f,-0.558505f}, + {-1294.27f,-323.468f,113.893f,-1.67552f}, + {-1302.65f,-317.192f,127.487f,2.30383f}, + {-1293.89f,-313.478f,107.328f,1.6057f}, + {-1312.41f,-312.999f,107.328f,1.5708f}, + {-1311.57f,-308.08f,91.7666f,-1.85005f}, + {-1314.7f,-322.131f,107.36f,0.645772f}, + {-1304.6f,-310.754f,113.859f,-0.401426f}, + //frostw + {-1308.24f,-273.26f,92.0514f,-0.139626f}, + {-1302.26f,-262.858f,95.9269f,0.418879f}, + {-1297.28f,-267.773f,126.756f,2.23402f}, + {-1299.08f,-256.89f,114.108f,-2.44346f}, + {-1303.41f,-268.237f,114.151f,-1.23918f}, + {-1304.43f,-273.682f,107.612f,0.244346f}, + {-1309.53f,-265.951f,92.1418f,-2.49582f}, + {-1295.55f,-263.865f,105.033f,0.925024f}, + {-1294.71f,-281.466f,107.664f,-1.50098f}, + {-1289.69f,-259.521f,107.612f,-2.19912f}, + + //the two buildings of the captains + //alliance + {-64.4987f,-289.33f,33.4616f,-2.82743f}, + {-5.98025f,-326.144f,38.8538f,0}, + {-2.67893f,-306.998f,33.4165f,0}, + {-60.25f,-309.232f,50.2408f,-1.46608f}, + {-48.7941f,-266.533f,47.7916f,2.44346f}, + {-3.40929f,-306.288f,33.34f,0}, + {-48.619f,-266.917f,47.8168f,0}, + {-62.9474f,-286.212f,66.7288f,0}, + {-5.05132f,-325.323f,38.8536f,0}, + {-64.2677f,-289.412f,33.469f,0}, +//horde + {-524.276f,-199.6f,82.8733f,-1.46608f}, + {-518.196f,-173.085f,102.43f,0}, + {-500.732f,-145.358f,88.5337f,2.44346f}, + {-501.084f,-150.784f,80.8506f,0}, + {-518.309f,-163.963f,102.521f,2.96706f}, + {-517.053f,-200.429f,80.759f,0}, + {-514.361f,-163.864f,104.163f,0}, + {-568.04f,-188.707f,81.55f,0}, + {-501.775f,-151.581f,81.2027f,0}, + {-509.975f,-191.652f,83.2978f,0}, + +//snowfall eyecandy + {-191.153f,-129.868f,78.5595f,-1.25664f }, + {-201.282f,-134.319f,78.6753f,-0.942478f }, + {-215.981f,-91.4101f,80.8702f,-1.74533f }, + {-200.465f,-96.418f,79.7587f,1.36136f }, + //mine supplies + //irondeep + {870.899f,-388.434f,61.6406f,-1.22173f}, + {825.214f,-320.174f,63.712f,-2.82743f}, + {837.117f,-452.556f,47.2331f,-3.12414f}, + {869.755f,-448.867f,52.5448f,-0.855212f}, + {949.877f,-458.198f,56.4874f,0.314159f}, + {900.35f,-479.024f,58.3553f,0.122173f}, + {854.449f,-442.255f,50.6589f,0.401426f}, + {886.685f,-442.358f,54.6962f,-1.22173f}, + {817.509f,-457.331f,48.4666f,2.07694f}, + {793.411f,-326.281f,63.1117f,-2.79253f}, + //coldtooth + {-934.212f,-57.3517f,80.277f,-0.0174535f}, + {-916.281f,-36.8579f,77.0227f,0.122173f}, + {-902.73f,-103.868f,75.4378f,-1.58825f}, + {-900.514f,-143.527f,75.9686f,1.8675f}, + {-862.882f,-0.353299f,72.1526f,-2.51327f}, + {-854.932f,-85.9184f,68.6056f,-2.04204f}, + {-851.833f,-118.959f,63.8672f,-0.0698131f}, + {-849.832f,-20.8421f,70.4672f,-1.81514f}, + {-844.25f,-60.0374f,72.1031f,-2.19912f}, + {-820.644f,-136.043f,63.1977f,2.40855f}, + {-947.642f,-208.807f,77.0101f,1.36136f}, + {-951.394f,-193.695f,67.634f,0.802851f} +}; + +const float BG_AV_DoorPositons[2][4] = { + {780.487f, -493.024f, 99.9553f, 3.0976f}, //alliance + {-1375.193f, -538.981f, 55.2824f, 0.72178f} //horde +}; + +//creaturestuff starts here +//is related to BG_AV_CreaturePos +enum BG_AV_CreaturePlace +{ + AV_CPLACE_SPIRIT_STORM_AID = 0, + AV_CPLACE_SPIRIT_STORM_GRAVE = 1, + AV_CPLACE_SPIRIT_STONE_GRAVE = 2, + AV_CPLACE_SPIRIT_SNOWFALL = 3, + AV_CPLACE_SPIRIT_ICE_GRAVE = 4, + AV_CPLACE_SPIRIT_FROSTWOLF = 5, + AV_CPLACE_SPIRIT_FROST_HUT = 6, + AV_CPLACE_SPIRIT_MAIN_ALLIANCE = 7, + AV_CPLACE_SPIRIT_MAIN_HORDE = 8, +//i don't will add for all 4 positions a variable.. i think one is enough to compute the rest + AV_CPLACE_DEFENSE_STORM_AID = 9, + AV_CPLACE_DEFEMSE_STORM_GRAVE = 13, + AV_CPLACE_DEFENSE_STONE_GRAVE = 17, + AV_CPLACE_DEFENSE_SNOWFALL = 21, + AV_CPLACE_DEFENSE_FROSTWOLF = 25, + AV_CPLACE_DEFENSE_ICE_GRAVE = 29, + AV_CPLACE_DEFENSE_FROST_HUT = 33, + + AV_CPLACE_DEFENSE_DUN_S = 37, + AV_CPLACE_DEFENSE_DUN_N = 41, + AV_CPLACE_DEFENSE_ICEWING = 45, + AV_CPLACE_DEFENSE_STONE_TOWER = 49, + AV_CPLACE_DEFENSE_ICE_TOWER = 53, + AV_CPLACE_DEFENSE_TOWERPOINT = 57, + AV_CPLACE_DEFENSE_FROST_E = 61, + AV_CPLACE_DEFENSE_FROST_t = 65, + + AV_CPLACE_A_MARSHAL_SOUTH = 69, + AV_CPLACE_A_MARSHAL_NORTH = 70, + AV_CPLACE_A_MARSHAL_ICE = 71, + AV_CPLACE_A_MARSHAL_STONE = 72, + AV_CPLACE_H_MARSHAL_ICE = 73, + AV_CPLACE_H_MARSHAL_TOWER = 74, + AV_CPLACE_H_MARSHAL_ETOWER = 75, + AV_CPLACE_H_MARSHAL_WTOWER = 76, + //irondeep + //miner: + AV_CPLACE_MINE_N_1_MIN = 77, + AV_CPLACE_MINE_N_1_MAX = 136, + //special types + AV_CPLACE_MINE_N_2_MIN = 137, + AV_CPLACE_MINE_N_2_MAX = 192, + //boss + AV_CPLACE_MINE_N_3 = 193, + //coldtooth + //miner: + AV_CPLACE_MINE_S_1_MIN = 194, + AV_CPLACE_MINE_S_1_MAX = 250, + //special types + AV_CPLACE_MINE_S_2_MIN = 251, + AV_CPLACE_MINE_S_2_MAX = 289, + //vermin + AV_CPLACE_MINE_S_S_MIN = 290, + AV_CPLACE_MINE_S_S_MAX = 299, + //boss + AV_CPLACE_MINE_S_3 = 300, + + //herald + AV_CPLACE_HERALD = 301, + + //node aura triggers + AV_CPLACE_TRIGGER01 = 302, + AV_CPLACE_TRIGGER02 = 303, + AV_CPLACE_TRIGGER03 = 304, + AV_CPLACE_TRIGGER04 = 305, + AV_CPLACE_TRIGGER05 = 306, + AV_CPLACE_TRIGGER06 = 307, + AV_CPLACE_TRIGGER07 = 308, + AV_CPLACE_TRIGGER08 = 309, + AV_CPLACE_TRIGGER09 = 310, + AV_CPLACE_TRIGGER10 = 311, + AV_CPLACE_TRIGGER11 = 312, + AV_CPLACE_TRIGGER12 = 313, + AV_CPLACE_TRIGGER13 = 314, + AV_CPLACE_TRIGGER14 = 315, + AV_CPLACE_TRIGGER15 = 316, + + //boss,captain triggers + AV_CPLACE_TRIGGER16 = 317, + AV_CPLACE_TRIGGER17 = 318, + AV_CPLACE_TRIGGER18 = 319, + AV_CPLACE_TRIGGER19 = 320, + + AV_CPLACE_MAX = 321 +}; + +//x, y, z, o +const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = { + //spiritguides + {643.000000f,44.000000f,69.740196f,-0.001854f}, + {676.000000f,-374.000000f,30.000000f,-0.001854f}, + {73.417755f,-496.433105f,48.731918f,-0.001854f}, + {-157.409195f,31.206272f,77.050598f,-0.001854f}, + {-531.217834f,-405.231384f,49.551376f,-0.001854f}, + {-1090.476807f,-253.308670f,57.672371f,-0.001854f}, + {-1496.065063f,-333.338409f,101.134804f,-0.001854f}, + {873.001770f,-491.283630f,96.541931f,-0.001854f}, + {-1437.670044f,-610.088989f,51.161900f,-0.001854f}, + //grave + //firstaid + {635.17f,-29.5594f,46.5056f,4.81711f}, + {642.488f,-32.9437f,46.365f,4.67748f}, + {642.326f,-27.9442f,46.9211f,4.59022f}, + {635.945f,-33.6171f,45.7164f,4.97419f}, + //stormpike + {669.272f,-297.304f,30.291f,4.66604f}, + {674.08f,-292.328f,30.4817f,0.0918785f}, + {667.01f,-288.532f,29.8809f,1.81583f}, + {664.153f,-294.042f,30.2851f,3.28531f}, + //stone + {81.7027f,-406.135f,47.7843f,0.598464f}, + {78.1431f,-409.215f,48.0401f,5.05953f}, + {73.4135f,-407.035f,46.7527f,3.34736f}, + {78.2258f,-401.859f,46.4202f,2.05852f}, + //snowfall + {-207.412f,-110.616f,78.7959f,2.43251f}, + {-197.95f,-112.205f,78.5686f,6.22441f}, + {-202.709f,-116.829f,78.4358f,5.13742f}, + {-202.059f,-108.314f,78.5783f,5.91968f}, + //ice + {-615.501f,-393.802f,60.4299f,3.06147f}, + {-608.513f,-392.717f,62.5724f,2.06323f}, + {-609.769f,-400.072f,60.7174f,5.22367f}, + {-616.093f,-398.293f,60.5628f,3.73613f}, + //frost + {-1077.7f,-340.21f,55.4682f,6.25569f}, + {-1082.74f,-333.821f,54.7962f,2.05459f}, + {-1090.66f,-341.267f,54.6768f,3.27746f}, + {-1081.58f,-344.63f,55.256f,4.75636f}, + //frost hut + {-1408.95f,-311.69f,89.2536f,4.49954f}, + {-1407.15f,-305.323f,89.1993f,2.86827f}, + {-1400.64f,-304.3f,89.7008f,1.0595f}, + {-1400.4f,-311.35f,89.3028f,4.99434f}, + //towers + //dun south - OK + {569.395f,-101.064f,52.8296f,2.34974f}, + {574.85f,-92.9842f,52.5869f,3.09325f}, + {575.411f,-83.597f,52.3626f,6.26573f}, + {571.352f,-75.6582f,52.479f,0.523599f}, + //dun north - OK + {668.60f,-122.53f,64.12f,2.34f}, //not 100% ok + {662.253f,-129.105f,64.1794f,2.77507f}, + {661.209f,-138.877f,64.2251f,3.38594f}, + {665.481f,-146.857f,64.1271f,3.75246f}, + //icewing - OK + {225.228f,-368.909f,56.9983f,6.23806f}, + {191.36f,-369.899f,57.1524f,3.24631f}, + {215.518f,-384.019f,56.9889f,5.09636f}, + {199.625f,-382.177f,56.8691f,4.08407f}, + //stone + {-172.851f,-452.366f,40.8725f,3.31829f}, + {-147.147f,-435.053f,40.8022f,0.599238f}, + {-169.456f,-440.325f,40.985f,2.59101f}, + {-163.494f,-434.904f,41.0725f,1.84174f}, + //ice - OK + {-573.522f,-271.854f,75.0078f,3.9619f}, + {-565.616f,-269.051f,74.9952f,5.02655f}, + {-562.825f,-261.087f,74.9898f,5.95157f}, + {-569.176f,-254.446f,74.8771f,0.820305f}, + //towerpoint + {-763.04f,-371.032f,90.7933f,5.25979f}, + {-759.764f,-358.264f,90.8681f,0.289795f}, + {-768.808f,-353.056f,90.8811f,1.52601f}, + {-775.944f,-362.639f,90.8949f,2.59573f}, + //frost etower + {-1294.13f,-313.045f,107.328f,0.270162f}, + {-1306.5f,-308.105f,113.767f,1.78755f}, + {-1294.78f,-319.966f,113.79f,5.94545f}, + {-1294.83f,-312.241f,113.799f,0.295293f}, + //frost wtower + {-1300.96f,-275.111f,114.058f,4.12804f}, + {-1302.41f,-259.256f,114.065f,1.67602f}, + {-1287.97f,-262.087f,114.165f,6.18264f}, + {-1291.59f,-271.166f,114.151f,5.28257f}, + + //alliance marshall + {721.104f,-7.64155f,50.7046f,3.45575f},// south + {723.058f,-14.1548f,50.7046f,3.40339f},// north + {715.691f,-4.72233f,50.2187f,3.47321f},// icewing + {720.046f,-19.9413f,50.2187f,3.36849f},// stone +//horde (coords not 100% ok) + {-1363.99f,-221.99f,98.4053f,4.93012f}, + {-1370.96f,-223.532f,98.4266f,4.93012f}, + {-1378.37f,-228.614f,99.3546f,5.38565f}, + {-1358.02f,-228.998f,98.868f,3.87768f}, + + //irondeep mine + //Irondeep Trogg + {971.671f,-442.657f,57.6951f,3.1765f}, + {969.979f,-457.148f,58.1119f,4.5204f}, + {958.692f,-333.477f,63.2276f,5.77704f}, + {957.113f,-325.92f,61.7589f,1.13446f}, + {948.25f,-448.268f,56.9009f,5.60251f}, + {934.727f,-385.802f,63.0344f,3.75246f}, + {931.751f,-403.458f,59.6737f,5.63741f}, + {931.146f,-359.666f,66.0294f,3.9619f}, + {929.702f,-412.401f,56.8776f,5.89921f}, + {926.849f,-379.074f,63.5286f,2.0944f}, + {921.972f,-358.597f,66.4313f,2.93215f}, + {921.449f,-341.981f,67.1264f,3.4383f}, + {921.1f,-395.812f,60.4615f,2.71695f}, + {919.274f,-394.986f,60.3478f,2.71696f}, + {916.852f,-393.891f,60.1726f,2.71695f}, + {914.568f,-326.21f,66.1733f,2.25147f}, + {913.064f,-395.773f,60.1364f,4.41568f}, + {909.246f,-474.576f,58.2067f,0.226893f}, + {909.246f,-474.576f,58.2901f,0.226893f}, + {907.209f,-428.267f,59.8065f,1.8675f}, + {905.973f,-459.528f,58.7594f,1.37189f}, + {905.067f,-396.074f,60.2085f,5.07891f}, + {901.809f,-457.709f,59.0116f,3.52557f}, + {900.962f,-427.44f,59.0842f,1.50098f}, + {897.929f,-471.742f,59.7729f,2.54818f}, + {893.376f,-343.171f,68.1499f,5.35816f}, + {890.584f,-406.049f,61.1925f,5.67232f}, + {888.208f,-332.564f,68.148f,1.93732f}, + {887.647f,-391.537f,61.8734f,1.37881f}, + {885.109f,-343.338f,67.0867f,3.78979f}, + {881.618f,-419.948f,53.5228f,0.593412f}, + {878.675f,-345.36f,66.1052f,3.45651f}, + {877.127f,-351.8f,66.5296f,5.74213f}, + {876.778f,-345.97f,65.7724f,3.45262f}, + {874.577f,-414.786f,52.7817f,1.67552f}, + {868.247f,-343.136f,64.9894f,1.6057f}, + {859.03f,-367.231f,47.4655f,0.0174533f}, + {857.513f,-351.817f,65.1867f,4.39823f}, + {852.632f,-372.416f,48.1657f,3.66519f}, + {849.86f,-340.944f,66.2447f,0.401426f}, + {847.99f,-386.287f,60.9277f,2.32374f}, + {847.601f,-423.072f,50.0852f,4.57276f}, + {847.135f,-411.307f,50.2106f,1.5708f}, + {835.077f,-379.418f,48.2755f,5.93412f}, + {834.87f,-453.304f,47.9075f,0.226893f}, + {834.634f,-365.981f,62.8801f,1.32645f}, + {834.354f,-355.526f,48.1491f,6.07375f}, + {833.702f,-327.506f,65.0439f,0.331613f}, + {833.151f,-374.228f,63.0938f,3.66519f}, + {831.711f,-346.785f,47.2975f,0.226893f}, + {827.874f,-413.624f,48.5818f,1.49241f}, + {827.728f,-415.483f,48.5593f,1.49238f}, + {827.016f,-424.543f,48.2856f,1.49236f}, + {823.222f,-334.283f,65.6306f,4.88692f}, + {821.892f,-464.723f,48.9451f,4.66003f}, + {821.006f,-387.635f,49.0728f,3.15905f}, + {817.26f,-447.432f,49.4308f,2.18166f}, + {805.399f,-320.146f,52.7712f,0.296706f}, + {801.405f,-328.055f,53.0195f,4.31096f}, + //irondeep skullthumber irondeep shaman + {955.812f,-440.302f,55.3411f,3.19395f}, + {937.378f,-377.816f,65.3919f,3.56047f}, + {925.059f,-331.347f,65.7564f,3.66519f}, + {922.918f,-396.634f,60.3942f,2.71695f}, + {909.99f,-462.154f,59.0811f,3.7001f}, + {907.893f,-388.787f,61.7923f,5.74213f}, + {898.801f,-437.105f,58.5266f,0.959931f}, + {884.237f,-407.597f,61.566f,0.820305f}, + {880.744f,-344.683f,66.4086f,3.4644f}, + {876.047f,-341.857f,65.8743f,4.45059f}, + {874.674f,-402.077f,61.7573f,0.26341f}, + {871.914f,-404.209f,62.1269f,6.06163f}, + {871.606f,-403.665f,62.0795f,0.765774f}, + {871.561f,-404.114f,62.1297f,0.00981727f}, + {871.528f,-404.248f,62.1455f,0.498032f}, + {871.493f,-404.122f,62.1331f,5.65727f}, + {871.282f,-403.843f,62.1108f,0.788382f}, + {868.294f,-392.395f,61.4772f,4.38685f}, + {868.256f,-392.363f,61.4803f,0.732738f}, + {867.804f,-392.51f,61.5089f,2.30167f}, + {867.612f,-392.371f,61.524f,2.86149f}, + {858.593f,-439.614f,50.2184f,0.872665f}, + {851.471f,-362.52f,47.314f,4.06662f}, + {846.939f,-347.279f,66.2876f,0.942478f}, + {842.08f,-421.775f,48.2659f,1.0821f}, + {838.358f,-371.212f,63.3299f,4.04916f}, + {827.57f,-417.483f,48.4538f,1.49237f}, + {827.012f,-457.397f,48.9331f,2.35619f}, + {825.535f,-322.373f,63.9357f,4.76475f}, + {867.635f,-443.605f,51.3347f,1.38626f}, + {957.293f,-455.039f,56.7395f,5.79449f}, + {950.077f,-326.672f,61.6552f,5.48033f}, + {936.692f,-356.78f,65.9835f,2.75762f}, + {926.475f,-419.345f,56.1833f,2.0944f}, + {924.729f,-397.453f,60.213f,2.71695f}, + {902.195f,-475.891f,58.312f,1.39626f}, + {897.464f,-338.758f,68.1715f,2.94961f}, + {884.237f,-407.597f,61.566f,0.820305f}, + {882.517f,-344.111f,66.7887f,3.46962f}, + {881.437f,-400.254f,61.2028f,0.263427f}, + {880.156f,-400.678f,61.3113f,3.41373f}, + {877.989f,-418.051f,52.9753f,4.46804f}, + {871.212f,-404.12f,62.1433f,3.6554f}, + {871.036f,-404.119f,62.2237f,4.50295f}, + {857.396f,-395.766f,61.263f,4.78684f}, + {857.276f,-395.395f,61.2418f,0.0845553f}, + {857.231f,-394.577f,61.2174f,1.96817f}, + {857.108f,-395.682f,61.2317f,4.87022f}, + {856.709f,-395.28f,61.1814f,2.54913f}, + {850.922f,-390.399f,60.8771f,2.85405f}, + {847.556f,-388.228f,60.9438f,2.56872f}, + {842.031f,-384.663f,61.6028f,2.56871f}, + {832.035f,-389.301f,47.5567f,2.11185f}, + {827.415f,-419.468f,48.3322f,1.49232f}, + {826.402f,-349.454f,47.2722f,1.51844f}, + {817.83f,-455.715f,48.4207f,0.925025f}, + {808.953f,-325.964f,52.4043f,3.01942f}, + // Morloch + {865.554f,-438.735f,50.7333f,2.12431f}, + //coldtooth mine + //miner/digger + {-917.648f,-46.8922f,77.0872f,5.27089f}, + {-912.689f,-45.4494f,76.2277f,4.60767f}, + {-905.455f,-84.5179f,75.3642f,3.29867f}, + {-904.332f,-111.509f,75.5925f,2.47837f}, + {-904.27f,-160.419f,61.9876f,3.61192f}, + {-904.023f,-90.4558f,75.3706f,3.40339f}, + {-978.678f,-37.3136f,75.8364f,2.84489f}, + {-973.076f,-36.5013f,77.5047f,1.0821f}, + {-963.951f,-87.734f,81.5555f,0.575959f}, + {-961.941f,-90.7252f,81.6629f,0.820305f}, + {-957.623f,-186.582f,66.6021f,1.95477f}, + {-952.476f,-179.778f,78.6771f,4.5204f}, + {-950.427f,-115.007f,79.6127f,3.68264f}, + {-950.25f,-151.95f,79.4598f,-1.81423f}, + {-950.169f,-188.099f,66.6184f,5.55015f}, + {-949.944f,-142.977f,80.5382f,2.70526f}, + {-947.854f,-170.5f,79.7618f,0.942478f}, + {-946.738f,-139.567f,80.0904f,2.3911f}, + {-945.503f,-65.0654f,79.7907f,5.02655f}, + {-943.678f,-110.986f,80.2557f,0.959931f}, + {-942.993f,-56.9881f,79.8915f,5.65487f}, + {-938.197f,-155.838f,61.3111f,1.65806f}, + {-930.488f,-214.524f,72.1431f,2.1236f}, + {-929.947f,-154.449f,61.5084f,1.67552f}, + {-927.412f,-135.313f,61.1987f,3.29867f}, + {-920.677f,-156.859f,62.8033f,3.15306f}, + {-916.75f,-136.094f,62.2357f,0.0698132f}, + {-915.319f,-132.718f,62.562f,1.16984f}, + {-913.589f,-146.794f,76.9366f,1.8675f}, + {-907.572f,-148.937f,76.6898f,4.76475f}, + {-902.02f,-64.6174f,73.9707f,1.19169f}, + {-899.489f,-61.7252f,73.2498f,5.09636f}, + {-894.792f,-127.141f,75.3834f,6.14356f}, + {-892.408f,-162.525f,64.1212f,2.69884f}, + {-892.326f,-123.158f,76.0318f,5.5676f}, + {-888.468f,-148.462f,61.8012f,1.65806f}, + {-883.268f,-159.738f,63.5311f,5.20108f}, + {-877.76f,-118.07f,65.215f,2.94961f}, + {-876.792f,-128.646f,64.1045f,3.40339f}, + {-874.901f,-36.6579f,69.4246f,2.00713f}, + {-874.856f,-151.351f,62.7537f,3.57875f}, + {-872.135f,-150.08f,62.7513f,3.57201f}, + {-870.288f,-149.217f,62.5413f,3.56624f}, + {-870.03f,-6.27443f,70.3867f,2.3911f}, + {-869.023f,-82.2118f,69.5848f,3.22886f}, + {-866.354f,-40.2455f,70.842f,0.0698132f}, + {-865.305f,-152.302f,63.5044f,4.86947f}, + {-861.926f,-79.0519f,71.4178f,0.20944f}, + {-857.292f,-152.277f,63.2114f,4.18879f}, + {-853.357f,-0.696194f,72.0655f,0.994838f}, + {-850.685f,-14.2596f,70.2298f,0.20944f}, + {-839.987f,-67.7695f,72.7916f,4.93928f}, + {-839.199f,-57.0558f,73.4891f,1.67552f}, + {-836.963f,-153.224f,63.3821f,4.46804f}, + {-832.721f,-67.7555f,72.9062f,4.99164f}, + {-821.496f,-143.095f,63.1292f,0.541052f}, + {-818.829f,-153.004f,62.1757f,6.12611f}, + //special + {-954.622f,-110.958f,80.7911f,6.24828f}, + {-951.477f,-53.9647f,80.0235f,5.32325f}, + {-946.812f,-126.04f,78.8601f,5.15265f}, + {-940.689f,-140.707f,79.9225f,2.79253f}, + {-933.954f,-159.632f,60.778f,2.56563f}, + {-922.537f,-130.291f,61.3756f,4.95674f}, + {-915.862f,-151.74f,76.9427f,0.942478f}, + {-888.321f,-159.831f,62.5303f,1.20428f}, + {-874.361f,-42.4751f,69.4316f,0.785398f}, + {-873.19f,-50.4899f,70.0568f,-2.41288f}, + {-868.511f,-148.386f,62.3547f,3.57875f}, + {-868.44f,-121.649f,64.5056f,3.33358f}, + {-868.324f,-77.7196f,71.4768f,5.41052f}, + {-859.846f,-19.6549f,70.7304f,1.97222f}, + {-828.05f,-150.508f,62.2019f,2.14675f}, + {-826.254f,-58.6911f,72.0041f,3.68264f}, + {-976.086f,-44.1775f,76.029f,1.46608f}, + {-971.864f,-87.4223f,81.4954f,5.8294f}, + {-966.551f,-74.1111f,80.0243f,4.2129f}, + {-958.509f,-173.652f,77.9013f,6.24828f}, + {-951.511f,-181.242f,65.529f,4.39823f}, + {-940.967f,-186.243f,77.698f,1.28164f}, + {-930.004f,-65.0898f,79.077f,0.0581657f}, + {-920.864f,-40.2009f,78.256f,5.16617f}, + {-919.089f,-148.021f,62.0317f,2.59327f}, + {-901.516f,-116.329f,75.6876f,0.471239f}, + {-897.864f,-84.4348f,74.083f,3.00197f}, + {-897.617f,-52.0457f,71.9503f,4.36332f}, + {-894.891f,-153.951f,61.6827f,3.23569f}, + {-893.933f,-111.625f,75.6591f,4.22536f}, + {-883.265f,-152.854f,61.8384f,0.0941087f}, + {-868.293f,-147.243f,62.1097f,3.2056f}, + {-867.501f,-11.8709f,70.018f,6.14356f}, + {-866.699f,-147.54f,62.1646f,3.57878f}, + {-866.566f,-91.1916f,67.4414f,4.56707f}, + {-857.272f,-141.142f,61.7356f,4.17134f}, + {-847.446f,-98.0061f,68.5131f,3.24631f}, + {-837.026f,-140.729f,62.5141f,5.51524f}, + {-824.204f,-65.053f,72.3381f,3.01942f}, + //vermin (s.th special for this mine) + {-951.955f,-197.5f,77.212f,5.63741f}, + {-944.837f,-199.608f,77.0737f,4.97419f}, + {-933.494f,-209.063f,73.7803f,5.88176f}, + {-929.666f,-201.308f,73.7032f,5.02655f}, + {-978.997f,-249.356f,65.4345f,5.05464f}, + {-974.565f,-224.828f,69.5858f,4.88846f}, + {-946.514f,-259.239f,66.0874f,3.78132f}, + {-918.402f,-250.439f,69.5271f,2.21352f}, + {-910.14f,-229.959f,72.9279f,0.27677f}, + {-851.563f,-88.6527f,68.5983f,3.61896f}, + //boss + {-848.902f,-92.931f,68.6325f,3.33350}, + //herald + {-48.459f,-288.802f,55.47f,1.0}, + //triggers + {637.083,-32.6603,45.9715,1.14353}, //firstaid_station + {669.007f,-294.078f,30.2909f,2.77507f}, //stormpike_grave + {77.8013f,-404.7f,46.7549f,-0.872665f}, //stoneheart_grave + {-202.581f,-112.73f,78.4876f,-0.715585f}, //snowfall_grave + {-611.962f,-396.17f,60.8351f,2.53682f}, //iceblood_grave + {-1082.45f,-346.823f,54.9219f,-1.53589f}, //frostwolf_grave + {-1402.21f,-307.431f,89.4424f,0.191986f}, //frostwolf_hut + {553.779f,-78.6566f,51.9378f,-1.22173f}, //dunbaldar_south + {674.001f,-143.125f,63.6615f,0.994838f}, //dunbaldar_north + {203.281f,-360.366f,56.3869f,-0.925024}, //icewing_bunker + {-152.437f,-441.758f,40.3982f,-1.95477f}, //stoneheart_bunker + {-571.88f,-262.777f,75.0087f,-0.802851f}, //iceblood_tower + {-768.907f,-363.71f,90.8949f,1.07991f}, //tower_point + {-1302.9f,-316.981f,113.867f,2.00713f}, //frostwolf_etower + {-1297.5f,-266.767f,114.15f,3.31044f}, //frostwolf_wtower + {-57.7891f,-286.597f,15.6479f,6.02139f}, //AV_NPC_A_CAPTAIN balinda + {722.43f,-10.9982f,50.7046f,3.42085f}, //AV_NPC_A_BOSS vanndar + {-545.23f,-165.35f,57.7886f,3.01145f}, //AV_NPC_H_CAPTAIN galvangar + {-1370.9f,-219.793f,98.4258f,5.04381f} //AV_NPC_H_BOSS drek thar +}; + +enum BG_AV_CreatureIds +{ + + AV_NPC_A_GRAVEDEFENSE0 = 0, // stormpike Defender + AV_NPC_A_GRAVEDEFENSE1 = 1, // seasoned defender + AV_NPC_A_GRAVEDEFENSE2 = 2, // veteran defender + AV_NPC_A_GRAVEDEFENSE3 = 3, // champion defender + AV_NPC_A_TOWERDEFENSE = 4, // stormpike bowman + AV_NPC_A_CAPTAIN = 5, // balinda + AV_NPC_A_BOSS = 6, // vanndar + + AV_NPC_H_GRAVEDEFENSE0 = 7, // frostwolf guardian + AV_NPC_H_GRAVEDEFENSE1 = 8, // seasoned guardian + AV_NPC_H_GRAVEDEFENSE2 = 9, // veteran guardian + AV_NPC_H_GRAVEDEFENSE3 = 10, // champion guardian + AV_NPC_H_TOWERDEFENSE = 11, // frostwolf bowman + AV_NPC_H_CAPTAIN = 12, // galvangar + AV_NPC_H_BOSS = 13, // drek thar + + AV_NPC_A_MARSHAL_SOUTH = 14, + AV_NPC_MARSHAL_NORTH = 15, + AV_NPC_A_MARSHAL_ICE = 16, + AV_NPC_A_MARSHAL_STONE = 17, + AV_NPC_H_MARSHAL_ICE = 18, + AV_NPC_H_MARSHAL_TOWER = 19, + AV_NPC_MARSHAL_ETOWER = 20, + AV_NPC_H_MARSHAL_WTOWER= 21, + AV_NPC_N_MINE_N_1 = 22, + AV_NPC_N_MINE_N_2 = 23, + AV_NPC_N_MINE_N_3 = 24, + AV_NPC_N_MINE_N_4 = 25, + AV_NPC_N_MINE_A_1 = 26, + AV_NPC_N_MINE_A_2 = 27, + AV_NPC_N_MINE_A_3 = 28, + AV_NPC_N_MINE_A_4 = 29, + AV_NPC_N_MINE_H_1 = 30, + AV_NPC_N_MINE_H_2 = 31, + AV_NPC_N_MINE_H_3 = 32, + AV_NPC_N_MINE_H_4 = 33, + AV_NPC_S_MINE_N_1 = 34, + AV_NPC_S_MINE_N_2 = 35, + AV_NPC_S_MINE_N_3 = 36, + AV_NPC_S_MINE_N_4 = 37, + AV_NPC_S_MINE_N_S = 38, + AV_NPC_S_MINE_A_1 = 39, + AV_NPC_S_MINE_A_2 = 40, + AV_NPC_S_MINE_A_3 = 41, + AV_NPC_S_MINE_A_4 = 42, + AV_NPC_S_MINE_H_1 = 43, + AV_NPC_S_MINE_H_2 = 44, + AV_NPC_S_MINE_H_3 = 45, + AV_NPC_S_MINE_H_4 = 46, + AV_NPC_HERALD = 47, + AV_NPC_INFO_MAX = 48 + +}; + +//entry, team, minlevel, maxlevel +//TODO this array should be removed, the only needed things are the entrys (for spawning(?) and handlekillunit) +const uint32 BG_AV_CreatureInfo[AV_NPC_INFO_MAX][4] = { + { 12050, 1216, 58, 58 }, //Stormpike Defender + { 13326, 1216, 59, 59 }, //Seasoned Defender + { 13331, 1216, 60, 60 }, //Veteran Defender + { 13422, 1216, 61, 61 }, //Champion Defender + { 13358, 1216, 59, 60 }, //Stormpike Bowman //i think its 60,61 and 69,70.. but this is until now not possible TODO look if this is ok + { 11949,469,0,0},//not spawned with this data, but used for handlekillunit + { 11948,469,0,0},//not spawned with this data, but used for handlekillunit + { 12053, 1214, 58, 58 }, //Frostwolf Guardian + { 13328, 1214, 59, 59 }, //Seasoned Guardian + { 13332, 1214, 60, 60 }, //Veteran Guardian + { 13421, 1214, 61, 61 }, //Champion Guardian + { 13359, 1214, 59, 60 }, //Frostwolf Bowman + { 11947,67,0,0}, //not spawned with this data, but used for handlekillunit + { 11946,67,0,0}, //not spawned with this data, but used for handlekillunit + { 14763, 1534, 60, 60 }, //Dun Baldar South Marshal + { 14762, 1534, 60, 60 }, //Dun Baldar North Marshal + { 14764, 1534, 60, 60 }, //Icewing Marshal + { 14765, 1534, 60, 60 }, //Stonehearth Marshal + + { 14773, 1214, 60, 60 }, //Iceblood Warmaster + { 14776, 1214, 60, 60 }, //Tower Point Warmaster + { 14772, 1214, 60, 60 }, //East Frostwolf Warmaster + { 14777, 1214, 60, 60 }, //West Frostwolf Warmaster + + { 10987, 59, 52, 53 }, //Irondeep Trogg + { 11600, 59, 53, 54 }, //Irondeep Shaman + { 11602, 59, 54, 55 }, //Irondeep Skullthumper + { 11657, 59, 58, 58 }, //Morloch + + {13396,469,52,53}, //irondeep alliance TODO: get the right ids + {13080,469,53,54}, + {13098,469,54,55}, + {13078,469,58,58}, + + {13397,67,52,53}, //irondeep horde + {13099,67,53,54}, + {13081,67,54,55}, + {13079,67,58,58}, + + { 11603, 59, 52, 53 }, //south mine neutral + { 11604, 59, 53, 54 }, + { 11605, 59, 54, 55 }, + { 11677, 59, 58, 58 }, + { 10982, 59, 52, 53 }, //vermin + + {13317,469,52,53}, //alliance + {13096,469,54,55}, //explorer + {13087,469,54,55}, //invader + {13086,469,58,58}, + + {13316,67,52,53}, //horde + {13097,67,54,55}, //surveypr + {13089,67,54,55}, //guard + {13088,67,58,58}, + {14848,67,58,58} //Herald + +}; + +//x,y,z,o,static_creature_info-id +const float BG_AV_StaticCreaturePos[AV_STATICCPLACE_MAX][5] = { //static creatures + {-1235.31f,-340.777f,60.5088f,3.31613f,0 },//2225 - Zora Guthrek + {-1244.02f,-323.795f,61.0485f,5.21853f,1 },//3343 - Grelkor + {-1235.16f,-332.302f,60.2985f,2.96706f,2 },//3625 - Rarck + {587.303f,-42.8257f,37.5615f,5.23599f,3 },//4255 - Brogus Thunderbrew + {643.635f,-58.3987f,41.7405f,4.72984f,4 },//4257 - Lana Thunderbrew + {591.464f,-44.452f,37.6166f,5.65487f,5 },//5134 - Jonivera Farmountain + {608.515f,-33.3935f,42.0003f,5.41052f,6 },//5135 - Svalbrad Farmountain + {617.656f,-32.0701f,42.7168f,4.06662f,7 },//5139 - Kurdrum Barleybeard + {-1183.76f,-268.295f,72.8233f,3.28122f,8 },//10364 - Yaelika Farclaw + {-1187.86f,-275.31f,73.0481f,3.63028f,9 },//10367 - Shrye Ragefist + {-1008.42f,-368.006f,55.3426f,5.95647f,10 },//10981 - Frostwolf + {-1091.92f,-424.28f,53.0139f,2.93958f,10 },//10981 - Frostwolf + {-558.455f,-198.768f,58.1755f,4.97946f,10 },//10981 - Frostwolf + {-861.247f,-312.51f,55.1427f,3.35382f,10 },//10981 - Frostwolf + {-1003.81f,-395.913f,50.4736f,2.85631f,10 },//10981 - Frostwolf + {-904.5f,-289.815f,65.1222f,5.7847f,10 },//10981 - Frostwolf + {-1064.41f,-438.839f,51.3614f,1.88857f,10 },//10981 - Frostwolf + {258.814f,76.2017f,18.6468f,6.19052f,11 },//10986 - Snowblind Harpy + {265.838f,-315.846f,-16.5429f,3.15917f,11 },//10986 - Snowblind Harpy + {426.485f,-51.1927f,-5.66286f,1.60347f,11 },//10986 - Snowblind Harpy + {452.044f,-33.9594f,-0.044651f,2.72815f,11 },//10986 - Snowblind Harpy + {266.032f,-315.639f,-16.5429f,4.67962f,11 },//10986 - Snowblind Harpy + {532.64f,-54.5863f,20.7024f,2.93215f,11 },//10986 - Snowblind Harpy + {295.183f,-299.908f,-34.6123f,0.135851f,12 },//10990 - Alterac Ram + {421.08f,-225.006f,-23.73f,0.166754f,12 },//10990 - Alterac Ram + {-55.7766f,-192.498f,20.4352f,6.12221f,12 },//10990 - Alterac Ram + {527.887f,-477.223f,62.3559f,0.170935f,12 },//10990 - Alterac Ram + {389.144f,-346.508f,-30.334f,4.14117f,12 },//10990 - Alterac Ram + {108.121f,-322.248f,37.5655f,4.46788f,12 },//10990 - Alterac Ram + {507.479f,-67.9403f,10.3571f,3.26304f,12 },//10990 - Alterac Ram + {329.071f,-185.016f,-29.1542f,0.356943f,12 },//10990 - Alterac Ram + {252.449f,-422.313f,35.1404f,4.53771f,12 },//10990 - Alterac Ram + {358.882f,-118.061f,-24.9119f,2.29257f,12 },//10990 - Alterac Ram + {487.151f,-174.229f,14.7558f,4.73192f,12 },//10990 - Alterac Ram + {449.652f,-123.561f,6.14273f,6.12029f,12 },//10990 - Alterac Ram + {272.419f,-261.802f,-41.8835f,3.66559f,12 },//10990 - Alterac Ram + {359.021f,-210.954f,-29.3483f,4.31339f,12 },//10990 - Alterac Ram + {450.598f,-318.048f,-37.7548f,0.655219f,12 },//10990 - Alterac Ram + {509.333f,-218.2f,3.05439f,3.66292f,12 },//10990 - Alterac Ram + {485.771f,-223.613f,-1.53f,2.04862f,12 },//10990 - Alterac Ram + {486.636f,-452.172f,39.6592f,2.3341f,12 },//10990 - Alterac Ram + {702.783f,-257.494f,25.9777f,1.68329f,12 },//10990 - Alterac Ram + {460.942f,-199.263f,-6.0149f,0.380506f,12 },//10990 - Alterac Ram + {483.108f,-115.307f,10.1056f,3.69701f,12 },//10990 - Alterac Ram + {471.601f,-154.174f,14.0702f,5.5807f,12 },//10990 - Alterac Ram + {213.938f,-420.793f,41.2549f,5.71394f,12 },//10990 - Alterac Ram + {289.387f,-294.685f,-33.9073f,0.555494f,12 },//10990 - Alterac Ram + {155.649f,-402.891f,43.3915f,5.94838f,12 },//10990 - Alterac Ram + {517.184f,-295.105f,-9.78195f,6.05668f,12 },//10990 - Alterac Ram + {102.334f,-332.165f,38.9812f,3.31445f,12 },//10990 - Alterac Ram + {320.244f,-107.793f,-42.6357f,-1.00311f,12 },//10990 - Alterac Ram + {217.976f,110.774f,15.7603f,4.56793f,13 },//11675 - Snowblind Windcaller + {269.872f,6.66684f,20.7592f,0.381212f,13 },//11675 - Snowblind Windcaller + {313.528f,-319.041f,-27.2373f,0.554098f,13 },//11675 - Snowblind Windcaller + {435.441f,-39.9289f,-0.169651f,0.549454f,13 },//11675 - Snowblind Windcaller + {315.115f,-317.62f,-29.1123f,0.90111f,13 },//11675 - Snowblind Windcaller + {428.091f,-122.731f,3.40332f,6.05901f,14 },//11678 - Snowblind Ambusher + {235.05f,85.5705f,18.3079f,-0.914255f,14 },//11678 - Snowblind Ambusher + {-1553.04f,-344.342f,64.4163f,6.09933f,15 },//11839 - Wildpaw Brute + {-545.23f,-165.35f,57.7886f,3.01145f,16 },//11947 - Captain Galvangar + {722.43f,-10.9982f,50.7046f,3.42085f,17 },//11948 - Vanndar Stormpike + {-57.7891f,-286.597f,15.6479f,6.02139f,18 },//11949 - Captain Balinda Stonehearth + {930.498f,-520.755f,93.7334f,1.8326f,19 },//11997 - Stormpike Herald + {-776.092f,-345.161f,67.4092f,1.89257f,20 },//12051 - Frostwolf Legionnaire + {-1224.63f,-308.144f,65.0087f,4.01139f,20 },//12051 - Frostwolf Legionnaire + {-713.039f,-442.515f,82.8638f,0.68724f,20 },//12051 - Frostwolf Legionnaire + {-711.783f,-444.061f,82.7039f,0.683494f,20 },//12051 - Frostwolf Legionnaire + {587.633f,-45.9816f,37.5438f,5.81195f,21 },//12096 - Stormpike Quartermaster + {-1293.79f,-194.407f,72.4398f,5.84685f,22 },//12097 - Frostwolf Quartermaster + {446.163f,-377.119f,-1.12725f,0.209526f,23 },//12127 - Stormpike Guardsman + {549.348f,-399.254f,53.3537f,3.24729f,23 },//12127 - Stormpike Guardsman + {549.801f,-401.217f,53.8305f,3.24729f,23 },//12127 - Stormpike Guardsman + {192.704f,-406.874f,42.9183f,6.10696f,23 },//12127 - Stormpike Guardsman + {441.305f,-435.765f,28.2385f,2.14472f,23 },//12127 - Stormpike Guardsman + {192.982f,-404.891f,43.0132f,6.1061f,23 },//12127 - Stormpike Guardsman + {355.342f,-391.989f,-0.486707f,3.00643f,23 },//12127 - Stormpike Guardsman + {446.035f,-375.104f,-1.12725f,0.21033f,23 },//12127 - Stormpike Guardsman + {697.864f,-433.238f,62.7914f,1.65776f,23 },//12127 - Stormpike Guardsman + {610.74f,-331.585f,30.8021f,5.14253f,23 },//12127 - Stormpike Guardsman + {609.815f,-329.775f,30.9271f,-2.38829f,23 },//12127 - Stormpike Guardsman + {695.874f,-433.434f,62.8543f,1.65776f,23 },//12127 - Stormpike Guardsman + {443.337f,-435.283f,28.6842f,2.13768f,23 },//12127 - Stormpike Guardsman + {-1251.5f,-316.327f,62.6565f,5.02655f,24 },//13176 - Smith Regzar + {-1332.0f,-331.243f,91.2631f,1.50098f,25 },//13179 - Wing Commander Guse + {569.983f,-94.9992f,38.0325f,1.39626f,26 },//13216 - Gaelden Hammersmith + {-1244.92f,-308.916f,63.2525f,1.62316f,27 },//13218 - Grunnda Wolfheart + {-1319.56f,-342.675f,60.3404f,1.20428f,28 },//13236 - Primalist Thurloga + {647.61f,-61.1548f,41.7405f,4.24115f,29 },//13257 - Murgot Deepforge + {-1321.64f,-343.73f,60.4833f,1.01229f,30 },//13284 - Frostwolf Shaman + {-1317.61f,-342.853f,60.3726f,2.47837f,30 },//13284 - Frostwolf Shaman + {-1319.31f,-344.475f,60.3825f,1.72788f,30 },//13284 - Frostwolf Shaman + {569.963f,-42.0218f,37.7581f,4.27606f,31 },//13438 - Wing Commander Slidore + {729.2f,-78.812f,51.6335f,3.97935f,32 },//13442 - Arch Druid Renferal + {729.118f,-82.8713f,51.6335f,2.53073f,33 },//13443 - Druid of the Grove + {725.554f,-79.4973f,51.6335f,5.27089f,33 },//13443 - Druid of the Grove + {724.768f,-84.1642f,51.6335f,0.733038f,33 },//13443 - Druid of the Grove + {596.68f,-83.0633f,39.0051f,6.24828f,34 },//13447 - Corporal Noreg Stormpike + {600.032f,-2.92475f,42.0788f,5.00909f,35 },//13577 - Stormpike Ram Rider Commander + {610.239f,-21.8454f,43.272f,4.90438f,36 },//13617 - Stormpike Stable Master + {613.422f,-150.764f,33.4517f,5.55015f,37 },//13797 - Mountaineer Boombellow + {-1213.91f,-370.619f,56.4455f,0.837758f,38 },//13798 - Jotek + {704.35f,-22.9071f,50.2187f,0.785398f,39 },//13816 - Prospector Stonehewer + {-1271.24f,-335.766f,62.3971f,5.75959f,40 },//14185 - Najak Hexxen + {-1268.64f,-332.688f,62.6171f,5.28835f,41 },//14186 - Ravak Grimtotem + {648.363f,-65.2233f,41.7405f,3.12414f,42 },//14187 - Athramanis + {648.238f,-67.8931f,41.7405f,2.60054f,43 },//14188 - Dirk Swindle + {-1223.44f,-309.833f,64.9331f,4.0131f,44 },//14282 - Frostwolf Bloodhound + {-1226.4f,-307.136f,64.9706f,4.0145f,44 },//14282 - Frostwolf Bloodhound + {356.001f,-389.969f,-0.438796f,3.0334f,45 },//14283 - Stormpike Owl + {355.835f,-394.005f,-0.60149f,3.02498f,45 },//14283 - Stormpike Owl + {882.266f,-496.378f,96.7707f,4.83248f,45 },//14283 - Stormpike Owl + {878.649f,-495.917f,96.6171f,4.67693f,45 },//14283 - Stormpike Owl + {932.851f,-511.017f,93.6748f,3.61004f,45 },//14283 - Stormpike Owl + {935.806f,-513.983f,93.7436f,3.61788f,45 },//14283 - Stormpike Owl + {947.412f,-509.982f,95.1098f,2.82743f,46 },//14284 - Stormpike Battleguard + {934.557f,-512.395f,93.662f,3.61004f,46 },//14284 - Stormpike Battleguard + {939.42f,-502.777f,94.5887f,5.14872f,46 },//14284 - Stormpike Battleguard + {854.276f,-494.241f,96.8017f,5.44543f,46 },//14284 - Stormpike Battleguard + {776.621f,-487.775f,99.4049f,3.50811f,46 },//14284 - Stormpike Battleguard + {880.169f,-495.699f,96.6204f,4.8325f,46 },//14284 - Stormpike Battleguard + {773.651f,-497.482f,99.0408f,2.11185f,46 },//14284 - Stormpike Battleguard + {949.1f,-506.913f,95.4237f,3.31613f,46 },//14284 - Stormpike Battleguard + {-1370.9f,-219.793f,98.4258f,5.04381f,47}, //drek thar + +}; + +const uint32 BG_AV_StaticCreatureInfo[51][4] = { + { 2225, 1215, 55, 55 }, //Zora Guthrek + { 3343, 1215, 55, 55 }, //Grelkor + { 3625, 1215, 55, 55 }, //Rarck + { 4255, 1217, 55, 55 }, //Brogus Thunderbrew + { 4257, 1217, 55, 55 }, //Lana Thunderbrew + { 5134, 1217, 55, 55 }, //Jonivera Farmountain + { 5135, 1217, 55, 55 }, //Svalbrad Farmountain + { 5139, 1217, 55, 55 }, //Kurdrum Barleybeard + { 10364, 1215, 55, 55 }, //Yaelika Farclaw + { 10367, 1215, 55, 55 }, //Shrye Ragefist + { 10981, 38, 50, 51 }, //Frostwolf + { 10986, 514, 52, 53 }, //Snowblind Harpy + { 10990, 1274, 50, 51 }, //Alterac Ram + { 11675, 514, 53, 53 }, //Snowblind Windcaller + { 11678, 14, 52, 53 }, //Snowblind Ambusher + { 11839, 39, 56, 56 }, //Wildpaw Brute + { 11947, 1214, 61, 61 }, //Captain Galvangar --TODO: doubled + { 11948, 1216, 63, 63 }, //Vanndar Stormpike + { 11949, 1216, 61, 61 }, //Captain Balinda Stonehearth + { 11997, 1334, 60, 60 }, //Stormpike Herald + { 12051, 1214, 57, 57 }, //Frostwolf Legionnaire + { 12096, 1217, 55, 55 }, //Stormpike Quartermaster + { 12097, 1215, 55, 55 }, //Frostwolf Quartermaster + { 12127, 1216, 57, 57 }, //Stormpike Guardsman + { 13176, 1215, 60, 60 }, //Smith Regzar + { 13179, 1215, 59, 59 }, //Wing Commander Guse + { 13216, 1217, 58, 58 }, //Gaelden Hammersmith + { 13218, 1215, 58, 58 }, //Grunnda Wolfheart + { 13236, 1214, 60, 60 }, //Primalist Thurloga + { 13257, 1216, 60, 60 }, //Murgot Deepforge + { 13284, 1214, 58, 58 }, //Frostwolf Shaman + { 13438, 1217, 58, 58 }, //Wing Commander Slidore + { 13442, 1216, 60, 60 }, //Arch Druid Renferal + { 13443, 1216, 60, 60 }, //Druid of the Grove + { 13447, 1216, 58, 58 }, //Corporal Noreg Stormpike + { 13577, 1216, 60, 60 }, //Stormpike Ram Rider Commander + { 13617, 1216, 60, 60 }, //Stormpike Stable Master + { 13797, 32, 60, 61 }, //Mountaineer Boombellow + { 13798, 1214, 60, 61 }, //Jotek + { 13816, 1216, 61, 61 }, //Prospector Stonehewer + { 14185, 877, 59, 59 }, //Najak Hexxen + { 14186, 105, 60, 60 }, //Ravak Grimtotem + { 14187, 1594, 60, 60 }, //Athramanis + { 14188, 57, 59, 59 }, //Dirk Swindle + { 14282, 1214, 53, 54 }, //Frostwolf Bloodhound + { 14283, 1216, 53, 54 }, //Stormpike Owl + { 14284, 1216, 61, 61 }, //Stormpike Battleguard + { 11946, 1214, 63, 63 }, //Drek'Thar //TODO: make the levels right (boss=0 maybe) + { 11948, 1216, 63, 63 }, //Vanndar Stormpike + { 11947, 1214, 61, 61 }, //Captain Galvangar + { 11949, 1216, 61, 61 } //Captain Balinda Stonehearth +}; + +enum BG_AV_Graveyards +{ + AV_GRAVE_STORM_AID = 751, + AV_GRAVE_STORM_GRAVE = 689, + AV_GRAVE_STONE_GRAVE = 729, + AV_GRAVE_SNOWFALL = 169, + AV_GRAVE_ICE_GRAVE = 749, + AV_GRAVE_FROSTWOLF = 690, + AV_GRAVE_FROST_HUT = 750, + AV_GRAVE_MAIN_ALLIANCE = 611, + AV_GRAVE_MAIN_HORDE = 610 +}; + +const uint32 BG_AV_GraveyardIds[9]= { + AV_GRAVE_STORM_AID, + AV_GRAVE_STORM_GRAVE, + AV_GRAVE_STONE_GRAVE, + AV_GRAVE_SNOWFALL, + AV_GRAVE_ICE_GRAVE, + AV_GRAVE_FROSTWOLF, + AV_GRAVE_FROST_HUT, + AV_GRAVE_MAIN_ALLIANCE, + AV_GRAVE_MAIN_HORDE +}; + +enum BG_AV_BUFF +{ //TODO add all other buffs here + AV_BUFF_ARMOR = 21163, + AV_BUFF_A_CAPTAIN = 23693, //the buff which the alliance captain does + AV_BUFF_H_CAPTAIN = 22751 //the buff which the horde captain does +}; +enum BG_AV_States +{ + POINT_NEUTRAL = 0, + POINT_ASSAULTED = 1, + POINT_DESTROYED = 2, + POINT_CONTROLED = 3 +}; + +enum BG_AV_WorldStates +{ + AV_Alliance_Score = 3127, + AV_Horde_Score = 3128, + AV_SHOW_H_SCORE = 3133, + AV_SHOW_A_SCORE = 3134, + +/* + //the comments behind the state shows which icon overlaps the other.. but is, until now, unused and maybe not a good solution (but give few performance (:) + +// Graves + + // Alliance + //Stormpike first aid station + AV_AID_A_C = 1325, + AV_AID_A_A = 1326, + AV_AID_H_C = 1327, + AV_AID_H_A = 1328, + //Stormpike Graveyard + AV_PIKEGRAVE_A_C = 1333, + AV_PIKEGRAVE_A_A = 1335, + AV_PIKEGRAVE_H_C = 1334, + AV_PIKEGRAVE_H_A = 1336, + //Stoneheart Grave + AV_STONEHEART_A_C = 1302, + AV_STONEHEART_A_A = 1304, //over hc + AV_STONEHEART_H_C = 1301, //over ac + AV_STONEHEART_H_A = 1303, //over aa + //Neutral + //Snowfall Grave +*/ + AV_SNOWFALL_N = 1966, //over aa +/* + AV_SNOWFALL_A_C = 1341, //over hc + AV_SNOWFALL_A_A = 1343, //over ha + AV_SNOWFALL_H_C = 1342, + AV_SNOWFALL_H_A = 1344, //over ac + //Horde + //Iceblood grave + AV_ICEBLOOD_A_C = 1346, //over hc + AV_ICEBLOOD_A_A = 1348, //over ac + AV_ICEBLOOD_H_C = 1347, + AV_ICEBLOOD_H_A = 1349, //over aa + //Frostwolf Grave + AV_FROSTWOLF_A_C = 1337, //over hc + AV_FROSTWOLF_A_A = 1339, //over ac + AV_FROSTWOLF_H_C = 1338, + AV_FROSTWOLF_H_A = 1340, //over aa + //Frostwolf Hut + AV_FROSTWOLFHUT_A_C = 1329, //over hc + AV_FROSTWOLFHUT_A_A = 1331, //over ha + AV_FROSTWOLFHUT_H_C = 1330, + AV_FROSTWOLFHUT_H_A = 1332, //over ac + +//Towers + //Alliance + //Dunbaldar South Bunker + AV_DUNS_CONTROLLED = 1361, + AV_DUNS_DESTROYED = 1370, + AV_DUNS_ASSAULTED = 1378, + //Dunbaldar North Bunker + AV_DUNN_CONTROLLED = 1362, + AV_DUNN_DESTROYED = 1371, + AV_DUNN_ASSAULTED = 1379, + //Icewing Bunker + AV_ICEWING_CONTROLLED = 1363, + AV_ICEWING_DESTROYED = 1372, + AV_ICEWING_ASSAULTED = 1380, + //Stoneheart Bunker + AV_STONEH_CONTROLLED = 1364, + AV_STONEH_DESTROYED = 1373, + AV_STONEH_ASSAULTED = 1381, + //Horde + //Iceblood Tower + AV_ICEBLOOD_CONTROLLED = 1385, + AV_ICEBLOOD_DESTROYED = 1368, + AV_ICEBLOOD_ASSAULTED = 1390, + //Tower Point + AV_TOWERPOINT_CONTROLLED = 1384, + AV_TOWERPOINT_DESTROYED = 1367, //goes over controlled + AV_TOWERPOINT_ASSAULTED = 1389, //goes over destroyed + //Frostwolf West + AV_FROSTWOLFW_CONTROLLED = 1382, + AV_FROSTWOLFW_DESTROYED = 1365, //over controlled + AV_FROSTWOLFW_ASSAULTED = 1387, //over destroyed + //Frostwolf East + AV_FROSTWOLFE_CONTROLLED = 1383, + AV_FROSTWOLFE_DESTROYED = 1366, + AV_FROSTWOLFE_ASSAULTED = 1388, + +//mines + + AV_N_MINE_N = 1360, + AV_N_MINE_A = 1358, + AV_N_MINE_H = 1359, + + AV_S_MINE_N = 1357, + AV_S_MINE_A = 1355, + AV_S_MINE_H = 1356, + +//towers assaulted by own team (unused) + AV_STONEH_UNUSED = 1377, + AV_ICEWING_UNUSED = 1376, + AV_DUNS_UNUSED = 1375, + AV_DUNN_UNUSED = 1374, + + AV_ICEBLOOD_UNUSED = 1395, + AV_TOWERPOINT_UNUSED = 1394, + AV_FROSTWOLFE_UNUSED = 1393, + AV_FROSTWOLFW_UNUSED = 1392 +*/ + +}; + +//alliance_control neutral_control horde_control +const uint32 BG_AV_MineWorldStates[2][3] = { + {1358, 1360,1359}, + {1355, 1357,1356} +}; + +//alliance_control alliance_assault h_control h_assault +const uint32 BG_AV_NodeWorldStates[16][4] = { + //Stormpike first aid station + {1325, 1326,1327,1328}, + //Stormpike Graveyard + {1333,1335,1334,1336}, + //Stoneheart Grave + {1302,1304,1301,1303}, + //Snowfall Grave + {1341,1343,1342,1344}, + //Iceblood grave + {1346,1348,1347,1349}, + //Frostwolf Grave + {1337,1339,1338,1340}, + //Frostwolf Hut + {1329,1331,1330,1332}, + //Dunbaldar South Bunker + {1361,1375,1370,1378}, + //Dunbaldar North Bunker + {1362,1374,1371,1379}, + //Icewing Bunker + {1363,1376,1372,1380}, + //Stoneheart Bunker + {1364,1377,1373,1381}, + //Iceblood Tower + {1368,1390,1385,1395}, + //Tower Point + {1367,1389,1384,1394}, + //Frostwolf East + {1366,1388,1383,1393}, + //Frostwolf West + {1365,1387,1382,1392}, +}; + +enum BG_AV_QuestIds +{ + AV_QUEST_A_SCRAPS1 = 7223, + AV_QUEST_A_SCRAPS2 = 6781, + AV_QUEST_H_SCRAPS1 = 7224, + AV_QUEST_H_SCRAPS2 = 6741, + AV_QUEST_A_COMMANDER1 = 6942, //soldier + AV_QUEST_H_COMMANDER1 = 6825, + AV_QUEST_A_COMMANDER2 = 6941, //leutnant + AV_QUEST_H_COMMANDER2 = 6826, + AV_QUEST_A_COMMANDER3 = 6943, //commander + AV_QUEST_H_COMMANDER3 = 6827, + AV_QUEST_A_BOSS1 = 7386, // 5 cristal/blood + AV_QUEST_H_BOSS1 = 7385, + AV_QUEST_A_BOSS2 = 6881, // 1 + AV_QUEST_H_BOSS2 = 6801, + AV_QUEST_A_NEAR_MINE = 5892, //the mine near start location of team + AV_QUEST_H_NEAR_MINE = 5893, + AV_QUEST_A_OTHER_MINE = 6982, //the other mine ;) + AV_QUEST_H_OTHER_MINE = 6985, + AV_QUEST_A_RIDER_HIDE = 7026, + AV_QUEST_H_RIDER_HIDE = 7002, + AV_QUEST_A_RIDER_TAME = 7027, + AV_QUEST_H_RIDER_TAME = 7001 +}; + +struct BG_AV_NodeInfo +{ + uint16 TotalOwner; + uint16 Owner; + uint16 PrevOwner; + BG_AV_States State; + BG_AV_States PrevState; + int Timer; + bool Tower; +}; + +inline BG_AV_Nodes &operator++(BG_AV_Nodes &i){ return i = BG_AV_Nodes(i + 1); } + +class BattleGroundAVScore : public BattleGroundScore +{ + public: + BattleGroundAVScore() : GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), TowersDefended(0), MinesCaptured(0), LeadersKilled(0), SecondaryObjectives(0) {}; + virtual ~BattleGroundAVScore() {}; + uint32 GraveyardsAssaulted; + uint32 GraveyardsDefended; + uint32 TowersAssaulted; + uint32 TowersDefended; + uint32 MinesCaptured; + uint32 LeadersKilled; + uint32 SecondaryObjectives; +}; + +class BattleGroundAV : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundAV(); + ~BattleGroundAV(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + virtual void ResetBGSubclass(); + + /*general stuff*/ + void UpdateScore(uint16 team, int16 points); + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + + /*handlestuff*/ //these are functions which get called from extern + virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj); + void HandleKillPlayer(Player* player, Player *killer); + void HandleKillUnit(Creature *unit, Player *killer); + void HandleQuestComplete(uint32 questid, Player *player); + bool PlayerCanDoMineQuest(int32 GOId,uint32 team); + + void EndBattleGround(uint32 winner); + + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); + + private: + /* Nodes occupying */ + void EventPlayerAssaultsPoint(Player* player, uint32 object); + void EventPlayerDefendsPoint(Player* player, uint32 object); + void EventPlayerDestroyedPoint(BG_AV_Nodes node); + + void AssaultNode(BG_AV_Nodes node,uint16 team); + void DestroyNode(BG_AV_Nodes node); + void InitNode(BG_AV_Nodes node, uint16 team, bool tower); + void DefendNode(BG_AV_Nodes node, uint16 team); + + void PopulateNode(BG_AV_Nodes node); + void DePopulateNode(BG_AV_Nodes node); + + const BG_AV_Nodes GetNodeThroughObject(uint32 object); + const uint32 GetObjectThroughNode(BG_AV_Nodes node); + const char* GetNodeName(BG_AV_Nodes node); + const bool IsTower(BG_AV_Nodes node) { return m_Nodes[node].Tower; } + + /*mine*/ + void ChangeMineOwner(uint8 mine, uint32 team, bool initial=false); + + /*worldstates*/ + void FillInitialWorldStates(WorldPacket& data); + const uint8 GetWorldStateType(uint8 state, uint16 team); + void SendMineWorldStates(uint32 mine); + void UpdateNodeWorldState(BG_AV_Nodes node); + + /*general */ + Creature* AddAVCreature(uint16 cinfoid, uint16 type); + const uint16 GetBonusHonor(uint8 kills); //TODO remove this when the core handles this right + + /*variables */ + int32 m_Team_Scores[2]; + uint32 m_Team_QuestStatus[2][9]; //[x][y] x=team y=questcounter + + BG_AV_NodeInfo m_Nodes[BG_AV_NODES_MAX]; + + uint32 m_Mine_Owner[2]; + uint32 m_Mine_PrevOwner[2]; //only for worldstates needed + int32 m_Mine_Timer; //ticks for both teams + uint32 m_Mine_Reclaim_Timer[2]; + uint32 m_CaptainBuffTimer[2]; + bool m_CaptainAlive[2]; + + uint8 m_MaxLevel; //TODO remove this when battleground-getmaxlevel() returns something usefull + bool m_IsInformedNearVictory[2]; + +}; + +#endif + diff --git a/src/server/game/BattleGrounds/BattleGroundBE.cpp b/src/server/game/BattleGrounds/BattleGroundBE.cpp new file mode 100644 index 00000000000..d6debe45ae3 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundBE.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundBE.h" +#include "Language.h" +#include "Object.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "WorldPacket.h" + +BattleGroundBE::BattleGroundBE() +{ + m_BgObjects.resize(BG_BE_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundBE::~BattleGroundBE() +{ + +} + +void BattleGroundBE::Update(uint32 diff) +{ + BattleGround::Update(diff); + + /*if (GetStatus() == STATUS_IN_PROGRESS) + { + // update something + }*/ +} + +void BattleGroundBE::StartingEventCloseDoors() +{ + for (uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + + for (uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; ++i) + SpawnBGObject(i, RESPAWN_ONE_DAY); +} + +void BattleGroundBE::StartingEventOpenDoors() +{ + for (uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_2; ++i) + DoorOpen(i); + + for (uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; ++i) + SpawnBGObject(i, 60); +} + +void BattleGroundBE::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundBEScore* sc = new BattleGroundBEScore; + + m_PlayerScores[plr->GetGUID()] = sc; + + UpdateArenaWorldState(); +} + +void BattleGroundBE::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player,killer); + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +bool BattleGroundBE::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),6238.930176,262.963470,0.889519,player->GetOrientation(),false); + return true; +} + +void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 4538: // buff trigger? + //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_1]; + break; + case 4539: // buff trigger? + //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_2]; + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if (buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +void BattleGroundBE::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0x9f3) << uint32(1); // 9 + UpdateArenaWorldState(); +} + +void BattleGroundBE::Reset() +{ + //call parent's class reset + BattleGround::Reset(); +} + +bool BattleGroundBE::SetupBattleGround() +{ + // gates + if (!AddObject(BG_BE_OBJECT_DOOR_1, BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_BE_OBJECT_DOOR_2, BG_BE_OBJECT_TYPE_DOOR_2, 6189.546f, 241.7099f, 3.101481f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_BE_OBJECT_DOOR_3, BG_BE_OBJECT_TYPE_DOOR_3, 6299.116f, 296.5494f, 3.308032f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_BE_OBJECT_DOOR_4, BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_BE_OBJECT_BUFF_1, BG_BE_OBJECT_TYPE_BUFF_1, 6249.042f, 275.3239f, 11.22033f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120) + || !AddObject(BG_BE_OBJECT_BUFF_2, BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120)) + { + sLog.outErrorDb("BatteGroundBE: Failed to spawn some object!"); + return false; + } + + return true; +} + +void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +{ + + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); + if (itr == m_PlayerScores.end()) // player not found... + return; + + //there is nothing special in this score + BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); + +} + +/* +21:45:46 id:231310 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86 +0000: 32 02 00 00 76 0e 00 00 00 00 00 00 09 00 f3 09 | 2...v........... +0010: 00 00 01 00 00 00 f1 09 00 00 01 00 00 00 f0 09 | ................ +0020: 00 00 02 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................ +0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................ +0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................ +0050: 00 00 00 00 00 00 | ...... + +spell 32724 - Gold Team +spell 32725 - Green Team +35774 Gold Team +35775 Green Team +*/ diff --git a/src/server/game/BattleGrounds/BattleGroundBE.h b/src/server/game/BattleGrounds/BattleGroundBE.h new file mode 100644 index 00000000000..760d4d278c9 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundBE.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDBE_H +#define __BATTLEGROUNDBE_H + +class BattleGround; + +enum BattleGroundBEObjectTypes +{ + BG_BE_OBJECT_DOOR_1 = 0, + BG_BE_OBJECT_DOOR_2 = 1, + BG_BE_OBJECT_DOOR_3 = 2, + BG_BE_OBJECT_DOOR_4 = 3, + BG_BE_OBJECT_BUFF_1 = 4, + BG_BE_OBJECT_BUFF_2 = 5, + BG_BE_OBJECT_MAX = 6 +}; + +enum BattleGroundBEObjects +{ + BG_BE_OBJECT_TYPE_DOOR_1 = 183971, + BG_BE_OBJECT_TYPE_DOOR_2 = 183973, + BG_BE_OBJECT_TYPE_DOOR_3 = 183970, + BG_BE_OBJECT_TYPE_DOOR_4 = 183972, + BG_BE_OBJECT_TYPE_BUFF_1 = 184663, + BG_BE_OBJECT_TYPE_BUFF_2 = 184664 +}; + +class BattleGroundBEScore : public BattleGroundScore +{ + public: + BattleGroundBEScore() {}; + virtual ~BattleGroundBEScore() {}; +}; + +class BattleGroundBE : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundBE(); + ~BattleGroundBE(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d); + void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundDS.cpp b/src/server/game/BattleGrounds/BattleGroundDS.cpp new file mode 100644 index 00000000000..9036ef83f93 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundDS.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundDS.h" +#include "Language.h" +#include "Player.h" +#include "Object.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" + +BattleGroundDS::BattleGroundDS() +{ + m_BgObjects.resize(BG_DS_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundDS::~BattleGroundDS() +{ + +} + +void BattleGroundDS::Update(uint32 diff) +{ + BattleGround::Update(diff); + if (getWaterFallTimer() < diff) + { + if (isWaterFallActive()) + { + setWaterFallTimer(urand(BG_DS_WATERFALL_TIMER_MIN, BG_DS_WATERFALL_TIMER_MAX)); + for (uint32 i = BG_DS_OBJECT_WATER_1; i <= BG_DS_OBJECT_WATER_2; ++i) + SpawnBGObject(i, getWaterFallTimer()); + setWaterFallActive(false); + } + else + { + setWaterFallTimer(BG_DS_WATERFALL_DURATION); + for (uint32 i = BG_DS_OBJECT_WATER_1; i <= BG_DS_OBJECT_WATER_2; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + setWaterFallActive(true); + } + } + else + setWaterFallTimer(getWaterFallTimer() - diff); +} + +void BattleGroundDS::StartingEventCloseDoors() +{ + for (uint32 i = BG_DS_OBJECT_DOOR_1; i <= BG_DS_OBJECT_DOOR_2; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + +void BattleGroundDS::StartingEventOpenDoors() +{ + for (uint32 i = BG_DS_OBJECT_DOOR_1; i <= BG_DS_OBJECT_DOOR_2; ++i) + DoorOpen(i); + + for (uint32 i = BG_DS_OBJECT_BUFF_1; i <= BG_DS_OBJECT_BUFF_2; ++i) + SpawnBGObject(i, 60); + + setWaterFallTimer(urand(BG_DS_WATERFALL_TIMER_MIN, BG_DS_WATERFALL_TIMER_MAX)); + setWaterFallActive(false); + + for (uint32 i = BG_DS_OBJECT_WATER_1; i <= BG_DS_OBJECT_WATER_2; ++i) + SpawnBGObject(i, getWaterFallTimer()); +} + +void BattleGroundDS::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundDSScore* sc = new BattleGroundDSScore; + + m_PlayerScores[plr->GetGUID()] = sc; + + UpdateArenaWorldState(); +} + +void BattleGroundDS::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +void BattleGroundDS::HandleKillPlayer(Player* player, Player* killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("BattleGroundDS: Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player,killer); + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +void BattleGroundDS::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + switch(Trigger) + { + case 5347: + case 5348: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +bool BattleGroundDS::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(), 1299.046, 784.825, 9.338, 2.422, false); + return true; +} + +void BattleGroundDS::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(3610) << uint32(1); // 9 show + UpdateArenaWorldState(); +} + +void BattleGroundDS::Reset() +{ + //call parent's class reset + BattleGround::Reset(); +} + + +bool BattleGroundDS::SetupBattleGround() +{ + // gates + if (!AddObject(BG_DS_OBJECT_DOOR_1, BG_DS_OBJECT_TYPE_DOOR_1, 1350.95, 817.2, 20.8096, 3.15, 0, 0, 0.99627, 0.0862864, RESPAWN_IMMEDIATELY) + || !AddObject(BG_DS_OBJECT_DOOR_2, BG_DS_OBJECT_TYPE_DOOR_2, 1232.65, 764.913, 20.0729, 6.3, 0, 0, 0.0310211, -0.999519, RESPAWN_IMMEDIATELY) + // water + || !AddObject(BG_DS_OBJECT_WATER_1, BG_DS_OBJECT_TYPE_WATER_1, 1291.56, 790.837, 7.1, 3.14238, 0, 0, 0.694215, -0.719768, 120) + || !AddObject(BG_DS_OBJECT_WATER_2, BG_DS_OBJECT_TYPE_WATER_2, 1291.56, 790.837, 7.1, 3.14238, 0, 0, 0.694215, -0.719768, 120) + // buffs + || !AddObject(BG_DS_OBJECT_BUFF_1, BG_DS_OBJECT_TYPE_BUFF_1, 1291.7, 813.424, 7.11472, 4.64562, 0, 0, 0.730314, -0.683111, 120) + || !AddObject(BG_DS_OBJECT_BUFF_2, BG_DS_OBJECT_TYPE_BUFF_2, 1291.7, 768.911, 7.11472, 1.55194, 0, 0, 0.700409, 0.713742, 120)) + { + sLog.outErrorDb("BatteGroundDS: Failed to spawn some object!"); + return false; + } + + return true; +} diff --git a/src/server/game/BattleGrounds/BattleGroundDS.h b/src/server/game/BattleGrounds/BattleGroundDS.h new file mode 100644 index 00000000000..2ced5c88fd1 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundDS.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDDS_H +#define __BATTLEGROUNDDS_H + +class BattleGround; + +enum BattleGroundDSObjectTypes +{ + BG_DS_OBJECT_DOOR_1 = 0, + BG_DS_OBJECT_DOOR_2 = 1, + BG_DS_OBJECT_WATER_1 = 2, + BG_DS_OBJECT_WATER_2 = 3, + BG_DS_OBJECT_BUFF_1 = 4, + BG_DS_OBJECT_BUFF_2 = 5, + BG_DS_OBJECT_MAX = 6 +}; + +enum BattleGroundDSObjects +{ + BG_DS_OBJECT_TYPE_DOOR_1 = 192642, + BG_DS_OBJECT_TYPE_DOOR_2 = 192643, + BG_DS_OBJECT_TYPE_WATER_1 = 194395, + BG_DS_OBJECT_TYPE_WATER_2 = 191877, + BG_DS_OBJECT_TYPE_BUFF_1 = 184663, + BG_DS_OBJECT_TYPE_BUFF_2 = 184664 +}; + +enum BattleGroundDSData +{ // These values are NOT blizzlike... need the correct data! + BG_DS_WATERFALL_TIMER_MIN = 30000, + BG_DS_WATERFALL_TIMER_MAX = 60000, + BG_DS_WATERFALL_DURATION = 10000, +}; + +class BattleGroundDSScore : public BattleGroundScore +{ + public: + BattleGroundDSScore() {}; + virtual ~BattleGroundDSScore() {}; + //TODO fix me +}; + +class BattleGroundDS : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundDS(); + ~BattleGroundDS(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d); + void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); + private: + uint32 m_waterTimer; + bool m_waterfallActive; + protected: + bool isWaterFallActive() { return m_waterfallActive; }; + void setWaterFallActive(bool active) { m_waterfallActive = active; }; + void setWaterFallTimer(uint32 timer) { m_waterTimer = timer; }; + uint32 getWaterFallTimer() { return m_waterTimer; }; +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundEY.cpp b/src/server/game/BattleGrounds/BattleGroundEY.cpp new file mode 100644 index 00000000000..20f023e4c2a --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundEY.cpp @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "BattleGroundMgr.h" +#include "BattleGround.h" +#include "BattleGroundEY.h" +#include "Creature.h" +#include "Language.h" +#include "Object.h" +#include "Player.h" +#include "Util.h" + +// these variables aren't used outside of this file, so declare them only here +uint32 BG_EY_HonorScoreTicks[BG_HONOR_MODE_NUM] = { + 330, // normal honor + 200 // holiday +}; + +BattleGroundEY::BattleGroundEY() +{ + m_BuffChange = true; + m_BgObjects.resize(BG_EY_OBJECT_MAX); + m_BgCreatures.resize(BG_EY_CREATURES_MAX); + m_Points_Trigger[FEL_REALVER] = TR_FEL_REALVER_BUFF; + m_Points_Trigger[BLOOD_ELF] = TR_BLOOD_ELF_BUFF; + m_Points_Trigger[DRAENEI_RUINS] = TR_DRAENEI_RUINS_BUFF; + m_Points_Trigger[MAGE_TOWER] = TR_MAGE_TOWER_BUFF; + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_EY_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_EY_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_EY_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_EY_HAS_BEGUN; +} + +BattleGroundEY::~BattleGroundEY() +{ +} + +void BattleGroundEY::Update(uint32 diff) +{ + BattleGround::Update(diff); + + if (GetStatus() == STATUS_IN_PROGRESS) + { + m_PointAddingTimer -= diff; + if (m_PointAddingTimer <= 0) + { + m_PointAddingTimer = BG_EY_FPOINTS_TICK_TIME; + if (m_TeamPointsCount[BG_TEAM_ALLIANCE] > 0) + AddPoints(ALLIANCE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_ALLIANCE] - 1]); + if (m_TeamPointsCount[BG_TEAM_HORDE] > 0) + AddPoints(HORDE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_HORDE] - 1]); + } + + if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN || m_FlagState == BG_EY_FLAG_STATE_ON_GROUND) + { + m_FlagsTimer -= diff; + + if (m_FlagsTimer < 0) + { + m_FlagsTimer = 0; + if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN) + RespawnFlag(true); + else + RespawnFlagAfterDrop(); + } + } + + m_TowerCapCheckTimer -= diff; + if (m_TowerCapCheckTimer <= 0) + { + //check if player joined point + /*I used this order of calls, because although we will check if one player is in gameobject's distance 2 times + but we can count of players on current point in CheckSomeoneLeftPoint + */ + this->CheckSomeoneJoinedPoint(); + //check if player left point + this->CheckSomeoneLeftPoint(); + this->UpdatePointStatuses(); + m_TowerCapCheckTimer = BG_EY_FPOINTS_TICK_TIME; + } + } +} + +void BattleGroundEY::StartingEventCloseDoors() +{ + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY); + + for (uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i) + SpawnBGObject(i, RESPAWN_ONE_DAY); +} + +void BattleGroundEY::StartingEventOpenDoors() +{ + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_ONE_DAY); + SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_ONE_DAY); + + for (uint32 i = BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER; i <= BG_EY_OBJECT_FLAG_NETHERSTORM; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + for (uint32 i = 0; i < EY_POINTS_MAX; ++i) + { + //randomly spawn buff + uint8 buff = urand(0, 2); + SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY); + } +} + +void BattleGroundEY::AddPoints(uint32 Team, uint32 Points) +{ + BattleGroundTeamId team_index = GetTeamIndexByTeamId(Team); + m_TeamScores[team_index] += Points; + m_HonorScoreTics[team_index] += Points; + if (m_HonorScoreTics[team_index] >= m_HonorTics) + { + RewardHonorToTeam(GetBonusHonorFromKill(1), Team); + m_HonorScoreTics[team_index] -= m_HonorTics; + } + UpdateTeamScore(Team); +} + +void BattleGroundEY::CheckSomeoneJoinedPoint() +{ + GameObject *obj = NULL; + for (uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + obj = HashMapHolder::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); + if (obj) + { + uint8 j = 0; + while (j < m_PlayersNearPoint[EY_POINTS_MAX].size()) + { + Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]); + if (!plr) + { + sLog.outError("BattleGroundEY:CheckSomeoneJoinedPoint: Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[EY_POINTS_MAX][j])); + ++j; + continue; + } + if (plr->CanCaptureTowerPoint() && plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) + { + //player joined point! + //show progress bar + UpdateWorldStateForPlayer(PROGRESS_BAR_PERCENT_GREY, BG_EY_PROGRESS_BAR_PERCENT_GREY, plr); + UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[i], plr); + UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_SHOW, plr); + //add player to point + m_PlayersNearPoint[i].push_back(m_PlayersNearPoint[EY_POINTS_MAX][j]); + //remove player from "free space" + m_PlayersNearPoint[EY_POINTS_MAX].erase(m_PlayersNearPoint[EY_POINTS_MAX].begin() + j); + } + else + ++j; + } + } + } +} + +void BattleGroundEY::CheckSomeoneLeftPoint() +{ + //reset current point counts + for (uint8 i = 0; i < 2*EY_POINTS_MAX; ++i) + m_CurrentPointPlayersCount[i] = 0; + GameObject *obj = NULL; + for (uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + obj = HashMapHolder::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); + if (obj) + { + uint8 j = 0; + while (j < m_PlayersNearPoint[i].size()) + { + Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[i][j]); + if (!plr) + { + sLog.outError("BattleGroundEY:CheckSomeoneLeftPoint Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[i][j])); + //move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug + m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); + m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); + ++j; + continue; + } + if (!plr->CanCaptureTowerPoint() || !plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) + //move player out of point (add him to players that are out of points + { + m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); + m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); + this->UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_DONT_SHOW, plr); + } + else + { + //player is neat flag, so update count: + m_CurrentPointPlayersCount[2 * i + GetTeamIndexByTeamId(plr->GetTeam())]++; + ++j; + } + } + } + } +} + +void BattleGroundEY::UpdatePointStatuses() +{ + for (uint8 point = 0; point < EY_POINTS_MAX; ++point) + { + if (m_PlayersNearPoint[point].empty()) + continue; + //count new point bar status: + m_PointBarStatus[point] += (m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] < BG_EY_POINT_MAX_CAPTURERS_COUNT) ? m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] : BG_EY_POINT_MAX_CAPTURERS_COUNT; + + if (m_PointBarStatus[point] > BG_EY_PROGRESS_BAR_ALI_CONTROLLED) + //point is fully alliance's + m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_ALI_CONTROLLED; + if (m_PointBarStatus[point] < BG_EY_PROGRESS_BAR_HORDE_CONTROLLED) + //point is fully horde's + m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_HORDE_CONTROLLED; + + uint32 pointOwnerTeamId = 0; + //find which team should own this point + if (m_PointBarStatus[point] <= BG_EY_PROGRESS_BAR_NEUTRAL_LOW) + pointOwnerTeamId = HORDE; + else if (m_PointBarStatus[point] >= BG_EY_PROGRESS_BAR_NEUTRAL_HIGH) + pointOwnerTeamId = ALLIANCE; + else + pointOwnerTeamId = EY_POINT_NO_OWNER; + + for (uint8 i = 0; i < m_PlayersNearPoint[point].size(); ++i) + { + Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[point][i]); + if (plr) + { + this->UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[point], plr); + //if point owner changed we must evoke event! + if (pointOwnerTeamId != m_PointOwnedByTeam[point]) + { + //point was uncontrolled and player is from team which captured point + if (m_PointState[point] == EY_POINT_STATE_UNCONTROLLED && plr->GetTeam() == pointOwnerTeamId) + this->EventTeamCapturedPoint(plr, point); + + //point was under control and player isn't from team which controlled it + if (m_PointState[point] == EY_POINT_UNDER_CONTROL && plr->GetTeam() != m_PointOwnedByTeam[point]) + this->EventTeamLostPoint(plr, point); + } + } + } + } +} + +void BattleGroundEY::UpdateTeamScore(uint32 Team) +{ + uint32 score = GetTeamScore(Team); + //TODO there should be some sound played when one team is near victory!! - and define variables + /*if (!m_IsInformedNearVictory && score >= BG_EY_WARNING_NEAR_VICTORY_SCORE) + { + if (Team == ALLIANCE) + SendMessageToAll(LANG_BG_EY_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + else + SendMessageToAll(LANG_BG_EY_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_EY_SOUND_NEAR_VICTORY); + m_IsInformedNearVictory = true; + }*/ + + if (score >= BG_EY_MAX_TEAM_SCORE) + { + score = BG_EY_MAX_TEAM_SCORE; + EndBattleGround(Team); + } + + if (Team == ALLIANCE) + UpdateWorldState(EY_ALLIANCE_RESOURCES, score); + else + UpdateWorldState(EY_HORDE_RESOURCES, score); +} + +void BattleGroundEY::EndBattleGround(uint32 winner) +{ + //win reward + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + //complete map reward + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + + BattleGround::EndBattleGround(winner); +} + +void BattleGroundEY::UpdatePointsCount(uint32 Team) +{ + if (Team == ALLIANCE) + UpdateWorldState(EY_ALLIANCE_BASE, m_TeamPointsCount[BG_TEAM_ALLIANCE]); + else + UpdateWorldState(EY_HORDE_BASE, m_TeamPointsCount[BG_TEAM_HORDE]); +} + +void BattleGroundEY::UpdatePointsIcons(uint32 Team, uint32 Point) +{ + //we MUST firstly send 0, after that we can send 1!!! + if (m_PointState[Point] == EY_POINT_UNDER_CONTROL) + { + UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 0); + if (Team == ALLIANCE) + UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 1); + else + UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 1); + } + else + { + if (Team == ALLIANCE) + UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 0); + else + UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 0); + UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 1); + } +} + +void BattleGroundEY::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map + BattleGroundEYScore* sc = new BattleGroundEYScore; + + m_PlayersNearPoint[EY_POINTS_MAX].push_back(plr->GetGUID()); + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid) +{ + // sometimes flag aura not removed :( + for (int j = EY_POINTS_MAX; j >= 0; --j) + { + for (size_t i = 0; i < m_PlayersNearPoint[j].size(); ++i) + if (m_PlayersNearPoint[j][i] == guid) + m_PlayersNearPoint[j].erase(m_PlayersNearPoint[j].begin() + i); + } + if (IsFlagPickedup()) + { + if (m_FlagKeeper == guid) + { + if (plr) + EventPlayerDroppedFlag(plr); + else + { + SetFlagPicker(0); + RespawnFlag(true); + } + } + } +} + +void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!Source->isAlive()) //hack code, must be removed later + return; + + switch(Trigger) + { + case TR_BLOOD_ELF_POINT: + if (m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_BLOOD_ELF); + break; + case TR_FEL_REALVER_POINT: + if (m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[FEL_REALVER] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_FEL_REALVER); + break; + case TR_MAGE_TOWER_POINT: + if (m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[MAGE_TOWER] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_MAGE_TOWER); + break; + case TR_DRAENEI_RUINS_POINT: + if (m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[DRAENEI_RUINS] == Source->GetTeam()) + if (m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_DRAENEI_RUINS); + break; + case 4512: + case 4515: + case 4517: + case 4519: + case 4530: + case 4531: + case 4568: + case 4569: + case 4570: + case 4571: + case 5866: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +bool BattleGroundEY::SetupBattleGround() +{ + // doors + if (!AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_EY_OBJECT_DOOR_H, BG_OBJECT_H_DOOR_EY_ENTRY, 1803.21f, 1539.49f, 1261.09f, 3.14159f, 0.173648f, 0, 0.984808f, 0, RESPAWN_IMMEDIATELY) + // banners (alliance) + || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) + // banners (horde) + || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) + // banners (natural) + || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) + // flags + || !AddObject(BG_EY_OBJECT_FLAG_NETHERSTORM, BG_OBJECT_FLAG2_EY_ENTRY, 2174.782227f, 1569.054688f, 1160.361938f, -1.448624f, 0, 0, 0.662620f, -0.748956f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_FEL_REALVER, BG_OBJECT_FLAG1_EY_ENTRY, 2044.28f, 1729.68f, 1189.96f, -0.017453f, 0, 0, 0.008727f, -0.999962f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_BLOOD_ELF, BG_OBJECT_FLAG1_EY_ENTRY, 2048.83f, 1393.65f, 1194.49f, 0.20944f, 0, 0, 0.104528f, 0.994522f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_DRAENEI_RUINS, BG_OBJECT_FLAG1_EY_ENTRY, 2286.56f, 1402.36f, 1197.11f, 3.72381f, 0, 0, 0.957926f, -0.287016f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_MAGE_TOWER, BG_OBJECT_FLAG1_EY_ENTRY, 2284.48f, 1731.23f, 1189.99f, 2.89725f, 0, 0, 0.992546f, 0.121869f, RESPAWN_ONE_DAY) + // tower cap + || !AddObject(BG_EY_OBJECT_TOWER_CAP_FEL_REALVER, BG_OBJECT_FR_TOWER_CAP_EY_ENTRY, 2024.600708f, 1742.819580f, 1195.157715f, 2.443461f, 0, 0, 0.939693f, 0.342020f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF, BG_OBJECT_BE_TOWER_CAP_EY_ENTRY, 2050.493164f, 1372.235962f, 1194.563477f, 1.710423f, 0, 0, 0.754710f, 0.656059f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS, BG_OBJECT_DR_TOWER_CAP_EY_ENTRY, 2301.010498f, 1386.931641f, 1197.183472f, 1.570796f, 0, 0, 0.707107f, 0.707107f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER, BG_OBJECT_HU_TOWER_CAP_EY_ENTRY, 2282.121582f, 1760.006958f, 1189.707153f, 1.919862f, 0, 0, 0.819152f, 0.573576f, RESPAWN_ONE_DAY) +) + { + sLog.outErrorDb("BatteGroundEY: Failed to spawn some object BattleGround not created!"); + return false; + } + + //buffs + for (int i = 0; i < EY_POINTS_MAX; ++i) + { + AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(m_Points_Trigger[i]); + if (!at) + { + sLog.outError("BattleGroundEY: Unknown trigger: %u", m_Points_Trigger[i]); + continue; + } + if (!AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3, Buff_Entries[0], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) +) + sLog.outError("BattleGroundEY: Cannot spawn buff"); + } + + WorldSafeLocsEntry const *sg = NULL; + sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); + if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) + { + sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); + return false; + } + + sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE); + if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) + { + sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); + return false; + } + + return true; +} + +void BattleGroundEY::Reset() +{ + //call parent's class reset + BattleGround::Reset(); + + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + m_TeamPointsCount[BG_TEAM_ALLIANCE] = 0; + m_TeamPointsCount[BG_TEAM_HORDE] = 0; + m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0; + m_HonorScoreTics[BG_TEAM_HORDE] = 0; + m_FlagState = BG_EY_FLAG_STATE_ON_BASE; + m_FlagCapturedBgObjectType = 0; + m_FlagKeeper = 0; + m_DroppedFlagGUID = 0; + m_PointAddingTimer = 0; + m_TowerCapCheckTimer = 0; + bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID()); + m_HonorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks; + + for (uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + m_PointOwnedByTeam[i] = EY_POINT_NO_OWNER; + m_PointState[i] = EY_POINT_STATE_UNCONTROLLED; + m_PointBarStatus[i] = BG_EY_PROGRESS_BAR_STATE_MIDDLE; + m_PlayersNearPoint[i].clear(); + m_PlayersNearPoint[i].reserve(15); //tip size + } + m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].clear(); + m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].reserve(30); +} + +void BattleGroundEY::RespawnFlag(bool send_message) +{ + if (m_FlagCapturedBgObjectType > 0) + SpawnBGObject(m_FlagCapturedBgObjectType, RESPAWN_ONE_DAY); + + m_FlagCapturedBgObjectType = 0; + m_FlagState = BG_EY_FLAG_STATE_ON_BASE; + SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_IMMEDIATELY); + + if (send_message) + { + SendMessageToAll(LANG_BG_EY_RESETED_FLAG, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_EY_SOUND_FLAG_RESET); // flags respawned sound... + } + + UpdateWorldState(NETHERSTORM_FLAG, 1); +} + +void BattleGroundEY::RespawnFlagAfterDrop() +{ + RespawnFlag(true); + + GameObject *obj = HashMapHolder::Find(GetDroppedFlagGUID()); + if (obj) + obj->Delete(); + else + sLog.outError("BattleGroundEY: Unknown dropped flag guid: %u",GUID_LOPART(GetDroppedFlagGUID())); + + SetDroppedFlagGUID(0); +} + +void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + BattleGround::HandleKillPlayer(player, killer); + EventPlayerDroppedFlag(player); +} + +void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + { + // if not running, do not cast things at the dropper player, neither send unnecessary messages + // just take off the aura + if (IsFlagPickedup() && GetFlagPickerGUID() == Source->GetGUID()) + { + SetFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + } + return; + } + + if (!IsFlagPickedup()) + return; + + if (GetFlagPickerGUID() != Source->GetGUID()) + return; + + SetFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + m_FlagState = BG_EY_FLAG_STATE_ON_GROUND; + m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; + Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); + Source->CastSpell(Source, BG_EY_PLAYER_DROPPED_FLAG_SPELL, true); + //this does not work correctly :((it should remove flag carrier name) + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_WAIT_RESPAWN); + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN); + + if (Source->GetTeam() == ALLIANCE) + SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL); + else + SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL); +} + +void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) +{ + if (GetStatus() != STATUS_IN_PROGRESS || IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10)) + return; + + if (Source->GetTeam() == ALLIANCE) + { + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER); + PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE); + } + else + { + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER); + PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_HORDE); + } + + if (m_FlagState == BG_EY_FLAG_STATE_ON_BASE) + UpdateWorldState(NETHERSTORM_FLAG, 0); + m_FlagState = BG_EY_FLAG_STATE_ON_PLAYER; + + SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_ONE_DAY); + SetFlagPicker(Source->GetGUID()); + //get flag aura on player + Source->CastSpell(Source, BG_EY_NETHERSTORM_FLAG_SPELL, true); + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + + if (Source->GetTeam() == ALLIANCE) + PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, Source->GetName()); + else + PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL, Source->GetName()); +} + +void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + //Natural point + uint32 Team = m_PointOwnedByTeam[Point]; + + if (!Team) + return; + + if (Team == ALLIANCE) + { + m_TeamPointsCount[BG_TEAM_ALLIANCE]--; + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 1, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 2, RESPAWN_ONE_DAY); + } + else + { + m_TeamPointsCount[BG_TEAM_HORDE]--; + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 1, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 2, RESPAWN_ONE_DAY); + } + + SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 1, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 2, RESPAWN_IMMEDIATELY); + + //buff isn't despawned + + m_PointOwnedByTeam[Point] = EY_POINT_NO_OWNER; + m_PointState[Point] = EY_POINT_NO_OWNER; + + if (Team == ALLIANCE) + SendMessageToAll(m_LoosingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + else + SendMessageToAll(m_LoosingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source); + + UpdatePointsIcons(Team, Point); + UpdatePointsCount(Team); + + //remove bonus honor aura trigger creature when node is lost + if (Point < EY_POINTS_MAX) + DelCreature(Point + 6);//NULL checks are in DelCreature! 0-5 spirit guides +} + +void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + uint32 Team = Source->GetTeam(); + + SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType, RESPAWN_ONE_DAY); + SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 1, RESPAWN_ONE_DAY); + SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 2, RESPAWN_ONE_DAY); + + if (Team == ALLIANCE) + { + m_TeamPointsCount[BG_TEAM_ALLIANCE]++; + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 1, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 2, RESPAWN_IMMEDIATELY); + } + else + { + m_TeamPointsCount[BG_TEAM_HORDE]++; + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 1, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 2, RESPAWN_IMMEDIATELY); + } + + //buff isn't respawned + + m_PointOwnedByTeam[Point] = Team; + m_PointState[Point] = EY_POINT_UNDER_CONTROL; + + if (Team == ALLIANCE) + SendMessageToAll(m_CapturingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + else + SendMessageToAll(m_CapturingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source); + + if (m_BgCreatures[Point]) + DelCreature(Point); + + WorldSafeLocsEntry const *sg = NULL; + sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId); + if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, Team)) + sLog.outError("BatteGroundEY: Failed to spawn spirit guide! point: %u, team: %u, graveyard_id: %u", + Point, Team, m_CapturingPointTypes[Point].GraveYardId); + +// SpawnBGCreature(Point,RESPAWN_IMMEDIATELY); + + UpdatePointsIcons(Team, Point); + UpdatePointsCount(Team); + + if (Point >= EY_POINTS_MAX) + return; + + Creature* trigger = GetBGCreature(Point + 6);//0-5 spirit guides + if (!trigger) + trigger = AddCreature(WORLD_TRIGGER,Point+6,Team,BG_EY_TriggerPositions[Point][0],BG_EY_TriggerPositions[Point][1],BG_EY_TriggerPositions[Point][2],BG_EY_TriggerPositions[Point][3]); + + //add bonus honor aura trigger creature when node is accupied + //cast bonus aura (+50% honor in 25yards) + //aura should only apply to players who have accupied the node, set correct faction for trigger + if (trigger) + { + trigger->setFaction(Team == ALLIANCE ? 84 : 83); + trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); + } +} + +void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType) +{ + if (GetStatus() != STATUS_IN_PROGRESS || GetFlagPickerGUID() != Source->GetGUID()) + return; + + SetFlagPicker(0); + m_FlagState = BG_EY_FLAG_STATE_WAIT_RESPAWN; + Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + + if (Source->GetTeam() == ALLIANCE) + PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE); + else + PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE); + + SpawnBGObject(BgObjectType, RESPAWN_IMMEDIATELY); + + m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; + m_FlagCapturedBgObjectType = BgObjectType; + + uint8 team_id = 0; + if (Source->GetTeam() == ALLIANCE) + { + team_id = BG_TEAM_ALLIANCE; + SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_A, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + } + else + { + team_id = BG_TEAM_HORDE; + SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_H, CHAT_MSG_BG_SYSTEM_HORDE, Source); + } + + if (m_TeamPointsCount[team_id] > 0) + AddPoints(Source->GetTeam(), BG_EY_FlagPoints[m_TeamPointsCount[team_id] - 1]); + + UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); +} + +void BattleGroundEY::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) +{ + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); + if (itr == m_PlayerScores.end()) // player not found + return; + + switch(type) + { + case SCORE_FLAG_CAPTURES: // flags captured + ((BattleGroundEYScore*)itr->second)->FlagCaptures += value; + break; + default: + BattleGround::UpdatePlayerScore(Source, type, value, doAddHonor); + break; + } +} + +void BattleGroundEY::FillInitialWorldStates(WorldPacket& data) +{ + data << uint32(EY_HORDE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_HORDE]); + data << uint32(EY_ALLIANCE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_ALLIANCE]); + data << uint32(0xab6) << uint32(0x0); + data << uint32(0xab5) << uint32(0x0); + data << uint32(0xab4) << uint32(0x0); + data << uint32(0xab3) << uint32(0x0); + data << uint32(0xab2) << uint32(0x0); + data << uint32(0xab1) << uint32(0x0); + data << uint32(0xab0) << uint32(0x0); + data << uint32(0xaaf) << uint32(0x0); + + data << uint32(DRAENEI_RUINS_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == HORDE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL); + + data << uint32(DRAENEI_RUINS_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == ALLIANCE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL); + + data << uint32(DRAENEI_RUINS_UNCONTROL) << uint32(m_PointState[DRAENEI_RUINS] != EY_POINT_UNDER_CONTROL); + + data << uint32(MAGE_TOWER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == ALLIANCE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL); + + data << uint32(MAGE_TOWER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == HORDE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL); + + data << uint32(MAGE_TOWER_UNCONTROL) << uint32(m_PointState[MAGE_TOWER] != EY_POINT_UNDER_CONTROL); + + data << uint32(FEL_REAVER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == HORDE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL); + + data << uint32(FEL_REAVER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == ALLIANCE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL); + + data << uint32(FEL_REAVER_UNCONTROL) << uint32(m_PointState[FEL_REALVER] != EY_POINT_UNDER_CONTROL); + + data << uint32(BLOOD_ELF_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == HORDE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL); + + data << uint32(BLOOD_ELF_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == ALLIANCE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL); + + data << uint32(BLOOD_ELF_UNCONTROL) << uint32(m_PointState[BLOOD_ELF] != EY_POINT_UNDER_CONTROL); + + data << uint32(NETHERSTORM_FLAG) << uint32(m_FlagState == BG_EY_FLAG_STATE_ON_BASE); + + data << uint32(0xad2) << uint32(0x1); + data << uint32(0xad1) << uint32(0x1); + data << uint32(0xabe) << uint32(GetTeamScore(HORDE)); + data << uint32(0xabd) << uint32(GetTeamScore(ALLIANCE)); + data << uint32(0xa05) << uint32(0x8e); + data << uint32(0xaa0) << uint32(0x0); + data << uint32(0xa9f) << uint32(0x0); + data << uint32(0xa9e) << uint32(0x0); + data << uint32(0xc0d) << uint32(0x17b); +} + +WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player) +{ + uint32 g_id = 0; + + switch(player->GetTeam()) + { + case ALLIANCE: g_id = EY_GRAVEYARD_MAIN_ALLIANCE; break; + case HORDE: g_id = EY_GRAVEYARD_MAIN_HORDE; break; + default: return NULL; + } + + float distance, nearestDistance; + + WorldSafeLocsEntry const* entry = NULL; + WorldSafeLocsEntry const* nearestEntry = NULL; + entry = sWorldSafeLocsStore.LookupEntry(g_id); + nearestEntry = entry; + + if (!entry) + { + sLog.outError("BattleGroundEY: Not found the main team graveyard. Graveyard system isn't working!"); + return NULL; + } + + float plr_x = player->GetPositionX(); + float plr_y = player->GetPositionY(); + float plr_z = player->GetPositionZ(); + + distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); + nearestDistance = distance; + + for (uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + if (m_PointOwnedByTeam[i] == player->GetTeam() && m_PointState[i] == EY_POINT_UNDER_CONTROL) + { + entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId); + if (!entry) + sLog.outError("BattleGroundEY: Not found graveyard: %u",m_CapturingPointTypes[i].GraveYardId); + else + { + distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); + if (distance < nearestDistance) + { + nearestDistance = distance; + nearestEntry = entry; + } + } + } + } + + return nearestEntry; +} + +bool BattleGroundEY::IsAllNodesConrolledByTeam(uint32 team) const +{ + uint32 count = 0; + for (int i = 0; i < EY_POINTS_MAX; ++i) + if (m_PointOwnedByTeam[i] == team && m_PointState[i] == EY_POINT_UNDER_CONTROL) + ++count; + + return count == EY_POINTS_MAX; +} diff --git a/src/server/game/BattleGrounds/BattleGroundEY.h b/src/server/game/BattleGrounds/BattleGroundEY.h new file mode 100644 index 00000000000..4fe23c4c821 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundEY.h @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDEY_H +#define __BATTLEGROUNDEY_H + +#include "Language.h" + +class BattleGround; + +#define BG_EY_FLAG_RESPAWN_TIME (8*IN_MILISECONDS) //8 seconds +#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds + +enum BG_EY_WorldStates +{ + EY_ALLIANCE_RESOURCES = 2749, + EY_HORDE_RESOURCES = 2750, + EY_ALLIANCE_BASE = 2752, + EY_HORDE_BASE = 2753, + DRAENEI_RUINS_HORDE_CONTROL = 2733, + DRAENEI_RUINS_ALLIANCE_CONTROL = 2732, + DRAENEI_RUINS_UNCONTROL = 2731, + MAGE_TOWER_ALLIANCE_CONTROL = 2730, + MAGE_TOWER_HORDE_CONTROL = 2729, + MAGE_TOWER_UNCONTROL = 2728, + FEL_REAVER_HORDE_CONTROL = 2727, + FEL_REAVER_ALLIANCE_CONTROL = 2726, + FEL_REAVER_UNCONTROL = 2725, + BLOOD_ELF_HORDE_CONTROL = 2724, + BLOOD_ELF_ALLIANCE_CONTROL = 2723, + BLOOD_ELF_UNCONTROL = 2722, + PROGRESS_BAR_PERCENT_GREY = 2720, //100 = empty (only grey), 0 = blue|red (no grey) + PROGRESS_BAR_STATUS = 2719, //50 init!, 48 ... hordak bere .. 33 .. 0 = full 100% hordacky , 100 = full alliance + PROGRESS_BAR_SHOW = 2718, //1 init, 0 druhy send - bez messagu, 1 = controlled aliance + NETHERSTORM_FLAG = 2757, + //set to 2 when flag is picked up, and to 1 if it is dropped + NETHERSTORM_FLAG_STATE_ALLIANCE = 2769, + NETHERSTORM_FLAG_STATE_HORDE = 2770 +}; + +enum BG_EY_ProgressBarConsts +{ + BG_EY_POINT_MAX_CAPTURERS_COUNT = 5, + BG_EY_POINT_RADIUS = 70, + BG_EY_PROGRESS_BAR_DONT_SHOW = 0, + BG_EY_PROGRESS_BAR_SHOW = 1, + BG_EY_PROGRESS_BAR_PERCENT_GREY = 40, + BG_EY_PROGRESS_BAR_STATE_MIDDLE = 50, + BG_EY_PROGRESS_BAR_HORDE_CONTROLLED = 0, + BG_EY_PROGRESS_BAR_NEUTRAL_LOW = 30, + BG_EY_PROGRESS_BAR_NEUTRAL_HIGH = 70, + BG_EY_PROGRESS_BAR_ALI_CONTROLLED = 100 +}; + +enum BG_EY_Sounds +{ + //strange ids, but sure about them + BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, + BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, + BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_EY_SOUND_FLAG_RESET = 8192 +}; + +enum BG_EY_Spells +{ + BG_EY_NETHERSTORM_FLAG_SPELL = 34976, + BG_EY_PLAYER_DROPPED_FLAG_SPELL = 34991 +}; + +enum EYBattleGroundObjectEntry +{ + BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door + BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door + BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) + BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) + BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) + BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) + BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) + BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) + BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt + BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt + BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt + BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt +}; + +enum EYBattleGroundPointsTrigger +{ + TR_BLOOD_ELF_POINT = 4476, + TR_FEL_REALVER_POINT = 4514, + TR_MAGE_TOWER_POINT = 4516, + TR_DRAENEI_RUINS_POINT = 4518, + TR_BLOOD_ELF_BUFF = 4568, + TR_FEL_REALVER_BUFF = 4569, + TR_MAGE_TOWER_BUFF = 4570, + TR_DRAENEI_RUINS_BUFF = 4571 +}; + +enum EYBattleGroundGaveyards +{ + EY_GRAVEYARD_MAIN_ALLIANCE = 1103, + EY_GRAVEYARD_MAIN_HORDE = 1104, + EY_GRAVEYARD_FEL_REALVER = 1105, + EY_GRAVEYARD_BLOOD_ELF = 1106, + EY_GRAVEYARD_DRAENEI_RUINS = 1107, + EY_GRAVEYARD_MAGE_TOWER = 1108 +}; + +enum EYBattleGroundPoints +{ + FEL_REALVER = 0, + BLOOD_ELF = 1, + DRAENEI_RUINS = 2, + MAGE_TOWER = 3, + + EY_PLAYERS_OUT_OF_POINTS = 4, + EY_POINTS_MAX = 4 +}; + +enum EYBattleGroundCreaturesTypes +{ + EY_SPIRIT_FEL_REALVER = 0, + EY_SPIRIT_BLOOD_ELF = 1, + EY_SPIRIT_DRAENEI_RUINS = 2, + EY_SPIRIT_MAGE_TOWER = 3, + EY_SPIRIT_MAIN_ALLIANCE = 4, + EY_SPIRIT_MAIN_HORDE = 5, + + EY_TRIGGER_FEL_REALVER = 6, + EY_TRIGGER_BLOOD_ELF = 7, + EY_TRIGGER_DRAENEI_RUINS = 8, + EY_TRIGGER_MAGE_TOWER = 9, + + BG_EY_CREATURES_MAX = 10 +}; + +enum EYBattleGroundObjectTypes +{ + BG_EY_OBJECT_DOOR_A = 0, + BG_EY_OBJECT_DOOR_H = 1, + BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER = 2, + BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT = 3, + BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT = 4, + BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER = 5, + BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT = 6, + BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT = 7, + BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER = 8, + BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT = 9, + BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT = 10, + BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER = 11, + BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT = 12, + BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT = 13, + BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER = 14, + BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT = 15, + BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT = 16, + BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER = 17, + BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT = 18, + BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT = 19, + BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER = 20, + BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT = 21, + BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT = 22, + BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER = 23, + BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT = 24, + BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT = 25, + BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER = 26, + BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT = 27, + BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT = 28, + BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER = 29, + BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT = 30, + BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT = 31, + BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER = 32, + BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT = 33, + BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT = 34, + BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER = 35, + BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT = 36, + BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT = 37, + BG_EY_OBJECT_TOWER_CAP_FEL_REALVER = 38, + BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF = 39, + BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS = 40, + BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER = 41, + BG_EY_OBJECT_FLAG_NETHERSTORM = 42, + BG_EY_OBJECT_FLAG_FEL_REALVER = 43, + BG_EY_OBJECT_FLAG_BLOOD_ELF = 44, + BG_EY_OBJECT_FLAG_DRAENEI_RUINS = 45, + BG_EY_OBJECT_FLAG_MAGE_TOWER = 46, + //buffs + BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER = 47, + BG_EY_OBJECT_REGENBUFF_FEL_REALVER = 48, + BG_EY_OBJECT_BERSERKBUFF_FEL_REALVER = 49, + BG_EY_OBJECT_SPEEDBUFF_BLOOD_ELF = 50, + BG_EY_OBJECT_REGENBUFF_BLOOD_ELF = 51, + BG_EY_OBJECT_BERSERKBUFF_BLOOD_ELF = 52, + BG_EY_OBJECT_SPEEDBUFF_DRAENEI_RUINS = 53, + BG_EY_OBJECT_REGENBUFF_DRAENEI_RUINS = 54, + BG_EY_OBJECT_BERSERKBUFF_DRAENEI_RUINS = 55, + BG_EY_OBJECT_SPEEDBUFF_MAGE_TOWER = 56, + BG_EY_OBJECT_REGENBUFF_MAGE_TOWER = 57, + BG_EY_OBJECT_BERSERKBUFF_MAGE_TOWER = 58, + BG_EY_OBJECT_MAX = 59 +}; + +#define BG_EY_NotEYWeekendHonorTicks 330 +#define BG_EY_EYWeekendHonorTicks 200 + +enum BG_EY_Score +{ + BG_EY_WARNING_NEAR_VICTORY_SCORE = 1400, + BG_EY_MAX_TEAM_SCORE = 1600 +}; + +enum BG_EY_FlagState +{ + BG_EY_FLAG_STATE_ON_BASE = 0, + BG_EY_FLAG_STATE_WAIT_RESPAWN = 1, + BG_EY_FLAG_STATE_ON_PLAYER = 2, + BG_EY_FLAG_STATE_ON_GROUND = 3 +}; + +enum EYBattleGroundPointState +{ + EY_POINT_NO_OWNER = 0, + EY_POINT_STATE_UNCONTROLLED = 0, + EY_POINT_UNDER_CONTROL = 3 +}; + +struct BattleGroundEYPointIconsStruct +{ + BattleGroundEYPointIconsStruct(uint32 _WorldStateControlIndex, uint32 _WorldStateAllianceControlledIndex, uint32 _WorldStateHordeControlledIndex) + : WorldStateControlIndex(_WorldStateControlIndex), WorldStateAllianceControlledIndex(_WorldStateAllianceControlledIndex), WorldStateHordeControlledIndex(_WorldStateHordeControlledIndex) {} + uint32 WorldStateControlIndex; + uint32 WorldStateAllianceControlledIndex; + uint32 WorldStateHordeControlledIndex; +}; + +// x, y, z, o +const float BG_EY_TriggerPositions[EY_POINTS_MAX][4] = { + {2044.28f, 1729.68f, 1189.96f, -0.017453f}, // FEL_REALVER center + {2048.83f, 1393.65f, 1194.49f, 0.20944f}, // BLOOD_ELF center + {2286.56f, 1402.36f, 1197.11f, 3.72381f}, // DRAENEI_RUINS center + {2284.48f, 1731.23f, 1189.99f, 2.89725f} // MAGE_TOWER center +}; + +struct BattleGroundEYLoosingPointStruct +{ + BattleGroundEYLoosingPointStruct(uint32 _SpawnNeutralObjectType, uint32 _DespawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _DespawnObjectTypeHorde, uint32 _MessageIdHorde) + : SpawnNeutralObjectType(_SpawnNeutralObjectType), + DespawnObjectTypeAlliance(_DespawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), + DespawnObjectTypeHorde(_DespawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde) + {} + + uint32 SpawnNeutralObjectType; + uint32 DespawnObjectTypeAlliance; + uint32 MessageIdAlliance; + uint32 DespawnObjectTypeHorde; + uint32 MessageIdHorde; +}; + +struct BattleGroundEYCapturingPointStruct +{ + BattleGroundEYCapturingPointStruct(uint32 _DespawnNeutralObjectType, uint32 _SpawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _SpawnObjectTypeHorde, uint32 _MessageIdHorde, uint32 _GraveYardId) + : DespawnNeutralObjectType(_DespawnNeutralObjectType), + SpawnObjectTypeAlliance(_SpawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), + SpawnObjectTypeHorde(_SpawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde), + GraveYardId(_GraveYardId) + {} + + uint32 DespawnNeutralObjectType; + uint32 SpawnObjectTypeAlliance; + uint32 MessageIdAlliance; + uint32 SpawnObjectTypeHorde; + uint32 MessageIdHorde; + uint32 GraveYardId; +}; + +const uint8 BG_EY_TickPoints[EY_POINTS_MAX] = {1, 2, 5, 10}; +const uint32 BG_EY_FlagPoints[EY_POINTS_MAX] = {75, 85, 100, 500}; + +//constant arrays: +const BattleGroundEYPointIconsStruct m_PointsIconStruct[EY_POINTS_MAX] = +{ + BattleGroundEYPointIconsStruct(FEL_REAVER_UNCONTROL, FEL_REAVER_ALLIANCE_CONTROL, FEL_REAVER_HORDE_CONTROL), + BattleGroundEYPointIconsStruct(BLOOD_ELF_UNCONTROL, BLOOD_ELF_ALLIANCE_CONTROL, BLOOD_ELF_HORDE_CONTROL), + BattleGroundEYPointIconsStruct(DRAENEI_RUINS_UNCONTROL, DRAENEI_RUINS_ALLIANCE_CONTROL, DRAENEI_RUINS_HORDE_CONTROL), + BattleGroundEYPointIconsStruct(MAGE_TOWER_UNCONTROL, MAGE_TOWER_ALLIANCE_CONTROL, MAGE_TOWER_HORDE_CONTROL) +}; +const BattleGroundEYLoosingPointStruct m_LoosingPointTypes[EY_POINTS_MAX] = +{ + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_H_F_RUINS), + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_H_B_TOWER), + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_H_D_RUINS), + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_H_M_TOWER) +}; +const BattleGroundEYCapturingPointStruct m_CapturingPointTypes[EY_POINTS_MAX] = +{ + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_H_F_RUINS, EY_GRAVEYARD_FEL_REALVER), + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_H_B_TOWER, EY_GRAVEYARD_BLOOD_ELF), + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_H_D_RUINS, EY_GRAVEYARD_DRAENEI_RUINS), + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_H_M_TOWER, EY_GRAVEYARD_MAGE_TOWER) +}; + +class BattleGroundEYScore : public BattleGroundScore +{ + public: + BattleGroundEYScore () : FlagCaptures(0) {}; + virtual ~BattleGroundEYScore() {}; + uint32 FlagCaptures; +}; + +class BattleGroundEY : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundEY(); + ~BattleGroundEY(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + /* BG Flags */ + uint64 GetFlagPickerGUID() const { return m_FlagKeeper; } + void SetFlagPicker(uint64 guid) { m_FlagKeeper = guid; } + bool IsFlagPickedup() const { return m_FlagKeeper != 0; } + uint8 GetFlagState() const { return m_FlagState; } + void RespawnFlag(bool send_message); + void RespawnFlagAfterDrop(); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleBuffUse(uint64 const& buff_guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + void HandleKillPlayer(Player *player, Player *killer); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); + virtual bool SetupBattleGround(); + virtual void Reset(); + void UpdateTeamScore(uint32 Team); + void EndBattleGround(uint32 winner); + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + virtual void FillInitialWorldStates(WorldPacket& data); + void SetDroppedFlagGUID(uint64 guid) { m_DroppedFlagGUID = guid;} + uint64 GetDroppedFlagGUID() const { return m_DroppedFlagGUID;} + + /* Battleground Events */ + virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); + virtual void EventPlayerDroppedFlag(Player *Source); + + /* achievement req. */ + bool IsAllNodesConrolledByTeam(uint32 team) const; + private: + void EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType); + void EventTeamCapturedPoint(Player *Source, uint32 Point); + void EventTeamLostPoint(Player *Source, uint32 Point); + void UpdatePointsCount(uint32 Team); + void UpdatePointsIcons(uint32 Team, uint32 Point); + + /* Point status updating procedures */ + void CheckSomeoneLeftPoint(); + void CheckSomeoneJoinedPoint(); + void UpdatePointStatuses(); + + /* Scorekeeping */ + uint32 GetTeamScore(uint32 Team) const { return m_TeamScores[GetTeamIndexByTeamId(Team)]; } + void AddPoints(uint32 Team, uint32 Points); + + void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } + void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } + + uint32 m_HonorScoreTics[2]; + uint32 m_TeamPointsCount[2]; + + uint32 m_Points_Trigger[EY_POINTS_MAX]; + + uint64 m_FlagKeeper; // keepers guid + uint64 m_DroppedFlagGUID; + uint32 m_FlagCapturedBgObjectType; // type that should be despawned when flag is captured + uint8 m_FlagState; // for checking flag state + int32 m_FlagsTimer; + int32 m_TowerCapCheckTimer; + + uint32 m_PointOwnedByTeam[EY_POINTS_MAX]; + uint8 m_PointState[EY_POINTS_MAX]; + int32 m_PointBarStatus[EY_POINTS_MAX]; + typedef std::vector PlayersNearPointType; + PlayersNearPointType m_PlayersNearPoint[EY_POINTS_MAX + 1]; + uint8 m_CurrentPointPlayersCount[2*EY_POINTS_MAX]; + + int32 m_PointAddingTimer; + uint32 m_HonorTics; +}; +#endif + diff --git a/src/server/game/BattleGrounds/BattleGroundHandler.cpp b/src/server/game/BattleGrounds/BattleGroundHandler.cpp new file mode 100644 index 00000000000..e779f2a8ab1 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundHandler.cpp @@ -0,0 +1,803 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +#include "ArenaTeam.h" +#include "BattleGroundMgr.h" +#include "BattleGroundWS.h" +#include "BattleGround.h" +#include "Chat.h" +#include "Language.h" +#include "Log.h" +#include "Player.h" +#include "Object.h" +#include "Opcodes.h" + +void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket & recv_data) +{ + uint64 guid; + recv_data >> guid; + sLog.outDebug("WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); + + Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); + if (!unit) + return; + + if (!unit->isBattleMaster()) // it's not battlemaster + return; + + // Stop the npc if moving + unit->StopMoving(); + + BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(unit->GetEntry()); + + if (!_player->GetBGAccessByLevel(bgTypeId)) + { + // temp, must be gossip message... + SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR); + return; + } + + SendBattlegGroundList(guid, bgTypeId); +} + +void WorldSession::SendBattlegGroundList(uint64 guid, BattleGroundTypeId bgTypeId) +{ + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId, 0); + SendPacket(&data); +} + +void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket & recv_data) +{ + uint64 guid; + uint32 bgTypeId_; + uint32 instanceId; + uint8 joinAsGroup; + bool isPremade = false; + Group * grp = NULL; + + recv_data >> guid; // battlemaster guid + recv_data >> bgTypeId_; // battleground type id (DBC id) + recv_data >> instanceId; // instance id, 0 if First Available selected + recv_data >> joinAsGroup; // join as group + + if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) + { + sLog.outError("Battleground: invalid bgtype (%u) received. possible cheater? player guid %u",bgTypeId_,_player->GetGUIDLow()); + return; + } + + BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); + + sLog.outDebug("WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid))); + + // can do this, since it's battleground, not arena + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, 0); + BattleGroundQueueTypeId bgQueueTypeIdRandom = BattleGroundMgr::BGQueueTypeId(BATTLEGROUND_RB, 0); + + // ignore if player is already in BG + if (_player->InBattleGround()) + return; + + // get bg instance or bg template if instance not found + BattleGround *bg = NULL; + if (instanceId) + bg = sBattleGroundMgr.GetBattleGroundThroughClientInstance(instanceId, bgTypeId); + + if (!bg) + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if (!bg) + return; + + // expected bracket entry + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); + if (!bracketEntry) + return; + + GroupJoinBattlegroundResult err; + + // check queue conditions + if (!joinAsGroup) + { + // check Deserter debuff + if (!_player->CanJoinToBattleground()) + { + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); + _player->GetSession()->SendPacket(&data); + return; + } + + if (_player->GetBattleGroundQueueIndex(bgQueueTypeIdRandom) < PLAYER_MAX_BATTLEGROUND_QUEUES) + { + //player is already in random queue + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_RANDOM_BG); + _player->GetSession()->SendPacket(&data); + return; + } + + if (_player->InBattleGroundQueue() && bgTypeId == BATTLEGROUND_RB) + { + //player is already in queue, can't start random queue + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_IN_NON_RANDOM_BG); + _player->GetSession()->SendPacket(&data); + return; + } + + // check if already in queue + if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + //player is already in this queue + return; + + // check if has free queue slots + if (!_player->HasFreeBattleGroundQueueId()) + { + WorldPacket data; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_TOO_MANY_QUEUES); + _player->GetSession()->SendPacket(&data); + return; + } + + BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + + GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, 0, false, isPremade, 0); + uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); + // already checked if queueSlot is valid, now just get it + uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); + + WorldPacket data; + // send status packet (in queue) + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); + SendPacket(&data); + sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); + } + else + { + grp = _player->GetGroup(); + // no group found, error + if (!grp) + return; + if (grp->GetLeaderGUID() != _player->GetGUID()) + return; + err = grp->CanJoinBattleGroundQueue(bg, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0); + isPremade = (grp->GetMembersCount() >= bg->GetMinPlayersPerTeam()); + + BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + GroupQueueInfo * ginfo = NULL; + uint32 avgTime = 0; + + if (err > 0) + { + sLog.outDebug("Battleground: the following players are joining as group:"); + ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, 0, false, isPremade, 0); + avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); + } + + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if (!member) continue; // this should never happen + + WorldPacket data; + + if (err <= 0) + { + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); + member->GetSession()->SendPacket(&data); + continue; + } + + // add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); + + // send status packet (in queue) + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); + member->GetSession()->SendPacket(&data); + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); + member->GetSession()->SendPacket(&data); + sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); + } + sLog.outDebug("Battleground: group end"); + + } + sBattleGroundMgr.ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); +} + +void WorldSession::HandleBattleGroundPlayerPositionsOpcode(WorldPacket & /*recv_data*/) +{ + // empty opcode + sLog.outDebug("WORLD: Recvd MSG_BATTLEGROUND_PLAYER_POSITIONS Message"); + + BattleGround *bg = _player->GetBattleGround(); + if (!bg) // can't be received if player not in battleground + return; + + switch(bg->GetTypeID(true)) + { + case BATTLEGROUND_WS: + { + uint32 count1 = 0; //always constant zero? + uint32 count2 = 0; //count of next fields + + Player *ali_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID()); + if (ali_plr) + ++count2; + + Player *horde_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID()); + if (horde_plr) + ++count2; + + WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2)); + data << count1; // alliance flag holders count - obsolete, now always 0 + /*for (uint8 i = 0; i < count1; ++i) + { + data << uint64(0); // guid + data << (float)0; // x + data << (float)0; // y + }*/ + data << count2; // horde flag holders count - obsolete, now count of next fields + if (ali_plr) + { + data << (uint64)ali_plr->GetGUID(); + data << (float)ali_plr->GetPositionX(); + data << (float)ali_plr->GetPositionY(); + } + if (horde_plr) + { + data << (uint64)horde_plr->GetGUID(); + data << (float)horde_plr->GetPositionX(); + data << (float)horde_plr->GetPositionY(); + } + + SendPacket(&data); + } + break; + case BATTLEGROUND_EY: + //TODO : fix me! + break; + case BATTLEGROUND_AB: + case BATTLEGROUND_AV: + { + //for other BG types - send default + WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4)); + data << uint32(0); + data << uint32(0); + SendPacket(&data); + } + break; + default: + //maybe it is sent also in arena - do nothing + break; + } +} + +void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("WORLD: Recvd MSG_PVP_LOG_DATA Message"); + + BattleGround *bg = _player->GetBattleGround(); + if (!bg) + return; + + WorldPacket data; + sBattleGroundMgr.BuildPvpLogDataPacket(&data, bg); + SendPacket(&data); + + sLog.outDebug("WORLD: Sent MSG_PVP_LOG_DATA Message"); +} + +void WorldSession::HandleBattlefieldListOpcode(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: Recvd CMSG_BATTLEFIELD_LIST Message"); + + uint32 bgTypeId; + recv_data >> bgTypeId; // id from DBC + + uint8 fromWhere; + recv_data >> fromWhere; // 0 - battlemaster (lua: ShowBattlefieldList), 1 - UI (lua: RequestBattlegroundInstanceInfo) + + uint8 unk1; + recv_data >> unk1; // Unknown 3.2.2 + + BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); + if (!bl) + { + sLog.outError("Battleground: invalid bgtype received."); + return; + } + + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundListPacket(&data, 0, _player, BattleGroundTypeId(bgTypeId), fromWhere); + SendPacket(&data); +} + +void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: Recvd CMSG_BATTLEFIELD_PORT Message"); + + uint8 type; // arenatype if arena + uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 + uint32 bgTypeId_; // type id from dbc + uint16 unk; // 0x1F90 constant? + uint8 action; // enter battle 0x1, leave queue 0x0 + + recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action; + + if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) + { + sLog.outError("BattlegroundHandler: invalid bgtype (%u) received.", bgTypeId_); + return; + } + if (!_player->InBattleGroundQueue()) + { + sLog.outError("BattlegroundHandler: Invalid CMSG_BATTLEFIELD_PORT received from player (%u), he is not in bg_queue.", _player->GetGUIDLow()); + return; + } + + //get GroupQueueInfo from BattleGroundQueue + BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type); + BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + //we must use temporary variable, because GroupQueueInfo pointer can be deleted in BattleGroundQueue::RemovePlayer() function + GroupQueueInfo ginfo; + if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo)) + { + sLog.outError("BattlegroundHandler: itrplayerstatus not found."); + return; + } + // if action == 1, then instanceId is required + if (!ginfo.IsInvitedToBGInstanceGUID && action == 1) + { + sLog.outError("BattlegroundHandler: instance not found."); + return; + } + + BattleGround *bg = sBattleGroundMgr.GetBattleGround(ginfo.IsInvitedToBGInstanceGUID, bgTypeId); + + // bg template might and must be used in case of leaving queue, when instance is not created yet + if (!bg && action == 0) + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if (!bg) + { + sLog.outError("BattlegroundHandler: bg_template not found for type id %u.", bgTypeId); + return; + } + + // expected bracket entry + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); + if (!bracketEntry) + return; + + //some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it + if (action == 1 && ginfo.ArenaType == 0) + { + //if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue + if (!_player->CanJoinToBattleground()) + { + //send bg command result to show nice message + WorldPacket data2; + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data2, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); + _player->GetSession()->SendPacket(&data2); + action = 0; + sLog.outDebug("Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); + } + //if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue + if (_player->getLevel() > bg->GetMaxLevel()) + { + sLog.outError("Battleground: Player %s (%u) has level (%u) higher than maxlevel (%u) of battleground (%u)! Do not port him to battleground!", + _player->GetName(), _player->GetGUIDLow(), _player->getLevel(), bg->GetMaxLevel(), bg->GetTypeID()); + action = 0; + } + } + uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); + WorldPacket data; + switch(action) + { + case 1: // port to battleground + if (!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) + return; // cheating? + + if (!_player->InBattleGround()) + _player->SetBattleGroundEntryPoint(); + + // resurrect the player + if (!_player->isAlive()) + { + _player->ResurrectPlayer(1.0f); + _player->SpawnCorpseBones(); + } + // stop taxi flight at port + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType()); + _player->GetSession()->SendPacket(&data); + // remove battleground queue status from BGmgr + bgQueue.RemovePlayer(_player->GetGUID(), false); + // this is still needed here if battleground "jumping" shouldn't add deserter debuff + // also this is required to prevent stuck at old battleground after SetBattleGroundId set to new + if (BattleGround *currentBg = _player->GetBattleGround()) + currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); + + // set the destination instance id + _player->SetBattleGroundId(bg->GetInstanceID(), bgTypeId); + // set the destination team + _player->SetBGTeam(ginfo.Team); + // bg->HandleBeforeTeleportToBattleGround(_player); + sBattleGroundMgr.SendToBattleGround(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId); + // add only in HandleMoveWorldPortAck() + // bg->AddPlayer(_player,team); + sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetInstanceID(), bg->GetTypeID(), bgQueueTypeId); + break; + case 0: // leave queue + // if player leaves rated arena match before match start, it is counted as he played but he lost + if (ginfo.IsRated) + { + ArenaTeam * at = objmgr.GetArenaTeamById(ginfo.Team); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u, because he has left queue!", GUID_LOPART(_player->GetGUID()), ginfo.OpponentsTeamRating); + at->MemberLost(_player, ginfo.OpponentsTeamRating); + at->SaveToDB(); + } + } + _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); + bgQueue.RemovePlayer(_player->GetGUID(), true); + // player left queue, we should update it - do not update Arena Queue + if (!ginfo.ArenaType) + sBattleGroundMgr.ScheduleQueueUpdate(ginfo.ArenaTeamRating, ginfo.ArenaType, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); + SendPacket(&data); + sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetTypeID(), bgQueueTypeId); + break; + default: + sLog.outError("Battleground port: unknown action %u", action); + break; + } +} + +void WorldSession::HandleLeaveBattlefieldOpcode(WorldPacket& recv_data) +{ + sLog.outDebug("WORLD: Recvd CMSG_LEAVE_BATTLEFIELD Message"); + + recv_data.read_skip(); // unk1 + recv_data.read_skip(); // unk2 + recv_data.read_skip(); // BattleGroundTypeId + recv_data.read_skip(); // unk3 + + // not allow leave battleground in combat + if (_player->isInCombat()) + if (BattleGround* bg = _player->GetBattleGround()) + if (bg->GetStatus() != STATUS_WAIT_LEAVE) + return; + + _player->LeaveBattleground(); +} + +void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket & /*recv_data*/) +{ + // empty opcode + sLog.outDebug("WORLD: Battleground status"); + + WorldPacket data; + // we must update all queues here + BattleGround *bg = NULL; + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + { + BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i); + if (!bgQueueTypeId) + continue; + BattleGroundTypeId bgTypeId = BattleGroundMgr::BGTemplateId(bgQueueTypeId); + uint8 arenaType = BattleGroundMgr::BGArenaType(bgQueueTypeId); + if (bgTypeId == _player->GetBattleGroundTypeId()) + { + bg = _player->GetBattleGround(); + //i cannot check any variable from player class because player class doesn't know if player is in 2v2 / 3v3 or 5v5 arena + //so i must use bg pointer to get that information + if (bg && bg->GetArenaType() == arenaType) + { + // this line is checked, i only don't know if GetStartTime is changing itself after bg end! + // send status in BattleGround + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType); + SendPacket(&data); + continue; + } + } + //we are sending update to player about queue - he can be invited there! + //get GroupQueueInfo for queue status + BattleGroundQueue& bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + GroupQueueInfo ginfo; + if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo)) + continue; + if (ginfo.IsInvitedToBGInstanceGUID) + { + bg = sBattleGroundMgr.GetBattleGround(ginfo.IsInvitedToBGInstanceGUID, bgTypeId); + if (!bg) + continue; + uint32 remainingTime = getMSTimeDiff(getMSTime(), ginfo.RemoveInviteTime); + // send status invited to BattleGround + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, arenaType); + SendPacket(&data); + } + else + { + bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if (!bg) + continue; + + // expected bracket entry + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); + if (!bracketEntry) + continue; + + uint32 avgTime = bgQueue.GetAverageQueueWaitTime(&ginfo, bracketEntry->GetBracketId()); + // send status in BattleGround Queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTimeDiff(ginfo.JoinTime, getMSTime()), arenaType); + SendPacket(&data); + } + } +} + +void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY"); + + BattleGround *bg = _player->GetBattleGround(); + + uint64 guid; + recv_data >> guid; + + Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); + if (!unit) + return; + + if (!unit->isSpiritService()) // it's not spirit service + return; + + if (bg) + sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, guid); +} + + +void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUEUE"); + + BattleGround *bg = _player->GetBattleGround(); + + uint64 guid; + recv_data >> guid; + + Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); + if (!unit) + return; + + if (!unit->isSpiritService()) // it's not spirit service + return; + + if (bg) + bg->AddPlayerToResurrectQueue(guid, _player->GetGUID()); +} + + +void WorldSession::HandleBattlemasterJoinArena(WorldPacket & recv_data) +{ + sLog.outDebug("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA"); + //recv_data.hexlike(); + + uint64 guid; // arena Battlemaster guid + uint8 arenaslot; // 2v2, 3v3 or 5v5 + uint8 asGroup; // asGroup + uint8 isRated; // isRated + Group * grp = NULL; + + recv_data >> guid >> arenaslot >> asGroup >> isRated; + + // ignore if we already in BG or BG queue + if (_player->InBattleGround()) + return; + + Creature *unit = GetPlayer()->GetMap()->GetCreature(guid); + if (!unit) + return; + + if (!unit->isBattleMaster()) // it's not battle master + return; + + uint8 arenatype = 0; + uint32 arenaRating = 0; + + switch(arenaslot) + { + case 0: + arenatype = ARENA_TYPE_2v2; + break; + case 1: + arenatype = ARENA_TYPE_3v3; + break; + case 2: + arenatype = ARENA_TYPE_5v5; + break; + default: + sLog.outError("Unknown arena slot %u at HandleBattlemasterJoinArena()", arenaslot); + return; + } + + //check existance + BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA); + if (!bg) + { + sLog.outError("Battleground: template bg (all arenas) not found"); + return; + } + + BattleGroundTypeId bgTypeId = bg->GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, arenatype); + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(),_player->getLevel()); + if (!bracketEntry) + return; + + GroupJoinBattlegroundResult err = ERR_GROUP_JOIN_BATTLEGROUND_FAIL; + + if (!asGroup) + { + // check if already in queue + if (_player->GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + //player is already in this queue + return; + // check if has free queue slots + if (!_player->HasFreeBattleGroundQueueId()) + return; + } + else + { + grp = _player->GetGroup(); + // no group found, error + if (!grp) + return; + if (grp->GetLeaderGUID() != _player->GetGUID()) + return; + err = grp->CanJoinBattleGroundQueue(bg, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot); + } + + uint32 ateamId = 0; + + if (isRated) + { + ateamId = _player->GetArenaTeamId(arenaslot); + // check real arenateam existence only here (if it was moved to group->CanJoin .. () then we would ahve to get it twice) + ArenaTeam * at = objmgr.GetArenaTeamById(ateamId); + if (!at) + { + _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + return; + } + // get the team rating for queueing + arenaRating = at->GetRating(); + // the arenateam id must match for everyone in the group + // get the personal ratings for queueing + uint32 avg_pers_rating = 0; + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + + // calc avg personal rating + avg_pers_rating += member->GetArenaPersonalRating(arenaslot); + } + + if (arenatype) + avg_pers_rating /= arenatype; + + // if avg personal rating is more than 150 points below the teams rating, the team will be queued against an opponent matching or similar to the average personal rating + if (avg_pers_rating + 150 < arenaRating) + arenaRating = avg_pers_rating; + + if (arenaRating <= 0) + arenaRating = 1; + } + + BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + if (asGroup) + { + uint32 avgTime = 0; + + if (err > 0) + { + sLog.outDebug("Battleground: arena join as group start"); + if (isRated) + { + sLog.outDebug("Battleground: arena team id %u, leader %s queued with rating %u for type %u",_player->GetArenaTeamId(arenaslot),_player->GetName(),arenaRating,arenatype); + bg->SetRated(true); + } + else + bg->SetRated(false); + + GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, ateamId); + avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); + } + + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if (!member) + continue; + + WorldPacket data; + + if (err <= 0) + { + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); + member->GetSession()->SendPacket(&data); + continue; + } + + // add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); + + // send status packet (in queue) + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype); + member->GetSession()->SendPacket(&data); + sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, err); + member->GetSession()->SendPacket(&data); + sLog.outDebug("Battleground: player joined queue for arena as group bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,member->GetGUIDLow(), member->GetName()); + } + } + else + { + GroupQueueInfo * ginfo = bgQueue.AddGroup(_player, NULL, bgTypeId, bracketEntry, arenatype, isRated, false, arenaRating, ateamId); + uint32 avgTime = bgQueue.GetAverageQueueWaitTime(ginfo, bracketEntry->GetBracketId()); + uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId); + + WorldPacket data; + // send status packet (in queue) + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype); + SendPacket(&data); + sLog.outDebug("Battleground: player joined queue for arena, skirmish, bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName()); + } + sBattleGroundMgr.ScheduleQueueUpdate(arenaRating, arenatype, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); +} + +void WorldSession::HandleReportPvPAFK(WorldPacket & recv_data) +{ + uint64 playerGuid; + recv_data >> playerGuid; + Player *reportedPlayer = objmgr.GetPlayer(playerGuid); + + if (!reportedPlayer) + { + sLog.outDebug("WorldSession::HandleReportPvPAFK: player not found"); + return; + } + + sLog.outDebug("WorldSession::HandleReportPvPAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName()); + + reportedPlayer->ReportedAfkBy(_player); +} diff --git a/src/server/game/BattleGrounds/BattleGroundIC.cpp b/src/server/game/BattleGrounds/BattleGroundIC.cpp new file mode 100644 index 00000000000..8dbcc81e5c6 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundIC.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundIC.h" +#include "Language.h" + +BattleGroundIC::BattleGroundIC() +{ + m_BgCreatures.resize(2); + m_BgObjects.resize(5); + //TODO FIX ME! + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; +} + +BattleGroundIC::~BattleGroundIC() +{ + +} + +void BattleGroundIC::Update(uint32 diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundIC::StartingEventCloseDoors() +{ +} + +void BattleGroundIC::StartingEventOpenDoors() +{ +} + +void BattleGroundIC::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundICScore* sc = new BattleGroundICScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundIC::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) +{ + +} + +void BattleGroundIC::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; +} + +void BattleGroundIC::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +{ + + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if (itr == m_PlayerScores.end()) // player not found... + return; + + BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); +} + +bool BattleGroundIC::SetupBattleGround() +{ + AddObject(0, 195157, 459.72f, -419.93f, 42.55f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); + AddObject(1, 195158, 797.72f, -1009.48f, 138.52f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); + AddObject(2, 195338, 418.98f, -838.33f, 51.09f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); + AddObject(3, 195343, 1267.45f, -390.88f, 24.23f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); + AddObject(4, 195333, 769.27f, -833.53f, 9.57f, 0, 0, 0, 0.9996573f, 0.02617699f, 10*MINUTE); + SpawnLeader(ALLIANCE); + SpawnLeader(HORDE); + return true; +} + +void BattleGroundIC::SpawnLeader(uint32 teamid) +{ + if (teamid == ALLIANCE) + AddCreature(34924, 0, ALLIANCE, 307.03f, -833.04f, 48.91f, 6.23f, 10*MINUTE); + else + AddCreature(34922, 1, HORDE, 1264.42f, -766.80f, 48.91f, 3.28f, 10*MINUTE); +} + +void BattleGroundIC::HandleKillUnit(Creature *unit, Player * /*killer*/) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + uint32 entry = unit->GetEntry(); + if (entry == 34924) + { + RewardHonorToTeam(500,HORDE); + EndBattleGround(HORDE); + } + else if (entry == 34922) + { + RewardHonorToTeam(500,ALLIANCE); + EndBattleGround(ALLIANCE); + } +} + +void BattleGroundIC::EndBattleGround(uint32 winner) +{ + BattleGround::EndBattleGround(winner); +} + +void BattleGroundIC::EventPlayerClickedOnFlag(Player * /*source*/, GameObject* /*target_obj*/) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; +} diff --git a/src/server/game/BattleGrounds/BattleGroundIC.h b/src/server/game/BattleGrounds/BattleGroundIC.h new file mode 100644 index 00000000000..e49ea01e850 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundIC.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDIC_H +#define __BATTLEGROUNDIC_H + +class BattleGround; + +enum Buffs +{ + OIL_REFINERY = 68719, + QUARRY = 68720 +}; + +class BattleGroundICScore : public BattleGroundScore +{ + public: + BattleGroundICScore() {}; + virtual ~BattleGroundICScore() {}; +}; + +class BattleGroundIC : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundIC(); + ~BattleGroundIC(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void SpawnLeader(uint32 teamid); + void HandleKillUnit(Creature *unit, Player *killer); + void EndBattleGround(uint32 winner); + void EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/); + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + + private: +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundMgr.cpp b/src/server/game/BattleGrounds/BattleGroundMgr.cpp new file mode 100644 index 00000000000..3bbe8c3cf31 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundMgr.cpp @@ -0,0 +1,2232 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "Policies/SingletonImp.h" + +#include "ArenaTeam.h" +#include "BattleGroundMgr.h" +#include "BattleGroundAV.h" +#include "BattleGroundAB.h" +#include "BattleGroundEY.h" +#include "BattleGroundWS.h" +#include "BattleGroundNA.h" +#include "BattleGroundBE.h" +#include "BattleGroundAA.h" +#include "BattleGroundRL.h" +#include "BattleGroundSA.h" +#include "BattleGroundDS.h" +#include "BattleGroundRV.h" +#include "BattleGroundIC.h" +#include "BattleGroundRB.h" +#include "Chat.h" +#include "Map.h" +#include "MapInstanced.h" +#include "MapManager.h" +#include "Player.h" +#include "GameEventMgr.h" +#include "ProgressBar.h" +#include "SharedDefines.h" +#include "Formulas.h" + +INSTANTIATE_SINGLETON_1(BattleGroundMgr); + +/*********************************************************/ +/*** BATTLEGROUND QUEUE SYSTEM ***/ +/*********************************************************/ + +BattleGroundQueue::BattleGroundQueue() +{ + for (uint32 i = 0; i < BG_TEAMS_COUNT; ++i) + { + for (uint32 j = 0; j < MAX_BATTLEGROUND_BRACKETS; ++j) + { + m_SumOfWaitTimes[i][j] = 0; + m_WaitTimeLastPlayer[i][j] = 0; + for (uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++k) + m_WaitTimes[i][j][k] = 0; + } + } +} + +BattleGroundQueue::~BattleGroundQueue() +{ + m_QueuedPlayers.clear(); + for (int i = 0; i < MAX_BATTLEGROUND_BRACKETS; ++i) + { + for (uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; ++j) + { + for (GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr) + delete (*itr); + m_QueuedGroups[i][j].clear(); + } + } +} + +/*********************************************************/ +/*** BATTLEGROUND QUEUE SELECTION POOLS ***/ +/*********************************************************/ + +// selection pool initialization, used to clean up from prev selection +void BattleGroundQueue::SelectionPool::Init() +{ + SelectedGroups.clear(); + PlayerCount = 0; +} + +// remove group info from selection pool +// returns true when we need to try to add new group to selection pool +// returns false when selection pool is ok or when we kicked smaller group than we need to kick +// sometimes it can be called on empty selection pool +bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size) +{ + //find maxgroup or LAST group with size == size and kick it + bool found = false; + GroupsQueueType::iterator groupToKick = SelectedGroups.begin(); + for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr) + { + if (abs((int32)((*itr)->Players.size() - size)) <= 1) + { + groupToKick = itr; + found = true; + } + else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size()) + groupToKick = itr; + } + //if pool is empty, do nothing + if (GetPlayerCount()) + { + //update player count + GroupQueueInfo* ginfo = (*groupToKick); + SelectedGroups.erase(groupToKick); + PlayerCount -= ginfo->Players.size(); + //return false if we kicked smaller group or there are enough players in selection pool + if (ginfo->Players.size() <= size + 1) + return false; + } + return true; +} + +// add group to selection pool +// used when building selection pools +// returns true if we can invite more players, or when we added group to selection pool +// returns false when selection pool is full +bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount) +{ + //if group is larger than desired count - don't allow to add it to pool + if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size()) + { + SelectedGroups.push_back(ginfo); + // increase selected players count + PlayerCount += ginfo->Players.size(); + return true; + } + if (PlayerCount < desiredCount) + return true; + return false; +} + +/*********************************************************/ +/*** BATTLEGROUND QUEUES ***/ +/*********************************************************/ + +// add group or player (grp == NULL) to bg queue with the given leader and bg specifications +GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, Group* grp, BattleGroundTypeId BgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid) +{ + BattleGroundBracketId bracketId = backetEntry->GetBracketId(); + + // create new ginfo + GroupQueueInfo* ginfo = new GroupQueueInfo; + ginfo->BgTypeId = BgTypeId; + ginfo->ArenaType = ArenaType; + ginfo->ArenaTeamId = arenateamid; + ginfo->IsRated = isRated; + ginfo->IsInvitedToBGInstanceGUID = 0; + ginfo->JoinTime = getMSTime(); + ginfo->RemoveInviteTime = 0; + ginfo->Team = leader->GetTeam(); + ginfo->ArenaTeamRating = arenaRating; + ginfo->OpponentsTeamRating = 0; + + ginfo->Players.clear(); + + //compute index (if group is premade or joined a rated match) to queues + uint32 index = 0; + if (!isRated && !isPremade) + index += BG_TEAMS_COUNT; + if (ginfo->Team == HORDE) + index++; + sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, bracket_id : %u, index : %u", BgTypeId, bracketId, index); + + uint32 lastOnlineTime = getMSTime(); + + //announce world (this don't need mutex) + if (isRated && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) + { + ArenaTeam *Team = objmgr.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 + { + //ACE_Guard guard(m_Lock); + if (grp) + { + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if (!member) + continue; // this should never happen + PlayerQueueInfo& pl_info = m_QueuedPlayers[member->GetGUID()]; + pl_info.LastOnlineTime = lastOnlineTime; + pl_info.GroupInfo = ginfo; + // add the pinfo to ginfo's list + ginfo->Players[member->GetGUID()] = &pl_info; + } + } + else + { + PlayerQueueInfo& pl_info = m_QueuedPlayers[leader->GetGUID()]; + pl_info.LastOnlineTime = lastOnlineTime; + pl_info.GroupInfo = ginfo; + ginfo->Players[leader->GetGUID()] = &pl_info; + } + + //add GroupInfo to m_QueuedGroups + m_QueuedGroups[bracketId][index].push_back(ginfo); + + //announce to world, this code needs mutex + if (!isRated && !isPremade && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) + { + if (BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId)) + { + char const* bgName = bg->GetName(); + uint32 MinPlayers = bg->GetMinPlayersPerTeam(); + uint32 qHorde = 0; + uint32 qAlliance = 0; + uint32 q_min_level = backetEntry->minLevel; + uint32 q_max_level = backetEntry->maxLevel; + GroupsQueueType::const_iterator itr; + for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr) + if (!(*itr)->IsInvitedToBGInstanceGUID) + qAlliance += (*itr)->Players.size(); + for (itr = m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[bracketId][BG_QUEUE_NORMAL_HORDE].end(); ++itr) + if (!(*itr)->IsInvitedToBGInstanceGUID) + qHorde += (*itr)->Players.size(); + + // Show queue status to player only (when joining queue) + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) + { + ChatHandler(leader).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, bgName, q_min_level, q_max_level, + qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); + } + // System message + else + { + sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, bgName, q_min_level, q_max_level, + qAlliance, (MinPlayers > qAlliance) ? MinPlayers - qAlliance : (uint32)0, qHorde, (MinPlayers > qHorde) ? MinPlayers - qHorde : (uint32)0); + } + } + } + //release mutex + } + + return ginfo; +} + +void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) +{ + uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) + { + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE + } + + //store pointer to arrayindex of player that was added first + uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][bracket_id]); + //remove his time from sum + m_SumOfWaitTimes[team_index][bracket_id] -= m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)]; + //set average time to new + m_WaitTimes[team_index][bracket_id][(*lastPlayerAddedPointer)] = timeInQueue; + //add new time to sum + m_SumOfWaitTimes[team_index][bracket_id] += timeInQueue; + //set index of last player added to next one + (*lastPlayerAddedPointer)++; + (*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; +} + +uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id) +{ + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) + { + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE + } + //check if there is enought values(we always add values > 0) + if (m_WaitTimes[team_index][bracket_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1]) + return (m_SumOfWaitTimes[team_index][bracket_id] / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME); + else + //if there aren't enough values return 0 - not available + return 0; +} + +//remove player from queue and from group info, if group info is empty then remove it too +void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount) +{ + //Player *plr = objmgr.GetPlayer(guid); + + int32 bracket_id = -1; // signed for proper for-loop finish + QueuedPlayersMap::iterator itr; + + //remove player from map, if he's there + itr = m_QueuedPlayers.find(guid); + if (itr == m_QueuedPlayers.end()) + { + sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid)); + return; + } + + GroupQueueInfo* group = itr->second.GroupInfo; + GroupsQueueType::iterator group_itr, group_itr_tmp; + // mostly people with the highest levels are in battlegrounds, thats why + // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 + // variable index removes useless searching in other team's queue + uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE; + + for (int32 bracket_id_tmp = MAX_BATTLEGROUND_BRACKETS - 1; bracket_id_tmp >= 0 && bracket_id == -1; --bracket_id_tmp) + { + //we must check premade and normal team's queue - because when players from premade are joining bg, + //they leave groupinfo so we can't use its players size to find out index + for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT; j += BG_QUEUE_NORMAL_ALLIANCE) + { + for (group_itr_tmp = m_QueuedGroups[bracket_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[bracket_id_tmp][j].end(); ++group_itr_tmp) + { + if ((*group_itr_tmp) == group) + { + bracket_id = bracket_id_tmp; + group_itr = group_itr_tmp; + //we must store index to be able to erase iterator + index = j; + break; + } + } + } + } + //player can't be in queue without group, but just in case + if (bracket_id == -1) + { + sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid)); + return; + } + sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from bracket_id %u", GUID_LOPART(guid), (uint32)bracket_id); + + // ALL variables are correctly set + // We can ignore leveling up in queue - it should not cause crash + // remove player from group + // if only one player there, remove group + + // remove player queue info from group queue info + std::map::iterator pitr = group->Players.find(guid); + if (pitr != group->Players.end()) + group->Players.erase(pitr); + + // if invited to bg, and should decrease invited count, then do it + if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID) + { + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId); + if (bg) + bg->DecreaseInvitedCount(group->Team); + } + + // remove player queue info + m_QueuedPlayers.erase(itr); + + // announce to world if arena team left queue for rated match, show only once + if (group->ArenaType && group->IsRated && group->Players.empty() && sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE)) + { + ArenaTeam *Team = objmgr.GetArenaTeamById(group->ArenaTeamId); + if (Team) + 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 loose + if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount) + { + ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating); + Player *plr = objmgr.GetPlayer(guid); + if (plr) + at->MemberLost(plr, group->OpponentsTeamRating); + else + at->OfflineMemberLost(guid, group->OpponentsTeamRating); + at->SaveToDB(); + } + } + + // remove group queue info if needed + if (group->Players.empty()) + { + m_QueuedGroups[bracket_id][index].erase(group_itr); + delete group; + } + // if group wasn't empty, so it wasn't deleted, and player have left a rated + // queue -> everyone from the group should leave too + // don't remove recursively if already invited to bg! + else if (!group->IsInvitedToBGInstanceGUID && group->IsRated) + { + // remove next player, this is recursive + // first send removal information + if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) + { + BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType); + uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); + plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to + // queue->removeplayer, it causes bugs + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); + plr2->GetSession()->SendPacket(&data); + } + // then actually delete, this may delete the group as well! + RemovePlayer(group->Players.begin()->first, decreaseInvitedCount); + } +} + +//returns true when player pl_guid is in queue and is invited to bgInstanceGuid +bool BattleGroundQueue::IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime) +{ + QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(pl_guid); + return (qItr != m_QueuedPlayers.end() + && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == bgInstanceGuid + && qItr->second.GroupInfo->RemoveInviteTime == removeTime); +} + +bool BattleGroundQueue::GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo) +{ + QueuedPlayersMap::const_iterator qItr = m_QueuedPlayers.find(guid); + if (qItr == m_QueuedPlayers.end()) + return false; + *ginfo = *(qItr->second.GroupInfo); + return true; +} + +bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) +{ + // set side if needed + if (side) + ginfo->Team = side; + + if (!ginfo->IsInvitedToBGInstanceGUID) + { + // not yet invited + // set invitation + ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); + BattleGroundTypeId bgTypeId = bg->GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType()); + BattleGroundBracketId bracket_id = bg->GetBracketId(); + + // set ArenaTeamId for rated matches + if (bg->isArena() && bg->isRated()) + bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId); + + ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; + + // loop through the players + for (std::map::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) + { + // get the player + Player* plr = objmgr.GetPlayer(itr->first); + // if offline, skip him, this should not happen - player is removed from queue when he logs out + if (!plr) + continue; + + // invite the player + PlayerInvitedToBGUpdateAverageWaitTime(ginfo, bracket_id); + //sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team); + + // set invited player counters + bg->IncreaseInvitedCount(ginfo->Team); + + plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); + + // create remind invite events + BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME)); + // create automatic remove events + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); + + WorldPacket data; + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + + sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); + + // send status packet + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); + plr->GetSession()->SendPacket(&data); + } + return true; + } + + return false; +} + +/* +This function is inviting players to already running battlegrounds +Invitation type is based on config file +large groups are disadvantageous, because they will be kicked first if invitation type = 1 +*/ +void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id) +{ + int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE); + int32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE); + + //iterator for iterating through bg queue + GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); + //count of groups in queue - used to stop cycles + uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + //index to queue which group is current + uint32 aliIndex = 0; + for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++) + ++Ali_itr; + //the same thing for horde + GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin(); + uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); + uint32 hordeIndex = 0; + for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++) + ++Horde_itr; + + //if ofc like BG queue invitation is set in config, then we are happy + if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) + return; + + /* + if we reached this code, then we have to solve NP - complete problem called Subset sum problem + So one solution is to check all possible invitation subgroups, or we can use these conditions: + 1. Last time when BattleGroundQueue::Update was executed we invited all possible players - so there is only small possibility + that we will invite now whole queue, because only 1 change has been made to queues from the last BattleGroundQueue::Update call + 2. Other thing we should consider is group order in queue + */ + + // At first we need to compare free space in bg and our selection pool + int32 diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); + int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + while (abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0)) + { + //each cycle execution we need to kick at least 1 group + if (diffAli < diffHorde) + { + //kick alliance group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) + { + for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++) + ++Ali_itr; + } + //if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break; + if (!m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + if (aliFree <= diffHorde + 1) + break; + m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); + } + } + else + { + //kick horde group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) + { + for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) + ++Horde_itr; + } + if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + if (hordeFree <= diffAli + 1) + break; + m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); + } + } + //count diffs after small update + diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); + diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + } +} + +// this method checks if premade versus premade battleground is possible +// then after 30 mins (default) in queue it moves premade group to normal queue +// it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players +bool BattleGroundQueue::CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam) +{ + //check match + if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) + { + //start premade match + //if groups aren't invited + GroupsQueueType::const_iterator ali_group, horde_group; + for (ali_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group) + if (!(*ali_group)->IsInvitedToBGInstanceGUID) + break; + for (horde_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group) + if (!(*horde_group)->IsInvitedToBGInstanceGUID) + break; + + if (ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end()) + { + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); + //add groups/players from normal queue to size of bigger group + uint32 maxPlayers = std::min(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + GroupsQueueType::const_iterator itr; + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + for (itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) + { + //if itr can join BG and player count is less that maxPlayers, then add group to selectionpool + if (!(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers)) + break; + } + } + //premade selection pools are set + return true; + } + } + // now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!! + // this could be 2 cycles but i'm checking only first team in queue - it can cause problem - + // if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg + // and when they click or after 80 seconds the queue info is removed from queue + uint32 time_before = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty()) + { + GroupsQueueType::iterator itr = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin(); + if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam)) + { + //we must insert group to normal queue and erase pointer from premade queue + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); + } + } + } + //selection pools are not set + return false; +} + +// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam +bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) +{ + GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + itr_team[i] = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); + for (; itr_team[i] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i])) + { + if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID) + { + m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers); + if (m_SelectionPools[i].GetPlayerCount() >= minPlayers) + break; + } + } + } + //try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg + uint32 j = BG_TEAM_ALLIANCE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + j = BG_TEAM_HORDE; + if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 + && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers) + { + //we will try to invite more groups to team with less players indexed by j + ++(itr_team[j]); //this will not cause a crash, because for cycle above reached break; + for (; itr_team[j] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j])) + { + if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID) + if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount())) + break; + } + // do not allow to start bg with more than 2 players more on 1 faction + if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) + return false; + } + //allow 1v0 if debug bg + if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())) + return true; + //return true if there are enough players in selection pools - enable to work .debug bg command correctly + return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers; +} + +// this method will check if we can invite players to same faction skirmish match +bool BattleGroundQueue::CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam) +{ + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) + return false; + uint32 teamIndex = BG_TEAM_ALLIANCE; + uint32 otherTeam = BG_TEAM_HORDE; + uint32 otherTeamId = HORDE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) + { + teamIndex = BG_TEAM_HORDE; + otherTeam = BG_TEAM_ALLIANCE; + otherTeamId = ALLIANCE; + } + //clear other team's selection + m_SelectionPools[otherTeam].Init(); + //store last ginfo pointer + GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back(); + //set itr_team to group that was added to selection pool latest + GroupsQueueType::iterator itr_team = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); + for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) + if (ginfo == *itr_team) + break; + if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end()) + return false; + GroupsQueueType::iterator itr_team2 = itr_team; + ++itr_team2; + //invite players to other selection pool + for (; itr_team2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team2) + { + //if selection pool is full then break; + if (!(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam)) + break; + } + if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam) + return false; + + //here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue + for (GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr) + { + //set correct team + (*itr)->Team = otherTeamId; + //add team to other queue + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + otherTeam].push_front(*itr); + //remove team from old queue + GroupsQueueType::iterator itr2 = itr_team; + ++itr2; + for (; itr2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr2) + { + if (*itr2 == *itr) + { + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); + break; + } + } + } + return true; +} + +/* +this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while (true) cycles to invite whole queue +it must be called after fully adding the members of a group to ensure group joining +should be called from BattleGround::RemovePlayer function in some cases +*/ +void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating) +{ + //if no players in queue - do nothing + if (m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty() && + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].empty() && + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].empty()) + return; + + //battleground with free slot for player should be always in the beggining of the queue + // maybe it would be better to create bgfreeslotqueue for each bracket_id + BGFreeSlotQueueType::iterator itr, next; + for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) + { + next = itr; + ++next; + // DO NOT allow queue manager to invite new player to arena + if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetBracketId() == bracket_id && + (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) + { + BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) + // and iterator is invalid + + // clear selection pools + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); + + // call a function that does the job for us + FillPlayersToBG(bg, bracket_id); + + // now everything is set, invite players + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg, (*citr)->Team); + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg, (*citr)->Team); + + if (!bg->HasFreeSlots()) + { + // remove BG from BGFreeSlotQueue + bg->RemoveFromBGFreeSlotQueue(); + } + } + } + + // finished iterating through the bgs with free slots, maybe we need to create a new bg + + BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if (!bg_template) + { + sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); + return; + } + + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(),bracket_id); + if (!bracketEntry) + { + sLog.outError("Battleground: Update: bg bracket entry not found for map %u bracket id %u", bg_template->GetMapId(), bracket_id); + return; + } + + // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) + uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); + uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); + if (sBattleGroundMgr.isTesting()) + MinPlayersPerTeam = 1; + if (bg_template->isArena()) + { + if (sBattleGroundMgr.isArenaTesting()) + { + MaxPlayersPerTeam = 1; + MinPlayersPerTeam = 1; + } + else + { + //this switch can be much shorter + MaxPlayersPerTeam = arenaType; + MinPlayersPerTeam = arenaType; + /*switch(arenaType) + { + case ARENA_TYPE_2v2: + MaxPlayersPerTeam = 2; + MinPlayersPerTeam = 2; + break; + case ARENA_TYPE_3v3: + MaxPlayersPerTeam = 3; + MinPlayersPerTeam = 3; + break; + case ARENA_TYPE_5v5: + MaxPlayersPerTeam = 5; + MinPlayersPerTeam = 5; + break; + }*/ + } + } + + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); + + if (bg_template->isBattleGround()) + { + //check if there is premade against premade match + if (CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)) + { + //create new battleground + BattleGround * bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, 0, false); + if (!bg2) + { + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); + return; + } + //invite those selection pools + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg2, (*citr)->Team); + //start bg + bg2->StartBattleGround(); + //clear structures + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); + } + } + + // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena) + if (!isRated) + { + // if there are enough players in pools, start new battleground or non rated arena + if (CheckNormalMatch(bg_template, bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam) + || (bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam))) + { + // we successfully created a pool + BattleGround * bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, false); + if (!bg2) + { + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); + return; + } + + // invite those selection pools + for (uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for (GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg2, (*citr)->Team); + // start bg + bg2->StartBattleGround(); + } + } + else if (bg_template->isArena()) + { + // found out the minimum and maximum ratings the newly added team should battle against + // arenaRating is the rating of the latest joined team, or 0 + // 0 is on (automatic update call) and we must set it to team's with longest wait time + if (!arenaRating) + { + GroupQueueInfo* front1 = NULL; + GroupQueueInfo* front2 = NULL; + if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty()) + { + front1 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].front(); + arenaRating = front1->ArenaTeamRating; + } + if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) + { + front2 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].front(); + arenaRating = front2->ArenaTeamRating; + } + if (front1 && front2) + { + if (front1->JoinTime < front2->JoinTime) + arenaRating = front1->ArenaTeamRating; + } + else if (!front1 && !front2) + return; //queues are empty + } + + //set rating range + uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); + uint32 arenaMaxRating = arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); + // if max rating difference is set and the time past since server startup is greater than the rating discard time + // (after what time the ratings aren't taken into account when making teams) then + // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account + // else leave the discard time on 0, this way all ratings will be discarded + uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); + + // we need to find 2 teams which will play next game + + GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; + + //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups + + for (uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) + { + // take the group that joined first + itr_team[i] = m_QueuedGroups[bracket_id][i].begin(); + for (; itr_team[i] != m_QueuedGroups[bracket_id][i].end(); ++(itr_team[i])) + { + // if group match conditions, then add it to pool + if (!(*itr_team[i])->IsInvitedToBGInstanceGUID + && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[i])->JoinTime < discardTime)) + { + m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam); + // break for cycle to be able to start selecting another group from same faction queue + break; + } + } + } + // now we are done if we have 2 groups - ali vs horde! + // if we don't have, we must try to continue search in same queue + // tmp variables are correctly set + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; + ++itr_team[BG_TEAM_ALLIANCE]; + for (; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) + { + if (!(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime)) + { + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); + break; + } + } + } + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; + ++itr_team[BG_TEAM_HORDE]; + for (; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) + { + if (!(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime)) + { + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); + break; + } + } + } + + //if we have 2 teams, then start new arena and invite players! + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + BattleGround* arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, bracketEntry, arenaType, true); + if (!arena) + { + sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); + return; + } + + (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); + (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating); + // now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer + if ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE) + { + // add to alliance queue + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); + // erase from horde queue + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); + itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); + } + if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE) + { + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); + itr_team[BG_TEAM_HORDE] = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); + } + + InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); + InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); + + sLog.outDebug("Starting rated arena match!"); + + arena->StartBattleGround(); + } + } +} + +/*********************************************************/ +/*** BATTLEGROUND QUEUE EVENTS ***/ +/*********************************************************/ + +bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) +{ + Player* plr = objmgr.GetPlayer(m_PlayerGuid); + // player logged off (we should do nothing, he is correctly removed from queue in another procedure) + if (!plr) + return true; + + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //if battleground ended and its instance deleted - do nothing + if (!bg) + return true; + + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue or in battleground + { + // check if player is invited to this bg + BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId]; + if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) + { + WorldPacket data; + //we must send remaining time in queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, m_ArenaType); + plr->GetSession()->SendPacket(&data); + } + } + return true; //event will be deleted +} + +void BGQueueInviteEvent::Abort(uint64 /*e_time*/) +{ + //do nothing +} + +/* + this event has many possibilities when it is executed: + 1. player is in battleground (he clicked enter on invitation window) + 2. player left battleground queue and he isn't there any more + 3. player left battleground queue and he joined it again and IsInvitedToBGInstanceGUID = 0 + 4. player left queue and he joined again and he has been invited to same battleground again -> we should not remove him from queue yet + 5. player is invited to bg and he didn't choose what to do and timer expired - only in this condition we should call queue::RemovePlayer + we must remove player in the 5. case even if battleground object doesn't exist! +*/ +bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) +{ + Player* plr = objmgr.GetPlayer(m_PlayerGuid); + if (!plr) + // player logged off (we should do nothing, he is correctly removed from queue in another procedure) + return true; + + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //battleground can be deleted already when we are removing queue info + //bg pointer can be NULL! so use it carefully! + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(m_BgQueueTypeId); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue, or in Battleground + { + // check if player is in queue for this BG and if we are removing his invite event + BattleGroundQueue &bgQueue = sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId]; + if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime)) + { + sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + + plr->RemoveBattleGroundQueueId(m_BgQueueTypeId); + bgQueue.RemovePlayer(m_PlayerGuid, true); + //update queues if battleground isn't ended + if (bg && bg->isBattleGround() && bg->GetStatus() != STATUS_WAIT_LEAVE) + sBattleGroundMgr.ScheduleQueueUpdate(0, 0, m_BgQueueTypeId, m_BgTypeId, bg->GetBracketId()); + + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); + plr->GetSession()->SendPacket(&data); + } + } + + //event will be deleted + return true; +} + +void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) +{ + //do nothing +} + +/*********************************************************/ +/*** BATTLEGROUND MANAGER ***/ +/*********************************************************/ + +BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false) +{ + for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) + m_BattleGrounds[i].clear(); + m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); + m_Testing=false; +} + +BattleGroundMgr::~BattleGroundMgr() +{ + DeleteAllBattleGrounds(); +} + +void BattleGroundMgr::DeleteAllBattleGrounds() +{ + for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i) + { + for (BattleGroundSet::iterator itr = m_BattleGrounds[i].begin(); itr != m_BattleGrounds[i].end();) + { + BattleGround * bg = itr->second; + m_BattleGrounds[i].erase(itr++); + if (!m_ClientBattleGroundIds[i][bg->GetBracketId()].empty()) + m_ClientBattleGroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID()); + delete bg; + } + } + + // destroy template battlegrounds that listed only in queues (other already terminated) + for (uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId) + { + // ~BattleGround call unregistring BG from queue + while (!BGFreeSlotQueue[bgTypeId].empty()) + delete BGFreeSlotQueue[bgTypeId].front(); + } +} + +// used to update running battlegrounds, and delete finished ones +void BattleGroundMgr::Update(uint32 diff) +{ + BattleGroundSet::iterator itr, next; + for (uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; ++i) + { + itr = m_BattleGrounds[i].begin(); + // skip updating battleground template + if (itr != m_BattleGrounds[i].end()) + ++itr; + for (; itr != m_BattleGrounds[i].end(); itr = next) + { + next = itr; + ++next; + itr->second->Update(diff); + // use the SetDeleteThis variable + // direct deletion caused crashes + if (itr->second->m_SetDeleteThis) + { + BattleGround * bg = itr->second; + m_BattleGrounds[i].erase(itr); + if (!m_ClientBattleGroundIds[i][bg->GetBracketId()].empty()) + m_ClientBattleGroundIds[i][bg->GetBracketId()].erase(bg->GetClientInstanceID()); + delete bg; + } + } + } + + // update scheduled queues + if (!m_QueueUpdateScheduler.empty()) + { + std::vector scheduled; + { + //copy vector and clear the other + scheduled = std::vector(m_QueueUpdateScheduler); + m_QueueUpdateScheduler.clear(); + //release lock + } + + for (uint8 i = 0; i < scheduled.size(); i++) + { + uint32 arenaRating = scheduled[i] >> 32; + uint8 arenaType = scheduled[i] >> 24 & 255; + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundQueueTypeId(scheduled[i] >> 16 & 255); + BattleGroundTypeId bgTypeId = BattleGroundTypeId((scheduled[i] >> 8) & 255); + BattleGroundBracketId bracket_id = BattleGroundBracketId(scheduled[i] & 255); + m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, bracket_id, arenaType, arenaRating > 0, arenaRating); + } + } + + // if rating difference counts, maybe force-update queues + if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER)) + { + // it's time to force update + if (m_NextRatingDiscardUpdate < diff) + { + // forced update for rated arenas (scan all, but skipped non rated) + sLog.outDebug("BattleGroundMgr: UPDATING ARENA QUEUES"); + for (int qtype = BATTLEGROUND_QUEUE_2v2; qtype <= BATTLEGROUND_QUEUE_5v5; ++qtype) + for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) + m_BattleGroundQueues[qtype].Update( + BATTLEGROUND_AA, BattleGroundBracketId(bracket), + BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId(qtype)), true, 0); + + m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); + } + else + m_NextRatingDiscardUpdate -= diff; + } + if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) + { + if (m_AutoDistributionTimeChecker < diff) + { + if (time(NULL) > m_NextAutoDistributionTime) + { + DistributeArenaPoints(); + m_NextAutoDistributionTime = m_NextAutoDistributionTime + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); + sWorld.setWorldState(WS_ARENA_DISTRIBUTION_TIME, uint64(m_NextAutoDistributionTime)); + } + m_AutoDistributionTimeChecker = 600000; // check 10 minutes + } + else + m_AutoDistributionTimeChecker -= diff; + } +} + +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype) +{ + // we can be in 2 queues in same time... + + if (StatusID == 0 || !bg) + { + data->Initialize(SMSG_BATTLEFIELD_STATUS, 4+8); + *data << uint32(QueueSlot); // queue id (0...1) + *data << uint64(0); + return; + } + + data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+8+1+1+4+1+4+4+4)); + *data << uint32(QueueSlot); // queue id (0...1) - player can be in 2 queues in time + // The following segment is read as uint64 in client but can be appended as their original type. + *data << uint8(arenatype); + sLog.outDebug("BattleGroundMgr::BuildBattleGroundStatusPacket: arenatype = %u for bg instanceID %u, TypeID %u.", arenatype, bg->GetClientInstanceID(), bg->GetTypeID()); + *data << uint8(bg->isArena() ? 0xC : 0x2); + *data << uint32(bg->GetTypeID()); + *data << uint16(0x1F90); + // End of uint64 segment, decomposed this way for simplicity + *data << uint8(0); // 3.3.0 + *data << uint8(0); // 3.3.0 + *data << uint32(bg->GetClientInstanceID()); + // alliance/horde for BG and skirmish/rated for Arenas + // following displays the minimap-icon 0 = faction icon 1 = arenaicon + *data << uint8(bg->isRated()); // 1 for rated match, 0 for bg or non rated match + + *data << uint32(StatusID); // status + switch(StatusID) + { + case STATUS_WAIT_QUEUE: // status_in_queue + *data << uint32(Time1); // average wait time, milliseconds + *data << uint32(Time2); // time in queue, updated every minute!, milliseconds + break; + case STATUS_WAIT_JOIN: // status_invite + *data << uint32(bg->GetMapId()); // map id + *data << uint32(Time1); // time to remove from queue, milliseconds + break; + case STATUS_IN_PROGRESS: // status_in_progress + *data << uint32(bg->GetMapId()); // map id + *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds + *data << uint32(Time2); // time from bg start, milliseconds + *data << uint8(/*bg->isArena() ? 0 :*/ 1); // unk, possibly 0 == preparation phase, 1 == battle + break; + default: + sLog.outError("Unknown BG status!"); + break; + } +} + +void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) +{ + uint8 type = (bg->isArena() ? 1 : 0); + // last check on 3.0.3 + data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); + *data << uint8(type); // type (battleground=0/arena=1) + + if (type) // arena + { + // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H + for (int i = 1; i >= 0; --i) + { + uint32 pointsLost = bg->m_ArenaTeamRatingChanges[i] < 0 ? abs(bg->m_ArenaTeamRatingChanges[i]) : 0; + uint32 pointsGained = bg->m_ArenaTeamRatingChanges[i] > 0 ? bg->m_ArenaTeamRatingChanges[i] : 0; + + *data << uint32(pointsLost); // Rating Lost + *data << uint32(pointsGained); // Rating gained + *data << uint32(0); // Matchmaking Value + sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); + } + for (int i = 1; i >= 0; --i) + { + uint32 at_id = bg->m_ArenaTeamIds[i]; + ArenaTeam * at = objmgr.GetArenaTeamById(at_id); + if (at) + *data << at->GetName(); + else + *data << (uint8)0; + } + } + + if (bg->GetStatus() != STATUS_WAIT_LEAVE) + { + *data << uint8(0); // bg not ended + } + else + { + *data << uint8(1); // bg ended + *data << uint8(bg->GetWinner()); // who win + } + + *data << (int32)(bg->GetPlayerScoresSize()); + + for (BattleGround::BattleGroundScoreMap::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr) + { + *data << (uint64)itr->first; + *data << (int32)itr->second->KillingBlows; + if (type == 0) + { + *data << (int32)itr->second->HonorableKills; + *data << (int32)itr->second->Deaths; + *data << (int32)(itr->second->BonusHonor); + } + else + { + Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(itr->first); + if (!team && plr) + team = plr->GetBGTeam(); + *data << uint8(team == ALLIANCE ? 1 : 0); // green or yellow + + } + *data << (int32)itr->second->DamageDone; // damage done + *data << (int32)itr->second->HealingDone; // healing done + switch(bg->GetTypeID(true)) // battleground specific things + { + case BATTLEGROUND_RB: + switch(bg->GetMapId()) + { + case 489: + *data << (uint32)0x00000002; // count of next fields + *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures + *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns + break; + case 566: + *data << (uint32)0x00000001; // count of next fields + *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures + break; + case 529: + *data << (uint32)0x00000002; // count of next fields + *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted + *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended + break; + case 30: + *data << (uint32)0x00000005; // count of next fields + *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted + *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended + *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted + *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended + *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured + break; + case 607: + *data << uint32(2); + *data << uint32(((BattleGroundSAScore*)itr->second)->demolishers_destroyed); + *data << uint32(((BattleGroundSAScore*)itr->second)->gates_destroyed); + break; + default: + *data << (int32)0; // 0 + break; + } + case BATTLEGROUND_AV: + *data << (uint32)0x00000005; // count of next fields + *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted + *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended + *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted + *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended + *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured + break; + case BATTLEGROUND_WS: + *data << (uint32)0x00000002; // count of next fields + *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures + *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns + break; + case BATTLEGROUND_AB: + *data << (uint32)0x00000002; // count of next fields + *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted + *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended + break; + case BATTLEGROUND_EY: + *data << (uint32)0x00000001; // count of next fields + *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures + break; + case BATTLEGROUND_NA: + case BATTLEGROUND_BE: + case BATTLEGROUND_AA: + case BATTLEGROUND_RL: + case BATTLEGROUND_SA: + *data << uint32(2); + *data << uint32(((BattleGroundSAScore*)itr->second)->demolishers_destroyed); + *data << uint32(((BattleGroundSAScore*)itr->second)->gates_destroyed); + break; + case BATTLEGROUND_DS: // wotlk + case BATTLEGROUND_RV: // wotlk + case BATTLEGROUND_IC: // wotlk + *data << (int32)0; // 0 + break; + default: + sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); + *data << (int32)0; + break; + } + } +} + +void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, GroupJoinBattlegroundResult result) +{ + data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + *data << int32(result); + if (result == ERR_BATTLEGROUND_JOIN_TIMED_OUT || result == ERR_BATTLEGROUND_JOIN_FAILED) + *data << uint64(0); // player guid +} + +void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value) +{ + data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4); + *data << uint32(field); + *data << uint32(value); +} + +void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid) +{ + data->Initialize(SMSG_PLAY_SOUND, 4); + *data << uint32(soundid); +} + +void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid) +{ + data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); + *data << uint64(guid); +} + +void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr) +{ + data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8); + *data << uint64(plr->GetGUID()); +} + +BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId) +{ + //cause at HandleBattleGroundJoinOpcode the clients sends the instanceid he gets from + //SMSG_BATTLEFIELD_LIST we need to find the battleground with this clientinstance-id + BattleGround* bg = GetBattleGroundTemplate(bgTypeId); + if (!bg) + return NULL; + + if (bg->isArena()) + return GetBattleGround(instanceId, bgTypeId); + + for (BattleGroundSet::iterator itr = m_BattleGrounds[bgTypeId].begin(); itr != m_BattleGrounds[bgTypeId].end(); ++itr) + { + if (itr->second->GetClientInstanceID() == instanceId) + return itr->second; + } + return NULL; +} + +BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId) +{ + if (!InstanceID) + return NULL; + //search if needed + BattleGroundSet::iterator itr; + if (bgTypeId == BATTLEGROUND_TYPE_NONE) + { + for (uint32 i = BATTLEGROUND_AV; i < MAX_BATTLEGROUND_TYPE_ID; i++) + { + itr = m_BattleGrounds[i].find(InstanceID); + if (itr != m_BattleGrounds[i].end()) + return itr->second; + } + return NULL; + } + itr = m_BattleGrounds[bgTypeId].find(InstanceID); + return ((itr != m_BattleGrounds[bgTypeId].end()) ? itr->second : NULL); +} + +BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId) +{ + //map is sorted and we can be sure that lowest instance id has only BG template + return m_BattleGrounds[bgTypeId].empty() ? NULL : m_BattleGrounds[bgTypeId].begin()->second; +} + +uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id) +{ + if (IsArenaType(bgTypeId)) + return 0; //arenas don't have client-instanceids + + // we create here an instanceid, which is just for + // displaying this to the client and without any other use.. + // the client-instanceIds are unique for each battleground-type + // the instance-id just needs to be as low as possible, beginning with 1 + // the following works, because std::set is default ordered with "<" + // the optimalization would be to use as bitmask std::vector - but that would only make code unreadable + uint32 lastId = 0; + for (std::set::iterator itr = m_ClientBattleGroundIds[bgTypeId][bracket_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][bracket_id].end();) + { + if ((++lastId) != *itr) //if there is a gap between the ids, we will break.. + break; + lastId = *itr; + } + m_ClientBattleGroundIds[bgTypeId][bracket_id].insert(lastId + 1); + return lastId + 1; +} + +// create a new battleground that will really be used to play +BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated) +{ + // get the template BG + BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); + BattleGroundTypeIdList *enabledBGsOrArenas = NULL; + + if (!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return NULL; + } + bool isRandom = false; + + if (bg_template->isArena()) + enabledBGsOrArenas = &m_EnabledArenas; + else if (bgTypeId == BATTLEGROUND_RB) + { + enabledBGsOrArenas = &m_EnabledBGs; + isRandom = true; + } + + if (enabledBGsOrArenas) + { + if (!enabledBGsOrArenas->size()) + return NULL; + uint8 size = enabledBGsOrArenas->size() - 1; + bgTypeId = enabledBGsOrArenas->at(urand(0,size)); + bg_template = GetBattleGroundTemplate(bgTypeId); + if (!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return NULL; + } + } + + BattleGround *bg = NULL; + // create a copy of the BG template + switch(bgTypeId) + { + case BATTLEGROUND_AV: + bg = new BattleGroundAV(*(BattleGroundAV*)bg_template); + break; + case BATTLEGROUND_WS: + bg = new BattleGroundWS(*(BattleGroundWS*)bg_template); + break; + case BATTLEGROUND_AB: + bg = new BattleGroundAB(*(BattleGroundAB*)bg_template); + break; + case BATTLEGROUND_NA: + bg = new BattleGroundNA(*(BattleGroundNA*)bg_template); + break; + case BATTLEGROUND_BE: + bg = new BattleGroundBE(*(BattleGroundBE*)bg_template); + break; + case BATTLEGROUND_AA: + bg = new BattleGroundAA(*(BattleGroundAA*)bg_template); + break; + case BATTLEGROUND_EY: + bg = new BattleGroundEY(*(BattleGroundEY*)bg_template); + break; + case BATTLEGROUND_RL: + bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); + break; + case BATTLEGROUND_SA: + bg = new BattleGroundSA(*(BattleGroundSA*)bg_template); + break; + case BATTLEGROUND_DS: + bg = new BattleGroundDS(*(BattleGroundDS*)bg_template); + break; + case BATTLEGROUND_RV: + bg = new BattleGroundRV(*(BattleGroundRV*)bg_template); + break; + case BATTLEGROUND_IC: + bg = new BattleGroundIC(*(BattleGroundIC*)bg_template); + break; + case BATTLEGROUND_RB: + bg = new BattleGroundRB(*(BattleGroundRB*)bg_template); + break; + default: + //error, but it is handled few lines above + return 0; + } + + // generate a new instance id + bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + bg->SetClientInstanceID(CreateClientVisibleInstanceId(isRandom ? BATTLEGROUND_RB : bgTypeId, bracketEntry->GetBracketId())); + + // reset the new bg (set status to status_wait_queue from status_none) + bg->Reset(); + + // start the joining of the bg + bg->SetStatus(STATUS_WAIT_JOIN); + bg->SetBracket(bracketEntry); + bg->SetArenaType(arenaType); + bg->SetRated(isRated); + bg->SetRandom(isRandom); + bg->SetTypeID(isRandom ? BATTLEGROUND_RB : bgTypeId); + bg->SetRandomTypeID(bgTypeId); + + return bg; +} + +// used to create the BG templates +uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) +{ + // Create the BG + BattleGround *bg = NULL; + switch(bgTypeId) + { + case BATTLEGROUND_AV: bg = new BattleGroundAV; break; + case BATTLEGROUND_WS: bg = new BattleGroundWS; break; + case BATTLEGROUND_AB: bg = new BattleGroundAB; break; + case BATTLEGROUND_NA: bg = new BattleGroundNA; break; + case BATTLEGROUND_BE: bg = new BattleGroundBE; break; + case BATTLEGROUND_AA: bg = new BattleGroundAA; break; + case BATTLEGROUND_EY: bg = new BattleGroundEY; break; + case BATTLEGROUND_RL: bg = new BattleGroundRL; break; + case BATTLEGROUND_SA: bg = new BattleGroundSA; break; + case BATTLEGROUND_DS: bg = new BattleGroundDS; break; + case BATTLEGROUND_RV: bg = new BattleGroundRV; break; + case BATTLEGROUND_IC: bg = new BattleGroundIC; break; + case BATTLEGROUND_RB: bg = new BattleGroundRB; break; + default: + bg = new BattleGround; + break; + } + + bg->SetMapId(MapID); + bg->SetTypeID(bgTypeId); + bg->SetInstanceID(0); + bg->SetArenaorBGType(IsArena); + bg->SetMinPlayersPerTeam(MinPlayersPerTeam); + bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); + bg->SetMinPlayers(MinPlayersPerTeam * 2); + bg->SetMaxPlayers(MaxPlayersPerTeam * 2); + bg->SetName(BattleGroundName); + bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); + bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); + bg->SetLevelRange(LevelMin, LevelMax); + + // add bg to update list + AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg); + + // return some not-null value, bgTypeId is good enough for me + return bgTypeId; +} + +void BattleGroundMgr::CreateInitialBattleGrounds() +{ + float AStartLoc[4]; + float HStartLoc[4]; + uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2; + BattlemasterListEntry const *bl; + WorldSafeLocsEntry const *start; + bool IsArena; + + uint32 count = 0; + + // 0 1 2 3 4 5 6 7 8 + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template WHERE disable = 0"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 bgTypeID_ = fields[0].GetUInt32(); + + // can be overwrite by values from DB + bl = sBattlemasterListStore.LookupEntry(bgTypeID_); + if (!bl) + { + sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_); + continue; + } + + BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_); + + IsArena = (bl->type == TYPE_ARENA); + MinPlayersPerTeam = fields[1].GetUInt32(); + MaxPlayersPerTeam = fields[2].GetUInt32(); + MinLvl = fields[3].GetUInt32(); + MaxLvl = fields[4].GetUInt32(); + //check values from DB + if (MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam) + { + MinPlayersPerTeam = 0; // by default now expected strong full bg requirement + MaxPlayersPerTeam = 40; + } + if (MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl) + { + //TO-DO: FIX ME + MinLvl = 0;//bl->minlvl; + MaxLvl = 80;//bl->maxlvl; + } + + start1 = fields[5].GetUInt32(); + + start = sWorldSafeLocsStore.LookupEntry(start1); + if (start) + { + AStartLoc[0] = start->x; + AStartLoc[1] = start->y; + AStartLoc[2] = start->z; + AStartLoc[3] = fields[6].GetFloat(); + } + else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_RB) + { + AStartLoc[0] = 0; + AStartLoc[1] = 0; + AStartLoc[2] = 0; + AStartLoc[3] = fields[6].GetFloat(); + } + else + { + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1); + continue; + } + + start2 = fields[7].GetUInt32(); + + start = sWorldSafeLocsStore.LookupEntry(start2); + if (start) + { + HStartLoc[0] = start->x; + HStartLoc[1] = start->y; + HStartLoc[2] = start->z; + HStartLoc[3] = fields[8].GetFloat(); + } + else if (bgTypeID == BATTLEGROUND_AA || bgTypeID == BATTLEGROUND_RB) + { + HStartLoc[0] = 0; + HStartLoc[1] = 0; + HStartLoc[2] = 0; + HStartLoc[3] = fields[8].GetFloat(); + } + else + { + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2); + continue; + } + + //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl); + if (!CreateBattleGround(bgTypeID, IsArena, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) + continue; + + if (IsArena) + { + if (bgTypeID != BATTLEGROUND_AA) + m_EnabledArenas.push_back(bgTypeID); + } + else if (bgTypeID != BATTLEGROUND_RB) + m_EnabledBGs.push_back(bgTypeID); + ++count; + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u battlegrounds", count); +} + +void BattleGroundMgr::InitAutomaticArenaPointDistribution() +{ + if (!sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) + return; + + uint64 wstime = sWorld.getWorldState(WS_ARENA_DISTRIBUTION_TIME); + time_t curtime = time(NULL); + sLog.outDebug("Initializing Automatic Arena Point Distribution"); + if (wstime < curtime) + { + m_NextAutoDistributionTime = curtime; // reset will be called in the next update + sLog.outDebug("Battleground: Next arena point distribution time in the past, reseting it now."); + } + else + m_NextAutoDistributionTime = time_t(wstime); + sLog.outDebug("Automatic Arena Point Distribution initialized."); +} + +void BattleGroundMgr::DistributeArenaPoints() +{ + // used to distribute arena points based on last week's stats + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START); + + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START); + + //temporary structure for storing maximum points to add values for all players + std::map PlayerPoints; + + //at first update all points for all team members + for (ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr) + if (ArenaTeam * at = team_itr->second) + at->UpdateArenaPointsHelper(PlayerPoints); + + //cycle that gives points to all players + for (std::map::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr) + { + //update to database + CharacterDatabase.PExecute("UPDATE characters SET arenaPoints = arenaPoints + '%u' WHERE guid = '%u'", plr_itr->second, plr_itr->first); + + //add points to player if online + Player* pl = objmgr.GetPlayer(plr_itr->first); + if (pl) + pl->ModifyArenaPoints(plr_itr->second); + } + + PlayerPoints.clear(); + + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END); + + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START); + for (ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr) + { + if (ArenaTeam * at = titr->second) + { + at->FinishWeek(); // set played this week etc values to 0 in memory, too + at->SaveToDB(); // save changes + at->NotifyStatsChanged(); // notify the players of the changes + } + } + + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END); + + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END); +} + +void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId, uint8 fromWhere) +{ + if (!plr) + return; + + uint32 win_kills = plr->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; + uint32 win_arena = plr->GetRandomWinner() ? BG_REWARD_WINNER_ARENA_LAST : BG_REWARD_WINNER_ARENA_FIRST; + uint32 loos_kills = plr->GetRandomWinner() ? BG_REWARD_LOOSER_HONOR_LAST : BG_REWARD_LOOSER_HONOR_FIRST; + + win_kills = (uint32)Trinity::Honor::hk_honor_at_level(plr->getLevel(), win_kills); + loos_kills = (uint32)Trinity::Honor::hk_honor_at_level(plr->getLevel(), loos_kills); + + data->Initialize(SMSG_BATTLEFIELD_LIST); + *data << uint64(guid); // battlemaster guid + *data << uint8(fromWhere); // from where you joined + *data << uint32(bgTypeId); // battleground id + *data << uint8(0); // unk + *data << uint8(0); // unk + + // Rewards + *data << uint8(plr->GetRandomWinner()); // 3.3.3 hasWin + *data << uint32(win_kills); // 3.3.3 winHonor + *data << uint32(win_arena); // 3.3.3 winArena + *data << uint32(loos_kills); // 3.3.3 lossHonor + + uint8 isRandom = bgTypeId == BATTLEGROUND_RB; + + *data << uint8(isRandom); // 3.3.3 isRandom + if (isRandom) + { + // Rewards (random) + *data << uint8(plr->GetRandomWinner()); // 3.3.3 hasWin_Random + *data << uint32(win_kills); // 3.3.3 winHonor_Random + *data << uint32(win_arena); // 3.3.3 winArena_Random + *data << uint32(loos_kills); // 3.3.3 lossHonor_Random + } + + if (bgTypeId == BATTLEGROUND_AA) // arena + { + *data << uint32(0); // unk (count?) + } + else // battleground + { + size_t count_pos = data->wpos(); + uint32 count = 0; + *data << uint32(0); // number of bg instances + + if (BattleGround* bgTemplate = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId)) + { + // expected bracket entry + if (PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgTemplate->GetMapId(),plr->getLevel())) + { + BattleGroundBracketId bracketId = bracketEntry->GetBracketId(); + for (std::set::iterator itr = m_ClientBattleGroundIds[bgTypeId][bracketId].begin(); itr != m_ClientBattleGroundIds[bgTypeId][bracketId].end();++itr) + { + *data << uint32(*itr); + ++count; + } + data->put(count_pos , count); + } + } + } +} + +void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGroundTypeId bgTypeId) +{ + BattleGround *bg = GetBattleGround(instanceId, bgTypeId); + if (bg) + { + uint32 mapid = bg->GetMapId(); + float x, y, z, O; + uint32 team = pl->GetBGTeam(); + if (team == 0) + team = pl->GetTeam(); + bg->GetTeamStartLoc(team, x, y, z, O); + + sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O); + pl->TeleportTo(mapid, x, y, z, O); + } + else + { + sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId); + } +} + +void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid) +{ + WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); + uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds + if (time_ == uint32(-1)) + time_ = 0; + data << guid << time_; + pl->GetSession()->SendPacket(&data); +} + +bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId) +{ + return (bgTypeId == BATTLEGROUND_AA || + bgTypeId == BATTLEGROUND_BE || + bgTypeId == BATTLEGROUND_NA || + bgTypeId == BATTLEGROUND_DS || + bgTypeId == BATTLEGROUND_RV || + bgTypeId == BATTLEGROUND_RL || + bgTypeId == BATTLEGROUND_DS); +} + +BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType) +{ + switch(bgTypeId) + { + case BATTLEGROUND_WS: + return BATTLEGROUND_QUEUE_WS; + case BATTLEGROUND_AB: + return BATTLEGROUND_QUEUE_AB; + case BATTLEGROUND_AV: + return BATTLEGROUND_QUEUE_AV; + case BATTLEGROUND_EY: + return BATTLEGROUND_QUEUE_EY; + case BATTLEGROUND_SA: + return BATTLEGROUND_QUEUE_SA; + case BATTLEGROUND_IC: + return BATTLEGROUND_QUEUE_IC; + case BATTLEGROUND_RB: + return BATTLEGROUND_QUEUE_RB; + case BATTLEGROUND_AA: + case BATTLEGROUND_NA: + case BATTLEGROUND_RL: + case BATTLEGROUND_BE: + case BATTLEGROUND_DS: + case BATTLEGROUND_RV: + switch(arenaType) + { + case ARENA_TYPE_2v2: + return BATTLEGROUND_QUEUE_2v2; + case ARENA_TYPE_3v3: + return BATTLEGROUND_QUEUE_3v3; + case ARENA_TYPE_5v5: + return BATTLEGROUND_QUEUE_5v5; + default: + return BATTLEGROUND_QUEUE_NONE; + } + default: + return BATTLEGROUND_QUEUE_NONE; + } +} + +BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId) +{ + switch(bgQueueTypeId) + { + case BATTLEGROUND_QUEUE_WS: + return BATTLEGROUND_WS; + case BATTLEGROUND_QUEUE_AB: + return BATTLEGROUND_AB; + case BATTLEGROUND_QUEUE_AV: + return BATTLEGROUND_AV; + case BATTLEGROUND_QUEUE_EY: + return BATTLEGROUND_EY; + case BATTLEGROUND_QUEUE_SA: + return BATTLEGROUND_SA; + case BATTLEGROUND_QUEUE_IC: + return BATTLEGROUND_IC; + case BATTLEGROUND_QUEUE_RB: + return BATTLEGROUND_RB; + case BATTLEGROUND_QUEUE_2v2: + case BATTLEGROUND_QUEUE_3v3: + case BATTLEGROUND_QUEUE_5v5: + return BATTLEGROUND_AA; + default: + return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing) + } +} + +uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId) +{ + switch(bgQueueTypeId) + { + case BATTLEGROUND_QUEUE_2v2: + return ARENA_TYPE_2v2; + case BATTLEGROUND_QUEUE_3v3: + return ARENA_TYPE_3v3; + case BATTLEGROUND_QUEUE_5v5: + return ARENA_TYPE_5v5; + default: + return 0; + } +} + +void BattleGroundMgr::ToggleTesting() +{ + m_Testing = !m_Testing; + if (m_Testing) + sWorld.SendWorldText(LANG_DEBUG_BG_ON); + else + sWorld.SendWorldText(LANG_DEBUG_BG_OFF); +} + +void BattleGroundMgr::ToggleArenaTesting() +{ + m_ArenaTesting = !m_ArenaTesting; + if (m_ArenaTesting) + sWorld.SendWorldText(LANG_DEBUG_ARENA_ON); + else + sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF); +} + +void BattleGroundMgr::SetHolidayWeekends(uint32 mask) +{ + for (uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype) + { + if (BattleGround * bg = GetBattleGroundTemplate(BattleGroundTypeId(bgtype))) + { + bg->SetHoliday(mask & (1 << bgtype)); + } + } +} + +void BattleGroundMgr::ScheduleQueueUpdate(uint32 arenaRating, uint8 arenaType, BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id) +{ + //This method must be atomic, TODO add mutex + //we will use only 1 number created of bgTypeId and bracket_id + uint64 schedule_id = ((uint64)arenaRating << 32) | (arenaType << 24) | (bgQueueTypeId << 16) | (bgTypeId << 8) | bracket_id; + bool found = false; + for (uint8 i = 0; i < m_QueueUpdateScheduler.size(); i++) + { + if (m_QueueUpdateScheduler[i] == schedule_id) + { + found = true; + break; + } + } + if (!found) + m_QueueUpdateScheduler.push_back(schedule_id); +} + +uint32 BattleGroundMgr::GetMaxRatingDifference() const +{ + // this is for stupid people who can't use brain and set max rating difference to 0 + uint32 diff = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); + if (diff == 0) + diff = 5000; + return diff; +} + +uint32 BattleGroundMgr::GetRatingDiscardTimer() const +{ + return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); +} + +uint32 BattleGroundMgr::GetPrematureFinishTime() const +{ + return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); +} + +void BattleGroundMgr::LoadBattleMastersEntry() +{ + mBattleMastersMap.clear(); // need for reload case + + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,bg_template FROM battlemaster_entry"); + + uint32 count = 0; + + if (!result) + { + barGoLink bar(1); + bar.step(); + + sLog.outString(); + sLog.outString(">> Loaded 0 battlemaster entries - table is empty!"); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + ++count; + bar.step(); + + Field *fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 bgTypeId = fields[1].GetUInt32(); + if (!sBattlemasterListStore.LookupEntry(bgTypeId)) + { + sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId); + continue; + } + + mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId); + + } while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u battlemaster entries", count); +} + +HolidayIds BattleGroundMgr::BGTypeToWeekendHolidayId(BattleGroundTypeId bgTypeId) +{ + switch (bgTypeId) + { + case BATTLEGROUND_AV: return HOLIDAY_CALL_TO_ARMS_AV; + case BATTLEGROUND_EY: return HOLIDAY_CALL_TO_ARMS_EY; + case BATTLEGROUND_WS: return HOLIDAY_CALL_TO_ARMS_WS; + case BATTLEGROUND_SA: return HOLIDAY_CALL_TO_ARMS_SA; + default: return HOLIDAY_NONE; + } +} + +BattleGroundTypeId BattleGroundMgr::WeekendHolidayIdToBGType(HolidayIds holiday) +{ + switch (holiday) + { + case HOLIDAY_CALL_TO_ARMS_AV: return BATTLEGROUND_AV; + case HOLIDAY_CALL_TO_ARMS_EY: return BATTLEGROUND_EY; + case HOLIDAY_CALL_TO_ARMS_WS: return BATTLEGROUND_WS; + case HOLIDAY_CALL_TO_ARMS_SA: return BATTLEGROUND_SA; + default: return BATTLEGROUND_TYPE_NONE; + } +} + +bool BattleGroundMgr::IsBGWeekend(BattleGroundTypeId bgTypeId) +{ + return IsHolidayActive(BGTypeToWeekendHolidayId(bgTypeId)); +} + +void BattleGroundMgr::DoCompleteAchievement(uint32 achievement, Player * player) +{ + AchievementEntry const* AE = GetAchievementStore()->LookupEntry(achievement); + + if (!player) + { + //Map::PlayerList const &PlayerList = this->GetPlayers(); + //GroupsQueueType::iterator group = SelectedGroups.begin(); + + //if (!PlayerList.isEmpty()) + //for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + // for (GroupsQueueType::iterator itr = group; itr != SelectedGroups.end(); ++itr) + // if (Player *pPlayer = itr->getSource()) + // pPlayer->CompletedAchievement(AE); + } + else + { + player->CompletedAchievement(AE); + } +} diff --git a/src/server/game/BattleGrounds/BattleGroundMgr.h b/src/server/game/BattleGrounds/BattleGroundMgr.h new file mode 100644 index 00000000000..569651a95b3 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundMgr.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDMGR_H +#define __BATTLEGROUNDMGR_H + +#include "Common.h" +#include "Policies/Singleton.h" + +#include "DBCEnums.h" +#include "BattleGround.h" + +typedef std::map BattleGroundSet; + +//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears +typedef std::list BGFreeSlotQueueType; + +typedef UNORDERED_MAP BattleMastersMap; + +#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day +#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10 +#define WS_ARENA_DISTRIBUTION_TIME 20001 // Custom worldstate + +struct GroupQueueInfo; // type predefinition +struct PlayerQueueInfo // stores information for players in queue +{ + uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes + GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo +}; + +struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) +{ + std::map Players; // player queue info map + uint32 Team; // Player team (ALLIANCE/HORDE) + BattleGroundTypeId BgTypeId; // battleground type id + bool IsRated; // rated + uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG + uint32 ArenaTeamId; // team id if rated match + uint32 JoinTime; // time when group was added + uint32 RemoveInviteTime; // time when we will remove invite for players in group + uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG + uint32 ArenaTeamRating; // if rated match, inited to the rating of the team + uint32 OpponentsTeamRating; // for rated arena matches +}; + +enum BattleGroundQueueGroupTypes +{ + BG_QUEUE_PREMADE_ALLIANCE = 0, + BG_QUEUE_PREMADE_HORDE = 1, + BG_QUEUE_NORMAL_ALLIANCE = 2, + BG_QUEUE_NORMAL_HORDE = 3 +}; +#define BG_QUEUE_GROUP_TYPES_COUNT 4 + +class BattleGround; +class BattleGroundQueue +{ + public: + BattleGroundQueue(); + ~BattleGroundQueue(); + + void Update(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id, uint8 arenaType = 0, bool isRated = false, uint32 minRating = 0); + + void FillPlayersToBG(BattleGround* bg, BattleGroundBracketId bracket_id); + bool CheckPremadeMatch(BattleGroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); + bool CheckNormalMatch(BattleGround* bg_template, BattleGroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); + bool CheckSkirmishForSameFaction(BattleGroundBracketId bracket_id, uint32 minPlayersPerTeam); + GroupQueueInfo * AddGroup(Player* leader, Group* group, BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* backetEntry, uint8 ArenaType, bool isRated, bool isPremade, uint32 ArenaRating, uint32 ArenaTeamId = 0); + void RemovePlayer(const uint64& guid, bool decreaseInvitedCount); + bool IsPlayerInvited(const uint64& pl_guid, const uint32 bgInstanceGuid, const uint32 removeTime); + bool GetPlayerGroupInfoData(const uint64& guid, GroupQueueInfo* ginfo); + void PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); + uint32 GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BattleGroundBracketId bracket_id); + + typedef std::map QueuedPlayersMap; + QueuedPlayersMap m_QueuedPlayers; + + //we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well + typedef std::list GroupsQueueType; + + /* + This two dimensional array is used to store All queued groups + First dimension specifies the bgTypeId + Second dimension specifies the player's group types - + BG_QUEUE_PREMADE_ALLIANCE is used for premade alliance groups and alliance rated arena teams + BG_QUEUE_PREMADE_HORDE is used for premade horde groups and horde rated arena teams + BG_QUEUE_NORMAL_ALLIANCE is used for normal (or small) alliance groups or non-rated arena matches + BG_QUEUE_NORMAL_HORDE is used for normal (or small) horde groups or non-rated arena matches + */ + GroupsQueueType m_QueuedGroups[MAX_BATTLEGROUND_BRACKETS][BG_QUEUE_GROUP_TYPES_COUNT]; + + // class to select and invite groups to bg + class SelectionPool + { + public: + void Init(); + bool AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount); + bool KickGroup(uint32 size); + uint32 GetPlayerCount() const {return PlayerCount;} + public: + GroupsQueueType SelectedGroups; + private: + uint32 PlayerCount; + }; + + //one selection pool for horde, other one for alliance + SelectionPool m_SelectionPools[BG_TEAMS_COUNT]; + + private: + + bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side); + uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; + uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; + uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; +}; + +/* + 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 +{ + public: + BGQueueInviteEvent(const uint64& pl_guid, uint32 BgInstanceGUID, BattleGroundTypeId BgTypeId, uint8 arenaType, uint32 removeTime) : + m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID), m_BgTypeId(BgTypeId), m_ArenaType(arenaType), m_RemoveTime(removeTime) + { + }; + virtual ~BGQueueInviteEvent() {}; + + virtual bool Execute(uint64 e_time, uint32 p_time); + virtual void Abort(uint64 e_time); + private: + uint64 m_PlayerGuid; + uint32 m_BgInstanceGUID; + BattleGroundTypeId m_BgTypeId; + uint8 m_ArenaType; + uint32 m_RemoveTime; +}; + +/* + This class is used to remove player from BG queue after 1 minute 20 seconds from first invitation + 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 +{ + public: + BGQueueRemoveEvent(const uint64& pl_guid, uint32 bgInstanceGUID, BattleGroundTypeId BgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 removeTime) + : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId) + {} + + virtual ~BGQueueRemoveEvent() {} + + virtual bool Execute(uint64 e_time, uint32 p_time); + virtual void Abort(uint64 e_time); + private: + uint64 m_PlayerGuid; + uint32 m_BgInstanceGUID; + uint32 m_RemoveTime; + BattleGroundTypeId m_BgTypeId; + BattleGroundQueueTypeId m_BgQueueTypeId; +}; + +class BattleGroundMgr +{ + public: + /* Construction */ + BattleGroundMgr(); + ~BattleGroundMgr(); + void Update(uint32 diff); + + /* Packet Building */ + void BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr); + void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid); + void BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player *plr, BattleGroundTypeId bgTypeId, uint8 fromWhere); + void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, GroupJoinBattlegroundResult result); + void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); + void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); + void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype); + void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); + void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid); + + /* Battlegrounds */ + BattleGround* GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId); + BattleGround* GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId); //there must be uint32 because MAX_BATTLEGROUND_TYPE_ID means unknown + + BattleGround* GetBattleGroundTemplate(BattleGroundTypeId bgTypeId); + BattleGround* CreateNewBattleGround(BattleGroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated); + + uint32 CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO); + + void AddBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId, BattleGround* BG) { m_BattleGrounds[bgTypeId][InstanceID] = BG; }; + void RemoveBattleGround(uint32 instanceID, BattleGroundTypeId bgTypeId) { m_BattleGrounds[bgTypeId].erase(instanceID); } + uint32 CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id); + + void CreateInitialBattleGrounds(); + void DeleteAllBattleGrounds(); + + void SendToBattleGround(Player *pl, uint32 InstanceID, BattleGroundTypeId bgTypeId); + + /* Battleground queues */ + //these queues are instantiated when creating BattlegroundMrg + BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; // public, because we need to access them in BG handler code + + BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPE_ID]; + + void ScheduleQueueUpdate(uint32 arenaRating, uint8 arenaType, BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BattleGroundBracketId bracket_id); + uint32 GetMaxRatingDifference() const; + uint32 GetRatingDiscardTimer() const; + uint32 GetPrematureFinishTime() const; + + void InitAutomaticArenaPointDistribution(); + void DistributeArenaPoints(); + void ToggleArenaTesting(); + void ToggleTesting(); + + void SetHolidayWeekends(uint32 mask); + void LoadBattleMastersEntry(); + BattleGroundTypeId GetBattleMasterBG(uint32 entry) const + { + BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry); + if (itr != mBattleMastersMap.end()) + return itr->second; + return BATTLEGROUND_WS; + } + + bool isArenaTesting() const { return m_ArenaTesting; } + bool isTesting() const { return m_Testing; } + + static bool IsArenaType(BattleGroundTypeId bgTypeId); + static bool IsBattleGroundType(BattleGroundTypeId bgTypeId) { return !BattleGroundMgr::IsArenaType(bgTypeId); } + static BattleGroundQueueTypeId BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType); + static BattleGroundTypeId BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId); + static uint8 BGArenaType(BattleGroundQueueTypeId bgQueueTypeId); + + static HolidayIds BGTypeToWeekendHolidayId(BattleGroundTypeId bgTypeId); + static BattleGroundTypeId WeekendHolidayIdToBGType(HolidayIds holiday); + static bool IsBGWeekend(BattleGroundTypeId bgTypeId); + void DoCompleteAchievement(uint32 achievement, Player * player = NULL); + private: + BattleMastersMap mBattleMastersMap; + + typedef std::vector BattleGroundTypeIdList; + /* Battlegrounds */ + BattleGroundSet m_BattleGrounds[MAX_BATTLEGROUND_TYPE_ID]; + BattleGroundTypeIdList m_EnabledArenas; + BattleGroundTypeIdList m_EnabledBGs; + std::vector m_QueueUpdateScheduler; + std::set m_ClientBattleGroundIds[MAX_BATTLEGROUND_TYPE_ID][MAX_BATTLEGROUND_BRACKETS]; //the instanceids just visible for the client + uint32 m_NextRatingDiscardUpdate; + time_t m_NextAutoDistributionTime; + uint32 m_AutoDistributionTimeChecker; + bool m_ArenaTesting; + bool m_Testing; +}; + +#define sBattleGroundMgr Trinity::Singleton::Instance() +#endif + diff --git a/src/server/game/BattleGrounds/BattleGroundNA.cpp b/src/server/game/BattleGrounds/BattleGroundNA.cpp new file mode 100644 index 00000000000..793cf13b3cb --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundNA.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundNA.h" +#include "Language.h" +#include "Object.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "WorldPacket.h" + +BattleGroundNA::BattleGroundNA() +{ + m_BgObjects.resize(BG_NA_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundNA::~BattleGroundNA() +{ + +} + +void BattleGroundNA::Update(uint32 diff) +{ + BattleGround::Update(diff); + + /*if (GetStatus() == STATUS_IN_PROGRESS) + { + // update something + }*/ +} + +void BattleGroundNA::StartingEventCloseDoors() +{ + for (uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + +void BattleGroundNA::StartingEventOpenDoors() +{ + for (uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; ++i) + DoorOpen(i); + + for (uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; ++i) + SpawnBGObject(i, 60); +} + +void BattleGroundNA::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundNAScore* sc = new BattleGroundNAScore; + + m_PlayerScores[plr->GetGUID()] = sc; + + UpdateArenaWorldState(); +} + +void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("BattleGroundNA: Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player,killer); + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +bool BattleGroundNA::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),4055.504395,2919.660645,13.611241,player->GetOrientation(),false); + return true; +} + +void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 4536: // buff trigger? + case 4537: // buff trigger? + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if (buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +void BattleGroundNA::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0xa11) << uint32(1); // 9 + UpdateArenaWorldState(); +} + +void BattleGroundNA::Reset() +{ + //call parent's class reset + BattleGround::Reset(); +} + +bool BattleGroundNA::SetupBattleGround() +{ + // gates + if (!AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854, 2966.833, 12.6462, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179, 2874.97, 12.39171, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709, 2981.777, 10.70117, -2.648788, 0, 0, 0.9697962, -0.2439165, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064, 2858.438, 10.23631, 0.4928045, 0, 0, 0.2439165, 0.9697962, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_NA_OBJECT_BUFF_1, BG_NA_OBJECT_TYPE_BUFF_1, 4009.189941, 2895.250000, 13.052700, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) + || !AddObject(BG_NA_OBJECT_BUFF_2, BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078, 2946.350098, 13.051300, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) + { + sLog.outErrorDb("BatteGroundNA: Failed to spawn some object!"); + return false; + } + + return true; +} + +/* +20:12:14 id:036668 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86 +0000: 2f 02 00 00 72 0e 00 00 00 00 00 00 09 00 11 0a | /...r........... +0010: 00 00 01 00 00 00 0f 0a 00 00 00 00 00 00 10 0a | ................ +0020: 00 00 00 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................ +0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................ +0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................ +0050: 00 00 00 00 00 00 | ...... +*/ diff --git a/src/server/game/BattleGrounds/BattleGroundNA.h b/src/server/game/BattleGrounds/BattleGroundNA.h new file mode 100644 index 00000000000..a11d311515d --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundNA.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDNA_H +#define __BATTLEGROUNDNA_H + +class BattleGround; + +enum BattleGroundNAObjectTypes +{ + BG_NA_OBJECT_DOOR_1 = 0, + BG_NA_OBJECT_DOOR_2 = 1, + BG_NA_OBJECT_DOOR_3 = 2, + BG_NA_OBJECT_DOOR_4 = 3, + BG_NA_OBJECT_BUFF_1 = 4, + BG_NA_OBJECT_BUFF_2 = 5, + BG_NA_OBJECT_MAX = 6 +}; + +enum BattleGroundNAObjects +{ + BG_NA_OBJECT_TYPE_DOOR_1 = 183978, + BG_NA_OBJECT_TYPE_DOOR_2 = 183980, + BG_NA_OBJECT_TYPE_DOOR_3 = 183977, + BG_NA_OBJECT_TYPE_DOOR_4 = 183979, + BG_NA_OBJECT_TYPE_BUFF_1 = 184663, + BG_NA_OBJECT_TYPE_BUFF_2 = 184664 +}; + +class BattleGroundNAScore : public BattleGroundScore +{ + public: + BattleGroundNAScore() {}; + virtual ~BattleGroundNAScore() {}; + //TODO fix me +}; + +class BattleGroundNA : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundNA(); + ~BattleGroundNA(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d); + void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundRB.cpp b/src/server/game/BattleGrounds/BattleGroundRB.cpp new file mode 100644 index 00000000000..cf22154ed11 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundRB.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundRB.h" +#include "Language.h" + +BattleGroundRB::BattleGroundRB() +{ + //TODO FIX ME! + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = 0; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; +} + +BattleGroundRB::~BattleGroundRB() +{ + +} + +void BattleGroundRB::Update(uint32 diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundRB::StartingEventCloseDoors() +{ +} + +void BattleGroundRB::StartingEventOpenDoors() +{ +} + +void BattleGroundRB::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundRBScore* sc = new BattleGroundRBScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundRB::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) +{ +} + +void BattleGroundRB::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; +} + +void BattleGroundRB::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +{ + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if (itr == m_PlayerScores.end()) // player not found... + return; + + BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); +} diff --git a/src/server/game/BattleGrounds/BattleGroundRB.h b/src/server/game/BattleGrounds/BattleGroundRB.h new file mode 100644 index 00000000000..a40ade5adfe --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundRB.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDRB_H +#define __BATTLEGROUNDRB_H + +class BattleGround; + +class BattleGroundRBScore : public BattleGroundScore +{ + public: + BattleGroundRBScore() {}; + virtual ~BattleGroundRBScore() {}; +}; + +class BattleGroundRB : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundRB(); + ~BattleGroundRB(); + void Update(uint32 diff); + + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + + private: +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundRL.cpp b/src/server/game/BattleGrounds/BattleGroundRL.cpp new file mode 100644 index 00000000000..ef2ec3cfa94 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundRL.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundRL.h" +#include "Language.h" +#include "Object.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "WorldPacket.h" + +BattleGroundRL::BattleGroundRL() +{ + m_BgObjects.resize(BG_RL_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundRL::~BattleGroundRL() +{ + +} + +void BattleGroundRL::Update(uint32 diff) +{ + BattleGround::Update(diff); + + /*if (GetStatus() == STATUS_IN_PROGRESS) + { + // update something + }*/ +} + +void BattleGroundRL::StartingEventCloseDoors() +{ + for (uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + +void BattleGroundRL::StartingEventOpenDoors() +{ + for (uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; ++i) + DoorOpen(i); + + for (uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; ++i) + SpawnBGObject(i, 60); +} + +void BattleGroundRL::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundRLScore* sc = new BattleGroundRLScore; + + m_PlayerScores[plr->GetGUID()] = sc; + + UpdateArenaWorldState(); +} + +void BattleGroundRL::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player,killer); + + UpdateArenaWorldState(); + CheckArenaWinConditions(); +} + +bool BattleGroundRL::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(),1285.810547,1667.896851,39.957642,player->GetOrientation(),false); + return true; +} + +void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 4696: // buff trigger? + case 4697: // buff trigger? + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if (buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +void BattleGroundRL::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(0xbba) << uint32(1); // 9 + UpdateArenaWorldState(); +} + +void BattleGroundRL::Reset() +{ + //call parent's reset + BattleGround::Reset(); +} + +bool BattleGroundRL::SetupBattleGround() +{ + // gates + if (!AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561, 1601.938, 31.60557, -1.457349, 0, 0, -0.6658813, 0.7460576, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648, 1730.557, 31.60557, 1.684245, 0, 0, 0.7460582, 0.6658807, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_RL_OBJECT_BUFF_1, BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971, 1632.719971, 36.730400, -1.448624, 0, 0, 0.6626201, -0.7489557, 120) + || !AddObject(BG_RL_OBJECT_BUFF_2, BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049, 1699.170044, 34.872601, -0.06981307, 0, 0, 0.03489945, -0.9993908, 120)) + { + sLog.outErrorDb("BatteGroundRL: Failed to spawn some object!"); + return false; + } + + return true; +} + +/* +Packet S->C, id 600, SMSG_INIT_WORLD_STATES (706), len 86 +0000: 3C 02 00 00 80 0F 00 00 00 00 00 00 09 00 BA 0B | <............... +0010: 00 00 01 00 00 00 B9 0B 00 00 02 00 00 00 B8 0B | ................ +0020: 00 00 00 00 00 00 D8 08 00 00 00 00 00 00 D7 08 | ................ +0030: 00 00 00 00 00 00 D6 08 00 00 00 00 00 00 D5 08 | ................ +0040: 00 00 00 00 00 00 D3 08 00 00 00 00 00 00 D4 08 | ................ +0050: 00 00 00 00 00 00 | ...... +*/ diff --git a/src/server/game/BattleGrounds/BattleGroundRL.h b/src/server/game/BattleGrounds/BattleGroundRL.h new file mode 100644 index 00000000000..3b4c10a7c9a --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundRL.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDRL_H +#define __BATTLEGROUNDRL_H + +class BattleGround; + +enum BattleGroundRLObjectTypes +{ + BG_RL_OBJECT_DOOR_1 = 0, + BG_RL_OBJECT_DOOR_2 = 1, + BG_RL_OBJECT_BUFF_1 = 2, + BG_RL_OBJECT_BUFF_2 = 3, + BG_RL_OBJECT_MAX = 4 +}; + +enum BattleGroundRLObjects +{ + BG_RL_OBJECT_TYPE_DOOR_1 = 185918, + BG_RL_OBJECT_TYPE_DOOR_2 = 185917, + BG_RL_OBJECT_TYPE_BUFF_1 = 184663, + BG_RL_OBJECT_TYPE_BUFF_2 = 184664 +}; + +class BattleGroundRLScore : public BattleGroundScore +{ + public: + BattleGroundRLScore() {}; + virtual ~BattleGroundRLScore() {}; + //TODO fix me +}; + +class BattleGroundRL : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundRL(); + ~BattleGroundRL(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundRV.cpp b/src/server/game/BattleGrounds/BattleGroundRV.cpp new file mode 100644 index 00000000000..fcc53dbbcf9 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundRV.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundRV.h" +#include "ObjectAccessor.h" +#include "Language.h" +#include "Player.h" +#include "WorldPacket.h" +#include "GameObject.h" + +BattleGroundRV::BattleGroundRV() +{ + m_BgObjects.resize(BG_RV_OBJECT_MAX); + + m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M; + m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S; + m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S; + m_StartDelayTimes[BG_STARTING_EVENT_FOURTH] = BG_START_DELAY_NONE; + //we must set messageIds + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_ARENA_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_ARENA_THIRTY_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN; +} + +BattleGroundRV::~BattleGroundRV() +{ + +} + +void BattleGroundRV::Update(uint32 diff) +{ + BattleGround::Update(diff); + + if (getTimer() < diff) + { + uint32 i; + switch(getState()) + { + case BG_RV_STATE_OPEN_FENCES: + { + setTimer(BG_RV_PILAR_TO_FIRE_TIMER); + setState(BG_RV_STATE_CLOSE_FIRE); + break; + } + case BG_RV_STATE_CLOSE_FIRE: + for (i = BG_RV_OBJECT_FIRE_1; i <= BG_RV_OBJECT_FIREDOOR_2; ++i) + DoorClose(i); + setTimer(BG_RV_FIRE_TO_PILAR_TIMER); + setState(BG_RV_STATE_OPEN_PILARS); + break; + case BG_RV_STATE_OPEN_PILARS: + for (i = BG_RV_OBJECT_PILAR_1; i <= BG_RV_OBJECT_PULLEY_2; ++i) + DoorOpen(i); + setTimer(BG_RV_PILAR_TO_FIRE_TIMER); + setState(BG_RV_STATE_OPEN_FIRE); + break; + case BG_RV_STATE_OPEN_FIRE: + for (i = BG_RV_OBJECT_FIRE_1; i <= BG_RV_OBJECT_FIREDOOR_2; ++i) + DoorOpen(i); + setTimer(BG_RV_FIRE_TO_PILAR_TIMER); + setState(BG_RV_STATE_CLOSE_PILARS); + break; + case BG_RV_STATE_CLOSE_PILARS: + uint32 i; + for (i = BG_RV_OBJECT_PILAR_1; i <= BG_RV_OBJECT_PULLEY_2; ++i) + DoorOpen(i); + setTimer(BG_RV_PILAR_TO_FIRE_TIMER); + setState(BG_RV_STATE_CLOSE_FIRE); + break; + } + } + else + setTimer(getTimer() - diff); +} + +void BattleGroundRV::StartingEventCloseDoors() +{ +} + +void BattleGroundRV::StartingEventOpenDoors() +{ + // Buff respawn + SpawnBGObject(BG_RV_OBJECT_BUFF_1, 90); + SpawnBGObject(BG_RV_OBJECT_BUFF_2, 90); + // Open fences + DoorOpen(BG_RV_OBJECT_FENCE_1); + DoorOpen(BG_RV_OBJECT_FENCE_2); + // Elevators + DoorOpen(BG_RV_OBJECT_ELEVATOR_1); + DoorOpen(BG_RV_OBJECT_ELEVATOR_2); + + setState(BG_RV_STATE_OPEN_FENCES); + setTimer(BG_RV_FIRST_TIMER); +} + +void BattleGroundRV::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundRVScore* sc = new BattleGroundRVScore; + + m_PlayerScores[plr->GetGUID()] = sc; + + UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); +} + +void BattleGroundRV::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ + if (GetStatus() == STATUS_WAIT_LEAVE) + return; + + UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); + + CheckArenaWinConditions(); +} + +void BattleGroundRV::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + if (!killer) + { + sLog.outError("BattleGroundRV: Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player, killer); + + UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); + UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); + + CheckArenaWinConditions(); +} + +bool BattleGroundRV::HandlePlayerUnderMap(Player *player) +{ + player->TeleportTo(GetMapId(), 763.5, -284, 28.276, 2.422, false); + return true; +} + + +void BattleGroundRV::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + switch(Trigger) + { + case 5224: + case 5226: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +void BattleGroundRV::FillInitialWorldStates(WorldPacket &data) +{ + data << uint32(BG_RV_WORLD_STATE_A) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); + data << uint32(BG_RV_WORLD_STATE_H) << uint32(GetAlivePlayersCountByTeam(HORDE)); + data << uint32(BG_RV_WORLD_STATE) << uint32(1); +} + +void BattleGroundRV::Reset() +{ + //call parent's class reset + BattleGround::Reset(); +} + +bool BattleGroundRV::SetupBattleGround() +{ + // Fence + if (!AddObject(BG_RV_OBJECT_FENCE_1, BG_RV_OBJECT_TYPE_FENCE_1, 763.432373, -274.058197, 28.276695, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_FENCE_2, BG_RV_OBJECT_TYPE_FENCE_2, 763.432373, -294.419464, 28.276684, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + // elevators + || !AddObject(BG_RV_OBJECT_ELEVATOR_1, BG_RV_OBJECT_TYPE_ELEVATOR_1, 763.536377, -294.535767, 0.505383, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_ELEVATOR_2, BG_RV_OBJECT_TYPE_ELEVATOR_2, 763.506348, -273.873352, 0.505383, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_RV_OBJECT_BUFF_1, BG_RV_OBJECT_TYPE_BUFF_1, 735.551819, -284.794678, 28.276682, 0.034906, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_BUFF_2, BG_RV_OBJECT_TYPE_BUFF_2, 791.224487, -284.794464, 28.276682, 2.600535, 0, 0, 0, RESPAWN_IMMEDIATELY) + // fire + || !AddObject(BG_RV_OBJECT_FIRE_1, BG_RV_OBJECT_TYPE_FIRE_1, 743.543457, -283.799469, 28.286655, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_FIRE_2, BG_RV_OBJECT_TYPE_FIRE_2, 782.971802, -283.799469, 28.286655, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_FIREDOOR_1, BG_RV_OBJECT_TYPE_FIREDOOR_1, 743.711060, -284.099609, 27.542587, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_FIREDOOR_2, BG_RV_OBJECT_TYPE_FIREDOOR_2, 783.221252, -284.133362, 27.535686, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + // Gear + || !AddObject(BG_RV_OBJECT_GEAR_1, BG_RV_OBJECT_TYPE_GEAR_1, 763.664551, -261.872986, 26.686588, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_GEAR_2, BG_RV_OBJECT_TYPE_GEAR_2, 763.578979, -306.146149, 26.665222, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + // Pulley + || !AddObject(BG_RV_OBJECT_PULLEY_1, BG_RV_OBJECT_TYPE_PULLEY_1, 700.722290, -283.990662, 39.517582, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PULLEY_2, BG_RV_OBJECT_TYPE_PULLEY_2, 826.303833, -283.996429, 39.517582, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + // Pilars + || !AddObject(BG_RV_OBJECT_PILAR_1, BG_RV_OBJECT_TYPE_PILAR_1, 763.632385, -306.162384, 25.909504, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PILAR_2, BG_RV_OBJECT_TYPE_PILAR_2, 723.644287, -284.493256, 24.648525, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PILAR_3, BG_RV_OBJECT_TYPE_PILAR_3, 763.611145, -261.856750, 25.909504, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PILAR_4, BG_RV_OBJECT_TYPE_PILAR_4, 802.211609, -284.493256, 24.648525, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) +/* + // Pilars Collision - Fixme: Use the collision pilars - should make u break LoS + || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_1, BG_RV_OBJECT_TYPE_PILAR_COLLISION_1, 763.632385, -306.162384, 30.639660, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_2, BG_RV_OBJECT_TYPE_PILAR_COLLISION_2, 723.644287, -284.493256, 32.382710, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_3, BG_RV_OBJECT_TYPE_PILAR_COLLISION_3, 763.611145, -261.856750, 30.639660, 0.000000, 0, 0, 0, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RV_OBJECT_PILAR_COLLISION_4, BG_RV_OBJECT_TYPE_PILAR_COLLISION_4, 802.211609, -284.493256, 32.382710, 3.141593, 0, 0, 0, RESPAWN_IMMEDIATELY) +*/ +) + { + sLog.outErrorDb("BatteGroundRV: Failed to spawn some object!"); + return false; + } + return true; +} diff --git a/src/server/game/BattleGrounds/BattleGroundRV.h b/src/server/game/BattleGrounds/BattleGroundRV.h new file mode 100644 index 00000000000..bf06478d364 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundRV.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDRV_H +#define __BATTLEGROUNDRV_H + +class BattleGround; + +enum BattleGroundRVObjectTypes +{ + BG_RV_OBJECT_BUFF_1, + BG_RV_OBJECT_BUFF_2, + BG_RV_OBJECT_FIRE_1, + BG_RV_OBJECT_FIRE_2, + BG_RV_OBJECT_FIREDOOR_1, + BG_RV_OBJECT_FIREDOOR_2, + + BG_RV_OBJECT_PILAR_1, + BG_RV_OBJECT_PILAR_3, + BG_RV_OBJECT_GEAR_1, + BG_RV_OBJECT_GEAR_2, + + BG_RV_OBJECT_PILAR_2, + BG_RV_OBJECT_PILAR_4, + BG_RV_OBJECT_PULLEY_1, + BG_RV_OBJECT_PULLEY_2, +/* + BG_RV_OBJECT_PILAR_COLLISION_1, + BG_RV_OBJECT_PILAR_COLLISION_2, + BG_RV_OBJECT_PILAR_COLLISION_3, + BG_RV_OBJECT_PILAR_COLLISION_4, +*/ + BG_RV_OBJECT_ELEVATOR_1, + BG_RV_OBJECT_ELEVATOR_2, + BG_RV_OBJECT_FENCE_1, + BG_RV_OBJECT_FENCE_2, + BG_RV_OBJECT_MAX, +}; + +enum BattleGroundRVObjects +{ + BG_RV_OBJECT_TYPE_BUFF_1 = 184663, + BG_RV_OBJECT_TYPE_BUFF_2 = 184664, + BG_RV_OBJECT_TYPE_FIRE_1 = 192704, + BG_RV_OBJECT_TYPE_FIRE_2 = 192705, + + BG_RV_OBJECT_TYPE_FIREDOOR_2 = 192387, + BG_RV_OBJECT_TYPE_FIREDOOR_1 = 192388, + BG_RV_OBJECT_TYPE_PULLEY_1 = 192389, + BG_RV_OBJECT_TYPE_PULLEY_2 = 192390, + BG_RV_OBJECT_TYPE_FENCE_1 = 192391, + BG_RV_OBJECT_TYPE_FENCE_2 = 192392, + BG_RV_OBJECT_TYPE_GEAR_1 = 192393, + BG_RV_OBJECT_TYPE_GEAR_2 = 192394, + BG_RV_OBJECT_TYPE_ELEVATOR_1 = 194582, + BG_RV_OBJECT_TYPE_ELEVATOR_2 = 194586, +/* + BG_RV_OBJECT_TYPE_PILAR_COLLISION_1 = 194580, // axe + BG_RV_OBJECT_TYPE_PILAR_COLLISION_2 = 194579, // arena + BG_RV_OBJECT_TYPE_PILAR_COLLISION_3 = 194581, // lightning + BG_RV_OBJECT_TYPE_PILAR_COLLISION_4 = 194578, // ivory +*/ + BG_RV_OBJECT_TYPE_PILAR_1 = 194583, // axe + BG_RV_OBJECT_TYPE_PILAR_2 = 194584, // arena + BG_RV_OBJECT_TYPE_PILAR_3 = 194585, // lightning + BG_RV_OBJECT_TYPE_PILAR_4 = 194587, // ivory +}; + +enum BattleGroundRVData +{ + BG_RV_STATE_OPEN_FENCES, + BG_RV_STATE_OPEN_PILARS, + BG_RV_STATE_CLOSE_PILARS, + BG_RV_STATE_OPEN_FIRE, + BG_RV_STATE_CLOSE_FIRE, + BG_RV_FIRE_TO_PILAR_TIMER = 20000, + BG_RV_PILAR_TO_FIRE_TIMER = 5000, + BG_RV_FIRST_TIMER = 20133, + BG_RV_WORLD_STATE_A = 0xe10, + BG_RV_WORLD_STATE_H = 0xe11, + BG_RV_WORLD_STATE = 0xe1a, +}; + +class BattleGroundRVScore : public BattleGroundScore +{ + public: + BattleGroundRVScore() {}; + virtual ~BattleGroundRVScore() {}; +}; + +class BattleGroundRV : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundRV(); + ~BattleGroundRV(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket &d); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void HandleKillPlayer(Player* player, Player *killer); + bool HandlePlayerUnderMap(Player * plr); + + private: + uint32 Timer; + uint32 State; + + protected: + uint32 getTimer() { return Timer; }; + void setTimer(uint32 timer) { Timer = timer; }; + + uint32 getState() { return State; }; + void setState(uint32 state) { State = state; }; +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundSA.cpp b/src/server/game/BattleGrounds/BattleGroundSA.cpp new file mode 100644 index 00000000000..ccde43ce948 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundSA.cpp @@ -0,0 +1,822 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundSA.h" +#include "Language.h" +#include "Player.h" +#include "GameObject.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" + + +BattleGroundSA::BattleGroundSA() +{ + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_SA_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_SA_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_SA_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_SA_HAS_BEGUN; + m_BgObjects.resize(BG_SA_MAXOBJ); + m_BgCreatures.resize(BG_SA_MAXNPC + BG_SA_MAX_GY); + TimerEnabled = false; + UpdateWaitTimer = 0; + SignaledRoundTwo = false; + SignaledRoundTwoHalfMin = false; + InitSecondRound = false; +} + +BattleGroundSA::~BattleGroundSA() +{ +} + +void BattleGroundSA::Reset() +{ + TotalTime = 0; + attackers = ((urand(0,1)) ? TEAM_ALLIANCE : TEAM_HORDE); + for (uint8 i = 0; i <= 5; i++) + GateStatus[i] = BG_SA_GATE_OK; + ShipsStarted = false; + status = BG_SA_WARMUP; +} + +bool BattleGroundSA::SetupBattleGround() +{ + return ResetObjs(); +} + +bool BattleGroundSA::ResetObjs() +{ + uint32 atF = BG_SA_Factions[attackers]; + uint32 defF = BG_SA_Factions[attackers ? TEAM_ALLIANCE : TEAM_HORDE]; + + + for (uint8 i = 0; i SetUInt32Value(GAMEOBJECT_FACTION, defF); + } + + GetBGObject(BG_SA_TITAN_RELIC)->SetUInt32Value(GAMEOBJECT_FACTION, atF); + GetBGObject(BG_SA_TITAN_RELIC)->Refresh(); + + for (uint8 i = 0; i <= 5; i++) + GateStatus[i] = BG_SA_GATE_OK; + + // MAD props for Kiper for discovering those values - 4 hours of his work. + GetBGObject(BG_SA_BOAT_ONE)->UpdateRotationFields(1.0f, 0.0002f); + GetBGObject(BG_SA_BOAT_TWO)->UpdateRotationFields(1.0f, 0.00001f); + SpawnBGObject(BG_SA_BOAT_ONE, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_SA_BOAT_TWO, RESPAWN_IMMEDIATELY); + + TotalTime = 0; + ShipsStarted = false; + + //Graveyards! + for (uint8 i = 0;i < BG_SA_MAX_GY; i++) + { + WorldSafeLocsEntry const *sg = NULL; + sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); + + if (!sg) + { + sLog.outError("SOTA: Can't find GY entry %u",BG_SA_GYEntries[i]); + return false; + } + + if (i == BG_SA_BEACH_GY) + { + GraveyardStatus[i] = attackers; + AddSpiritGuide(i + BG_SA_MAXNPC, sg->x, sg->y, sg->z, BG_SA_GYOrientation[i], ((attackers == TEAM_HORDE)? HORDE : ALLIANCE)); + } + else + { + GraveyardStatus[i] = ((attackers == TEAM_HORDE)? TEAM_ALLIANCE : TEAM_HORDE); + if (!AddSpiritGuide(i + BG_SA_MAXNPC, sg->x, sg->y, sg->z, BG_SA_GYOrientation[i], ((attackers == TEAM_HORDE)? ALLIANCE : HORDE))) + sLog.outError("SOTA: couldn't spawn GY: %u",i); + } + } + + //GY capture points + for (uint8 i = BG_SA_CENTRAL_FLAG; i < BG_SA_MAXOBJ; i++) + { + AddObject(i, BG_SA_ObjEntries[(i + (attackers == TEAM_ALLIANCE ? 3:0))], + BG_SA_ObjSpawnlocs[i][0], BG_SA_ObjSpawnlocs[i][1], + BG_SA_ObjSpawnlocs[i][2], BG_SA_ObjSpawnlocs[i][3], + 0,0,0,0,RESPAWN_ONE_DAY); + GetBGObject(i)->SetUInt32Value(GAMEOBJECT_FACTION, atF); + } + + //Player may enter BEFORE we set up bG - lets update his worldstates anyway... + UpdateWorldState(BG_SA_RIGHT_GY_HORDE , GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_HORDE?1:0); + UpdateWorldState(BG_SA_LEFT_GY_HORDE , GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_HORDE?1:0); + UpdateWorldState(BG_SA_CENTER_GY_HORDE , GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_HORDE?1:0); + + UpdateWorldState(BG_SA_RIGHT_GY_ALLIANCE , GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); + UpdateWorldState(BG_SA_LEFT_GY_ALLIANCE , GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); + UpdateWorldState(BG_SA_CENTER_GY_ALLIANCE , GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); + + if (attackers == TEAM_ALLIANCE) + { + UpdateWorldState(BG_SA_ALLY_ATTACKS, 1); + UpdateWorldState(BG_SA_HORDE_ATTACKS, 0); + + UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_ALL, 1); + UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_ALL, 1); + UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_HRD, 0); + UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_HRD, 0); + + UpdateWorldState(BG_SA_HORDE_DEFENCE_TOKEN,1); + UpdateWorldState(BG_SA_ALLIANCE_DEFENCE_TOKEN,0); + } + else + { + UpdateWorldState(BG_SA_HORDE_ATTACKS, 1); + UpdateWorldState(BG_SA_ALLY_ATTACKS, 0); + + UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_ALL, 0); + UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_ALL, 0); + UpdateWorldState(BG_SA_RIGHT_ATT_TOKEN_HRD, 1); + UpdateWorldState(BG_SA_LEFT_ATT_TOKEN_HRD, 1); + + UpdateWorldState(BG_SA_HORDE_DEFENCE_TOKEN,0); + UpdateWorldState(BG_SA_ALLIANCE_DEFENCE_TOKEN,1); + } + + UpdateWorldState(BG_SA_PURPLE_GATEWS, 1); + UpdateWorldState(BG_SA_RED_GATEWS, 1); + UpdateWorldState(BG_SA_BLUE_GATEWS, 1); + UpdateWorldState(BG_SA_GREEN_GATEWS, 1); + UpdateWorldState(BG_SA_YELLOW_GATEWS, 1); + UpdateWorldState(BG_SA_ANCIENT_GATEWS, 1); + + TeleportPlayers(); + return true; +} + +void BattleGroundSA::StartShips() +{ + if (ShipsStarted) + return; + + DoorOpen(BG_SA_BOAT_ONE); + DoorOpen(BG_SA_BOAT_TWO); + + for (int i = BG_SA_BOAT_ONE; i <= BG_SA_BOAT_TWO; i++) + { + for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end();itr++) + { + if (Player* p = objmgr.GetPlayer(itr->first)) + { + if (p->GetTeamId() != attackers) + continue; + + UpdateData data; + WorldPacket pkt; + GetBGObject(i)->BuildValuesUpdateBlockForPlayer(&data, p); + data.BuildPacket(&pkt); + p->GetSession()->SendPacket(&pkt); + } + } + } + ShipsStarted = true; +} + +void BattleGroundSA::Update(uint32 diff) +{ + if (InitSecondRound) + { + if (UpdateWaitTimer < diff) + { + if (!SignaledRoundTwo) + { + SignaledRoundTwo = true; + InitSecondRound = false; + SendMessageToAll(LANG_BG_SA_ROUND_TWO_ONE_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + }else + { + UpdateWaitTimer -= diff; + return; + } + } + BattleGround::Update(diff); + TotalTime += diff; + + if (status == BG_SA_WARMUP ) + { + BG_SA_ENDROUNDTIME = BG_SA_ROUNDLENGTH; + if (TotalTime >= BG_SA_WARMUPLENGTH) + { + TotalTime = 0; + ToggleTimer(); + DemolisherStartState(false); + status = BG_SA_ROUND_ONE; + } + if (TotalTime >= BG_SA_BOAT_START) + StartShips(); + return; + } + else if (status == BG_SA_SECOND_WARMUP) + { + if (RoundScores[0].time= 60000) + { + SendWarningToAll(LANG_BG_SA_HAS_BEGUN); + TotalTime = 0; + ToggleTimer(); + DemolisherStartState(false); + status = BG_SA_ROUND_TWO; + } + if (TotalTime >= 30000) + { + if (!SignaledRoundTwoHalfMin) + { + SignaledRoundTwoHalfMin = true; + SendMessageToAll(LANG_BG_SA_ROUND_TWO_START_HALF_MINUTE, CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + } + StartShips(); + return; + } + else if (GetStatus() == STATUS_IN_PROGRESS) + { + if (status == BG_SA_ROUND_ONE) + { + if (TotalTime >= BG_SA_ROUNDLENGTH) + { + RoundScores[0].winner = attackers; + RoundScores[0].time = BG_SA_ROUNDLENGTH; + TotalTime = 0; + status = BG_SA_SECOND_WARMUP; + attackers = (attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; + status = BG_SA_SECOND_WARMUP; + ToggleTimer(); + ResetObjs(); + return; + } + } + else if (status == BG_SA_ROUND_TWO) + { + if (TotalTime >= BG_SA_ENDROUNDTIME) + { + RoundScores[1].time = BG_SA_ROUNDLENGTH; + RoundScores[1].winner = (attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; + + if (RoundScores[0].time == RoundScores[1].time) + EndBattleGround(NULL); + else if (RoundScores[0].time < RoundScores[1].time) + EndBattleGround(RoundScores[0].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + else + EndBattleGround(RoundScores[1].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + return; + } + } + if (status == BG_SA_ROUND_ONE || status == BG_SA_ROUND_TWO) + { + SendTime(); + UpdateDemolisherSpawns(); + } + } +} + +void BattleGroundSA::StartingEventCloseDoors() +{ +} + +void BattleGroundSA::StartingEventOpenDoors() +{ +} + +void BattleGroundSA::FillInitialWorldStates(WorldPacket& data) +{ + uint32 ally_attacks = uint32(attackers == TEAM_ALLIANCE ? 1 : 0); + uint32 horde_attacks = uint32(attackers == TEAM_HORDE ? 1 : 0); + + data << uint32(BG_SA_ANCIENT_GATEWS) << uint32(GateStatus[BG_SA_ANCIENT_GATE]); + data << uint32(BG_SA_YELLOW_GATEWS) << uint32(GateStatus[BG_SA_YELLOW_GATE]); + data << uint32(BG_SA_GREEN_GATEWS) << uint32(GateStatus[BG_SA_GREEN_GATE]); + data << uint32(BG_SA_BLUE_GATEWS) << uint32(GateStatus[BG_SA_BLUE_GATE]); + data << uint32(BG_SA_RED_GATEWS) << uint32(GateStatus[BG_SA_RED_GATE]); + data << uint32(BG_SA_PURPLE_GATEWS) << uint32(GateStatus[BG_SA_PURPLE_GATE]); + + data << uint32(BG_SA_BONUS_TIMER) << uint32(0); + + data << uint32(BG_SA_HORDE_ATTACKS)<< horde_attacks; + data << uint32(BG_SA_ALLY_ATTACKS) << ally_attacks; + + //Time will be sent on first update... + data << uint32(BG_SA_ENABLE_TIMER) << ((TimerEnabled) ? uint32(1) : uint32(0)); + data << uint32(BG_SA_TIMER_MINS) << uint32(0); + data << uint32(BG_SA_TIMER_SEC_TENS) << uint32(0); + data << uint32(BG_SA_TIMER_SEC_DECS) << uint32(0); + + data << uint32(BG_SA_RIGHT_GY_HORDE) << uint32(GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_HORDE?1:0); + data << uint32(BG_SA_LEFT_GY_HORDE) << uint32(GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_HORDE?1:0); + data << uint32(BG_SA_CENTER_GY_HORDE) << uint32(GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_HORDE?1:0); + + data << uint32(BG_SA_RIGHT_GY_ALLIANCE) << uint32(GraveyardStatus[BG_SA_RIGHT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); + data << uint32(BG_SA_LEFT_GY_ALLIANCE) << uint32(GraveyardStatus[BG_SA_LEFT_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); + data << uint32(BG_SA_CENTER_GY_ALLIANCE) << uint32(GraveyardStatus[BG_SA_CENTRAL_CAPTURABLE_GY] == TEAM_ALLIANCE?1:0); + + data << uint32(BG_SA_HORDE_DEFENCE_TOKEN) << ally_attacks; + data << uint32(BG_SA_ALLIANCE_DEFENCE_TOKEN) << horde_attacks; + + data << uint32(BG_SA_LEFT_ATT_TOKEN_HRD) << horde_attacks; + data << uint32(BG_SA_RIGHT_ATT_TOKEN_HRD) << horde_attacks; + data << uint32(BG_SA_RIGHT_ATT_TOKEN_ALL) << ally_attacks; + data << uint32(BG_SA_LEFT_ATT_TOKEN_ALL) << ally_attacks; +} + +void BattleGroundSA::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundSAScore* sc = new BattleGroundSAScore; + + if (!ShipsStarted) + { + if (plr->GetTeamId() == attackers) + { + plr->CastSpell(plr,12438,true);//Without this player falls before boat loads... + + if (urand(0,1)) + plr->TeleportTo(607, 2682.936f, -830.368f, 50.0f, 2.895f, 0); + else + plr->TeleportTo(607, 2577.003f, 980.261f, 50.0f, 0.807f, 0); + + } + else + plr->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); + } + else + { + if (plr->GetTeamId() == attackers) + plr->TeleportTo(607, 1600.381f, -106.263f, 8.8745f, 3.78f, 0); + else + plr->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); + } + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundSA::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) +{ +} + +void BattleGroundSA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; +} + +void BattleGroundSA::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +{ + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); + if (itr == m_PlayerScores.end()) // player not found... + return; + + if (type == SCORE_DESTROYED_DEMOLISHER) + ((BattleGroundSAScore*)itr->second)->demolishers_destroyed += value; + else if (type == SCORE_DESTROYED_WALL) + ((BattleGroundSAScore*)itr->second)->gates_destroyed += value; + else + BattleGround::UpdatePlayerScore(Source,type,value, doAddHonor); +} + +void BattleGroundSA::TeleportPlayers() +{ + for (BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + { + if (Player *plr = objmgr.GetPlayer(itr->first)) + { + // should remove spirit of redemption + if (plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + + if (!plr->isAlive()) + { + plr->ResurrectPlayer(1.0f); + plr->SpawnCorpseBones(); + } + + plr->SetHealth(plr->GetMaxHealth()); + plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); + plr->CombatStopWithPets(true); + + if (plr->GetTeamId() == attackers) + { + plr->CastSpell(plr,12438,true); //Without this player falls before boat loads... + + if (urand(0,1)) + plr->TeleportTo(607, 2682.936f, -830.368f, 50.0f, 2.895f, 0); + else + plr->TeleportTo(607, 2577.003f, 980.261f, 50.0f, 0.807f, 0); + } + else + plr->TeleportTo(607, 1209.7f, -65.16f, 70.1f, 0.0f, 0); + } + } +} + +void BattleGroundSA::EventPlayerDamagedGO(Player* plr, GameObject* go, uint8 hitType, uint32 destroyedEvent) +{ + if (!go || !go->GetGOInfo()) + return; + + switch(hitType) + { + case BG_OBJECT_DMG_HIT_TYPE_JUST_DAMAGED://under attack + SendWarningToAll(LANG_BG_SA_IS_UNDER_ATTACK, go->GetGOInfo()->name); + break; + case BG_OBJECT_DMG_HIT_TYPE_DAMAGED: + break; + case BG_OBJECT_DMG_HIT_TYPE_JUST_HIGH_DAMAGED: + { + uint32 i = GetGateIDFromDestroyEventID(destroyedEvent); + GateStatus[i] = BG_SA_GATE_DAMAGED; + uint32 uws = GetWorldStateFromGateID(i); + if (uws) + UpdateWorldState(uws, GateStatus[i]); + break; + } + case BG_OBJECT_DMG_HIT_TYPE_HIGH_DAMAGED: + break; + case BG_OBJECT_DMG_HIT_TYPE_JUST_DESTROYED://handled at DestroyGate() + if (destroyedEvent == 19837) + SendWarningToAll(LANG_BG_SA_CHAMBER_BREACHED); + else + SendWarningToAll(LANG_BG_SA_WAS_DESTROYED, go->GetGOInfo()->name); + break; + } +} + +void BattleGroundSA::HandleKillUnit(Creature* unit, Player* killer) +{ + if (!unit) + return; + + if (unit->GetEntry() == 28781) //Demolisher + UpdatePlayerScore(killer, SCORE_DESTROYED_DEMOLISHER, 1); +} + +/* + You may ask what the fuck does it do? + Prevents owner overwriting guns faction with own. + */ +void BattleGroundSA::OverrideGunFaction() +{ + if (!m_BgCreatures[0]) + return; + + for (uint8 i = BG_SA_GUN_1; i <= BG_SA_GUN_10;i++) + { + if (Creature* gun = GetBGCreature(i)) + gun->setFaction(BG_SA_Factions[attackers? TEAM_ALLIANCE : TEAM_HORDE]); + } + + for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4;i++) + { + if (Creature* dem = GetBGCreature(i)) + dem->setFaction(BG_SA_Factions[attackers]); + } +} + +void BattleGroundSA::DemolisherStartState(bool start) +{ + if (!m_BgCreatures[0]) + return; + + for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4; i++) + { + if (Creature* dem = GetBGCreature(i)) + { + if (start) + dem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + else + dem->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + } + } +} + +void BattleGroundSA::DestroyGate(Player* pl, GameObject* /*go*/, uint32 destroyedEvent) +{ + uint32 i = GetGateIDFromDestroyEventID(destroyedEvent); + if (!GateStatus[i]) + return; + + if (GameObject* g = GetBGObject(i)) + { + if (g->GetGOValue()->building.health == 0) + { + GateStatus[i] = BG_SA_GATE_DESTROYED; + uint32 uws = GetWorldStateFromGateID(i); + if (uws) + UpdateWorldState(uws, GateStatus[i]); + bool rewardHonor = true; + switch(i) + { + case BG_SA_GREEN_GATE: + if (GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) + rewardHonor = false; + break; + case BG_SA_BLUE_GATE: + if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED) + rewardHonor = false; + break; + case BG_SA_RED_GATE: + if (GateStatus[BG_SA_PURPLE_GATE] == BG_SA_GATE_DESTROYED) + rewardHonor = false; + break; + case BG_SA_PURPLE_GATE: + if (GateStatus[BG_SA_RED_GATE] == BG_SA_GATE_DESTROYED) + rewardHonor = false; + break; + } + + if (i < 5) + DelObject(i+9); + UpdatePlayerScore(pl,SCORE_DESTROYED_WALL, 1); + if (rewardHonor) + UpdatePlayerScore(pl,SCORE_BONUS_HONOR,(GetBonusHonorFromKill(1))); + } + } +} + +WorldSafeLocsEntry const* BattleGroundSA::GetClosestGraveYard(Player* player) +{ + uint32 safeloc = 0; + WorldSafeLocsEntry const* ret; + WorldSafeLocsEntry const* closest; + float dist, nearest; + float x,y,z; + + player->GetPosition(x,y,z); + + if (player->GetTeamId() == attackers) + safeloc = BG_SA_GYEntries[BG_SA_BEACH_GY]; + else + safeloc = BG_SA_GYEntries[BG_SA_DEFENDER_LAST_GY]; + + closest = sWorldSafeLocsStore.LookupEntry(safeloc); + nearest = sqrt((closest->x - x)*(closest->x - x) + (closest->y - y)*(closest->y - y)+(closest->z - z)*(closest->z - z)); + + for (uint8 i = BG_SA_RIGHT_CAPTURABLE_GY; i < BG_SA_MAX_GY; i++) + { + if (GraveyardStatus[i] != player->GetTeamId()) + continue; + + ret = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); + dist = sqrt((ret->x - x)*(ret->x - x) + (ret->y - y)*(ret->y - y)+(ret->z - z)*(ret->z - z)); + if (dist < nearest) + { + closest = ret; + nearest = dist; + } + } + + return closest; +} + +void BattleGroundSA::SendTime() +{ + uint32 end_of_round = (BG_SA_ENDROUNDTIME - TotalTime); + UpdateWorldState(BG_SA_TIMER_MINS, end_of_round/60000); + UpdateWorldState(BG_SA_TIMER_SEC_TENS, (end_of_round%60000)/10000); + UpdateWorldState(BG_SA_TIMER_SEC_DECS, ((end_of_round%60000)%10000)/1000); +} + +void BattleGroundSA::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) +{ + switch(target_obj->GetEntry()) + { + case 191307: + case 191308: + if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) + CaptureGraveyard(BG_SA_LEFT_CAPTURABLE_GY, Source); + break; + case 191305: + case 191306: + if (GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) + CaptureGraveyard(BG_SA_RIGHT_CAPTURABLE_GY, Source); + break; + case 191310: + case 191309: + if ((GateStatus[BG_SA_GREEN_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_BLUE_GATE] == BG_SA_GATE_DESTROYED) && (GateStatus[BG_SA_RED_GATE] == BG_SA_GATE_DESTROYED || GateStatus[BG_SA_PURPLE_GATE] == BG_SA_GATE_DESTROYED)) + CaptureGraveyard(BG_SA_CENTRAL_CAPTURABLE_GY, Source); + break; + default: + return; + }; +} + +void BattleGroundSA::CaptureGraveyard(BG_SA_Graveyards i, Player *Source) +{ + DelCreature(BG_SA_MAXNPC + i); + GraveyardStatus[i] = Source->GetTeamId(); + WorldSafeLocsEntry const *sg = NULL; + sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); + AddSpiritGuide(i + BG_SA_MAXNPC, sg->x, sg->y, sg->z, BG_SA_GYOrientation[i], (GraveyardStatus[i] == TEAM_ALLIANCE? ALLIANCE : HORDE)); + uint32 npc = 0; + uint32 flag = 0; + + switch(i) + { + case BG_SA_LEFT_CAPTURABLE_GY: + flag = BG_SA_LEFT_FLAG; + DelObject(flag); + AddObject(flag,BG_SA_ObjEntries[(flag + (Source->GetTeamId() == TEAM_ALLIANCE ? 0:3))], + BG_SA_ObjSpawnlocs[flag][0],BG_SA_ObjSpawnlocs[flag][1], + BG_SA_ObjSpawnlocs[flag][2],BG_SA_ObjSpawnlocs[flag][3],0,0,0,0,RESPAWN_ONE_DAY); + + npc = BG_SA_NPC_RIGSPARK; + AddCreature(BG_SA_NpcEntries[npc], npc, attackers, + BG_SA_NpcSpawnlocs[npc][0], BG_SA_NpcSpawnlocs[npc][1], + BG_SA_NpcSpawnlocs[npc][2], BG_SA_NpcSpawnlocs[npc][3]); + + UpdateWorldState(BG_SA_LEFT_GY_ALLIANCE, (GraveyardStatus[i] == TEAM_ALLIANCE? 1:0)); + UpdateWorldState(BG_SA_LEFT_GY_HORDE, (GraveyardStatus[i] == TEAM_ALLIANCE? 0:1)); + if (Source->GetTeamId() == TEAM_ALLIANCE) + SendWarningToAll(LANG_BG_SA_A_GY_WEST); + else + SendWarningToAll(LANG_BG_SA_H_GY_WEST); + break; + case BG_SA_RIGHT_CAPTURABLE_GY: + flag = BG_SA_RIGHT_FLAG; + DelObject(flag); + AddObject(flag,BG_SA_ObjEntries[(flag + (Source->GetTeamId() == TEAM_ALLIANCE ? 0:3))], + BG_SA_ObjSpawnlocs[flag][0],BG_SA_ObjSpawnlocs[flag][1], + BG_SA_ObjSpawnlocs[flag][2],BG_SA_ObjSpawnlocs[flag][3],0,0,0,0,RESPAWN_ONE_DAY); + + npc = BG_SA_NPC_SPARKLIGHT; + AddCreature(BG_SA_NpcEntries[npc], npc, attackers, + BG_SA_NpcSpawnlocs[npc][0], BG_SA_NpcSpawnlocs[npc][1], + BG_SA_NpcSpawnlocs[npc][2], BG_SA_NpcSpawnlocs[npc][3]); + + UpdateWorldState(BG_SA_RIGHT_GY_ALLIANCE, (GraveyardStatus[i] == TEAM_ALLIANCE? 1:0)); + UpdateWorldState(BG_SA_RIGHT_GY_HORDE, (GraveyardStatus[i] == TEAM_ALLIANCE? 0:1)); + if (Source->GetTeamId() == TEAM_ALLIANCE) + SendWarningToAll(LANG_BG_SA_A_GY_EAST); + else + SendWarningToAll(LANG_BG_SA_H_GY_EAST); + break; + case BG_SA_CENTRAL_CAPTURABLE_GY: + flag = BG_SA_CENTRAL_FLAG; + DelObject(flag); + AddObject(flag,BG_SA_ObjEntries[(flag + (Source->GetTeamId() == TEAM_ALLIANCE ? 0:3))], + BG_SA_ObjSpawnlocs[flag][0],BG_SA_ObjSpawnlocs[flag][1], + BG_SA_ObjSpawnlocs[flag][2],BG_SA_ObjSpawnlocs[flag][3],0,0,0,0,RESPAWN_ONE_DAY); + + UpdateWorldState(BG_SA_CENTER_GY_ALLIANCE, (GraveyardStatus[i] == TEAM_ALLIANCE? 1:0)); + UpdateWorldState(BG_SA_CENTER_GY_HORDE, (GraveyardStatus[i] == TEAM_ALLIANCE? 0:1)); + if (Source->GetTeamId() == TEAM_ALLIANCE) + SendWarningToAll(LANG_BG_SA_A_GY_SOUTH); + else + SendWarningToAll(LANG_BG_SA_H_GY_SOUTH); + break; + default: + ASSERT(0); + break; + }; +} + +void BattleGroundSA::EventPlayerUsedGO(Player* Source, GameObject* object) +{ + if (object->GetEntry() == BG_SA_ObjEntries[BG_SA_TITAN_RELIC] && GateStatus[BG_SA_ANCIENT_GATE] == BG_SA_GATE_DESTROYED) + { + if (Source->GetTeamId() == attackers) + { + if (Source->GetTeamId() == ALLIANCE) + SendMessageToAll(LANG_BG_SA_ALLIANCE_CAPTURED_RELIC, CHAT_MSG_BG_SYSTEM_NEUTRAL); + else SendMessageToAll(LANG_BG_SA_HORDE_CAPTURED_RELIC, CHAT_MSG_BG_SYSTEM_NEUTRAL); + + if (status == BG_SA_ROUND_ONE) + { + RoundScores[0].winner = attackers; + RoundScores[0].time = TotalTime; + attackers = (attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; + status = BG_SA_SECOND_WARMUP; + TotalTime = 0; + ToggleTimer(); + SendWarningToAll(LANG_BG_SA_ROUND_ONE_END); + UpdateWaitTimer = 5000; + SignaledRoundTwo = false; + SignaledRoundTwoHalfMin = false; + InitSecondRound = true; + ResetObjs(); + } + else if (status == BG_SA_ROUND_TWO) + { + RoundScores[1].winner = attackers; + RoundScores[1].time = TotalTime;ToggleTimer(); + if (RoundScores[0].time == RoundScores[1].time) + EndBattleGround(NULL); + else if (RoundScores[0].time < RoundScores[1].time) + EndBattleGround(RoundScores[0].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + else + EndBattleGround(RoundScores[1].winner == TEAM_ALLIANCE ? ALLIANCE : HORDE); + } + } + } +} + +void BattleGroundSA::ToggleTimer() +{ + TimerEnabled = !TimerEnabled; + UpdateWorldState(BG_SA_ENABLE_TIMER, (TimerEnabled) ? 1 : 0); +} + +void BattleGroundSA::EndBattleGround(uint32 winner) +{ + //honor reward for winning + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE); + else if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE); + + //complete map_end rewards (even if no team wins) + RewardHonorToTeam(GetBonusHonorFromKill(2), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(2), HORDE); + + BattleGround::EndBattleGround(winner); +} + +void BattleGroundSA::UpdateDemolisherSpawns() +{ + for (uint8 i = BG_SA_DEMOLISHER_1; i <= BG_SA_DEMOLISHER_4; i++) + { + if (m_BgCreatures[i]) + { + if (Creature *Demolisher = GetBGCreature(i)) + { + if (Demolisher->isDead()) + { + uint8 gy = (i >= BG_SA_DEMOLISHER_3 ? 3 : 2); + if (GraveyardStatus[gy] == attackers) + Demolisher->Relocate(BG_SA_NpcSpawnlocs[i + 6][0], BG_SA_NpcSpawnlocs[i + 6][1], + BG_SA_NpcSpawnlocs[i + 6][2], BG_SA_NpcSpawnlocs[i + 6][3]); + else + Demolisher->Relocate(BG_SA_NpcSpawnlocs[i][0], BG_SA_NpcSpawnlocs[i][1], + BG_SA_NpcSpawnlocs[i][2], BG_SA_NpcSpawnlocs[i][3]); + + Demolisher->Respawn(); + } + } + } + } +} + + diff --git a/src/server/game/BattleGrounds/BattleGroundSA.h b/src/server/game/BattleGrounds/BattleGroundSA.h new file mode 100644 index 00000000000..760be3ca02e --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundSA.h @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDSA_H +#define __BATTLEGROUNDSA_H + +class BattleGround; + +class BattleGroundSAScore : public BattleGroundScore +{ + public: + BattleGroundSAScore(): demolishers_destroyed(0), gates_destroyed(0) {}; + virtual ~BattleGroundSAScore() {}; + uint8 demolishers_destroyed; + uint8 gates_destroyed; +}; + +#define BG_SA_FLAG_AMOUNT 3 +#define BG_SA_DEMOLISHER_AMOUNT 4 + +enum BG_SA_Status + { + BG_SA_NOTSTARTED = 0, + BG_SA_WARMUP, + BG_SA_ROUND_ONE, + BG_SA_SECOND_WARMUP, + BG_SA_ROUND_TWO, + BG_SA_BONUS_ROUND + }; + +enum BG_SA_GateState + { + BG_SA_GATE_OK = 1, + BG_SA_GATE_DAMAGED = 2, + BG_SA_GATE_DESTROYED = 3 + }; + +enum BG_SA_Timers + { + BG_SA_BOAT_START = 60000, + BG_SA_WARMUPLENGTH = 120000, + BG_SA_ROUNDLENGTH = 600000 + }; + +enum BG_SA_WorldStates + { + BG_SA_TIMER_MINS = 3559, + BG_SA_TIMER_SEC_TENS = 3560, + BG_SA_TIMER_SEC_DECS = 3561, + BG_SA_ALLY_ATTACKS = 4352, + BG_SA_HORDE_ATTACKS = 4353, + + BG_SA_PURPLE_GATEWS = 3614, + BG_SA_RED_GATEWS = 3617, + BG_SA_BLUE_GATEWS = 3620, + BG_SA_GREEN_GATEWS = 3623, + BG_SA_YELLOW_GATEWS = 3638, + BG_SA_ANCIENT_GATEWS = 3849, + + + BG_SA_LEFT_GY_ALLIANCE = 3635, + BG_SA_RIGHT_GY_ALLIANCE = 3636, + BG_SA_CENTER_GY_ALLIANCE = 3637, + + BG_SA_RIGHT_ATT_TOKEN_ALL = 3627, + BG_SA_LEFT_ATT_TOKEN_ALL = 3626, + + BG_SA_LEFT_ATT_TOKEN_HRD = 3629, + BG_SA_RIGHT_ATT_TOKEN_HRD = 3628, + + BG_SA_HORDE_DEFENCE_TOKEN = 3631, + BG_SA_ALLIANCE_DEFENCE_TOKEN = 3630, + + BG_SA_RIGHT_GY_HORDE = 3632, + BG_SA_LEFT_GY_HORDE = 3633, + BG_SA_CENTER_GY_HORDE = 3634, + + BG_SA_BONUS_TIMER = 0xdf3, + BG_SA_ENABLE_TIMER = 3564, + }; + +enum BG_SA_NPCs + { + BG_SA_GUN_1 = 0, + BG_SA_GUN_2, + BG_SA_GUN_3, + BG_SA_GUN_4, + BG_SA_GUN_5, + BG_SA_GUN_6, + BG_SA_GUN_7, + BG_SA_GUN_8, + BG_SA_GUN_9, + BG_SA_GUN_10, + BG_SA_DEMOLISHER_1, + BG_SA_DEMOLISHER_2, + BG_SA_DEMOLISHER_3, + BG_SA_DEMOLISHER_4, + BG_SA_NPC_SPARKLIGHT, + BG_SA_NPC_RIGSPARK, + BG_SA_MAXNPC + }; + +const uint32 BG_SA_NpcEntries[BG_SA_MAXNPC] = + { + 27894, + 27894, + 27894, + 27894, + 27894, + 27894, + 27894, + 27894, + 27894, + 27894, + //4 beach demolishers + 28781, + 28781, + 28781, + 28781, + //Fizzle Sparklight, or whatever his name was + 29260, + 29262, + }; + +const float BG_SA_NpcSpawnlocs[BG_SA_MAXNPC + BG_SA_DEMOLISHER_AMOUNT][4] = + { + //Cannons + { 1436.429f, 110.05f, 41.407f, 5.4f }, + { 1404.9023f, 84.758f, 41.183f, 5.46f }, + { 1068.693f, -86.951f, 93.81f, 0.02f }, + { 1068.83f, -127.56f, 96.45f, 0.0912f }, + { 1422.115f, -196.433f, 42.1825f, 1.0222f }, + { 1454.887f, -220.454f, 41.956f, 0.9627f }, + { 1232.345f, -187.517f, 66.945f, 0.45f }, + { 1249.634f, -224.189f, 66.72f, 0.635f }, + { 1236.213f, 92.287f, 64.965f, 5.751f }, + { 1215.11f, 57.772f, 64.739f, 5.78f } , + //Demolishers + { 1611.597656,-117.270073,8.719355,2.513274}, + { 1575.562500,-158.421875,5.024450,2.129302}, + { 1618.047729,61.424641,7.248210,3.979351}, + { 1575.103149,98.873344,2.830360,3.752458}, + //Npcs + { 1348.644165, -298.786469, 31.080130, 1.710423}, + { 1358.191040, 195.527786, 31.018187, 4.171337}, + //Demolishers2 + { 1371.055786, -317.071136, 35.007359, 1.947460}, + { 1424.034912, -260.195190, 31.084425, 2.820013}, + { 1353.139893, 223.745438, 35.265411, 4.343684}, + { 1404.809570, 197.027237, 32.046032, 3.605401}, + }; + +enum BG_SA_Objects + { + BG_SA_GREEN_GATE = 0, + BG_SA_YELLOW_GATE, + BG_SA_BLUE_GATE, + BG_SA_RED_GATE, + BG_SA_PURPLE_GATE, + BG_SA_ANCIENT_GATE, + BG_SA_TITAN_RELIC, + BG_SA_BOAT_ONE, + BG_SA_BOAT_TWO, + BG_SA_SIGIL_1, + BG_SA_SIGIL_2, + BG_SA_SIGIL_3, + BG_SA_SIGIL_4, + BG_SA_SIGIL_5, + BG_SA_CENTRAL_FLAGPOLE, + BG_SA_RIGHT_FLAGPOLE, + BG_SA_LEFT_FLAGPOLE, + BG_SA_CENTRAL_FLAG, + BG_SA_RIGHT_FLAG, + BG_SA_LEFT_FLAG, + BG_SA_MAXOBJ + }; + +const float BG_SA_ObjSpawnlocs[BG_SA_MAXOBJ][4] = + { + { 1411.57f, 108.163f, 28.692f, 5.441f }, + { 1055.452f, -108.1f, 82.134f, 0.034f }, + { 1431.3413f, -219.437f, 30.893f, 0.9736f }, + { 1227.667f, -212.555f, 55.372f, 0.5023f }, + { 1214.681f, 81.21f, 53.413f, 5.745f }, + { 878.555f, -108.989f, 119.835f, 0.0565f }, + { 836.5f, -108.8f, 120.219f, 0.0f }, + //Ships + { 2679.696777, -826.891235, 3.712860, 5.78367f}, //rot2 1 rot3 0.0002 + { 2574.003662, 981.261475, 2.603424, 0.807696}, + //Sigils + { 1414.054f, 106.72f, 41.442f, 5.441f }, + { 1060.63f, -107.8f, 94.7f, 0.034f }, + { 1433.383f, -216.4f, 43.642f, 0.9736f }, + { 1230.75f, -210.724f, 67.611f, 0.5023f }, + { 1217.8f, 79.532f, 66.58f, 5.745f }, + //Flagpoles + { 1215.114258,-65.711861,70.084267,-3.124123}, + {1338.863892,-153.336533,30.895121,-2.530723}, + {1309.124268,9.410645,30.893402,-1.623156}, + //Flags + { 1215.108032,-65.715767,70.084267,-3.124123}, + { 1338.859253,-153.327316,30.895077,-2.530723}, + { 1309.192017,9.416233,30.893402,1.518436}, + }; + +/* Ships: + * 193182 - ally + * 193183 - horde + * 193184 - horde + * 193185 - ally + * Banners: + * 191308 - left one, + * 191306 - right one, + * 191310 - central, + * Ally ones, substract 1 + * to get horde ones. + */ + +const uint32 BG_SA_ObjEntries[BG_SA_MAXOBJ + BG_SA_FLAG_AMOUNT] = + { + 190722, + 190727, + 190724, + 190726, + 190723, + 192549, + 192834, + 193182, + 193185, + 192687, + 192685, + 192689, + 192690, + 192691, + 191311, + 191311, + 191311, + 191310, + 191306, + 191308, + 191309, + 191305, + 191307, + }; + +const uint32 BG_SA_Factions[2] = + { + 1732, + 1735, + }; + +enum BG_SA_Graveyards + { + BG_SA_BEACH_GY = 0, + BG_SA_DEFENDER_LAST_GY, + BG_SA_RIGHT_CAPTURABLE_GY, + BG_SA_LEFT_CAPTURABLE_GY, + BG_SA_CENTRAL_CAPTURABLE_GY, + BG_SA_MAX_GY + }; + +const uint32 BG_SA_GYEntries[BG_SA_MAX_GY] = + { + 1350, + 1349, + 1347, + 1346, + 1348, + }; + +const float BG_SA_GYOrientation[BG_SA_MAX_GY] = + { + 6.202f, + 1.926f, //right capturable GY + 3.917f, //left capturable GY + 3.104f, //center, capturable + 6.148f, //defender last GY + }; + +struct BG_SA_RoundScore +{ + TeamId winner; + uint32 time; +}; + +class BattleGroundSA : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundSA(); + ~BattleGroundSA(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + virtual bool SetupBattleGround(); + virtual void Reset(); + virtual void FillInitialWorldStates(WorldPacket& data); + virtual void EventPlayerDamagedGO(Player* plr, GameObject* go, uint8 hitType, uint32 destroyedEvent); + virtual void HandleKillUnit(Creature* unit, Player* killer); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); + virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); + virtual void EventPlayerUsedGO(Player* Source, GameObject* object); + uint32 GetGateIDFromDestroyEventID(uint32 id) + { + uint32 i = 0; + switch(id) + { + case 19046: i = BG_SA_GREEN_GATE; break; //Green gate destroyed + case 19045: i = BG_SA_BLUE_GATE; break; //blue gate + case 19047: i = BG_SA_RED_GATE; break; //red gate + case 19048: i = BG_SA_PURPLE_GATE; break; //purple gate + case 19049: i = BG_SA_YELLOW_GATE; break; //yellow gate + case 19837: i = BG_SA_ANCIENT_GATE; break; //ancient gate + } + return i; + } + uint32 GetWorldStateFromGateID(uint32 id) + { + uint32 uws = 0; + switch(id) + { + case BG_SA_GREEN_GATE: uws = BG_SA_GREEN_GATEWS; break; + case BG_SA_YELLOW_GATE: uws = BG_SA_YELLOW_GATEWS; break; + case BG_SA_BLUE_GATE: uws = BG_SA_BLUE_GATEWS; break; + case BG_SA_RED_GATE: uws = BG_SA_RED_GATEWS; break; + case BG_SA_PURPLE_GATE: uws = BG_SA_PURPLE_GATEWS; break; + case BG_SA_ANCIENT_GATE: uws = BG_SA_ANCIENT_GATEWS; break; + } + return uws; + } + void EndBattleGround(uint32 winner); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + + private: + bool ResetObjs(); + void StartShips(); + void TeleportPlayers(); + void OverrideGunFaction(); + void DemolisherStartState(bool start); + void DestroyGate(Player* pl, GameObject* /*go*/, uint32 destroyedEvent); + void SendTime(); + void CaptureGraveyard(BG_SA_Graveyards i, Player *Source); + void ToggleTimer(); + void UpdateDemolisherSpawns(); + TeamId attackers; + uint32 TotalTime; + uint32 BG_SA_ENDROUNDTIME; + bool ShipsStarted; + BG_SA_GateState GateStatus[6]; + BG_SA_Status status; + TeamId GraveyardStatus[BG_SA_MAX_GY]; + BG_SA_RoundScore RoundScores[2]; + bool TimerEnabled; + uint32 UpdateWaitTimer;//5secs before starting the 1min countdown for second round + bool SignaledRoundTwo; + bool SignaledRoundTwoHalfMin; + bool InitSecondRound; +}; +#endif diff --git a/src/server/game/BattleGrounds/BattleGroundWS.cpp b/src/server/game/BattleGrounds/BattleGroundWS.cpp new file mode 100644 index 00000000000..71872511274 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundWS.cpp @@ -0,0 +1,841 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BattleGround.h" +#include "BattleGroundWS.h" +#include "Creature.h" +#include "GameObject.h" +#include "Language.h" +#include "Object.h" +#include "ObjectMgr.h" +#include "BattleGroundMgr.h" +#include "Player.h" +#include "World.h" +#include "WorldPacket.h" + +// these variables aren't used outside of this file, so declare them only here +enum BG_WSG_Rewards +{ + BG_WSG_WIN = 0, + BG_WSG_FLAG_CAP, + BG_WSG_MAP_COMPLETE, + BG_WSG_REWARD_NUM +}; + +uint32 BG_WSG_Honor[BG_HONOR_MODE_NUM][BG_WSG_REWARD_NUM] = { + {20,40,40}, // normal honor + {60,40,80} // holiday +}; + +uint32 BG_WSG_Reputation[BG_HONOR_MODE_NUM][BG_WSG_REWARD_NUM] = { + {0,35,0}, // normal honor + {0,45,0} // holiday +}; + +BattleGroundWS::BattleGroundWS() +{ + m_BgObjects.resize(BG_WS_OBJECT_MAX); + m_BgCreatures.resize(BG_CREATURES_MAX_WS); + + m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES; + m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE; + m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN; +} + +BattleGroundWS::~BattleGroundWS() +{ +} + +void BattleGroundWS::Update(uint32 diff) +{ + BattleGround::Update(diff); + + if (GetStatus() == STATUS_IN_PROGRESS) + { + if (GetStartTime() >= 25*MINUTE*IN_MILISECONDS) // Òàéìåð òèêàòü íà÷èíàåò ïîñëå 25 ìèíóò + { + if (GetTeamScore(ALLIANCE) == 0) + { + if (GetTeamScore(HORDE) == 0) // No one scored - result is tie + EndBattleGround(NULL); + else // Horde has more points and thus wins + EndBattleGround(HORDE); + } + + else if (GetTeamScore(HORDE) == 0) + EndBattleGround(ALLIANCE); // Alliance has > 0, Horde has 0, alliance wins + + else if (GetTeamScore(HORDE) == GetTeamScore(ALLIANCE)) // Team score equal, winner is team that scored the first flag + EndBattleGround(m_FirstFlagCaptureTeam); + + else if (GetTeamScore(HORDE) > GetTeamScore(ALLIANCE)) // Last but not least, check who has the higher score + EndBattleGround(HORDE); + else + EndBattleGround(ALLIANCE); + } + else if (GetStartTime() > m_minutesElapsed*MINUTE*IN_MILISECONDS) + { + ++m_minutesElapsed; + UpdateWorldState(BG_WS_STATE_TIMER, 25-m_minutesElapsed); + } + + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + { + m_FlagsTimer[BG_TEAM_ALLIANCE] -= diff; + + if (m_FlagsTimer[BG_TEAM_ALLIANCE] < 0) + { + m_FlagsTimer[BG_TEAM_ALLIANCE] = 0; + RespawnFlag(ALLIANCE, true); + } + } + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + { + m_FlagsDropTimer[BG_TEAM_ALLIANCE] -= diff; + + if (m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0) + { + m_FlagsDropTimer[BG_TEAM_ALLIANCE] = 0; + RespawnFlagAfterDrop(ALLIANCE); + m_BothFlagsKept = false; + } + } + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + { + m_FlagsTimer[BG_TEAM_HORDE] -= diff; + + if (m_FlagsTimer[BG_TEAM_HORDE] < 0) + { + m_FlagsTimer[BG_TEAM_HORDE] = 0; + RespawnFlag(HORDE, true); + } + } + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + { + m_FlagsDropTimer[BG_TEAM_HORDE] -= diff; + + if (m_FlagsDropTimer[BG_TEAM_HORDE] < 0) + { + m_FlagsDropTimer[BG_TEAM_HORDE] = 0; + RespawnFlagAfterDrop(HORDE); + m_BothFlagsKept = false; + } + } + if (m_BothFlagsKept) + { + m_FlagSpellForceTimer += diff; + if (m_FlagDebuffState == 0 && m_FlagSpellForceTimer >= 600000) //10 minutes + { + if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[0])) + plr->CastSpell(plr,WS_SPELL_FOCUSED_ASSAULT,true); + if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[1])) + plr->CastSpell(plr,WS_SPELL_FOCUSED_ASSAULT,true); + m_FlagDebuffState = 1; + } + else if (m_FlagDebuffState == 1 && m_FlagSpellForceTimer >= 900000) //15 minutes + { + if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[0])) + { + plr->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); + plr->CastSpell(plr,WS_SPELL_BRUTAL_ASSAULT,true); + } + if (Player * plr = objmgr.GetPlayer(m_FlagKeepers[1])) + { + plr->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); + plr->CastSpell(plr,WS_SPELL_BRUTAL_ASSAULT,true); + } + m_FlagDebuffState = 2; + } + } + else + { + m_FlagSpellForceTimer = 0; //reset timer. + m_FlagDebuffState = 0; + } + } +} + +void BattleGroundWS::StartingEventCloseDoors() +{ + for (uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; ++i) + { + DoorClose(i); + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + } + for (uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; ++i) + SpawnBGObject(i, RESPAWN_ONE_DAY); + + UpdateWorldState(BG_WS_STATE_TIMER_ACTIVE, 1); + UpdateWorldState(BG_WS_STATE_TIMER, 25); +} + +void BattleGroundWS::StartingEventOpenDoors() +{ + for (uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_A_4; ++i) + DoorOpen(i); + for (uint32 i = BG_WS_OBJECT_DOOR_H_1; i <= BG_WS_OBJECT_DOOR_H_2; ++i) + DoorOpen(i); + + SpawnBGObject(BG_WS_OBJECT_DOOR_A_5, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_A_6, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_H_3, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_H_4, RESPAWN_ONE_DAY); + + for (uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); +} + +void BattleGroundWS::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundWGScore* sc = new BattleGroundWGScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundWS::RespawnFlag(uint32 Team, bool captured) +{ + if (Team == ALLIANCE) + { + sLog.outDebug("Respawn Alliance flag"); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; + } + else + { + sLog.outDebug("Respawn Horde flag"); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; + } + + if (captured) + { + //when map_update will be allowed for battlegrounds this code will be useless + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); + SendMessageToAll(LANG_BG_WS_F_PLACED, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); // flag respawned sound... + } + m_BothFlagsKept = false; +} + +void BattleGroundWS::RespawnFlagAfterDrop(uint32 team) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + RespawnFlag(team,false); + if (team == ALLIANCE) + { + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); + SendMessageToAll(LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + else + { + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); + SendMessageToAll(LANG_BG_WS_HORDE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL); + } + + PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); + + GameObject *obj = GetBgMap()->GetGameObject(GetDroppedFlagGUID(team)); + if (obj) + obj->Delete(); + else + sLog.outError("unknown droped flag bg, guid: %u",GUID_LOPART(GetDroppedFlagGUID(team))); + + SetDroppedFlagGUID(0,team); + m_BothFlagsKept = false; +} + +void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + uint32 winner = 0; + + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + if (Source->GetTeam() == ALLIANCE) + { + if (!this->IsHordeFlagPickedup()) + return; + SetHordeFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time + // horde flag in base (but not respawned yet) + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; + // Drop Horde Flag from Player + Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); + if (m_FlagDebuffState == 1) + Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); + if (m_FlagDebuffState == 2) + Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); + if (GetTeamScore(ALLIANCE) < BG_WS_MAX_TEAM_SCORE) + AddPoint(ALLIANCE, 1); + PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE); + RewardReputationToTeam(890, m_ReputationCapture, ALLIANCE); + } + else + { + if (!this->IsAllianceFlagPickedup()) + return; + SetAllianceFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time + // alliance flag in base (but not respawned yet) + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; + // Drop Alliance Flag from Player + Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); + if (m_FlagDebuffState == 1) + Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); + if (m_FlagDebuffState == 2) + Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); + if (GetTeamScore(HORDE) < BG_WS_MAX_TEAM_SCORE) + AddPoint(HORDE, 1); + PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_HORDE); + RewardReputationToTeam(889, m_ReputationCapture, HORDE); + } + //for flag capture is reward 2 honorable kills + RewardHonorToTeam(GetBonusHonorFromKill(2), Source->GetTeam()); + + SpawnBGObject(BG_WS_OBJECT_H_FLAG, BG_WS_FLAG_RESPAWN_TIME); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, BG_WS_FLAG_RESPAWN_TIME); + + if (Source->GetTeam() == ALLIANCE) + SendMessageToAll(LANG_BG_WS_CAPTURED_HF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + else + SendMessageToAll(LANG_BG_WS_CAPTURED_AF, CHAT_MSG_BG_SYSTEM_HORDE, Source); + + UpdateFlagState(Source->GetTeam(), 1); // flag state none + UpdateTeamScore(Source->GetTeam()); + // only flag capture should be updated + UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures + + if (!m_FirstFlagCaptureTeam) + SetFirstFlagCapture(Source->GetTeam()); + + if (GetTeamScore(ALLIANCE) == BG_WS_MAX_TEAM_SCORE) + winner = ALLIANCE; + + if (GetTeamScore(HORDE) == BG_WS_MAX_TEAM_SCORE) + winner = HORDE; + + if (winner) + { + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 0); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 0); + UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, 1); + UpdateWorldState(BG_WS_FLAG_STATE_HORDE, 1); + UpdateWorldState(BG_WS_STATE_TIMER_ACTIVE, 0); + + RewardHonorToTeam(BG_WSG_Honor[m_HonorMode][BG_WSG_WIN], winner); + EndBattleGround(winner); + } + else + { + m_FlagsTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_RESPAWN_TIME; + } +} + +void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + { + // if not running, do not cast things at the dropper player (prevent spawning the "dropped" flag), neither send unnecessary messages + // just take off the aura + if (Source->GetTeam() == ALLIANCE) + { + if (!this->IsHordeFlagPickedup()) + return; + if (GetHordeFlagPickerGUID() == Source->GetGUID()) + { + SetHordeFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); + } + } + else + { + if (!this->IsAllianceFlagPickedup()) + return; + if (GetAllianceFlagPickerGUID() == Source->GetGUID()) + { + SetAllianceFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); + } + } + return; + } + + bool set = false; + + if (Source->GetTeam() == ALLIANCE) + { + if (!IsHordeFlagPickedup()) + return; + if (GetHordeFlagPickerGUID() == Source->GetGUID()) + { + SetHordeFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); + if (m_FlagDebuffState == 1) + Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); + if (m_FlagDebuffState == 2) + Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_GROUND; + Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG_DROPPED, true); + set = true; + } + } + else + { + if (!IsAllianceFlagPickedup()) + return; + if (GetAllianceFlagPickerGUID() == Source->GetGUID()) + { + SetAllianceFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); + if (m_FlagDebuffState == 1) + Source->RemoveAurasDueToSpell(WS_SPELL_FOCUSED_ASSAULT); + if (m_FlagDebuffState == 2) + Source->RemoveAurasDueToSpell(WS_SPELL_BRUTAL_ASSAULT); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND; + Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG_DROPPED, true); + set = true; + } + } + + if (set) + { + Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); + UpdateFlagState(Source->GetTeam(), 1); + + if (Source->GetTeam() == ALLIANCE) + { + SendMessageToAll(LANG_BG_WS_DROPPED_HF, CHAT_MSG_BG_SYSTEM_HORDE, Source); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, uint32(-1)); + } + else + { + SendMessageToAll(LANG_BG_WS_DROPPED_AF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, uint32(-1)); + } + + m_FlagsDropTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_DROP_TIME; + } +} + +void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + int32 message_id = 0; + ChatMsg type = CHAT_MSG_BG_SYSTEM_NEUTRAL; + + //alliance flag picked up from base + if (Source->GetTeam() == HORDE && this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_BASE + && this->m_BgObjects[BG_WS_OBJECT_A_FLAG] == target_obj->GetGUID()) + { + message_id = LANG_BG_WS_PICKEDUP_AF; + type = CHAT_MSG_BG_SYSTEM_HORDE; + PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); + SetAllianceFlagPicker(Source->GetGUID()); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; + //update world state to show correct flag carrier + UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); + Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true); + if (m_FlagState[1] == BG_WS_FLAG_STATE_ON_PLAYER) + m_BothFlagsKept = true; + } + + //horde flag picked up from base + if (Source->GetTeam() == ALLIANCE && this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_BASE + && this->m_BgObjects[BG_WS_OBJECT_H_FLAG] == target_obj->GetGUID()) + { + message_id = LANG_BG_WS_PICKEDUP_HF; + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); + SetHordeFlagPicker(Source->GetGUID()); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; + //update world state to show correct flag carrier + UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); + Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true); + if (m_FlagState[0] == BG_WS_FLAG_STATE_ON_PLAYER) + m_BothFlagsKept = true; + } + + //Alliance flag on ground(not in base) (returned or picked up again from ground!) + if (GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) + { + if (Source->GetTeam() == ALLIANCE) + { + message_id = LANG_BG_WS_RETURNED_AF; + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + UpdateFlagState(HORDE, BG_WS_FLAG_STATE_WAIT_RESPAWN); + RespawnFlag(ALLIANCE, false); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); + PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED); + UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1); + m_BothFlagsKept = false; + } + else + { + message_id = LANG_BG_WS_PICKEDUP_AF; + type = CHAT_MSG_BG_SYSTEM_HORDE; + PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); + SetAllianceFlagPicker(Source->GetGUID()); + Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; + UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); + if (m_FlagDebuffState == 1) + Source->CastSpell(Source,WS_SPELL_FOCUSED_ASSAULT,true); + if (m_FlagDebuffState == 2) + Source->CastSpell(Source,WS_SPELL_BRUTAL_ASSAULT,true); + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); + } + //called in HandleGameObjectUseOpcode: + //target_obj->Delete(); + } + + //Horde flag on ground(not in base) (returned or picked up again) + if (GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) + { + if (Source->GetTeam() == HORDE) + { + message_id = LANG_BG_WS_RETURNED_HF; + type = CHAT_MSG_BG_SYSTEM_HORDE; + UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_WAIT_RESPAWN); + RespawnFlag(HORDE, false); + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); + PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED); + UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1); + m_BothFlagsKept = false; + } + else + { + message_id = LANG_BG_WS_PICKEDUP_HF; + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); + SetHordeFlagPicker(Source->GetGUID()); + Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; + UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); + if (m_FlagDebuffState == 1) + Source->CastSpell(Source,WS_SPELL_FOCUSED_ASSAULT,true); + if (m_FlagDebuffState == 2) + Source->CastSpell(Source,WS_SPELL_BRUTAL_ASSAULT,true); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); + } + //called in HandleGameObjectUseOpcode: + //target_obj->Delete(); + } + + if (!message_id) + return; + + SendMessageToAll(message_id, type, Source); + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); +} + +void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid) +{ + // sometimes flag aura not removed :( + if (IsAllianceFlagPickedup() && m_FlagKeepers[BG_TEAM_ALLIANCE] == guid) + { + if (!plr) + { + sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); + this->SetAllianceFlagPicker(0); + this->RespawnFlag(ALLIANCE, false); + } + else + this->EventPlayerDroppedFlag(plr); + } + if (IsHordeFlagPickedup() && m_FlagKeepers[BG_TEAM_HORDE] == guid) + { + if (!plr) + { + sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); + this->SetHordeFlagPicker(0); + this->RespawnFlag(HORDE, false); + } + else + this->EventPlayerDroppedFlag(plr); + } +} + +void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value) +{ + if (team == ALLIANCE) + UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, value); + else + UpdateWorldState(BG_WS_FLAG_STATE_HORDE, value); +} + +void BattleGroundWS::UpdateTeamScore(uint32 team) +{ + if (team == ALLIANCE) + UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, GetTeamScore(team)); + else + UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, GetTeamScore(team)); +} + +void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). + //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_1]; + break; + case 3687: // Horde elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). + //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_2]; + break; + case 3706: // Alliance elixir of regeneration spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_1]; + break; + case 3708: // Horde elixir of regeneration spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_2]; + break; + case 3707: // Alliance elixir of berserk spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_1]; + break; + case 3709: // Horde elixir of berserk spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_2]; + break; + case 3646: // Alliance Flag spawn + if (m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE]) + if (GetHordeFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source); + break; + case 3647: // Horde Flag spawn + if (m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE]) + if (GetAllianceFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source); + break; + case 3649: // unk1 + case 3688: // unk2 + case 4628: // unk3 + case 4629: // unk4 + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if (buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +bool BattleGroundWS::SetupBattleGround() +{ + // flags + if (!AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, BG_WS_FLAG_RESPAWN_TIME/1000) + || !AddObject(BG_WS_OBJECT_H_FLAG, BG_OBJECT_H_FLAG_WS_ENTRY, 916.0226f, 1434.405f, 345.413f, 0.01745329f, 0, 0, 0.008726535f, 0.9999619f, BG_WS_FLAG_RESPAWN_TIME/1000) + // buffs + || !AddObject(BG_WS_OBJECT_SPEEDBUFF_1, BG_OBJECTID_SPEEDBUFF_ENTRY, 1449.93f, 1470.71f, 342.6346f, -1.64061f, 0, 0, 0.7313537f, -0.6819983f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_SPEEDBUFF_2, BG_OBJECTID_SPEEDBUFF_ENTRY, 1005.171f, 1447.946f, 335.9032f, 1.64061f, 0, 0, 0.7313537f, 0.6819984f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_REGENBUFF_1, BG_OBJECTID_REGENBUFF_ENTRY, 1317.506f, 1550.851f, 313.2344f, -0.2617996f, 0, 0, 0.1305263f, -0.9914448f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_REGENBUFF_2, BG_OBJECTID_REGENBUFF_ENTRY, 1110.451f, 1353.656f, 316.5181f, -0.6806787f, 0, 0, 0.333807f, -0.9426414f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_BERSERKBUFF_1, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1320.09f, 1378.79f, 314.7532f, 1.186824f, 0, 0, 0.5591929f, 0.8290376f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_BERSERKBUFF_2, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1139.688f, 1560.288f, 306.8432f, -2.443461f, 0, 0, 0.9396926f, -0.3420201f, BUFF_RESPAWN_TIME) + // alliance gates + || !AddObject(BG_WS_OBJECT_DOOR_A_1, BG_OBJECT_DOOR_A_1_WS_ENTRY, 1503.335f, 1493.466f, 352.1888f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_2, BG_OBJECT_DOOR_A_2_WS_ENTRY, 1492.478f, 1457.912f, 342.9689f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_3, BG_OBJECT_DOOR_A_3_WS_ENTRY, 1468.503f, 1494.357f, 351.8618f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_4, BG_OBJECT_DOOR_A_4_WS_ENTRY, 1471.555f, 1458.778f, 362.6332f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_5, BG_OBJECT_DOOR_A_5_WS_ENTRY, 1492.347f, 1458.34f, 342.3712f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_6, BG_OBJECT_DOOR_A_6_WS_ENTRY, 1503.466f, 1493.367f, 351.7352f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY) + // horde gates + || !AddObject(BG_WS_OBJECT_DOOR_H_1, BG_OBJECT_DOOR_H_1_WS_ENTRY, 949.1663f, 1423.772f, 345.6241f, -0.5756807f, -0.01673368f, -0.004956111f, -0.2839723f, 0.9586737f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_H_2, BG_OBJECT_DOOR_H_2_WS_ENTRY, 953.0507f, 1459.842f, 340.6526f, -1.99662f, -0.1971825f, 0.1575096f, -0.8239487f, 0.5073641f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_H_3, BG_OBJECT_DOOR_H_3_WS_ENTRY, 949.9523f, 1422.751f, 344.9273f, 0.0f, 0, 0, 0, 1, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_H_4, BG_OBJECT_DOOR_H_4_WS_ENTRY, 950.7952f, 1459.583f, 342.1523f, 0.05235988f, 0, 0, 0.02617695f, 0.9996573f, RESPAWN_IMMEDIATELY) +) + { + sLog.outErrorDb("BatteGroundWS: Failed to spawn some object BattleGround not created!"); + return false; + } + + WorldSafeLocsEntry const *sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); + if (!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) + { + sLog.outErrorDb("BatteGroundWS: Failed to spawn Alliance spirit guide! BattleGround not created!"); + return false; + } + + sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); + if (!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) + { + sLog.outErrorDb("BatteGroundWS: Failed to spawn Horde spirit guide! BattleGround not created!"); + return false; + } + + sLog.outDebug("BatteGroundWS: BG objects and spirit guides spawned"); + + return true; +} + +void BattleGroundWS::Reset() +{ + //call parent's class reset + BattleGround::Reset(); + + m_FlagKeepers[BG_TEAM_ALLIANCE] = 0; + m_FlagKeepers[BG_TEAM_HORDE] = 0; + m_DroppedFlagGUID[BG_TEAM_ALLIANCE] = 0; + m_DroppedFlagGUID[BG_TEAM_HORDE] = 0; + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID()); + m_ReputationCapture = (isBGWeekend) ? 45 : 35; + m_HonorWinKills = (isBGWeekend) ? 3 : 1; + m_HonorEndKills = (isBGWeekend) ? 4 : 2; + // For WorldState + m_minutesElapsed = 0; + m_FirstFlagCaptureTeam = 0; + + /* Spirit nodes is static at this BG and then not required deleting at BG reset. + if (m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE]) + DelCreature(WS_SPIRIT_MAIN_ALLIANCE); + if (m_BgCreatures[WS_SPIRIT_MAIN_HORDE]) + DelCreature(WS_SPIRIT_MAIN_HORDE); + */ +} + +void BattleGroundWS::EndBattleGround(uint32 winner) +{ + //win reward + if (winner == ALLIANCE) + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorWinKills), ALLIANCE); + if (winner == HORDE) + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorWinKills), HORDE); + //complete map_end rewards (even if no team wins) + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), HORDE); + + BattleGround::EndBattleGround(winner); +} + +void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer) +{ + if (GetStatus() != STATUS_IN_PROGRESS) + return; + + EventPlayerDroppedFlag(player); + + BattleGround::HandleKillPlayer(player, killer); +} + +void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor) +{ + + BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID()); + if (itr == m_PlayerScores.end()) // player not found + return; + + switch(type) + { + case SCORE_FLAG_CAPTURES: // flags captured + ((BattleGroundWGScore*)itr->second)->FlagCaptures += value; + break; + case SCORE_FLAG_RETURNS: // flags returned + ((BattleGroundWGScore*)itr->second)->FlagReturns += value; + break; + default: + BattleGround::UpdatePlayerScore(Source, type, value, doAddHonor); + break; + } +} + +WorldSafeLocsEntry const* BattleGroundWS::GetClosestGraveYard(Player* player) +{ + //if status in progress, it returns main graveyards with spiritguides + //else it will return the graveyard in the flagroom - this is especially good + //if a player dies in preparation phase - then the player can't cheat + //and teleport to the graveyard outside the flagroom + //and start running around, while the doors are still closed + if (player->GetTeam() == ALLIANCE) + { + if (GetStatus() == STATUS_IN_PROGRESS) + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); + else + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_ALLIANCE); + } + else + { + if (GetStatus() == STATUS_IN_PROGRESS) + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); + else + return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_HORDE); + } +} + +void BattleGroundWS::FillInitialWorldStates(WorldPacket& data) +{ + data << uint32(BG_WS_FLAG_CAPTURES_ALLIANCE) << uint32(GetTeamScore(ALLIANCE)); + data << uint32(BG_WS_FLAG_CAPTURES_HORDE) << uint32(GetTeamScore(HORDE)); + + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(-1); + else if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(1); + else + data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(0); + + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(-1); + else if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(1); + else + data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(0); + + data << uint32(BG_WS_FLAG_CAPTURES_MAX) << uint32(BG_WS_MAX_TEAM_SCORE); + + if (GetStatus() == STATUS_IN_PROGRESS) + { + data << uint32(BG_WS_STATE_TIMER_ACTIVE) << uint32(1); + data << uint32(BG_WS_STATE_TIMER) << uint32(25-m_minutesElapsed); + } + else + data << uint32(BG_WS_STATE_TIMER_ACTIVE) << uint32(0); + + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(2); + else + data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(1); + + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(2); + else + data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(1); + +} + diff --git a/src/server/game/BattleGrounds/BattleGroundWS.h b/src/server/game/BattleGrounds/BattleGroundWS.h new file mode 100644 index 00000000000..1a733c14570 --- /dev/null +++ b/src/server/game/BattleGrounds/BattleGroundWS.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDWS_H +#define __BATTLEGROUNDWS_H + +#include "BattleGround.h" + +enum BG_WS_TimerOrScore +{ + BG_WS_MAX_TEAM_SCORE = 3, + BG_WS_FLAG_RESPAWN_TIME = 23000, + BG_WS_FLAG_DROP_TIME = 10000, + BG_WS_SPELL_FORCE_TIME = 600000, + BG_WS_SPELL_BRUTAL_TIME = 900000 +}; + +enum BG_WS_Sound +{ + BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_WS_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_WS_SOUND_FLAG_PLACED = 8232, + BG_WS_SOUND_FLAG_RETURNED = 8192, + BG_WS_SOUND_HORDE_FLAG_PICKED_UP = 8212, + BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP = 8174, + BG_WS_SOUND_FLAGS_RESPAWNED = 8232 +}; + +enum BG_WS_SpellId +{ + BG_WS_SPELL_WARSONG_FLAG = 23333, + BG_WS_SPELL_WARSONG_FLAG_DROPPED = 23334, + BG_WS_SPELL_SILVERWING_FLAG = 23335, + BG_WS_SPELL_SILVERWING_FLAG_DROPPED = 23336, + BG_WS_SPELL_FOCUSED_ASSAULT = 46392, + BG_WS_SPELL_BRUTAL_ASSAULT = 46393 +}; + +enum BG_WS_WorldStates +{ + BG_WS_FLAG_UNK_ALLIANCE = 1545, + BG_WS_FLAG_UNK_HORDE = 1546, +// FLAG_UNK = 1547, + BG_WS_FLAG_CAPTURES_ALLIANCE = 1581, + BG_WS_FLAG_CAPTURES_HORDE = 1582, + BG_WS_FLAG_CAPTURES_MAX = 1601, + BG_WS_FLAG_STATE_HORDE = 2338, + BG_WS_FLAG_STATE_ALLIANCE = 2339, + BG_WS_STATE_TIMER = 4248, + BG_WS_STATE_TIMER_ACTIVE = 4247 +}; + +enum BG_WS_ObjectTypes +{ + BG_WS_OBJECT_DOOR_A_1 = 0, + BG_WS_OBJECT_DOOR_A_2 = 1, + BG_WS_OBJECT_DOOR_A_3 = 2, + BG_WS_OBJECT_DOOR_A_4 = 3, + BG_WS_OBJECT_DOOR_A_5 = 4, + BG_WS_OBJECT_DOOR_A_6 = 5, + BG_WS_OBJECT_DOOR_H_1 = 6, + BG_WS_OBJECT_DOOR_H_2 = 7, + BG_WS_OBJECT_DOOR_H_3 = 8, + BG_WS_OBJECT_DOOR_H_4 = 9, + BG_WS_OBJECT_A_FLAG = 10, + BG_WS_OBJECT_H_FLAG = 11, + BG_WS_OBJECT_SPEEDBUFF_1 = 12, + BG_WS_OBJECT_SPEEDBUFF_2 = 13, + BG_WS_OBJECT_REGENBUFF_1 = 14, + BG_WS_OBJECT_REGENBUFF_2 = 15, + BG_WS_OBJECT_BERSERKBUFF_1 = 16, + BG_WS_OBJECT_BERSERKBUFF_2 = 17, + BG_WS_OBJECT_MAX = 18 +}; + +enum BG_WS_ObjectEntry +{ + BG_OBJECT_DOOR_A_1_WS_ENTRY = 179918, + BG_OBJECT_DOOR_A_2_WS_ENTRY = 179919, + BG_OBJECT_DOOR_A_3_WS_ENTRY = 179920, + BG_OBJECT_DOOR_A_4_WS_ENTRY = 179921, + BG_OBJECT_DOOR_A_5_WS_ENTRY = 180322, + BG_OBJECT_DOOR_A_6_WS_ENTRY = 180322, + BG_OBJECT_DOOR_H_1_WS_ENTRY = 179916, + BG_OBJECT_DOOR_H_2_WS_ENTRY = 179917, + BG_OBJECT_DOOR_H_3_WS_ENTRY = 180322, + BG_OBJECT_DOOR_H_4_WS_ENTRY = 180322, + BG_OBJECT_A_FLAG_WS_ENTRY = 179830, + BG_OBJECT_H_FLAG_WS_ENTRY = 179831, + BG_OBJECT_A_FLAG_GROUND_WS_ENTRY = 179785, + BG_OBJECT_H_FLAG_GROUND_WS_ENTRY = 179786 +}; + +enum BG_WS_FlagState +{ + BG_WS_FLAG_STATE_ON_BASE = 0, + BG_WS_FLAG_STATE_WAIT_RESPAWN = 1, + BG_WS_FLAG_STATE_ON_PLAYER = 2, + BG_WS_FLAG_STATE_ON_GROUND = 3 +}; + +enum BG_WS_Graveyards +{ + WS_GRAVEYARD_FLAGROOM_ALLIANCE = 769, + WS_GRAVEYARD_FLAGROOM_HORDE = 770, + WS_GRAVEYARD_MAIN_ALLIANCE = 771, + WS_GRAVEYARD_MAIN_HORDE = 772 +}; + +enum BG_WS_CreatureTypes +{ + WS_SPIRIT_MAIN_ALLIANCE = 0, + WS_SPIRIT_MAIN_HORDE = 1, + + BG_CREATURES_MAX_WS = 2 +}; + +enum BG_WS_CarrierDebuffs +{ + WS_SPELL_FOCUSED_ASSAULT = 46392, + WS_SPELL_BRUTAL_ASSAULT = 46393 +}; + +class BattleGroundWGScore : public BattleGroundScore +{ + public: + BattleGroundWGScore() : FlagCaptures(0), FlagReturns(0) {}; + virtual ~BattleGroundWGScore() {}; + uint32 FlagCaptures; + uint32 FlagReturns; +}; + +class BattleGroundWS : public BattleGround +{ + friend class BattleGroundMgr; + + public: + /* Construction */ + BattleGroundWS(); + ~BattleGroundWS(); + void Update(uint32 diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + virtual void StartingEventCloseDoors(); + virtual void StartingEventOpenDoors(); + + /* BG Flags */ + uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; } + uint64 GetHordeFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_HORDE]; } + void SetAllianceFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_ALLIANCE] = guid; } + void SetHordeFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_HORDE] = guid; } + bool IsAllianceFlagPickedup() const { return m_FlagKeepers[BG_TEAM_ALLIANCE] != 0; } + bool IsHordeFlagPickedup() const { return m_FlagKeepers[BG_TEAM_HORDE] != 0; } + void RespawnFlag(uint32 Team, bool captured); + void RespawnFlagAfterDrop(uint32 Team); + uint8 GetFlagState(uint32 team) { return m_FlagState[GetTeamIndexByTeamId(team)]; } + void AddTimedAura(uint32 aura); + void RemoveTimedAura(uint32 aura); + bool IsBrutalTimerDone; + bool IsForceTimerDone; + + /* Battleground Events */ + virtual void EventPlayerDroppedFlag(Player *Source); + virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); + virtual void EventPlayerCapturedFlag(Player *Source); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + void HandleKillPlayer(Player *player, Player *killer); + bool SetupBattleGround(); + virtual void Reset(); + void EndBattleGround(uint32 winner); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); + + void UpdateFlagState(uint32 team, uint32 value); + void SetFirstFlagCapture(uint32 team) { m_FirstFlagCaptureTeam = team; } + void UpdateTeamScore(uint32 team); + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value, bool doAddHonor = true); + void SetDroppedFlagGUID(uint64 guid, uint32 TeamID) { m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)] = guid;} + uint64 GetDroppedFlagGUID(uint32 TeamID) { return m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)];} + virtual void FillInitialWorldStates(WorldPacket& data); + + /* Scorekeeping */ + uint32 GetTeamScore(uint32 TeamID) const { return m_TeamScores[GetTeamIndexByTeamId(TeamID)]; } + void AddPoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] += Points; } + void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } + void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } + private: + uint64 m_FlagKeepers[2]; // 0 - alliance, 1 - horde + uint64 m_DroppedFlagGUID[2]; + uint8 m_FlagState[2]; // for checking flag state + int32 m_FlagsTimer[2]; + int32 m_FlagsDropTimer[2]; + uint32 m_FirstFlagCaptureTeam; // Winner is based on this if score is equal + + uint32 m_ReputationCapture; + uint32 m_HonorWinKills; + uint32 m_HonorEndKills; + int32 m_FlagSpellForceTimer; + bool m_BothFlagsKept; + uint8 m_FlagDebuffState; // 0 - no debuffs, 1 - focused assault, 2 - brutal assault + uint8 m_minutesElapsed; +}; +#endif + diff --git a/src/server/game/Calendar.cpp b/src/server/game/Calendar.cpp deleted file mode 100644 index 0c1efb20f87..00000000000 --- a/src/server/game/Calendar.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ diff --git a/src/server/game/Calendar.h b/src/server/game/Calendar.h deleted file mode 100644 index 2d35a6c23c7..00000000000 --- a/src/server/game/Calendar.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CALENDAR_H -#define TRINITY_CALENDAR_H - -class Calendar -{ - -}; -#endif diff --git a/src/server/game/CalendarHandler.cpp b/src/server/game/CalendarHandler.cpp deleted file mode 100644 index f6679c5d5ac..00000000000 --- a/src/server/game/CalendarHandler.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "WorldPacket.h" -#include "WorldSession.h" - -#include "InstanceSaveMgr.h" -#include "Log.h" -#include "Opcodes.h" -#include "Player.h" - -void WorldSession::HandleCalendarGetCalendar(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_GET_CALENDAR"); // empty - - time_t cur_time = time(NULL); - - WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR,4+4*0+4+4*0+4+4); - - data << uint32(0); // invite count - /* - for (;;) - { - uint64 inviteId; - uint64 unkGuid0; - uint8 unk1, unk2, unk3; - uint64 creatorGuid; - } - */ - - data << uint32(0); // event count - /* - for (;;) - { - uint64 eventId; - std::string title; // 128 chars - uint32 type; - uint32 occurrenceTime; - uint32 flags; - uint32 unk4; -- possibly mapid for dungeon/raid - uint64 creatorGuid; - } - */ - - data << uint32(0); // unk - data << uint32(secsToTimeBitFields(cur_time)); // current time - - uint32 counter = 0; - size_t p_counter = data.wpos(); - data << uint32(counter); // instance save count - - for (int i = 0; i < MAX_DIFFICULTY; ++i) - { - for (Player::BoundInstancesMap::const_iterator itr = _player->m_boundInstances[i].begin(); itr != _player->m_boundInstances[i].end(); ++itr) - { - if (itr->second.perm) - { - InstanceSave *save = itr->second.save; - data << uint32(save->GetMapId()); - data << uint32(save->GetDifficulty()); - data << uint32(save->GetResetTime() - cur_time); - data << uint64(save->GetInstanceId()); // instance save id as unique instance copy id - ++counter; - } - } - } - - data.put(p_counter, counter); - - data << uint32(1135753200); // unk (28.12.2005 12:00) - - counter = 0; - p_counter = data.wpos(); - data << uint32(counter); // raid reset count - - ResetTimeByMapDifficultyMap const& resets = sInstanceSaveManager.GetResetTimeMap(); - for (ResetTimeByMapDifficultyMap::const_iterator itr = resets.begin(); itr != resets.end(); ++itr) - { - uint32 mapid = PAIR32_LOPART(itr->first); - MapEntry const* mapEnt = sMapStore.LookupEntry(mapid); - if (!mapEnt || !mapEnt->IsRaid()) - continue; - - data << uint32(mapid); - data << uint32(itr->second - cur_time); - data << uint32(mapEnt->unk_time); - ++counter; - } - - data.put(p_counter, counter); - - data << uint32(0); // holiday count? - /* - for (;;) - { - uint32 unk5, unk6, unk7, unk8, unk9; - for (uint32 j = 0; j < 26; ++j) - { - uint32 unk10; - } - for (uint32 j = 0; j < 10; ++j) - { - uint32 unk11; - } - for (uint32 j = 0; j < 10; ++j) - { - uint32 unk12; - } - std::string holidayName; // 64 chars - } - */ - - sLog.outDebug("Sending calendar"); - data.hexlike(); - SendPacket(&data); -} - -void WorldSession::HandleCalendarGetEvent(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_GET_EVENT"); - recv_data.hexlike(); - recv_data.read_skip(); // unk -} - -void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_GUILD_FILTER"); - recv_data.hexlike(); - recv_data.read_skip(); // unk1 - recv_data.read_skip(); // unk2 - recv_data.read_skip(); // unk3 -} - -void WorldSession::HandleCalendarArenaTeam(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_ARENA_TEAM"); - recv_data.hexlike(); - recv_data.read_skip(); // unk -} - -void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_ADD_EVENT"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //std::string unk1, unk2; - //recv_data >> (std::string)unk1; - //recv_data >> (std::string)unk2; - - //uint8 unk3, unk4; - //uint32 unk5, unk6, unk7, unk8, unk9, count = 0; - //recv_data >> (uint8)unk3; - //recv_data >> (uint8)unk4; - //recv_data >> (uint32)unk5; - //recv_data >> (uint32)unk6; - //recv_data >> (uint32)unk7; - //recv_data >> (uint32)unk8; - //recv_data >> (uint32)unk9; - //if (!((unk9 >> 6) & 1)) - //{ - // recv_data >> (uint32)count; - // if (count) - // { - // uint8 unk12,unk13; - // uint64 guid; - // for (int i=0; i> (uint8)unk12; - // recv_data >> (uint8)unk13; - // } - // } - //} -} - -void WorldSession::HandleCalendarUpdateEvent(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_UPDATE_EVENT"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> std::string - //recv_data >> std::string - //recv_data >> uint8 - //recv_data >> uint8 - //recv_data >> uint32 - //recv_data >> uint32 - //recv_data >> uint32 - //recv_data >> uint32 - //recv_data >> uint32 -} - -void WorldSession::HandleCalendarRemoveEvent(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_REMOVE_EVENT"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint32 - -} - -void WorldSession::HandleCalendarCopyEvent(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_COPY_EVENT"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint32 - -} - -void WorldSession::HandleCalendarEventInvite(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_INVITE"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> std::string - //recv_data >> uint8 - //recv_data >> uint8 - -} - -void WorldSession::HandleCalendarEventRsvp(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_RSVP"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint32 - -} - -void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data.readPackGUID(guid) - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint64 -} - -void WorldSession::HandleCalendarEventStatus(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_STATUS"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data.readPackGUID(guid) - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint32 -} - -void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data.readPackGUID(guid) - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint32 -} - -void WorldSession::HandleCalendarComplain(WorldPacket &recv_data) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_COMPLAIN"); - recv_data.hexlike(); - recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam - - //recv_data >> uint64 - //recv_data >> uint64 - //recv_data >> uint64 -} - -void WorldSession::HandleCalendarGetNumPending(WorldPacket & /*recv_data*/) -{ - sLog.outDebug("WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); // empty - - WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4); - data << uint32(0); // 0 - no pending invites, 1 - some pending invites - SendPacket(&data); -} diff --git a/src/server/game/Calender/Calendar.cpp b/src/server/game/Calender/Calendar.cpp new file mode 100644 index 00000000000..0c1efb20f87 --- /dev/null +++ b/src/server/game/Calender/Calendar.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ diff --git a/src/server/game/Calender/Calendar.h b/src/server/game/Calender/Calendar.h new file mode 100644 index 00000000000..2d35a6c23c7 --- /dev/null +++ b/src/server/game/Calender/Calendar.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CALENDAR_H +#define TRINITY_CALENDAR_H + +class Calendar +{ + +}; +#endif diff --git a/src/server/game/Calender/CalendarHandler.cpp b/src/server/game/Calender/CalendarHandler.cpp new file mode 100644 index 00000000000..f6679c5d5ac --- /dev/null +++ b/src/server/game/Calender/CalendarHandler.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +#include "InstanceSaveMgr.h" +#include "Log.h" +#include "Opcodes.h" +#include "Player.h" + +void WorldSession::HandleCalendarGetCalendar(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_CALENDAR"); // empty + + time_t cur_time = time(NULL); + + WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR,4+4*0+4+4*0+4+4); + + data << uint32(0); // invite count + /* + for (;;) + { + uint64 inviteId; + uint64 unkGuid0; + uint8 unk1, unk2, unk3; + uint64 creatorGuid; + } + */ + + data << uint32(0); // event count + /* + for (;;) + { + uint64 eventId; + std::string title; // 128 chars + uint32 type; + uint32 occurrenceTime; + uint32 flags; + uint32 unk4; -- possibly mapid for dungeon/raid + uint64 creatorGuid; + } + */ + + data << uint32(0); // unk + data << uint32(secsToTimeBitFields(cur_time)); // current time + + uint32 counter = 0; + size_t p_counter = data.wpos(); + data << uint32(counter); // instance save count + + for (int i = 0; i < MAX_DIFFICULTY; ++i) + { + for (Player::BoundInstancesMap::const_iterator itr = _player->m_boundInstances[i].begin(); itr != _player->m_boundInstances[i].end(); ++itr) + { + if (itr->second.perm) + { + InstanceSave *save = itr->second.save; + data << uint32(save->GetMapId()); + data << uint32(save->GetDifficulty()); + data << uint32(save->GetResetTime() - cur_time); + data << uint64(save->GetInstanceId()); // instance save id as unique instance copy id + ++counter; + } + } + } + + data.put(p_counter, counter); + + data << uint32(1135753200); // unk (28.12.2005 12:00) + + counter = 0; + p_counter = data.wpos(); + data << uint32(counter); // raid reset count + + ResetTimeByMapDifficultyMap const& resets = sInstanceSaveManager.GetResetTimeMap(); + for (ResetTimeByMapDifficultyMap::const_iterator itr = resets.begin(); itr != resets.end(); ++itr) + { + uint32 mapid = PAIR32_LOPART(itr->first); + MapEntry const* mapEnt = sMapStore.LookupEntry(mapid); + if (!mapEnt || !mapEnt->IsRaid()) + continue; + + data << uint32(mapid); + data << uint32(itr->second - cur_time); + data << uint32(mapEnt->unk_time); + ++counter; + } + + data.put(p_counter, counter); + + data << uint32(0); // holiday count? + /* + for (;;) + { + uint32 unk5, unk6, unk7, unk8, unk9; + for (uint32 j = 0; j < 26; ++j) + { + uint32 unk10; + } + for (uint32 j = 0; j < 10; ++j) + { + uint32 unk11; + } + for (uint32 j = 0; j < 10; ++j) + { + uint32 unk12; + } + std::string holidayName; // 64 chars + } + */ + + sLog.outDebug("Sending calendar"); + data.hexlike(); + SendPacket(&data); +} + +void WorldSession::HandleCalendarGetEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_EVENT"); + recv_data.hexlike(); + recv_data.read_skip(); // unk +} + +void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GUILD_FILTER"); + recv_data.hexlike(); + recv_data.read_skip(); // unk1 + recv_data.read_skip(); // unk2 + recv_data.read_skip(); // unk3 +} + +void WorldSession::HandleCalendarArenaTeam(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_ARENA_TEAM"); + recv_data.hexlike(); + recv_data.read_skip(); // unk +} + +void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_ADD_EVENT"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //std::string unk1, unk2; + //recv_data >> (std::string)unk1; + //recv_data >> (std::string)unk2; + + //uint8 unk3, unk4; + //uint32 unk5, unk6, unk7, unk8, unk9, count = 0; + //recv_data >> (uint8)unk3; + //recv_data >> (uint8)unk4; + //recv_data >> (uint32)unk5; + //recv_data >> (uint32)unk6; + //recv_data >> (uint32)unk7; + //recv_data >> (uint32)unk8; + //recv_data >> (uint32)unk9; + //if (!((unk9 >> 6) & 1)) + //{ + // recv_data >> (uint32)count; + // if (count) + // { + // uint8 unk12,unk13; + // uint64 guid; + // for (int i=0; i> (uint8)unk12; + // recv_data >> (uint8)unk13; + // } + // } + //} +} + +void WorldSession::HandleCalendarUpdateEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_UPDATE_EVENT"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> std::string + //recv_data >> std::string + //recv_data >> uint8 + //recv_data >> uint8 + //recv_data >> uint32 + //recv_data >> uint32 + //recv_data >> uint32 + //recv_data >> uint32 + //recv_data >> uint32 +} + +void WorldSession::HandleCalendarRemoveEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_REMOVE_EVENT"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint32 + +} + +void WorldSession::HandleCalendarCopyEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_COPY_EVENT"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint32 + +} + +void WorldSession::HandleCalendarEventInvite(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_INVITE"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> std::string + //recv_data >> uint8 + //recv_data >> uint8 + +} + +void WorldSession::HandleCalendarEventRsvp(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_RSVP"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint32 + +} + +void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data.readPackGUID(guid) + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint64 +} + +void WorldSession::HandleCalendarEventStatus(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_STATUS"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data.readPackGUID(guid) + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint32 +} + +void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data.readPackGUID(guid) + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint32 +} + +void WorldSession::HandleCalendarComplain(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_COMPLAIN"); + recv_data.hexlike(); + recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam + + //recv_data >> uint64 + //recv_data >> uint64 + //recv_data >> uint64 +} + +void WorldSession::HandleCalendarGetNumPending(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); // empty + + WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4); + data << uint32(0); // 0 - no pending invites, 1 - some pending invites + SendPacket(&data); +} diff --git a/src/server/game/Cell.h b/src/server/game/Cell.h deleted file mode 100644 index 49e0329ace6..00000000000 --- a/src/server/game/Cell.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CELL_H -#define TRINITY_CELL_H - -#include - -#include "GameSystem/TypeContainer.h" -#include "GameSystem/TypeContainerVisitor.h" - -#include "GridDefines.h" - -class Map; -class WorldObject; - -enum District -{ - UPPER_DISTRICT = 1, - LOWER_DISTRICT = 1 << 1, - LEFT_DISTRICT = 1 << 2, - RIGHT_DISTRICT = 1 << 3, - CENTER_DISTRICT = 1 << 4, - UPPER_LEFT_DISTRICT = (UPPER_DISTRICT | LEFT_DISTRICT), - UPPER_RIGHT_DISTRICT = (UPPER_DISTRICT | RIGHT_DISTRICT), - LOWER_LEFT_DISTRICT = (LOWER_DISTRICT | LEFT_DISTRICT), - LOWER_RIGHT_DISTRICT = (LOWER_DISTRICT | RIGHT_DISTRICT), - ALL_DISTRICT = (UPPER_DISTRICT | LOWER_DISTRICT | LEFT_DISTRICT | RIGHT_DISTRICT | CENTER_DISTRICT) -}; - -struct CellArea -{ - CellArea() : right_offset(0), left_offset(0), upper_offset(0), lower_offset(0) {} - CellArea(int right, int left, int upper, int lower) : right_offset(right), left_offset(left), upper_offset(upper), lower_offset(lower) {} - bool operator!() const { return !right_offset && !left_offset && !upper_offset && !lower_offset; } - - void ResizeBorders(CellPair& begin_cell, CellPair& end_cell) const - { - begin_cell << left_offset; - begin_cell -= lower_offset; - end_cell >> right_offset; - end_cell += upper_offset; - } - - int right_offset; - int left_offset; - int upper_offset; - int lower_offset; -}; - -struct Cell -{ - Cell() { data.All = 0; } - Cell(const Cell &cell) { data.All = cell.data.All; } - explicit Cell(CellPair const& p); - - void operator|=(Cell &cell) - { - data.Part.reserved = 0; - cell.data.Part.reserved = 0; - uint32 x, y, old_x, old_y; - Compute(x, y); - cell.Compute(old_x, old_y); - - if (std::abs(int(x-old_x)) > 1 || std::abs(int(y-old_y)) > 1) - { - data.Part.reserved = ALL_DISTRICT; - cell.data.Part.reserved = ALL_DISTRICT; - return; - } - - if (x < old_x) - { - data.Part.reserved |= LEFT_DISTRICT; - cell.data.Part.reserved |= RIGHT_DISTRICT; - } - else if (old_x < x) - { - data.Part.reserved |= RIGHT_DISTRICT; - cell.data.Part.reserved |= LEFT_DISTRICT; - } - if (y < old_y) - { - data.Part.reserved |= UPPER_DISTRICT; - cell.data.Part.reserved |= LOWER_DISTRICT; - } - else if (old_y < y) - { - data.Part.reserved |= LOWER_DISTRICT; - cell.data.Part.reserved |= UPPER_DISTRICT; - } - } - - void Compute(uint32 &x, uint32 &y) const - { - x = data.Part.grid_x*MAX_NUMBER_OF_CELLS + data.Part.cell_x; - y = data.Part.grid_y*MAX_NUMBER_OF_CELLS + data.Part.cell_y; - } - - bool DiffCell(const Cell &cell) const - { - return(data.Part.cell_x != cell.data.Part.cell_x || - data.Part.cell_y != cell.data.Part.cell_y); - } - - bool DiffGrid(const Cell &cell) const - { - return(data.Part.grid_x != cell.data.Part.grid_x || - data.Part.grid_y != cell.data.Part.grid_y); - } - - uint32 CellX() const { return data.Part.cell_x; } - uint32 CellY() const { return data.Part.cell_y; } - uint32 GridX() const { return data.Part.grid_x; } - uint32 GridY() const { return data.Part.grid_y; } - bool NoCreate() const { return data.Part.nocreate; } - void SetNoCreate() { data.Part.nocreate = 1; } - - CellPair cellPair() const - { - return CellPair( - data.Part.grid_x*MAX_NUMBER_OF_CELLS+data.Part.cell_x, - data.Part.grid_y*MAX_NUMBER_OF_CELLS+data.Part.cell_y); - } - - Cell& operator=(const Cell &cell) - { - this->data.All = cell.data.All; - return *this; - } - - bool operator == (const Cell &cell) const { return (data.All == cell.data.All); } - bool operator != (const Cell &cell) const { return !operator == (cell); } - union - { - struct - { - unsigned grid_x : 6; - unsigned grid_y : 6; - unsigned cell_x : 6; - unsigned cell_y : 6; - unsigned nocreate : 1; - unsigned reserved : 7; - } Part; - uint32 All; - } data; - - template void Visit(const CellPair&, TypeContainerVisitor &visitor, Map &) const; - template void Visit(const CellPair&, TypeContainerVisitor &visitor, Map &, const WorldObject&, float) const; - template void Visit(const CellPair&, TypeContainerVisitor &visitor, Map &, float, float, float) const; - - static CellArea CalculateCellArea(const WorldObject &obj, float radius); - static CellArea CalculateCellArea(float x, float y, float radius); - -private: - template void VisitCircle(TypeContainerVisitor &, Map &, const CellPair&, const CellPair&) const; -}; - -#endif - diff --git a/src/server/game/CellImpl.h b/src/server/game/CellImpl.h deleted file mode 100644 index d906e81a5c9..00000000000 --- a/src/server/game/CellImpl.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CELLIMPL_H -#define TRINITY_CELLIMPL_H - -#include - -#include "Cell.h" -#include "Map.h" -#include "Object.h" - -inline Cell::Cell(CellPair const& p) -{ - data.Part.grid_x = p.x_coord / MAX_NUMBER_OF_CELLS; - data.Part.grid_y = p.y_coord / MAX_NUMBER_OF_CELLS; - data.Part.cell_x = p.x_coord % MAX_NUMBER_OF_CELLS; - data.Part.cell_y = p.y_coord % MAX_NUMBER_OF_CELLS; - data.Part.nocreate = 0; - data.Part.reserved = 0; -} - -template -inline void -Cell::Visit(const CellPair& standing_cell, TypeContainerVisitor &visitor, Map &m) const -{ - if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) - return; - - uint16 district = (District)this->data.Part.reserved; - if (district == CENTER_DISTRICT) - { - m.Visit(*this, visitor); - return; - } - - // set up the cell range based on the district - // the overloaded operators handle range checking - CellPair begin_cell = standing_cell; - CellPair end_cell = standing_cell; - - switch(district) - { - case ALL_DISTRICT: - { - begin_cell << 1; begin_cell -= 1; // upper left - end_cell >> 1; end_cell += 1; // lower right - break; - } - case UPPER_LEFT_DISTRICT: - { - begin_cell << 1; begin_cell -= 1; // upper left - break; - } - case UPPER_RIGHT_DISTRICT: - { - begin_cell -= 1; // up - end_cell >> 1; // right - break; - } - case LOWER_LEFT_DISTRICT: - { - begin_cell << 1; // left - end_cell += 1; // down - break; - } - case LOWER_RIGHT_DISTRICT: - { - end_cell >> 1; end_cell += 1; // lower right - break; - } - case LEFT_DISTRICT: - { - begin_cell -= 1; // up - end_cell >> 1; end_cell += 1; // lower right - break; - } - case RIGHT_DISTRICT: - { - begin_cell << 1; begin_cell -= 1; // upper left - end_cell += 1; // down - break; - } - case UPPER_DISTRICT: - { - begin_cell << 1; begin_cell -= 1; // upper left - end_cell >> 1; // right - break; - } - case LOWER_DISTRICT: - { - begin_cell << 1; // left - end_cell >> 1; end_cell += 1; // lower right - break; - } - default: - { - assert(false); - break; - } - } - - // loop the cell range - for (uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++) - { - for (uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; y++) - { - CellPair cell_pair(x,y); - Cell r_zone(cell_pair); - r_zone.data.Part.nocreate = this->data.Part.nocreate; - m.Visit(r_zone, visitor); - } - } -} - -inline int CellHelper(const float radius) -{ - if (radius < 1.0f) - return 0; - - return (int)ceilf(radius/SIZE_OF_GRID_CELL); -} - -inline CellArea Cell::CalculateCellArea(const WorldObject &obj, float radius) -{ - return Cell::CalculateCellArea(obj.GetPositionX(), obj.GetPositionY(), radius); -} - -inline CellArea Cell::CalculateCellArea(float x, float y, float radius) -{ - if (radius <= 0.0f) - return CellArea(); - - //lets calculate object coord offsets from cell borders. - //TODO: add more correct/generic method for this task - const float x_offset = (x - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL; - const float y_offset = (y - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL; - - const float x_val = floor(x_offset + CENTER_GRID_CELL_ID + 0.5f); - const float y_val = floor(y_offset + CENTER_GRID_CELL_ID + 0.5f); - - const float x_off = (x_offset - x_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL; - const float y_off = (y_offset - y_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL; - - const float tmp_diff = radius - CENTER_GRID_CELL_OFFSET; - //lets calculate upper/lower/right/left corners for cell search - int right = CellHelper(tmp_diff + x_off); - int left = CellHelper(tmp_diff - x_off); - int upper = CellHelper(tmp_diff + y_off); - int lower = CellHelper(tmp_diff - y_off); - - return CellArea(right, left, upper, lower); -} - -template -inline void -Cell::Visit(const CellPair& standing_cell, TypeContainerVisitor &visitor, Map &m, float radius, float x_off, float y_off) const -{ - if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) - return; - - //no jokes here... Actually placing ASSERT() here was good idea, but - //we had some problems with DynamicObjects, which pass radius = 0.0f (DB issue?) - //maybe it is better to just return when radius <= 0.0f? - if (radius <= 0.0f) - { - m.Visit(*this, visitor); - return; - } - //lets limit the upper value for search radius - if (radius > 333.0f) - radius = 333.0f; - - //lets calculate object coord offsets from cell borders. - CellArea area = Cell::CalculateCellArea(x_off, y_off, radius); - //if radius fits inside standing cell - if (!area) - { - m.Visit(*this, visitor); - return; - } - - CellPair begin_cell = standing_cell; - CellPair end_cell = standing_cell; - - area.ResizeBorders(begin_cell, end_cell); - //visit all cells, found in CalculateCellArea() - //if radius is known to reach cell area more than 4x4 then we should call optimized VisitCircle - //currently this technique works with MAX_NUMBER_OF_CELLS 16 and higher, with lower values - //there are nothing to optimize because SIZE_OF_GRID_CELL is too big... - if (((end_cell.x_coord - begin_cell.x_coord) > 4) && ((end_cell.y_coord - begin_cell.y_coord) > 4)) - { - VisitCircle(visitor, m, begin_cell, end_cell); - return; - } - - //ALWAYS visit standing cell first!!! Since we deal with small radiuses - //it is very essential to call visitor for standing cell firstly... - m.Visit(*this, visitor); - - // loop the cell range - for (uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; ++x) - { - for (uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; ++y) - { - CellPair cell_pair(x,y); - //lets skip standing cell since we already visited it - if (cell_pair != standing_cell) - { - Cell r_zone(cell_pair); - r_zone.data.Part.nocreate = this->data.Part.nocreate; - m.Visit(r_zone, visitor); - } - } - } -} - -template -inline void -Cell::Visit(const CellPair& l, TypeContainerVisitor &visitor, Map &m, const WorldObject &obj, float radius) const -{ - //we should increase search radius by object's radius, otherwise - //we could have problems with huge creatures, which won't attack nearest players etc - Visit(l, visitor, m, radius + obj.GetObjectSize(), obj.GetPositionX(), obj.GetPositionY()); -} - -template -inline void -Cell::VisitCircle(TypeContainerVisitor &visitor, Map &m, const CellPair& begin_cell, const CellPair& end_cell) const -{ - //here is an algorithm for 'filling' circum-squared octagon - uint32 x_shift = (uint32)ceilf((end_cell.x_coord - begin_cell.x_coord) * 0.3f - 0.5f); - //lets calculate x_start/x_end coords for central strip... - const uint32 x_start = begin_cell.x_coord + x_shift; - const uint32 x_end = end_cell.x_coord - x_shift; - - //visit central strip with constant width... - for (uint32 x = x_start; x <= x_end; ++x) - { - for (uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; ++y) - { - CellPair cell_pair(x,y); - Cell r_zone(cell_pair); - r_zone.data.Part.nocreate = this->data.Part.nocreate; - m.Visit(r_zone, visitor); - } - } - - //if x_shift == 0 then we have too small cell area, which were already - //visited at previous step, so just return from procedure... - if (x_shift == 0) - return; - - uint32 y_start = end_cell.y_coord; - uint32 y_end = begin_cell.y_coord; - //now we are visiting borders of an octagon... - for (uint32 step = 1; step <= (x_start - begin_cell.x_coord); ++step) - { - //each step reduces strip height by 2 cells... - y_end += 1; - y_start -= 1; - for (uint32 y = y_start; y >= y_end; --y) - { - //we visit cells symmetrically from both sides, heading from center to sides and from up to bottom - //e.g. filling 2 trapezoids after filling central cell strip... - CellPair cell_pair_left(x_start - step, y); - Cell r_zone_left(cell_pair_left); - r_zone_left.data.Part.nocreate = this->data.Part.nocreate; - m.Visit(r_zone_left, visitor); - - //right trapezoid cell visit - CellPair cell_pair_right(x_end + step, y); - Cell r_zone_right(cell_pair_right); - r_zone_right.data.Part.nocreate = this->data.Part.nocreate; - m.Visit(r_zone_right, visitor); - } - } -} -#endif - diff --git a/src/server/game/Channel.cpp b/src/server/game/Channel.cpp deleted file mode 100644 index 0047892972b..00000000000 --- a/src/server/game/Channel.cpp +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Channel.h" -#include "Chat.h" -#include "ObjectMgr.h" -#include "SocialMgr.h" -#include "World.h" - -Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team) - : m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0), m_Team(Team) -{ - // set special flags if built-in channel - ChatChannelsEntry const* ch = GetChannelEntryFor(channel_id); - if (ch) // it's built-in channel - { - channel_id = ch->ChannelID; // built-in channel - m_announce = false; // no join/leave announces - - m_flags |= CHANNEL_FLAG_GENERAL; // for all built-in channels - - if (ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel - m_flags |= CHANNEL_FLAG_TRADE; - - if (ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels - m_flags |= CHANNEL_FLAG_CITY; - - if (ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel - m_flags |= CHANNEL_FLAG_LFG; - else // for all other channels - m_flags |= CHANNEL_FLAG_NOT_LFG; - m_IsSaved = false; - } - else // it's custom channel - { - m_flags |= CHANNEL_FLAG_CUSTOM; - //load not built in channel if saved - std::string _name(name); - CharacterDatabase.escape_string(_name); - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT m_announce, m_moderate, m_public, m_password, BannedList FROM channels WHERE m_name = '%s' AND m_team = '%u'", _name.c_str(), m_Team); - if (result)//load - { - Field *fields = result->Fetch(); - m_announce = fields[0].GetBool(); - m_moderate = fields[1].GetBool(); - m_public = fields[2].GetBool(); - m_password = fields[3].GetString(); - const char* db_BannedList = fields[4].GetString(); - - m_IsSaved = true; - - if (db_BannedList) - { - Tokens tokens = StrSplit(db_BannedList, " "); - Tokens::iterator iter; - for (iter = tokens.begin(); iter != tokens.end(); ++iter) - { - uint64 banned_guid = atol((*iter).c_str()); - if (banned_guid) - { - sLog.outDebug("Channel(%s) loaded banned guid: %u",name.c_str(), banned_guid); - banned.insert(banned_guid); - } - } - } - } - else // save - { - // _name is already escaped at this point. - if (CharacterDatabase.PExecute("INSERT INTO channels (m_name, m_team, m_announce, m_moderate, m_public, m_password) " - "VALUES ('%s', '%u', '1', '0', '1', '')", _name.c_str(), m_Team)) - { - sLog.outDebug("New Channel(%s) saved", name.c_str()); - m_IsSaved = true; - } - } - } -} - -bool Channel::_UpdateStringInDB(const std::string& colName, const std::string& colValue) const -{ - // Prevent SQL-injection - std::string _name(m_name); - std::string _colValue(colValue); - CharacterDatabase.escape_string(_colValue); - CharacterDatabase.escape_string(_name); - return CharacterDatabase.PExecute("UPDATE channels SET %s = '%s' WHERE m_name = '%s' AND m_team = '%u'", - colName.c_str(), _colValue.c_str(), _name.c_str(), m_Team); -} - -bool Channel::_UpdateIntInDB(const std::string& colName, int colValue) const -{ - // Prevent SQL-injection - std::string _name(m_name); - CharacterDatabase.escape_string(_name); - return CharacterDatabase.PExecute("UPDATE channels SET %s = '%u' WHERE m_name = '%s' AND m_team = '%u'", - colName.c_str(), colValue, _name.c_str(), m_Team); -} - -void Channel::_UpdateBanListInDB() const -{ - // save banlist - if (m_IsSaved) - { - std::ostringstream banlist; - BannedList::const_iterator iter; - for (iter = banned.begin(); iter != banned.end(); ++iter) - banlist << (*iter) << " "; - std::string banListStr = banlist.str(); - if (_UpdateStringInDB("BannedList", banListStr)) - sLog.outDebug("Channel(%s) BannedList saved", m_name.c_str()); - } -} - -void Channel::Join(uint64 p, const char *pass) -{ - WorldPacket data; - if (IsOn(p)) - { - if (!IsConstant()) // non send error message for built-in channels - { - MakePlayerAlreadyMember(&data, p); - SendToOne(&data, p); - } - return; - } - - if (IsBanned(p)) - { - MakeBanned(&data); - SendToOne(&data, p); - return; - } - - if (m_password.length() > 0 && strcmp(pass, m_password.c_str())) - { - MakeWrongPassword(&data); - SendToOne(&data, p); - return; - } - - Player *plr = objmgr.GetPlayer(p); - - if (plr) - { - if (HasFlag(CHANNEL_FLAG_LFG) && - sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER && plr->GetGroup()) - { - MakeNotInLfg(&data); - SendToOne(&data, p); - return; - } - - if (plr->GetGuildId() && (GetFlags() == 0x38)) - return; - - plr->JoinedChannel(this); - } - - if (m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL))) - { - MakeJoined(&data, p); - SendToAll(&data); - } - - data.clear(); - - PlayerInfo pinfo; - pinfo.player = p; - pinfo.flags = MEMBER_FLAG_NONE; - players[p] = pinfo; - - MakeYouJoined(&data); - SendToOne(&data, p); - - JoinNotify(p); - - // if no owner first logged will become - if (!IsConstant() && !m_ownerGUID) - { - SetOwner(p, (players.size() > 1 ? true : false)); - players[p].SetModerator(true); - } - /* - else if (!IsConstant() && m_ownerGUID && plr && m_ownerGUID == plr->GetGUID())) - { - SetOwner(p, (players.size() > 1 ? true : false)); - players[p].SetModerator(true); - }*/ -} - -void Channel::Leave(uint64 p, bool send) -{ - if (!IsOn(p)) - { - if (send) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - } - else - { - Player *plr = objmgr.GetPlayer(p); - - if (send) - { - WorldPacket data; - MakeYouLeft(&data); - SendToOne(&data, p); - if (plr) - plr->LeftChannel(this); - data.clear(); - } - - bool changeowner = players[p].IsOwner(); - - players.erase(p); - if (m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL))) - { - WorldPacket data; - MakeLeft(&data, p); - SendToAll(&data); - } - - LeaveNotify(p); - - if (changeowner) - { - uint64 newowner = !players.empty() ? players.begin()->second.player : 0; - players[newowner].SetModerator(true); - SetOwner(newowner); - } - } -} - -void Channel::KickOrBan(uint64 good, const char *badname, bool ban) -{ - AccountTypes sec = SEC_PLAYER; - Player *gplr = objmgr.GetPlayer(good); - if (gplr) - sec = gplr->GetSession()->GetSecurity(); - - if (!IsOn(good)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, good); - } - else if (!players[good].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, good); - } - else - { - Player *bad = objmgr.GetPlayer(badname); - if (bad == NULL || !IsOn(bad->GetGUID())) - { - WorldPacket data; - MakePlayerNotFound(&data, badname); - SendToOne(&data, good); - } - else if (sec < SEC_GAMEMASTER && bad->GetGUID() == m_ownerGUID && good != m_ownerGUID) - { - WorldPacket data; - MakeNotOwner(&data); - SendToOne(&data, good); - } - else - { - bool changeowner = (m_ownerGUID == bad->GetGUID()); - - WorldPacket data; - - if (ban && !IsBanned(bad->GetGUID())) - { - banned.insert(bad->GetGUID()); - MakePlayerBanned(&data, bad->GetGUID(), good); - _UpdateBanListInDB(); - - } - else - MakePlayerKicked(&data, bad->GetGUID(), good); - - SendToAll(&data); - players.erase(bad->GetGUID()); - bad->LeftChannel(this); - - if (changeowner) - { - uint64 newowner = !players.empty() ? good : false; - players[newowner].SetModerator(true); - SetOwner(newowner); - } - } - } -} - -void Channel::UnBan(uint64 good, const char *badname) -{ - uint32 sec = 0; - Player *gplr = objmgr.GetPlayer(good); - if (gplr) - sec = gplr->GetSession()->GetSecurity(); - - if (!IsOn(good)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, good); - } - else if (!players[good].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, good); - } - else - { - Player *bad = objmgr.GetPlayer(badname); - if (bad == NULL || !IsBanned(bad->GetGUID())) - { - WorldPacket data; - MakePlayerNotFound(&data, badname); - SendToOne(&data, good); - } - else - { - banned.erase(bad->GetGUID()); - - WorldPacket data; - MakePlayerUnbanned(&data, bad->GetGUID(), good); - SendToAll(&data); - //save banlist - _UpdateBanListInDB(); - } - } -} - -void Channel::Password(uint64 p, const char *pass) -{ - std::string plName; - uint32 sec = 0; - Player *plr = objmgr.GetPlayer(p); - if (plr) - sec = plr->GetSession()->GetSecurity(); - - ChatHandler chat(plr); - - if (!m_public && sec <= SEC_MODERATOR) - { - chat.PSendSysMessage(LANG_CHANNEL_NOT_PUBLIC); - return; - } - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, p); - } - else - { - m_password = pass; - - WorldPacket data; - MakePasswordChanged(&data, p); - SendToAll(&data); - if (m_IsSaved && _UpdateStringInDB("m_password", m_password)) - sLog.outDebug("Channel(%s) password saved", m_name.c_str()); - } -} - -void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set) -{ - Player *plr = objmgr.GetPlayer(p); - if (!plr) - return; - - uint32 sec = plr->GetSession()->GetSecurity(); - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, p); - } - else - { - Player *newp = objmgr.GetPlayer(p2n); - if (!newp) - { - WorldPacket data; - MakePlayerNotFound(&data, p2n); - SendToOne(&data, p); - return; - } - - if (p == m_ownerGUID && newp->GetGUID() == m_ownerGUID && mod) - return; - - if (!IsOn(newp->GetGUID())) - { - WorldPacket data; - MakePlayerNotFound(&data, p2n); - SendToOne(&data, p); - return; - } - - // allow make moderator from another team only if both is GMs - // at this moment this only way to show channel post for GM from another team - if ((plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || newp->GetSession()->GetSecurity() < SEC_GAMEMASTER) && - plr->GetTeam() != newp->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) - { - WorldPacket data; - MakePlayerNotFound(&data, p2n); - SendToOne(&data, p); - return; - } - - if (m_ownerGUID == newp->GetGUID() && m_ownerGUID != p) - { - WorldPacket data; - MakeNotOwner(&data); - SendToOne(&data, p); - return; - } - - if (mod) - SetModerator(newp->GetGUID(), set); - else - SetMute(newp->GetGUID(), set); - } -} - -void Channel::SetOwner(uint64 p, const char *newname) -{ - Player *plr = objmgr.GetPlayer(p); - if (!plr) - return; - - uint32 sec = plr->GetSession()->GetSecurity(); - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - return; - } - - if (sec < SEC_GAMEMASTER && p != m_ownerGUID) - { - WorldPacket data; - MakeNotOwner(&data); - SendToOne(&data, p); - return; - } - - Player *newp = objmgr.GetPlayer(newname); - if (newp == NULL || !IsOn(newp->GetGUID())) - { - WorldPacket data; - MakePlayerNotFound(&data, newname); - SendToOne(&data, p); - return; - } - - if (newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) - { - WorldPacket data; - MakePlayerNotFound(&data, newname); - SendToOne(&data, p); - return; - } - - players[newp->GetGUID()].SetModerator(true); - SetOwner(newp->GetGUID()); -} - -void Channel::SendWhoOwner(uint64 p) -{ - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else - { - WorldPacket data; - MakeChannelOwner(&data); - SendToOne(&data, p); - } -} - -void Channel::List(Player* player) -{ - uint64 p = player->GetGUID(); - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else - { - WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+players.size()*(8+1)); - data << uint8(1); // channel type? - data << GetName(); // channel name - data << uint8(GetFlags()); // channel flags? - - size_t pos = data.wpos(); - data << uint32(0); // size of list, placeholder - - uint32 gmLevelInWhoList = sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); - - uint32 count = 0; - for (PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) - { - Player *plr = objmgr.GetPlayer(i->first); - - // PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters - // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if (plr && (player->GetSession()->GetSecurity() > SEC_PLAYER || plr->GetSession()->GetSecurity() <= gmLevelInWhoList) && - plr->IsVisibleGloballyFor(player)) - { - data << uint64(i->first); - data << uint8(i->second.flags); // flags seems to be changed... - ++count; - } - } - - data.put(pos,count); - - SendToOne(&data, p); - } -} - -void Channel::Announce(uint64 p) -{ - uint32 sec = 0; - Player *plr = objmgr.GetPlayer(p); - if (plr) - sec = plr->GetSession()->GetSecurity(); - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, p); - } - else - { - m_announce = !m_announce; - - WorldPacket data; - if (m_announce) - MakeAnnouncementsOn(&data, p); - else - MakeAnnouncementsOff(&data, p); - SendToAll(&data); - if (m_IsSaved && _UpdateIntInDB("m_announce", m_announce ? 1 : 0)) - sLog.outDebug("Channel(%s) announce saved", m_name.c_str()); - - } -} - -void Channel::Moderate(uint64 p) -{ - uint32 sec = 0; - Player *plr = objmgr.GetPlayer(p); - if (plr) - sec = plr->GetSession()->GetSecurity(); - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, p); - } - else - { - m_moderate = !m_moderate; - - WorldPacket data; - if (m_moderate) - MakeModerationOn(&data, p); - else - MakeModerationOff(&data, p); - SendToAll(&data); - if (m_IsSaved && _UpdateIntInDB("m_announce", m_announce ? 1 : 0)) - sLog.outDebug("Channel(%s) announce saved", m_name.c_str()); - } -} - -void Channel::Say(uint64 p, const char *what, uint32 lang) -{ - if (!what) - return; - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) - lang = LANG_UNIVERSAL; - - uint32 sec = 0; - Player *plr = objmgr.GetPlayer(p); - if (plr) - sec = plr->GetSession()->GetSecurity(); - - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - } - else if (players[p].IsMuted()) - { - WorldPacket data; - MakeMuted(&data); - SendToOne(&data, p); - } - else if (m_moderate && !players[p].IsModerator() && sec < SEC_GAMEMASTER) - { - WorldPacket data; - MakeNotModerator(&data); - SendToOne(&data, p); - } - else - { - uint32 messageLength = strlen(what) + 1; - - WorldPacket data(SMSG_MESSAGECHAT, 1+4+8+4+m_name.size()+1+8+4+messageLength+1); - data << (uint8)CHAT_MSG_CHANNEL; - data << (uint32)lang; - data << p; // 2.1.0 - data << uint32(0); // 2.1.0 - data << m_name; - data << p; - data << messageLength; - data << what; - data << uint8(plr ? plr->chatTag() : 0); - - SendToAll(&data, !players[p].IsModerator() ? p : false); - } -} - -void Channel::Invite(uint64 p, const char *newname) -{ - if (!IsOn(p)) - { - WorldPacket data; - MakeNotMember(&data); - SendToOne(&data, p); - return; - } - - Player *newp = objmgr.GetPlayer(newname); - if (!newp) - { - WorldPacket data; - MakePlayerNotFound(&data, newname); - SendToOne(&data, p); - return; - } - - Player *plr = objmgr.GetPlayer(p); - if (!plr) - return; - - if (newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) - { - WorldPacket data; - MakeInviteWrongFaction(&data); - SendToOne(&data, p); - return; - } - - if (IsOn(newp->GetGUID())) - { - WorldPacket data; - MakePlayerAlreadyMember(&data, newp->GetGUID()); - SendToOne(&data, p); - return; - } - - WorldPacket data; - if (!newp->GetSocial()->HasIgnore(GUID_LOPART(p))) - { - MakeInvite(&data, p); - SendToOne(&data, newp->GetGUID()); - data.clear(); - } - MakePlayerInvited(&data, newp->GetName()); - SendToOne(&data, p); -} - -void Channel::SetOwner(uint64 guid, bool exclaim) -{ - if (m_ownerGUID) - { - // [] will re-add player after it possible removed - PlayerList::iterator p_itr = players.find(m_ownerGUID); - if (p_itr != players.end()) - p_itr->second.SetOwner(false); - } - - m_ownerGUID = guid; - if (m_ownerGUID) - { - uint8 oldFlag = GetPlayerFlags(m_ownerGUID); - players[m_ownerGUID].SetModerator(true); - players[m_ownerGUID].SetOwner(true); - - WorldPacket data; - MakeModeChange(&data, m_ownerGUID, oldFlag); - SendToAll(&data); - - if (exclaim) - { - MakeOwnerChanged(&data, m_ownerGUID); - SendToAll(&data); - } - if (m_IsSaved && _UpdateIntInDB("m_moderate", m_moderate ? 1 : 0)) - sLog.outDebug("Channel(%s) moderate saved", m_name.c_str()); - - } -} - -void Channel::SendToAll(WorldPacket *data, uint64 p) -{ - for (PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) - { - Player *plr = objmgr.GetPlayer(i->first); - if (plr) - { - if (!p || !plr->GetSocial()->HasIgnore(GUID_LOPART(p))) - plr->GetSession()->SendPacket(data); - } - } -} - -void Channel::SendToAllButOne(WorldPacket *data, uint64 who) -{ - for (PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) - { - if (i->first != who) - { - Player *plr = objmgr.GetPlayer(i->first); - if (plr) - plr->GetSession()->SendPacket(data); - } - } -} - -void Channel::SendToOne(WorldPacket *data, uint64 who) -{ - Player *plr = objmgr.GetPlayer(who); - if (plr) - plr->GetSession()->SendPacket(data); -} - -void Channel::Voice(uint64 /*guid1*/, uint64 /*guid2*/) -{ - -} - -void Channel::DeVoice(uint64 /*guid1*/, uint64 /*guid2*/) -{ - -} - -// done -void Channel::MakeNotifyPacket(WorldPacket *data, uint8 notify_type) -{ - data->Initialize(SMSG_CHANNEL_NOTIFY, 1+m_name.size()+1); - *data << uint8(notify_type); - *data << m_name; -} - -// done 0x00 -void Channel::MakeJoined(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_JOINED_NOTICE); - *data << uint64(guid); -} - -// done 0x01 -void Channel::MakeLeft(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_LEFT_NOTICE); - *data << uint64(guid); -} - -// done 0x02 -void Channel::MakeYouJoined(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE); - *data << uint8(GetFlags()); - *data << uint32(GetChannelId()); - *data << uint32(0); -} - -// done 0x03 -void Channel::MakeYouLeft(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE); - *data << uint32(GetChannelId()); - *data << uint8(0); // can be 0x00 and 0x01 -} - -// done 0x04 -void Channel::MakeWrongPassword(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE); -} - -// done 0x05 -void Channel::MakeNotMember(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE); -} - -// done 0x06 -void Channel::MakeNotModerator(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE); -} - -// done 0x07 -void Channel::MakePasswordChanged(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE); - *data << uint64(guid); -} - -// done 0x08 -void Channel::MakeOwnerChanged(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE); - *data << uint64(guid); -} - -// done 0x09 -void Channel::MakePlayerNotFound(WorldPacket *data, const std::string& name) -{ - MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE); - *data << name; -} - -// done 0x0A -void Channel::MakeNotOwner(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE); -} - -// done 0x0B -void Channel::MakeChannelOwner(WorldPacket *data) -{ - std::string name = ""; - - if (!objmgr.GetPlayerNameByGUID(m_ownerGUID, name) || name.empty()) - name = "PLAYER_NOT_FOUND"; - - MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE); - *data << ((IsConstant() || !m_ownerGUID) ? "Nobody" : name); -} - -// done 0x0C -void Channel::MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags) -{ - MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE); - *data << uint64(guid); - *data << uint8(oldflags); - *data << uint8(GetPlayerFlags(guid)); -} - -// done 0x0D -void Channel::MakeAnnouncementsOn(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE); - *data << uint64(guid); -} - -// done 0x0E -void Channel::MakeAnnouncementsOff(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE); - *data << uint64(guid); -} - -// done 0x0F -void Channel::MakeModerationOn(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_MODERATION_ON_NOTICE); - *data << uint64(guid); -} - -// done 0x10 -void Channel::MakeModerationOff(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_MODERATION_OFF_NOTICE); - *data << uint64(guid); -} - -// done 0x11 -void Channel::MakeMuted(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_MUTED_NOTICE); -} - -// done 0x12 -void Channel::MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good) -{ - MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE); - *data << uint64(bad); - *data << uint64(good); -} - -// done 0x13 -void Channel::MakeBanned(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_BANNED_NOTICE); -} - -// done 0x14 -void Channel::MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good) -{ - MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE); - *data << uint64(bad); - *data << uint64(good); -} - -// done 0x15 -void Channel::MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good) -{ - MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE); - *data << uint64(bad); - *data << uint64(good); -} - -// done 0x16 -void Channel::MakePlayerNotBanned(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE); - *data << uint64(guid); -} - -// done 0x17 -void Channel::MakePlayerAlreadyMember(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE); - *data << uint64(guid); -} - -// done 0x18 -void Channel::MakeInvite(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_INVITE_NOTICE); - *data << uint64(guid); -} - -// done 0x19 -void Channel::MakeInviteWrongFaction(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE); -} - -// done 0x1A -void Channel::MakeWrongFaction(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE); -} - -// done 0x1B -void Channel::MakeInvalidName(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE); -} - -// done 0x1C -void Channel::MakeNotModerated(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE); -} - -// done 0x1D -void Channel::MakePlayerInvited(WorldPacket *data, const std::string& name) -{ - MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE); - *data << name; -} - -// done 0x1E -void Channel::MakePlayerInviteBanned(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE); - *data << uint64(guid); -} - -// done 0x1F -void Channel::MakeThrottled(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE); -} - -// done 0x20 -void Channel::MakeNotInArea(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE); -} - -// done 0x21 -void Channel::MakeNotInLfg(WorldPacket *data) -{ - MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE); -} - -// done 0x22 -void Channel::MakeVoiceOn(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE); - *data << uint64(guid); -} - -// done 0x23 -void Channel::MakeVoiceOff(WorldPacket *data, uint64 guid) -{ - MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE); - *data << uint64(guid); -} - -void Channel::JoinNotify(uint64 guid) -{ - WorldPacket data; - - if (IsConstant()) - data.Initialize(SMSG_USERLIST_ADD, 8+1+1+4+GetName().size()+1); - else - data.Initialize(SMSG_USERLIST_UPDATE, 8+1+1+4+GetName().size()+1); - - data << uint64(guid); - data << uint8(GetPlayerFlags(guid)); - data << uint8(GetFlags()); - data << uint32(GetNumPlayers()); - data << GetName(); - SendToAll(&data); -} - -void Channel::LeaveNotify(uint64 guid) -{ - WorldPacket data(SMSG_USERLIST_REMOVE, 8+1+4+GetName().size()+1); - data << uint64(guid); - data << uint8(GetFlags()); - data << uint32(GetNumPlayers()); - data << GetName(); - SendToAll(&data); -} - diff --git a/src/server/game/Channel.h b/src/server/game/Channel.h deleted file mode 100644 index d0b5923e30e..00000000000 --- a/src/server/game/Channel.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _CHANNEL_H -#define _CHANNEL_H - -#include -#include -#include - -#include "Common.h" - -#include "Opcodes.h" -#include "Player.h" -#include "WorldPacket.h" - -enum ChatNotify -{ - CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel."; - CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel."; - //CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel."; - CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined - //CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]"; - CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left - CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s."; - CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s."; - CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s."; - CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s."; - CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s."; - CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found."; - CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner."; - CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s."; - CHAT_MODE_CHANGE_NOTICE = 0x0C, //? - CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s."; - CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s."; - CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s."; - CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s."; - CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak."; - CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s."; - CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel."; - CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s."; - CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s."; - CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned."; - CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel."; - CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'."; - CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s."; - CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s."; - CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name"; - CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated"; - CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel"; - CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned."; - CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message."; - CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone. - CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels. - CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s."; - CHAT_VOICE_OFF_NOTICE = 0x23, //+ "[%s] Channel voice disabled by %s."; -}; - -enum ChannelFlags -{ - CHANNEL_FLAG_NONE = 0x00, - CHANNEL_FLAG_CUSTOM = 0x01, - // 0x02 - CHANNEL_FLAG_TRADE = 0x04, - CHANNEL_FLAG_NOT_LFG = 0x08, - CHANNEL_FLAG_GENERAL = 0x10, - CHANNEL_FLAG_CITY = 0x20, - CHANNEL_FLAG_LFG = 0x40, - CHANNEL_FLAG_VOICE = 0x80 - // General 0x18 = 0x10 | 0x08 - // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04 - // LocalDefence 0x18 = 0x10 | 0x08 - // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08 - // LookingForGroup 0x50 = 0x40 | 0x10 -}; - -enum ChannelDBCFlags -{ - CHANNEL_DBC_FLAG_NONE = 0x00000, - CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG - CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment - CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense - CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade - CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment - CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment - CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense - CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment - CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup -}; - -enum ChannelMemberFlags -{ - MEMBER_FLAG_NONE = 0x00, - MEMBER_FLAG_OWNER = 0x01, - MEMBER_FLAG_MODERATOR = 0x02, - MEMBER_FLAG_VOICED = 0x04, - MEMBER_FLAG_MUTED = 0x08, - MEMBER_FLAG_CUSTOM = 0x10, - MEMBER_FLAG_MIC_MUTED = 0x20, - // 0x40 - // 0x80 -}; - -class Channel -{ - struct PlayerInfo - { - uint64 player; - uint8 flags; - - bool HasFlag(uint8 flag) { return flags & flag; } - void SetFlag(uint8 flag) { if (!HasFlag(flag)) flags |= flag; } - bool IsOwner() { return flags & MEMBER_FLAG_OWNER; } - void SetOwner(bool state) - { - if (state) flags |= MEMBER_FLAG_OWNER; - else flags &= ~MEMBER_FLAG_OWNER; - } - bool IsModerator() { return flags & MEMBER_FLAG_MODERATOR; } - void SetModerator(bool state) - { - if (state) flags |= MEMBER_FLAG_MODERATOR; - else flags &= ~MEMBER_FLAG_MODERATOR; - } - bool IsMuted() { return flags & MEMBER_FLAG_MUTED; } - void SetMuted(bool state) - { - if (state) flags |= MEMBER_FLAG_MUTED; - else flags &= ~MEMBER_FLAG_MUTED; - } - }; - - typedef std::map PlayerList; - PlayerList players; - typedef std::set BannedList; - BannedList banned; - bool m_announce; - bool m_moderate; - bool m_public; - std::string m_name; - std::string m_password; - uint8 m_flags; - uint32 m_channelId; - uint64 m_ownerGUID; - bool m_IsSaved; - - private: - // initial packet data (notify type and channel name) - void MakeNotifyPacket(WorldPacket *data, uint8 notify_type); - // type specific packet data - void MakeJoined(WorldPacket *data, uint64 guid); //+ 0x00 - void MakeLeft(WorldPacket *data, uint64 guid); //+ 0x01 - void MakeYouJoined(WorldPacket *data); //+ 0x02 - void MakeYouLeft(WorldPacket *data); //+ 0x03 - void MakeWrongPassword(WorldPacket *data); //? 0x04 - void MakeNotMember(WorldPacket *data); //? 0x05 - void MakeNotModerator(WorldPacket *data); //? 0x06 - void MakePasswordChanged(WorldPacket *data, uint64 guid); //+ 0x07 - void MakeOwnerChanged(WorldPacket *data, uint64 guid); //? 0x08 - void MakePlayerNotFound(WorldPacket *data, const std::string& name); //+ 0x09 - void MakeNotOwner(WorldPacket *data); //? 0x0A - void MakeChannelOwner(WorldPacket *data); //? 0x0B - void MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags); //+ 0x0C - void MakeAnnouncementsOn(WorldPacket *data, uint64 guid); //+ 0x0D - void MakeAnnouncementsOff(WorldPacket *data, uint64 guid); //+ 0x0E - void MakeModerationOn(WorldPacket *data, uint64 guid); //+ 0x0F - void MakeModerationOff(WorldPacket *data, uint64 guid); //+ 0x10 - void MakeMuted(WorldPacket *data); //? 0x11 - void MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good); //? 0x12 - void MakeBanned(WorldPacket *data); //? 0x13 - void MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x14 - void MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x15 - void MakePlayerNotBanned(WorldPacket *data, uint64 guid); //? 0x16 - void MakePlayerAlreadyMember(WorldPacket *data, uint64 guid); //+ 0x17 - void MakeInvite(WorldPacket *data, uint64 guid); //? 0x18 - void MakeInviteWrongFaction(WorldPacket *data); //? 0x19 - void MakeWrongFaction(WorldPacket *data); //? 0x1A - void MakeInvalidName(WorldPacket *data); //? 0x1B - void MakeNotModerated(WorldPacket *data); //? 0x1C - void MakePlayerInvited(WorldPacket *data, const std::string& name); //+ 0x1D - void MakePlayerInviteBanned(WorldPacket *data, uint64 guid); //? 0x1E - void MakeThrottled(WorldPacket *data); //? 0x1F - void MakeNotInArea(WorldPacket *data); //? 0x20 - void MakeNotInLfg(WorldPacket *data); //? 0x21 - void MakeVoiceOn(WorldPacket *data, uint64 guid); //+ 0x22 - void MakeVoiceOff(WorldPacket *data, uint64 guid); //+ 0x23 - - void SendToAll(WorldPacket *data, uint64 p = 0); - void SendToAllButOne(WorldPacket *data, uint64 who); - void SendToOne(WorldPacket *data, uint64 who); - - bool IsOn(uint64 who) const { return players.find(who) != players.end(); } - bool IsBanned(uint64 guid) const { return banned.find(guid) != banned.end(); } - - bool _UpdateStringInDB(const std::string& colName, const std::string& colValue) const; - bool _UpdateIntInDB(const std::string& colName, int colValue) const; - void _UpdateBanListInDB() const; - - uint8 GetPlayerFlags(uint64 p) const - { - PlayerList::const_iterator p_itr = players.find(p); - if (p_itr == players.end()) - return 0; - - return p_itr->second.flags; - } - - void SetModerator(uint64 p, bool set) - { - if (players[p].IsModerator() != set) - { - uint8 oldFlag = GetPlayerFlags(p); - players[p].SetModerator(set); - - WorldPacket data; - MakeModeChange(&data, p, oldFlag); - SendToAll(&data); - } - } - - void SetMute(uint64 p, bool set) - { - if (players[p].IsMuted() != set) - { - uint8 oldFlag = GetPlayerFlags(p); - players[p].SetMuted(set); - - WorldPacket data; - MakeModeChange(&data, p, oldFlag); - SendToAll(&data); - } - } - - public: - uint32 m_Team; - Channel(const std::string& name, uint32 channel_id, uint32 Team = 0); - std::string GetName() const { return m_name; } - uint32 GetChannelId() const { return m_channelId; } - bool IsConstant() const { return m_channelId != 0; } - bool IsAnnounce() const { return m_announce; } - bool IsLFG() const { return GetFlags() & CHANNEL_FLAG_LFG; } - std::string GetPassword() const { return m_password; } - void SetPassword(const std::string& npassword) { m_password = npassword; } - void SetAnnounce(bool nannounce) { m_announce = nannounce; } - uint32 GetNumPlayers() const { return players.size(); } - uint8 GetFlags() const { return m_flags; } - bool HasFlag(uint8 flag) { return m_flags & flag; } - - void Join(uint64 p, const char *pass); - void Leave(uint64 p, bool send = true); - void KickOrBan(uint64 good, const char *badname, bool ban); - void Kick(uint64 good, const char *badname) { KickOrBan(good, badname, false); } - void Ban(uint64 good, const char *badname) { KickOrBan(good, badname, true); } - void UnBan(uint64 good, const char *badname); - void Password(uint64 p, const char *pass); - void SetMode(uint64 p, const char *p2n, bool mod, bool set); - void SetOwner(uint64 p, bool exclaim = true); - void SetOwner(uint64 p, const char *newname); - void SendWhoOwner(uint64 p); - void SetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, true); } - void UnsetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, false); } - void SetMute(uint64 p, const char *newname) { SetMode(p, newname, false, true); } - void UnsetMute(uint64 p, const char *newname) { SetMode(p, newname, false, false); } - void List(Player* p); - void Announce(uint64 p); - void Moderate(uint64 p); - void Say(uint64 p, const char *what, uint32 lang); - void Invite(uint64 p, const char *newp); - void Voice(uint64 guid1, uint64 guid2); - void DeVoice(uint64 guid1, uint64 guid2); - void JoinNotify(uint64 guid); // invisible notify - void LeaveNotify(uint64 guid); // invisible notify -}; -#endif - diff --git a/src/server/game/ChannelHandler.cpp b/src/server/game/ChannelHandler.cpp deleted file mode 100644 index 0f615579cb6..00000000000 --- a/src/server/game/ChannelHandler.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Policies/SingletonImp.h" - -#include "ObjectMgr.h" // for normalizePlayerName -#include "ChannelMgr.h" - -void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - - uint32 channel_id; - uint8 unknown1, unknown2; - std::string channelname, pass; - - recvPacket >> channel_id >> unknown1 >> unknown2; - recvPacket >> channelname; - - if (channelname.empty()) - return; - - recvPacket >> pass; - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - { - cMgr->team = _player->GetTeam(); - if (Channel *chn = cMgr->GetJoinChannel(channelname, channel_id)) - chn->Join(_player->GetGUID(), pass.c_str()); - } -} - -void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - - uint32 unk; - std::string channelname; - recvPacket >> unk; // channel id? - recvPacket >> channelname; - - if (channelname.empty()) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - { - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Leave(_player->GetGUID(), true); - cMgr->LeftChannel(channelname); - } -} - -void WorldSession::HandleChannelList(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->List(_player); -} - -void WorldSession::HandleChannelPassword(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, pass; - recvPacket >> channelname; - - recvPacket >> pass; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Password(_player->GetGUID(), pass.c_str()); -} - -void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, newp; - recvPacket >> channelname; - - recvPacket >> newp; - - if (!normalizePlayerName(newp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->SetOwner(_player->GetGUID(), newp.c_str()); -} - -void WorldSession::HandleChannelOwner(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->SendWhoOwner(_player->GetGUID()); -} - -void WorldSession::HandleChannelModerator(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->SetModerator(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->UnsetModerator(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelMute(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->SetMute(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->UnsetMute(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelInvite(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Invite(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelKick(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Kick(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelBan(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Ban(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelUnban(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - - std::string channelname, otp; - recvPacket >> channelname; - - recvPacket >> otp; - - if (!normalizePlayerName(otp)) - return; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->UnBan(_player->GetGUID(), otp.c_str()); -} - -void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Announce(_player->GetGUID()); -} - -void WorldSession::HandleChannelModerate(WorldPacket& recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->Moderate(_player->GetGUID()); -} - -void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->List(_player); -} - -void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - { - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - { - WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, chn->GetName().size()+1+1+4); - data << chn->GetName(); - data << uint8(chn->GetFlags()); - data << uint32(chn->GetNumPlayers()); - SendPacket(&data); - } - } -} - -void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); - //recvPacket.hexlike(); - std::string channelname; - recvPacket >> channelname; - /*if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - if (Channel *chn = cMgr->GetChannel(channelname, _player)) - chn->JoinNotify(_player->GetGUID());*/ -} - diff --git a/src/server/game/ChannelMgr.cpp b/src/server/game/ChannelMgr.cpp deleted file mode 100644 index f31d3ffde50..00000000000 --- a/src/server/game/ChannelMgr.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ChannelMgr.h" -#include "Policies/SingletonImp.h" -#include "World.h" - -INSTANTIATE_SINGLETON_1(AllianceChannelMgr); -INSTANTIATE_SINGLETON_1(HordeChannelMgr); - -ChannelMgr* channelMgr(uint32 team) -{ - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) - return &Trinity::Singleton::Instance(); // cross-faction - - if (team == ALLIANCE) - return &Trinity::Singleton::Instance(); - if (team == HORDE) - return &Trinity::Singleton::Instance(); - - return NULL; -} - -ChannelMgr::~ChannelMgr() -{ - for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr) - delete itr->second; - - channels.clear(); -} - -Channel *ChannelMgr::GetJoinChannel(std::string name, uint32 channel_id) -{ - std::wstring wname; - Utf8toWStr(name,wname); - wstrToLower(wname); - - if (channels.find(wname) == channels.end()) - { - Channel *nchan = new Channel(name,channel_id, team); - channels[wname] = nchan; - return nchan; - } - - return channels[wname]; -} - -Channel *ChannelMgr::GetChannel(std::string name, Player *p, bool pkt) -{ - std::wstring wname; - Utf8toWStr(name,wname); - wstrToLower(wname); - - ChannelMap::const_iterator i = channels.find(wname); - - if (i == channels.end()) - { - if (pkt) - { - WorldPacket data; - MakeNotOnPacket(&data,name); - p->GetSession()->SendPacket(&data); - } - - return NULL; - } - else - return i->second; -} - -void ChannelMgr::LeftChannel(std::string name) -{ - std::wstring wname; - Utf8toWStr(name,wname); - wstrToLower(wname); - - ChannelMap::const_iterator i = channels.find(wname); - - if (i == channels.end()) - return; - - Channel* channel = i->second; - - if (channel->GetNumPlayers() == 0 && !channel->IsConstant()) - { - channels.erase(wname); - delete channel; - } -} - -void ChannelMgr::MakeNotOnPacket(WorldPacket *data, std::string name) -{ - data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size - (*data) << (uint8)0x05 << name; -} diff --git a/src/server/game/ChannelMgr.h b/src/server/game/ChannelMgr.h deleted file mode 100644 index 6f3b7c415ae..00000000000 --- a/src/server/game/ChannelMgr.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __TRINITY_CHANNELMGR_H -#define __TRINITY_CHANNELMGR_H - -#include "Common.h" -#include "Channel.h" -#include "Policies/Singleton.h" - -#include -#include - -#include "Policies/Singleton.h" - -#include "Channel.h" -#include "World.h" - -class ChannelMgr -{ - public: - uint32 team; - typedef std::map ChannelMap; - ChannelMgr() {team = 0;} - ~ChannelMgr(); - - Channel *GetJoinChannel(std::string name, uint32 channel_id); - Channel *GetChannel(std::string name, Player *p, bool pkt = true); - void LeftChannel(std::string name); - private: - ChannelMap channels; - void MakeNotOnPacket(WorldPacket *data, std::string name); -}; - -class AllianceChannelMgr : public ChannelMgr {}; -class HordeChannelMgr : public ChannelMgr {}; - -ChannelMgr* channelMgr(uint32 team); - -#endif - diff --git a/src/server/game/CharacterHandler.cpp b/src/server/game/CharacterHandler.cpp deleted file mode 100644 index 416827d73ea..00000000000 --- a/src/server/game/CharacterHandler.cpp +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectAccessor.h" -#include "ObjectMgr.h" -#include "SystemConfig.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Auth/md5.h" -#include "Database/DatabaseEnv.h" -#include "Database/DatabaseImpl.h" - -#include "ArenaTeam.h" -#include "Chat.h" -#include "Group.h" -#include "Guild.h" -#include "Language.h" -#include "Log.h" -#include "Opcodes.h" -#include "Player.h" -#include "PlayerDump.h" -#include "SharedDefines.h" -#include "SocialMgr.h" -#include "UpdateMask.h" -#include "Util.h" -#include "ScriptMgr.h" - -class LoginQueryHolder : public SqlQueryHolder -{ - private: - uint32 m_accountId; - uint64 m_guid; - public: - LoginQueryHolder(uint32 accountId, uint64 guid) - : m_accountId(accountId), m_guid(guid) { } - uint64 GetGuid() const { return m_guid; } - uint32 GetAccountId() const { return m_accountId; } - bool Initialize(); -}; - -bool LoginQueryHolder::Initialize() -{ - SetSize(MAX_PLAYER_LOGIN_QUERY); - - bool res = true; - - // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. - // !!! NOTE: including unused `zone`,`online` - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags," - "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost," - "resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty," - "arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk," - "health, power1, power2, power3, power4, power5, power6, power7, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT guid FROM group_member WHERE memberGuid =%u", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_mask,recalculate_mask,stackcount,amount0,amount1,amount2,base_amount0,base_amount1,base_amount2,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADWEKLYQUESTSTATUS,"SELECT quest FROM character_queststatus_weekly WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT a.button,a.action,a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activespec AND a.guid = '%u' ORDER BY button", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" UI64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid)); - if (sWorld.getConfig(CONFIG_DECLINED_NAMES_USED)) - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid)); - // in other case still be dummy query - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADARENAINFO, "SELECT arenateamid, played_week, played_season, wons_season, personal_rating FROM arena_team_member WHERE guid='%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBGDATA, "SELECT instance_id, team, join_x, join_y, join_z, join_o, join_map, taxi_start, taxi_end, mount_spell FROM character_battleground_data WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid='%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, "SELECT type, time, data FROM character_account_data WHERE guid='%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid)); - res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADRANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = '%u'", GUID_LOPART(m_guid)); - - return res; -} - -// don't call WorldSession directly -// it may get deleted before the query callbacks get executed -// instead pass an account id to this handler -class CharacterHandler -{ - - public: - void HandleCharEnumCallback(QueryResult_AutoPtr result, uint32 account) - { - WorldSession * session = sWorld.FindSession(account); - if (!session) - return; - session->HandleCharEnum(result); - } - void HandlePlayerLoginCallback(QueryResult_AutoPtr /*dummy*/, SqlQueryHolder * holder) - { - if (!holder) return; - WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId()); - if (!session) - { - delete holder; - return; - } - session->HandlePlayerLogin((LoginQueryHolder*)holder); - } -} chrHandler; - -void WorldSession::HandleCharEnum(QueryResult_AutoPtr result) -{ - WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size - - uint8 num = 0; - - data << num; - - if (result) - { - do - { - uint32 guidlow = (*result)[0].GetUInt32(); - sLog.outDetail("Loading char guid %u from account %u.",guidlow,GetAccountId()); - if (Player::BuildEnumData(result, &data)) - ++num; - } - while (result->NextRow()); - } - - data.put(0, num); - - SendPacket(&data); -} - -void WorldSession::HandleCharEnumOpcode(WorldPacket & /*recv_data*/) -{ - /// get all the data necessary for loading all characters (along with their pets) on the account - CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(), - !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? - // ------- Query Without Declined Names -------- - // 0 1 2 3 4 5 6 7 - "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, " - // 8 9 10 11 12 13 14 - "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, " - // 15 16 17 18 19 - "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache " - "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' " - "LEFT JOIN guild_member ON characters.guid = guild_member.guid " - "WHERE characters.account = '%u' ORDER BY characters.guid" - : - // --------- Query With Declined Names --------- - // 0 1 2 3 4 5 6 7 - "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, " - // 8 9 10 11 12 13 14 - "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, " - // 15 16 17 18 19 20 - "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, character_declinedname.genitive " - "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' " - "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid " - "LEFT JOIN guild_member ON characters.guid = guild_member.guid " - "WHERE characters.account = '%u' ORDER BY characters.guid", - PET_SAVE_AS_CURRENT,GetAccountId()); -} - -void WorldSession::HandleCharCreateOpcode(WorldPacket & recv_data) -{ - std::string name; - uint8 race_,class_; - - recv_data >> name; - - recv_data >> race_; - recv_data >> class_; - - WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases - - if (GetSecurity() == SEC_PLAYER) - { - if (uint32 mask = sWorld.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED)) - { - bool disabled = false; - - uint32 team = Player::TeamForRace(race_); - switch(team) - { - case ALLIANCE: disabled = mask & (1<<0); break; - case HORDE: disabled = mask & (1<<1); break; - } - - if (disabled) - { - data << (uint8)CHAR_CREATE_DISABLED; - SendPacket(&data); - return; - } - } - } - - ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_); - ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_); - - if (!classEntry || !raceEntry) - { - data << (uint8)CHAR_CREATE_FAILED; - SendPacket(&data); - sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_); - return; - } - - // prevent character creating Expansion race without Expansion account - if (raceEntry->expansion > Expansion()) - { - data << (uint8)CHAR_CREATE_EXPANSION; - sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u race (%u)",Expansion(),GetAccountId(),raceEntry->expansion,race_); - SendPacket(&data); - return; - } - - // prevent character creating Expansion class without Expansion account - if (classEntry->expansion > Expansion()) - { - data << (uint8)CHAR_CREATE_EXPANSION_CLASS; - sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u class (%u)",Expansion(),GetAccountId(),classEntry->expansion,class_); - SendPacket(&data); - return; - } - - // prevent character creating with invalid name - if (!normalizePlayerName(name)) - { - data << (uint8)CHAR_NAME_NO_NAME; - SendPacket(&data); - sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId()); - return; - } - - // check name limitations - uint8 res = ObjectMgr::CheckPlayerName(name,true); - if (res != CHAR_NAME_SUCCESS) - { - data << uint8(res); - SendPacket(&data); - return; - } - - if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) - { - data << (uint8)CHAR_NAME_RESERVED; - SendPacket(&data); - return; - } - - if (objmgr.GetPlayerGUIDByName(name)) - { - data << (uint8)CHAR_CREATE_NAME_IN_USE; - SendPacket(&data); - return; - } - - QueryResult_AutoPtr resultacct = LoginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); - if (resultacct) - { - Field *fields=resultacct->Fetch(); - uint32 acctcharcount = fields[0].GetUInt32(); - - if (acctcharcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) - { - data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT; - SendPacket(&data); - return; - } - } - - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId()); - uint8 charcount = 0; - if (result) - { - Field *fields=result->Fetch(); - charcount = fields[0].GetUInt8(); - - if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM)) - { - data << (uint8)CHAR_CREATE_SERVER_LIMIT; - SendPacket(&data); - return; - } - } - - // speedup check for heroic class disabled case - uint32 heroic_free_slots = sWorld.getConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); - if (heroic_free_slots == 0 && GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) - { - data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; - SendPacket(&data); - return; - } - - // speedup check for heroic class disabled case - uint32 req_level_for_heroic = sWorld.getConfig(CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING); - if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; - SendPacket(&data); - return; - } - - bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; - uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS); - - bool have_same_race = false; - - // if 0 then allowed creating without any characters - bool have_req_level_for_heroic = (req_level_for_heroic == 0); - - if (!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) - { - QueryResult_AutoPtr result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s", - GetAccountId(), (skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1"); - if (result2) - { - uint32 team_= Player::TeamForRace(race_); - - Field* field = result2->Fetch(); - uint8 acc_race = field[1].GetUInt32(); - - if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) - { - uint8 acc_class = field[2].GetUInt32(); - if (acc_class == CLASS_DEATH_KNIGHT) - { - if (heroic_free_slots > 0) - --heroic_free_slots; - - if (heroic_free_slots == 0) - { - data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; - SendPacket(&data); - return; - } - } - - if (!have_req_level_for_heroic) - { - uint32 acc_level = field[0].GetUInt32(); - if (acc_level >= req_level_for_heroic) - have_req_level_for_heroic = true; - } - } - - // need to check team only for first character - // TODO: what to if account already has characters of both races? - if (!AllowTwoSideAccounts) - { - uint32 acc_team=0; - if (acc_race > 0) - acc_team = Player::TeamForRace(acc_race); - - if (acc_team != team_) - { - data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; - SendPacket(&data); - return; - } - } - - // search same race for cinematic or same class if need - // TODO: check if cinematic already shown? (already logged in?; cinematic field) - while ((skipCinematics == 1 && !have_same_race) || class_ == CLASS_DEATH_KNIGHT) - { - if (!result2->NextRow()) - break; - - field = result2->Fetch(); - acc_race = field[1].GetUInt32(); - - if (!have_same_race) - have_same_race = race_ == acc_race; - - if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) - { - uint8 acc_class = field[2].GetUInt32(); - if (acc_class == CLASS_DEATH_KNIGHT) - { - if (heroic_free_slots > 0) - --heroic_free_slots; - - if (heroic_free_slots == 0) - { - data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; - SendPacket(&data); - return; - } - } - - if (!have_req_level_for_heroic) - { - uint32 acc_level = field[0].GetUInt32(); - if (acc_level >= req_level_for_heroic) - have_req_level_for_heroic = true; - } - } - } - } - } - - if (GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic) - { - data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; - SendPacket(&data); - return; - } - - // extract other data required for player creating - uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; - recv_data >> gender >> skin >> face; - recv_data >> hairStyle >> hairColor >> facialHair >> outfitId; - - if (recv_data.rpos() < recv_data.wpos()) - { - uint8 unk; - recv_data >> unk; - sLog.outDebug("Character creation %s (account %u) has unhandled tail data: [%u]", name.c_str(), GetAccountId(), unk); - } - - Player * pNewChar = new Player(this); - if (!pNewChar->Create(objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId)) - { - // Player not create (race/class problem?) - pNewChar->CleanupsBeforeDelete(); - delete pNewChar; - - data << (uint8)CHAR_CREATE_ERROR; - SendPacket(&data); - - return; - } - - if ((have_same_race && skipCinematics == 1) || skipCinematics == 2) - pNewChar->setCinematic(1); // not show intro - - pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login - - // Player created, save it now - pNewChar->SaveToDB(); - charcount+=1; - - LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); - LoginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); - - pNewChar->CleanupsBeforeDelete(); - - data << (uint8)CHAR_CREATE_SUCCESS; - SendPacket(&data); - - std::string IP_str = GetRemoteAddress(); - sLog.outBasic("Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), pNewChar->GetGUIDLow()); - sLog.outChar("Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), pNewChar->GetGUIDLow()); - delete pNewChar; // created only to call SaveToDB() - -} - -void WorldSession::HandleCharDeleteOpcode(WorldPacket & recv_data) -{ - uint64 guid; - recv_data >> guid; - - // can't delete loaded character - if (objmgr.GetPlayer(guid)) - return; - - uint32 accountId = 0; - std::string name; - - // is guild leader - if (objmgr.GetGuildByLeader(guid)) - { - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER; - SendPacket(&data); - return; - } - - // is arena team captain - if (objmgr.GetArenaTeamByCaptain(guid)) - { - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN; - SendPacket(&data); - return; - } - - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid)); - if (result) - { - Field *fields = result->Fetch(); - accountId = fields[0].GetUInt32(); - name = fields[1].GetCppString(); - } - - // prevent deleting other players' characters using cheating tools - if (accountId != GetAccountId()) - return; - - std::string IP_str = GetRemoteAddress(); - sLog.outDetail("Account: %d (IP: %s) Delete Character:[%s] (GUID: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); - sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (GUID: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); - - if (sLog.IsOutCharDump()) // optimize GetPlayerDump call - { - std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid)); - sLog.outCharDump(dump.c_str(),GetAccountId(),GUID_LOPART(guid),name.c_str()); - } - - Player::DeleteFromDB(guid, GetAccountId()); - - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_SUCCESS; - SendPacket(&data); -} - -void WorldSession::HandlePlayerLoginOpcode(WorldPacket & recv_data) -{ - if (PlayerLoading() || GetPlayer() != NULL) - { - sLog.outError("Player tryes to login again, AccountId = %d",GetAccountId()); - return; - } - - m_playerLoading = true; - uint64 playerGuid = 0; - - DEBUG_LOG("WORLD: Recvd Player Logon Message"); - - recv_data >> playerGuid; - - LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid); - if (!holder->Initialize()) - { - delete holder; // delete all unprocessed queries - m_playerLoading = false; - return; - } - - CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder); -} - -void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) -{ - uint64 playerGuid = holder->GetGuid(); - - Player* pCurrChar = new Player(this); - // for send server info and strings (config) - ChatHandler chH = ChatHandler(pCurrChar); - - // "GetAccountId() == db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools) - if (!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder)) - { - KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick - delete pCurrChar; // delete it manually - delete holder; // delete all unprocessed queries - m_playerLoading = false; - return; - } - - pCurrChar->GetMotionMaster()->Initialize(); - - SetPlayer(pCurrChar); - - pCurrChar->SendDungeonDifficulty(false); - - WorldPacket data(SMSG_LOGIN_VERIFY_WORLD, 20); - data << pCurrChar->GetMapId(); - data << pCurrChar->GetPositionX(); - data << pCurrChar->GetPositionY(); - data << pCurrChar->GetPositionZ(); - data << pCurrChar->GetOrientation(); - SendPacket(&data); - - // load player specific part before send times - LoadAccountData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA),PER_CHARACTER_CACHE_MASK); - SendAccountDataTimes(PER_CHARACTER_CACHE_MASK); - - data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0 - data << uint8(2); // unknown value - data << uint8(0); // enable(1)/disable(0) voice chat interface in client - SendPacket(&data); - - // Send MOTD - { - data.Initialize(SMSG_MOTD, 50); // new in 2.0.1 - data << (uint32)0; - - uint32 linecount=0; - std::string str_motd = sWorld.GetMotd(); - std::string::size_type pos, nextpos; - - pos = 0; - while ((nextpos= str_motd.find('@',pos)) != std::string::npos) - { - if (nextpos != pos) - { - data << str_motd.substr(pos,nextpos-pos); - ++linecount; - } - pos = nextpos+1; - } - - if (posGetGUIDLow()); - QueryResult_AutoPtr resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD); - - if (resultGuild) - { - Field *fields = resultGuild->Fetch(); - pCurrChar->SetInGuild(fields[0].GetUInt32()); - pCurrChar->SetRank(fields[1].GetUInt32()); - } - else if (pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership - { - pCurrChar->SetInGuild(0); - pCurrChar->SetRank(0); - } - - if (pCurrChar->GetGuildId() != 0) - { - Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId()); - if (guild) - { - data.Initialize(SMSG_GUILD_EVENT, (1+1+guild->GetMOTD().size()+1)); - data << uint8(GE_MOTD); - data << uint8(1); - data << guild->GetMOTD(); - SendPacket(&data); - DEBUG_LOG("WORLD: Sent guild-motd (SMSG_GUILD_EVENT)"); - - guild->DisplayGuildBankTabsInfo(this); - - guild->BroadcastEvent(GE_SIGNED_ON, pCurrChar->GetGUID(), 1, pCurrChar->GetName(), "", ""); - } - else - { - // remove wrong guild data - sLog.outError("Player %s (GUID: %u) marked as member not existed guild (id: %u), removing guild membership for player.",pCurrChar->GetName(),pCurrChar->GetGUIDLow(),pCurrChar->GetGuildId()); - pCurrChar->SetInGuild(0); - } - } - - data.Initialize(SMSG_LEARNED_DANCE_MOVES, 4+4); - data << uint32(0); - data << uint32(0); - SendPacket(&data); - - pCurrChar->SendInitialPacketsBeforeAddToMap(); - - //Show cinematic at the first time that player login - if (!pCurrChar->getCinematic()) - { - pCurrChar->setCinematic(1); - - if (ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass())) - { - if (cEntry->CinematicSequence) - pCurrChar->SendCinematicStart(cEntry->CinematicSequence); - else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace())) - pCurrChar->SendCinematicStart(rEntry->CinematicSequence); - - // send new char string if not empty - if (!sWorld.GetNewCharString().empty()) - chH.PSendSysMessage(sWorld.GetNewCharString().c_str()); - } - } - - if (!pCurrChar->GetMap()->Add(pCurrChar) || !pCurrChar->CheckInstanceLoginValid()) - { - AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId()); - if (at) - pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation()); - else - pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation()); - } - - ObjectAccessor::Instance().AddObject(pCurrChar); - //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName()); - - pCurrChar->SendInitialPacketsAfterAddToMap(); - - CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow()); - LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); - pCurrChar->SetInGameTime(getMSTime()); - - // announce group about member online (must be after add to player list to receive announce to self) - if (Group *group = pCurrChar->GetGroup()) - { - //pCurrChar->groupInfo.group->SendInit(this); // useless - group->SendUpdate(); - group->ResetMaxEnchantingLevel(); - } - - // friend status - sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), true); - - // Place character in world (and load zone) before some object loading - pCurrChar->LoadCorpse(); - - // setting Ghost+speed if dead - if (pCurrChar->m_deathState != ALIVE) - { - // not blizz like, we must correctly save and load player instead... - if (pCurrChar->getRace() == RACE_NIGHTELF) - pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) - pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) - - pCurrChar->SetMovement(MOVE_WATER_WALK); - } - - pCurrChar->ContinueTaxiFlight(); - - // reset for all pets before pet loading - if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) - Pet::resetTalentsForAllPetsOf(pCurrChar); - - // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned) - pCurrChar->LoadPet(); - - // Set FFA PvP for non GM in non-rest mode - if (sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING)) - pCurrChar->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); - - if (pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) - pCurrChar->SetContestedPvP(); - - // Apply at_login requests - if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS)) - { - pCurrChar->resetSpells(); - SendNotification(LANG_RESET_SPELLS); - } - - if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) - { - pCurrChar->resetTalents(true); - pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state - SendNotification(LANG_RESET_TALENTS); - } - - if (pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST)) - pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST); - - // show time before shutdown if shutdown planned. - if (sWorld.IsShutdowning()) - sWorld.ShutdownMsg(true,pCurrChar); - - if (sWorld.getConfig(CONFIG_ALL_TAXI_PATHS)) - pCurrChar->SetTaxiCheater(true); - - if (pCurrChar->isGameMaster()) - SendNotification(LANG_GM_ON); - - std::string IP_str = GetRemoteAddress(); - sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (GUID: %u)", - GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUIDLow()); - - if (!pCurrChar->IsStandState() && !pCurrChar->hasUnitState(UNIT_STAT_STUNNED)) - pCurrChar->SetStandState(UNIT_STAND_STATE_STAND); - - m_playerLoading = false; - - //Hook for OnLogin Event - sScriptMgr.OnLogin(pCurrChar); - delete holder; -} - -void WorldSession::HandleSetFactionAtWar(WorldPacket & recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_ATWAR"); - - uint32 repListID; - uint8 flag; - - recv_data >> repListID; - recv_data >> flag; - - GetPlayer()->GetReputationMgr().SetAtWar(repListID,flag); -} - -//I think this function is never used :/ I dunno, but i guess this opcode not exists -void WorldSession::HandleSetFactionCheat(WorldPacket & /*recv_data*/) -{ - sLog.outError("WORLD SESSION: HandleSetFactionCheat, not expected call, please report."); - /* - uint32 FactionID; - uint32 Standing; - - recv_data >> FactionID; - recv_data >> Standing; - - std::list::iterator itr; - - for (itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr) - { - if (itr->ReputationListID == FactionID) - { - itr->Standing += Standing; - itr->Flags = (itr->Flags | 1); - break; - } - } - */ - GetPlayer()->GetReputationMgr().SendStates(); -} - -void WorldSession::HandleMeetingStoneInfo(WorldPacket & /*recv_data*/) -{ - DEBUG_LOG("WORLD: Received CMSG_MEETING_STONE_INFO"); - - //SendLfgUpdate(0, 0, 0); -} - -void WorldSession::HandleTutorialFlag(WorldPacket & recv_data) -{ - uint32 iFlag; - recv_data >> iFlag; - - uint32 wInt = (iFlag / 32); - if (wInt >= 8) - { - //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID()); - return; - } - uint32 rInt = (iFlag % 32); - - uint32 tutflag = GetTutorialInt(wInt); - tutflag |= (1 << rInt); - SetTutorialInt(wInt, tutflag); - - //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag); -} - -void WorldSession::HandleTutorialClear(WorldPacket & /*recv_data*/) -{ - for (int i = 0; i < 8; ++i) - SetTutorialInt(i, 0xFFFFFFFF); -} - -void WorldSession::HandleTutorialReset(WorldPacket & /*recv_data*/) -{ - for (int i = 0; i < 8; ++i) - SetTutorialInt(i, 0x00000000); -} - -void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket & recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION"); - uint32 fact; - recv_data >> fact; - GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact); -} - -void WorldSession::HandleSetFactionInactiveOpcode(WorldPacket & recv_data) -{ - DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE"); - uint32 replistid; - uint8 inactive; - recv_data >> replistid >> inactive; - - _player->GetReputationMgr().SetInactive(replistid, inactive); -} - -void WorldSession::HandleShowingHelmOpcode(WorldPacket & /*recv_data*/) -{ - DEBUG_LOG("CMSG_SHOWING_HELM for %s", _player->GetName()); - _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM); -} - -void WorldSession::HandleShowingCloakOpcode(WorldPacket & /*recv_data*/) -{ - DEBUG_LOG("CMSG_SHOWING_CLOAK for %s", _player->GetName()); - _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK); -} - -void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) -{ - uint64 guid; - std::string newname; - - recv_data >> guid; - recv_data >> newname; - - // prevent character rename to invalid name - if (!normalizePlayerName(newname)) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_NAME_NO_NAME); - SendPacket(&data); - return; - } - - uint8 res = ObjectMgr::CheckPlayerName(newname,true); - if (res != CHAR_NAME_SUCCESS) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(res); - SendPacket(&data); - return; - } - - // check name limitations - if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_NAME_RESERVED); - SendPacket(&data); - return; - } - - std::string escaped_newname = newname; - CharacterDatabase.escape_string(escaped_newname); - - // make sure that the character belongs to the current account, that rename at login is enabled - // and that there is no character with the desired new name - CharacterDatabase.AsyncPQuery(&WorldSession::HandleChangePlayerNameOpcodeCallBack, - GetAccountId(), newname, - "SELECT guid, name FROM characters WHERE guid = %d AND account = %d AND (at_login & %d) = %d AND NOT EXISTS (SELECT NULL FROM characters WHERE name = '%s')", - GUID_LOPART(guid), GetAccountId(), AT_LOGIN_RENAME, AT_LOGIN_RENAME, escaped_newname.c_str() -); -} - -void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult_AutoPtr result, uint32 accountId, std::string newname) -{ - WorldSession * session = sWorld.FindSession(accountId); - if (!session) - return; - - if (!result) - { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_CREATE_ERROR); - session->SendPacket(&data); - return; - } - - uint32 guidLow = result->Fetch()[0].GetUInt32(); - uint64 guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER); - std::string oldname = result->Fetch()[1].GetCppString(); - - CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME), guidLow); - CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow); - - sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s", session->GetAccountId(), session->GetRemoteAddress().c_str(), oldname.c_str(), guidLow, newname.c_str()); - - WorldPacket data(SMSG_CHAR_RENAME, 1+8+(newname.size()+1)); - data << uint8(RESPONSE_SUCCESS); - data << uint64(guid); - data << newname; - session->SendPacket(&data); -} - -void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) -{ - uint64 guid; - - recv_data >> guid; - - // not accept declined names for unsupported languages - std::string name; - if (!objmgr.GetPlayerNameByGUID(guid, name)) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(1); - data << uint64(guid); - SendPacket(&data); - return; - } - - std::wstring wname; - if (!Utf8toWStr(name, wname)) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(1); - data << uint64(guid); - SendPacket(&data); - return; - } - - if (!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(1); - data << uint64(guid); - SendPacket(&data); - return; - } - - std::string name2; - DeclinedName declinedname; - - recv_data >> name2; - - if (name2 != name) // character have different name - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(1); - data << uint64(guid); - SendPacket(&data); - return; - } - - for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - { - recv_data >> declinedname.name[i]; - if (!normalizePlayerName(declinedname.name[i])) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(1); - data << uint64(guid); - SendPacket(&data); - return; - } - } - - if (!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname, 0), declinedname)) - { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(1); - data << uint64(guid); - SendPacket(&data); - return; - } - - for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - CharacterDatabase.escape_string(declinedname.name[i]); - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid)); - CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')", - GUID_LOPART(guid), declinedname.name[0].c_str(), declinedname.name[1].c_str(), declinedname.name[2].c_str(), declinedname.name[3].c_str(), declinedname.name[4].c_str()); - CharacterDatabase.CommitTransaction(); - - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); - data << uint32(0); // OK - data << uint64(guid); - SendPacket(&data); -} - -void WorldSession::HandleAlterAppearance(WorldPacket & recv_data) -{ - sLog.outDebug("CMSG_ALTER_APPEARANCE"); - - uint32 Hair, Color, FacialHair, SkinColor; - recv_data >> Hair >> Color >> FacialHair >> SkinColor; - - BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair); - - if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->getGender()) - return; - - BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair); - - if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->getGender()) - return; - - BarberShopStyleEntry const* bs_skinColor = sBarberShopStyleStore.LookupEntry(SkinColor); - - if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->getGender())) - return; - - uint32 Cost = _player->GetBarberShopCost(bs_hair->hair_id, Color, bs_facialHair->hair_id, bs_skinColor); - - // 0 - ok - // 1,3 - not enough money - // 2 - you have to seat on barber chair - if (_player->GetMoney() < Cost) - { - WorldPacket data(SMSG_BARBER_SHOP_RESULT, 4); - data << uint32(1); // no money - SendPacket(&data); - return; - } - else - { - WorldPacket data(SMSG_BARBER_SHOP_RESULT, 4); - data << uint32(0); // ok - SendPacket(&data); - } - - _player->ModifyMoney(-int32(Cost)); // it isn't free - _player->GetAchievementMgr().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)); - if (bs_skinColor) - _player->SetByteValue(PLAYER_BYTES, 0, uint8(bs_skinColor->hair_id)); - - _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1); - - _player->SetStandState(0); // stand up -} - -void WorldSession::HandleRemoveGlyph(WorldPacket & recv_data) -{ - uint32 slot; - recv_data >> slot; - - if (slot >= MAX_GLYPH_SLOT_INDEX) - { - sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot); - return; - } - - if (uint32 glyph = _player->GetGlyph(slot)) - { - if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph)) - { - _player->RemoveAurasDueToSpell(gp->SpellId); - _player->SetGlyph(slot, 0); - _player->SendTalentsInfoData(false); - } - } -} - -void WorldSession::HandleCharCustomize(WorldPacket& recv_data) -{ - uint64 guid; - std::string newname; - - recv_data >> guid; - recv_data >> newname; - - uint8 gender, skin, face, hairStyle, hairColor, facialHair; - recv_data >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face; - - QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid)); - if (!result) - { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); - return; - } - - Field *fields = result->Fetch(); - uint32 at_loginFlags = fields[0].GetUInt32(); - - if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE)) - { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); - return; - } - - // prevent character rename to invalid name - if (!normalizePlayerName(newname)) - { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_NAME_NO_NAME); - SendPacket(&data); - return; - } - - uint8 res = ObjectMgr::CheckPlayerName(newname,true); - if (res != CHAR_NAME_SUCCESS) - { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(res); - SendPacket(&data); - return; - } - - // check name limitations - if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) - { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_NAME_RESERVED); - SendPacket(&data); - return; - } - - // character with this name already exist - if (uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) - { - if (newguid != guid) - { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_NAME_IN_USE); - SendPacket(&data); - return; - } - } - - CharacterDatabase.escape_string(newname); - if (QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid ='%u'", GUID_LOPART(guid))) - { - std::string oldname = result->Fetch()[0].GetCppString(); - std::string IP_str = GetRemoteAddress(); - sLog.outChar("Account: %d (IP: %s), Character[%s] (guid:%u) Customized to: %s", GetAccountId(), IP_str.c_str(), oldname.c_str(), GUID_LOPART(guid), newname.c_str()); - } - Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair); - CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_CUSTOMIZE), GUID_LOPART(guid)); - CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid)); - - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1+8+(newname.size()+1)+6); - data << uint8(RESPONSE_SUCCESS); - data << uint64(guid); - data << newname; - data << uint8(gender); - data << uint8(skin); - data << uint8(face); - data << uint8(hairStyle); - data << uint8(hairColor); - data << uint8(facialHair); - SendPacket(&data); -} - -void WorldSession::HandleEquipmentSetSave(WorldPacket &recv_data) -{ - sLog.outDebug("CMSG_EQUIPMENT_SET_SAVE"); - - uint64 setGuid; - if (!recv_data.readPackGUID(setGuid)) - return; - - uint32 index; - recv_data >> index; - if (index >= MAX_EQUIPMENT_SET_INDEX) // client set slots amount - return; - - std::string name; - recv_data >> name; - - std::string iconName; - recv_data >> iconName; - - EquipmentSet eqSet; - - eqSet.Guid = setGuid; - eqSet.Name = name; - eqSet.IconName = iconName; - eqSet.state = EQUIPMENT_SET_NEW; - - for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - uint64 itemGuid; - if (!recv_data.readPackGUID(itemGuid)) - return; - - Item *item = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); - - if (!item && itemGuid) // cheating check 1 - return; - - if (item && item->GetGUID() != itemGuid) // cheating check 2 - return; - - eqSet.Items[i] = GUID_LOPART(itemGuid); - } - - _player->SetEquipmentSet(index, eqSet); -} - -void WorldSession::HandleEquipmentSetDelete(WorldPacket &recv_data) -{ - sLog.outDebug("CMSG_EQUIPMENT_SET_DELETE"); - - uint64 setGuid; - if (!recv_data.readPackGUID(setGuid)) - return; - - _player->DeleteEquipmentSet(setGuid); -} - -void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data) -{ - sLog.outDebug("CMSG_EQUIPMENT_SET_USE"); - recv_data.hexlike(); - - for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - uint64 itemGuid; - if (!recv_data.readPackGUID(itemGuid)) - return; - - uint8 srcbag, srcslot; - recv_data >> srcbag >> srcslot; - - sLog.outDebug("Item " UI64FMTD ": srcbag %u, srcslot %u", itemGuid, srcbag, srcslot); - - Item *item = _player->GetItemByGuid(itemGuid); - - uint16 dstpos = i | (INVENTORY_SLOT_BAG_0 << 8); - - if (!item) - { - Item *uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!uItem) - continue; - - ItemPosCountVec sDest; - uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sDest, uItem, false); - if (msg == EQUIP_ERR_OK) - { - _player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true); - _player->StoreItem(sDest, uItem, true); - } - else - _player->SendEquipError(msg, uItem, NULL); - - continue; - } - - if (item->GetPos() == dstpos) - continue; - - _player->SwapItem(item->GetPos(), dstpos); - } - - WorldPacket data(SMSG_EQUIPMENT_SET_USE_RESULT, 1); - data << uint8(0); // 4 - equipment swap failed - inventory is full - SendPacket(&data); -} - -void WorldSession::HandleOnPVPKill(Player *killed) -{ - sScriptMgr.OnPVPKill(GetPlayer(), killed); -} - -bool WorldSession::HandleOnPlayerChat(const char *text) -{ - return sScriptMgr.OnPlayerChat(GetPlayer(), text); -} - -uint32 WorldSession::HandleOnGetXP(uint32 amount) -{ - return sScriptMgr.OnGetXP(GetPlayer(), amount); -} - -int32 WorldSession::HandleOnGetMoney(int32 amount) -{ - return sScriptMgr.OnGetMoney(GetPlayer(), amount); -} - -void WorldSession::HandleOnAreaChange(AreaTableEntry const *pArea) -{ - sScriptMgr.OnAreaChange(GetPlayer(), pArea); -} - -bool WorldSession::HandleOnItemClick(Item *pItem) -{ - return sScriptMgr.OnItemClick(GetPlayer(), pItem); -} - -bool WorldSession::HandleOnItemOpen(Item *pItem) -{ - return sScriptMgr.OnItemOpen(GetPlayer(), pItem); -} - -bool WorldSession::HandleOnGoClick(GameObject *pGameObject) -{ - return sScriptMgr.OnGoClick(GetPlayer(), pGameObject); -} - -void WorldSession::HandleOnCreatureKill(Creature *pCreature) -{ - sScriptMgr.OnCreatureKill(GetPlayer(), pCreature); -} diff --git a/src/server/game/Chat.cpp b/src/server/game/Chat.cpp deleted file mode 100644 index 6e1adffa625..00000000000 --- a/src/server/game/Chat.cpp +++ /dev/null @@ -1,2458 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectMgr.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Database/DatabaseEnv.h" - -#include "AccountMgr.h" -#include "CellImpl.h" -#include "Chat.h" -#include "GridNotifiersImpl.h" -#include "Language.h" -#include "Log.h" -#include "Opcodes.h" -#include "Player.h" -#include "UpdateMask.h" -#include "SpellMgr.h" - -// Supported shift-links (client generated and server side) -// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r -// - client, item icon shift click, not used in server currently -// |color|Harea:area_id|h[name]|h|r -// |color|Hcreature:creature_guid|h[name]|h|r -// |color|Hcreature_entry:creature_id|h[name]|h|r -// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog -// |color|Hgameevent:id|h[name]|h|r -// |color|Hgameobject:go_guid|h[name]|h|r -// |color|Hgameobject_entry:go_id|h[name]|h|r -// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - client, at shift click in glyphs dialog, GlyphSlot.dbc, GlyphProperties.dbc -// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0:reporter_level|h[name]|h|r -// - client, item icon shift click -// |color|Hitemset:itemset_id|h[name]|h|r -// |color|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link -// |color|Hquest:quest_id:quest_level|h[name]|h|r - client, quest list name shift-click -// |color|Hskill:skill_id|h[name]|h|r -// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click -// |color|Htalent:talent_id,rank|h[name]|h|r - client, talent icon shift-click -// |color|Htaxinode:id|h[name]|h|r -// |color|Htele:id|h[name]|h|r -// |color|Htitle:id|h[name]|h|r -// |color|Htrade:spell_id,cur_value,max_value,unk3int,unk3str|h[name]|h|r - client, spellbook profession icon shift-click - -bool ChatHandler::load_command_table = true; - -ChatCommand * ChatHandler::getCommandTable() -{ - static ChatCommand accountSetCommandTable[] = - { - { "addon", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAccountSetAddonCommand, "", NULL }, - { "gmlevel", SEC_CONSOLE, true, &ChatHandler::HandleAccountSetGmLevelCommand, "", NULL }, - { "password", SEC_CONSOLE, true, &ChatHandler::HandleAccountSetPasswordCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand accountCommandTable[] = - { - { "addon", SEC_MODERATOR, false, &ChatHandler::HandleAccountAddonCommand, "", NULL }, - { "create", SEC_CONSOLE, true, &ChatHandler::HandleAccountCreateCommand, "", NULL }, - { "delete", SEC_CONSOLE, true, &ChatHandler::HandleAccountDeleteCommand, "", NULL }, - { "onlinelist", SEC_CONSOLE, true, &ChatHandler::HandleAccountOnlineListCommand, "", NULL }, - { "lock", SEC_PLAYER, false, &ChatHandler::HandleAccountLockCommand, "", NULL }, - { "set", SEC_ADMINISTRATOR, true, NULL, "", accountSetCommandTable }, - { "password", SEC_PLAYER, false, &ChatHandler::HandleAccountPasswordCommand, "", NULL }, - { "", SEC_PLAYER, false, &ChatHandler::HandleAccountCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand banCommandTable[] = - { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanIPCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand baninfoCommandTable[] = - { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoIPCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand banlistCommandTable[] = - { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListIPCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand castCommandTable[] = - { - { "back", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastBackCommand, "", NULL }, - { "dist", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastDistCommand, "", NULL }, - { "self", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastSelfCommand, "", NULL }, - { "target", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastTargetCommand, "", NULL }, - { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand characterCommandTable[] = - { - { "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL }, - { "delete", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeleteCommand, "", NULL }, - { "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterLevelCommand, "", NULL }, - { "rename", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterRenameCommand, "", NULL }, - { "reputation", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterReputationCommand, "", NULL }, - { "titles", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterTitlesCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand channelSetCommandTable[] = - { - { "public", SEC_ADMINISTRATOR, true, &ChatHandler::HandleChannelSetPublic, "", NULL }, - }; - - static ChatCommand channelCommandTable[] = - { - { "set", SEC_ADMINISTRATOR, true, NULL, "", channelSetCommandTable }, - }; - - static ChatCommand debugPlayCommandTable[] = - { - { "cinematic", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayCinematicCommand, "", NULL }, - { "movie", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayMovieCommand, "", NULL }, - { "sound", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlaySoundCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand debugSendCommandTable[] = - { - { "buyerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendBuyErrorCommand, "", NULL }, - { "channelnotify", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendChannelNotifyCommand, "", NULL }, - { "chatmmessage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendChatMsgCommand, "", NULL }, - { "equiperror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendEquipErrorCommand, "", NULL }, - { "largepacket", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendLargePacketCommand, "", NULL }, - { "opcode", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendOpcodeCommand, "", NULL }, - { "poi", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendPoiCommand, "", NULL }, - { "qpartymsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendQuestPartyMsgCommand, "", NULL }, - { "qinvalidmsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendQuestInvalidMsgCommand, "", NULL }, - { "sellerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSellErrorCommand, "", NULL }, - { "setphaseshift", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSetPhaseShiftCommand, "", NULL }, - { "spellfail", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSpellFailCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand debugCommandTable[] = - { - { "setbit", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSet32Bit, "", NULL }, - { "threat", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugThreatList, "", NULL }, - { "hostil", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugHostileRefList, "", NULL }, - { "anim", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugAnimCommand, "", NULL }, - { "arena", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugArenaCommand, "", NULL }, - { "bg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugBattlegroundCommand, "", NULL }, - { "getitemstate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemStateCommand, "", NULL }, - { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugGetLootRecipientCommand, "", NULL }, - { "getvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetValueCommand, "", NULL }, - { "getitemvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemValueCommand, "", NULL }, - { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugMod32ValueCommand, "", NULL }, - { "play", SEC_MODERATOR, false, NULL, "", debugPlayCommandTable }, - { "send", SEC_ADMINISTRATOR, false, NULL, "", debugSendCommandTable }, - { "setaurastate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetAuraStateCommand, "", NULL }, - { "setitemvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetItemValueCommand, "", NULL }, - { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValueCommand, "", NULL }, - { "spawnvehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpawnVehicle, "", NULL }, - { "setvid", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetVehicleId, "", NULL }, - { "entervehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugEnterVehicle, "", NULL }, - { "uws", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateWorldStateCommand, "", NULL }, - { "update", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateCommand, "", NULL }, - { "itemexpire", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugItemExpireCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand eventCommandTable[] = - { - { "activelist", SEC_GAMEMASTER, true, &ChatHandler::HandleEventActiveListCommand, "", NULL }, - { "start", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStartCommand, "", NULL }, - { "stop", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStopCommand, "", NULL }, - { "", SEC_GAMEMASTER, true, &ChatHandler::HandleEventInfoCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand gmCommandTable[] = - { - { "chat", SEC_MODERATOR, false, &ChatHandler::HandleGMChatCommand, "", NULL }, - { "fly", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGMFlyCommand, "", NULL }, - { "ingame", SEC_PLAYER, true, &ChatHandler::HandleGMListIngameCommand, "", NULL }, - { "list", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMListFullCommand, "", NULL }, - { "visible", SEC_MODERATOR, false, &ChatHandler::HandleGMVisibleCommand, "", NULL }, - { "", SEC_MODERATOR, false, &ChatHandler::HandleGMCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand goCommandTable[] = - { - { "creature", SEC_MODERATOR, false, &ChatHandler::HandleGoCreatureCommand, "", NULL }, - { "graveyard", SEC_MODERATOR, false, &ChatHandler::HandleGoGraveyardCommand, "", NULL }, - { "grid", SEC_MODERATOR, false, &ChatHandler::HandleGoGridCommand, "", NULL }, - { "object", SEC_MODERATOR, false, &ChatHandler::HandleGoObjectCommand, "", NULL }, - { "taxinode", SEC_MODERATOR, false, &ChatHandler::HandleGoTaxinodeCommand, "", NULL }, - { "trigger", SEC_MODERATOR, false, &ChatHandler::HandleGoTriggerCommand, "", NULL }, - { "zonexy", SEC_MODERATOR, false, &ChatHandler::HandleGoZoneXYCommand, "", NULL }, - { "xy", SEC_MODERATOR, false, &ChatHandler::HandleGoXYCommand, "", NULL }, - { "xyz", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, - { "ticket", SEC_MODERATOR, false, &ChatHandler::HandleGoTicketCommand, "", NULL }, - { "", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand gobjectCommandTable[] = - { - { "activate", SEC_GAMEMASTER, false, &ChatHandler::HandleActivateObjectCommand, "", NULL }, - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectAddCommand, "", NULL }, - { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectDeleteCommand, "", NULL }, - { "info", SEC_GAMEMASTER, false, &ChatHandler::HandleGOInfoCommand, "", NULL }, - { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectMoveCommand, "", NULL }, - { "near", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectNearCommand, "", NULL }, - { "state", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectStateCommand, "", NULL }, - { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectPhaseCommand, "", NULL }, - { "target", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTargetCommand, "", NULL }, - { "tempadd", SEC_GAMEMASTER, false, &ChatHandler::HandleTempGameObjectCommand, "", NULL }, - { "turn", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTurnCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand groupCommandTable[] = - { - { "leader", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupLeaderCommand, "", NULL }, - { "disband", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupDisbandCommand, "", NULL }, - { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupRemoveCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand guildCommandTable[] = - { - { "create", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildCreateCommand, "", NULL }, - { "delete", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildDeleteCommand, "", NULL }, - { "invite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildInviteCommand, "", NULL }, - { "uninvite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildUninviteCommand, "", NULL }, - { "rank", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildRankCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand honorCommandTable[] = - { - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddCommand, "", NULL }, - { "addkill", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddKillCommand, "", NULL }, - { "update", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorUpdateCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand instanceCommandTable[] = - { - { "listbinds", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceListBindsCommand, "", NULL }, - { "unbind", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceUnbindCommand, "", NULL }, - { "stats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceStatsCommand, "", NULL }, - { "savedata", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL }, - { "open", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceOpenCommand, "", NULL }, - { "close", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceCloseCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand learnCommandTable[] = - { - { "all", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllCommand, "", NULL }, - { "all_gm", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllGMCommand, "", NULL }, - { "all_crafts", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllCraftsCommand, "", NULL }, - { "all_default", SEC_MODERATOR, false, &ChatHandler::HandleLearnAllDefaultCommand, "", NULL }, - { "all_lang", SEC_MODERATOR, false, &ChatHandler::HandleLearnAllLangCommand, "", NULL }, - { "all_myclass", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyClassCommand, "", NULL }, - { "all_mypettalents",SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyPetTalentsCommand,"", NULL }, - { "all_myspells", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMySpellsCommand, "", NULL }, - { "all_mytalents", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyTalentsCommand, "", NULL }, - { "all_recipes", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllRecipesCommand, "", NULL }, - { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand listCommandTable[] = - { - { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListCreatureCommand, "", NULL }, - { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListItemCommand, "", NULL }, - { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListObjectCommand, "", NULL }, - { "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand lookupPlayerCommandTable[] = - { - { "ip", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL }, - { "account", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL }, - { "email", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand lookupCommandTable[] = - { - { "area", SEC_MODERATOR, true, &ChatHandler::HandleLookupAreaCommand, "", NULL }, - { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupCreatureCommand, "", NULL }, - { "event", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupEventCommand, "", NULL }, - { "faction", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupFactionCommand, "", NULL }, - { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemCommand, "", NULL }, - { "itemset", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemSetCommand, "", NULL }, - { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupObjectCommand, "", NULL }, - { "quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupQuestCommand, "", NULL }, - { "player", SEC_GAMEMASTER, true, NULL, "", lookupPlayerCommandTable }, - { "skill", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSkillCommand, "", NULL }, - { "spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSpellCommand, "", NULL }, - { "taxinode", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupTaxiNodeCommand, "", NULL }, - { "tele", SEC_MODERATOR, true, &ChatHandler::HandleLookupTeleCommand, "", NULL }, - { "title", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupTitleCommand, "", NULL }, - { "map", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupMapCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand modifyCommandTable[] = - { - { "hp", SEC_MODERATOR, false, &ChatHandler::HandleModifyHPCommand, "", NULL }, - { "mana", SEC_MODERATOR, false, &ChatHandler::HandleModifyManaCommand, "", NULL }, - { "rage", SEC_MODERATOR, false, &ChatHandler::HandleModifyRageCommand, "", NULL }, - { "runicpower", SEC_MODERATOR, false, &ChatHandler::HandleModifyRunicPowerCommand, "", NULL }, - { "energy", SEC_MODERATOR, false, &ChatHandler::HandleModifyEnergyCommand, "", NULL }, - { "money", SEC_MODERATOR, false, &ChatHandler::HandleModifyMoneyCommand, "", NULL }, - { "speed", SEC_MODERATOR, false, &ChatHandler::HandleModifySpeedCommand, "", NULL }, - { "swim", SEC_MODERATOR, false, &ChatHandler::HandleModifySwimCommand, "", NULL }, - { "scale", SEC_MODERATOR, false, &ChatHandler::HandleModifyScaleCommand, "", NULL }, - { "bit", SEC_MODERATOR, false, &ChatHandler::HandleModifyBitCommand, "", NULL }, - { "bwalk", SEC_MODERATOR, false, &ChatHandler::HandleModifyBWalkCommand, "", NULL }, - { "fly", SEC_MODERATOR, false, &ChatHandler::HandleModifyFlyCommand, "", NULL }, - { "aspeed", SEC_MODERATOR, false, &ChatHandler::HandleModifyASpeedCommand, "", NULL }, - { "faction", SEC_MODERATOR, false, &ChatHandler::HandleModifyFactionCommand, "", NULL }, - { "spell", SEC_MODERATOR, false, &ChatHandler::HandleModifySpellCommand, "", NULL }, - { "tp", SEC_MODERATOR, false, &ChatHandler::HandleModifyTalentCommand, "", NULL }, - { "mount", SEC_MODERATOR, false, &ChatHandler::HandleModifyMountCommand, "", NULL }, - { "honor", SEC_MODERATOR, false, &ChatHandler::HandleModifyHonorCommand, "", NULL }, - { "rep", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyRepCommand, "", NULL }, - { "arena", SEC_MODERATOR, false, &ChatHandler::HandleModifyArenaCommand, "", NULL }, - { "drunk", SEC_MODERATOR, false, &ChatHandler::HandleModifyDrunkCommand, "", NULL }, - { "standstate", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyStandStateCommand, "", NULL }, - { "morph", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyMorphCommand, "", NULL }, - { "phase", SEC_ADMINISTRATOR, false, &ChatHandler::HandleModifyPhaseCommand, "", NULL }, - { "gender", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyGenderCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand npcCommandTable[] = - { - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddCommand, "", NULL }, - { "additem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddVendorItemCommand, "", NULL }, - { "addmove", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddMoveCommand, "", NULL }, - { "allowmove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAllowMovementCommand, "", NULL }, - { "changeentry", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcChangeEntryCommand, "", NULL }, - { "changelevel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcChangeLevelCommand, "", NULL }, - { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDeleteCommand, "", NULL }, - { "delitem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDelVendorItemCommand, "", NULL }, - { "factionid", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFactionIdCommand, "", NULL }, - { "flag", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFlagCommand, "", NULL }, - { "follow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFollowCommand, "", NULL }, - { "info", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcInfoCommand, "", NULL }, - { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcMoveCommand, "", NULL }, - { "playemote", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcPlayEmoteCommand, "", NULL }, - { "setmodel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetModelCommand, "", NULL }, - { "setmovetype", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetMoveTypeCommand, "", NULL }, - { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetPhaseCommand, "", NULL }, - { "spawndist", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnDistCommand, "", NULL }, - { "spawntime", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnTimeCommand, "", NULL }, - { "say", SEC_MODERATOR, false, &ChatHandler::HandleNpcSayCommand, "", NULL }, - { "textemote", SEC_MODERATOR, false, &ChatHandler::HandleNpcTextEmoteCommand, "", NULL }, - { "unfollow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcUnFollowCommand, "", NULL }, - { "whisper", SEC_MODERATOR, false, &ChatHandler::HandleNpcWhisperCommand, "", NULL }, - { "yell", SEC_MODERATOR, false, &ChatHandler::HandleNpcYellCommand, "", NULL }, - { "tempadd", SEC_GAMEMASTER, false, &ChatHandler::HandleTempAddSpwCommand, "", NULL }, - { "tame", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcTameCommand, "", NULL }, - { "setdeathstate", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetDeathStateCommand, "", NULL }, - { "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL }, - { "setlink", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL }, - - //{ TODO: fix or remove this commands - { "addweapon", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAddWeaponCommand, "", NULL }, - { "name", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcNameCommand, "", NULL }, - { "subname", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSubNameCommand, "", NULL }, - //} - - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand petCommandTable[] = - { - { "create", SEC_GAMEMASTER, false, &ChatHandler::HandleCreatePetCommand, "", NULL }, - { "learn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetLearnCommand, "", NULL }, - { "unlearn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetUnlearnCommand, "", NULL }, - { "tp", SEC_GAMEMASTER, false, &ChatHandler::HandlePetTpCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand pdumpCommandTable[] = - { - { "load", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpLoadCommand, "", NULL }, - { "write", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpWriteCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand questCommandTable[] = - { - { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestAdd, "", NULL }, - { "complete", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestComplete, "", NULL }, - { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestRemove, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand reloadCommandTable[] = - { - { "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllCommand, "", NULL }, - { "all_achievement",SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllAchievementCommand,"", NULL }, - { "all_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllAreaCommand, "", NULL }, - { "all_eventai", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllEventAICommand, "", NULL }, - { "all_item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllItemCommand, "", NULL }, - { "all_locales", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllLocalesCommand, "", NULL }, - { "all_loot", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllLootCommand, "", NULL }, - { "all_npc", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllNpcCommand, "", NULL }, - { "all_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllQuestCommand, "", NULL }, - { "all_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllScriptsCommand, "", NULL }, - { "all_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllSpellCommand, "", NULL }, - - { "config", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConfigCommand, "", NULL }, - - { "access_requirement", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAccessRequirementCommand, "", NULL }, - { "achievement_criteria_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementCriteriaDataCommand, "", NULL }, - { "achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementRewardCommand, "", NULL }, - { "areatrigger_involvedrelation",SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestAreaTriggersCommand, "", NULL }, - { "areatrigger_tavern", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL }, - { "areatrigger_teleport", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL }, - { "autobroadcast", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAutobroadcastCommand, "", NULL }, - { "command", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCommandCommand, "", NULL }, - { "conditions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConditions, "", NULL }, - { "creature_ai_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAIScriptsCommand, "", NULL }, - { "creature_ai_summons", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAISummonsCommand, "", NULL }, - { "creature_ai_texts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAITextsCommand, "", NULL }, - { "creature_involvedrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand,"",NULL }, - { "creature_linked_respawn", SEC_GAMEMASTER, true, &ChatHandler::HandleReloadCreatureLinkedRespawnCommand, "", NULL }, - { "creature_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesCreatureCommand, "", NULL }, - { "creature_questrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureQuestRelationsCommand, "", NULL }, - { "creature_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureTemplateCommand, "", NULL }, - //{ "db_script_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDbScriptStringCommand, "", NULL }, - { "disenchant_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL }, - { "event_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventScriptsCommand, "", NULL }, - { "fishing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesFishingCommand, "", NULL }, - { "game_graveyard_zone", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameGraveyardZoneCommand, "", NULL }, - { "game_tele", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameTeleCommand, "", NULL }, - { "gameobject_involvedrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGOQuestInvRelationsCommand, "", NULL }, - { "gameobject_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesGameobjectCommand, "", NULL }, - { "gameobject_questrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGOQuestRelationsCommand, "", NULL }, - { "gameobject_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL }, - { "gossip_menu", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuCommand, "", NULL }, - { "gossip_menu_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuOptionCommand, "", NULL }, - { "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL }, - { "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL }, - { "locales_achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesAchievementRewardCommand,"", NULL }, - { "locales_creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesCreatureCommand, "", NULL }, - { "locales_gameobject", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesGameobjectCommand, "", NULL }, - { "locales_item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesItemCommand, "", NULL }, - { "locales_npc_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesNpcTextCommand, "", NULL }, - { "locales_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL }, - { "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL }, - { "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL }, -// { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL }, - { "mail_level_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMailLevelRewardCommand, "", NULL }, - { "mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMailCommand, "", NULL }, - { "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL }, - { "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL }, - { "npc_spellclick_spells", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellClickSpellsCommand, "",NULL}, - { "npc_trainer", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL }, - { "npc_vendor", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcVendorCommand, "", NULL }, - { "page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPageTextsCommand, "", NULL }, - { "pickpocketing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL}, - { "points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPointsOfInterestCommand, "",NULL}, - { "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL }, - { "quest_end_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL }, - { "quest_start_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL }, - { "quest_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL }, - { "reference_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL }, - { "reserved_name", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadReservedNameCommand, "", NULL }, - { "skill_discovery_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand, "", NULL }, - { "skill_extra_item_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL }, - { "skill_fishing_base_level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL }, - { "skinning_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL }, - { "spell_required", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellRequiredCommand, "", NULL }, - { "spell_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAreaCommand, "", NULL }, - { "spell_bonus_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellBonusesCommand, "", NULL }, - { "spell_group", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellGroupsCommand, "", NULL }, - { "spell_learn_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL }, - { "spell_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSpellCommand, "", NULL }, - { "spell_linked_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLinkedSpellCommand, "", NULL }, - { "spell_pet_auras", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL }, - { "spell_proc_event", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL }, - { "spell_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL }, - { "spell_target_position", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL }, - { "spell_threats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL }, - { "spell_disabled", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellDisabledCommand, "", NULL }, - { "spell_group_stack_rules", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellGroupStackRulesCommand, "", NULL }, - { "trinity_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadTrinityStringCommand, "", NULL }, - { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL }, - { "waypoint_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadWpScriptsCommand, "", NULL }, - { "gm_tickets", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMTicketReloadCommand, "", NULL }, - - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand resetCommandTable[] = - { - { "achievements", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetAchievementsCommand, "", NULL }, - { "honor", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetHonorCommand, "", NULL }, - { "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetLevelCommand, "", NULL }, - { "spells", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetSpellsCommand, "", NULL }, - { "stats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetStatsCommand, "", NULL }, - { "talents", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetTalentsCommand, "", NULL }, - { "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetAllCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand sendCommandTable[] = - { - { "items", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendItemsCommand, "", NULL }, - { "mail", SEC_MODERATOR, true, &ChatHandler::HandleSendMailCommand, "", NULL }, - { "message", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMessageCommand, "", NULL }, - { "money", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMoneyCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand serverIdleRestartCommandTable[] = - { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleRestartCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand serverIdleShutdownCommandTable[] = - { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleShutDownCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand serverRestartCommandTable[] = - { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerRestartCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand serverShutdownCommandTable[] = - { - { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, - { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand serverSetCommandTable[] = - { - { "difftime", SEC_CONSOLE, true, &ChatHandler::HandleServerSetDiffTimeCommand, "", NULL }, - { "loglevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogLevelCommand, "", NULL }, - { "logfilelevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogFileLevelCommand, "", NULL }, - { "motd", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetMotdCommand, "", NULL }, - { "closed", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetClosedCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand serverCommandTable[] = - { - { "corpses", SEC_GAMEMASTER, true, &ChatHandler::HandleServerCorpsesCommand, "", NULL }, - { "exit", SEC_CONSOLE, true, &ChatHandler::HandleServerExitCommand, "", NULL }, - { "idlerestart", SEC_ADMINISTRATOR, true, NULL, "", serverIdleRestartCommandTable }, - { "idleshutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, - { "info", SEC_PLAYER, true, &ChatHandler::HandleServerInfoCommand, "", NULL }, - { "motd", SEC_PLAYER, true, &ChatHandler::HandleServerMotdCommand, "", NULL }, - { "plimit", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerPLimitCommand, "", NULL }, - { "restart", SEC_ADMINISTRATOR, true, NULL, "", serverRestartCommandTable }, - { "shutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, - { "set", SEC_ADMINISTRATOR, true, NULL, "", serverSetCommandTable }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand teleCommandTable[] = - { - { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleTeleAddCommand, "", NULL }, - { "del", SEC_ADMINISTRATOR, true, &ChatHandler::HandleTeleDelCommand, "", NULL }, - { "name", SEC_MODERATOR, true, &ChatHandler::HandleTeleNameCommand, "", NULL }, - { "group", SEC_MODERATOR, false, &ChatHandler::HandleTeleGroupCommand, "", NULL }, - { "", SEC_MODERATOR, false, &ChatHandler::HandleTeleCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand titlesCommandTable[] = - { - { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesAddCommand, "", NULL }, - { "current", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesCurrentCommand, "", NULL }, - { "remove", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesRemoveCommand, "", NULL }, - { "setmask", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesSetMaskCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand unbanCommandTable[] = - { - { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanAccountCommand, "", NULL }, - { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanCharacterCommand, "", NULL }, - { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanIPCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand wpCommandTable[] = - { - { "show", SEC_GAMEMASTER, false, &ChatHandler::HandleWpShowCommand, "", NULL }, - { "addwp", SEC_GAMEMASTER, false, &ChatHandler::HandleWpAddCommand, "", NULL }, - { "load", SEC_GAMEMASTER, false, &ChatHandler::HandleWpLoadPathCommand, "", NULL }, - { "modify", SEC_GAMEMASTER, false, &ChatHandler::HandleWpModifyCommand, "", NULL }, - { "event", SEC_GAMEMASTER, false, &ChatHandler::HandleWpEventCommand, "", NULL }, - { "unload", SEC_GAMEMASTER, false, &ChatHandler::HandleWpUnLoadPathCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand ticketCommandTable[] = - { - { "list", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListCommand, "", NULL }, - { "onlinelist", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListOnlineCommand, "", NULL }, - { "viewname", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketGetByNameCommand, "", NULL }, - { "viewid", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketGetByIdCommand, "", NULL }, - { "close", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketCloseByIdCommand, "", NULL }, - { "closedlist", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListClosedCommand, "", NULL }, - { "delete", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGMTicketDeleteByIdCommand, "", NULL }, - { "assign", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketAssignToCommand, "", NULL }, - { "unassign", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketUnAssignCommand, "", NULL }, - { "comment", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketCommentCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - static ChatCommand commandTable[] = - { - { "account", SEC_PLAYER, true, NULL, "", accountCommandTable }, - { "gm", SEC_MODERATOR, true, NULL, "", gmCommandTable }, - { "npc", SEC_MODERATOR, false, NULL, "", npcCommandTable }, - { "go", SEC_MODERATOR, false, NULL, "", goCommandTable }, - { "learn", SEC_MODERATOR, false, NULL, "", learnCommandTable }, - { "modify", SEC_MODERATOR, false, NULL, "", modifyCommandTable }, - { "debug", SEC_MODERATOR, true, NULL, "", debugCommandTable }, - { "tele", SEC_MODERATOR, true, NULL, "", teleCommandTable }, - { "character", SEC_GAMEMASTER, false, NULL, "", characterCommandTable}, - { "event", SEC_GAMEMASTER, false, NULL, "", eventCommandTable }, - { "gobject", SEC_GAMEMASTER, false, NULL, "", gobjectCommandTable }, - { "honor", SEC_GAMEMASTER, false, NULL, "", honorCommandTable }, - { "wp", SEC_GAMEMASTER, false, NULL, "", wpCommandTable }, - { "titles", SEC_GAMEMASTER, false, NULL, "", titlesCommandTable }, - { "quest", SEC_ADMINISTRATOR, false, NULL, "", questCommandTable }, - { "reload", SEC_ADMINISTRATOR, true, NULL, "", reloadCommandTable }, - { "list", SEC_ADMINISTRATOR, true, NULL, "", listCommandTable }, - { "lookup", SEC_ADMINISTRATOR, true, NULL, "", lookupCommandTable }, - { "pdump", SEC_ADMINISTRATOR, true, NULL, "", pdumpCommandTable }, - { "guild", SEC_ADMINISTRATOR, true, NULL, "", guildCommandTable }, - { "cast", SEC_ADMINISTRATOR, false, NULL, "", castCommandTable }, - { "reset", SEC_ADMINISTRATOR, true, NULL, "", resetCommandTable }, - { "instance", SEC_ADMINISTRATOR, true, NULL, "", instanceCommandTable }, - { "server", SEC_ADMINISTRATOR, true, NULL, "", serverCommandTable }, - - { "channel", SEC_ADMINISTRATOR, true, NULL, "", channelCommandTable }, - - { "pet", SEC_GAMEMASTER, false, NULL, "", petCommandTable }, - { "loadpath", SEC_ADMINISTRATOR, false, &ChatHandler::HandleReloadAllPaths, "", NULL }, - { "ahbotoptions", SEC_GAMEMASTER, true, &ChatHandler::HandleAHBotOptionsCommand, "", NULL }, - { "ticket", SEC_MODERATOR, false, NULL, "", ticketCommandTable }, - - { "aura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAuraCommand, "", NULL }, - { "unaura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnAuraCommand, "", NULL }, - { "nameannounce", SEC_MODERATOR, false, &ChatHandler::HandleNameAnnounceCommand, "", NULL }, - { "gmnameannounce", SEC_MODERATOR, false, &ChatHandler::HandleGMNameAnnounceCommand, "", NULL }, - { "announce", SEC_MODERATOR, true, &ChatHandler::HandleAnnounceCommand, "", NULL }, - { "gmannounce", SEC_MODERATOR, true, &ChatHandler::HandleGMAnnounceCommand, "", NULL }, - { "notify", SEC_MODERATOR, true, &ChatHandler::HandleNotifyCommand, "", NULL }, - { "gmnotify", SEC_MODERATOR, true, &ChatHandler::HandleGMNotifyCommand, "", NULL }, - { "goname", SEC_MODERATOR, false, &ChatHandler::HandleGonameCommand, "", NULL }, - { "namego", SEC_MODERATOR, false, &ChatHandler::HandleNamegoCommand, "", NULL }, - { "groupgo", SEC_MODERATOR, false, &ChatHandler::HandleGroupgoCommand, "", NULL }, - { "commands", SEC_PLAYER, true, &ChatHandler::HandleCommandsCommand, "", NULL }, - { "demorph", SEC_GAMEMASTER, false, &ChatHandler::HandleDeMorphCommand, "", NULL }, - { "die", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDieCommand, "", NULL }, - { "revive", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReviveCommand, "", NULL }, - { "dismount", SEC_PLAYER, false, &ChatHandler::HandleDismountCommand, "", NULL }, - { "gps", SEC_MODERATOR, false, &ChatHandler::HandleGPSCommand, "", NULL }, - { "guid", SEC_GAMEMASTER, false, &ChatHandler::HandleGUIDCommand, "", NULL }, - { "help", SEC_PLAYER, true, &ChatHandler::HandleHelpCommand, "", NULL }, - { "itemmove", SEC_GAMEMASTER, false, &ChatHandler::HandleItemMoveCommand, "", NULL }, - { "cooldown", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCooldownCommand, "", NULL }, - { "unlearn", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnLearnCommand, "", NULL }, - { "distance", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGetDistanceCommand, "", NULL }, - { "recall", SEC_MODERATOR, false, &ChatHandler::HandleRecallCommand, "", NULL }, - { "save", SEC_PLAYER, false, &ChatHandler::HandleSaveCommand, "", NULL }, - { "saveall", SEC_MODERATOR, true, &ChatHandler::HandleSaveAllCommand, "", NULL }, - { "kick", SEC_GAMEMASTER, true, &ChatHandler::HandleKickPlayerCommand, "", NULL }, - { "ban", SEC_ADMINISTRATOR, true, NULL, "", banCommandTable }, - { "unban", SEC_ADMINISTRATOR, true, NULL, "", unbanCommandTable }, - { "baninfo", SEC_ADMINISTRATOR, false, NULL, "", baninfoCommandTable }, - { "banlist", SEC_ADMINISTRATOR, true, NULL, "", banlistCommandTable }, - { "start", SEC_PLAYER, false, &ChatHandler::HandleStartCommand, "", NULL }, - { "taxicheat", SEC_MODERATOR, false, &ChatHandler::HandleTaxiCheatCommand, "", NULL }, - { "linkgrave", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLinkGraveCommand, "", NULL }, - { "neargrave", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNearGraveCommand, "", NULL }, - { "explorecheat", SEC_ADMINISTRATOR, false, &ChatHandler::HandleExploreCheatCommand, "", NULL }, - { "hover", SEC_ADMINISTRATOR, false, &ChatHandler::HandleHoverCommand, "", NULL }, - { "levelup", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLevelUpCommand, "", NULL }, - { "showarea", SEC_ADMINISTRATOR, false, &ChatHandler::HandleShowAreaCommand, "", NULL }, - { "hidearea", SEC_ADMINISTRATOR, false, &ChatHandler::HandleHideAreaCommand, "", NULL }, - { "additem", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddItemCommand, "", NULL }, - { "additemset", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddItemSetCommand, "", NULL }, - { "bank", SEC_ADMINISTRATOR, false, &ChatHandler::HandleBankCommand, "", NULL }, - { "wchange", SEC_ADMINISTRATOR, false, &ChatHandler::HandleChangeWeather, "", NULL }, - { "maxskill", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMaxSkillCommand, "", NULL }, - { "setskill", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSetSkillCommand, "", NULL }, - { "whispers", SEC_MODERATOR, false, &ChatHandler::HandleWhispersCommand, "", NULL }, - { "pinfo", SEC_GAMEMASTER, true, &ChatHandler::HandlePInfoCommand, "", NULL }, - { "respawn", SEC_ADMINISTRATOR, false, &ChatHandler::HandleRespawnCommand, "", NULL }, - { "send", SEC_MODERATOR, true, NULL, "", sendCommandTable }, - { "mute", SEC_MODERATOR, true, &ChatHandler::HandleMuteCommand, "", NULL }, - { "unmute", SEC_MODERATOR, true, &ChatHandler::HandleUnmuteCommand, "", NULL }, - { "movegens", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMovegensCommand, "", NULL }, - { "cometome", SEC_ADMINISTRATOR, false, &ChatHandler::HandleComeToMeCommand, "", NULL }, - { "damage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDamageCommand, "", NULL }, - { "combatstop", SEC_GAMEMASTER, false, &ChatHandler::HandleCombatStopCommand, "", NULL }, - { "flusharenapoints",SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, - { "repairitems", SEC_GAMEMASTER, true, &ChatHandler::HandleRepairitemsCommand, "", NULL }, - { "waterwalk", SEC_GAMEMASTER, false, &ChatHandler::HandleWaterwalkCommand, "", NULL }, - - { "freeze", SEC_MODERATOR, false, &ChatHandler::HandleFreezeCommand, "", NULL }, - { "unfreeze", SEC_MODERATOR, false, &ChatHandler::HandleUnFreezeCommand, "", NULL }, - { "listfreeze", SEC_MODERATOR, false, &ChatHandler::HandleListFreezeCommand, "", NULL }, - - { "possess", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePossessCommand, "", NULL }, - { "unpossess", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnPossessCommand, "", NULL }, - { "bindsight", SEC_ADMINISTRATOR, false, &ChatHandler::HandleBindSightCommand, "", NULL }, - { "unbindsight", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnbindSightCommand, "", NULL }, - { "playall", SEC_GAMEMASTER, false, &ChatHandler::HandlePlayAllCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - - if (load_command_table) - { - load_command_table = false; - - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT name,security,help FROM command"); - if (result) - { - do - { - Field *fields = result->Fetch(); - std::string name = fields[0].GetCppString(); - - SetDataForCommandInTable(commandTable, name.c_str(), fields[1].GetUInt16(), fields[2].GetCppString(), name); - - } while (result->NextRow()); - } - } - - return commandTable; -} - -const char *ChatHandler::GetTrinityString(int32 entry) const -{ - return m_session->GetTrinityString(entry); -} - -bool ChatHandler::isAvailable(ChatCommand const& cmd) const -{ - // check security level only for simple command (without child commands) - return m_session->GetSecurity() >= cmd.SecurityLevel; -} - -bool ChatHandler::HasLowerSecurity(Player* target, uint64 guid, bool strong) -{ - WorldSession* target_session = NULL; - uint32 target_account = 0; - - if (target) - target_session = target->GetSession(); - else if (guid) - target_account = objmgr.GetPlayerAccountIdByGUID(guid); - - if (!target_session && !target_account) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return true; - } - - return HasLowerSecurityAccount(target_session,target_account,strong); -} - -bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_account, bool strong) -{ - uint32 target_sec; - - // allow everything from console and RA console - if (!m_session) - return false; - - // ignore only for non-players for non strong checks (when allow apply command at least to same sec level) - if (m_session->GetSecurity() > SEC_PLAYER && !strong && !sWorld.getConfig(CONFIG_GM_LOWER_SECURITY)) - return false; - - if (target) - target_sec = target->GetSecurity(); - else if (target_account) - target_sec = accmgr.GetSecurity(target_account); - else - return true; // caller must report error for (target == NULL && target_account == 0) - - if (m_session->GetSecurity() < target_sec || (strong && m_session->GetSecurity() <= target_sec)) - { - SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); - SetSentErrorMessage(true); - return true; - } - - return false; -} - -bool ChatHandler::hasStringAbbr(const char* name, const char* part) -{ - // non "" command - if (*name) - { - // "" part from non-"" command - if (!*part) - return false; - - for (;;) - { - if (!*part) - return true; - else if (!*name) - return false; - else if (tolower(*name) != tolower(*part)) - return false; - ++name; ++part; - } - } - // allow with any for "" - - return true; -} - -void ChatHandler::SendSysMessage(const char *str) -{ - WorldPacket data; - - // need copy to prevent corruption by strtok call in LineFromMessage original string - char* buf = strdup(str); - char* pos = buf; - - while (char* line = LineFromMessage(pos)) - { - FillSystemMessageData(&data, line); - m_session->SendPacket(&data); - } - - free(buf); -} - -void ChatHandler::SendGlobalSysMessage(const char *str) -{ - // Chat output - WorldPacket data; - - // need copy to prevent corruption by strtok call in LineFromMessage original string - char* buf = strdup(str); - char* pos = buf; - - while (char* line = LineFromMessage(pos)) - { - FillSystemMessageData(&data, line); - sWorld.SendGlobalMessage(&data); - } - - free(buf); -} - -void ChatHandler::SendGlobalGMSysMessage(const char *str) -{ - // Chat output - WorldPacket data; - - // need copy to prevent corruption by strtok call in LineFromMessage original string - char* buf = strdup(str); - char* pos = buf; - - while (char* line = LineFromMessage(pos)) - { - FillSystemMessageData(&data, line); - sWorld.SendGlobalGMMessage(&data); - } - free(buf); -} - -void ChatHandler::SendSysMessage(int32 entry) -{ - SendSysMessage(GetTrinityString(entry)); -} - -void ChatHandler::PSendSysMessage(int32 entry, ...) -{ - const char *format = GetTrinityString(entry); - va_list ap; - char str [2048]; - va_start(ap, entry); - vsnprintf(str,2048,format, ap); - va_end(ap); - SendSysMessage(str); -} - -void ChatHandler::PSendSysMessage(const char *format, ...) -{ - va_list ap; - char str [2048]; - va_start(ap, format); - vsnprintf(str,2048,format, ap); - va_end(ap); - SendSysMessage(str); -} - -bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcmd) -{ - char const* oldtext = text; - std::string cmd = ""; - - while (*text != ' ' && *text != '\0') - { - cmd += *text; - ++text; - } - - while (*text == ' ') ++text; - - for (uint32 i = 0; table[i].Name != NULL; ++i) - { - if (!hasStringAbbr(table[i].Name, cmd.c_str())) - continue; - - // select subcommand from child commands list - if (table[i].ChildCommands != NULL) - { - if (!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd)) - { - if (text && text[0] != '\0') - SendSysMessage(LANG_NO_SUBCMD); - else - SendSysMessage(LANG_CMD_SYNTAX); - - ShowHelpForCommand(table[i].ChildCommands,text); - } - - return true; - } - - // must be available and have handler - if (!table[i].Handler || !isAvailable(table[i])) - continue; - - SetSentErrorMessage(false); - // table[i].Name == "" is special case: send original command to handler - if ((this->*(table[i].Handler))(strlen(table[i].Name) != 0 ? text : oldtext)) - { - if (table[i].SecurityLevel > SEC_PLAYER) - { - // chat case - if (m_session) - { - Player* p = m_session->GetPlayer(); - uint64 sel_guid = p->GetSelection(); - sLog.outCommand(m_session->GetAccountId(),"Command: %s [Player: %s (Account: %u) X: %f Y: %f Z: %f Map: %u Selected: %s (GUID: %u)]", - fullcmd.c_str(),p->GetName(),m_session->GetAccountId(),p->GetPositionX(),p->GetPositionY(),p->GetPositionZ(),p->GetMapId(), - GetLogNameForGuid(sel_guid),GUID_LOPART(sel_guid)); - } - } - } - // some commands have custom error messages. Don't send the default one in these cases. - else if (!sentErrorMessage) - { - if (!table[i].Help.empty()) - SendSysMessage(table[i].Help.c_str()); - else - SendSysMessage(LANG_CMD_SYNTAX); - } - - return true; - } - - return false; -} - -bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand) -{ - std::string cmd = ""; - - while (*text != ' ' && *text != '\0') - { - cmd += *text; - ++text; - } - - while (*text == ' ') ++text; - - for (uint32 i = 0; table[i].Name != NULL; i++) - { - // for data fill use full explicit command names - if (table[i].Name != cmd) - continue; - - // select subcommand from child commands list (including "") - if (table[i].ChildCommands != NULL) - { - if (SetDataForCommandInTable(table[i].ChildCommands, text, security, help, fullcommand)) - return true; - else if (*text) - return false; - - // fail with "" subcommands, then use normal level up command instead - } - // expected subcommand by full name DB content - else if (*text) - { - sLog.outErrorDb("Table `command` have unexpected subcommand '%s' in command '%s', skip.",text,fullcommand.c_str()); - return false; - } - - if (table[i].SecurityLevel != security) - sLog.outDetail("Table `command` overwrite for command '%s' default security (%u) by %u",fullcommand.c_str(),table[i].SecurityLevel,security); - - table[i].SecurityLevel = security; - table[i].Help = help; - return true; - } - - // in case "" command let process by caller - if (!cmd.empty()) - { - if (table == getCommandTable()) - sLog.outErrorDb("Table `command` have not existed command '%s', skip.",cmd.c_str()); - else - sLog.outErrorDb("Table `command` have not existed subcommand '%s' in command '%s', skip.",cmd.c_str(),fullcommand.c_str()); - } - - return false; -} - -int ChatHandler::ParseCommands(const char* text) -{ - ASSERT(text); - ASSERT(*text); - - std::string fullcmd = text; - - if (m_session && m_session->GetSecurity() <= SEC_PLAYER && sWorld.getConfig(CONFIG_ALLOW_PLAYER_COMMANDS) == 0) - return 0; - - if (m_session && !m_session->HandleOnPlayerChat(text)) - return 0; - - /// chat case (.command or !command format) - if (m_session) - { - if (text[0] != '!' && text[0] != '.') - return 0; - } - - /// ignore single . and ! in line - if (strlen(text) < 2) - return 0; - // original `text` can't be used. It content destroyed in command code processing. - - /// ignore messages staring from many dots. - if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!')) - return 0; - - /// skip first . or ! (in console allowed use command with . and ! and without its) - if (text[0] == '!' || text[0] == '.') - ++text; - - if (!ExecuteCommandInTable(getCommandTable(), text, fullcmd)) - { - if (m_session && m_session->GetSecurity() == SEC_PLAYER) - return 0; - SendSysMessage(LANG_NO_CMD); - } - return 1; -} - -bool ChatHandler::isValidChatMessage(const char* message) -{ -/* - -valid examples: -|cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r -|cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r -|cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r -|cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r -|cff71d5ff|Hspell:21563|h[Command]|h|r -|cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r -|cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r -|cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r - -| will be escaped to || -*/ - - if (strlen(message) > 255) - return false; - - const char validSequence[6] = "cHhhr"; - const char* validSequenceIterator = validSequence; - - // more simple checks - if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3) - { - const std::string validCommands = "cHhr|"; - - while (*message) - { - // find next pipe command - message = strchr(message, '|'); - - if (!message) - return true; - - ++message; - char commandChar = *message; - if (validCommands.find(commandChar) == std::string::npos) - return false; - - ++message; - // validate sequence - if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2) - { - if (commandChar == *validSequenceIterator) - { - if (validSequenceIterator == validSequence+4) - validSequenceIterator = validSequence; - else - ++validSequenceIterator; - } - else - return false; - } - } - return true; - } - - std::istringstream reader(message); - char buffer[256]; - - uint32 color; - - ItemPrototype const* linkedItem; - Quest const* linkedQuest; - SpellEntry const *linkedSpell; - AchievementEntry const* linkedAchievement; - ItemRandomPropertiesEntry const* itemProperty; - ItemRandomSuffixEntry const* itemSuffix; - - while (!reader.eof()) - { - if (validSequence == validSequenceIterator) - { - linkedItem = NULL; - linkedQuest = NULL; - linkedSpell = NULL; - linkedAchievement = NULL; - itemProperty = NULL; - itemSuffix = NULL; - - reader.ignore(255, '|'); - } - else if (reader.get() != '|') - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage sequence aborted unexpectedly"); -#endif - return false; - } - - // pipe has always to be followed by at least one char - if (reader.peek() == '\0') - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage pipe followed by \\0"); -#endif - return false; - } - - // no further pipe commands - if (reader.eof()) - break; - - char commandChar; - reader >> commandChar; - - // | in normal messages is escaped by || - if (commandChar != '|') - { - if (commandChar == *validSequenceIterator) - { - if (validSequenceIterator == validSequence+4) - validSequenceIterator = validSequence; - else - ++validSequenceIterator; - } - else - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage invalid sequence, expected %c but got %c", *validSequenceIterator, commandChar); -#endif - return false; - } - } - else if (validSequence != validSequenceIterator) - { - // no escaped pipes in sequences -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage got escaped pipe in sequence"); -#endif - return false; - } - - switch (commandChar) - { - case 'c': - color = 0; - // validate color, expect 8 hex chars - for (int i=0; i<8; i++) - { - char c; - reader >> c; - if (!c) - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage got \\0 while reading color in |c command"); -#endif - return false; - } - - color <<= 4; - // check for hex char - if (c >= '0' && c <= '9') - { - color |= c-'0'; - continue; - } - if (c >= 'a' && c <= 'f') - { - color |= 10+c-'a'; - continue; - } -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage got non hex char '%c' while reading color", c); -#endif - return false; - } - break; - case 'H': - // read chars up to colon = link type - reader.getline(buffer, 256, ':'); - - if (strcmp(buffer, "item") == 0) - { - // read item entry - reader.getline(buffer, 256, ':'); - - linkedItem= objmgr.GetItemPrototype(atoi(buffer)); - if (!linkedItem) - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage got invalid itemID %u in |item command", atoi(buffer)); -#endif - return false; - } - - if (color != ItemQualityColors[linkedItem->Quality]) - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage linked item has color %u, but user claims %u", ItemQualityColors[linkedItem->Quality], - color); -#endif - return false; - } - - // the itementry is followed by several integers which describe an instance of this item - - // position relative after itemEntry - const uint8 randomPropertyPosition = 6; - - int32 propertyId = 0; - bool negativeNumber = false; - char c; - for (uint8 i=0; i='0' && c <= '9') - { - propertyId*=10; - propertyId += c-'0'; - } else if (c == '-') - negativeNumber = true; - else - return false; - } - } - if (negativeNumber) - propertyId *= -1; - - if (propertyId > 0) - { - itemProperty = sItemRandomPropertiesStore.LookupEntry(propertyId); - if (!itemProperty) - return false; - } - else if (propertyId < 0) - { - itemSuffix = sItemRandomSuffixStore.LookupEntry(-propertyId); - if (!itemSuffix) - return false; - } - - // ignore other integers - while ((c >='0' && c <= '9') || c == ':') - { - reader.ignore(1); - c = reader.peek(); - } - } - else if (strcmp(buffer, "quest") == 0) - { - // no color check for questlinks, each client will adapt it anyway - uint32 questid= 0; - // read questid - char c = reader.peek(); - while (c >='0' && c <= '9') - { - reader.ignore(1); - questid *= 10; - questid += c-'0'; - c = reader.peek(); - } - - linkedQuest = objmgr.GetQuestTemplate(questid); - - if (!linkedQuest) - { -#ifdef MANOGS_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage Questtemplate %u not found", questid); -#endif - return false; - } - c = reader.peek(); - // level - while (c !='|' && c != '\0') - { - reader.ignore(1); - c = reader.peek(); - } - } - else if (strcmp(buffer, "trade") == 0) - { - if (color != CHAT_LINK_COLOR_TRADE) - return false; - - // read spell entry - reader.getline(buffer, 256, ':'); - linkedSpell = sSpellStore.LookupEntry(atoi(buffer)); - if (!linkedSpell) - return false; - - char c = reader.peek(); - // base64 encoded stuff - while (c !='|' && c != '\0') - { - reader.ignore(1); - c = reader.peek(); - } - } - else if (strcmp(buffer, "talent") == 0) - { - // talent links are always supposed to be blue - if (color != CHAT_LINK_COLOR_TALENT) - return false; - - // read talent entry - reader.getline(buffer, 256, ':'); - TalentEntry const *talentInfo = sTalentStore.LookupEntry(atoi(buffer)); - if (!talentInfo) - return false; - - linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]); - if (!linkedSpell) - return false; - - char c = reader.peek(); - // skillpoints? whatever, drop it - while (c !='|' && c != '\0') - { - reader.ignore(1); - c = reader.peek(); - } - } - else if (strcmp(buffer, "spell") == 0) - { - if (color != CHAT_LINK_COLOR_SPELL) - return false; - - uint32 spellid = 0; - // read spell entry - char c = reader.peek(); - while (c >='0' && c <= '9') - { - reader.ignore(1); - spellid *= 10; - spellid += c-'0'; - c = reader.peek(); - } - linkedSpell = sSpellStore.LookupEntry(spellid); - if (!linkedSpell) - return false; - } - else if (strcmp(buffer, "enchant") == 0) - { - if (color != CHAT_LINK_COLOR_ENCHANT) - return false; - - uint32 spellid = 0; - // read spell entry - char c = reader.peek(); - while (c >='0' && c <= '9') - { - reader.ignore(1); - spellid *= 10; - spellid += c-'0'; - c = reader.peek(); - } - linkedSpell = sSpellStore.LookupEntry(spellid); - if (!linkedSpell) - return false; - } - else if (strcmp(buffer, "achievement") == 0) - { - if (color != CHAT_LINK_COLOR_ACHIEVEMENT) - return false; - reader.getline(buffer, 256, ':'); - uint32 achievementId = atoi(buffer); - linkedAchievement = sAchievementStore.LookupEntry(achievementId); - - if (!linkedAchievement) - return false; - - char c = reader.peek(); - // skip progress - while (c !='|' && c != '\0') - { - reader.ignore(1); - c = reader.peek(); - } - } - else if (strcmp(buffer, "glyph") == 0) - { - if (color != CHAT_LINK_COLOR_GLYPH) - return false; - - // first id is slot, drop it - reader.getline(buffer, 256, ':'); - uint32 glyphId = 0; - char c = reader.peek(); - while (c >= '0' && c <= '9') - { - glyphId *= 10; - glyphId += c-'0'; - reader.ignore(1); - c = reader.peek(); - } - GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId); - if (!glyph) - return false; - - linkedSpell = sSpellStore.LookupEntry(glyph->SpellId); - - if (!linkedSpell) - return false; - } - else - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage user sent unsupported link type '%s'", buffer); -#endif - return false; - } - break; - case 'h': - // if h is next element in sequence, this one must contain the linked text :) - if (*validSequenceIterator == 'h') - { - // links start with '[' - if (reader.get() != '[') - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage link caption doesn't start with '['"); -#endif - return false; - } - reader.getline(buffer, 256, ']'); - - // verify the link name - if (linkedSpell) - { - // spells with that flag have a prefix of "$PROFESSION: " - if (linkedSpell->Attributes & SPELL_ATTR_TRADESPELL) - { - // lookup skillid - SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(linkedSpell->Id); - if (bounds.first == bounds.second) - { - return false; - } - - SkillLineAbilityEntry const *skillInfo = bounds.first->second; - - if (!skillInfo) - { - return false; - } - - SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId); - if (!skillLine) - { - return false; - } - - for (uint8 i=0; iname[i]); - if (skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0) - { - // found the prefix, remove it to perform spellname validation below - // -2 = strlen(": ") - uint32 spellNameLength = strlen(buffer)-skillLineNameLength-2; - memmove(buffer, buffer+skillLineNameLength+2, spellNameLength+1); - } - } - } - bool foundName = false; - for (uint8 i=0; iSpellName[i] && strcmp(linkedSpell->SpellName[i], buffer) == 0) - { - foundName = true; - break; - } - } - if (!foundName) - return false; - } - else if (linkedQuest) - { - if (linkedQuest->GetTitle() != buffer) - { - QuestLocale const *ql = objmgr.GetQuestLocale(linkedQuest->GetQuestId()); - - if (!ql) - { -#ifdef MANOGS_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage default questname didn't match and there is no locale"); -#endif - return false; - } - - bool foundName = false; - for (uint8 i=0; iTitle.size(); i++) - { - if (ql->Title[i] == buffer) - { - foundName = true; - break; - } - } - if (!foundName) - { -#ifdef MANOGS_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage no quest locale title matched") -#endif - return false; - } - } - } - else if (linkedItem) - { - char* const* suffix = itemSuffix?itemSuffix->nameSuffix:(itemProperty?itemProperty->nameSuffix:NULL); - - std::string expectedName = std::string(linkedItem->Name1); - if (suffix) - { - expectedName += " "; - expectedName += suffix[LOCALE_enUS]; - } - - if (expectedName != buffer) - { - ItemLocale const *il = objmgr.GetItemLocale(linkedItem->ItemId); - - bool foundName = false; - for (uint8 i=LOCALE_koKR; i= il->Name.size()) - // using strange database/client combinations can lead to this case - expectedName = linkedItem->Name1; - else - expectedName = il->Name[dbIndex]; - if (suffix) - { - expectedName += " "; - expectedName += suffix[i]; - } - if (expectedName == buffer) - { - foundName = true; - break; - } - } - if (!foundName) - { -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage linked item name wasn't found in any localization"); -#endif - return false; - } - } - } - else if (linkedAchievement) - { - bool foundName = false; - for (uint8 i=0; iname[i] && strcmp(linkedAchievement->name[i], buffer) == 0) - { - foundName = true; - break; - } - } - if (!foundName) - return false; - } - // that place should never be reached - if nothing linked has been set in |H - // it will return false before - else - return false; - } - break; - case 'r': - case '|': - // no further payload - break; - default: -#ifdef TRINITY_DEBUG - sLog.outBasic("ChatHandler::isValidChatMessage got invalid command |%c", commandChar); -#endif - return false; - } - } - - // check if every opened sequence was also closed properly -#ifdef TRINITY_DEBUG - if (validSequence != validSequenceIterator) - sLog.outBasic("ChatHandler::isValidChatMessage EOF in active sequence"); -#endif - return validSequence == validSequenceIterator; -} - -bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd) -{ - std::string list; - for (uint32 i = 0; table[i].Name != NULL; ++i) - { - // must be available (ignore handler existence for show command with possibe avalable subcomands - if (!isAvailable(table[i])) - continue; - - /// for empty subcmd show all available - if (*subcmd && !hasStringAbbr(table[i].Name, subcmd)) - continue; - - if (m_session) - list += "\n "; - else - list += "\n\r "; - - list += table[i].Name; - - if (table[i].ChildCommands) - list += " ..."; - } - - if (list.empty()) - return false; - - if (table == getCommandTable()) - { - SendSysMessage(LANG_AVIABLE_CMD); - PSendSysMessage("%s",list.c_str()); - } - else - PSendSysMessage(LANG_SUBCMDS_LIST,cmd,list.c_str()); - - return true; -} - -bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd) -{ - if (*cmd) - { - for (uint32 i = 0; table[i].Name != NULL; ++i) - { - // must be available (ignore handler existence for show command with possibe avalable subcomands - if (!isAvailable(table[i])) - continue; - - if (!hasStringAbbr(table[i].Name, cmd)) - continue; - - // have subcommand - char const* subcmd = (*cmd) ? strtok(NULL, " ") : ""; - - if (table[i].ChildCommands && subcmd && *subcmd) - { - if (ShowHelpForCommand(table[i].ChildCommands, subcmd)) - return true; - } - - if (!table[i].Help.empty()) - SendSysMessage(table[i].Help.c_str()); - - if (table[i].ChildCommands) - if (ShowHelpForSubCommands(table[i].ChildCommands,table[i].Name,subcmd ? subcmd : "")) - return true; - - return !table[i].Help.empty(); - } - } - else - { - for (uint32 i = 0; table[i].Name != NULL; ++i) - { - // must be available (ignore handler existence for show command with possibe avalable subcomands - if (!isAvailable(table[i])) - continue; - - if (strlen(table[i].Name)) - continue; - - if (!table[i].Help.empty()) - SendSysMessage(table[i].Help.c_str()); - - if (table[i].ChildCommands) - if (ShowHelpForSubCommands(table[i].ChildCommands,"","")) - return true; - - return !table[i].Help.empty(); - } - } - - return ShowHelpForSubCommands(table,"",cmd); -} - -//Note: target_guid used only in CHAT_MSG_WHISPER_INFORM mode (in this case channelName ignored) -void ChatHandler::FillMessageData(WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker) -{ - uint32 messageLength = (message ? strlen(message) : 0) + 1; - - data->Initialize(SMSG_MESSAGECHAT, 100); // guess size - *data << uint8(type); - if ((type != CHAT_MSG_CHANNEL && type != CHAT_MSG_WHISPER) || language == LANG_ADDON) - *data << uint32(language); - else - *data << uint32(LANG_UNIVERSAL); - - switch(type) - { - case CHAT_MSG_SAY: - case CHAT_MSG_PARTY: - case CHAT_MSG_PARTY_LEADER: - case CHAT_MSG_RAID: - case CHAT_MSG_GUILD: - case CHAT_MSG_OFFICER: - case CHAT_MSG_YELL: - case CHAT_MSG_WHISPER: - case CHAT_MSG_CHANNEL: - case CHAT_MSG_RAID_LEADER: - case CHAT_MSG_RAID_WARNING: - case CHAT_MSG_BG_SYSTEM_NEUTRAL: - case CHAT_MSG_BG_SYSTEM_ALLIANCE: - case CHAT_MSG_BG_SYSTEM_HORDE: - case CHAT_MSG_BATTLEGROUND: - case CHAT_MSG_BATTLEGROUND_LEADER: - target_guid = session ? session->GetPlayer()->GetGUID() : 0; - break; - case CHAT_MSG_MONSTER_SAY: - case CHAT_MSG_MONSTER_PARTY: - case CHAT_MSG_MONSTER_YELL: - case CHAT_MSG_MONSTER_WHISPER: - case CHAT_MSG_MONSTER_EMOTE: - case CHAT_MSG_RAID_BOSS_WHISPER: - case CHAT_MSG_RAID_BOSS_EMOTE: - case CHAT_MSG_BATTLENET: - { - *data << uint64(speaker->GetGUID()); - *data << uint32(0); // 2.1.0 - *data << uint32(strlen(speaker->GetName()) + 1); - *data << speaker->GetName(); - uint64 listener_guid = 0; - *data << uint64(listener_guid); - if (listener_guid && !IS_PLAYER_GUID(listener_guid)) - { - *data << uint32(1); // string listener_name_length - *data << uint8(0); // string listener_name - } - *data << uint32(messageLength); - *data << message; - *data << uint8(0); - return; - } - default: - if (type != CHAT_MSG_WHISPER_INFORM && type != CHAT_MSG_IGNORED && type != CHAT_MSG_DND && type != CHAT_MSG_AFK) - target_guid = 0; // only for CHAT_MSG_WHISPER_INFORM used original value target_guid - break; - } - - *data << uint64(target_guid); // there 0 for BG messages - *data << uint32(0); // can be chat msg group or something - - if (type == CHAT_MSG_CHANNEL) - { - ASSERT(channelName); - *data << channelName; - } - - *data << uint64(target_guid); - *data << uint32(messageLength); - *data << message; - if (session != 0 && type != CHAT_MSG_WHISPER_INFORM && type != CHAT_MSG_DND && type != CHAT_MSG_AFK) - *data << uint8(session->GetPlayer()->chatTag()); - else - *data << uint8(0); -} - -Player * ChatHandler::getSelectedPlayer() -{ - if (!m_session) - return NULL; - - uint64 guid = m_session->GetPlayer()->GetSelection(); - - if (guid == 0) - return m_session->GetPlayer(); - - return objmgr.GetPlayer(guid); -} - -Unit* ChatHandler::getSelectedUnit() -{ - if (!m_session) - return NULL; - - uint64 guid = m_session->GetPlayer()->GetSelection(); - - if (guid == 0) - return m_session->GetPlayer(); - - return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid); -} - -WorldObject *ChatHandler::getSelectedObject() -{ - if (!m_session) - return NULL; - - uint64 guid = m_session->GetPlayer()->GetSelection(); - - if (guid == 0) - return GetNearbyGameObject(); - - return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid); -} - -Creature* ChatHandler::getSelectedCreature() -{ - if (!m_session) - return NULL; - - return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection()); -} - -char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1) -{ - // skip empty - if (!text) - return NULL; - - // skip spaces - while (*text == ' '||*text == '\t'||*text == '\b') - ++text; - - if (!*text) - return NULL; - - // return non link case - if (text[0] != '|') - return strtok(text, " "); - - // [name] Shift-click form |color|linkType:key|h[name]|h|r - // or - // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r - - char* check = strtok(text, "|"); // skip color - if (!check) - return NULL; // end of data - - char* cLinkType = strtok(NULL, ":"); // linktype - if (!cLinkType) - return NULL; // end of data - - if (strcmp(cLinkType,linkType) != 0) - { - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function - SendSysMessage(LANG_WRONG_LINK_TYPE); - return NULL; - } - - char* cKeys = strtok(NULL, "|"); // extract keys and values - char* cKeysTail = strtok(NULL, ""); - - char* cKey = strtok(cKeys, ":|"); // extract key - if (something1) - *something1 = strtok(NULL, ":|"); // extract something - - strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function - return cKey; -} - -char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1) -{ - // skip empty - if (!text) - return NULL; - - // skip spaces - while (*text == ' '||*text == '\t'||*text == '\b') - ++text; - - if (!*text) - return NULL; - - // return non link case - if (text[0] != '|') - return strtok(text, " "); - - // [name] Shift-click form |color|linkType:key|h[name]|h|r - // or - // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r - // or - // [name] Shift-click form |linkType:key|h[name]|h|r - - char* tail; - - if (text[1] == 'c') - { - char* check = strtok(text, "|"); // skip color - if (!check) - return NULL; // end of data - - tail = strtok(NULL, ""); // tail - } - else - tail = text+1; // skip first | - - char* cLinkType = strtok(tail, ":"); // linktype - if (!cLinkType) - return NULL; // end of data - - for (int i = 0; linkTypes[i]; ++i) - { - if (strcmp(cLinkType,linkTypes[i]) == 0) - { - char* cKeys = strtok(NULL, "|"); // extract keys and values - char* cKeysTail = strtok(NULL, ""); - - char* cKey = strtok(cKeys, ":|"); // extract key - if (something1) - *something1 = strtok(NULL, ":|"); // extract something - - strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function - if (found_idx) - *found_idx = i; - return cKey; - } - } - - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function - SendSysMessage(LANG_WRONG_LINK_TYPE); - return NULL; -} - -char const *fmtstring(char const *format, ...) -{ - va_list argptr; - #define MAX_FMT_STRING 32000 - static char temp_buffer[MAX_FMT_STRING]; - static char string[MAX_FMT_STRING]; - static int index = 0; - char *buf; - int len; - - va_start(argptr, format); - vsnprintf(temp_buffer,MAX_FMT_STRING, format, argptr); - va_end(argptr); - - len = strlen(temp_buffer); - - if (len >= MAX_FMT_STRING) - return "ERROR"; - - if (len + index >= MAX_FMT_STRING-1) - { - index = 0; - } - - buf = &string[index]; - memcpy(buf, temp_buffer, len+1); - - index += len + 1; - - return buf; -} - -GameObject* ChatHandler::GetNearbyGameObject() -{ - if (!m_session) - return NULL; - - Player* pl = m_session->GetPlayer(); - GameObject* obj = NULL; - Trinity::NearestGameObjectCheck check(*pl); - Trinity::GameObjectLastSearcher searcher(pl, obj, check); - pl->VisitNearbyGridObject(999, searcher); - return obj; -} - -GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry) -{ - if (!m_session) - return NULL; - - Player* pl = m_session->GetPlayer(); - - GameObject* obj = pl->GetMap()->GetGameObject(MAKE_NEW_GUID(lowguid, entry, HIGHGUID_GAMEOBJECT)); - - if (!obj && objmgr.GetGOData(lowguid)) // guid is DB guid of object - { - // search near player then - CellPair p(Trinity::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - Trinity::GameObjectWithDbGUIDCheck go_check(*pl,lowguid); - Trinity::GameObjectSearcher checker(pl,obj,go_check); - - TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); - cell.Visit(p, object_checker, *pl->GetMap()); - } - - return obj; -} - -enum SpellLinkType -{ - SPELL_LINK_SPELL = 0, - SPELL_LINK_TALENT = 1, - SPELL_LINK_ENCHANT = 2, - SPELL_LINK_TRADE = 3, - SPELL_LINK_GLYPH = 4 -}; - -static char const* const spellKeys[] = -{ - "Hspell", // normal spell - "Htalent", // talent spell - "Henchant", // enchanting recipe spell - "Htrade", // profession/skill spell - "Hglyph", // glyph - 0 -}; - -uint32 ChatHandler::extractSpellIdFromLink(char* text) -{ - // number or [name] Shift-click form |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - // number or [name] Shift-click form |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r - // number or [name] Shift-click form |color|Htalent:talent_id,rank|h[name]|h|r - // number or [name] Shift-click form |color|Htrade:spell_id,skill_id,max_value,cur_value|h[name]|h|r - int type = 0; - char* param1_str = NULL; - char* idS = extractKeyFromLink(text,spellKeys,&type,¶m1_str); - if (!idS) - return 0; - - uint32 id = (uint32)atol(idS); - - switch(type) - { - case SPELL_LINK_SPELL: - return id; - case SPELL_LINK_TALENT: - { - // talent - TalentEntry const* talentEntry = sTalentStore.LookupEntry(id); - if (!talentEntry) - return 0; - - int32 rank = param1_str ? (uint32)atol(param1_str) : 0; - if (rank >= MAX_TALENT_RANK) - return 0; - - if (rank < 0) - rank = 0; - - return talentEntry->RankID[rank]; - } - case SPELL_LINK_ENCHANT: - case SPELL_LINK_TRADE: - return id; - case SPELL_LINK_GLYPH: - { - uint32 glyph_prop_id = param1_str ? (uint32)atol(param1_str) : 0; - - GlyphPropertiesEntry const* glyphPropEntry = sGlyphPropertiesStore.LookupEntry(glyph_prop_id); - if (!glyphPropEntry) - return 0; - - return glyphPropEntry->SpellId; - } - } - - // unknown type? - return 0; -} - -GameTele const* ChatHandler::extractGameTeleFromLink(char* text) -{ - // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r - char* cId = extractKeyFromLink(text,"Htele"); - if (!cId) - return false; - - // id case (explicit or from shift link) - if (cId[0] >= '0' || cId[0] >= '9') - if (uint32 id = atoi(cId)) - return objmgr.GetGameTele(id); - - return objmgr.GetGameTele(cId); -} - -enum GuidLinkType -{ - SPELL_LINK_PLAYER = 0, // must be first for selection in not link case - SPELL_LINK_CREATURE = 1, - SPELL_LINK_GAMEOBJECT = 2 -}; - -static char const* const guidKeys[] = -{ - "Hplayer", - "Hcreature", - "Hgameobject", - 0 -}; - -uint64 ChatHandler::extractGuidFromLink(char* text) -{ - int type = 0; - - // |color|Hcreature:creature_guid|h[name]|h|r - // |color|Hgameobject:go_guid|h[name]|h|r - // |color|Hplayer:name|h[name]|h|r - char* idS = extractKeyFromLink(text,guidKeys,&type); - if (!idS) - return 0; - - switch(type) - { - case SPELL_LINK_PLAYER: - { - std::string name = idS; - if (!normalizePlayerName(name)) - return 0; - - if (Player* player = objmgr.GetPlayer(name.c_str())) - return player->GetGUID(); - - if (uint64 guid = objmgr.GetPlayerGUIDByName(name)) - return guid; - - return 0; - } - case SPELL_LINK_CREATURE: - { - uint32 lowguid = (uint32)atol(idS); - - if (CreatureData const* data = objmgr.GetCreatureData(lowguid)) - return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT); - else - return 0; - } - case SPELL_LINK_GAMEOBJECT: - { - uint32 lowguid = (uint32)atol(idS); - - if (GameObjectData const* data = objmgr.GetGOData(lowguid)) - return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_GAMEOBJECT); - else - return 0; - } - } - - // unknown type? - return 0; -} - -std::string ChatHandler::extractPlayerNameFromLink(char* text) -{ - // |color|Hplayer:name|h[name]|h|r - char* name_str = extractKeyFromLink(text,"Hplayer"); - if (!name_str) - return ""; - - std::string name = name_str; - if (!normalizePlayerName(name)) - return ""; - - return name; -} - -bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* player_guid /*=NULL*/,std::string* player_name /*= NULL*/) -{ - if (args && *args) - { - std::string name = extractPlayerNameFromLink(args); - if (name.empty()) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - - Player* pl = objmgr.GetPlayer(name.c_str()); - - // if allowed player pointer - if (player) - *player = pl; - - // if need guid value from DB (in name case for check player existence) - uint64 guid = !pl && (player_guid || player_name) ? objmgr.GetPlayerGUIDByName(name) : 0; - - // if allowed player guid (if no then only online players allowed) - if (player_guid) - *player_guid = pl ? pl->GetGUID() : guid; - - if (player_name) - *player_name = pl || guid ? name : ""; - } - else - { - Player* pl = getSelectedPlayer(); - // if allowed player pointer - if (player) - *player = pl; - // if allowed player guid (if no then only online players allowed) - if (player_guid) - *player_guid = pl ? pl->GetGUID() : 0; - - if (player_name) - *player_name = pl ? pl->GetName() : ""; - } - - // some from req. data must be provided (note: name is empty if player not exist) - if ((!player || !*player) && (!player_guid || !*player_guid) && (!player_name || player_name->empty())) - { - SendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - - return true; -} - -void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2) -{ - char* p1 = strtok(args, " "); - char* p2 = strtok(NULL, " "); - - if (!p2) - { - p2 = p1; - p1 = NULL; - } - - if (arg1) - *arg1 = p1; - - if (arg2) - *arg2 = p2; -} - -char* ChatHandler::extractQuotedArg(char* args) -{ - if (!*args) - return NULL; - - if (*args == '"') - return strtok(args+1, "\""); - else - { - char* space = strtok(args, "\""); - if (!space) - return false; - return strtok(NULL, "\""); - } -} - -bool ChatHandler::needReportToTarget(Player* chr) const -{ - Player* pl = m_session->GetPlayer(); - return pl != chr && pl->IsVisibleGloballyFor(chr); -} - -LocaleConstant ChatHandler::GetSessionDbcLocale() const -{ - return m_session->GetSessionDbcLocale(); -} - -int ChatHandler::GetSessionDbLocaleIndex() const -{ - return m_session->GetSessionDbLocaleIndex(); -} - -const char *CliHandler::GetTrinityString(int32 entry) const -{ - return objmgr.GetTrinityStringForDBCLocale(entry); -} - -bool CliHandler::isAvailable(ChatCommand const& cmd) const -{ - // skip non-console commands in console case - return cmd.AllowConsole; -} - -void CliHandler::SendSysMessage(const char *str) -{ - m_print(str); - m_print("\r\n"); -} - -std::string CliHandler::GetNameLink() const -{ - return GetTrinityString(LANG_CONSOLE_COMMAND); -} - -bool CliHandler::needReportToTarget(Player* /*chr*/) const -{ - return true; -} - -bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline) -{ - plr = NULL; - guid = 0; - - if (cname) - { - std::string name = cname; - if (!name.empty()) - { - if (!normalizePlayerName(name)) - { - PSendSysMessage(LANG_PLAYER_NOT_FOUND); - SetSentErrorMessage(true); - return false; - } - - plr = objmgr.GetPlayer(name.c_str()); - if (offline) - guid = objmgr.GetPlayerGUIDByName(name.c_str()); - } - } - - if (plr) - { - group = plr->GetGroup(); - if (!guid || !offline) - guid = plr->GetGUID(); - } - else - { - if (getSelectedPlayer()) - plr = getSelectedPlayer(); - else - plr = m_session->GetPlayer(); - - if (!guid || !offline) - guid = plr->GetGUID(); - group = plr->GetGroup(); - } - - return true; -} - -LocaleConstant CliHandler::GetSessionDbcLocale() const -{ - return sWorld.GetDefaultDbcLocale(); -} - -int CliHandler::GetSessionDbLocaleIndex() const -{ - return objmgr.GetDBCLocaleIndex(); -} diff --git a/src/server/game/Chat.h b/src/server/game/Chat.h deleted file mode 100644 index 58e6f6214c8..00000000000 --- a/src/server/game/Chat.h +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITYCORE_CHAT_H -#define TRINITYCORE_CHAT_H - -#include "SharedDefines.h" - -class ChatHandler; -class WorldSession; -class Creature; -class Player; -class Unit; -struct GameTele; - -class ChatCommand -{ - public: - const char * Name; - uint32 SecurityLevel; // function pointer required correct align (use uint32) - bool AllowConsole; - bool (ChatHandler::*Handler)(const char* args); - std::string Help; - ChatCommand * ChildCommands; -}; - -class ChatHandler -{ - public: - explicit ChatHandler(WorldSession* session) : m_session(session) {} - explicit ChatHandler(Player* player) : m_session(player->GetSession()) {} - ~ChatHandler() {} - - static void FillMessageData(WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker); - - void FillMessageData(WorldPacket *data, uint8 type, uint32 language, uint64 target_guid, const char* message) - { - FillMessageData(data, m_session, type, language, NULL, target_guid, message, NULL); - } - - void FillSystemMessageData(WorldPacket *data, const char* message) - { - FillMessageData(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, 0, message); - } - - static char* LineFromMessage(char*& pos) { char* start = strtok(pos,"\n"); pos = NULL; return start; } - - // function with different implementation for chat/console - virtual const char *GetTrinityString(int32 entry) const; - virtual void SendSysMessage(const char *str); - - void SendSysMessage(int32 entry); - void PSendSysMessage(const char *format, ...) ATTR_PRINTF(2,3); - void PSendSysMessage(int32 entry, ...); - std::string PGetParseString(int32 entry, ...); - - int ParseCommands(const char* text); - - static ChatCommand* getCommandTable(); - - bool isValidChatMessage(const char* msg); - void SendGlobalSysMessage(const char *str); - protected: - explicit ChatHandler() : m_session(NULL) {} // for CLI subclass - - bool hasStringAbbr(const char* name, const char* part); - - // function with different implementation for chat/console - virtual bool isAvailable(ChatCommand const& cmd) const; - virtual std::string GetNameLink() const { return GetNameLink(m_session->GetPlayer()); } - virtual bool needReportToTarget(Player* chr) const; - virtual LocaleConstant GetSessionDbcLocale() const; - virtual int GetSessionDbLocaleIndex() const; - - bool HasLowerSecurity(Player* target, uint64 guid, bool strong = false); - bool HasLowerSecurityAccount(WorldSession* target, uint32 account, bool strong = false); - - void SendGlobalGMSysMessage(const char *str); - - static bool SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand); - bool ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcommand); - bool ShowHelpForCommand(ChatCommand *table, const char* cmd); - bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd); - - bool HandleAccountCommand(const char* args); - bool HandleAccountAddonCommand(const char* args); - bool HandleAccountCreateCommand(const char* args); - bool HandleAccountDeleteCommand(const char* args); - bool HandleAccountLockCommand(const char* args); - bool HandleAccountOnlineListCommand(const char* args); - bool HandleAccountPasswordCommand(const char* args); - bool HandleAccountSetAddonCommand(const char* args); - bool HandleAccountSetGmLevelCommand(const char* args); - bool HandleAccountSetPasswordCommand(const char* args); - - bool HandleAHBotOptionsCommand(const char * args); - bool HandleNameAnnounceCommand(const char* args); - bool HandleGMNameAnnounceCommand(const char* args); - bool HandleGMAnnounceCommand(const char* args); - bool HandleGMNotifyCommand(const char* args); - - bool HandleBanAccountCommand(const char* args); - bool HandleBanCharacterCommand(const char* args); - bool HandleBanIPCommand(const char* args); - bool HandleBanInfoAccountCommand(const char* args); - bool HandleBanInfoCharacterCommand(const char* args); - bool HandleBanInfoIPCommand(const char* args); - bool HandleBanListAccountCommand(const char* args); - bool HandleBanListCharacterCommand(const char* args); - bool HandleBanListIPCommand(const char* args); - - bool HandleCastCommand(const char *args); - bool HandleCastBackCommand(const char *args); - bool HandleCastDistCommand(const char *args); - bool HandleCastSelfCommand(const char *args); - bool HandleCastTargetCommand(const char *args); - - bool HandleCharacterCustomizeCommand(const char * args); - bool HandleCharacterDeleteCommand(const char* args); - bool HandleCharacterLevelCommand(const char* args); - bool HandleCharacterRenameCommand(const char * args); - bool HandleCharacterReputationCommand(const char* args); - bool HandleCharacterTitlesCommand(const char* args); - - bool HandleChannelSetPublic(const char *args); - - bool HandleDebugAnimCommand(const char* args); - bool HandleDebugArenaCommand(const char * args); - bool HandleDebugBattlegroundCommand(const char * args); - bool HandleDebugGetItemStateCommand(const char * args); - bool HandleDebugGetLootRecipientCommand(const char * args); - bool HandleDebugGetValueCommand(const char* args); - bool HandleDebugGetItemValueCommand(const char* args); - bool HandleDebugMod32ValueCommand(const char* args); - bool HandleDebugSetAuraStateCommand(const char * args); - bool HandleDebugSetItemValueCommand(const char * args); - bool HandleDebugItemExpireCommand(const char * args); - bool HandleDebugSetVehicleId(const char * args); - bool HandleDebugEnterVehicle(const char * args); - bool HandleDebugSetValueCommand(const char* args); - bool HandleDebugSpawnVehicle(const char * args); - bool HandleDebugSpellCheckCommand(const char* args); - bool HandleDebugUpdateCommand(const char* args); - bool HandleDebugUpdateWorldStateCommand(const char* args); - - bool HandleDebugSet32Bit(const char* args); - bool HandleDebugThreatList(const char * args); - bool HandleDebugHostileRefList(const char * args); - bool HandlePossessCommand(const char* args); - bool HandleUnPossessCommand(const char* args); - bool HandleBindSightCommand(const char* args); - bool HandleUnbindSightCommand(const char* args); - - bool HandleDebugPlayCinematicCommand(const char* args); - bool HandleDebugPlayMovieCommand(const char* args); - bool HandleDebugPlaySoundCommand(const char* args); - - bool HandleDebugSendBuyErrorCommand(const char* args); - bool HandleDebugSendChannelNotifyCommand(const char* args); - bool HandleDebugSendChatMsgCommand(const char* args); - bool HandleDebugSendEquipErrorCommand(const char* args); - bool HandleDebugSendLargePacketCommand(const char * args); - bool HandleDebugSendOpcodeCommand(const char* args); - bool HandleDebugSendPoiCommand(const char* args); - bool HandleDebugSendQuestPartyMsgCommand(const char* args); - bool HandleDebugSendQuestInvalidMsgCommand(const char* args); - bool HandleDebugSendSellErrorCommand(const char* args); - bool HandleDebugSendSetPhaseShiftCommand(const char * args); - bool HandleDebugSendSpellFailCommand(const char* args); - - bool HandleEventActiveListCommand(const char* args); - bool HandleEventStartCommand(const char* args); - bool HandleEventStopCommand(const char* args); - bool HandleEventInfoCommand(const char* args); - - bool HandleGameObjectAddCommand(const char* args); - bool HandleGameObjectDeleteCommand(const char* args); - bool HandleGOInfoCommand(const char* args); - bool HandleGameObjectMoveCommand(const char* args); - bool HandleGameObjectNearCommand(const char* args); - bool HandleGameObjectPhaseCommand(const char* args); - bool HandleGameObjectStateCommand(const char* args); - bool HandleGameObjectTargetCommand(const char* args); - bool HandleGameObjectTurnCommand(const char* args); - - bool HandleGMCommand(const char* args); - bool HandleGMChatCommand(const char* args); - bool HandleGMFlyCommand(const char* args); - bool HandleGMListFullCommand(const char* args); - bool HandleGMListIngameCommand(const char* args); - bool HandleGMVisibleCommand(const char* args); - - bool HandleGoCommand(const char* args); - bool HandleGoCreatureCommand(const char* args); - bool HandleGoGraveyardCommand(const char* args); - bool HandleGoGridCommand(const char* args); - bool HandleGoObjectCommand(const char* args); - bool HandleGoTaxinodeCommand(const char* args); - bool HandleGoTriggerCommand(const char* args); - bool HandleGoXYCommand(const char* args); - bool HandleGoXYZCommand(const char* args); - bool HandleGoZoneXYCommand(const char* args); - - bool HandleGoTicketCommand(const char* args); - - bool HandleGuildCreateCommand(const char* args); - bool HandleGuildInviteCommand(const char* args); - bool HandleGuildUninviteCommand(const char* args); - bool HandleGuildRankCommand(const char* args); - bool HandleGuildDeleteCommand(const char* args); - - bool HandleHonorAddCommand(const char* args); - bool HandleHonorAddKillCommand(const char* args); - bool HandleHonorUpdateCommand(const char* args); - - bool HandleInstanceListBindsCommand(const char* args); - bool HandleInstanceUnbindCommand(const char* args); - bool HandleInstanceStatsCommand(const char* args); - bool HandleInstanceSaveDataCommand(const char * args); - bool HandleInstanceOpenCommand(const char * args); - bool HandleInstanceCloseCommand(const char * args); - bool HandleInstanceOpenCloseCommand(const char * args, bool open); - - bool HandleLearnCommand(const char* args); - bool HandleLearnAllCommand(const char* args); - bool HandleLearnAllGMCommand(const char* args); - bool HandleLearnAllCraftsCommand(const char* args); - bool HandleLearnAllRecipesCommand(const char* args); - bool HandleLearnAllDefaultCommand(const char* args); - bool HandleLearnAllLangCommand(const char* args); - bool HandleLearnAllMyClassCommand(const char* args); - bool HandleLearnAllMyPetTalentsCommand(const char* args); - bool HandleLearnAllMySpellsCommand(const char* args); - bool HandleLearnAllMyTalentsCommand(const char* args); - - bool HandleListAurasCommand(const char * args); - bool HandleListCreatureCommand(const char* args); - bool HandleListItemCommand(const char* args); - bool HandleListObjectCommand(const char* args); - - bool HandleLookupAreaCommand(const char* args); - bool HandleLookupCreatureCommand(const char* args); - bool HandleLookupEventCommand(const char* args); - bool HandleLookupFactionCommand(const char * args); - bool HandleLookupItemCommand(const char * args); - bool HandleLookupItemSetCommand(const char * args); - bool HandleLookupObjectCommand(const char* args); - bool HandleLookupPlayerIpCommand(const char* args); - bool HandleLookupPlayerAccountCommand(const char* args); - bool HandleLookupPlayerEmailCommand(const char* args); - bool HandleLookupQuestCommand(const char* args); - bool HandleLookupSkillCommand(const char* args); - bool HandleLookupSpellCommand(const char* args); - bool HandleLookupTaxiNodeCommand(const char * args); - bool HandleLookupTeleCommand(const char * args); - bool HandleLookupMapCommand(const char* args); - bool HandleLookupTitleCommand(const char * args); - - bool HandleModifyHPCommand(const char* args); - bool HandleModifyManaCommand(const char* args); - bool HandleModifyRageCommand(const char* args); - bool HandleModifyRunicPowerCommand(const char* args); - bool HandleModifyEnergyCommand(const char* args); - bool HandleModifyMoneyCommand(const char* args); - bool HandleModifyASpeedCommand(const char* args); - bool HandleModifySpeedCommand(const char* args); - bool HandleModifyBWalkCommand(const char* args); - bool HandleModifyFlyCommand(const char* args); - bool HandleModifySwimCommand(const char* args); - bool HandleModifyScaleCommand(const char* args); - bool HandleModifyMountCommand(const char* args); - bool HandleModifyBitCommand(const char* args); - bool HandleModifyFactionCommand(const char* args); - bool HandleModifySpellCommand(const char* args); - bool HandleModifyTalentCommand (const char* args); - bool HandleModifyHonorCommand (const char* args); - bool HandleModifyRepCommand(const char* args); - bool HandleModifyArenaCommand(const char* args); - bool HandleModifyPhaseCommand(const char* args); - bool HandleModifyGenderCommand(const char* args); - - //-----------------------Npc Commands----------------------- - bool HandleNpcAddCommand(const char* args); - bool HandleNpcAddMoveCommand(const char* args); - bool HandleNpcAddVendorItemCommand(const char* args); - bool HandleNpcAllowMovementCommand(const char* args); - bool HandleNpcChangeEntryCommand(const char *args); - bool HandleNpcChangeLevelCommand(const char* args); - bool HandleNpcDeleteCommand(const char* args); - bool HandleNpcDelVendorItemCommand(const char* args); - bool HandleNpcFactionIdCommand(const char* args); - bool HandleNpcFlagCommand(const char* args); - bool HandleNpcFollowCommand(const char* args); - bool HandleNpcInfoCommand(const char* args); - bool HandleNpcMoveCommand(const char* args); - bool HandleNpcPlayEmoteCommand(const char* args); - bool HandleNpcSayCommand(const char* args); - bool HandleNpcSetDeathStateCommand(const char* args); - bool HandleNpcSetModelCommand(const char* args); - bool HandleNpcSetMoveTypeCommand(const char* args); - bool HandleNpcSetPhaseCommand(const char* args); - bool HandleNpcSpawnDistCommand(const char* args); - bool HandleNpcSpawnTimeCommand(const char* args); - bool HandleNpcTameCommand(const char* args); - bool HandleNpcTextEmoteCommand(const char* args); - bool HandleNpcUnFollowCommand(const char* args); - bool HandleNpcWhisperCommand(const char* args); - bool HandleNpcYellCommand(const char* args); - bool HandleNpcAddFormationCommand(const char* args); - bool HandleNpcSetLinkCommand(const char* args); - - //TODO: NpcCommands that needs to be fixed : - bool HandleNpcAddWeaponCommand(const char* args); - bool HandleNpcNameCommand(const char* args); - bool HandleNpcSubNameCommand(const char* args); - //---------------------------------------------------------- - - bool HandlePDumpLoadCommand(const char *args); - bool HandlePDumpWriteCommand(const char *args); - - bool HandleQuestAdd(const char * args); - bool HandleQuestRemove(const char * args); - bool HandleQuestComplete(const char * args); - - bool HandleReloadAllCommand(const char* args); - bool HandleReloadAllAchievementCommand(const char* args); - bool HandleReloadAllAreaCommand(const char* args); - bool HandleReloadAllItemCommand(const char* args); - bool HandleReloadAllLootCommand(const char* args); - bool HandleReloadAllNpcCommand(const char* args); - bool HandleReloadAllQuestCommand(const char* args); - bool HandleReloadAllScriptsCommand(const char* args); - bool HandleReloadAllEventAICommand(const char* args); - bool HandleReloadAllSpellCommand(const char* args); - bool HandleReloadAllLocalesCommand(const char* args); - - bool HandleReloadConfigCommand(const char* args); - - bool HandleReloadAccessRequirementCommand(const char* args); - bool HandleReloadAchievementCriteriaDataCommand(const char* args); - bool HandleReloadAchievementRewardCommand(const char* args); - bool HandleReloadAreaTriggerTavernCommand(const char* args); - bool HandleReloadAreaTriggerTeleportCommand(const char* args); - bool HandleReloadAutobroadcastCommand(const char* args); - bool HandleReloadEventScriptsCommand(const char* args); - bool HandleReloadEventAITextsCommand(const char* args); - bool HandleReloadEventAISummonsCommand(const char* args); - bool HandleReloadEventAIScriptsCommand(const char* args); - bool HandleReloadCommandCommand(const char* args); - bool HandleReloadCreatureTemplateCommand(const char* args); - bool HandleReloadCreatureQuestRelationsCommand(const char* args); - bool HandleReloadCreatureQuestInvRelationsCommand(const char* args); - bool HandleReloadCreatureLinkedRespawnCommand(const char* args); - bool HandleReloadDbScriptStringCommand(const char* args); - bool HandleReloadGameGraveyardZoneCommand(const char* args); - bool HandleReloadGameObjectScriptsCommand(const char* args); - bool HandleReloadGameTeleCommand(const char* args); - bool HandleReloadGossipMenuCommand(const char* args); - bool HandleReloadGossipMenuOptionCommand(const char* args); - bool HandleReloadGOQuestRelationsCommand(const char* args); - bool HandleReloadGOQuestInvRelationsCommand(const char* args); - bool HandleReloadItemEnchantementsCommand(const char* args); - bool HandleReloadLocalesAchievementRewardCommand(const char* args); - bool HandleReloadLocalesCreatureCommand(const char* args); - bool HandleReloadLocalesGameobjectCommand(const char* args); - bool HandleReloadLocalesItemCommand(const char* args); - bool HandleReloadLocalesNpcTextCommand(const char* args); - bool HandleReloadLocalesPageTextCommand(const char* args); - bool HandleReloadLocalesPointsOfInterestCommand(const char* args); - bool HandleReloadLocalesQuestCommand(const char* args); -// bool HandleReloadAuctionsCommand(const char* args); - bool HandleReloadLootTemplatesCreatureCommand(const char* args); - bool HandleReloadLootTemplatesDisenchantCommand(const char* args); - bool HandleReloadLootTemplatesFishingCommand(const char* args); - bool HandleReloadLootTemplatesGameobjectCommand(const char* args); - bool HandleReloadLootTemplatesItemCommand(const char* args); - bool HandleReloadLootTemplatesMailCommand(const char* args); - bool HandleReloadMailLevelRewardCommand(const char* args); - bool HandleReloadLootTemplatesMillingCommand(const char* args); - bool HandleReloadLootTemplatesPickpocketingCommand(const char* args); - bool HandleReloadLootTemplatesProspectingCommand(const char* args); - bool HandleReloadLootTemplatesReferenceCommand(const char* args); - bool HandleReloadLootTemplatesSkinningCommand(const char* args); - bool HandleReloadLootTemplatesSpellCommand(const char* args); - bool HandleReloadTrinityStringCommand(const char* args); - bool HandleReloadNpcGossipCommand(const char* args); - bool HandleReloadNpcTrainerCommand(const char* args); - bool HandleReloadNpcVendorCommand(const char* args); - bool HandleReloadPageTextsCommand(const char* args); - bool HandleReloadPointsOfInterestCommand(const char* args); - bool HandleReloadSpellClickSpellsCommand(const char* args); - bool HandleReloadQuestAreaTriggersCommand(const char* args); - bool HandleReloadQuestEndScriptsCommand(const char* args); - bool HandleReloadQuestStartScriptsCommand(const char* args); - bool HandleReloadQuestTemplateCommand(const char* args); - bool HandleReloadReservedNameCommand(const char*); - bool HandleReloadSkillDiscoveryTemplateCommand(const char* args); - bool HandleReloadSkillExtraItemTemplateCommand(const char* args); - bool HandleReloadSkillFishingBaseLevelCommand(const char* args); - bool HandleReloadSpellRequiredCommand(const char* args); - bool HandleReloadSpellAreaCommand(const char* args); - bool HandleReloadSpellGroupsCommand(const char* args); - bool HandleReloadSpellLearnSpellCommand(const char* args); - bool HandleReloadSpellLinkedSpellCommand(const char* args); - bool HandleReloadSpellProcEventCommand(const char* args); - bool HandleReloadSpellBonusesCommand(const char* args); - bool HandleReloadSpellScriptsCommand(const char* args); - bool HandleReloadSpellTargetPositionCommand(const char* args); - bool HandleReloadSpellThreatsCommand(const char* args); - bool HandleReloadSpellPetAurasCommand(const char* args); - bool HandleReloadSpellDisabledCommand(const char* args); - bool HandleReloadSpellGroupStackRulesCommand(const char* args); - bool HandleReloadAuctionsCommand(const char* args); - bool HandleReloadWpScriptsCommand(const char* args); - bool HandleReloadConditions(const char* args); - - bool HandleResetAchievementsCommand(const char * args); - bool HandleResetAllCommand(const char * args); - bool HandleResetHonorCommand(const char * args); - bool HandleResetLevelCommand(const char * args); - bool HandleResetSpellsCommand(const char * args); - bool HandleResetStatsCommand(const char * args); - bool HandleResetTalentsCommand(const char * args); - - bool HandleSendItemsCommand(const char* args); - bool HandleSendMailCommand(const char* args); - bool HandleSendMessageCommand(const char * args); - bool HandleSendMoneyCommand(const char* args); - - bool HandleServerCorpsesCommand(const char* args); - bool HandleServerExitCommand(const char* args); - bool HandleServerIdleRestartCommand(const char* args); - bool HandleServerIdleShutDownCommand(const char* args); - bool HandleServerInfoCommand(const char* args); - bool HandleServerMotdCommand(const char* args); - bool HandleServerPLimitCommand(const char* args); - bool HandleServerRestartCommand(const char* args); - bool HandleServerSetLogLevelCommand(const char* args); - bool HandleServerSetMotdCommand(const char* args); - bool HandleServerShutDownCommand(const char* args); - bool HandleServerShutDownCancelCommand(const char* args); - bool HandleServerSetClosedCommand(const char* args); - - bool HandleServerSetLogFileLevelCommand(const char* args); - bool HandleServerSetDiffTimeCommand(const char* args); - - bool HandleTeleCommand(const char * args); - bool HandleTeleAddCommand(const char * args); - bool HandleTeleDelCommand(const char * args); - bool HandleTeleGroupCommand(const char* args); - bool HandleTeleNameCommand(const char* args); - - bool HandleTitlesAddCommand(const char* args); - bool HandleTitlesCurrentCommand(const char* args); - bool HandleTitlesRemoveCommand(const char* args); - bool HandleTitlesSetMaskCommand(const char* args); - - bool HandleUnBanAccountCommand(const char* args); - bool HandleUnBanCharacterCommand(const char* args); - bool HandleUnBanIPCommand(const char* args); - - bool HandleWpAddCommand(const char* args); - bool HandleWpLoadPathCommand(const char* args); - bool HandleWpUnLoadPathCommand(const char* args); - bool HandleWpModifyCommand(const char* args); - bool HandleWpEventCommand(const char* args); - bool HandleWpShowCommand(const char* args); - bool HandleReloadAllPaths(const char *args); - - bool HandleHelpCommand(const char* args); - bool HandleCommandsCommand(const char* args); - bool HandleStartCommand(const char* args); - bool HandleDismountCommand(const char* args); - bool HandleSaveCommand(const char* args); - - bool HandleNamegoCommand(const char* args); - bool HandleGonameCommand(const char* args); - bool HandleGroupgoCommand(const char* args); - bool HandleRecallCommand(const char* args); - bool HandleAnnounceCommand(const char* args); - bool HandleNotifyCommand(const char* args); - bool HandleGPSCommand(const char* args); - bool HandleTaxiCheatCommand(const char* args); - bool HandleWhispersCommand(const char* args); - bool HandleModifyDrunkCommand(const char* args); - - bool HandleGUIDCommand(const char* args); - bool HandleItemMoveCommand(const char* args); - bool HandleDeMorphCommand(const char* args); - bool HandlePInfoCommand(const char* args); - bool HandleMuteCommand(const char* args); - bool HandleUnmuteCommand(const char* args); - bool HandleMovegensCommand(const char* args); - bool HandleFreezeCommand(const char *args); - bool HandleUnFreezeCommand(const char *args); - bool HandleListFreezeCommand(const char* args); - - bool HandleCooldownCommand(const char* args); - bool HandleUnLearnCommand(const char* args); - bool HandleGetDistanceCommand(const char* args); - bool HandleModifyStandStateCommand(const char* args); - bool HandleDieCommand(const char* args); - bool HandleDamageCommand(const char *args); - bool HandleReviveCommand(const char* args); - bool HandleModifyMorphCommand(const char* args); - bool HandleAuraCommand(const char* args); - bool HandleUnAuraCommand(const char* args); - bool HandleLinkGraveCommand(const char* args); - bool HandleNearGraveCommand(const char* args); - bool HandleActivateObjectCommand(const char* args); - bool HandleSpawnTransportCommand(const char* args); - bool HandleExploreCheatCommand(const char* args); - bool HandleHoverCommand(const char* args); - bool HandleWaterwalkCommand(const char* args); - bool HandleLevelUpCommand(const char* args); - bool HandleShowAreaCommand(const char* args); - bool HandleHideAreaCommand(const char* args); - bool HandleAddItemCommand(const char* args); - bool HandleAddItemSetCommand(const char* args); - bool HandlePetTpCommand(const char* args); - bool HandlePetUnlearnCommand(const char* args); - bool HandlePetLearnCommand(const char* args); - bool HandleCreatePetCommand(const char* args); - - bool HandleGroupLeaderCommand(const char* args); - bool HandleGroupDisbandCommand(const char* args); - bool HandleGroupRemoveCommand(const char* args); - - bool HandleBankCommand(const char* args); - bool HandleChangeWeather(const char* args); - bool HandleKickPlayerCommand(const char * args); - - // GM ticket command handlers - bool HandleGMTicketListCommand(const char* args); - bool HandleGMTicketListOnlineCommand(const char* args); - bool HandleGMTicketListClosedCommand(const char* args); - bool HandleGMTicketGetByIdCommand(const char* args); - bool HandleGMTicketGetByNameCommand(const char* args); - bool HandleGMTicketCloseByIdCommand(const char* args); - bool HandleGMTicketAssignToCommand(const char* args); - bool HandleGMTicketUnAssignCommand(const char* args); - bool HandleGMTicketCommentCommand(const char* args); - bool HandleGMTicketDeleteByIdCommand(const char* args); - bool HandleGMTicketReloadCommand(const char*); - - bool HandleMaxSkillCommand(const char* args); - bool HandleSetSkillCommand(const char* args); - bool HandleRespawnCommand(const char* args); - bool HandleComeToMeCommand(const char *args); - bool HandleCombatStopCommand(const char *args); - - /*bool HandleCharDeleteCommand(const char *args); - bool HandleSendMessageCommand(const char * args);*/ - - bool HandleFlushArenaPointsCommand(const char *args); - bool HandlePlayAllCommand(const char* args); - bool HandleRepairitemsCommand(const char* args); - - bool HandleTempGameObjectCommand(const char* args); - bool HandleTempAddSpwCommand(const char* args); - - //! Development Commands - - /*bool HandleQuestAdd(const char * args); - bool HandleQuestRemove(const char * args); - bool HandleQuestComplete(const char * args);*/ - - //bool HandleSet32Bit(const char* args); - bool HandleSaveAllCommand(const char* args); - - Player* getSelectedPlayer(); - Creature* getSelectedCreature(); - Unit* getSelectedUnit(); - WorldObject* getSelectedObject(); - - char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL); - char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL); - - // if args have single value then it return in arg2 and arg1 == NULL - void extractOptFirstArg(char* args, char** arg1, char** arg2); - char* extractQuotedArg(char* args); - - uint32 extractSpellIdFromLink(char* text); - uint64 extractGuidFromLink(char* text); - GameTele const* extractGameTeleFromLink(char* text); - bool GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline = false); - std::string extractPlayerNameFromLink(char* text); - // select by arg (name/link) or in-game selection online/offline player - bool extractPlayerTarget(char* args, Player** player, uint64* player_guid = NULL, std::string* player_name = NULL); - - std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; } - std::string GetNameLink(Player* chr) const { return playerLink(chr->GetName()); } - - GameObject* GetNearbyGameObject(); - GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry); - - // Utility methods for commands - bool LookupPlayerSearchCommand(QueryResult_AutoPtr result, int32 limit); - bool HandleBanListHelper(QueryResult_AutoPtr result); - bool HandleBanHelper(BanMode mode,char const* args); - bool HandleBanInfoHelper(uint32 accountid, char const* accountname); - bool HandleUnBanHelper(BanMode mode,char const* args); - void HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel); - void HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id); - - void SetSentErrorMessage(bool val){ sentErrorMessage = val;}; - private: - 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 -{ - public: - typedef void Print(char const*); - explicit CliHandler(Print* zprint) : m_print(zprint) {} - - // overwrite functions - const char *GetTrinityString(int32 entry) const; - bool isAvailable(ChatCommand const& cmd) const; - void SendSysMessage(const char *str); - std::string GetNameLink() const; - bool needReportToTarget(Player* chr) const; - LocaleConstant GetSessionDbcLocale() const; - int GetSessionDbLocaleIndex() const; - - private: - Print* m_print; -}; - -char const *fmtstring(char const *format, ...); - -#endif - diff --git a/src/server/game/Chat/Channel.cpp b/src/server/game/Chat/Channel.cpp new file mode 100644 index 00000000000..0047892972b --- /dev/null +++ b/src/server/game/Chat/Channel.cpp @@ -0,0 +1,1102 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Channel.h" +#include "Chat.h" +#include "ObjectMgr.h" +#include "SocialMgr.h" +#include "World.h" + +Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team) + : m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0), m_Team(Team) +{ + // set special flags if built-in channel + ChatChannelsEntry const* ch = GetChannelEntryFor(channel_id); + if (ch) // it's built-in channel + { + channel_id = ch->ChannelID; // built-in channel + m_announce = false; // no join/leave announces + + m_flags |= CHANNEL_FLAG_GENERAL; // for all built-in channels + + if (ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel + m_flags |= CHANNEL_FLAG_TRADE; + + if (ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels + m_flags |= CHANNEL_FLAG_CITY; + + if (ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel + m_flags |= CHANNEL_FLAG_LFG; + else // for all other channels + m_flags |= CHANNEL_FLAG_NOT_LFG; + m_IsSaved = false; + } + else // it's custom channel + { + m_flags |= CHANNEL_FLAG_CUSTOM; + //load not built in channel if saved + std::string _name(name); + CharacterDatabase.escape_string(_name); + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT m_announce, m_moderate, m_public, m_password, BannedList FROM channels WHERE m_name = '%s' AND m_team = '%u'", _name.c_str(), m_Team); + if (result)//load + { + Field *fields = result->Fetch(); + m_announce = fields[0].GetBool(); + m_moderate = fields[1].GetBool(); + m_public = fields[2].GetBool(); + m_password = fields[3].GetString(); + const char* db_BannedList = fields[4].GetString(); + + m_IsSaved = true; + + if (db_BannedList) + { + Tokens tokens = StrSplit(db_BannedList, " "); + Tokens::iterator iter; + for (iter = tokens.begin(); iter != tokens.end(); ++iter) + { + uint64 banned_guid = atol((*iter).c_str()); + if (banned_guid) + { + sLog.outDebug("Channel(%s) loaded banned guid: %u",name.c_str(), banned_guid); + banned.insert(banned_guid); + } + } + } + } + else // save + { + // _name is already escaped at this point. + if (CharacterDatabase.PExecute("INSERT INTO channels (m_name, m_team, m_announce, m_moderate, m_public, m_password) " + "VALUES ('%s', '%u', '1', '0', '1', '')", _name.c_str(), m_Team)) + { + sLog.outDebug("New Channel(%s) saved", name.c_str()); + m_IsSaved = true; + } + } + } +} + +bool Channel::_UpdateStringInDB(const std::string& colName, const std::string& colValue) const +{ + // Prevent SQL-injection + std::string _name(m_name); + std::string _colValue(colValue); + CharacterDatabase.escape_string(_colValue); + CharacterDatabase.escape_string(_name); + return CharacterDatabase.PExecute("UPDATE channels SET %s = '%s' WHERE m_name = '%s' AND m_team = '%u'", + colName.c_str(), _colValue.c_str(), _name.c_str(), m_Team); +} + +bool Channel::_UpdateIntInDB(const std::string& colName, int colValue) const +{ + // Prevent SQL-injection + std::string _name(m_name); + CharacterDatabase.escape_string(_name); + return CharacterDatabase.PExecute("UPDATE channels SET %s = '%u' WHERE m_name = '%s' AND m_team = '%u'", + colName.c_str(), colValue, _name.c_str(), m_Team); +} + +void Channel::_UpdateBanListInDB() const +{ + // save banlist + if (m_IsSaved) + { + std::ostringstream banlist; + BannedList::const_iterator iter; + for (iter = banned.begin(); iter != banned.end(); ++iter) + banlist << (*iter) << " "; + std::string banListStr = banlist.str(); + if (_UpdateStringInDB("BannedList", banListStr)) + sLog.outDebug("Channel(%s) BannedList saved", m_name.c_str()); + } +} + +void Channel::Join(uint64 p, const char *pass) +{ + WorldPacket data; + if (IsOn(p)) + { + if (!IsConstant()) // non send error message for built-in channels + { + MakePlayerAlreadyMember(&data, p); + SendToOne(&data, p); + } + return; + } + + if (IsBanned(p)) + { + MakeBanned(&data); + SendToOne(&data, p); + return; + } + + if (m_password.length() > 0 && strcmp(pass, m_password.c_str())) + { + MakeWrongPassword(&data); + SendToOne(&data, p); + return; + } + + Player *plr = objmgr.GetPlayer(p); + + if (plr) + { + if (HasFlag(CHANNEL_FLAG_LFG) && + sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER && plr->GetGroup()) + { + MakeNotInLfg(&data); + SendToOne(&data, p); + return; + } + + if (plr->GetGuildId() && (GetFlags() == 0x38)) + return; + + plr->JoinedChannel(this); + } + + if (m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL))) + { + MakeJoined(&data, p); + SendToAll(&data); + } + + data.clear(); + + PlayerInfo pinfo; + pinfo.player = p; + pinfo.flags = MEMBER_FLAG_NONE; + players[p] = pinfo; + + MakeYouJoined(&data); + SendToOne(&data, p); + + JoinNotify(p); + + // if no owner first logged will become + if (!IsConstant() && !m_ownerGUID) + { + SetOwner(p, (players.size() > 1 ? true : false)); + players[p].SetModerator(true); + } + /* + else if (!IsConstant() && m_ownerGUID && plr && m_ownerGUID == plr->GetGUID())) + { + SetOwner(p, (players.size() > 1 ? true : false)); + players[p].SetModerator(true); + }*/ +} + +void Channel::Leave(uint64 p, bool send) +{ + if (!IsOn(p)) + { + if (send) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + } + else + { + Player *plr = objmgr.GetPlayer(p); + + if (send) + { + WorldPacket data; + MakeYouLeft(&data); + SendToOne(&data, p); + if (plr) + plr->LeftChannel(this); + data.clear(); + } + + bool changeowner = players[p].IsOwner(); + + players.erase(p); + if (m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL))) + { + WorldPacket data; + MakeLeft(&data, p); + SendToAll(&data); + } + + LeaveNotify(p); + + if (changeowner) + { + uint64 newowner = !players.empty() ? players.begin()->second.player : 0; + players[newowner].SetModerator(true); + SetOwner(newowner); + } + } +} + +void Channel::KickOrBan(uint64 good, const char *badname, bool ban) +{ + AccountTypes sec = SEC_PLAYER; + Player *gplr = objmgr.GetPlayer(good); + if (gplr) + sec = gplr->GetSession()->GetSecurity(); + + if (!IsOn(good)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, good); + } + else if (!players[good].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, good); + } + else + { + Player *bad = objmgr.GetPlayer(badname); + if (bad == NULL || !IsOn(bad->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, badname); + SendToOne(&data, good); + } + else if (sec < SEC_GAMEMASTER && bad->GetGUID() == m_ownerGUID && good != m_ownerGUID) + { + WorldPacket data; + MakeNotOwner(&data); + SendToOne(&data, good); + } + else + { + bool changeowner = (m_ownerGUID == bad->GetGUID()); + + WorldPacket data; + + if (ban && !IsBanned(bad->GetGUID())) + { + banned.insert(bad->GetGUID()); + MakePlayerBanned(&data, bad->GetGUID(), good); + _UpdateBanListInDB(); + + } + else + MakePlayerKicked(&data, bad->GetGUID(), good); + + SendToAll(&data); + players.erase(bad->GetGUID()); + bad->LeftChannel(this); + + if (changeowner) + { + uint64 newowner = !players.empty() ? good : false; + players[newowner].SetModerator(true); + SetOwner(newowner); + } + } + } +} + +void Channel::UnBan(uint64 good, const char *badname) +{ + uint32 sec = 0; + Player *gplr = objmgr.GetPlayer(good); + if (gplr) + sec = gplr->GetSession()->GetSecurity(); + + if (!IsOn(good)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, good); + } + else if (!players[good].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, good); + } + else + { + Player *bad = objmgr.GetPlayer(badname); + if (bad == NULL || !IsBanned(bad->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, badname); + SendToOne(&data, good); + } + else + { + banned.erase(bad->GetGUID()); + + WorldPacket data; + MakePlayerUnbanned(&data, bad->GetGUID(), good); + SendToAll(&data); + //save banlist + _UpdateBanListInDB(); + } + } +} + +void Channel::Password(uint64 p, const char *pass) +{ + std::string plName; + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if (plr) + sec = plr->GetSession()->GetSecurity(); + + ChatHandler chat(plr); + + if (!m_public && sec <= SEC_MODERATOR) + { + chat.PSendSysMessage(LANG_CHANNEL_NOT_PUBLIC); + return; + } + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + m_password = pass; + + WorldPacket data; + MakePasswordChanged(&data, p); + SendToAll(&data); + if (m_IsSaved && _UpdateStringInDB("m_password", m_password)) + sLog.outDebug("Channel(%s) password saved", m_name.c_str()); + } +} + +void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set) +{ + Player *plr = objmgr.GetPlayer(p); + if (!plr) + return; + + uint32 sec = plr->GetSession()->GetSecurity(); + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + Player *newp = objmgr.GetPlayer(p2n); + if (!newp) + { + WorldPacket data; + MakePlayerNotFound(&data, p2n); + SendToOne(&data, p); + return; + } + + if (p == m_ownerGUID && newp->GetGUID() == m_ownerGUID && mod) + return; + + if (!IsOn(newp->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, p2n); + SendToOne(&data, p); + return; + } + + // allow make moderator from another team only if both is GMs + // at this moment this only way to show channel post for GM from another team + if ((plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || newp->GetSession()->GetSecurity() < SEC_GAMEMASTER) && + plr->GetTeam() != newp->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + { + WorldPacket data; + MakePlayerNotFound(&data, p2n); + SendToOne(&data, p); + return; + } + + if (m_ownerGUID == newp->GetGUID() && m_ownerGUID != p) + { + WorldPacket data; + MakeNotOwner(&data); + SendToOne(&data, p); + return; + } + + if (mod) + SetModerator(newp->GetGUID(), set); + else + SetMute(newp->GetGUID(), set); + } +} + +void Channel::SetOwner(uint64 p, const char *newname) +{ + Player *plr = objmgr.GetPlayer(p); + if (!plr) + return; + + uint32 sec = plr->GetSession()->GetSecurity(); + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + return; + } + + if (sec < SEC_GAMEMASTER && p != m_ownerGUID) + { + WorldPacket data; + MakeNotOwner(&data); + SendToOne(&data, p); + return; + } + + Player *newp = objmgr.GetPlayer(newname); + if (newp == NULL || !IsOn(newp->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, newname); + SendToOne(&data, p); + return; + } + + if (newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + { + WorldPacket data; + MakePlayerNotFound(&data, newname); + SendToOne(&data, p); + return; + } + + players[newp->GetGUID()].SetModerator(true); + SetOwner(newp->GetGUID()); +} + +void Channel::SendWhoOwner(uint64 p) +{ + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else + { + WorldPacket data; + MakeChannelOwner(&data); + SendToOne(&data, p); + } +} + +void Channel::List(Player* player) +{ + uint64 p = player->GetGUID(); + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else + { + WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+players.size()*(8+1)); + data << uint8(1); // channel type? + data << GetName(); // channel name + data << uint8(GetFlags()); // channel flags? + + size_t pos = data.wpos(); + data << uint32(0); // size of list, placeholder + + uint32 gmLevelInWhoList = sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); + + uint32 count = 0; + for (PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + { + Player *plr = objmgr.GetPlayer(i->first); + + // PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters + // MODERATOR, GAME MASTER, ADMINISTRATOR can see all + if (plr && (player->GetSession()->GetSecurity() > SEC_PLAYER || plr->GetSession()->GetSecurity() <= gmLevelInWhoList) && + plr->IsVisibleGloballyFor(player)) + { + data << uint64(i->first); + data << uint8(i->second.flags); // flags seems to be changed... + ++count; + } + } + + data.put(pos,count); + + SendToOne(&data, p); + } +} + +void Channel::Announce(uint64 p) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if (plr) + sec = plr->GetSession()->GetSecurity(); + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + m_announce = !m_announce; + + WorldPacket data; + if (m_announce) + MakeAnnouncementsOn(&data, p); + else + MakeAnnouncementsOff(&data, p); + SendToAll(&data); + if (m_IsSaved && _UpdateIntInDB("m_announce", m_announce ? 1 : 0)) + sLog.outDebug("Channel(%s) announce saved", m_name.c_str()); + + } +} + +void Channel::Moderate(uint64 p) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if (plr) + sec = plr->GetSession()->GetSecurity(); + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if (!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + m_moderate = !m_moderate; + + WorldPacket data; + if (m_moderate) + MakeModerationOn(&data, p); + else + MakeModerationOff(&data, p); + SendToAll(&data); + if (m_IsSaved && _UpdateIntInDB("m_announce", m_announce ? 1 : 0)) + sLog.outDebug("Channel(%s) announce saved", m_name.c_str()); + } +} + +void Channel::Say(uint64 p, const char *what, uint32 lang) +{ + if (!what) + return; + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + lang = LANG_UNIVERSAL; + + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if (plr) + sec = plr->GetSession()->GetSecurity(); + + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if (players[p].IsMuted()) + { + WorldPacket data; + MakeMuted(&data); + SendToOne(&data, p); + } + else if (m_moderate && !players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + uint32 messageLength = strlen(what) + 1; + + WorldPacket data(SMSG_MESSAGECHAT, 1+4+8+4+m_name.size()+1+8+4+messageLength+1); + data << (uint8)CHAT_MSG_CHANNEL; + data << (uint32)lang; + data << p; // 2.1.0 + data << uint32(0); // 2.1.0 + data << m_name; + data << p; + data << messageLength; + data << what; + data << uint8(plr ? plr->chatTag() : 0); + + SendToAll(&data, !players[p].IsModerator() ? p : false); + } +} + +void Channel::Invite(uint64 p, const char *newname) +{ + if (!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + return; + } + + Player *newp = objmgr.GetPlayer(newname); + if (!newp) + { + WorldPacket data; + MakePlayerNotFound(&data, newname); + SendToOne(&data, p); + return; + } + + Player *plr = objmgr.GetPlayer(p); + if (!plr) + return; + + if (newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + { + WorldPacket data; + MakeInviteWrongFaction(&data); + SendToOne(&data, p); + return; + } + + if (IsOn(newp->GetGUID())) + { + WorldPacket data; + MakePlayerAlreadyMember(&data, newp->GetGUID()); + SendToOne(&data, p); + return; + } + + WorldPacket data; + if (!newp->GetSocial()->HasIgnore(GUID_LOPART(p))) + { + MakeInvite(&data, p); + SendToOne(&data, newp->GetGUID()); + data.clear(); + } + MakePlayerInvited(&data, newp->GetName()); + SendToOne(&data, p); +} + +void Channel::SetOwner(uint64 guid, bool exclaim) +{ + if (m_ownerGUID) + { + // [] will re-add player after it possible removed + PlayerList::iterator p_itr = players.find(m_ownerGUID); + if (p_itr != players.end()) + p_itr->second.SetOwner(false); + } + + m_ownerGUID = guid; + if (m_ownerGUID) + { + uint8 oldFlag = GetPlayerFlags(m_ownerGUID); + players[m_ownerGUID].SetModerator(true); + players[m_ownerGUID].SetOwner(true); + + WorldPacket data; + MakeModeChange(&data, m_ownerGUID, oldFlag); + SendToAll(&data); + + if (exclaim) + { + MakeOwnerChanged(&data, m_ownerGUID); + SendToAll(&data); + } + if (m_IsSaved && _UpdateIntInDB("m_moderate", m_moderate ? 1 : 0)) + sLog.outDebug("Channel(%s) moderate saved", m_name.c_str()); + + } +} + +void Channel::SendToAll(WorldPacket *data, uint64 p) +{ + for (PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + { + Player *plr = objmgr.GetPlayer(i->first); + if (plr) + { + if (!p || !plr->GetSocial()->HasIgnore(GUID_LOPART(p))) + plr->GetSession()->SendPacket(data); + } + } +} + +void Channel::SendToAllButOne(WorldPacket *data, uint64 who) +{ + for (PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + { + if (i->first != who) + { + Player *plr = objmgr.GetPlayer(i->first); + if (plr) + plr->GetSession()->SendPacket(data); + } + } +} + +void Channel::SendToOne(WorldPacket *data, uint64 who) +{ + Player *plr = objmgr.GetPlayer(who); + if (plr) + plr->GetSession()->SendPacket(data); +} + +void Channel::Voice(uint64 /*guid1*/, uint64 /*guid2*/) +{ + +} + +void Channel::DeVoice(uint64 /*guid1*/, uint64 /*guid2*/) +{ + +} + +// done +void Channel::MakeNotifyPacket(WorldPacket *data, uint8 notify_type) +{ + data->Initialize(SMSG_CHANNEL_NOTIFY, 1+m_name.size()+1); + *data << uint8(notify_type); + *data << m_name; +} + +// done 0x00 +void Channel::MakeJoined(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_JOINED_NOTICE); + *data << uint64(guid); +} + +// done 0x01 +void Channel::MakeLeft(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_LEFT_NOTICE); + *data << uint64(guid); +} + +// done 0x02 +void Channel::MakeYouJoined(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE); + *data << uint8(GetFlags()); + *data << uint32(GetChannelId()); + *data << uint32(0); +} + +// done 0x03 +void Channel::MakeYouLeft(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE); + *data << uint32(GetChannelId()); + *data << uint8(0); // can be 0x00 and 0x01 +} + +// done 0x04 +void Channel::MakeWrongPassword(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE); +} + +// done 0x05 +void Channel::MakeNotMember(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE); +} + +// done 0x06 +void Channel::MakeNotModerator(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE); +} + +// done 0x07 +void Channel::MakePasswordChanged(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE); + *data << uint64(guid); +} + +// done 0x08 +void Channel::MakeOwnerChanged(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE); + *data << uint64(guid); +} + +// done 0x09 +void Channel::MakePlayerNotFound(WorldPacket *data, const std::string& name) +{ + MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE); + *data << name; +} + +// done 0x0A +void Channel::MakeNotOwner(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE); +} + +// done 0x0B +void Channel::MakeChannelOwner(WorldPacket *data) +{ + std::string name = ""; + + if (!objmgr.GetPlayerNameByGUID(m_ownerGUID, name) || name.empty()) + name = "PLAYER_NOT_FOUND"; + + MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE); + *data << ((IsConstant() || !m_ownerGUID) ? "Nobody" : name); +} + +// done 0x0C +void Channel::MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags) +{ + MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE); + *data << uint64(guid); + *data << uint8(oldflags); + *data << uint8(GetPlayerFlags(guid)); +} + +// done 0x0D +void Channel::MakeAnnouncementsOn(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE); + *data << uint64(guid); +} + +// done 0x0E +void Channel::MakeAnnouncementsOff(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE); + *data << uint64(guid); +} + +// done 0x0F +void Channel::MakeModerationOn(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_MODERATION_ON_NOTICE); + *data << uint64(guid); +} + +// done 0x10 +void Channel::MakeModerationOff(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_MODERATION_OFF_NOTICE); + *data << uint64(guid); +} + +// done 0x11 +void Channel::MakeMuted(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_MUTED_NOTICE); +} + +// done 0x12 +void Channel::MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good) +{ + MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE); + *data << uint64(bad); + *data << uint64(good); +} + +// done 0x13 +void Channel::MakeBanned(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_BANNED_NOTICE); +} + +// done 0x14 +void Channel::MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good) +{ + MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE); + *data << uint64(bad); + *data << uint64(good); +} + +// done 0x15 +void Channel::MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good) +{ + MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE); + *data << uint64(bad); + *data << uint64(good); +} + +// done 0x16 +void Channel::MakePlayerNotBanned(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE); + *data << uint64(guid); +} + +// done 0x17 +void Channel::MakePlayerAlreadyMember(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE); + *data << uint64(guid); +} + +// done 0x18 +void Channel::MakeInvite(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_INVITE_NOTICE); + *data << uint64(guid); +} + +// done 0x19 +void Channel::MakeInviteWrongFaction(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE); +} + +// done 0x1A +void Channel::MakeWrongFaction(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE); +} + +// done 0x1B +void Channel::MakeInvalidName(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE); +} + +// done 0x1C +void Channel::MakeNotModerated(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE); +} + +// done 0x1D +void Channel::MakePlayerInvited(WorldPacket *data, const std::string& name) +{ + MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE); + *data << name; +} + +// done 0x1E +void Channel::MakePlayerInviteBanned(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE); + *data << uint64(guid); +} + +// done 0x1F +void Channel::MakeThrottled(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE); +} + +// done 0x20 +void Channel::MakeNotInArea(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE); +} + +// done 0x21 +void Channel::MakeNotInLfg(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE); +} + +// done 0x22 +void Channel::MakeVoiceOn(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE); + *data << uint64(guid); +} + +// done 0x23 +void Channel::MakeVoiceOff(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE); + *data << uint64(guid); +} + +void Channel::JoinNotify(uint64 guid) +{ + WorldPacket data; + + if (IsConstant()) + data.Initialize(SMSG_USERLIST_ADD, 8+1+1+4+GetName().size()+1); + else + data.Initialize(SMSG_USERLIST_UPDATE, 8+1+1+4+GetName().size()+1); + + data << uint64(guid); + data << uint8(GetPlayerFlags(guid)); + data << uint8(GetFlags()); + data << uint32(GetNumPlayers()); + data << GetName(); + SendToAll(&data); +} + +void Channel::LeaveNotify(uint64 guid) +{ + WorldPacket data(SMSG_USERLIST_REMOVE, 8+1+4+GetName().size()+1); + data << uint64(guid); + data << uint8(GetFlags()); + data << uint32(GetNumPlayers()); + data << GetName(); + SendToAll(&data); +} + diff --git a/src/server/game/Chat/Channel.h b/src/server/game/Chat/Channel.h new file mode 100644 index 00000000000..d0b5923e30e --- /dev/null +++ b/src/server/game/Chat/Channel.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CHANNEL_H +#define _CHANNEL_H + +#include +#include +#include + +#include "Common.h" + +#include "Opcodes.h" +#include "Player.h" +#include "WorldPacket.h" + +enum ChatNotify +{ + CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel."; + CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel."; + //CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel."; + CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined + //CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]"; + CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left + CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s."; + CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s."; + CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s."; + CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s."; + CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s."; + CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found."; + CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner."; + CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s."; + CHAT_MODE_CHANGE_NOTICE = 0x0C, //? + CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s."; + CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s."; + CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s."; + CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s."; + CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak."; + CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s."; + CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel."; + CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s."; + CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s."; + CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned."; + CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel."; + CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'."; + CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s."; + CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s."; + CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name"; + CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated"; + CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel"; + CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned."; + CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message."; + CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone. + CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels. + CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s."; + CHAT_VOICE_OFF_NOTICE = 0x23, //+ "[%s] Channel voice disabled by %s."; +}; + +enum ChannelFlags +{ + CHANNEL_FLAG_NONE = 0x00, + CHANNEL_FLAG_CUSTOM = 0x01, + // 0x02 + CHANNEL_FLAG_TRADE = 0x04, + CHANNEL_FLAG_NOT_LFG = 0x08, + CHANNEL_FLAG_GENERAL = 0x10, + CHANNEL_FLAG_CITY = 0x20, + CHANNEL_FLAG_LFG = 0x40, + CHANNEL_FLAG_VOICE = 0x80 + // General 0x18 = 0x10 | 0x08 + // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04 + // LocalDefence 0x18 = 0x10 | 0x08 + // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08 + // LookingForGroup 0x50 = 0x40 | 0x10 +}; + +enum ChannelDBCFlags +{ + CHANNEL_DBC_FLAG_NONE = 0x00000, + CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG + CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment + CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense + CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade + CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment + CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment + CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense + CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment + CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup +}; + +enum ChannelMemberFlags +{ + MEMBER_FLAG_NONE = 0x00, + MEMBER_FLAG_OWNER = 0x01, + MEMBER_FLAG_MODERATOR = 0x02, + MEMBER_FLAG_VOICED = 0x04, + MEMBER_FLAG_MUTED = 0x08, + MEMBER_FLAG_CUSTOM = 0x10, + MEMBER_FLAG_MIC_MUTED = 0x20, + // 0x40 + // 0x80 +}; + +class Channel +{ + struct PlayerInfo + { + uint64 player; + uint8 flags; + + bool HasFlag(uint8 flag) { return flags & flag; } + void SetFlag(uint8 flag) { if (!HasFlag(flag)) flags |= flag; } + bool IsOwner() { return flags & MEMBER_FLAG_OWNER; } + void SetOwner(bool state) + { + if (state) flags |= MEMBER_FLAG_OWNER; + else flags &= ~MEMBER_FLAG_OWNER; + } + bool IsModerator() { return flags & MEMBER_FLAG_MODERATOR; } + void SetModerator(bool state) + { + if (state) flags |= MEMBER_FLAG_MODERATOR; + else flags &= ~MEMBER_FLAG_MODERATOR; + } + bool IsMuted() { return flags & MEMBER_FLAG_MUTED; } + void SetMuted(bool state) + { + if (state) flags |= MEMBER_FLAG_MUTED; + else flags &= ~MEMBER_FLAG_MUTED; + } + }; + + typedef std::map PlayerList; + PlayerList players; + typedef std::set BannedList; + BannedList banned; + bool m_announce; + bool m_moderate; + bool m_public; + std::string m_name; + std::string m_password; + uint8 m_flags; + uint32 m_channelId; + uint64 m_ownerGUID; + bool m_IsSaved; + + private: + // initial packet data (notify type and channel name) + void MakeNotifyPacket(WorldPacket *data, uint8 notify_type); + // type specific packet data + void MakeJoined(WorldPacket *data, uint64 guid); //+ 0x00 + void MakeLeft(WorldPacket *data, uint64 guid); //+ 0x01 + void MakeYouJoined(WorldPacket *data); //+ 0x02 + void MakeYouLeft(WorldPacket *data); //+ 0x03 + void MakeWrongPassword(WorldPacket *data); //? 0x04 + void MakeNotMember(WorldPacket *data); //? 0x05 + void MakeNotModerator(WorldPacket *data); //? 0x06 + void MakePasswordChanged(WorldPacket *data, uint64 guid); //+ 0x07 + void MakeOwnerChanged(WorldPacket *data, uint64 guid); //? 0x08 + void MakePlayerNotFound(WorldPacket *data, const std::string& name); //+ 0x09 + void MakeNotOwner(WorldPacket *data); //? 0x0A + void MakeChannelOwner(WorldPacket *data); //? 0x0B + void MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags); //+ 0x0C + void MakeAnnouncementsOn(WorldPacket *data, uint64 guid); //+ 0x0D + void MakeAnnouncementsOff(WorldPacket *data, uint64 guid); //+ 0x0E + void MakeModerationOn(WorldPacket *data, uint64 guid); //+ 0x0F + void MakeModerationOff(WorldPacket *data, uint64 guid); //+ 0x10 + void MakeMuted(WorldPacket *data); //? 0x11 + void MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good); //? 0x12 + void MakeBanned(WorldPacket *data); //? 0x13 + void MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x14 + void MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x15 + void MakePlayerNotBanned(WorldPacket *data, uint64 guid); //? 0x16 + void MakePlayerAlreadyMember(WorldPacket *data, uint64 guid); //+ 0x17 + void MakeInvite(WorldPacket *data, uint64 guid); //? 0x18 + void MakeInviteWrongFaction(WorldPacket *data); //? 0x19 + void MakeWrongFaction(WorldPacket *data); //? 0x1A + void MakeInvalidName(WorldPacket *data); //? 0x1B + void MakeNotModerated(WorldPacket *data); //? 0x1C + void MakePlayerInvited(WorldPacket *data, const std::string& name); //+ 0x1D + void MakePlayerInviteBanned(WorldPacket *data, uint64 guid); //? 0x1E + void MakeThrottled(WorldPacket *data); //? 0x1F + void MakeNotInArea(WorldPacket *data); //? 0x20 + void MakeNotInLfg(WorldPacket *data); //? 0x21 + void MakeVoiceOn(WorldPacket *data, uint64 guid); //+ 0x22 + void MakeVoiceOff(WorldPacket *data, uint64 guid); //+ 0x23 + + void SendToAll(WorldPacket *data, uint64 p = 0); + void SendToAllButOne(WorldPacket *data, uint64 who); + void SendToOne(WorldPacket *data, uint64 who); + + bool IsOn(uint64 who) const { return players.find(who) != players.end(); } + bool IsBanned(uint64 guid) const { return banned.find(guid) != banned.end(); } + + bool _UpdateStringInDB(const std::string& colName, const std::string& colValue) const; + bool _UpdateIntInDB(const std::string& colName, int colValue) const; + void _UpdateBanListInDB() const; + + uint8 GetPlayerFlags(uint64 p) const + { + PlayerList::const_iterator p_itr = players.find(p); + if (p_itr == players.end()) + return 0; + + return p_itr->second.flags; + } + + void SetModerator(uint64 p, bool set) + { + if (players[p].IsModerator() != set) + { + uint8 oldFlag = GetPlayerFlags(p); + players[p].SetModerator(set); + + WorldPacket data; + MakeModeChange(&data, p, oldFlag); + SendToAll(&data); + } + } + + void SetMute(uint64 p, bool set) + { + if (players[p].IsMuted() != set) + { + uint8 oldFlag = GetPlayerFlags(p); + players[p].SetMuted(set); + + WorldPacket data; + MakeModeChange(&data, p, oldFlag); + SendToAll(&data); + } + } + + public: + uint32 m_Team; + Channel(const std::string& name, uint32 channel_id, uint32 Team = 0); + std::string GetName() const { return m_name; } + uint32 GetChannelId() const { return m_channelId; } + bool IsConstant() const { return m_channelId != 0; } + bool IsAnnounce() const { return m_announce; } + bool IsLFG() const { return GetFlags() & CHANNEL_FLAG_LFG; } + std::string GetPassword() const { return m_password; } + void SetPassword(const std::string& npassword) { m_password = npassword; } + void SetAnnounce(bool nannounce) { m_announce = nannounce; } + uint32 GetNumPlayers() const { return players.size(); } + uint8 GetFlags() const { return m_flags; } + bool HasFlag(uint8 flag) { return m_flags & flag; } + + void Join(uint64 p, const char *pass); + void Leave(uint64 p, bool send = true); + void KickOrBan(uint64 good, const char *badname, bool ban); + void Kick(uint64 good, const char *badname) { KickOrBan(good, badname, false); } + void Ban(uint64 good, const char *badname) { KickOrBan(good, badname, true); } + void UnBan(uint64 good, const char *badname); + void Password(uint64 p, const char *pass); + void SetMode(uint64 p, const char *p2n, bool mod, bool set); + void SetOwner(uint64 p, bool exclaim = true); + void SetOwner(uint64 p, const char *newname); + void SendWhoOwner(uint64 p); + void SetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, true); } + void UnsetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, false); } + void SetMute(uint64 p, const char *newname) { SetMode(p, newname, false, true); } + void UnsetMute(uint64 p, const char *newname) { SetMode(p, newname, false, false); } + void List(Player* p); + void Announce(uint64 p); + void Moderate(uint64 p); + void Say(uint64 p, const char *what, uint32 lang); + void Invite(uint64 p, const char *newp); + void Voice(uint64 guid1, uint64 guid2); + void DeVoice(uint64 guid1, uint64 guid2); + void JoinNotify(uint64 guid); // invisible notify + void LeaveNotify(uint64 guid); // invisible notify +}; +#endif + diff --git a/src/server/game/Chat/ChannelHandler.cpp b/src/server/game/Chat/ChannelHandler.cpp new file mode 100644 index 00000000000..0f615579cb6 --- /dev/null +++ b/src/server/game/Chat/ChannelHandler.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Policies/SingletonImp.h" + +#include "ObjectMgr.h" // for normalizePlayerName +#include "ChannelMgr.h" + +void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + + uint32 channel_id; + uint8 unknown1, unknown2; + std::string channelname, pass; + + recvPacket >> channel_id >> unknown1 >> unknown2; + recvPacket >> channelname; + + if (channelname.empty()) + return; + + recvPacket >> pass; + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + cMgr->team = _player->GetTeam(); + if (Channel *chn = cMgr->GetJoinChannel(channelname, channel_id)) + chn->Join(_player->GetGUID(), pass.c_str()); + } +} + +void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + + uint32 unk; + std::string channelname; + recvPacket >> unk; // channel id? + recvPacket >> channelname; + + if (channelname.empty()) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Leave(_player->GetGUID(), true); + cMgr->LeftChannel(channelname); + } +} + +void WorldSession::HandleChannelList(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->List(_player); +} + +void WorldSession::HandleChannelPassword(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, pass; + recvPacket >> channelname; + + recvPacket >> pass; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Password(_player->GetGUID(), pass.c_str()); +} + +void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, newp; + recvPacket >> channelname; + + recvPacket >> newp; + + if (!normalizePlayerName(newp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SetOwner(_player->GetGUID(), newp.c_str()); +} + +void WorldSession::HandleChannelOwner(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SendWhoOwner(_player->GetGUID()); +} + +void WorldSession::HandleChannelModerator(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SetModerator(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->UnsetModerator(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelMute(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SetMute(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->UnsetMute(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelInvite(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Invite(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelKick(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Kick(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelBan(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Ban(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelUnban(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + + std::string channelname, otp; + recvPacket >> channelname; + + recvPacket >> otp; + + if (!normalizePlayerName(otp)) + return; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->UnBan(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Announce(_player->GetGUID()); +} + +void WorldSession::HandleChannelModerate(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Moderate(_player->GetGUID()); +} + +void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->List(_player); +} + +void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + { + WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, chn->GetName().size()+1+1+4); + data << chn->GetName(); + data << uint8(chn->GetFlags()); + data << uint32(chn->GetNumPlayers()); + SendPacket(&data); + } + } +} + +void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + std::string channelname; + recvPacket >> channelname; + /*if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->JoinNotify(_player->GetGUID());*/ +} + diff --git a/src/server/game/Chat/ChannelMgr.cpp b/src/server/game/Chat/ChannelMgr.cpp new file mode 100644 index 00000000000..f31d3ffde50 --- /dev/null +++ b/src/server/game/Chat/ChannelMgr.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ChannelMgr.h" +#include "Policies/SingletonImp.h" +#include "World.h" + +INSTANTIATE_SINGLETON_1(AllianceChannelMgr); +INSTANTIATE_SINGLETON_1(HordeChannelMgr); + +ChannelMgr* channelMgr(uint32 team) +{ + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + return &Trinity::Singleton::Instance(); // cross-faction + + if (team == ALLIANCE) + return &Trinity::Singleton::Instance(); + if (team == HORDE) + return &Trinity::Singleton::Instance(); + + return NULL; +} + +ChannelMgr::~ChannelMgr() +{ + for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr) + delete itr->second; + + channels.clear(); +} + +Channel *ChannelMgr::GetJoinChannel(std::string name, uint32 channel_id) +{ + std::wstring wname; + Utf8toWStr(name,wname); + wstrToLower(wname); + + if (channels.find(wname) == channels.end()) + { + Channel *nchan = new Channel(name,channel_id, team); + channels[wname] = nchan; + return nchan; + } + + return channels[wname]; +} + +Channel *ChannelMgr::GetChannel(std::string name, Player *p, bool pkt) +{ + std::wstring wname; + Utf8toWStr(name,wname); + wstrToLower(wname); + + ChannelMap::const_iterator i = channels.find(wname); + + if (i == channels.end()) + { + if (pkt) + { + WorldPacket data; + MakeNotOnPacket(&data,name); + p->GetSession()->SendPacket(&data); + } + + return NULL; + } + else + return i->second; +} + +void ChannelMgr::LeftChannel(std::string name) +{ + std::wstring wname; + Utf8toWStr(name,wname); + wstrToLower(wname); + + ChannelMap::const_iterator i = channels.find(wname); + + if (i == channels.end()) + return; + + Channel* channel = i->second; + + if (channel->GetNumPlayers() == 0 && !channel->IsConstant()) + { + channels.erase(wname); + delete channel; + } +} + +void ChannelMgr::MakeNotOnPacket(WorldPacket *data, std::string name) +{ + data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size + (*data) << (uint8)0x05 << name; +} diff --git a/src/server/game/Chat/ChannelMgr.h b/src/server/game/Chat/ChannelMgr.h new file mode 100644 index 00000000000..6f3b7c415ae --- /dev/null +++ b/src/server/game/Chat/ChannelMgr.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __TRINITY_CHANNELMGR_H +#define __TRINITY_CHANNELMGR_H + +#include "Common.h" +#include "Channel.h" +#include "Policies/Singleton.h" + +#include +#include + +#include "Policies/Singleton.h" + +#include "Channel.h" +#include "World.h" + +class ChannelMgr +{ + public: + uint32 team; + typedef std::map ChannelMap; + ChannelMgr() {team = 0;} + ~ChannelMgr(); + + Channel *GetJoinChannel(std::string name, uint32 channel_id); + Channel *GetChannel(std::string name, Player *p, bool pkt = true); + void LeftChannel(std::string name); + private: + ChannelMap channels; + void MakeNotOnPacket(WorldPacket *data, std::string name); +}; + +class AllianceChannelMgr : public ChannelMgr {}; +class HordeChannelMgr : public ChannelMgr {}; + +ChannelMgr* channelMgr(uint32 team); + +#endif + diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp new file mode 100644 index 00000000000..6e1adffa625 --- /dev/null +++ b/src/server/game/Chat/Chat.cpp @@ -0,0 +1,2458 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Database/DatabaseEnv.h" + +#include "AccountMgr.h" +#include "CellImpl.h" +#include "Chat.h" +#include "GridNotifiersImpl.h" +#include "Language.h" +#include "Log.h" +#include "Opcodes.h" +#include "Player.h" +#include "UpdateMask.h" +#include "SpellMgr.h" + +// Supported shift-links (client generated and server side) +// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r +// - client, item icon shift click, not used in server currently +// |color|Harea:area_id|h[name]|h|r +// |color|Hcreature:creature_guid|h[name]|h|r +// |color|Hcreature_entry:creature_id|h[name]|h|r +// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog +// |color|Hgameevent:id|h[name]|h|r +// |color|Hgameobject:go_guid|h[name]|h|r +// |color|Hgameobject_entry:go_id|h[name]|h|r +// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - client, at shift click in glyphs dialog, GlyphSlot.dbc, GlyphProperties.dbc +// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0:reporter_level|h[name]|h|r +// - client, item icon shift click +// |color|Hitemset:itemset_id|h[name]|h|r +// |color|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link +// |color|Hquest:quest_id:quest_level|h[name]|h|r - client, quest list name shift-click +// |color|Hskill:skill_id|h[name]|h|r +// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click +// |color|Htalent:talent_id,rank|h[name]|h|r - client, talent icon shift-click +// |color|Htaxinode:id|h[name]|h|r +// |color|Htele:id|h[name]|h|r +// |color|Htitle:id|h[name]|h|r +// |color|Htrade:spell_id,cur_value,max_value,unk3int,unk3str|h[name]|h|r - client, spellbook profession icon shift-click + +bool ChatHandler::load_command_table = true; + +ChatCommand * ChatHandler::getCommandTable() +{ + static ChatCommand accountSetCommandTable[] = + { + { "addon", SEC_ADMINISTRATOR, true, &ChatHandler::HandleAccountSetAddonCommand, "", NULL }, + { "gmlevel", SEC_CONSOLE, true, &ChatHandler::HandleAccountSetGmLevelCommand, "", NULL }, + { "password", SEC_CONSOLE, true, &ChatHandler::HandleAccountSetPasswordCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand accountCommandTable[] = + { + { "addon", SEC_MODERATOR, false, &ChatHandler::HandleAccountAddonCommand, "", NULL }, + { "create", SEC_CONSOLE, true, &ChatHandler::HandleAccountCreateCommand, "", NULL }, + { "delete", SEC_CONSOLE, true, &ChatHandler::HandleAccountDeleteCommand, "", NULL }, + { "onlinelist", SEC_CONSOLE, true, &ChatHandler::HandleAccountOnlineListCommand, "", NULL }, + { "lock", SEC_PLAYER, false, &ChatHandler::HandleAccountLockCommand, "", NULL }, + { "set", SEC_ADMINISTRATOR, true, NULL, "", accountSetCommandTable }, + { "password", SEC_PLAYER, false, &ChatHandler::HandleAccountPasswordCommand, "", NULL }, + { "", SEC_PLAYER, false, &ChatHandler::HandleAccountCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand banCommandTable[] = + { + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanIPCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand baninfoCommandTable[] = + { + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoIPCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand banlistCommandTable[] = + { + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListIPCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand castCommandTable[] = + { + { "back", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastBackCommand, "", NULL }, + { "dist", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastDistCommand, "", NULL }, + { "self", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastSelfCommand, "", NULL }, + { "target", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastTargetCommand, "", NULL }, + { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand characterCommandTable[] = + { + { "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL }, + { "delete", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeleteCommand, "", NULL }, + { "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterLevelCommand, "", NULL }, + { "rename", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterRenameCommand, "", NULL }, + { "reputation", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterReputationCommand, "", NULL }, + { "titles", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterTitlesCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand channelSetCommandTable[] = + { + { "public", SEC_ADMINISTRATOR, true, &ChatHandler::HandleChannelSetPublic, "", NULL }, + }; + + static ChatCommand channelCommandTable[] = + { + { "set", SEC_ADMINISTRATOR, true, NULL, "", channelSetCommandTable }, + }; + + static ChatCommand debugPlayCommandTable[] = + { + { "cinematic", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayCinematicCommand, "", NULL }, + { "movie", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayMovieCommand, "", NULL }, + { "sound", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlaySoundCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand debugSendCommandTable[] = + { + { "buyerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendBuyErrorCommand, "", NULL }, + { "channelnotify", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendChannelNotifyCommand, "", NULL }, + { "chatmmessage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendChatMsgCommand, "", NULL }, + { "equiperror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendEquipErrorCommand, "", NULL }, + { "largepacket", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendLargePacketCommand, "", NULL }, + { "opcode", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendOpcodeCommand, "", NULL }, + { "poi", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendPoiCommand, "", NULL }, + { "qpartymsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendQuestPartyMsgCommand, "", NULL }, + { "qinvalidmsg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendQuestInvalidMsgCommand, "", NULL }, + { "sellerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSellErrorCommand, "", NULL }, + { "setphaseshift", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSetPhaseShiftCommand, "", NULL }, + { "spellfail", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSpellFailCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand debugCommandTable[] = + { + { "setbit", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSet32Bit, "", NULL }, + { "threat", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugThreatList, "", NULL }, + { "hostil", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugHostileRefList, "", NULL }, + { "anim", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugAnimCommand, "", NULL }, + { "arena", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugArenaCommand, "", NULL }, + { "bg", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugBattlegroundCommand, "", NULL }, + { "getitemstate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemStateCommand, "", NULL }, + { "lootrecipient", SEC_GAMEMASTER, false, &ChatHandler::HandleDebugGetLootRecipientCommand, "", NULL }, + { "getvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetValueCommand, "", NULL }, + { "getitemvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugGetItemValueCommand, "", NULL }, + { "Mod32Value", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugMod32ValueCommand, "", NULL }, + { "play", SEC_MODERATOR, false, NULL, "", debugPlayCommandTable }, + { "send", SEC_ADMINISTRATOR, false, NULL, "", debugSendCommandTable }, + { "setaurastate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetAuraStateCommand, "", NULL }, + { "setitemvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetItemValueCommand, "", NULL }, + { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValueCommand, "", NULL }, + { "spawnvehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpawnVehicle, "", NULL }, + { "setvid", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetVehicleId, "", NULL }, + { "entervehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugEnterVehicle, "", NULL }, + { "uws", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateWorldStateCommand, "", NULL }, + { "update", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugUpdateCommand, "", NULL }, + { "itemexpire", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugItemExpireCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand eventCommandTable[] = + { + { "activelist", SEC_GAMEMASTER, true, &ChatHandler::HandleEventActiveListCommand, "", NULL }, + { "start", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStartCommand, "", NULL }, + { "stop", SEC_GAMEMASTER, true, &ChatHandler::HandleEventStopCommand, "", NULL }, + { "", SEC_GAMEMASTER, true, &ChatHandler::HandleEventInfoCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand gmCommandTable[] = + { + { "chat", SEC_MODERATOR, false, &ChatHandler::HandleGMChatCommand, "", NULL }, + { "fly", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGMFlyCommand, "", NULL }, + { "ingame", SEC_PLAYER, true, &ChatHandler::HandleGMListIngameCommand, "", NULL }, + { "list", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMListFullCommand, "", NULL }, + { "visible", SEC_MODERATOR, false, &ChatHandler::HandleGMVisibleCommand, "", NULL }, + { "", SEC_MODERATOR, false, &ChatHandler::HandleGMCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand goCommandTable[] = + { + { "creature", SEC_MODERATOR, false, &ChatHandler::HandleGoCreatureCommand, "", NULL }, + { "graveyard", SEC_MODERATOR, false, &ChatHandler::HandleGoGraveyardCommand, "", NULL }, + { "grid", SEC_MODERATOR, false, &ChatHandler::HandleGoGridCommand, "", NULL }, + { "object", SEC_MODERATOR, false, &ChatHandler::HandleGoObjectCommand, "", NULL }, + { "taxinode", SEC_MODERATOR, false, &ChatHandler::HandleGoTaxinodeCommand, "", NULL }, + { "trigger", SEC_MODERATOR, false, &ChatHandler::HandleGoTriggerCommand, "", NULL }, + { "zonexy", SEC_MODERATOR, false, &ChatHandler::HandleGoZoneXYCommand, "", NULL }, + { "xy", SEC_MODERATOR, false, &ChatHandler::HandleGoXYCommand, "", NULL }, + { "xyz", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, + { "ticket", SEC_MODERATOR, false, &ChatHandler::HandleGoTicketCommand, "", NULL }, + { "", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand gobjectCommandTable[] = + { + { "activate", SEC_GAMEMASTER, false, &ChatHandler::HandleActivateObjectCommand, "", NULL }, + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectAddCommand, "", NULL }, + { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectDeleteCommand, "", NULL }, + { "info", SEC_GAMEMASTER, false, &ChatHandler::HandleGOInfoCommand, "", NULL }, + { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectMoveCommand, "", NULL }, + { "near", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectNearCommand, "", NULL }, + { "state", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectStateCommand, "", NULL }, + { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectPhaseCommand, "", NULL }, + { "target", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTargetCommand, "", NULL }, + { "tempadd", SEC_GAMEMASTER, false, &ChatHandler::HandleTempGameObjectCommand, "", NULL }, + { "turn", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTurnCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand groupCommandTable[] = + { + { "leader", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupLeaderCommand, "", NULL }, + { "disband", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupDisbandCommand, "", NULL }, + { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupRemoveCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand guildCommandTable[] = + { + { "create", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildCreateCommand, "", NULL }, + { "delete", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildDeleteCommand, "", NULL }, + { "invite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildInviteCommand, "", NULL }, + { "uninvite", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildUninviteCommand, "", NULL }, + { "rank", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildRankCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand honorCommandTable[] = + { + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddCommand, "", NULL }, + { "addkill", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddKillCommand, "", NULL }, + { "update", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorUpdateCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand instanceCommandTable[] = + { + { "listbinds", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceListBindsCommand, "", NULL }, + { "unbind", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceUnbindCommand, "", NULL }, + { "stats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceStatsCommand, "", NULL }, + { "savedata", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL }, + { "open", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceOpenCommand, "", NULL }, + { "close", SEC_ADMINISTRATOR, true, &ChatHandler::HandleInstanceCloseCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand learnCommandTable[] = + { + { "all", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllCommand, "", NULL }, + { "all_gm", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllGMCommand, "", NULL }, + { "all_crafts", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllCraftsCommand, "", NULL }, + { "all_default", SEC_MODERATOR, false, &ChatHandler::HandleLearnAllDefaultCommand, "", NULL }, + { "all_lang", SEC_MODERATOR, false, &ChatHandler::HandleLearnAllLangCommand, "", NULL }, + { "all_myclass", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyClassCommand, "", NULL }, + { "all_mypettalents",SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyPetTalentsCommand,"", NULL }, + { "all_myspells", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMySpellsCommand, "", NULL }, + { "all_mytalents", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllMyTalentsCommand, "", NULL }, + { "all_recipes", SEC_GAMEMASTER, false, &ChatHandler::HandleLearnAllRecipesCommand, "", NULL }, + { "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand listCommandTable[] = + { + { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListCreatureCommand, "", NULL }, + { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListItemCommand, "", NULL }, + { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListObjectCommand, "", NULL }, + { "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand lookupPlayerCommandTable[] = + { + { "ip", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL }, + { "account", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL }, + { "email", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand lookupCommandTable[] = + { + { "area", SEC_MODERATOR, true, &ChatHandler::HandleLookupAreaCommand, "", NULL }, + { "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupCreatureCommand, "", NULL }, + { "event", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupEventCommand, "", NULL }, + { "faction", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupFactionCommand, "", NULL }, + { "item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemCommand, "", NULL }, + { "itemset", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupItemSetCommand, "", NULL }, + { "object", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupObjectCommand, "", NULL }, + { "quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupQuestCommand, "", NULL }, + { "player", SEC_GAMEMASTER, true, NULL, "", lookupPlayerCommandTable }, + { "skill", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSkillCommand, "", NULL }, + { "spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupSpellCommand, "", NULL }, + { "taxinode", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupTaxiNodeCommand, "", NULL }, + { "tele", SEC_MODERATOR, true, &ChatHandler::HandleLookupTeleCommand, "", NULL }, + { "title", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupTitleCommand, "", NULL }, + { "map", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupMapCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand modifyCommandTable[] = + { + { "hp", SEC_MODERATOR, false, &ChatHandler::HandleModifyHPCommand, "", NULL }, + { "mana", SEC_MODERATOR, false, &ChatHandler::HandleModifyManaCommand, "", NULL }, + { "rage", SEC_MODERATOR, false, &ChatHandler::HandleModifyRageCommand, "", NULL }, + { "runicpower", SEC_MODERATOR, false, &ChatHandler::HandleModifyRunicPowerCommand, "", NULL }, + { "energy", SEC_MODERATOR, false, &ChatHandler::HandleModifyEnergyCommand, "", NULL }, + { "money", SEC_MODERATOR, false, &ChatHandler::HandleModifyMoneyCommand, "", NULL }, + { "speed", SEC_MODERATOR, false, &ChatHandler::HandleModifySpeedCommand, "", NULL }, + { "swim", SEC_MODERATOR, false, &ChatHandler::HandleModifySwimCommand, "", NULL }, + { "scale", SEC_MODERATOR, false, &ChatHandler::HandleModifyScaleCommand, "", NULL }, + { "bit", SEC_MODERATOR, false, &ChatHandler::HandleModifyBitCommand, "", NULL }, + { "bwalk", SEC_MODERATOR, false, &ChatHandler::HandleModifyBWalkCommand, "", NULL }, + { "fly", SEC_MODERATOR, false, &ChatHandler::HandleModifyFlyCommand, "", NULL }, + { "aspeed", SEC_MODERATOR, false, &ChatHandler::HandleModifyASpeedCommand, "", NULL }, + { "faction", SEC_MODERATOR, false, &ChatHandler::HandleModifyFactionCommand, "", NULL }, + { "spell", SEC_MODERATOR, false, &ChatHandler::HandleModifySpellCommand, "", NULL }, + { "tp", SEC_MODERATOR, false, &ChatHandler::HandleModifyTalentCommand, "", NULL }, + { "mount", SEC_MODERATOR, false, &ChatHandler::HandleModifyMountCommand, "", NULL }, + { "honor", SEC_MODERATOR, false, &ChatHandler::HandleModifyHonorCommand, "", NULL }, + { "rep", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyRepCommand, "", NULL }, + { "arena", SEC_MODERATOR, false, &ChatHandler::HandleModifyArenaCommand, "", NULL }, + { "drunk", SEC_MODERATOR, false, &ChatHandler::HandleModifyDrunkCommand, "", NULL }, + { "standstate", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyStandStateCommand, "", NULL }, + { "morph", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyMorphCommand, "", NULL }, + { "phase", SEC_ADMINISTRATOR, false, &ChatHandler::HandleModifyPhaseCommand, "", NULL }, + { "gender", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyGenderCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand npcCommandTable[] = + { + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddCommand, "", NULL }, + { "additem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddVendorItemCommand, "", NULL }, + { "addmove", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddMoveCommand, "", NULL }, + { "allowmove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAllowMovementCommand, "", NULL }, + { "changeentry", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcChangeEntryCommand, "", NULL }, + { "changelevel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcChangeLevelCommand, "", NULL }, + { "delete", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDeleteCommand, "", NULL }, + { "delitem", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcDelVendorItemCommand, "", NULL }, + { "factionid", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFactionIdCommand, "", NULL }, + { "flag", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFlagCommand, "", NULL }, + { "follow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcFollowCommand, "", NULL }, + { "info", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcInfoCommand, "", NULL }, + { "move", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcMoveCommand, "", NULL }, + { "playemote", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcPlayEmoteCommand, "", NULL }, + { "setmodel", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetModelCommand, "", NULL }, + { "setmovetype", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetMoveTypeCommand, "", NULL }, + { "setphase", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetPhaseCommand, "", NULL }, + { "spawndist", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnDistCommand, "", NULL }, + { "spawntime", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSpawnTimeCommand, "", NULL }, + { "say", SEC_MODERATOR, false, &ChatHandler::HandleNpcSayCommand, "", NULL }, + { "textemote", SEC_MODERATOR, false, &ChatHandler::HandleNpcTextEmoteCommand, "", NULL }, + { "unfollow", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcUnFollowCommand, "", NULL }, + { "whisper", SEC_MODERATOR, false, &ChatHandler::HandleNpcWhisperCommand, "", NULL }, + { "yell", SEC_MODERATOR, false, &ChatHandler::HandleNpcYellCommand, "", NULL }, + { "tempadd", SEC_GAMEMASTER, false, &ChatHandler::HandleTempAddSpwCommand, "", NULL }, + { "tame", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcTameCommand, "", NULL }, + { "setdeathstate", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetDeathStateCommand, "", NULL }, + { "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL }, + { "setlink", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL }, + + //{ TODO: fix or remove this commands + { "addweapon", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAddWeaponCommand, "", NULL }, + { "name", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcNameCommand, "", NULL }, + { "subname", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSubNameCommand, "", NULL }, + //} + + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand petCommandTable[] = + { + { "create", SEC_GAMEMASTER, false, &ChatHandler::HandleCreatePetCommand, "", NULL }, + { "learn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetLearnCommand, "", NULL }, + { "unlearn", SEC_GAMEMASTER, false, &ChatHandler::HandlePetUnlearnCommand, "", NULL }, + { "tp", SEC_GAMEMASTER, false, &ChatHandler::HandlePetTpCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand pdumpCommandTable[] = + { + { "load", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpLoadCommand, "", NULL }, + { "write", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpWriteCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand questCommandTable[] = + { + { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestAdd, "", NULL }, + { "complete", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestComplete, "", NULL }, + { "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestRemove, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand reloadCommandTable[] = + { + { "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllCommand, "", NULL }, + { "all_achievement",SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllAchievementCommand,"", NULL }, + { "all_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllAreaCommand, "", NULL }, + { "all_eventai", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllEventAICommand, "", NULL }, + { "all_item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllItemCommand, "", NULL }, + { "all_locales", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllLocalesCommand, "", NULL }, + { "all_loot", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllLootCommand, "", NULL }, + { "all_npc", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllNpcCommand, "", NULL }, + { "all_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllQuestCommand, "", NULL }, + { "all_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllScriptsCommand, "", NULL }, + { "all_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllSpellCommand, "", NULL }, + + { "config", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConfigCommand, "", NULL }, + + { "access_requirement", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAccessRequirementCommand, "", NULL }, + { "achievement_criteria_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementCriteriaDataCommand, "", NULL }, + { "achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementRewardCommand, "", NULL }, + { "areatrigger_involvedrelation",SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestAreaTriggersCommand, "", NULL }, + { "areatrigger_tavern", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL }, + { "areatrigger_teleport", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL }, + { "autobroadcast", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAutobroadcastCommand, "", NULL }, + { "command", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCommandCommand, "", NULL }, + { "conditions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConditions, "", NULL }, + { "creature_ai_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAIScriptsCommand, "", NULL }, + { "creature_ai_summons", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAISummonsCommand, "", NULL }, + { "creature_ai_texts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAITextsCommand, "", NULL }, + { "creature_involvedrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand,"",NULL }, + { "creature_linked_respawn", SEC_GAMEMASTER, true, &ChatHandler::HandleReloadCreatureLinkedRespawnCommand, "", NULL }, + { "creature_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesCreatureCommand, "", NULL }, + { "creature_questrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureQuestRelationsCommand, "", NULL }, + { "creature_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCreatureTemplateCommand, "", NULL }, + //{ "db_script_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDbScriptStringCommand, "", NULL }, + { "disenchant_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL }, + { "event_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventScriptsCommand, "", NULL }, + { "fishing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesFishingCommand, "", NULL }, + { "game_graveyard_zone", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameGraveyardZoneCommand, "", NULL }, + { "game_tele", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameTeleCommand, "", NULL }, + { "gameobject_involvedrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGOQuestInvRelationsCommand, "", NULL }, + { "gameobject_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesGameobjectCommand, "", NULL }, + { "gameobject_questrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGOQuestRelationsCommand, "", NULL }, + { "gameobject_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL }, + { "gossip_menu", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuCommand, "", NULL }, + { "gossip_menu_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuOptionCommand, "", NULL }, + { "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL }, + { "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL }, + { "locales_achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesAchievementRewardCommand,"", NULL }, + { "locales_creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesCreatureCommand, "", NULL }, + { "locales_gameobject", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesGameobjectCommand, "", NULL }, + { "locales_item", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesItemCommand, "", NULL }, + { "locales_npc_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesNpcTextCommand, "", NULL }, + { "locales_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL }, + { "locales_points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPointsOfInterestCommand, "", NULL }, + { "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL }, +// { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL }, + { "mail_level_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMailLevelRewardCommand, "", NULL }, + { "mail_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMailCommand, "", NULL }, + { "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL }, + { "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL }, + { "npc_spellclick_spells", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellClickSpellsCommand, "",NULL}, + { "npc_trainer", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL }, + { "npc_vendor", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcVendorCommand, "", NULL }, + { "page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPageTextsCommand, "", NULL }, + { "pickpocketing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL}, + { "points_of_interest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadPointsOfInterestCommand, "",NULL}, + { "prospecting_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL }, + { "quest_end_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL }, + { "quest_start_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL }, + { "quest_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL }, + { "reference_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL }, + { "reserved_name", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadReservedNameCommand, "", NULL }, + { "skill_discovery_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand, "", NULL }, + { "skill_extra_item_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL }, + { "skill_fishing_base_level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL }, + { "skinning_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL }, + { "spell_required", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellRequiredCommand, "", NULL }, + { "spell_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAreaCommand, "", NULL }, + { "spell_bonus_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellBonusesCommand, "", NULL }, + { "spell_group", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellGroupsCommand, "", NULL }, + { "spell_learn_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL }, + { "spell_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSpellCommand, "", NULL }, + { "spell_linked_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLinkedSpellCommand, "", NULL }, + { "spell_pet_auras", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL }, + { "spell_proc_event", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL }, + { "spell_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL }, + { "spell_target_position", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL }, + { "spell_threats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL }, + { "spell_disabled", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellDisabledCommand, "", NULL }, + { "spell_group_stack_rules", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellGroupStackRulesCommand, "", NULL }, + { "trinity_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadTrinityStringCommand, "", NULL }, + { "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL }, + { "waypoint_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadWpScriptsCommand, "", NULL }, + { "gm_tickets", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMTicketReloadCommand, "", NULL }, + + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand resetCommandTable[] = + { + { "achievements", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetAchievementsCommand, "", NULL }, + { "honor", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetHonorCommand, "", NULL }, + { "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetLevelCommand, "", NULL }, + { "spells", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetSpellsCommand, "", NULL }, + { "stats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetStatsCommand, "", NULL }, + { "talents", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetTalentsCommand, "", NULL }, + { "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetAllCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand sendCommandTable[] = + { + { "items", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendItemsCommand, "", NULL }, + { "mail", SEC_MODERATOR, true, &ChatHandler::HandleSendMailCommand, "", NULL }, + { "message", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMessageCommand, "", NULL }, + { "money", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMoneyCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand serverIdleRestartCommandTable[] = + { + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleRestartCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand serverIdleShutdownCommandTable[] = + { + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleShutDownCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand serverRestartCommandTable[] = + { + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerRestartCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand serverShutdownCommandTable[] = + { + { "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL }, + { "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand serverSetCommandTable[] = + { + { "difftime", SEC_CONSOLE, true, &ChatHandler::HandleServerSetDiffTimeCommand, "", NULL }, + { "loglevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogLevelCommand, "", NULL }, + { "logfilelevel", SEC_CONSOLE, true, &ChatHandler::HandleServerSetLogFileLevelCommand, "", NULL }, + { "motd", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetMotdCommand, "", NULL }, + { "closed", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetClosedCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand serverCommandTable[] = + { + { "corpses", SEC_GAMEMASTER, true, &ChatHandler::HandleServerCorpsesCommand, "", NULL }, + { "exit", SEC_CONSOLE, true, &ChatHandler::HandleServerExitCommand, "", NULL }, + { "idlerestart", SEC_ADMINISTRATOR, true, NULL, "", serverIdleRestartCommandTable }, + { "idleshutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, + { "info", SEC_PLAYER, true, &ChatHandler::HandleServerInfoCommand, "", NULL }, + { "motd", SEC_PLAYER, true, &ChatHandler::HandleServerMotdCommand, "", NULL }, + { "plimit", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerPLimitCommand, "", NULL }, + { "restart", SEC_ADMINISTRATOR, true, NULL, "", serverRestartCommandTable }, + { "shutdown", SEC_ADMINISTRATOR, true, NULL, "", serverShutdownCommandTable }, + { "set", SEC_ADMINISTRATOR, true, NULL, "", serverSetCommandTable }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand teleCommandTable[] = + { + { "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleTeleAddCommand, "", NULL }, + { "del", SEC_ADMINISTRATOR, true, &ChatHandler::HandleTeleDelCommand, "", NULL }, + { "name", SEC_MODERATOR, true, &ChatHandler::HandleTeleNameCommand, "", NULL }, + { "group", SEC_MODERATOR, false, &ChatHandler::HandleTeleGroupCommand, "", NULL }, + { "", SEC_MODERATOR, false, &ChatHandler::HandleTeleCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand titlesCommandTable[] = + { + { "add", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesAddCommand, "", NULL }, + { "current", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesCurrentCommand, "", NULL }, + { "remove", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesRemoveCommand, "", NULL }, + { "setmask", SEC_GAMEMASTER, false, &ChatHandler::HandleTitlesSetMaskCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand unbanCommandTable[] = + { + { "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanAccountCommand, "", NULL }, + { "character", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanCharacterCommand, "", NULL }, + { "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanIPCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand wpCommandTable[] = + { + { "show", SEC_GAMEMASTER, false, &ChatHandler::HandleWpShowCommand, "", NULL }, + { "addwp", SEC_GAMEMASTER, false, &ChatHandler::HandleWpAddCommand, "", NULL }, + { "load", SEC_GAMEMASTER, false, &ChatHandler::HandleWpLoadPathCommand, "", NULL }, + { "modify", SEC_GAMEMASTER, false, &ChatHandler::HandleWpModifyCommand, "", NULL }, + { "event", SEC_GAMEMASTER, false, &ChatHandler::HandleWpEventCommand, "", NULL }, + { "unload", SEC_GAMEMASTER, false, &ChatHandler::HandleWpUnLoadPathCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand ticketCommandTable[] = + { + { "list", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListCommand, "", NULL }, + { "onlinelist", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListOnlineCommand, "", NULL }, + { "viewname", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketGetByNameCommand, "", NULL }, + { "viewid", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketGetByIdCommand, "", NULL }, + { "close", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketCloseByIdCommand, "", NULL }, + { "closedlist", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListClosedCommand, "", NULL }, + { "delete", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGMTicketDeleteByIdCommand, "", NULL }, + { "assign", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketAssignToCommand, "", NULL }, + { "unassign", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketUnAssignCommand, "", NULL }, + { "comment", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketCommentCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand commandTable[] = + { + { "account", SEC_PLAYER, true, NULL, "", accountCommandTable }, + { "gm", SEC_MODERATOR, true, NULL, "", gmCommandTable }, + { "npc", SEC_MODERATOR, false, NULL, "", npcCommandTable }, + { "go", SEC_MODERATOR, false, NULL, "", goCommandTable }, + { "learn", SEC_MODERATOR, false, NULL, "", learnCommandTable }, + { "modify", SEC_MODERATOR, false, NULL, "", modifyCommandTable }, + { "debug", SEC_MODERATOR, true, NULL, "", debugCommandTable }, + { "tele", SEC_MODERATOR, true, NULL, "", teleCommandTable }, + { "character", SEC_GAMEMASTER, false, NULL, "", characterCommandTable}, + { "event", SEC_GAMEMASTER, false, NULL, "", eventCommandTable }, + { "gobject", SEC_GAMEMASTER, false, NULL, "", gobjectCommandTable }, + { "honor", SEC_GAMEMASTER, false, NULL, "", honorCommandTable }, + { "wp", SEC_GAMEMASTER, false, NULL, "", wpCommandTable }, + { "titles", SEC_GAMEMASTER, false, NULL, "", titlesCommandTable }, + { "quest", SEC_ADMINISTRATOR, false, NULL, "", questCommandTable }, + { "reload", SEC_ADMINISTRATOR, true, NULL, "", reloadCommandTable }, + { "list", SEC_ADMINISTRATOR, true, NULL, "", listCommandTable }, + { "lookup", SEC_ADMINISTRATOR, true, NULL, "", lookupCommandTable }, + { "pdump", SEC_ADMINISTRATOR, true, NULL, "", pdumpCommandTable }, + { "guild", SEC_ADMINISTRATOR, true, NULL, "", guildCommandTable }, + { "cast", SEC_ADMINISTRATOR, false, NULL, "", castCommandTable }, + { "reset", SEC_ADMINISTRATOR, true, NULL, "", resetCommandTable }, + { "instance", SEC_ADMINISTRATOR, true, NULL, "", instanceCommandTable }, + { "server", SEC_ADMINISTRATOR, true, NULL, "", serverCommandTable }, + + { "channel", SEC_ADMINISTRATOR, true, NULL, "", channelCommandTable }, + + { "pet", SEC_GAMEMASTER, false, NULL, "", petCommandTable }, + { "loadpath", SEC_ADMINISTRATOR, false, &ChatHandler::HandleReloadAllPaths, "", NULL }, + { "ahbotoptions", SEC_GAMEMASTER, true, &ChatHandler::HandleAHBotOptionsCommand, "", NULL }, + { "ticket", SEC_MODERATOR, false, NULL, "", ticketCommandTable }, + + { "aura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAuraCommand, "", NULL }, + { "unaura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnAuraCommand, "", NULL }, + { "nameannounce", SEC_MODERATOR, false, &ChatHandler::HandleNameAnnounceCommand, "", NULL }, + { "gmnameannounce", SEC_MODERATOR, false, &ChatHandler::HandleGMNameAnnounceCommand, "", NULL }, + { "announce", SEC_MODERATOR, true, &ChatHandler::HandleAnnounceCommand, "", NULL }, + { "gmannounce", SEC_MODERATOR, true, &ChatHandler::HandleGMAnnounceCommand, "", NULL }, + { "notify", SEC_MODERATOR, true, &ChatHandler::HandleNotifyCommand, "", NULL }, + { "gmnotify", SEC_MODERATOR, true, &ChatHandler::HandleGMNotifyCommand, "", NULL }, + { "goname", SEC_MODERATOR, false, &ChatHandler::HandleGonameCommand, "", NULL }, + { "namego", SEC_MODERATOR, false, &ChatHandler::HandleNamegoCommand, "", NULL }, + { "groupgo", SEC_MODERATOR, false, &ChatHandler::HandleGroupgoCommand, "", NULL }, + { "commands", SEC_PLAYER, true, &ChatHandler::HandleCommandsCommand, "", NULL }, + { "demorph", SEC_GAMEMASTER, false, &ChatHandler::HandleDeMorphCommand, "", NULL }, + { "die", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDieCommand, "", NULL }, + { "revive", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReviveCommand, "", NULL }, + { "dismount", SEC_PLAYER, false, &ChatHandler::HandleDismountCommand, "", NULL }, + { "gps", SEC_MODERATOR, false, &ChatHandler::HandleGPSCommand, "", NULL }, + { "guid", SEC_GAMEMASTER, false, &ChatHandler::HandleGUIDCommand, "", NULL }, + { "help", SEC_PLAYER, true, &ChatHandler::HandleHelpCommand, "", NULL }, + { "itemmove", SEC_GAMEMASTER, false, &ChatHandler::HandleItemMoveCommand, "", NULL }, + { "cooldown", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCooldownCommand, "", NULL }, + { "unlearn", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnLearnCommand, "", NULL }, + { "distance", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGetDistanceCommand, "", NULL }, + { "recall", SEC_MODERATOR, false, &ChatHandler::HandleRecallCommand, "", NULL }, + { "save", SEC_PLAYER, false, &ChatHandler::HandleSaveCommand, "", NULL }, + { "saveall", SEC_MODERATOR, true, &ChatHandler::HandleSaveAllCommand, "", NULL }, + { "kick", SEC_GAMEMASTER, true, &ChatHandler::HandleKickPlayerCommand, "", NULL }, + { "ban", SEC_ADMINISTRATOR, true, NULL, "", banCommandTable }, + { "unban", SEC_ADMINISTRATOR, true, NULL, "", unbanCommandTable }, + { "baninfo", SEC_ADMINISTRATOR, false, NULL, "", baninfoCommandTable }, + { "banlist", SEC_ADMINISTRATOR, true, NULL, "", banlistCommandTable }, + { "start", SEC_PLAYER, false, &ChatHandler::HandleStartCommand, "", NULL }, + { "taxicheat", SEC_MODERATOR, false, &ChatHandler::HandleTaxiCheatCommand, "", NULL }, + { "linkgrave", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLinkGraveCommand, "", NULL }, + { "neargrave", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNearGraveCommand, "", NULL }, + { "explorecheat", SEC_ADMINISTRATOR, false, &ChatHandler::HandleExploreCheatCommand, "", NULL }, + { "hover", SEC_ADMINISTRATOR, false, &ChatHandler::HandleHoverCommand, "", NULL }, + { "levelup", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLevelUpCommand, "", NULL }, + { "showarea", SEC_ADMINISTRATOR, false, &ChatHandler::HandleShowAreaCommand, "", NULL }, + { "hidearea", SEC_ADMINISTRATOR, false, &ChatHandler::HandleHideAreaCommand, "", NULL }, + { "additem", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddItemCommand, "", NULL }, + { "additemset", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAddItemSetCommand, "", NULL }, + { "bank", SEC_ADMINISTRATOR, false, &ChatHandler::HandleBankCommand, "", NULL }, + { "wchange", SEC_ADMINISTRATOR, false, &ChatHandler::HandleChangeWeather, "", NULL }, + { "maxskill", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMaxSkillCommand, "", NULL }, + { "setskill", SEC_ADMINISTRATOR, false, &ChatHandler::HandleSetSkillCommand, "", NULL }, + { "whispers", SEC_MODERATOR, false, &ChatHandler::HandleWhispersCommand, "", NULL }, + { "pinfo", SEC_GAMEMASTER, true, &ChatHandler::HandlePInfoCommand, "", NULL }, + { "respawn", SEC_ADMINISTRATOR, false, &ChatHandler::HandleRespawnCommand, "", NULL }, + { "send", SEC_MODERATOR, true, NULL, "", sendCommandTable }, + { "mute", SEC_MODERATOR, true, &ChatHandler::HandleMuteCommand, "", NULL }, + { "unmute", SEC_MODERATOR, true, &ChatHandler::HandleUnmuteCommand, "", NULL }, + { "movegens", SEC_ADMINISTRATOR, false, &ChatHandler::HandleMovegensCommand, "", NULL }, + { "cometome", SEC_ADMINISTRATOR, false, &ChatHandler::HandleComeToMeCommand, "", NULL }, + { "damage", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDamageCommand, "", NULL }, + { "combatstop", SEC_GAMEMASTER, false, &ChatHandler::HandleCombatStopCommand, "", NULL }, + { "flusharenapoints",SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL }, + { "repairitems", SEC_GAMEMASTER, true, &ChatHandler::HandleRepairitemsCommand, "", NULL }, + { "waterwalk", SEC_GAMEMASTER, false, &ChatHandler::HandleWaterwalkCommand, "", NULL }, + + { "freeze", SEC_MODERATOR, false, &ChatHandler::HandleFreezeCommand, "", NULL }, + { "unfreeze", SEC_MODERATOR, false, &ChatHandler::HandleUnFreezeCommand, "", NULL }, + { "listfreeze", SEC_MODERATOR, false, &ChatHandler::HandleListFreezeCommand, "", NULL }, + + { "possess", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePossessCommand, "", NULL }, + { "unpossess", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnPossessCommand, "", NULL }, + { "bindsight", SEC_ADMINISTRATOR, false, &ChatHandler::HandleBindSightCommand, "", NULL }, + { "unbindsight", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnbindSightCommand, "", NULL }, + { "playall", SEC_GAMEMASTER, false, &ChatHandler::HandlePlayAllCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + if (load_command_table) + { + load_command_table = false; + + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT name,security,help FROM command"); + if (result) + { + do + { + Field *fields = result->Fetch(); + std::string name = fields[0].GetCppString(); + + SetDataForCommandInTable(commandTable, name.c_str(), fields[1].GetUInt16(), fields[2].GetCppString(), name); + + } while (result->NextRow()); + } + } + + return commandTable; +} + +const char *ChatHandler::GetTrinityString(int32 entry) const +{ + return m_session->GetTrinityString(entry); +} + +bool ChatHandler::isAvailable(ChatCommand const& cmd) const +{ + // check security level only for simple command (without child commands) + return m_session->GetSecurity() >= cmd.SecurityLevel; +} + +bool ChatHandler::HasLowerSecurity(Player* target, uint64 guid, bool strong) +{ + WorldSession* target_session = NULL; + uint32 target_account = 0; + + if (target) + target_session = target->GetSession(); + else if (guid) + target_account = objmgr.GetPlayerAccountIdByGUID(guid); + + if (!target_session && !target_account) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return true; + } + + return HasLowerSecurityAccount(target_session,target_account,strong); +} + +bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_account, bool strong) +{ + uint32 target_sec; + + // allow everything from console and RA console + if (!m_session) + return false; + + // ignore only for non-players for non strong checks (when allow apply command at least to same sec level) + if (m_session->GetSecurity() > SEC_PLAYER && !strong && !sWorld.getConfig(CONFIG_GM_LOWER_SECURITY)) + return false; + + if (target) + target_sec = target->GetSecurity(); + else if (target_account) + target_sec = accmgr.GetSecurity(target_account); + else + return true; // caller must report error for (target == NULL && target_account == 0) + + if (m_session->GetSecurity() < target_sec || (strong && m_session->GetSecurity() <= target_sec)) + { + SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); + SetSentErrorMessage(true); + return true; + } + + return false; +} + +bool ChatHandler::hasStringAbbr(const char* name, const char* part) +{ + // non "" command + if (*name) + { + // "" part from non-"" command + if (!*part) + return false; + + for (;;) + { + if (!*part) + return true; + else if (!*name) + return false; + else if (tolower(*name) != tolower(*part)) + return false; + ++name; ++part; + } + } + // allow with any for "" + + return true; +} + +void ChatHandler::SendSysMessage(const char *str) +{ + WorldPacket data; + + // need copy to prevent corruption by strtok call in LineFromMessage original string + char* buf = strdup(str); + char* pos = buf; + + while (char* line = LineFromMessage(pos)) + { + FillSystemMessageData(&data, line); + m_session->SendPacket(&data); + } + + free(buf); +} + +void ChatHandler::SendGlobalSysMessage(const char *str) +{ + // Chat output + WorldPacket data; + + // need copy to prevent corruption by strtok call in LineFromMessage original string + char* buf = strdup(str); + char* pos = buf; + + while (char* line = LineFromMessage(pos)) + { + FillSystemMessageData(&data, line); + sWorld.SendGlobalMessage(&data); + } + + free(buf); +} + +void ChatHandler::SendGlobalGMSysMessage(const char *str) +{ + // Chat output + WorldPacket data; + + // need copy to prevent corruption by strtok call in LineFromMessage original string + char* buf = strdup(str); + char* pos = buf; + + while (char* line = LineFromMessage(pos)) + { + FillSystemMessageData(&data, line); + sWorld.SendGlobalGMMessage(&data); + } + free(buf); +} + +void ChatHandler::SendSysMessage(int32 entry) +{ + SendSysMessage(GetTrinityString(entry)); +} + +void ChatHandler::PSendSysMessage(int32 entry, ...) +{ + const char *format = GetTrinityString(entry); + va_list ap; + char str [2048]; + va_start(ap, entry); + vsnprintf(str,2048,format, ap); + va_end(ap); + SendSysMessage(str); +} + +void ChatHandler::PSendSysMessage(const char *format, ...) +{ + va_list ap; + char str [2048]; + va_start(ap, format); + vsnprintf(str,2048,format, ap); + va_end(ap); + SendSysMessage(str); +} + +bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcmd) +{ + char const* oldtext = text; + std::string cmd = ""; + + while (*text != ' ' && *text != '\0') + { + cmd += *text; + ++text; + } + + while (*text == ' ') ++text; + + for (uint32 i = 0; table[i].Name != NULL; ++i) + { + if (!hasStringAbbr(table[i].Name, cmd.c_str())) + continue; + + // select subcommand from child commands list + if (table[i].ChildCommands != NULL) + { + if (!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd)) + { + if (text && text[0] != '\0') + SendSysMessage(LANG_NO_SUBCMD); + else + SendSysMessage(LANG_CMD_SYNTAX); + + ShowHelpForCommand(table[i].ChildCommands,text); + } + + return true; + } + + // must be available and have handler + if (!table[i].Handler || !isAvailable(table[i])) + continue; + + SetSentErrorMessage(false); + // table[i].Name == "" is special case: send original command to handler + if ((this->*(table[i].Handler))(strlen(table[i].Name) != 0 ? text : oldtext)) + { + if (table[i].SecurityLevel > SEC_PLAYER) + { + // chat case + if (m_session) + { + Player* p = m_session->GetPlayer(); + uint64 sel_guid = p->GetSelection(); + sLog.outCommand(m_session->GetAccountId(),"Command: %s [Player: %s (Account: %u) X: %f Y: %f Z: %f Map: %u Selected: %s (GUID: %u)]", + fullcmd.c_str(),p->GetName(),m_session->GetAccountId(),p->GetPositionX(),p->GetPositionY(),p->GetPositionZ(),p->GetMapId(), + GetLogNameForGuid(sel_guid),GUID_LOPART(sel_guid)); + } + } + } + // some commands have custom error messages. Don't send the default one in these cases. + else if (!sentErrorMessage) + { + if (!table[i].Help.empty()) + SendSysMessage(table[i].Help.c_str()); + else + SendSysMessage(LANG_CMD_SYNTAX); + } + + return true; + } + + return false; +} + +bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand) +{ + std::string cmd = ""; + + while (*text != ' ' && *text != '\0') + { + cmd += *text; + ++text; + } + + while (*text == ' ') ++text; + + for (uint32 i = 0; table[i].Name != NULL; i++) + { + // for data fill use full explicit command names + if (table[i].Name != cmd) + continue; + + // select subcommand from child commands list (including "") + if (table[i].ChildCommands != NULL) + { + if (SetDataForCommandInTable(table[i].ChildCommands, text, security, help, fullcommand)) + return true; + else if (*text) + return false; + + // fail with "" subcommands, then use normal level up command instead + } + // expected subcommand by full name DB content + else if (*text) + { + sLog.outErrorDb("Table `command` have unexpected subcommand '%s' in command '%s', skip.",text,fullcommand.c_str()); + return false; + } + + if (table[i].SecurityLevel != security) + sLog.outDetail("Table `command` overwrite for command '%s' default security (%u) by %u",fullcommand.c_str(),table[i].SecurityLevel,security); + + table[i].SecurityLevel = security; + table[i].Help = help; + return true; + } + + // in case "" command let process by caller + if (!cmd.empty()) + { + if (table == getCommandTable()) + sLog.outErrorDb("Table `command` have not existed command '%s', skip.",cmd.c_str()); + else + sLog.outErrorDb("Table `command` have not existed subcommand '%s' in command '%s', skip.",cmd.c_str(),fullcommand.c_str()); + } + + return false; +} + +int ChatHandler::ParseCommands(const char* text) +{ + ASSERT(text); + ASSERT(*text); + + std::string fullcmd = text; + + if (m_session && m_session->GetSecurity() <= SEC_PLAYER && sWorld.getConfig(CONFIG_ALLOW_PLAYER_COMMANDS) == 0) + return 0; + + if (m_session && !m_session->HandleOnPlayerChat(text)) + return 0; + + /// chat case (.command or !command format) + if (m_session) + { + if (text[0] != '!' && text[0] != '.') + return 0; + } + + /// ignore single . and ! in line + if (strlen(text) < 2) + return 0; + // original `text` can't be used. It content destroyed in command code processing. + + /// ignore messages staring from many dots. + if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!')) + return 0; + + /// skip first . or ! (in console allowed use command with . and ! and without its) + if (text[0] == '!' || text[0] == '.') + ++text; + + if (!ExecuteCommandInTable(getCommandTable(), text, fullcmd)) + { + if (m_session && m_session->GetSecurity() == SEC_PLAYER) + return 0; + SendSysMessage(LANG_NO_CMD); + } + return 1; +} + +bool ChatHandler::isValidChatMessage(const char* message) +{ +/* + +valid examples: +|cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r +|cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r +|cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r +|cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r +|cff71d5ff|Hspell:21563|h[Command]|h|r +|cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r +|cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r +|cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r + +| will be escaped to || +*/ + + if (strlen(message) > 255) + return false; + + const char validSequence[6] = "cHhhr"; + const char* validSequenceIterator = validSequence; + + // more simple checks + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3) + { + const std::string validCommands = "cHhr|"; + + while (*message) + { + // find next pipe command + message = strchr(message, '|'); + + if (!message) + return true; + + ++message; + char commandChar = *message; + if (validCommands.find(commandChar) == std::string::npos) + return false; + + ++message; + // validate sequence + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2) + { + if (commandChar == *validSequenceIterator) + { + if (validSequenceIterator == validSequence+4) + validSequenceIterator = validSequence; + else + ++validSequenceIterator; + } + else + return false; + } + } + return true; + } + + std::istringstream reader(message); + char buffer[256]; + + uint32 color; + + ItemPrototype const* linkedItem; + Quest const* linkedQuest; + SpellEntry const *linkedSpell; + AchievementEntry const* linkedAchievement; + ItemRandomPropertiesEntry const* itemProperty; + ItemRandomSuffixEntry const* itemSuffix; + + while (!reader.eof()) + { + if (validSequence == validSequenceIterator) + { + linkedItem = NULL; + linkedQuest = NULL; + linkedSpell = NULL; + linkedAchievement = NULL; + itemProperty = NULL; + itemSuffix = NULL; + + reader.ignore(255, '|'); + } + else if (reader.get() != '|') + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage sequence aborted unexpectedly"); +#endif + return false; + } + + // pipe has always to be followed by at least one char + if (reader.peek() == '\0') + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage pipe followed by \\0"); +#endif + return false; + } + + // no further pipe commands + if (reader.eof()) + break; + + char commandChar; + reader >> commandChar; + + // | in normal messages is escaped by || + if (commandChar != '|') + { + if (commandChar == *validSequenceIterator) + { + if (validSequenceIterator == validSequence+4) + validSequenceIterator = validSequence; + else + ++validSequenceIterator; + } + else + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage invalid sequence, expected %c but got %c", *validSequenceIterator, commandChar); +#endif + return false; + } + } + else if (validSequence != validSequenceIterator) + { + // no escaped pipes in sequences +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got escaped pipe in sequence"); +#endif + return false; + } + + switch (commandChar) + { + case 'c': + color = 0; + // validate color, expect 8 hex chars + for (int i=0; i<8; i++) + { + char c; + reader >> c; + if (!c) + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got \\0 while reading color in |c command"); +#endif + return false; + } + + color <<= 4; + // check for hex char + if (c >= '0' && c <= '9') + { + color |= c-'0'; + continue; + } + if (c >= 'a' && c <= 'f') + { + color |= 10+c-'a'; + continue; + } +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got non hex char '%c' while reading color", c); +#endif + return false; + } + break; + case 'H': + // read chars up to colon = link type + reader.getline(buffer, 256, ':'); + + if (strcmp(buffer, "item") == 0) + { + // read item entry + reader.getline(buffer, 256, ':'); + + linkedItem= objmgr.GetItemPrototype(atoi(buffer)); + if (!linkedItem) + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got invalid itemID %u in |item command", atoi(buffer)); +#endif + return false; + } + + if (color != ItemQualityColors[linkedItem->Quality]) + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage linked item has color %u, but user claims %u", ItemQualityColors[linkedItem->Quality], + color); +#endif + return false; + } + + // the itementry is followed by several integers which describe an instance of this item + + // position relative after itemEntry + const uint8 randomPropertyPosition = 6; + + int32 propertyId = 0; + bool negativeNumber = false; + char c; + for (uint8 i=0; i='0' && c <= '9') + { + propertyId*=10; + propertyId += c-'0'; + } else if (c == '-') + negativeNumber = true; + else + return false; + } + } + if (negativeNumber) + propertyId *= -1; + + if (propertyId > 0) + { + itemProperty = sItemRandomPropertiesStore.LookupEntry(propertyId); + if (!itemProperty) + return false; + } + else if (propertyId < 0) + { + itemSuffix = sItemRandomSuffixStore.LookupEntry(-propertyId); + if (!itemSuffix) + return false; + } + + // ignore other integers + while ((c >='0' && c <= '9') || c == ':') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if (strcmp(buffer, "quest") == 0) + { + // no color check for questlinks, each client will adapt it anyway + uint32 questid= 0; + // read questid + char c = reader.peek(); + while (c >='0' && c <= '9') + { + reader.ignore(1); + questid *= 10; + questid += c-'0'; + c = reader.peek(); + } + + linkedQuest = objmgr.GetQuestTemplate(questid); + + if (!linkedQuest) + { +#ifdef MANOGS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage Questtemplate %u not found", questid); +#endif + return false; + } + c = reader.peek(); + // level + while (c !='|' && c != '\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if (strcmp(buffer, "trade") == 0) + { + if (color != CHAT_LINK_COLOR_TRADE) + return false; + + // read spell entry + reader.getline(buffer, 256, ':'); + linkedSpell = sSpellStore.LookupEntry(atoi(buffer)); + if (!linkedSpell) + return false; + + char c = reader.peek(); + // base64 encoded stuff + while (c !='|' && c != '\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if (strcmp(buffer, "talent") == 0) + { + // talent links are always supposed to be blue + if (color != CHAT_LINK_COLOR_TALENT) + return false; + + // read talent entry + reader.getline(buffer, 256, ':'); + TalentEntry const *talentInfo = sTalentStore.LookupEntry(atoi(buffer)); + if (!talentInfo) + return false; + + linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]); + if (!linkedSpell) + return false; + + char c = reader.peek(); + // skillpoints? whatever, drop it + while (c !='|' && c != '\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if (strcmp(buffer, "spell") == 0) + { + if (color != CHAT_LINK_COLOR_SPELL) + return false; + + uint32 spellid = 0; + // read spell entry + char c = reader.peek(); + while (c >='0' && c <= '9') + { + reader.ignore(1); + spellid *= 10; + spellid += c-'0'; + c = reader.peek(); + } + linkedSpell = sSpellStore.LookupEntry(spellid); + if (!linkedSpell) + return false; + } + else if (strcmp(buffer, "enchant") == 0) + { + if (color != CHAT_LINK_COLOR_ENCHANT) + return false; + + uint32 spellid = 0; + // read spell entry + char c = reader.peek(); + while (c >='0' && c <= '9') + { + reader.ignore(1); + spellid *= 10; + spellid += c-'0'; + c = reader.peek(); + } + linkedSpell = sSpellStore.LookupEntry(spellid); + if (!linkedSpell) + return false; + } + else if (strcmp(buffer, "achievement") == 0) + { + if (color != CHAT_LINK_COLOR_ACHIEVEMENT) + return false; + reader.getline(buffer, 256, ':'); + uint32 achievementId = atoi(buffer); + linkedAchievement = sAchievementStore.LookupEntry(achievementId); + + if (!linkedAchievement) + return false; + + char c = reader.peek(); + // skip progress + while (c !='|' && c != '\0') + { + reader.ignore(1); + c = reader.peek(); + } + } + else if (strcmp(buffer, "glyph") == 0) + { + if (color != CHAT_LINK_COLOR_GLYPH) + return false; + + // first id is slot, drop it + reader.getline(buffer, 256, ':'); + uint32 glyphId = 0; + char c = reader.peek(); + while (c >= '0' && c <= '9') + { + glyphId *= 10; + glyphId += c-'0'; + reader.ignore(1); + c = reader.peek(); + } + GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId); + if (!glyph) + return false; + + linkedSpell = sSpellStore.LookupEntry(glyph->SpellId); + + if (!linkedSpell) + return false; + } + else + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage user sent unsupported link type '%s'", buffer); +#endif + return false; + } + break; + case 'h': + // if h is next element in sequence, this one must contain the linked text :) + if (*validSequenceIterator == 'h') + { + // links start with '[' + if (reader.get() != '[') + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage link caption doesn't start with '['"); +#endif + return false; + } + reader.getline(buffer, 256, ']'); + + // verify the link name + if (linkedSpell) + { + // spells with that flag have a prefix of "$PROFESSION: " + if (linkedSpell->Attributes & SPELL_ATTR_TRADESPELL) + { + // lookup skillid + SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(linkedSpell->Id); + if (bounds.first == bounds.second) + { + return false; + } + + SkillLineAbilityEntry const *skillInfo = bounds.first->second; + + if (!skillInfo) + { + return false; + } + + SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId); + if (!skillLine) + { + return false; + } + + for (uint8 i=0; iname[i]); + if (skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0) + { + // found the prefix, remove it to perform spellname validation below + // -2 = strlen(": ") + uint32 spellNameLength = strlen(buffer)-skillLineNameLength-2; + memmove(buffer, buffer+skillLineNameLength+2, spellNameLength+1); + } + } + } + bool foundName = false; + for (uint8 i=0; iSpellName[i] && strcmp(linkedSpell->SpellName[i], buffer) == 0) + { + foundName = true; + break; + } + } + if (!foundName) + return false; + } + else if (linkedQuest) + { + if (linkedQuest->GetTitle() != buffer) + { + QuestLocale const *ql = objmgr.GetQuestLocale(linkedQuest->GetQuestId()); + + if (!ql) + { +#ifdef MANOGS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage default questname didn't match and there is no locale"); +#endif + return false; + } + + bool foundName = false; + for (uint8 i=0; iTitle.size(); i++) + { + if (ql->Title[i] == buffer) + { + foundName = true; + break; + } + } + if (!foundName) + { +#ifdef MANOGS_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage no quest locale title matched") +#endif + return false; + } + } + } + else if (linkedItem) + { + char* const* suffix = itemSuffix?itemSuffix->nameSuffix:(itemProperty?itemProperty->nameSuffix:NULL); + + std::string expectedName = std::string(linkedItem->Name1); + if (suffix) + { + expectedName += " "; + expectedName += suffix[LOCALE_enUS]; + } + + if (expectedName != buffer) + { + ItemLocale const *il = objmgr.GetItemLocale(linkedItem->ItemId); + + bool foundName = false; + for (uint8 i=LOCALE_koKR; i= il->Name.size()) + // using strange database/client combinations can lead to this case + expectedName = linkedItem->Name1; + else + expectedName = il->Name[dbIndex]; + if (suffix) + { + expectedName += " "; + expectedName += suffix[i]; + } + if (expectedName == buffer) + { + foundName = true; + break; + } + } + if (!foundName) + { +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage linked item name wasn't found in any localization"); +#endif + return false; + } + } + } + else if (linkedAchievement) + { + bool foundName = false; + for (uint8 i=0; iname[i] && strcmp(linkedAchievement->name[i], buffer) == 0) + { + foundName = true; + break; + } + } + if (!foundName) + return false; + } + // that place should never be reached - if nothing linked has been set in |H + // it will return false before + else + return false; + } + break; + case 'r': + case '|': + // no further payload + break; + default: +#ifdef TRINITY_DEBUG + sLog.outBasic("ChatHandler::isValidChatMessage got invalid command |%c", commandChar); +#endif + return false; + } + } + + // check if every opened sequence was also closed properly +#ifdef TRINITY_DEBUG + if (validSequence != validSequenceIterator) + sLog.outBasic("ChatHandler::isValidChatMessage EOF in active sequence"); +#endif + return validSequence == validSequenceIterator; +} + +bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd) +{ + std::string list; + for (uint32 i = 0; table[i].Name != NULL; ++i) + { + // must be available (ignore handler existence for show command with possibe avalable subcomands + if (!isAvailable(table[i])) + continue; + + /// for empty subcmd show all available + if (*subcmd && !hasStringAbbr(table[i].Name, subcmd)) + continue; + + if (m_session) + list += "\n "; + else + list += "\n\r "; + + list += table[i].Name; + + if (table[i].ChildCommands) + list += " ..."; + } + + if (list.empty()) + return false; + + if (table == getCommandTable()) + { + SendSysMessage(LANG_AVIABLE_CMD); + PSendSysMessage("%s",list.c_str()); + } + else + PSendSysMessage(LANG_SUBCMDS_LIST,cmd,list.c_str()); + + return true; +} + +bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd) +{ + if (*cmd) + { + for (uint32 i = 0; table[i].Name != NULL; ++i) + { + // must be available (ignore handler existence for show command with possibe avalable subcomands + if (!isAvailable(table[i])) + continue; + + if (!hasStringAbbr(table[i].Name, cmd)) + continue; + + // have subcommand + char const* subcmd = (*cmd) ? strtok(NULL, " ") : ""; + + if (table[i].ChildCommands && subcmd && *subcmd) + { + if (ShowHelpForCommand(table[i].ChildCommands, subcmd)) + return true; + } + + if (!table[i].Help.empty()) + SendSysMessage(table[i].Help.c_str()); + + if (table[i].ChildCommands) + if (ShowHelpForSubCommands(table[i].ChildCommands,table[i].Name,subcmd ? subcmd : "")) + return true; + + return !table[i].Help.empty(); + } + } + else + { + for (uint32 i = 0; table[i].Name != NULL; ++i) + { + // must be available (ignore handler existence for show command with possibe avalable subcomands + if (!isAvailable(table[i])) + continue; + + if (strlen(table[i].Name)) + continue; + + if (!table[i].Help.empty()) + SendSysMessage(table[i].Help.c_str()); + + if (table[i].ChildCommands) + if (ShowHelpForSubCommands(table[i].ChildCommands,"","")) + return true; + + return !table[i].Help.empty(); + } + } + + return ShowHelpForSubCommands(table,"",cmd); +} + +//Note: target_guid used only in CHAT_MSG_WHISPER_INFORM mode (in this case channelName ignored) +void ChatHandler::FillMessageData(WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker) +{ + uint32 messageLength = (message ? strlen(message) : 0) + 1; + + data->Initialize(SMSG_MESSAGECHAT, 100); // guess size + *data << uint8(type); + if ((type != CHAT_MSG_CHANNEL && type != CHAT_MSG_WHISPER) || language == LANG_ADDON) + *data << uint32(language); + else + *data << uint32(LANG_UNIVERSAL); + + switch(type) + { + case CHAT_MSG_SAY: + case CHAT_MSG_PARTY: + case CHAT_MSG_PARTY_LEADER: + case CHAT_MSG_RAID: + case CHAT_MSG_GUILD: + case CHAT_MSG_OFFICER: + case CHAT_MSG_YELL: + case CHAT_MSG_WHISPER: + case CHAT_MSG_CHANNEL: + case CHAT_MSG_RAID_LEADER: + case CHAT_MSG_RAID_WARNING: + case CHAT_MSG_BG_SYSTEM_NEUTRAL: + case CHAT_MSG_BG_SYSTEM_ALLIANCE: + case CHAT_MSG_BG_SYSTEM_HORDE: + case CHAT_MSG_BATTLEGROUND: + case CHAT_MSG_BATTLEGROUND_LEADER: + target_guid = session ? session->GetPlayer()->GetGUID() : 0; + break; + case CHAT_MSG_MONSTER_SAY: + case CHAT_MSG_MONSTER_PARTY: + case CHAT_MSG_MONSTER_YELL: + case CHAT_MSG_MONSTER_WHISPER: + case CHAT_MSG_MONSTER_EMOTE: + case CHAT_MSG_RAID_BOSS_WHISPER: + case CHAT_MSG_RAID_BOSS_EMOTE: + case CHAT_MSG_BATTLENET: + { + *data << uint64(speaker->GetGUID()); + *data << uint32(0); // 2.1.0 + *data << uint32(strlen(speaker->GetName()) + 1); + *data << speaker->GetName(); + uint64 listener_guid = 0; + *data << uint64(listener_guid); + if (listener_guid && !IS_PLAYER_GUID(listener_guid)) + { + *data << uint32(1); // string listener_name_length + *data << uint8(0); // string listener_name + } + *data << uint32(messageLength); + *data << message; + *data << uint8(0); + return; + } + default: + if (type != CHAT_MSG_WHISPER_INFORM && type != CHAT_MSG_IGNORED && type != CHAT_MSG_DND && type != CHAT_MSG_AFK) + target_guid = 0; // only for CHAT_MSG_WHISPER_INFORM used original value target_guid + break; + } + + *data << uint64(target_guid); // there 0 for BG messages + *data << uint32(0); // can be chat msg group or something + + if (type == CHAT_MSG_CHANNEL) + { + ASSERT(channelName); + *data << channelName; + } + + *data << uint64(target_guid); + *data << uint32(messageLength); + *data << message; + if (session != 0 && type != CHAT_MSG_WHISPER_INFORM && type != CHAT_MSG_DND && type != CHAT_MSG_AFK) + *data << uint8(session->GetPlayer()->chatTag()); + else + *data << uint8(0); +} + +Player * ChatHandler::getSelectedPlayer() +{ + if (!m_session) + return NULL; + + uint64 guid = m_session->GetPlayer()->GetSelection(); + + if (guid == 0) + return m_session->GetPlayer(); + + return objmgr.GetPlayer(guid); +} + +Unit* ChatHandler::getSelectedUnit() +{ + if (!m_session) + return NULL; + + uint64 guid = m_session->GetPlayer()->GetSelection(); + + if (guid == 0) + return m_session->GetPlayer(); + + return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid); +} + +WorldObject *ChatHandler::getSelectedObject() +{ + if (!m_session) + return NULL; + + uint64 guid = m_session->GetPlayer()->GetSelection(); + + if (guid == 0) + return GetNearbyGameObject(); + + return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid); +} + +Creature* ChatHandler::getSelectedCreature() +{ + if (!m_session) + return NULL; + + return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection()); +} + +char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1) +{ + // skip empty + if (!text) + return NULL; + + // skip spaces + while (*text == ' '||*text == '\t'||*text == '\b') + ++text; + + if (!*text) + return NULL; + + // return non link case + if (text[0] != '|') + return strtok(text, " "); + + // [name] Shift-click form |color|linkType:key|h[name]|h|r + // or + // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r + + char* check = strtok(text, "|"); // skip color + if (!check) + return NULL; // end of data + + char* cLinkType = strtok(NULL, ":"); // linktype + if (!cLinkType) + return NULL; // end of data + + if (strcmp(cLinkType,linkType) != 0) + { + strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function + SendSysMessage(LANG_WRONG_LINK_TYPE); + return NULL; + } + + char* cKeys = strtok(NULL, "|"); // extract keys and values + char* cKeysTail = strtok(NULL, ""); + + char* cKey = strtok(cKeys, ":|"); // extract key + if (something1) + *something1 = strtok(NULL, ":|"); // extract something + + strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces + strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function + return cKey; +} + +char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1) +{ + // skip empty + if (!text) + return NULL; + + // skip spaces + while (*text == ' '||*text == '\t'||*text == '\b') + ++text; + + if (!*text) + return NULL; + + // return non link case + if (text[0] != '|') + return strtok(text, " "); + + // [name] Shift-click form |color|linkType:key|h[name]|h|r + // or + // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r + // or + // [name] Shift-click form |linkType:key|h[name]|h|r + + char* tail; + + if (text[1] == 'c') + { + char* check = strtok(text, "|"); // skip color + if (!check) + return NULL; // end of data + + tail = strtok(NULL, ""); // tail + } + else + tail = text+1; // skip first | + + char* cLinkType = strtok(tail, ":"); // linktype + if (!cLinkType) + return NULL; // end of data + + for (int i = 0; linkTypes[i]; ++i) + { + if (strcmp(cLinkType,linkTypes[i]) == 0) + { + char* cKeys = strtok(NULL, "|"); // extract keys and values + char* cKeysTail = strtok(NULL, ""); + + char* cKey = strtok(cKeys, ":|"); // extract key + if (something1) + *something1 = strtok(NULL, ":|"); // extract something + + strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces + strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function + if (found_idx) + *found_idx = i; + return cKey; + } + } + + strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function + SendSysMessage(LANG_WRONG_LINK_TYPE); + return NULL; +} + +char const *fmtstring(char const *format, ...) +{ + va_list argptr; + #define MAX_FMT_STRING 32000 + static char temp_buffer[MAX_FMT_STRING]; + static char string[MAX_FMT_STRING]; + static int index = 0; + char *buf; + int len; + + va_start(argptr, format); + vsnprintf(temp_buffer,MAX_FMT_STRING, format, argptr); + va_end(argptr); + + len = strlen(temp_buffer); + + if (len >= MAX_FMT_STRING) + return "ERROR"; + + if (len + index >= MAX_FMT_STRING-1) + { + index = 0; + } + + buf = &string[index]; + memcpy(buf, temp_buffer, len+1); + + index += len + 1; + + return buf; +} + +GameObject* ChatHandler::GetNearbyGameObject() +{ + if (!m_session) + return NULL; + + Player* pl = m_session->GetPlayer(); + GameObject* obj = NULL; + Trinity::NearestGameObjectCheck check(*pl); + Trinity::GameObjectLastSearcher searcher(pl, obj, check); + pl->VisitNearbyGridObject(999, searcher); + return obj; +} + +GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry) +{ + if (!m_session) + return NULL; + + Player* pl = m_session->GetPlayer(); + + GameObject* obj = pl->GetMap()->GetGameObject(MAKE_NEW_GUID(lowguid, entry, HIGHGUID_GAMEOBJECT)); + + if (!obj && objmgr.GetGOData(lowguid)) // guid is DB guid of object + { + // search near player then + CellPair p(Trinity::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + Trinity::GameObjectWithDbGUIDCheck go_check(*pl,lowguid); + Trinity::GameObjectSearcher checker(pl,obj,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + cell.Visit(p, object_checker, *pl->GetMap()); + } + + return obj; +} + +enum SpellLinkType +{ + SPELL_LINK_SPELL = 0, + SPELL_LINK_TALENT = 1, + SPELL_LINK_ENCHANT = 2, + SPELL_LINK_TRADE = 3, + SPELL_LINK_GLYPH = 4 +}; + +static char const* const spellKeys[] = +{ + "Hspell", // normal spell + "Htalent", // talent spell + "Henchant", // enchanting recipe spell + "Htrade", // profession/skill spell + "Hglyph", // glyph + 0 +}; + +uint32 ChatHandler::extractSpellIdFromLink(char* text) +{ + // number or [name] Shift-click form |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r + // number or [name] Shift-click form |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r + // number or [name] Shift-click form |color|Htalent:talent_id,rank|h[name]|h|r + // number or [name] Shift-click form |color|Htrade:spell_id,skill_id,max_value,cur_value|h[name]|h|r + int type = 0; + char* param1_str = NULL; + char* idS = extractKeyFromLink(text,spellKeys,&type,¶m1_str); + if (!idS) + return 0; + + uint32 id = (uint32)atol(idS); + + switch(type) + { + case SPELL_LINK_SPELL: + return id; + case SPELL_LINK_TALENT: + { + // talent + TalentEntry const* talentEntry = sTalentStore.LookupEntry(id); + if (!talentEntry) + return 0; + + int32 rank = param1_str ? (uint32)atol(param1_str) : 0; + if (rank >= MAX_TALENT_RANK) + return 0; + + if (rank < 0) + rank = 0; + + return talentEntry->RankID[rank]; + } + case SPELL_LINK_ENCHANT: + case SPELL_LINK_TRADE: + return id; + case SPELL_LINK_GLYPH: + { + uint32 glyph_prop_id = param1_str ? (uint32)atol(param1_str) : 0; + + GlyphPropertiesEntry const* glyphPropEntry = sGlyphPropertiesStore.LookupEntry(glyph_prop_id); + if (!glyphPropEntry) + return 0; + + return glyphPropEntry->SpellId; + } + } + + // unknown type? + return 0; +} + +GameTele const* ChatHandler::extractGameTeleFromLink(char* text) +{ + // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r + char* cId = extractKeyFromLink(text,"Htele"); + if (!cId) + return false; + + // id case (explicit or from shift link) + if (cId[0] >= '0' || cId[0] >= '9') + if (uint32 id = atoi(cId)) + return objmgr.GetGameTele(id); + + return objmgr.GetGameTele(cId); +} + +enum GuidLinkType +{ + SPELL_LINK_PLAYER = 0, // must be first for selection in not link case + SPELL_LINK_CREATURE = 1, + SPELL_LINK_GAMEOBJECT = 2 +}; + +static char const* const guidKeys[] = +{ + "Hplayer", + "Hcreature", + "Hgameobject", + 0 +}; + +uint64 ChatHandler::extractGuidFromLink(char* text) +{ + int type = 0; + + // |color|Hcreature:creature_guid|h[name]|h|r + // |color|Hgameobject:go_guid|h[name]|h|r + // |color|Hplayer:name|h[name]|h|r + char* idS = extractKeyFromLink(text,guidKeys,&type); + if (!idS) + return 0; + + switch(type) + { + case SPELL_LINK_PLAYER: + { + std::string name = idS; + if (!normalizePlayerName(name)) + return 0; + + if (Player* player = objmgr.GetPlayer(name.c_str())) + return player->GetGUID(); + + if (uint64 guid = objmgr.GetPlayerGUIDByName(name)) + return guid; + + return 0; + } + case SPELL_LINK_CREATURE: + { + uint32 lowguid = (uint32)atol(idS); + + if (CreatureData const* data = objmgr.GetCreatureData(lowguid)) + return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT); + else + return 0; + } + case SPELL_LINK_GAMEOBJECT: + { + uint32 lowguid = (uint32)atol(idS); + + if (GameObjectData const* data = objmgr.GetGOData(lowguid)) + return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_GAMEOBJECT); + else + return 0; + } + } + + // unknown type? + return 0; +} + +std::string ChatHandler::extractPlayerNameFromLink(char* text) +{ + // |color|Hplayer:name|h[name]|h|r + char* name_str = extractKeyFromLink(text,"Hplayer"); + if (!name_str) + return ""; + + std::string name = name_str; + if (!normalizePlayerName(name)) + return ""; + + return name; +} + +bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* player_guid /*=NULL*/,std::string* player_name /*= NULL*/) +{ + if (args && *args) + { + std::string name = extractPlayerNameFromLink(args); + if (name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + Player* pl = objmgr.GetPlayer(name.c_str()); + + // if allowed player pointer + if (player) + *player = pl; + + // if need guid value from DB (in name case for check player existence) + uint64 guid = !pl && (player_guid || player_name) ? objmgr.GetPlayerGUIDByName(name) : 0; + + // if allowed player guid (if no then only online players allowed) + if (player_guid) + *player_guid = pl ? pl->GetGUID() : guid; + + if (player_name) + *player_name = pl || guid ? name : ""; + } + else + { + Player* pl = getSelectedPlayer(); + // if allowed player pointer + if (player) + *player = pl; + // if allowed player guid (if no then only online players allowed) + if (player_guid) + *player_guid = pl ? pl->GetGUID() : 0; + + if (player_name) + *player_name = pl ? pl->GetName() : ""; + } + + // some from req. data must be provided (note: name is empty if player not exist) + if ((!player || !*player) && (!player_guid || !*player_guid) && (!player_name || player_name->empty())) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2) +{ + char* p1 = strtok(args, " "); + char* p2 = strtok(NULL, " "); + + if (!p2) + { + p2 = p1; + p1 = NULL; + } + + if (arg1) + *arg1 = p1; + + if (arg2) + *arg2 = p2; +} + +char* ChatHandler::extractQuotedArg(char* args) +{ + if (!*args) + return NULL; + + if (*args == '"') + return strtok(args+1, "\""); + else + { + char* space = strtok(args, "\""); + if (!space) + return false; + return strtok(NULL, "\""); + } +} + +bool ChatHandler::needReportToTarget(Player* chr) const +{ + Player* pl = m_session->GetPlayer(); + return pl != chr && pl->IsVisibleGloballyFor(chr); +} + +LocaleConstant ChatHandler::GetSessionDbcLocale() const +{ + return m_session->GetSessionDbcLocale(); +} + +int ChatHandler::GetSessionDbLocaleIndex() const +{ + return m_session->GetSessionDbLocaleIndex(); +} + +const char *CliHandler::GetTrinityString(int32 entry) const +{ + return objmgr.GetTrinityStringForDBCLocale(entry); +} + +bool CliHandler::isAvailable(ChatCommand const& cmd) const +{ + // skip non-console commands in console case + return cmd.AllowConsole; +} + +void CliHandler::SendSysMessage(const char *str) +{ + m_print(str); + m_print("\r\n"); +} + +std::string CliHandler::GetNameLink() const +{ + return GetTrinityString(LANG_CONSOLE_COMMAND); +} + +bool CliHandler::needReportToTarget(Player* /*chr*/) const +{ + return true; +} + +bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline) +{ + plr = NULL; + guid = 0; + + if (cname) + { + std::string name = cname; + if (!name.empty()) + { + if (!normalizePlayerName(name)) + { + PSendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + plr = objmgr.GetPlayer(name.c_str()); + if (offline) + guid = objmgr.GetPlayerGUIDByName(name.c_str()); + } + } + + if (plr) + { + group = plr->GetGroup(); + if (!guid || !offline) + guid = plr->GetGUID(); + } + else + { + if (getSelectedPlayer()) + plr = getSelectedPlayer(); + else + plr = m_session->GetPlayer(); + + if (!guid || !offline) + guid = plr->GetGUID(); + group = plr->GetGroup(); + } + + return true; +} + +LocaleConstant CliHandler::GetSessionDbcLocale() const +{ + return sWorld.GetDefaultDbcLocale(); +} + +int CliHandler::GetSessionDbLocaleIndex() const +{ + return objmgr.GetDBCLocaleIndex(); +} diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h new file mode 100644 index 00000000000..58e6f6214c8 --- /dev/null +++ b/src/server/game/Chat/Chat.h @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITYCORE_CHAT_H +#define TRINITYCORE_CHAT_H + +#include "SharedDefines.h" + +class ChatHandler; +class WorldSession; +class Creature; +class Player; +class Unit; +struct GameTele; + +class ChatCommand +{ + public: + const char * Name; + uint32 SecurityLevel; // function pointer required correct align (use uint32) + bool AllowConsole; + bool (ChatHandler::*Handler)(const char* args); + std::string Help; + ChatCommand * ChildCommands; +}; + +class ChatHandler +{ + public: + explicit ChatHandler(WorldSession* session) : m_session(session) {} + explicit ChatHandler(Player* player) : m_session(player->GetSession()) {} + ~ChatHandler() {} + + static void FillMessageData(WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker); + + void FillMessageData(WorldPacket *data, uint8 type, uint32 language, uint64 target_guid, const char* message) + { + FillMessageData(data, m_session, type, language, NULL, target_guid, message, NULL); + } + + void FillSystemMessageData(WorldPacket *data, const char* message) + { + FillMessageData(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, 0, message); + } + + static char* LineFromMessage(char*& pos) { char* start = strtok(pos,"\n"); pos = NULL; return start; } + + // function with different implementation for chat/console + virtual const char *GetTrinityString(int32 entry) const; + virtual void SendSysMessage(const char *str); + + void SendSysMessage(int32 entry); + void PSendSysMessage(const char *format, ...) ATTR_PRINTF(2,3); + void PSendSysMessage(int32 entry, ...); + std::string PGetParseString(int32 entry, ...); + + int ParseCommands(const char* text); + + static ChatCommand* getCommandTable(); + + bool isValidChatMessage(const char* msg); + void SendGlobalSysMessage(const char *str); + protected: + explicit ChatHandler() : m_session(NULL) {} // for CLI subclass + + bool hasStringAbbr(const char* name, const char* part); + + // function with different implementation for chat/console + virtual bool isAvailable(ChatCommand const& cmd) const; + virtual std::string GetNameLink() const { return GetNameLink(m_session->GetPlayer()); } + virtual bool needReportToTarget(Player* chr) const; + virtual LocaleConstant GetSessionDbcLocale() const; + virtual int GetSessionDbLocaleIndex() const; + + bool HasLowerSecurity(Player* target, uint64 guid, bool strong = false); + bool HasLowerSecurityAccount(WorldSession* target, uint32 account, bool strong = false); + + void SendGlobalGMSysMessage(const char *str); + + static bool SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand); + bool ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcommand); + bool ShowHelpForCommand(ChatCommand *table, const char* cmd); + bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd); + + bool HandleAccountCommand(const char* args); + bool HandleAccountAddonCommand(const char* args); + bool HandleAccountCreateCommand(const char* args); + bool HandleAccountDeleteCommand(const char* args); + bool HandleAccountLockCommand(const char* args); + bool HandleAccountOnlineListCommand(const char* args); + bool HandleAccountPasswordCommand(const char* args); + bool HandleAccountSetAddonCommand(const char* args); + bool HandleAccountSetGmLevelCommand(const char* args); + bool HandleAccountSetPasswordCommand(const char* args); + + bool HandleAHBotOptionsCommand(const char * args); + bool HandleNameAnnounceCommand(const char* args); + bool HandleGMNameAnnounceCommand(const char* args); + bool HandleGMAnnounceCommand(const char* args); + bool HandleGMNotifyCommand(const char* args); + + bool HandleBanAccountCommand(const char* args); + bool HandleBanCharacterCommand(const char* args); + bool HandleBanIPCommand(const char* args); + bool HandleBanInfoAccountCommand(const char* args); + bool HandleBanInfoCharacterCommand(const char* args); + bool HandleBanInfoIPCommand(const char* args); + bool HandleBanListAccountCommand(const char* args); + bool HandleBanListCharacterCommand(const char* args); + bool HandleBanListIPCommand(const char* args); + + bool HandleCastCommand(const char *args); + bool HandleCastBackCommand(const char *args); + bool HandleCastDistCommand(const char *args); + bool HandleCastSelfCommand(const char *args); + bool HandleCastTargetCommand(const char *args); + + bool HandleCharacterCustomizeCommand(const char * args); + bool HandleCharacterDeleteCommand(const char* args); + bool HandleCharacterLevelCommand(const char* args); + bool HandleCharacterRenameCommand(const char * args); + bool HandleCharacterReputationCommand(const char* args); + bool HandleCharacterTitlesCommand(const char* args); + + bool HandleChannelSetPublic(const char *args); + + bool HandleDebugAnimCommand(const char* args); + bool HandleDebugArenaCommand(const char * args); + bool HandleDebugBattlegroundCommand(const char * args); + bool HandleDebugGetItemStateCommand(const char * args); + bool HandleDebugGetLootRecipientCommand(const char * args); + bool HandleDebugGetValueCommand(const char* args); + bool HandleDebugGetItemValueCommand(const char* args); + bool HandleDebugMod32ValueCommand(const char* args); + bool HandleDebugSetAuraStateCommand(const char * args); + bool HandleDebugSetItemValueCommand(const char * args); + bool HandleDebugItemExpireCommand(const char * args); + bool HandleDebugSetVehicleId(const char * args); + bool HandleDebugEnterVehicle(const char * args); + bool HandleDebugSetValueCommand(const char* args); + bool HandleDebugSpawnVehicle(const char * args); + bool HandleDebugSpellCheckCommand(const char* args); + bool HandleDebugUpdateCommand(const char* args); + bool HandleDebugUpdateWorldStateCommand(const char* args); + + bool HandleDebugSet32Bit(const char* args); + bool HandleDebugThreatList(const char * args); + bool HandleDebugHostileRefList(const char * args); + bool HandlePossessCommand(const char* args); + bool HandleUnPossessCommand(const char* args); + bool HandleBindSightCommand(const char* args); + bool HandleUnbindSightCommand(const char* args); + + bool HandleDebugPlayCinematicCommand(const char* args); + bool HandleDebugPlayMovieCommand(const char* args); + bool HandleDebugPlaySoundCommand(const char* args); + + bool HandleDebugSendBuyErrorCommand(const char* args); + bool HandleDebugSendChannelNotifyCommand(const char* args); + bool HandleDebugSendChatMsgCommand(const char* args); + bool HandleDebugSendEquipErrorCommand(const char* args); + bool HandleDebugSendLargePacketCommand(const char * args); + bool HandleDebugSendOpcodeCommand(const char* args); + bool HandleDebugSendPoiCommand(const char* args); + bool HandleDebugSendQuestPartyMsgCommand(const char* args); + bool HandleDebugSendQuestInvalidMsgCommand(const char* args); + bool HandleDebugSendSellErrorCommand(const char* args); + bool HandleDebugSendSetPhaseShiftCommand(const char * args); + bool HandleDebugSendSpellFailCommand(const char* args); + + bool HandleEventActiveListCommand(const char* args); + bool HandleEventStartCommand(const char* args); + bool HandleEventStopCommand(const char* args); + bool HandleEventInfoCommand(const char* args); + + bool HandleGameObjectAddCommand(const char* args); + bool HandleGameObjectDeleteCommand(const char* args); + bool HandleGOInfoCommand(const char* args); + bool HandleGameObjectMoveCommand(const char* args); + bool HandleGameObjectNearCommand(const char* args); + bool HandleGameObjectPhaseCommand(const char* args); + bool HandleGameObjectStateCommand(const char* args); + bool HandleGameObjectTargetCommand(const char* args); + bool HandleGameObjectTurnCommand(const char* args); + + bool HandleGMCommand(const char* args); + bool HandleGMChatCommand(const char* args); + bool HandleGMFlyCommand(const char* args); + bool HandleGMListFullCommand(const char* args); + bool HandleGMListIngameCommand(const char* args); + bool HandleGMVisibleCommand(const char* args); + + bool HandleGoCommand(const char* args); + bool HandleGoCreatureCommand(const char* args); + bool HandleGoGraveyardCommand(const char* args); + bool HandleGoGridCommand(const char* args); + bool HandleGoObjectCommand(const char* args); + bool HandleGoTaxinodeCommand(const char* args); + bool HandleGoTriggerCommand(const char* args); + bool HandleGoXYCommand(const char* args); + bool HandleGoXYZCommand(const char* args); + bool HandleGoZoneXYCommand(const char* args); + + bool HandleGoTicketCommand(const char* args); + + bool HandleGuildCreateCommand(const char* args); + bool HandleGuildInviteCommand(const char* args); + bool HandleGuildUninviteCommand(const char* args); + bool HandleGuildRankCommand(const char* args); + bool HandleGuildDeleteCommand(const char* args); + + bool HandleHonorAddCommand(const char* args); + bool HandleHonorAddKillCommand(const char* args); + bool HandleHonorUpdateCommand(const char* args); + + bool HandleInstanceListBindsCommand(const char* args); + bool HandleInstanceUnbindCommand(const char* args); + bool HandleInstanceStatsCommand(const char* args); + bool HandleInstanceSaveDataCommand(const char * args); + bool HandleInstanceOpenCommand(const char * args); + bool HandleInstanceCloseCommand(const char * args); + bool HandleInstanceOpenCloseCommand(const char * args, bool open); + + bool HandleLearnCommand(const char* args); + bool HandleLearnAllCommand(const char* args); + bool HandleLearnAllGMCommand(const char* args); + bool HandleLearnAllCraftsCommand(const char* args); + bool HandleLearnAllRecipesCommand(const char* args); + bool HandleLearnAllDefaultCommand(const char* args); + bool HandleLearnAllLangCommand(const char* args); + bool HandleLearnAllMyClassCommand(const char* args); + bool HandleLearnAllMyPetTalentsCommand(const char* args); + bool HandleLearnAllMySpellsCommand(const char* args); + bool HandleLearnAllMyTalentsCommand(const char* args); + + bool HandleListAurasCommand(const char * args); + bool HandleListCreatureCommand(const char* args); + bool HandleListItemCommand(const char* args); + bool HandleListObjectCommand(const char* args); + + bool HandleLookupAreaCommand(const char* args); + bool HandleLookupCreatureCommand(const char* args); + bool HandleLookupEventCommand(const char* args); + bool HandleLookupFactionCommand(const char * args); + bool HandleLookupItemCommand(const char * args); + bool HandleLookupItemSetCommand(const char * args); + bool HandleLookupObjectCommand(const char* args); + bool HandleLookupPlayerIpCommand(const char* args); + bool HandleLookupPlayerAccountCommand(const char* args); + bool HandleLookupPlayerEmailCommand(const char* args); + bool HandleLookupQuestCommand(const char* args); + bool HandleLookupSkillCommand(const char* args); + bool HandleLookupSpellCommand(const char* args); + bool HandleLookupTaxiNodeCommand(const char * args); + bool HandleLookupTeleCommand(const char * args); + bool HandleLookupMapCommand(const char* args); + bool HandleLookupTitleCommand(const char * args); + + bool HandleModifyHPCommand(const char* args); + bool HandleModifyManaCommand(const char* args); + bool HandleModifyRageCommand(const char* args); + bool HandleModifyRunicPowerCommand(const char* args); + bool HandleModifyEnergyCommand(const char* args); + bool HandleModifyMoneyCommand(const char* args); + bool HandleModifyASpeedCommand(const char* args); + bool HandleModifySpeedCommand(const char* args); + bool HandleModifyBWalkCommand(const char* args); + bool HandleModifyFlyCommand(const char* args); + bool HandleModifySwimCommand(const char* args); + bool HandleModifyScaleCommand(const char* args); + bool HandleModifyMountCommand(const char* args); + bool HandleModifyBitCommand(const char* args); + bool HandleModifyFactionCommand(const char* args); + bool HandleModifySpellCommand(const char* args); + bool HandleModifyTalentCommand (const char* args); + bool HandleModifyHonorCommand (const char* args); + bool HandleModifyRepCommand(const char* args); + bool HandleModifyArenaCommand(const char* args); + bool HandleModifyPhaseCommand(const char* args); + bool HandleModifyGenderCommand(const char* args); + + //-----------------------Npc Commands----------------------- + bool HandleNpcAddCommand(const char* args); + bool HandleNpcAddMoveCommand(const char* args); + bool HandleNpcAddVendorItemCommand(const char* args); + bool HandleNpcAllowMovementCommand(const char* args); + bool HandleNpcChangeEntryCommand(const char *args); + bool HandleNpcChangeLevelCommand(const char* args); + bool HandleNpcDeleteCommand(const char* args); + bool HandleNpcDelVendorItemCommand(const char* args); + bool HandleNpcFactionIdCommand(const char* args); + bool HandleNpcFlagCommand(const char* args); + bool HandleNpcFollowCommand(const char* args); + bool HandleNpcInfoCommand(const char* args); + bool HandleNpcMoveCommand(const char* args); + bool HandleNpcPlayEmoteCommand(const char* args); + bool HandleNpcSayCommand(const char* args); + bool HandleNpcSetDeathStateCommand(const char* args); + bool HandleNpcSetModelCommand(const char* args); + bool HandleNpcSetMoveTypeCommand(const char* args); + bool HandleNpcSetPhaseCommand(const char* args); + bool HandleNpcSpawnDistCommand(const char* args); + bool HandleNpcSpawnTimeCommand(const char* args); + bool HandleNpcTameCommand(const char* args); + bool HandleNpcTextEmoteCommand(const char* args); + bool HandleNpcUnFollowCommand(const char* args); + bool HandleNpcWhisperCommand(const char* args); + bool HandleNpcYellCommand(const char* args); + bool HandleNpcAddFormationCommand(const char* args); + bool HandleNpcSetLinkCommand(const char* args); + + //TODO: NpcCommands that needs to be fixed : + bool HandleNpcAddWeaponCommand(const char* args); + bool HandleNpcNameCommand(const char* args); + bool HandleNpcSubNameCommand(const char* args); + //---------------------------------------------------------- + + bool HandlePDumpLoadCommand(const char *args); + bool HandlePDumpWriteCommand(const char *args); + + bool HandleQuestAdd(const char * args); + bool HandleQuestRemove(const char * args); + bool HandleQuestComplete(const char * args); + + bool HandleReloadAllCommand(const char* args); + bool HandleReloadAllAchievementCommand(const char* args); + bool HandleReloadAllAreaCommand(const char* args); + bool HandleReloadAllItemCommand(const char* args); + bool HandleReloadAllLootCommand(const char* args); + bool HandleReloadAllNpcCommand(const char* args); + bool HandleReloadAllQuestCommand(const char* args); + bool HandleReloadAllScriptsCommand(const char* args); + bool HandleReloadAllEventAICommand(const char* args); + bool HandleReloadAllSpellCommand(const char* args); + bool HandleReloadAllLocalesCommand(const char* args); + + bool HandleReloadConfigCommand(const char* args); + + bool HandleReloadAccessRequirementCommand(const char* args); + bool HandleReloadAchievementCriteriaDataCommand(const char* args); + bool HandleReloadAchievementRewardCommand(const char* args); + bool HandleReloadAreaTriggerTavernCommand(const char* args); + bool HandleReloadAreaTriggerTeleportCommand(const char* args); + bool HandleReloadAutobroadcastCommand(const char* args); + bool HandleReloadEventScriptsCommand(const char* args); + bool HandleReloadEventAITextsCommand(const char* args); + bool HandleReloadEventAISummonsCommand(const char* args); + bool HandleReloadEventAIScriptsCommand(const char* args); + bool HandleReloadCommandCommand(const char* args); + bool HandleReloadCreatureTemplateCommand(const char* args); + bool HandleReloadCreatureQuestRelationsCommand(const char* args); + bool HandleReloadCreatureQuestInvRelationsCommand(const char* args); + bool HandleReloadCreatureLinkedRespawnCommand(const char* args); + bool HandleReloadDbScriptStringCommand(const char* args); + bool HandleReloadGameGraveyardZoneCommand(const char* args); + bool HandleReloadGameObjectScriptsCommand(const char* args); + bool HandleReloadGameTeleCommand(const char* args); + bool HandleReloadGossipMenuCommand(const char* args); + bool HandleReloadGossipMenuOptionCommand(const char* args); + bool HandleReloadGOQuestRelationsCommand(const char* args); + bool HandleReloadGOQuestInvRelationsCommand(const char* args); + bool HandleReloadItemEnchantementsCommand(const char* args); + bool HandleReloadLocalesAchievementRewardCommand(const char* args); + bool HandleReloadLocalesCreatureCommand(const char* args); + bool HandleReloadLocalesGameobjectCommand(const char* args); + bool HandleReloadLocalesItemCommand(const char* args); + bool HandleReloadLocalesNpcTextCommand(const char* args); + bool HandleReloadLocalesPageTextCommand(const char* args); + bool HandleReloadLocalesPointsOfInterestCommand(const char* args); + bool HandleReloadLocalesQuestCommand(const char* args); +// bool HandleReloadAuctionsCommand(const char* args); + bool HandleReloadLootTemplatesCreatureCommand(const char* args); + bool HandleReloadLootTemplatesDisenchantCommand(const char* args); + bool HandleReloadLootTemplatesFishingCommand(const char* args); + bool HandleReloadLootTemplatesGameobjectCommand(const char* args); + bool HandleReloadLootTemplatesItemCommand(const char* args); + bool HandleReloadLootTemplatesMailCommand(const char* args); + bool HandleReloadMailLevelRewardCommand(const char* args); + bool HandleReloadLootTemplatesMillingCommand(const char* args); + bool HandleReloadLootTemplatesPickpocketingCommand(const char* args); + bool HandleReloadLootTemplatesProspectingCommand(const char* args); + bool HandleReloadLootTemplatesReferenceCommand(const char* args); + bool HandleReloadLootTemplatesSkinningCommand(const char* args); + bool HandleReloadLootTemplatesSpellCommand(const char* args); + bool HandleReloadTrinityStringCommand(const char* args); + bool HandleReloadNpcGossipCommand(const char* args); + bool HandleReloadNpcTrainerCommand(const char* args); + bool HandleReloadNpcVendorCommand(const char* args); + bool HandleReloadPageTextsCommand(const char* args); + bool HandleReloadPointsOfInterestCommand(const char* args); + bool HandleReloadSpellClickSpellsCommand(const char* args); + bool HandleReloadQuestAreaTriggersCommand(const char* args); + bool HandleReloadQuestEndScriptsCommand(const char* args); + bool HandleReloadQuestStartScriptsCommand(const char* args); + bool HandleReloadQuestTemplateCommand(const char* args); + bool HandleReloadReservedNameCommand(const char*); + bool HandleReloadSkillDiscoveryTemplateCommand(const char* args); + bool HandleReloadSkillExtraItemTemplateCommand(const char* args); + bool HandleReloadSkillFishingBaseLevelCommand(const char* args); + bool HandleReloadSpellRequiredCommand(const char* args); + bool HandleReloadSpellAreaCommand(const char* args); + bool HandleReloadSpellGroupsCommand(const char* args); + bool HandleReloadSpellLearnSpellCommand(const char* args); + bool HandleReloadSpellLinkedSpellCommand(const char* args); + bool HandleReloadSpellProcEventCommand(const char* args); + bool HandleReloadSpellBonusesCommand(const char* args); + bool HandleReloadSpellScriptsCommand(const char* args); + bool HandleReloadSpellTargetPositionCommand(const char* args); + bool HandleReloadSpellThreatsCommand(const char* args); + bool HandleReloadSpellPetAurasCommand(const char* args); + bool HandleReloadSpellDisabledCommand(const char* args); + bool HandleReloadSpellGroupStackRulesCommand(const char* args); + bool HandleReloadAuctionsCommand(const char* args); + bool HandleReloadWpScriptsCommand(const char* args); + bool HandleReloadConditions(const char* args); + + bool HandleResetAchievementsCommand(const char * args); + bool HandleResetAllCommand(const char * args); + bool HandleResetHonorCommand(const char * args); + bool HandleResetLevelCommand(const char * args); + bool HandleResetSpellsCommand(const char * args); + bool HandleResetStatsCommand(const char * args); + bool HandleResetTalentsCommand(const char * args); + + bool HandleSendItemsCommand(const char* args); + bool HandleSendMailCommand(const char* args); + bool HandleSendMessageCommand(const char * args); + bool HandleSendMoneyCommand(const char* args); + + bool HandleServerCorpsesCommand(const char* args); + bool HandleServerExitCommand(const char* args); + bool HandleServerIdleRestartCommand(const char* args); + bool HandleServerIdleShutDownCommand(const char* args); + bool HandleServerInfoCommand(const char* args); + bool HandleServerMotdCommand(const char* args); + bool HandleServerPLimitCommand(const char* args); + bool HandleServerRestartCommand(const char* args); + bool HandleServerSetLogLevelCommand(const char* args); + bool HandleServerSetMotdCommand(const char* args); + bool HandleServerShutDownCommand(const char* args); + bool HandleServerShutDownCancelCommand(const char* args); + bool HandleServerSetClosedCommand(const char* args); + + bool HandleServerSetLogFileLevelCommand(const char* args); + bool HandleServerSetDiffTimeCommand(const char* args); + + bool HandleTeleCommand(const char * args); + bool HandleTeleAddCommand(const char * args); + bool HandleTeleDelCommand(const char * args); + bool HandleTeleGroupCommand(const char* args); + bool HandleTeleNameCommand(const char* args); + + bool HandleTitlesAddCommand(const char* args); + bool HandleTitlesCurrentCommand(const char* args); + bool HandleTitlesRemoveCommand(const char* args); + bool HandleTitlesSetMaskCommand(const char* args); + + bool HandleUnBanAccountCommand(const char* args); + bool HandleUnBanCharacterCommand(const char* args); + bool HandleUnBanIPCommand(const char* args); + + bool HandleWpAddCommand(const char* args); + bool HandleWpLoadPathCommand(const char* args); + bool HandleWpUnLoadPathCommand(const char* args); + bool HandleWpModifyCommand(const char* args); + bool HandleWpEventCommand(const char* args); + bool HandleWpShowCommand(const char* args); + bool HandleReloadAllPaths(const char *args); + + bool HandleHelpCommand(const char* args); + bool HandleCommandsCommand(const char* args); + bool HandleStartCommand(const char* args); + bool HandleDismountCommand(const char* args); + bool HandleSaveCommand(const char* args); + + bool HandleNamegoCommand(const char* args); + bool HandleGonameCommand(const char* args); + bool HandleGroupgoCommand(const char* args); + bool HandleRecallCommand(const char* args); + bool HandleAnnounceCommand(const char* args); + bool HandleNotifyCommand(const char* args); + bool HandleGPSCommand(const char* args); + bool HandleTaxiCheatCommand(const char* args); + bool HandleWhispersCommand(const char* args); + bool HandleModifyDrunkCommand(const char* args); + + bool HandleGUIDCommand(const char* args); + bool HandleItemMoveCommand(const char* args); + bool HandleDeMorphCommand(const char* args); + bool HandlePInfoCommand(const char* args); + bool HandleMuteCommand(const char* args); + bool HandleUnmuteCommand(const char* args); + bool HandleMovegensCommand(const char* args); + bool HandleFreezeCommand(const char *args); + bool HandleUnFreezeCommand(const char *args); + bool HandleListFreezeCommand(const char* args); + + bool HandleCooldownCommand(const char* args); + bool HandleUnLearnCommand(const char* args); + bool HandleGetDistanceCommand(const char* args); + bool HandleModifyStandStateCommand(const char* args); + bool HandleDieCommand(const char* args); + bool HandleDamageCommand(const char *args); + bool HandleReviveCommand(const char* args); + bool HandleModifyMorphCommand(const char* args); + bool HandleAuraCommand(const char* args); + bool HandleUnAuraCommand(const char* args); + bool HandleLinkGraveCommand(const char* args); + bool HandleNearGraveCommand(const char* args); + bool HandleActivateObjectCommand(const char* args); + bool HandleSpawnTransportCommand(const char* args); + bool HandleExploreCheatCommand(const char* args); + bool HandleHoverCommand(const char* args); + bool HandleWaterwalkCommand(const char* args); + bool HandleLevelUpCommand(const char* args); + bool HandleShowAreaCommand(const char* args); + bool HandleHideAreaCommand(const char* args); + bool HandleAddItemCommand(const char* args); + bool HandleAddItemSetCommand(const char* args); + bool HandlePetTpCommand(const char* args); + bool HandlePetUnlearnCommand(const char* args); + bool HandlePetLearnCommand(const char* args); + bool HandleCreatePetCommand(const char* args); + + bool HandleGroupLeaderCommand(const char* args); + bool HandleGroupDisbandCommand(const char* args); + bool HandleGroupRemoveCommand(const char* args); + + bool HandleBankCommand(const char* args); + bool HandleChangeWeather(const char* args); + bool HandleKickPlayerCommand(const char * args); + + // GM ticket command handlers + bool HandleGMTicketListCommand(const char* args); + bool HandleGMTicketListOnlineCommand(const char* args); + bool HandleGMTicketListClosedCommand(const char* args); + bool HandleGMTicketGetByIdCommand(const char* args); + bool HandleGMTicketGetByNameCommand(const char* args); + bool HandleGMTicketCloseByIdCommand(const char* args); + bool HandleGMTicketAssignToCommand(const char* args); + bool HandleGMTicketUnAssignCommand(const char* args); + bool HandleGMTicketCommentCommand(const char* args); + bool HandleGMTicketDeleteByIdCommand(const char* args); + bool HandleGMTicketReloadCommand(const char*); + + bool HandleMaxSkillCommand(const char* args); + bool HandleSetSkillCommand(const char* args); + bool HandleRespawnCommand(const char* args); + bool HandleComeToMeCommand(const char *args); + bool HandleCombatStopCommand(const char *args); + + /*bool HandleCharDeleteCommand(const char *args); + bool HandleSendMessageCommand(const char * args);*/ + + bool HandleFlushArenaPointsCommand(const char *args); + bool HandlePlayAllCommand(const char* args); + bool HandleRepairitemsCommand(const char* args); + + bool HandleTempGameObjectCommand(const char* args); + bool HandleTempAddSpwCommand(const char* args); + + //! Development Commands + + /*bool HandleQuestAdd(const char * args); + bool HandleQuestRemove(const char * args); + bool HandleQuestComplete(const char * args);*/ + + //bool HandleSet32Bit(const char* args); + bool HandleSaveAllCommand(const char* args); + + Player* getSelectedPlayer(); + Creature* getSelectedCreature(); + Unit* getSelectedUnit(); + WorldObject* getSelectedObject(); + + char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL); + char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL); + + // if args have single value then it return in arg2 and arg1 == NULL + void extractOptFirstArg(char* args, char** arg1, char** arg2); + char* extractQuotedArg(char* args); + + uint32 extractSpellIdFromLink(char* text); + uint64 extractGuidFromLink(char* text); + GameTele const* extractGameTeleFromLink(char* text); + bool GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline = false); + std::string extractPlayerNameFromLink(char* text); + // select by arg (name/link) or in-game selection online/offline player + bool extractPlayerTarget(char* args, Player** player, uint64* player_guid = NULL, std::string* player_name = NULL); + + std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; } + std::string GetNameLink(Player* chr) const { return playerLink(chr->GetName()); } + + GameObject* GetNearbyGameObject(); + GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry); + + // Utility methods for commands + bool LookupPlayerSearchCommand(QueryResult_AutoPtr result, int32 limit); + bool HandleBanListHelper(QueryResult_AutoPtr result); + bool HandleBanHelper(BanMode mode,char const* args); + bool HandleBanInfoHelper(uint32 accountid, char const* accountname); + bool HandleUnBanHelper(BanMode mode,char const* args); + void HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel); + void HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id); + + void SetSentErrorMessage(bool val){ sentErrorMessage = val;}; + private: + 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 +{ + public: + typedef void Print(char const*); + explicit CliHandler(Print* zprint) : m_print(zprint) {} + + // overwrite functions + const char *GetTrinityString(int32 entry) const; + bool isAvailable(ChatCommand const& cmd) const; + void SendSysMessage(const char *str); + std::string GetNameLink() const; + bool needReportToTarget(Player* chr) const; + LocaleConstant GetSessionDbcLocale() const; + int GetSessionDbLocaleIndex() const; + + private: + Print* m_print; +}; + +char const *fmtstring(char const *format, ...); + +#endif + diff --git a/src/server/game/Chat/ChatHandler.cpp b/src/server/game/Chat/ChatHandler.cpp new file mode 100644 index 00000000000..88e2b5473a5 --- /dev/null +++ b/src/server/game/Chat/ChatHandler.cpp @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Database/DatabaseEnv.h" + +#include "CellImpl.h" +#include "Chat.h" +#include "ChannelMgr.h" +#include "GridNotifiersImpl.h" +#include "Group.h" +#include "Guild.h" +#include "Language.h" +#include "Log.h" +#include "Opcodes.h" +#include "Player.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" +#include "Util.h" + +bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang) +{ + if (lang != LANG_ADDON) + { + // strip invisible characters for non-addon messages + if (sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) + stripLineInvisibleChars(msg); + + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR + && !ChatHandler(this).isValidChatMessage(msg.c_str())) + { + sLog.outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(), + GetPlayer()->GetGUIDLow(), msg.c_str()); + if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) + KickPlayer(); + return false; + } + } + + return true; +} + +void WorldSession::HandleMessagechatOpcode(WorldPacket & recv_data) +{ + uint32 type; + uint32 lang; + + recv_data >> type; + recv_data >> lang; + + if (type >= MAX_CHAT_MSG_TYPE) + { + sLog.outError("CHAT: Wrong message type received: %u", type); + return; + } + + //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang); + + // prevent talking at unknown language (cheating) + LanguageDesc const* langDesc = GetLanguageDescByID(lang); + if (!langDesc) + { + SendNotification(LANG_UNKNOWN_LANGUAGE); + return; + } + if (langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id)) + { + // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language) + Unit::AuraEffectList const& langAuras = _player->GetAuraEffectsByType(SPELL_AURA_COMPREHEND_LANGUAGE); + bool foundAura = false; + for (Unit::AuraEffectList::const_iterator i = langAuras.begin(); i != langAuras.end(); ++i) + { + if ((*i)->GetMiscValue() == int32(lang)) + { + foundAura = true; + break; + } + } + if (!foundAura) + { + SendNotification(LANG_NOT_LEARNED_LANGUAGE); + return; + } + } + + if (lang == LANG_ADDON) + { + if (sWorld.getConfig(CONFIG_CHATLOG_ADDON)) + { + std::string msg = ""; + recv_data >> msg; + + if (msg.empty()) + { + sLog.outDebug("Player %s send empty addon msg", GetPlayer()->GetName()); + return; + } + + sLog.outChat("[ADDON] Player %s sends: %s", + GetPlayer()->GetName(), msg.c_str()); + } + + // Disabled addon channel? + if (!sWorld.getConfig(CONFIG_ADDON_CHANNEL)) + return; + } + // LANG_ADDON should not be changed nor be affected by flood control + else + { + // send in universal language if player in .gmon mode (ignore spell effects) + if (_player->isGameMaster()) + lang = LANG_UNIVERSAL; + else + { + // send in universal language in two side iteration allowed mode + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)) + lang = LANG_UNIVERSAL; + else + { + switch(type) + { + case CHAT_MSG_PARTY: + case CHAT_MSG_RAID: + case CHAT_MSG_RAID_LEADER: + case CHAT_MSG_RAID_WARNING: + // allow two side chat at group channel if two side group allowed + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) + lang = LANG_UNIVERSAL; + break; + case CHAT_MSG_GUILD: + case CHAT_MSG_OFFICER: + // allow two side chat at guild channel if two side guild allowed + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + lang = LANG_UNIVERSAL; + break; + } + } + + // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used) + Unit::AuraEffectList const& ModLangAuras = _player->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE); + if (!ModLangAuras.empty()) + lang = ModLangAuras.front()->GetMiscValue(); + } + + if (!_player->CanSpeak()) + { + std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); + SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); + return; + } + + if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND) + GetPlayer()->UpdateSpeakTime(); + } + + if (GetPlayer()->HasAura(1852) && type != CHAT_MSG_WHISPER) + { + std::string msg=""; + recv_data >> msg; + + SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName()); + return; + } + + switch(type) + { + case CHAT_MSG_SAY: + case CHAT_MSG_EMOTE: + case CHAT_MSG_YELL: + { + std::string msg; + recv_data >> msg; + + if (msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_SAY_LEVEL_REQ)) + { + SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld.getConfig(CONFIG_CHAT_SAY_LEVEL_REQ)); + return; + } + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + if (type == CHAT_MSG_SAY) + GetPlayer()->Say(msg, lang); + else if (type == CHAT_MSG_EMOTE) + GetPlayer()->TextEmote(msg); + else if (type == CHAT_MSG_YELL) + GetPlayer()->Yell(msg, lang); + } break; + + case CHAT_MSG_WHISPER: + { + std::string to, msg; + recv_data >> to; + recv_data >> msg; + + if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ)) + { + SendNotification(GetTrinityString(LANG_WHISPER_REQ), sWorld.getConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ)); + return; + } + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + if (!normalizePlayerName(to)) + { + SendPlayerNotFoundNotice(to); + break; + } + + Player *player = objmgr.GetPlayer(to.c_str()); + uint32 tSecurity = GetSecurity(); + uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER; + if (!player || (tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers())) + { + SendPlayerNotFoundNotice(to); + return; + } + + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER) + { + uint32 sidea = GetPlayer()->GetTeam(); + uint32 sideb = player->GetTeam(); + if (sidea != sideb) + { + SendWrongFactionNotice(); + return; + } + } + + if (GetPlayer()->HasAura(1852) && !player->isGameMaster()) + { + SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName()); + return; + } + + GetPlayer()->Whisper(msg, lang, player->GetGUID()); + } break; + + case CHAT_MSG_PARTY: + case CHAT_MSG_PARTY_LEADER: + { + std::string msg; + recv_data >> msg; + + if (msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + // if player is in battleground, he cannot say to battleground members by /p + Group *group = GetPlayer()->GetOriginalGroup(); + if (!group) + { + group = _player->GetGroup(); + if (!group || group->isBGGroup()) + return; + } + + if ((type == CHAT_MSG_PARTY_LEADER) && !group->IsLeader(_player->GetGUID())) + return; + + WorldPacket data; + ChatHandler::FillMessageData(&data, this, type, lang, NULL, 0, msg.c_str(), NULL); + group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID())); + + if (sWorld.getConfig(CONFIG_CHATLOG_PARTY)) + sLog.outChat("[PARTY] Player %s tells group with leader %s: %s", + GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); + } break; + + case CHAT_MSG_GUILD: + { + std::string msg; + recv_data >> msg; + + if (msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + if (GetPlayer()->GetGuildId()) + { + Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + + if (guild) + guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); + + if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_GUILD)) + { + sLog.outChat("[GUILD] Player %s tells guild %s: %s", + GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); + } + else if (lang == LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_ADDON)) + { + sLog.outChat("[ADDON] Player %s sends to guild %s: %s", + GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); + } + } + + break; + } + case CHAT_MSG_OFFICER: + { + std::string msg; + recv_data >> msg; + + if (msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + if (GetPlayer()->GetGuildId()) + { + Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + + if (guild) + guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); + + if (sWorld.getConfig(CONFIG_CHATLOG_GUILD)) + sLog.outChat("[OFFICER] Player %s tells guild %s officers: %s", + GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); + } + break; + } + case CHAT_MSG_RAID: + { + std::string msg; + recv_data >> msg; + + if (msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + // if player is in battleground, he cannot say to battleground members by /ra + Group *group = GetPlayer()->GetOriginalGroup(); + if (!group) + { + group = GetPlayer()->GetGroup(); + if (!group || group->isBGGroup() || !group->isRaidGroup()) + return; + } + + WorldPacket data; + ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(), NULL); + group->BroadcastPacket(&data, false); + + if (sWorld.getConfig(CONFIG_CHATLOG_RAID)) + sLog.outChat("[RAID] Player %s tells raid with leader %s: %s", + GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); + } break; + case CHAT_MSG_RAID_LEADER: + { + std::string msg; + recv_data >> msg; + + if (msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + // if player is in battleground, he cannot say to battleground members by /ra + Group *group = GetPlayer()->GetOriginalGroup(); + if (!group) + { + group = GetPlayer()->GetGroup(); + if (!group || group->isBGGroup() || !group->isRaidGroup() || !group->IsLeader(_player->GetGUID())) + return; + } + + WorldPacket data; + ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(), NULL); + group->BroadcastPacket(&data, false); + + if (sWorld.getConfig(CONFIG_CHATLOG_RAID)) + sLog.outChat("[RAID] Leader player %s tells raid: %s", + GetPlayer()->GetName(), msg.c_str()); + } break; + case CHAT_MSG_RAID_WARNING: + { + std::string msg; + recv_data >> msg; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + Group *group = GetPlayer()->GetGroup(); + if (!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup()) + return; + + WorldPacket data; + //in battleground, raid warning is sent only to players in battleground - code is ok + ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(), NULL); + group->BroadcastPacket(&data, false); + + if (sWorld.getConfig(CONFIG_CHATLOG_RAID)) + sLog.outChat("[RAID] Leader player %s warns raid with: %s", + GetPlayer()->GetName(), msg.c_str()); + } break; + + case CHAT_MSG_BATTLEGROUND: + { + std::string msg; + recv_data >> msg; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() + Group *group = GetPlayer()->GetGroup(); + if (!group || !group->isBGGroup()) + return; + + WorldPacket data; + ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(), NULL); + group->BroadcastPacket(&data, false); + + if (sWorld.getConfig(CONFIG_CHATLOG_BGROUND)) + sLog.outChat("[BATTLEGROUND] Player %s tells battleground with leader %s: %s", + GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); + } break; + + case CHAT_MSG_BATTLEGROUND_LEADER: + { + std::string msg; + recv_data >> msg; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (msg.empty()) + break; + + // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() + Group *group = GetPlayer()->GetGroup(); + if (!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID())) + return; + + WorldPacket data; + ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(), NULL); + group->BroadcastPacket(&data, false); + + if (sWorld.getConfig(CONFIG_CHATLOG_BGROUND)) + sLog.outChat("[RAID] Leader player %s tells battleground: %s", + GetPlayer()->GetName(), msg.c_str()); + } break; + + case CHAT_MSG_CHANNEL: + { + std::string channel, msg; + recv_data >> channel; + recv_data >> msg; + + if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) + return; + + if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ)) + { + SendNotification(GetTrinityString(LANG_CHANNEL_REQ), sWorld.getConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ)); + return; + } + + if (msg.empty()) + break; + + if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + + if (Channel *chn = cMgr->GetChannel(channel, _player)) + { + chn->Say(_player->GetGUID(), msg.c_str(), lang); + + if ((chn->HasFlag(CHANNEL_FLAG_TRADE) || + chn->HasFlag(CHANNEL_FLAG_GENERAL) || + chn->HasFlag(CHANNEL_FLAG_CITY) || + chn->HasFlag(CHANNEL_FLAG_LFG)) && + sWorld.getConfig(CONFIG_CHATLOG_SYSCHAN)) + sLog.outChat("[SYSCHAN] Player %s tells channel %s: %s", + GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str()); + else if (sWorld.getConfig(CONFIG_CHATLOG_CHANNEL)) + sLog.outChat("[CHANNEL] Player %s tells channel %s: %s", + GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str()); + } + } + } break; + + case CHAT_MSG_AFK: + { + std::string msg; + recv_data >> msg; + + if ((msg.empty() || !_player->isAFK()) && !_player->isInCombat()) + { + if (!_player->isAFK()) + { + if (msg.empty()) + msg = GetTrinityString(LANG_PLAYER_AFK_DEFAULT); + _player->afkMsg = msg; + } + _player->ToggleAFK(); + if (_player->isAFK() && _player->isDND()) + _player->ToggleDND(); + } + } break; + + case CHAT_MSG_DND: + { + std::string msg; + recv_data >> msg; + + if (msg.empty() || !_player->isDND()) + { + if (!_player->isDND()) + { + if (msg.empty()) + msg = GetTrinityString(LANG_PLAYER_DND_DEFAULT); + _player->dndMsg = msg; + } + _player->ToggleDND(); + if (_player->isDND() && _player->isAFK()) + _player->ToggleAFK(); + } + } break; + + default: + sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang); + break; + } +} + +void WorldSession::HandleEmoteOpcode(WorldPacket & recv_data) +{ + if (!GetPlayer()->isAlive()) + return; + + uint32 emote; + recv_data >> emote; + GetPlayer()->HandleEmoteCommand(emote); +} + +namespace Trinity +{ + class EmoteChatBuilder + { + public: + EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target) + : i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {} + + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL; + uint32 namlen = (nam ? strlen(nam) : 0) + 1; + + data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); + data << i_player.GetGUID(); + data << (uint32)i_text_emote; + data << i_emote_num; + data << (uint32)namlen; + if (namlen > 1) + data.append(nam, namlen); + else + data << (uint8)0x00; + } + + private: + Player const& i_player; + uint32 i_text_emote; + uint32 i_emote_num; + Unit const* i_target; + }; +} // namespace Trinity + +void WorldSession::HandleTextEmoteOpcode(WorldPacket & recv_data) +{ + if (!GetPlayer()->isAlive()) + return; + + if (!GetPlayer()->CanSpeak()) + { + std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); + SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); + return; + } + + uint32 text_emote, emoteNum; + uint64 guid; + + recv_data >> text_emote; + recv_data >> emoteNum; + recv_data >> guid; + + EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote); + if (!em) + return; + + uint32 emote_anim = em->textid; + + switch(emote_anim) + { + case EMOTE_STATE_SLEEP: + case EMOTE_STATE_SIT: + case EMOTE_STATE_KNEEL: + case EMOTE_ONESHOT_NONE: + break; + default: + GetPlayer()->HandleEmoteCommand(emote_anim); + break; + } + + Unit* unit = ObjectAccessor::GetUnit(*_player, guid); + + CellPair p = Trinity::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit); + Trinity::LocalizedPacketDo emote_do(emote_builder); + Trinity::PlayerDistWorker > emote_worker(GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), emote_do); + TypeContainerVisitor >, WorldTypeMapContainer> message(emote_worker); + cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE)); + + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit); + + //Send scripted event call + if (unit && unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->AI()) + ((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(), text_emote); +} + +void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data) +{ + uint64 iguid; + uint8 unk; + //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED"); + + recv_data >> iguid; + recv_data >> unk; // probably related to spam reporting + + Player *player = objmgr.GetPlayer(iguid); + if (!player || !player->GetSession()) + return; + + WorldPacket data; + ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(), NULL); + player->GetSession()->SendPacket(&data); +} + +void WorldSession::HandleChannelDeclineInvite(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); +} + +void WorldSession::SendPlayerNotFoundNotice(std::string name) +{ + WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, name.size()+1); + data << name; + SendPacket(&data); +} + +void WorldSession::SendPlayerAmbiguousNotice(std::string name) +{ + WorldPacket data(SMSG_CHAT_PLAYER_AMBIGUOUS, name.size()+1); + data << name; + SendPacket(&data); +} + +void WorldSession::SendWrongFactionNotice() +{ + WorldPacket data(SMSG_CHAT_WRONG_FACTION, 0); + SendPacket(&data); +} + +void WorldSession::SendChatRestrictedNotice(ChatRestrictionType restriction) +{ + WorldPacket data(SMSG_CHAT_RESTRICTED, 1); + data << uint8(restriction); + SendPacket(&data); +} diff --git a/src/server/game/Chat/Debugcmds.cpp b/src/server/game/Chat/Debugcmds.cpp new file mode 100644 index 00000000000..ee8c623c3d0 --- /dev/null +++ b/src/server/game/Chat/Debugcmds.cpp @@ -0,0 +1,1127 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "Vehicle.h" +#include "Player.h" +#include "Opcodes.h" +#include "Chat.h" +#include "Log.h" +#include "Unit.h" +#include "GossipDef.h" +#include "Language.h" +#include "BattleGroundMgr.h" +#include +#include "ObjectMgr.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "SpellMgr.h" +#include "ScriptMgr.h" + +bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args) +{ + if (!*args) + return false; + + char* px = strtok((char*)args, " "); + if (!px) + return false; + + uint8 failnum = (uint8)atoi(px); + if (failnum == 0 && *px != '0') + return false; + + char* p1 = strtok(NULL, " "); + uint8 failarg1 = p1 ? (uint8)atoi(p1) : 0; + + char* p2 = strtok(NULL, " "); + uint8 failarg2 = p2 ? (uint8)atoi(p2) : 0; + + WorldPacket data(SMSG_CAST_FAILED, 5); + data << uint8(0); + data << uint32(133); + data << uint8(failnum); + if (p1 || p2) + data << uint32(failarg1); + if (p2) + data << uint32(failarg2); + + m_session->SendPacket(&data); + + return true; +} + +bool ChatHandler::HandleDebugSendPoiCommand(const char* args) +{ + if (!*args) + return false; + + Player *pPlayer = m_session->GetPlayer(); + Unit* target = getSelectedUnit(); + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + return true; + } + + char* icon_text = strtok((char*)args, " "); + char* flags_text = strtok(NULL, " "); + if (!icon_text || !flags_text) + return false; + + uint32 icon = atol(icon_text); + uint32 flags = atol(flags_text); + + sLog.outDetail("Command : POI, NPC = %u, icon = %u flags = %u", target->GetGUIDLow(), icon,flags); + pPlayer->PlayerTalkClass->SendPointOfInterest(target->GetPositionX(), target->GetPositionY(), Poi_Icon(icon), flags, 30, "Test POI"); + return true; +} + +bool ChatHandler::HandleDebugSendEquipErrorCommand(const char* args) +{ + if (!*args) + return false; + + uint8 msg = atoi(args); + m_session->GetPlayer()->SendEquipError(msg, 0, 0); + return true; +} + +bool ChatHandler::HandleDebugSendSellErrorCommand(const char* args) +{ + if (!*args) + return false; + + uint8 msg = atoi(args); + m_session->GetPlayer()->SendSellError(msg, 0, 0, 0); + return true; +} + +bool ChatHandler::HandleDebugSendBuyErrorCommand(const char* args) +{ + if (!*args) + return false; + + uint8 msg = atoi(args); + m_session->GetPlayer()->SendBuyError(msg, 0, 0, 0); + return true; +} + +bool ChatHandler::HandleDebugSendOpcodeCommand(const char* /*args*/) +{ + Unit *unit = getSelectedUnit(); + Player *player = NULL; + if (!unit || (unit->GetTypeId() != TYPEID_PLAYER)) + player = m_session->GetPlayer(); + else + player = (Player*)unit; + if (!unit) unit = player; + + std::ifstream ifs("opcode.txt"); + if (ifs.bad()) + return false; + + uint32 opcode; + ifs >> opcode; + + WorldPacket data(opcode, 0); + + while (!ifs.eof()) + { + std::string type; + ifs >> type; + + if (type == "") + break; + + if (type == "uint8") + { + uint16 val1; + ifs >> val1; + data << uint8(val1); + } + else if (type == "uint16") + { + uint16 val2; + ifs >> val2; + data << val2; + } + else if (type == "uint32") + { + uint32 val3; + ifs >> val3; + data << val3; + } + else if (type == "uint64") + { + uint64 val4; + ifs >> val4; + data << val4; + } + else if (type == "float") + { + float val5; + ifs >> val5; + data << val5; + } + else if (type == "string") + { + std::string val6; + ifs >> val6; + data << val6; + } + else if (type == "appitsguid") + { + data.append(unit->GetPackGUID()); + } + else if (type == "appmyguid") + { + data.append(player->GetPackGUID()); + } + else if (type == "appgoguid") + { + GameObject *obj = GetNearbyGameObject(); + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, 0); + SetSentErrorMessage(true); + ifs.close(); + return false; + } + data.append(obj->GetPackGUID()); + } + else if (type == "goguid") + { + GameObject *obj = GetNearbyGameObject(); + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, 0); + SetSentErrorMessage(true); + ifs.close(); + return false; + } + data << uint64(obj->GetGUID()); + } + else if (type == "myguid") + { + data << uint64(player->GetGUID()); + } + else if (type == "itsguid") + { + data << uint64(unit->GetGUID()); + } + else if (type == "pos") + { + data << unit->GetPositionX(); + data << unit->GetPositionY(); + data << unit->GetPositionZ(); + } + else if (type == "mypos") + { + data << player->GetPositionX(); + data << player->GetPositionY(); + data << player->GetPositionZ(); + } + else + { + sLog.outDebug("Sending opcode: unknown type '%s'", type.c_str()); + break; + } + } + ifs.close(); + sLog.outDebug("Sending opcode %u", data.GetOpcode()); + data.hexlike(); + player->GetSession()->SendPacket(&data); + PSendSysMessage(LANG_COMMAND_OPCODESENT, data.GetOpcode(), unit->GetName()); + return true; +} + +bool ChatHandler::HandleDebugUpdateWorldStateCommand(const char* args) +{ + char* w = strtok((char*)args, " "); + char* s = strtok(NULL, " "); + + if (!w || !s) + return false; + + uint32 world = (uint32)atoi(w); + uint32 state = (uint32)atoi(s); + m_session->GetPlayer()->SendUpdateWorldState(world, state); + return true; +} + +bool ChatHandler::HandleDebugPlayCinematicCommand(const char* args) +{ + // USAGE: .debug play cinematic #cinematicid + // #cinematicid - ID decimal number from CinemaicSequences.dbc (1st column) + if (!*args) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 dwId = atoi((char*)args); + + if (!sCinematicSequencesStore.LookupEntry(dwId)) + { + PSendSysMessage(LANG_CINEMATIC_NOT_EXIST, dwId); + SetSentErrorMessage(true); + return false; + } + + m_session->GetPlayer()->SendCinematicStart(dwId); + return true; +} + +bool ChatHandler::HandleDebugPlayMovieCommand(const char* args) +{ + // USAGE: .debug play movie #movieid + // #movieid - ID decimal number from Movie.dbc (1st column) + if (!*args) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 dwId = atoi((char*)args); + + if (!sMovieStore.LookupEntry(dwId)) + { + PSendSysMessage(LANG_MOVIE_NOT_EXIST, dwId); + SetSentErrorMessage(true); + return false; + } + + m_session->GetPlayer()->SendMovieStart(dwId); + return true; +} + +//Play sound +bool ChatHandler::HandleDebugPlaySoundCommand(const char* args) +{ + // USAGE: .debug playsound #soundid + // #soundid - ID decimal number from SoundEntries.dbc (1st column) + if (!*args) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 dwSoundId = atoi((char*)args); + + if (!sSoundEntriesStore.LookupEntry(dwSoundId)) + { + PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId); + SetSentErrorMessage(true); + return false; + } + + Unit* unit = getSelectedUnit(); + if (!unit) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (m_session->GetPlayer()->GetSelection()) + unit->PlayDistanceSound(dwSoundId,m_session->GetPlayer()); + else + unit->PlayDirectSound(dwSoundId,m_session->GetPlayer()); + + PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId); + return true; +} + +//Send notification in channel +bool ChatHandler::HandleDebugSendChannelNotifyCommand(const char* args) +{ + if (!*args) + return false; + + const char *name = "test"; + uint8 code = atoi(args); + + WorldPacket data(SMSG_CHANNEL_NOTIFY, (1+10)); + data << code; // notify type + data << name; // channel name + data << uint32(0); + data << uint32(0); + m_session->SendPacket(&data); + return true; +} + +//Send notification in chat +bool ChatHandler::HandleDebugSendChatMsgCommand(const char* args) +{ + if (!*args) + return false; + + const char *msg = "testtest"; + uint8 type = atoi(args); + WorldPacket data; + ChatHandler::FillMessageData(&data, m_session, type, 0, "chan", m_session->GetPlayer()->GetGUID(), msg, m_session->GetPlayer()); + m_session->SendPacket(&data); + return true; +} + +bool ChatHandler::HandleDebugSendQuestPartyMsgCommand(const char* args) +{ + uint32 msg = atol((char*)args); + m_session->GetPlayer()->SendPushToPartyResponse(m_session->GetPlayer(), msg); + return true; +} + +bool ChatHandler::HandleDebugGetLootRecipientCommand(const char* /*args*/) +{ + Creature* target = getSelectedCreature(); + if (!target) + return false; + + PSendSysMessage("loot recipient: %s", target->hasLootRecipient()?(target->GetLootRecipient()?target->GetLootRecipient()->GetName():"offline"):"no loot recipient"); + return true; +} + +bool ChatHandler::HandleDebugSendQuestInvalidMsgCommand(const char* args) +{ + uint32 msg = atol((char*)args); + m_session->GetPlayer()->SendCanTakeQuestResponse(msg); + return true; +} + +bool ChatHandler::HandleDebugGetItemStateCommand(const char* args) +{ + if (!*args) + return false; + + std::string state_str = args; + + ItemUpdateState state = ITEM_UNCHANGED; + bool list_queue = false, check_all = false; + if (state_str == "unchanged") state = ITEM_UNCHANGED; + else if (state_str == "changed") state = ITEM_CHANGED; + else if (state_str == "new") state = ITEM_NEW; + else if (state_str == "removed") state = ITEM_REMOVED; + else if (state_str == "queue") list_queue = true; + else if (state_str == "check_all") check_all = true; + else return false; + + Player* player = getSelectedPlayer(); + if (!player) player = m_session->GetPlayer(); + + if (!list_queue && !check_all) + { + state_str = "The player has the following " + state_str + " items: "; + SendSysMessage(state_str.c_str()); + for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) + { + if (i >= BUYBACK_SLOT_START && i < BUYBACK_SLOT_END) + continue; + + Item *item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (!item) continue; + if (!item->IsBag()) + { + if (item->GetState() == state) + PSendSysMessage("bag: 255 slot: %d guid: %d owner: %d", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID())); + } + else + { + Bag *bag = (Bag*)item; + for (uint8 j = 0; j < bag->GetBagSize(); ++j) + { + Item* item2 = bag->GetItemByPos(j); + if (item2 && item2->GetState() == state) + PSendSysMessage("bag: 255 slot: %d guid: %d owner: %d", item2->GetSlot(), item2->GetGUIDLow(), GUID_LOPART(item2->GetOwnerGUID())); + } + } + } + } + + if (list_queue) + { + std::vector &updateQueue = player->GetItemUpdateQueue(); + for (size_t i = 0; i < updateQueue.size(); ++i) + { + Item *item = updateQueue[i]; + if (!item) continue; + + Bag *container = item->GetContainer(); + uint8 bag_slot = container ? container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0); + + std::string st; + switch(item->GetState()) + { + case ITEM_UNCHANGED: st = "unchanged"; break; + case ITEM_CHANGED: st = "changed"; break; + case ITEM_NEW: st = "new"; break; + case ITEM_REMOVED: st = "removed"; break; + } + + PSendSysMessage("bag: %d slot: %d guid: %d - state: %s", bag_slot, item->GetSlot(), item->GetGUIDLow(), st.c_str()); + } + if (updateQueue.empty()) + PSendSysMessage("updatequeue empty"); + } + + if (check_all) + { + bool error = false; + std::vector &updateQueue = player->GetItemUpdateQueue(); + for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) + { + if (i >= BUYBACK_SLOT_START && i < BUYBACK_SLOT_END) + continue; + + Item *item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (!item) continue; + + if (item->GetSlot() != i) + { + PSendSysMessage("item at slot %d, guid %d has an incorrect slot value: %d", i, item->GetGUIDLow(), item->GetSlot()); + error = true; continue; + } + + if (item->GetOwnerGUID() != player->GetGUID()) + { + PSendSysMessage("for the item at slot %d and itemguid %d, owner's guid (%d) and player's guid (%d) don't match!", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); + error = true; continue; + } + + if (Bag *container = item->GetContainer()) + { + PSendSysMessage("item at slot: %d guid: %d has a container (slot: %d, guid: %d) but shouldnt!", item->GetSlot(), item->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow()); + error = true; continue; + } + + if (item->IsInUpdateQueue()) + { + uint16 qp = item->GetQueuePos(); + if (qp > updateQueue.size()) + { + PSendSysMessage("item at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", item->GetSlot(), item->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] == NULL) + { + PSendSysMessage("item at slot: %d guid: %d has a queuepos (%d) that points to NULL in the queue!", item->GetSlot(), item->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] != item) + { + PSendSysMessage("item at slot: %d guid: %d has has a queuepos (%d) that points to another item in the queue (bag: %d, slot: %d, guid: %d)", item->GetSlot(), item->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow()); + error = true; continue; + } + } + else if (item->GetState() != ITEM_UNCHANGED) + { + PSendSysMessage("item at slot: %d guid: %d is not in queue but should be (state: %d)!", item->GetSlot(), item->GetGUIDLow(), item->GetState()); + error = true; continue; + } + + if (item->IsBag()) + { + Bag *bag = (Bag*)item; + for (uint8 j = 0; j < bag->GetBagSize(); ++j) + { + Item* item2 = bag->GetItemByPos(j); + if (!item2) continue; + + if (item2->GetSlot() != j) + { + PSendSysMessage("the item in bag %d slot %d, guid %d has an incorrect slot value: %d", bag->GetSlot(), j, item2->GetGUIDLow(), item2->GetSlot()); + error = true; continue; + } + + if (item2->GetOwnerGUID() != player->GetGUID()) + { + PSendSysMessage("for the item in bag %d at slot %d and itemguid %d, owner's guid (%d) and player's guid (%d) don't match!", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow(), GUID_LOPART(item2->GetOwnerGUID()), player->GetGUIDLow()); + error = true; continue; + } + + Bag *container = item2->GetContainer(); + if (!container) + { + PSendSysMessage("the item in bag %d at slot %d with guid %d has no container!", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow()); + error = true; continue; + } + + if (container != bag) + { + PSendSysMessage("the item in bag %d at slot %d with guid %d has a different container(slot %d guid %d)!", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow()); + error = true; continue; + } + + if (item2->IsInUpdateQueue()) + { + uint16 qp = item2->GetQueuePos(); + if (qp > updateQueue.size()) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] == NULL) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) that points to NULL in the queue!", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] != item2) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d has has a queuepos (%d) that points to another item in the queue (bag: %d, slot: %d, guid: %d)", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow()); + error = true; continue; + } + } + else if (item2->GetState() != ITEM_UNCHANGED) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d is not in queue but should be (state: %d)!", bag->GetSlot(), item2->GetSlot(), item2->GetGUIDLow(), item2->GetState()); + error = true; continue; + } + } + } + } + + for (size_t i = 0; i < updateQueue.size(); ++i) + { + Item *item = updateQueue[i]; + if (!item) continue; + + if (item->GetOwnerGUID() != player->GetGUID()) + { + PSendSysMessage("queue(" SIZEFMTD "): for the an item (guid %d), the owner's guid (%d) and player's guid (%d) don't match!", i, item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); + error = true; continue; + } + + if (item->GetQueuePos() != i) + { + PSendSysMessage("queue(" SIZEFMTD "): for the an item (guid %d), the queuepos doesn't match it's position in the queue!", i, item->GetGUIDLow()); + error = true; continue; + } + + if (item->GetState() == ITEM_REMOVED) continue; + Item *test = player->GetItemByPos(item->GetBagSlot(), item->GetSlot()); + + if (test == NULL) + { + PSendSysMessage("queue(" SIZEFMTD "): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow()); + error = true; continue; + } + + if (test != item) + { + PSendSysMessage("queue(" SIZEFMTD "): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow()); + error = true; continue; + } + } + if (!error) + SendSysMessage("All OK!"); + } + + return true; +} + +bool ChatHandler::HandleDebugBattlegroundCommand(const char * /*args*/) +{ + sBattleGroundMgr.ToggleTesting(); + return true; +} + +bool ChatHandler::HandleDebugArenaCommand(const char * /*args*/) +{ + sBattleGroundMgr.ToggleArenaTesting(); + return true; +} + +bool ChatHandler::HandleDebugThreatList(const char * /*args*/) +{ + Creature* target = getSelectedCreature(); + if (!target || target->isTotem() || target->isPet()) + return false; + + std::list& tlist = target->getThreatManager().getThreatList(); + std::list::iterator itr; + uint32 cnt = 0; + PSendSysMessage("Threat list of %s (guid %u)",target->GetName(), target->GetGUIDLow()); + for (itr = tlist.begin(); itr != tlist.end(); ++itr) + { + Unit* unit = (*itr)->getTarget(); + if (!unit) + continue; + ++cnt; + PSendSysMessage(" %u. %s (guid %u) - threat %f",cnt,unit->GetName(), unit->GetGUIDLow(), (*itr)->getThreat()); + } + SendSysMessage("End of threat list."); + return true; +} + +bool ChatHandler::HandleDebugHostileRefList(const char * /*args*/) +{ + Unit* target = getSelectedUnit(); + if (!target) + target = m_session->GetPlayer(); + HostileReference* ref = target->getHostileRefManager().getFirst(); + uint32 cnt = 0; + PSendSysMessage("Hostil reference list of %s (guid %u)",target->GetName(), target->GetGUIDLow()); + while (ref) + { + if (Unit * unit = ref->getSource()->getOwner()) + { + ++cnt; + PSendSysMessage(" %u. %s (guid %u) - threat %f",cnt,unit->GetName(), unit->GetGUIDLow(), ref->getThreat()); + } + ref = ref->next(); + } + SendSysMessage("End of hostil reference list."); + return true; +} + +bool ChatHandler::HandleDebugSetVehicleId(const char *args) +{ + Unit* target = getSelectedUnit(); + if (!target || target->IsVehicle()) + return false; + + if (!args) + return false; + + char* i = strtok((char*)args, " "); + if (!i) + return false; + + uint32 id = (uint32)atoi(i); + //target->SetVehicleId(id); + PSendSysMessage("Vehicle id set to %u", id); + return true; +} + +bool ChatHandler::HandleDebugEnterVehicle(const char * args) +{ + Unit* target = getSelectedUnit(); + if (!target || !target->IsVehicle()) + return false; + + if (!args) + return false; + + char* i = strtok((char*)args, " "); + if (!i) + return false; + + char* j = strtok(NULL, " "); + + uint32 entry = (uint32)atoi(i); + int8 seatId = j ? (int8)atoi(j) : -1; + + if (!entry) + m_session->GetPlayer()->EnterVehicle(target, seatId); + else + { + Creature *passenger = NULL; + Trinity::AllCreaturesOfEntryInRange check(m_session->GetPlayer(), entry, 20.0f); + Trinity::CreatureSearcher searcher(m_session->GetPlayer(), passenger, check); + m_session->GetPlayer()->VisitNearbyObject(30.0f, searcher); + if (!passenger || passenger == target) + return false; + passenger->EnterVehicle(target, seatId); + } + + PSendSysMessage("Unit %u entered vehicle %d", entry, (int32)seatId); + return true; +} + +bool ChatHandler::HandleDebugSpawnVehicle(const char* args) +{ + if (!*args) + return false; + + char* e = strtok((char*)args, " "); + char* i = strtok(NULL, " "); + + if (!e) + return false; + + uint32 entry = (uint32)atoi(e); + + float x, y, z, o = m_session->GetPlayer()->GetOrientation(); + m_session->GetPlayer()->GetClosePoint(x, y, z, m_session->GetPlayer()->GetObjectSize()); + + if (!i) + return m_session->GetPlayer()->SummonCreature(entry, x, y, z, o); + + uint32 id = (uint32)atoi(i); + + CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry); + + if (!ci) + return false; + + VehicleEntry const *ve = sVehicleStore.LookupEntry(id); + + if (!ve) + return false; + + Creature *v = new Creature; + + Map *map = m_session->GetPlayer()->GetMap(); + + if (!v->Create(objmgr.GenerateLowGuid(HIGHGUID_VEHICLE), map, m_session->GetPlayer()->GetPhaseMask(), entry, id, m_session->GetPlayer()->GetTeam(), x, y, z, o)) + { + delete v; + return false; + } + + map->Add(v->ToCreature()); + + return true; +} + +bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/) +{ + const char* stuffingString = "This is a dummy string to push the packet's size beyond 128000 bytes. "; + std::ostringstream ss; + while (ss.str().size() < 128000) + ss << stuffingString; + SendSysMessage(ss.str().c_str()); + return true; +} + +bool ChatHandler::HandleDebugSendSetPhaseShiftCommand(const char* args) +{ + if (!*args) + return false; + + uint32 PhaseShift = atoi(args); + m_session->SendSetPhaseShift(PhaseShift); + return true; +} + +bool ChatHandler::HandleDebugGetItemValueCommand(const char* args) +{ + if (!*args) + return false; + + char* e = strtok((char*)args, " "); + char* f = strtok(NULL, " "); + + if (!e || !f) + return false; + + uint32 guid = (uint32)atoi(e); + uint32 index = (uint32)atoi(f); + + Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM)); + + if (!i) + return false; + + if (index >= i->GetValuesCount()) + return false; + + uint32 value = i->GetUInt32Value(index); + + PSendSysMessage("Item %u: value at %u is %u", guid, index, value); + + return true; +} + +bool ChatHandler::HandleDebugSetItemValueCommand(const char* args) +{ + if (!*args) + return false; + + char* e = strtok((char*)args, " "); + char* f = strtok(NULL, " "); + char* g = strtok(NULL, " "); + + if (!e || !f || !g) + return false; + + uint32 guid = (uint32)atoi(e); + uint32 index = (uint32)atoi(f); + uint32 value = (uint32)atoi(g); + + Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM)); + + if (!i) + return false; + + if (index >= i->GetValuesCount()) + return false; + + i->SetUInt32Value(index, value); + + return true; +} + +bool ChatHandler::HandleDebugItemExpireCommand(const char* args) +{ + if (!*args) + return false; + + char* e = strtok((char*)args, " "); + if (!e) + return false; + + uint32 guid = (uint32)atoi(e); + + Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM)); + + if (!i) + return false; + + m_session->GetPlayer()->DestroyItem(i->GetBagSlot(),i->GetSlot(), true); + sScriptMgr.ItemExpire(m_session->GetPlayer(),i->GetProto()); + + return true; +} + +//show animation +bool ChatHandler::HandleDebugAnimCommand(const char* args) +{ + if (!*args) + return false; + + uint32 anim_id = atoi((char*)args); + m_session->GetPlayer()->HandleEmoteCommand(anim_id); + return true; +} + +bool ChatHandler::HandleDebugSetAuraStateCommand(const char* args) +{ + if (!*args) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Unit* unit = getSelectedUnit(); + if (!unit) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + int32 state = atoi((char*)args); + if (!state) + { + // reset all states + for (int i = 1; i <= 32; ++i) + unit->ModifyAuraState(AuraState(i),false); + return true; + } + + unit->ModifyAuraState(AuraState(abs(state)),state > 0); + return true; +} + +bool ChatHandler::HandleDebugSetValueCommand(const char* args) +{ + if (!*args) + return false; + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + char* pz = strtok(NULL, " "); + + if (!px || !py) + return false; + + WorldObject* target = getSelectedObject(); + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + uint64 guid = target->GetGUID(); + + uint32 Opcode = (uint32)atoi(px); + if (Opcode >= target->GetValuesCount()) + { + PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount()); + return false; + } + uint32 iValue; + float fValue; + bool isint32 = true; + if (pz) + isint32 = (bool)atoi(pz); + if (isint32) + { + iValue = (uint32)atoi(py); + sLog.outDebug(GetTrinityString(LANG_SET_UINT), GUID_LOPART(guid), Opcode, iValue); + target->SetUInt32Value(Opcode , iValue); + PSendSysMessage(LANG_SET_UINT_FIELD, GUID_LOPART(guid), Opcode,iValue); + } + else + { + fValue = (float)atof(py); + sLog.outDebug(GetTrinityString(LANG_SET_FLOAT), GUID_LOPART(guid), Opcode, fValue); + target->SetFloatValue(Opcode , fValue); + PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), Opcode,fValue); + } + + return true; +} + +bool ChatHandler::HandleDebugGetValueCommand(const char* args) +{ + if (!*args) + return false; + + char* px = strtok((char*)args, " "); + char* pz = strtok(NULL, " "); + + if (!px) + return false; + + Unit* target = getSelectedUnit(); + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + uint64 guid = target->GetGUID(); + + uint32 Opcode = (uint32)atoi(px); + if (Opcode >= target->GetValuesCount()) + { + PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount()); + return false; + } + uint32 iValue; + float fValue; + bool isint32 = true; + if (pz) + isint32 = (bool)atoi(pz); + + if (isint32) + { + iValue = target->GetUInt32Value(Opcode); + sLog.outDebug(GetTrinityString(LANG_GET_UINT), GUID_LOPART(guid), Opcode, iValue); + PSendSysMessage(LANG_GET_UINT_FIELD, GUID_LOPART(guid), Opcode, iValue); + } + else + { + fValue = target->GetFloatValue(Opcode); + sLog.outDebug(GetTrinityString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue); + PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue); + } + + return true; +} + +bool ChatHandler::HandleDebugMod32ValueCommand(const char* args) +{ + if (!*args) + return false; + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + + if (!px || !py) + return false; + + uint32 Opcode = (uint32)atoi(px); + int Value = atoi(py); + + if (Opcode >= m_session->GetPlayer()->GetValuesCount()) + { + PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, m_session->GetPlayer()->GetGUIDLow(), m_session->GetPlayer()->GetValuesCount()); + return false; + } + + sLog.outDebug(GetTrinityString(LANG_CHANGE_32BIT), Opcode, Value); + + int CurrentValue = (int)m_session->GetPlayer()->GetUInt32Value(Opcode); + + CurrentValue += Value; + m_session->GetPlayer()->SetUInt32Value(Opcode , (uint32)CurrentValue); + + PSendSysMessage(LANG_CHANGE_32BIT_FIELD, Opcode,CurrentValue); + + return true; +} + +bool ChatHandler::HandleDebugUpdateCommand(const char* args) +{ + if (!*args) + return false; + + uint32 updateIndex; + uint32 value; + + char* pUpdateIndex = strtok((char*)args, " "); + + Unit* chr = getSelectedUnit(); + if (chr == NULL) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (!pUpdateIndex) + { + return true; + } + updateIndex = atoi(pUpdateIndex); + //check updateIndex + if (chr->GetTypeId() == TYPEID_PLAYER) + { + if (updateIndex >= PLAYER_END) return true; + } + else + { + if (updateIndex >= UNIT_END) return true; + } + + char* pvalue = strtok(NULL, " "); + if (!pvalue) + { + value=chr->GetUInt32Value(updateIndex); + + PSendSysMessage(LANG_UPDATE, chr->GetGUIDLow(),updateIndex,value); + return true; + } + + value=atoi(pvalue); + + PSendSysMessage(LANG_UPDATE_CHANGE, chr->GetGUIDLow(),updateIndex,value); + + chr->SetUInt32Value(updateIndex,value); + + return true; +} diff --git a/src/server/game/Chat/Level0.cpp b/src/server/game/Chat/Level0.cpp new file mode 100644 index 00000000000..ed021ac00d4 --- /dev/null +++ b/src/server/game/Chat/Level0.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "World.h" +#include "Player.h" +#include "Opcodes.h" +#include "Chat.h" +#include "ObjectAccessor.h" +#include "Language.h" +#include "AccountMgr.h" +#include "SystemConfig.h" +#include "revision.h" +#include "Util.h" + +bool ChatHandler::HandleHelpCommand(const char* args) +{ + char* cmd = strtok((char*)args, " "); + if (!cmd) + { + ShowHelpForCommand(getCommandTable(), "help"); + ShowHelpForCommand(getCommandTable(), ""); + } + else + { + if (!ShowHelpForCommand(getCommandTable(), cmd)) + SendSysMessage(LANG_NO_HELP_CMD); + } + + return true; +} + +bool ChatHandler::HandleCommandsCommand(const char* /*args*/) +{ + ShowHelpForCommand(getCommandTable(), ""); + return true; +} + +bool ChatHandler::HandleAccountCommand(const char* /*args*/) +{ + AccountTypes gmlevel = m_session->GetSecurity(); + PSendSysMessage(LANG_ACCOUNT_LEVEL, uint32(gmlevel)); + return true; +} + +bool ChatHandler::HandleStartCommand(const char* /*args*/) +{ + Player *chr = m_session->GetPlayer(); + + if (chr->isInFlight()) + { + SendSysMessage(LANG_YOU_IN_FLIGHT); + SetSentErrorMessage(true); + return false; + } + + if (chr->isInCombat()) + { + SendSysMessage(LANG_YOU_IN_COMBAT); + SetSentErrorMessage(true); + return false; + } + + if ((chr->isDead()) || (chr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))) + { + // if player is dead and stuck, send ghost to graveyard + chr->RepopAtGraveyard(); + return true; + } + + // cast spell Stuck + chr->CastSpell(chr,7355,false); + return true; +} + +bool ChatHandler::HandleServerInfoCommand(const char* /*args*/) +{ + uint32 PlayersNum = sWorld.GetPlayerCount(); + uint32 MaxPlayersNum = sWorld.GetMaxPlayerCount(); + uint32 activeClientsNum = sWorld.GetActiveSessionCount(); + uint32 queuedClientsNum = sWorld.GetQueuedSessionCount(); + uint32 maxActiveClientsNum = sWorld.GetMaxActiveSessionCount(); + uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount(); + std::string uptime = secsToTimeString(sWorld.GetUptime()); + uint32 updateTime = sWorld.GetUpdateTime(); + + PSendSysMessage(_FULLVERSION); + //if (m_session) + // full = _FULLVERSION(REVISION_DATE,REVISION_TIME,"|cffffffff|Hurl:" REVISION_ID "|h" REVISION_ID "|h|r"); + //else + // full = _FULLVERSION(REVISION_DATE,REVISION_TIME,REVISION_ID); + + //SendSysMessage(full); + //PSendSysMessage(LANG_USING_WORLD_DB,sWorld.GetDBVersion()); + //PSendSysMessage(LANG_USING_EVENT_AI,sWorld.GetCreatureEventAIVersion()); + PSendSysMessage(LANG_CONNECTED_PLAYERS, PlayersNum, MaxPlayersNum); + PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum); + PSendSysMessage(LANG_UPTIME, uptime.c_str()); + PSendSysMessage("Update time diff: %u.", updateTime); + + return true; +} + +bool ChatHandler::HandleDismountCommand(const char* /*args*/) +{ + //If player is not mounted, so go out :) + if (!m_session->GetPlayer()->IsMounted()) + { + SendSysMessage(LANG_CHAR_NON_MOUNTED); + SetSentErrorMessage(true); + return false; + } + + if (m_session->GetPlayer()->isInFlight()) + { + SendSysMessage(LANG_YOU_IN_FLIGHT); + SetSentErrorMessage(true); + return false; + } + + m_session->GetPlayer()->Unmount(); + m_session->GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED); + return true; +} + +bool ChatHandler::HandleSaveCommand(const char* /*args*/) +{ + Player *player=m_session->GetPlayer(); + + // save GM account without delay and output message (testing, etc) + if (m_session->GetSecurity() > SEC_PLAYER) + { + player->SaveToDB(); + SendSysMessage(LANG_PLAYER_SAVED); + return true; + } + + // save or plan save after 20 sec (logout delay) if current next save time more this value and _not_ output any messages to prevent cheat planning + uint32 save_interval = sWorld.getConfig(CONFIG_INTERVAL_SAVE); + if ((save_interval == 0 || save_interval > 20*IN_MILISECONDS && player->GetSaveTimer() <= save_interval - 20*IN_MILISECONDS)) + player->SaveToDB(); + + return true; +} + +bool ChatHandler::HandleGMListIngameCommand(const char* /*args*/) +{ + bool first = true; + + ObjectAccessor::Guard guard(*HashMapHolder::GetLock()); + HashMapHolder::MapType &m = ObjectAccessor::Instance().GetPlayers(); + for (HashMapHolder::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr) + { + AccountTypes itr_sec = itr->second->GetSession()->GetSecurity(); + if ((itr->second->isGameMaster() || (itr_sec > SEC_PLAYER && itr_sec <= sWorld.getConfig(CONFIG_GM_LEVEL_IN_GM_LIST))) && + (!m_session || itr->second->IsVisibleGloballyFor(m_session->GetPlayer()))) + { + if (first) + { + SendSysMessage(LANG_GMS_ON_SRV); + first = false; + } + + SendSysMessage(GetNameLink(itr->second).c_str()); + } + } + + if (first) + SendSysMessage(LANG_GMS_NOT_LOGGED); + + return true; +} + +bool ChatHandler::HandleAccountPasswordCommand(const char* args) +{ + if (!*args) + return false; + + char *old_pass = strtok ((char*)args, " "); + char *new_pass = strtok (NULL, " "); + char *new_pass_c = strtok (NULL, " "); + + if (!old_pass || !new_pass || !new_pass_c) + return false; + + std::string password_old = old_pass; + std::string password_new = new_pass; + std::string password_new_c = new_pass_c; + + if (strcmp(new_pass, new_pass_c) != 0) + { + SendSysMessage (LANG_NEW_PASSWORDS_NOT_MATCH); + SetSentErrorMessage (true); + return false; + } + + if (!accmgr.CheckPassword (m_session->GetAccountId(), password_old)) + { + SendSysMessage (LANG_COMMAND_WRONGOLDPASSWORD); + SetSentErrorMessage (true); + return false; + } + + AccountOpResult result = accmgr.ChangePassword(m_session->GetAccountId(), password_new); + + switch(result) + { + case AOR_OK: + SendSysMessage(LANG_COMMAND_PASSWORD); + break; + case AOR_PASS_TOO_LONG: + SendSysMessage(LANG_PASSWORD_TOO_LONG); + SetSentErrorMessage(true); + return false; + case AOR_NAME_NOT_EXIST: // not possible case, don't want get account name for output + default: + SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleAccountAddonCommand(const char* args) +{ + if (!*args) + return false; + + char *szExp = strtok((char*)args," "); + + uint32 account_id = m_session->GetAccountId(); + + int expansion=atoi(szExp); //get int anyway (0 if error) + if (expansion < 0 || expansion > sWorld.getConfig(CONFIG_EXPANSION)) + return false; + + // No SQL injection + LoginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'", expansion, account_id); + PSendSysMessage(LANG_ACCOUNT_ADDON, expansion); + return true; +} + +bool ChatHandler::HandleAccountLockCommand(const char* args) +{ + if (!*args) + { + SendSysMessage(LANG_USE_BOL); + return true; + } + + std::string argstr = (char*)args; + if (argstr == "on") + { + LoginDatabase.PExecute("UPDATE account SET locked = '1' WHERE id = '%d'",m_session->GetAccountId()); + PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED); + return true; + } + + if (argstr == "off") + { + LoginDatabase.PExecute("UPDATE account SET locked = '0' WHERE id = '%d'",m_session->GetAccountId()); + PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED); + return true; + } + + SendSysMessage(LANG_USE_BOL); + return true; +} + +/// Display the 'Message of the day' for the realm +bool ChatHandler::HandleServerMotdCommand(const char* /*args*/) +{ + PSendSysMessage(LANG_MOTD_CURRENT, sWorld.GetMotd()); + return true; +} + diff --git a/src/server/game/Chat/Level1.cpp b/src/server/game/Chat/Level1.cpp new file mode 100644 index 00000000000..11189d519a0 --- /dev/null +++ b/src/server/game/Chat/Level1.cpp @@ -0,0 +1,2960 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "AccountMgr.h" +#include "Opcodes.h" +#include "Chat.h" +#include "Log.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "Language.h" +#include "CellImpl.h" +#include "InstanceSaveMgr.h" +#include "Util.h" + +#ifdef _DEBUG_VMAPS +#include "VMapFactory.h" +#endif + +//-----------------------Npc Commands----------------------- +bool ChatHandler::HandleNpcSayCommand(const char* args) +{ + if (!*args) + return false; + + Creature* pCreature = getSelectedCreature(); + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->MonsterSay(args, LANG_UNIVERSAL, 0); + + // make some emotes + char lastchar = args[strlen(args) - 1]; + switch(lastchar) + { + case '?': pCreature->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); break; + case '!': pCreature->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); break; + default: pCreature->HandleEmoteCommand(EMOTE_ONESHOT_TALK); break; + } + + return true; +} + +bool ChatHandler::HandleNpcYellCommand(const char* args) +{ + if (!*args) + return false; + + Creature* pCreature = getSelectedCreature(); + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->MonsterYell(args, LANG_UNIVERSAL, 0); + + // make an emote + pCreature->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT); + + return true; +} + +//show text emote by creature in chat +bool ChatHandler::HandleNpcTextEmoteCommand(const char* args) +{ + if (!*args) + return false; + + Creature* pCreature = getSelectedCreature(); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->MonsterTextEmote(args, 0); + + return true; +} + +// make npc whisper to player +bool ChatHandler::HandleNpcWhisperCommand(const char* args) +{ + if (!*args) + return false; + + char* receiver_str = strtok((char*)args, " "); + char* text = strtok(NULL, ""); + + uint64 guid = m_session->GetPlayer()->GetSelection(); + Creature* pCreature = m_session->GetPlayer()->GetMap()->GetCreature(guid); + + if (!pCreature || !receiver_str || !text) + { + return false; + } + + uint64 receiver_guid= atol(receiver_str); + + // check online security + if (HasLowerSecurity(objmgr.GetPlayer(receiver_guid), 0)) + return false; + + pCreature->MonsterWhisper(text,receiver_guid); + + return true; +} +//---------------------------------------------------------- + +bool ChatHandler::HandleNameAnnounceCommand(const char* args) +{ + WorldPacket data; + if (!*args) + return false; + + sWorld.SendWorldText(LANG_ANNOUNCE_COLOR, m_session->GetPlayer()->GetName(), args); + return true; +} + +bool ChatHandler::HandleGMNameAnnounceCommand(const char* args) +{ + WorldPacket data; + if (!*args) + return false; + + sWorld.SendGMText(LANG_GM_ANNOUNCE_COLOR, m_session->GetPlayer()->GetName(), args); + return true; +} + +// global announce +bool ChatHandler::HandleAnnounceCommand(const char* args) +{ + if (!*args) + return false; + + sWorld.SendWorldText(LANG_SYSTEMMESSAGE,args); + return true; +} + +// announce to logged in GMs +bool ChatHandler::HandleGMAnnounceCommand(const char* args) +{ + if (!*args) + return false; + + sWorld.SendGMText(LANG_GM_BROADCAST,args); + return true; +} + +//notification player at the screen +bool ChatHandler::HandleNotifyCommand(const char* args) +{ + if (!*args) + return false; + + std::string str = GetTrinityString(LANG_GLOBAL_NOTIFY); + str += args; + + WorldPacket data(SMSG_NOTIFICATION, (str.size()+1)); + data << str; + sWorld.SendGlobalMessage(&data); + + return true; +} + +//notification GM at the screen +bool ChatHandler::HandleGMNotifyCommand(const char* args) +{ + if (!*args) + return false; + + std::string str = GetTrinityString(LANG_GM_NOTIFY); + str += args; + + WorldPacket data(SMSG_NOTIFICATION, (str.size()+1)); + data << str; + sWorld.SendGlobalGMMessage(&data); + + return true; +} + +//Enable\Dissable GM Mode +bool ChatHandler::HandleGMCommand(const char* args) +{ + if (!*args) + { + if (m_session->GetPlayer()->isGameMaster()) + m_session->SendNotification(LANG_GM_ON); + else + m_session->SendNotification(LANG_GM_OFF); + return true; + } + + std::string argstr = (char*)args; + + if (argstr == "on") + { + m_session->GetPlayer()->SetGameMaster(true); + m_session->SendNotification(LANG_GM_ON); + m_session->GetPlayer()->UpdateTriggerVisibility(); + #ifdef _DEBUG_VMAPS + VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); + vMapManager->processCommand("stoplog"); + #endif + return true; + } + + if (argstr == "off") + { + m_session->GetPlayer()->SetGameMaster(false); + m_session->SendNotification(LANG_GM_OFF); + m_session->GetPlayer()->UpdateTriggerVisibility(); + #ifdef _DEBUG_VMAPS + VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); + vMapManager->processCommand("startlog"); + #endif + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + +// Enables or disables hiding of the staff badge +bool ChatHandler::HandleGMChatCommand(const char* args) +{ + if (!*args) + { + if (m_session->GetPlayer()->isGMChat()) + m_session->SendNotification(LANG_GM_CHAT_ON); + else + m_session->SendNotification(LANG_GM_CHAT_OFF); + return true; + } + + std::string argstr = (char*)args; + + if (argstr == "on") + { + m_session->GetPlayer()->SetGMChat(true); + m_session->SendNotification(LANG_GM_CHAT_ON); + return true; + } + + if (argstr == "off") + { + m_session->GetPlayer()->SetGMChat(false); + m_session->SendNotification(LANG_GM_CHAT_OFF); + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + +std::string ChatHandler::PGetParseString(int32 entry, ...) +{ + const char *format = GetTrinityString(entry); + va_list ap; + char str [1024]; + va_start(ap, entry); + vsnprintf(str,1024,format, ap); + va_end(ap); + return (std::string)str; +} + +bool ChatHandler::HandleGMTicketListCommand(const char* /*args*/) +{ + SendSysMessage(LANG_COMMAND_TICKETSHOWLIST); + for (GmTicketList::iterator itr = objmgr.m_GMTicketList.begin(); itr != objmgr.m_GMTicketList.end(); ++itr) + { + if ((*itr)->closed != 0) + continue; + std::string gmname; + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, (*itr)->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - (*itr)->createtime, true, false)).c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - (*itr)->timestamp, true, false)).c_str()); + if (objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname)) + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + } + SendSysMessage(ss.str().c_str()); + } + return true; +} + +bool ChatHandler::HandleGMTicketListOnlineCommand(const char* /*args*/) +{ + SendSysMessage(LANG_COMMAND_TICKETSHOWONLINELIST); + for (GmTicketList::iterator itr = objmgr.m_GMTicketList.begin(); itr != objmgr.m_GMTicketList.end(); ++itr) + { + if ((*itr)->closed != 0 || !objmgr.GetPlayer((*itr)->playerGuid)) + continue; + + std::string gmname; + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, (*itr)->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - (*itr)->createtime, true, false)).c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - (*itr)->timestamp, true, false)).c_str()); + if (objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname)) + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + } + SendSysMessage(ss.str().c_str()); + } + return true; +} + +bool ChatHandler::HandleGMTicketListClosedCommand(const char* /*args*/) +{ + SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST); + for (GmTicketList::iterator itr = objmgr.m_GMTicketList.begin(); itr != objmgr.m_GMTicketList.end(); ++itr) + { + if ((*itr)->closed == 0) + continue; + + std::string gmname; + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, (*itr)->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - (*itr)->createtime, true, false)).c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - (*itr)->timestamp, true, false)).c_str()); + if (objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname)) + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + } + SendSysMessage(ss.str().c_str()); + } + return true; +} + +bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args) +{ + if (!*args) + return false; + + uint64 tguid = atoi(args); + GM_Ticket *ticket = objmgr.GetGMTicket(tguid); + if (!ticket || ticket->closed != 0) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + std::string gmname; + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - ticket->createtime, true, false)).c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - ticket->timestamp, true, false)).c_str()); + if (objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname)) + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + } + ss << PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, ticket->message.c_str()); + if (ticket->comment != "") + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, ticket->comment.c_str()); + } + SendSysMessage(ss.str().c_str()); + return true; +} + +bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args) +{ + if (!*args) + return false; + + std::string name = (char*)args; + normalizePlayerName(name); + + Player *plr = objmgr.GetPlayer(name.c_str()); + if (!plr) + { + SendSysMessage(LANG_NO_PLAYERS_FOUND); + return true; + } + + GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(plr->GetGUID()); + if (!ticket) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + std::string gmname; + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(time(NULL) - ticket->createtime, true, false)).c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(time(NULL) - ticket->timestamp, true, false)).c_str()); + if (objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname)) + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + } + ss << PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, ticket->message.c_str()); + if (ticket->comment != "") + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, ticket->comment.c_str()); + } + SendSysMessage(ss.str().c_str()); + return true; +} + +bool ChatHandler::HandleGMTicketCloseByIdCommand(const char* args) +{ + if (!*args) + return false; + + uint64 tguid = atoi(args); + GM_Ticket *ticket = objmgr.GetGMTicket(tguid); + if (!ticket || ticket->closed != 0) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + if (ticket && ticket->assignedToGM != 0 && ticket->assignedToGM != m_session->GetPlayer()->GetGUID()) + { + PSendSysMessage(LANG_COMMAND_TICKETCANNOTCLOSE, ticket->guid); + return true; + } + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETCLOSED, m_session->GetPlayer()->GetName()); + SendGlobalGMSysMessage(ss.str().c_str()); + Player *plr = objmgr.GetPlayer(ticket->playerGuid); + objmgr.RemoveGMTicket(ticket, m_session->GetPlayer()->GetGUID()); + + if (!plr || !plr->IsInWorld()) + return true; + + // send abandon ticket + WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4); + data << uint32(9); + plr->GetSession()->SendPacket(&data); + return true; +} + +bool ChatHandler::HandleGMTicketAssignToCommand(const char* args) +{ + if (!*args) + return false; + + char* tguid = strtok((char*)args, " "); + uint64 ticketGuid = atoi(tguid); + char* targetgm = strtok(NULL, " "); + + if (!targetgm) + return false; + + std::string targm = targetgm; + if (!normalizePlayerName(targm)) + return false; + + Player *cplr = m_session->GetPlayer(); + GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid); + + if (!ticket || ticket->closed != 0) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + uint64 tarGUID = objmgr.GetPlayerGUIDByName(targm.c_str()); + uint64 accid = objmgr.GetPlayerAccountIdByGUID(tarGUID); + uint32 gmlevel = accmgr.GetSecurity(accid, realmID); + + if (!tarGUID || gmlevel == SEC_PLAYER) + { + SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A); + return true; + } + + if (ticket->assignedToGM == tarGUID) + { + PSendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_B, ticket->guid); + return true; + } + + std::string gmname; + objmgr.GetPlayerNameByGUID(tarGUID, gmname); + if (ticket->assignedToGM != 0 && ticket->assignedToGM != cplr->GetGUID()) + { + PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->guid, gmname.c_str()); + return true; + } + + ticket->assignedToGM = tarGUID; + objmgr.AddOrUpdateGMTicket(*ticket); + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + SendGlobalGMSysMessage(ss.str().c_str()); + return true; +} + +bool ChatHandler::HandleGMTicketUnAssignCommand(const char* args) +{ + if (!*args) + return false; + + uint64 ticketGuid = atoi(args); + Player *cplr = m_session->GetPlayer(); + GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid); + + if (!ticket|| ticket->closed != 0) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + if (ticket->assignedToGM == 0) + { + PSendSysMessage(LANG_COMMAND_TICKETNOTASSIGNED, ticket->guid); + return true; + } + + std::string gmname; + objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname); + Player *plr = objmgr.GetPlayer(ticket->assignedToGM); + if (plr && plr->IsInWorld() && plr->GetSession()->GetSecurity() > cplr->GetSession()->GetSecurity()) + { + SendSysMessage(LANG_COMMAND_TICKETUNASSIGNSECURITY); + return true; + } + + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETLISTUNASSIGNED, cplr->GetName()); + SendGlobalGMSysMessage(ss.str().c_str()); + ticket->assignedToGM = 0; + objmgr.AddOrUpdateGMTicket(*ticket); + return true; +} + +bool ChatHandler::HandleGMTicketCommentCommand(const char* args) +{ + if (!*args) + return false; + + char* tguid = strtok((char*)args, " "); + uint64 ticketGuid = atoi(tguid); + char* comment = strtok(NULL, "\n"); + + if (!comment) + return false; + + Player *cplr = m_session->GetPlayer(); + GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid); + + if (!ticket || ticket->closed != 0) + { + PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + if (ticket->assignedToGM != 0 && ticket->assignedToGM != cplr->GetGUID()) + { + PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->guid); + return true; + } + + std::string gmname; + objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname); + ticket->comment = comment; + objmgr.AddOrUpdateGMTicket(*ticket); + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + if (objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname)) + { + ss << PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, gmname.c_str()); + } + ss << PGetParseString(LANG_COMMAND_TICKETLISTADDCOMMENT, cplr->GetName(), ticket->comment.c_str()); + SendGlobalGMSysMessage(ss.str().c_str()); + return true; +} + +bool ChatHandler::HandleGMTicketDeleteByIdCommand(const char* args) +{ + if (!*args) + return false; + uint64 ticketGuid = atoi(args); + GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid); + + if (!ticket) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + if (ticket->closed == 0) + { + SendSysMessage(LANG_COMMAND_TICKETCLOSEFIRST); + return true; + } + + std::stringstream ss; + ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid); + ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str()); + ss << PGetParseString(LANG_COMMAND_TICKETDELETED, m_session->GetPlayer()->GetName()); + SendGlobalGMSysMessage(ss.str().c_str()); + Player *plr = objmgr.GetPlayer(ticket->playerGuid); + objmgr.RemoveGMTicket(ticket, -1, true); + if (plr && plr->IsInWorld()) + { + // Force abandon ticket + WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4); + data << uint32(9); + plr->GetSession()->SendPacket(&data); + } + + ticket = NULL; + return true; +} + +bool ChatHandler::HandleGMTicketReloadCommand(const char*) +{ + objmgr.LoadGMTickets(); + return true; +} + +//Enable\Dissable Invisible mode +bool ChatHandler::HandleGMVisibleCommand(const char* args) +{ + if (!*args) + { + PSendSysMessage(LANG_YOU_ARE, m_session->GetPlayer()->isGMVisible() ? GetTrinityString(LANG_VISIBLE) : GetTrinityString(LANG_INVISIBLE)); + return true; + } + + std::string argstr = (char*)args; + + if (argstr == "on") + { + m_session->GetPlayer()->SetGMVisible(true); + m_session->SendNotification(LANG_INVISIBLE_VISIBLE); + return true; + } + + if (argstr == "off") + { + m_session->SendNotification(LANG_INVISIBLE_INVISIBLE); + m_session->GetPlayer()->SetGMVisible(false); + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + +bool ChatHandler::HandleGPSCommand(const char* args) +{ + WorldObject *obj = NULL; + if (*args) + { + uint64 guid = extractGuidFromLink((char*)args); + if (guid) + obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); + + if (!obj) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + } + else + { + obj = getSelectedUnit(); + + if (!obj) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + } + CellPair cell_val = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + Cell cell(cell_val); + + uint32 zone_id, area_id; + obj->GetZoneAndAreaId(zone_id,area_id); + + MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId()); + AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id); + AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id); + + float zone_x = obj->GetPositionX(); + float zone_y = obj->GetPositionY(); + + Map2ZoneCoordinates(zone_x,zone_y,zone_id); + + Map const *map = obj->GetMap(); + float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT); + float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()); + + GridPair p = Trinity::ComputeGridPair(obj->GetPositionX(), obj->GetPositionY()); + + int gx=63-p.x_coord; + int gy=63-p.y_coord; + + uint32 have_map = Map::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0; + uint32 have_vmap = Map::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0; + + if(have_vmap) + { + if(map->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ())) + PSendSysMessage("You are outdoors"); + else + PSendSysMessage("You are indoor"); + } + else PSendSysMessage("no VMAP available for area info"); + + PSendSysMessage(LANG_MAP_POSITION, + obj->GetMapId(), (mapEntry ? mapEntry->name[GetSessionDbcLocale()] : ""), + zone_id, (zoneEntry ? zoneEntry->area_name[GetSessionDbcLocale()] : ""), + area_id, (areaEntry ? areaEntry->area_name[GetSessionDbcLocale()] : ""), + obj->GetPhaseMask(), + obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), + cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), + zone_x, zone_y, ground_z, floor_z, have_map, have_vmap); + + sLog.outDebug("Player %s GPS call for %s '%s' (%s: %u):", + m_session ? GetNameLink().c_str() : GetTrinityString(LANG_CONSOLE_COMMAND), + (obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(), + (obj->GetTypeId() == TYPEID_PLAYER ? "GUID" : "Entry"), (obj->GetTypeId() == TYPEID_PLAYER ? obj->GetGUIDLow(): obj->GetEntry())); + sLog.outDebug(GetTrinityString(LANG_MAP_POSITION), + obj->GetMapId(), (mapEntry ? mapEntry->name[sWorld.GetDefaultDbcLocale()] : ""), + zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : ""), + area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : ""), + obj->GetPhaseMask(), + obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), + cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), + zone_x, zone_y, ground_z, floor_z, have_map, have_vmap); + + LiquidData liquid_status; + ZLiquidStatus res = map->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status); + if (res) + { + PSendSysMessage(LANG_LIQUID_STATUS, liquid_status.level, liquid_status.depth_level, liquid_status.type, res); + } + return true; +} + +//Summon Player +bool ChatHandler::HandleNamegoCommand(const char* args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + Player* _player = m_session->GetPlayer(); + if (target == _player || target_guid == _player->GetGUID()) + { + PSendSysMessage(LANG_CANT_TELEPORT_SELF); + SetSentErrorMessage(true); + return false; + } + + if (target) + { + std::string nameLink = playerLink(target_name); + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + if (target->IsBeingTeleported()) + { + PSendSysMessage(LANG_IS_TELEPORTED, nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + Map* pMap = m_session->GetPlayer()->GetMap(); + + if (pMap->IsBattleGroundOrArena()) + { + // only allow if gm mode is on + if (!target->isGameMaster()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM,nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + // if both players are in different bgs + else if (target->GetBattleGroundId() && m_session->GetPlayer()->GetBattleGroundId() != target->GetBattleGroundId()) + { + target->LeaveBattleground(false); // Note: should be changed so target gets no Deserter debuff + //PSendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG,nameLink.c_str()); + //SetSentErrorMessage(true); + //return false; + } + // all's well, set bg id + // when porting out from the bg, it will be reset to 0 + target->SetBattleGroundId(m_session->GetPlayer()->GetBattleGroundId(), m_session->GetPlayer()->GetBattleGroundTypeId()); + // remember current position as entry point for return at bg end teleportation + if (!target->GetMap()->IsBattleGroundOrArena()) + target->SetBattleGroundEntryPoint(); + } + else if (pMap->IsDungeon()) + { + Map* cMap = target->GetMap(); + if (cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId()) + { + target->UnbindInstance(pMap->GetInstanceId(), target->GetDungeonDifficulty(), true); + // cannot summon from instance to instance + //PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,nameLink.c_str()); + //SetSentErrorMessage(true); + //return false; + } + + // we are in instance, and can summon only player in our group with us as lead + if (!m_session->GetPlayer()->GetGroup() || !target->GetGroup() || + (target->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) || + (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID())) + // the last check is a bit excessive, but let it be, just in case + { + PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + } + + PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),""); + if (needReportToTarget(target)) + ChatHandler(target).PSendSysMessage(LANG_SUMMONED_BY, playerLink(_player->GetName()).c_str()); + + // stop flight if need + if (target->isInFlight()) + { + target->GetMotionMaster()->MovementExpired(); + target->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + target->SaveRecallPosition(); + + // before GM + float x,y,z; + m_session->GetPlayer()->GetClosePoint(x,y,z,target->GetObjectSize()); + target->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,target->GetOrientation()); + target->SetPhaseMask(m_session->GetPlayer()->GetPhaseMask(), true); + } + else + { + // check offline security + if (HasLowerSecurity(NULL, target_guid)) + return false; + + std::string nameLink = playerLink(target_name); + + PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),GetTrinityString(LANG_OFFLINE)); + + // in point where GM stay + Player::SavePositionInDB(m_session->GetPlayer()->GetMapId(), + m_session->GetPlayer()->GetPositionX(), + m_session->GetPlayer()->GetPositionY(), + m_session->GetPlayer()->GetPositionZ(), + m_session->GetPlayer()->GetOrientation(), + m_session->GetPlayer()->GetZoneId(), + target_guid); + } + + return true; +} + +//Teleport to Player +bool ChatHandler::HandleGonameCommand(const char* args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + Player* _player = m_session->GetPlayer(); + if (target == _player || target_guid == _player->GetGUID()) + { + SendSysMessage(LANG_CANT_TELEPORT_SELF); + SetSentErrorMessage(true); + return false; + } + + if (target) + { + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + std::string chrNameLink = playerLink(target_name); + + Map* cMap = target->GetMap(); + if (cMap->IsBattleGroundOrArena()) + { + // only allow if gm mode is on + if (!_player->isGameMaster()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + // if both players are in different bgs + else if (_player->GetBattleGroundId() && _player->GetBattleGroundId() != target->GetBattleGroundId()) + { + _player->LeaveBattleground(false); // Note: should be changed so _player gets no Deserter debuff + //PSendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG,chrNameLink.c_str()); + //SetSentErrorMessage(true); + //return false; + } + // all's well, set bg id + // when porting out from the bg, it will be reset to 0 + _player->SetBattleGroundId(target->GetBattleGroundId(), target->GetBattleGroundTypeId()); + // remember current position as entry point for return at bg end teleportation + if (!_player->GetMap()->IsBattleGroundOrArena()) + _player->SetBattleGroundEntryPoint(); + } + else if (cMap->IsDungeon()) + { + //Map* pMap = _player->GetMap(); + + // we have to go to instance, and can go to player only if: + // 1) we are in his group (either as leader or as member) + // 2) we are not bound to any group and have GM mode on + if (_player->GetGroup()) + { + // we are in group, we can go only if we are in the player group + if (_player->GetGroup() != target->GetGroup()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_INST_PARTY,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + } + else + { + // we are not in group, let's verify our GM mode + if (!_player->isGameMaster()) + { + PSendSysMessage(LANG_CANNOT_GO_TO_INST_GM,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + } + + // if the player or the player's group is bound to another instance + // the player will not be bound to another one + InstancePlayerBind *pBind = _player->GetBoundInstance(target->GetMapId(), target->GetDifficulty(cMap->IsRaid())); + if (!pBind) + { + Group *group = _player->GetGroup(); + // if no bind exists, create a solo bind + InstanceGroupBind *gBind = group ? group->GetBoundInstance(target) : NULL; // if no bind exists, create a solo bind + if (!gBind) + if (InstanceSave *save = sInstanceSaveManager.GetInstanceSave(target->GetInstanceId())) + _player->BindToInstance(save, !save->CanReset()); + } + + if (cMap->IsRaid()) + _player->SetRaidDifficulty(target->GetRaidDifficulty()); + else + _player->SetDungeonDifficulty(target->GetDungeonDifficulty()); + } + + PSendSysMessage(LANG_APPEARING_AT, chrNameLink.c_str()); + //if (needReportToTarget(target)) + // ChatHandler(target).PSendSysMessage(LANG_APPEARING_TO, GetNameLink().c_str()); + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + // to point to see at target with same orientation + float x,y,z; + target->GetContactPoint(_player,x,y,z); + + _player->TeleportTo(target->GetMapId(), x, y, z, _player->GetAngle(target), TELE_TO_GM_MODE); + _player->SetPhaseMask(target->GetPhaseMask(), true); + } + else + { + // check offline security + if (HasLowerSecurity(NULL, target_guid)) + return false; + + std::string nameLink = playerLink(target_name); + + PSendSysMessage(LANG_APPEARING_AT, nameLink.c_str()); + + // to point where player stay (if loaded) + float x,y,z,o; + uint32 map; + bool in_flight; + if (!Player::LoadPositionFromDB(map,x,y,z,o,in_flight,target_guid)) + return false; + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(map, x, y, z,_player->GetOrientation()); + } + + return true; +} + +// Teleport player to last position +bool ChatHandler::HandleRecallCommand(const char* args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + if (target->IsBeingTeleported()) + { + PSendSysMessage(LANG_IS_TELEPORTED, GetNameLink(target).c_str()); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (target->isInFlight()) + { + target->GetMotionMaster()->MovementExpired(); + target->CleanupAfterTaxiFlight(); + } + + target->TeleportTo(target->m_recallMap, target->m_recallX, target->m_recallY, target->m_recallZ, target->m_recallO); + return true; +} + +//Edit Player HP +bool ChatHandler::HandleModifyHPCommand(const char* args) +{ + if (!*args) + return false; + + int32 hp = atoi((char*)args); + int32 hpm = atoi((char*)args); + + if (hp < 1 || hpm < 1 || hpm < hp) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_HP, GetNameLink(chr).c_str(), hp, hpm); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, GetNameLink().c_str(), hp, hpm); + + chr->SetMaxHealth(hpm); + chr->SetHealth(hp); + + return true; +} + +//Edit Player Mana +bool ChatHandler::HandleModifyManaCommand(const char* args) +{ + if (!*args) + return false; + + // char* pmana = strtok((char*)args, " "); + // if (!pmana) + // return false; + + // char* pmanaMax = strtok(NULL, " "); + // if (!pmanaMax) + // return false; + + // int32 manam = atoi(pmanaMax); + // int32 mana = atoi(pmana); + int32 mana = atoi((char*)args); + int32 manam = atoi((char*)args); + + if (mana <= 0 || manam <= 0 || manam < mana) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_MANA, GetNameLink(chr).c_str(), mana, manam); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, GetNameLink().c_str(), mana, manam); + + chr->SetMaxPower(POWER_MANA,manam); + chr->SetPower(POWER_MANA, mana); + + return true; +} + +//Edit Player Energy +bool ChatHandler::HandleModifyEnergyCommand(const char* args) +{ + if (!*args) + return false; + + // char* pmana = strtok((char*)args, " "); + // if (!pmana) + // return false; + + // char* pmanaMax = strtok(NULL, " "); + // if (!pmanaMax) + // return false; + + // int32 manam = atoi(pmanaMax); + // int32 mana = atoi(pmana); + + int32 energy = atoi((char*)args)*10; + int32 energym = atoi((char*)args)*10; + + if (energy <= 0 || energym <= 0 || energym < energy) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (!chr) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_ENERGY, GetNameLink(chr).c_str(), energy/10, energym/10); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, GetNameLink().c_str(), energy/10, energym/10); + + chr->SetMaxPower(POWER_ENERGY,energym); + chr->SetPower(POWER_ENERGY, energy); + + sLog.outDetail(GetTrinityString(LANG_CURRENT_ENERGY),chr->GetMaxPower(POWER_ENERGY)); + + return true; +} + +//Edit Player Rage +bool ChatHandler::HandleModifyRageCommand(const char* args) +{ + if (!*args) + return false; + + // char* pmana = strtok((char*)args, " "); + // if (!pmana) + // return false; + + // char* pmanaMax = strtok(NULL, " "); + // if (!pmanaMax) + // return false; + + // int32 manam = atoi(pmanaMax); + // int32 mana = atoi(pmana); + + int32 rage = atoi((char*)args)*10; + int32 ragem = atoi((char*)args)*10; + + if (rage <= 0 || ragem <= 0 || ragem < rage) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_RAGE, GetNameLink(chr).c_str(), rage/10, ragem/10); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_RAGE_CHANGED, GetNameLink().c_str(), rage/10, ragem/10); + + chr->SetMaxPower(POWER_RAGE,ragem); + chr->SetPower(POWER_RAGE, rage); + + return true; +} + +// Edit Player Runic Power +bool ChatHandler::HandleModifyRunicPowerCommand(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) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_RUNIC_POWER, GetNameLink(chr).c_str(), rune/10, runem/10); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_RUNIC_POWER_CHANGED, GetNameLink().c_str(), rune/10, runem/10); + + chr->SetMaxPower(POWER_RUNIC_POWER,runem); + chr->SetPower(POWER_RUNIC_POWER, rune); + + return true; +} + +//Edit Player Faction +bool ChatHandler::HandleModifyFactionCommand(const char* args) +{ + if (!*args) + return false; + + char* pfactionid = extractKeyFromLink((char*)args,"Hfaction"); + + Creature* chr = getSelectedCreature(); + if (!chr) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (!pfactionid) + { + if (chr) + { + uint32 factionid = chr->getFaction(); + uint32 flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS); + uint32 npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS); + uint32 dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS); + PSendSysMessage(LANG_CURRENT_FACTION,chr->GetGUIDLow(),factionid,flag,npcflag,dyflag); + } + return true; + } + + if (!chr) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + uint32 factionid = atoi(pfactionid); + uint32 flag; + + char *pflag = strtok(NULL, " "); + if (!pflag) + flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS); + else + flag = atoi(pflag); + + char* pnpcflag = strtok(NULL, " "); + + uint32 npcflag; + if (!pnpcflag) + npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS); + else + npcflag = atoi(pnpcflag); + + char* pdyflag = strtok(NULL, " "); + + uint32 dyflag; + if (!pdyflag) + dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS); + else + dyflag = atoi(pdyflag); + + if (!sFactionTemplateStore.LookupEntry(factionid)) + { + PSendSysMessage(LANG_WRONG_FACTION, factionid); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_FACTION, chr->GetGUIDLow(),factionid,flag,npcflag,dyflag); + + chr->setFaction(factionid); + chr->SetUInt32Value(UNIT_FIELD_FLAGS,flag); + chr->SetUInt32Value(UNIT_NPC_FLAGS,npcflag); + chr->SetUInt32Value(UNIT_DYNAMIC_FLAGS,dyflag); + + return true; +} + +//Edit Player Spell +bool ChatHandler::HandleModifySpellCommand(const char* args) +{ + if (!*args) return false; + char* pspellflatid = strtok((char*)args, " "); + if (!pspellflatid) + return false; + + char* pop = strtok(NULL, " "); + if (!pop) + return false; + + char* pval = strtok(NULL, " "); + if (!pval) + return false; + + uint16 mark; + + char* pmark = strtok(NULL, " "); + + uint8 spellflatid = atoi(pspellflatid); + uint8 op = atoi(pop); + uint16 val = atoi(pval); + if (!pmark) + mark = 65535; + else + mark = atoi(pmark); + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, GetNameLink().c_str(), spellflatid, val, mark); + + WorldPacket data(SMSG_SET_FLAT_SPELL_MODIFIER, (1+1+2+2)); + data << uint8(spellflatid); + data << uint8(op); + data << uint16(val); + data << uint16(mark); + chr->GetSession()->SendPacket(&data); + + return true; +} + +//Edit Player TP +bool ChatHandler::HandleModifyTalentCommand (const char* args) +{ + if (!*args) + return false; + + int tp = atoi((char*)args); + if (tp < 0) + return false; + + Unit* target = getSelectedUnit(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + if (target->GetTypeId() == TYPEID_PLAYER) + { + // check online security + if (HasLowerSecurity((Player*)target, 0)) + return false; + target->ToPlayer()->SetFreeTalentPoints(tp); + target->ToPlayer()->SendTalentsInfoData(false); + return true; + } + else if (target->ToCreature()->isPet()) + { + Unit *owner = target->GetOwner(); + if (owner && owner->GetTypeId() == TYPEID_PLAYER && ((Pet *)target)->IsPermanentPetFor(owner->ToPlayer())) + { + // check online security + if (HasLowerSecurity((Player*)owner, 0)) + return false; + ((Pet *)target)->SetFreeTalentPoints(tp); + owner->ToPlayer()->SendTalentsInfoData(true); + return true; + } + } + + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; +} + +//Enable On\OFF all taxi paths +bool ChatHandler::HandleTaxiCheatCommand(const char* args) +{ + if (!*args) + { + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; + } + + std::string argstr = (char*)args; + + Player *chr = getSelectedPlayer(); + if (!chr) + { + chr=m_session->GetPlayer(); + } + + // check online security + else if (HasLowerSecurity(chr, 0)) + return false; + + if (argstr == "on") + { + chr->SetTaxiCheater(true); + PSendSysMessage(LANG_YOU_GIVE_TAXIS, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_ADDED, GetNameLink().c_str()); + return true; + } + + if (argstr == "off") + { + chr->SetTaxiCheater(false); + PSendSysMessage(LANG_YOU_REMOVE_TAXIS, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_REMOVED, GetNameLink().c_str()); + + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + +//Edit Player Aspeed +bool ChatHandler::HandleModifyASpeedCommand(const char* args) +{ + if (!*args) + return false; + + float ASpeed = (float)atof((char*)args); + + if (ASpeed > 50.0f || ASpeed < 0.1f) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + + if (chr->isInFlight()) + { + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chrNameLink.c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, GetNameLink().c_str(), ASpeed); + + chr->SetSpeed(MOVE_WALK, ASpeed,true); + chr->SetSpeed(MOVE_RUN, ASpeed,true); + chr->SetSpeed(MOVE_SWIM, ASpeed,true); + //chr->SetSpeed(MOVE_TURN, ASpeed,true); + chr->SetSpeed(MOVE_FLIGHT, ASpeed,true); + return true; +} + +//Edit Player Speed +bool ChatHandler::HandleModifySpeedCommand(const char* args) +{ + if (!*args) + return false; + + float Speed = (float)atof((char*)args); + + if (Speed > 50.0f || Speed < 0.1f) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + + if (chr->isInFlight()) + { + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chrNameLink.c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, GetNameLink().c_str(), Speed); + + chr->SetSpeed(MOVE_RUN,Speed,true); + + return true; +} + +//Edit Player Swim Speed +bool ChatHandler::HandleModifySwimCommand(const char* args) +{ + if (!*args) + return false; + + float Swim = (float)atof((char*)args); + + if (Swim > 50.0f || Swim < 0.1f) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + + if (chr->isInFlight()) + { + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chrNameLink.c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, GetNameLink().c_str(), Swim); + + chr->SetSpeed(MOVE_SWIM,Swim,true); + + return true; +} + +//Edit Player Walk Speed +bool ChatHandler::HandleModifyBWalkCommand(const char* args) +{ + if (!*args) + return false; + + float BSpeed = (float)atof((char*)args); + + if (BSpeed > 50.0f || BSpeed < 0.1f) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + std::string chrNameLink = GetNameLink(chr); + + if (chr->isInFlight()) + { + PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chrNameLink.c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, GetNameLink().c_str(), BSpeed); + + chr->SetSpeed(MOVE_RUN_BACK,BSpeed,true); + + return true; +} + +//Edit Player Fly +bool ChatHandler::HandleModifyFlyCommand(const char* args) +{ + if (!*args) + return false; + + float FSpeed = (float)atof((char*)args); + + if (FSpeed > 50.0f || FSpeed < 0.1f) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, GetNameLink().c_str(), FSpeed); + + chr->SetSpeed(MOVE_FLIGHT,FSpeed,true); + + return true; +} + +//Edit Player Scale +bool ChatHandler::HandleModifyScaleCommand(const char* args) +{ + if (!*args) + return false; + + float Scale = (float)atof((char*)args); + if (Scale > 10.0f || Scale < 0.1f) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, GetNameLink().c_str(), Scale); + + chr->SetFloatValue(OBJECT_FIELD_SCALE_X, Scale); + + return true; +} + +//Enable Player mount +bool ChatHandler::HandleModifyMountCommand(const char* args) +{ + if (!*args) + return false; + + uint16 mId = 1147; + float speed = (float)15; + uint32 num = 0; + + num = atoi((char*)args); + switch(num) + { + case 1: + mId=14340; + break; + case 2: + mId=4806; + break; + case 3: + mId=6471; + break; + case 4: + mId=12345; + break; + case 5: + mId=6472; + break; + case 6: + mId=6473; + break; + case 7: + mId=10670; + break; + case 8: + mId=10719; + break; + case 9: + mId=10671; + break; + case 10: + mId=10672; + break; + case 11: + mId=10720; + break; + case 12: + mId=14349; + break; + case 13: + mId=11641; + break; + case 14: + mId=12244; + break; + case 15: + mId=12242; + break; + case 16: + mId=14578; + break; + case 17: + mId=14579; + break; + case 18: + mId=14349; + break; + case 19: + mId=12245; + break; + case 20: + mId=14335; + break; + case 21: + mId=207; + break; + case 22: + mId=2328; + break; + case 23: + mId=2327; + break; + case 24: + mId=2326; + break; + case 25: + mId=14573; + break; + case 26: + mId=14574; + break; + case 27: + mId=14575; + break; + case 28: + mId=604; + break; + case 29: + mId=1166; + break; + case 30: + mId=2402; + break; + case 31: + mId=2410; + break; + case 32: + mId=2409; + break; + case 33: + mId=2408; + break; + case 34: + mId=2405; + break; + case 35: + mId=14337; + break; + case 36: + mId=6569; + break; + case 37: + mId=10661; + break; + case 38: + mId=10666; + break; + case 39: + mId=9473; + break; + case 40: + mId=9476; + break; + case 41: + mId=9474; + break; + case 42: + mId=14374; + break; + case 43: + mId=14376; + break; + case 44: + mId=14377; + break; + case 45: + mId=2404; + break; + case 46: + mId=2784; + break; + case 47: + mId=2787; + break; + case 48: + mId=2785; + break; + case 49: + mId=2736; + break; + case 50: + mId=2786; + break; + case 51: + mId=14347; + break; + case 52: + mId=14346; + break; + case 53: + mId=14576; + break; + case 54: + mId=9695; + break; + case 55: + mId=9991; + break; + case 56: + mId=6448; + break; + case 57: + mId=6444; + break; + case 58: + mId=6080; + break; + case 59: + mId=6447; + break; + case 60: + mId=4805; + break; + case 61: + mId=9714; + break; + case 62: + mId=6448; + break; + case 63: + mId=6442; + break; + case 64: + mId=14632; + break; + case 65: + mId=14332; + break; + case 66: + mId=14331; + break; + case 67: + mId=8469; + break; + case 68: + mId=2830; + break; + case 69: + mId=2346; + break; + default: + SendSysMessage(LANG_NO_MOUNT); + SetSentErrorMessage(true); + return false; + } + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + PSendSysMessage(LANG_YOU_GIVE_MOUNT, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_MOUNT_GIVED, GetNameLink().c_str()); + + chr->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); + chr->Mount(mId); + + WorldPacket data(SMSG_FORCE_RUN_SPEED_CHANGE, (8+4+1+4)); + data.append(chr->GetPackGUID()); + data << (uint32)0; + data << (uint8)0; //new 2.1.0 + data << float(speed); + chr->SendMessageToSet(&data, true); + + data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, (8+4+4)); + data.append(chr->GetPackGUID()); + data << (uint32)0; + data << float(speed); + chr->SendMessageToSet(&data, true); + + return true; +} + +//Edit Player money +bool ChatHandler::HandleModifyMoneyCommand(const char* args) +{ + if (!*args) + return false; + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(chr, 0)) + return false; + + int32 addmoney = atoi((char*)args); + + uint32 moneyuser = chr->GetMoney(); + + if (addmoney < 0) + { + int32 newmoney = int32(moneyuser) + addmoney; + + sLog.outDetail(GetTrinityString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney); + if (newmoney <= 0) + { + PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, GetNameLink().c_str()); + + chr->SetMoney(0); + } + else + { + if (newmoney > MAX_MONEY_AMOUNT) + newmoney = MAX_MONEY_AMOUNT; + + PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, GetNameLink().c_str(), abs(addmoney)); + chr->SetMoney(newmoney); + } + } + else + { + PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, GetNameLink().c_str(), addmoney); + + if (addmoney >=MAX_MONEY_AMOUNT) + chr->SetMoney(MAX_MONEY_AMOUNT); + else + chr->ModifyMoney(addmoney); + } + + sLog.outDetail(GetTrinityString(LANG_NEW_MONEY), moneyuser, addmoney, chr->GetMoney()); + + return true; +} + +//Edit Unit field +bool ChatHandler::HandleModifyBitCommand(const char* args) +{ + if (!*args) + return false; + + Unit *unit = getSelectedUnit(); + if (!unit) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (unit->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity(unit->ToPlayer(), 0)) + return false; + + char* pField = strtok((char*)args, " "); + if (!pField) + return false; + + char* pBit = strtok(NULL, " "); + if (!pBit) + return false; + + uint16 field = atoi(pField); + uint32 bit = atoi(pBit); + + if (field < OBJECT_END || field >= unit->GetValuesCount()) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + if (bit < 1 || bit > 32) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + if (unit->HasFlag(field, (1<<(bit-1)))) + { + unit->RemoveFlag(field, (1<<(bit-1))); + PSendSysMessage(LANG_REMOVE_BIT, bit, field); + } + else + { + unit->SetFlag(field, (1<<(bit-1))); + PSendSysMessage(LANG_SET_BIT, bit, field); + } + return true; +} + +bool ChatHandler::HandleModifyHonorCommand (const char* args) +{ + if (!*args) + return false; + + Player *target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + int32 amount = (uint32)atoi(args); + + target->ModifyHonorPoints(amount); + + PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, GetNameLink(target).c_str(), target->GetHonorPoints()); + + return true; +} + +bool ChatHandler::HandleTeleCommand(const char * args) +{ + if (!*args) + return false; + + Player* _player = m_session->GetPlayer(); + + // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r + GameTele const* tele = extractGameTeleFromLink((char*)args); + + if (!tele) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + + if (_player->isInCombat()) + { + SendSysMessage(LANG_YOU_IN_COMBAT); + SetSentErrorMessage(true); + return false; + } + + MapEntry const * me = sMapStore.LookupEntry(tele->mapId); + if (!me || me->IsBattleGroundOrArena()) + { + SendSysMessage(LANG_CANNOT_TELE_TO_BG); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation); + return true; +} + +bool ChatHandler::HandleLookupAreaCommand(const char* args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr (namepart,wnamepart)) + return false; + + bool found = false; + + // converting string that we try to find to lower case + wstrToLower (wnamepart); + + // Search in AreaTable.dbc + for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows (); ++areaflag) + { + AreaTableEntry const *areaEntry = sAreaStore.LookupEntry (areaflag); + if (areaEntry) + { + int loc = GetSessionDbcLocale (); + std::string name = areaEntry->area_name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo (name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale ()) + continue; + + name = areaEntry->area_name[loc]; + if (name.empty ()) + continue; + + if (Utf8FitTo (name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + // send area in "id - [name]" format + std::ostringstream ss; + if (m_session) + ss << areaEntry->ID << " - |cffffffff|Harea:" << areaEntry->ID << "|h[" << name << " " << localeNames[loc]<< "]|h|r"; + else + ss << areaEntry->ID << " - " << name << " " << localeNames[loc]; + + SendSysMessage (ss.str ().c_str()); + + if (!found) + found = true; + } + } + } + + if (!found) + SendSysMessage (LANG_COMMAND_NOAREAFOUND); + + return true; +} + +//Find tele in game_tele order by name +bool ChatHandler::HandleLookupTeleCommand(const char * args) +{ + if (!*args) + { + SendSysMessage(LANG_COMMAND_TELE_PARAMETER); + SetSentErrorMessage(true); + return false; + } + + char const* str = strtok((char*)args, " "); + if (!str) + return false; + + std::string namepart = str; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + std::ostringstream reply; + + GameTeleMap const & teleMap = objmgr.GetGameTeleMap(); + for (GameTeleMap::const_iterator itr = teleMap.begin(); itr != teleMap.end(); ++itr) + { + GameTele const* tele = &itr->second; + + if (tele->wnameLow.find(wnamepart) == std::wstring::npos) + continue; + + if (m_session) + reply << " |cffffffff|Htele:" << itr->first << "|h[" << tele->name << "]|h|r\n"; + else + reply << " " << itr->first << " " << tele->name << "\n"; + } + + if (reply.str().empty()) + SendSysMessage(LANG_COMMAND_TELE_NOLOCATION); + else + PSendSysMessage(LANG_COMMAND_TELE_LOCATION,reply.str().c_str()); + + return true; +} + +//Enable\Dissable accept whispers (for GM) +bool ChatHandler::HandleWhispersCommand(const char* args) +{ + if (!*args) + { + PSendSysMessage(LANG_COMMAND_WHISPERACCEPTING, m_session->GetPlayer()->isAcceptWhispers() ? GetTrinityString(LANG_ON) : GetTrinityString(LANG_OFF)); + return true; + } + + std::string argstr = (char*)args; + // whisper on + if (argstr == "on") + { + m_session->GetPlayer()->SetAcceptWhispers(true); + SendSysMessage(LANG_COMMAND_WHISPERON); + return true; + } + + // whisper off + if (argstr == "off") + { + m_session->GetPlayer()->SetAcceptWhispers(false); + SendSysMessage(LANG_COMMAND_WHISPEROFF); + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + +//Save all players in the world +bool ChatHandler::HandleSaveAllCommand(const char* /*args*/) +{ + ObjectAccessor::Instance().SaveAllPlayers(); + SendSysMessage(LANG_PLAYERS_SAVED); + return true; +} + +//Send mail by command +bool ChatHandler::HandleSendMailCommand(const char* args) +{ + // format: name "subject text" "mail text" + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + char* tail1 = strtok(NULL, ""); + if (!tail1) + return false; + + char* msgSubject = extractQuotedArg(tail1); + if (!msgSubject) + return false; + + char* tail2 = strtok(NULL, ""); + if (!tail2) + return false; + + char* msgText = extractQuotedArg(tail2); + if (!msgText) + return false; + + // msgSubject, msgText isn't NUL after prev. check + std::string subject = msgSubject; + std::string text = msgText; + + // from console show not existed sender + MailSender sender(MAIL_NORMAL,m_session ? m_session->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM); + + MailDraft(subject, text) + .SendMailTo(MailReceiver(target,GUID_LOPART(target_guid)),sender); + + std::string nameLink = playerLink(target_name); + PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); + return true; +} + +// teleport player to given game_tele.entry +bool ChatHandler::HandleTeleNameCommand(const char * args) +{ + char* nameStr; + char* teleStr; + extractOptFirstArg((char*)args,&nameStr,&teleStr); + if (!teleStr) + return false; + + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) + return false; + + // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r + GameTele const* tele = extractGameTeleFromLink(teleStr); + if (!tele) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + +/* MapEntry const * me = sMapStore.LookupEntry(tele->mapId); + if (!me || me->IsBattleGroundOrArena()) + { + SendSysMessage(LANG_CANNOT_TELE_TO_BG); + SetSentErrorMessage(true); + return false; + } + + Player *chr = objmgr.GetPlayer(name.c_str());*/ + + if (target) + { + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + std::string chrNameLink = playerLink(target_name); + + if (target->IsBeingTeleported() == true) + { + PSendSysMessage(LANG_IS_TELEPORTED, chrNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_TELEPORTING_TO, chrNameLink.c_str(),"", tele->name.c_str()); + if (needReportToTarget(target)) + ChatHandler(target).PSendSysMessage(LANG_TELEPORTED_TO_BY, GetNameLink().c_str()); + + // stop flight if need + if (target->isInFlight()) + { + target->GetMotionMaster()->MovementExpired(); + target->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + target->SaveRecallPosition(); + + target->TeleportTo(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation); + } + else + { + // check offline security + if (HasLowerSecurity(NULL, target_guid)) + return false; + + std::string nameLink = playerLink(target_name); + + PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), GetTrinityString(LANG_OFFLINE), tele->name.c_str()); + Player::SavePositionInDB(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation, + MapManager::Instance().GetZoneId(tele->mapId,tele->position_x,tele->position_y,tele->position_z),target_guid); + } + + return true; +} + +//Teleport group to given game_tele.entry +bool ChatHandler::HandleTeleGroupCommand(const char * args) +{ + if (!*args) + return false; + + Player *player = getSelectedPlayer(); + if (!player) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(player, 0)) + return false; + + // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r + GameTele const* tele = extractGameTeleFromLink((char*)args); + if (!tele) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + + MapEntry const * me = sMapStore.LookupEntry(tele->mapId); + if (!me || me->IsBattleGroundOrArena()) + { + SendSysMessage(LANG_CANNOT_TELE_TO_BG); + SetSentErrorMessage(true); + return false; + } + + std::string nameLink = GetNameLink(player); + + Group *grp = player->GetGroup(); + if (!grp) + { + PSendSysMessage(LANG_NOT_IN_GROUP,nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *pl = itr->getSource(); + + if (!pl || !pl->GetSession()) + continue; + + // check online security + if (HasLowerSecurity(pl, 0)) + return false; + + std::string plNameLink = GetNameLink(pl); + + if (pl->IsBeingTeleported()) + { + PSendSysMessage(LANG_IS_TELEPORTED, plNameLink.c_str()); + continue; + } + + PSendSysMessage(LANG_TELEPORTING_TO, plNameLink.c_str(),"", tele->name.c_str()); + if (needReportToTarget(pl)) + ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, nameLink.c_str()); + + // stop flight if need + if (pl->isInFlight()) + { + pl->GetMotionMaster()->MovementExpired(); + pl->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + pl->SaveRecallPosition(); + + pl->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation); + } + + return true; +} + +//Summon group of player +bool ChatHandler::HandleGroupgoCommand(const char* args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + Group *grp = target->GetGroup(); + + std::string nameLink = GetNameLink(target); + + if (!grp) + { + PSendSysMessage(LANG_NOT_IN_GROUP,nameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + Map* gmMap = m_session->GetPlayer()->GetMap(); + bool to_instance = gmMap->Instanceable(); + + // we are in instance, and can summon only player in our group with us as lead + if (to_instance && ( + !m_session->GetPlayer()->GetGroup() || (grp->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) || + (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()))) + // the last check is a bit excessive, but let it be, just in case + { + SendSysMessage(LANG_CANNOT_SUMMON_TO_INST); + SetSentErrorMessage(true); + return false; + } + + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *pl = itr->getSource(); + + if (!pl || pl == m_session->GetPlayer() || !pl->GetSession()) + continue; + + // check online security + if (HasLowerSecurity(pl, 0)) + return false; + + std::string plNameLink = GetNameLink(pl); + + if (pl->IsBeingTeleported() == true) + { + PSendSysMessage(LANG_IS_TELEPORTED, plNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + if (to_instance) + { + Map* plMap = pl->GetMap(); + + if (plMap->Instanceable() && plMap->GetInstanceId() != gmMap->GetInstanceId()) + { + // cannot summon from instance to instance + PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,plNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + } + + PSendSysMessage(LANG_SUMMONING, plNameLink.c_str(),""); + if (needReportToTarget(pl)) + ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, GetNameLink().c_str()); + + // stop flight if need + if (pl->isInFlight()) + { + pl->GetMotionMaster()->MovementExpired(); + pl->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + pl->SaveRecallPosition(); + + // before GM + float x,y,z; + m_session->GetPlayer()->GetClosePoint(x,y,z,pl->GetObjectSize()); + pl->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,pl->GetOrientation()); + } + + return true; +} + +bool ChatHandler::HandleGoTaxinodeCommand(const char* args) +{ + Player* _player = m_session->GetPlayer(); + + if (!*args) + return false; + + char* cNodeId = extractKeyFromLink((char*)args,"Htaxinode"); + if (!cNodeId) + return false; + + int32 i_nodeId = atoi(cNodeId); + if (!i_nodeId) + return false; + + TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i_nodeId); + if (!node) + { + PSendSysMessage(LANG_COMMAND_GOTAXINODENOTFOUND,i_nodeId); + SetSentErrorMessage(true); + return false; + } + + if ((node->x == 0.0f && node->y == 0.0f && node->z == 0.0f) || + !MapManager::IsValidMapCoord(node->map_id,node->x,node->y,node->z)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,node->x,node->y,node->map_id); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(node->map_id, node->x, node->y, node->z, _player->GetOrientation()); + return true; +} + +//teleport at coordinates +bool ChatHandler::HandleGoXYCommand(const char* args) +{ + if (!*args) + return false; + + Player* _player = m_session->GetPlayer(); + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + char* pmapid = strtok(NULL, " "); + + if (!px || !py) + return false; + + float x = (float)atof(px); + float y = (float)atof(py); + uint32 mapid; + if (pmapid) + mapid = (uint32)atoi(pmapid); + else mapid = _player->GetMapId(); + + if (!MapManager::IsValidMapCoord(mapid,x,y)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + Map const *map = MapManager::Instance().CreateBaseMap(mapid); + float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y)); + + _player->TeleportTo(mapid, x, y, z, _player->GetOrientation()); + + return true; +} + +//teleport at coordinates, including Z +bool ChatHandler::HandleGoXYZCommand(const char* args) +{ + if (!*args) + return false; + + Player* _player = m_session->GetPlayer(); + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + char* pz = strtok(NULL, " "); + char* pmapid = strtok(NULL, " "); + + if (!px || !py || !pz) + return false; + + float x = (float)atof(px); + float y = (float)atof(py); + float z = (float)atof(pz); + uint32 mapid; + if (pmapid) + mapid = (uint32)atoi(pmapid); + else + mapid = _player->GetMapId(); + + if (!MapManager::IsValidMapCoord(mapid,x,y,z)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(mapid, x, y, z, _player->GetOrientation()); + + return true; +} + +//teleport at coordinates +bool ChatHandler::HandleGoZoneXYCommand(const char* args) +{ + if (!*args) + return false; + + Player* _player = m_session->GetPlayer(); + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + char* tail = strtok(NULL,""); + + char* cAreaId = extractKeyFromLink(tail,"Harea"); // string or [name] Shift-click form |color|Harea:area_id|h[name]|h|r + + if (!px || !py) + return false; + + float x = (float)atof(px); + float y = (float)atof(py); + + // prevent accept wrong numeric args + if ((x == 0.0f && *px != '0') || (y == 0.0f && *py != '0')) + return false; + + uint32 areaid = cAreaId ? (uint32)atoi(cAreaId) : _player->GetZoneId(); + + AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaid); + + if (x<0 || x>100 || y<0 || y>100 || !areaEntry) + { + PSendSysMessage(LANG_INVALID_ZONE_COORD,x,y,areaid); + SetSentErrorMessage(true); + return false; + } + + // update to parent zone if exist (client map show only zones without parents) + AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry; + + Map const *map = MapManager::Instance().CreateBaseMap(zoneEntry->mapid); + + if (map->Instanceable()) + { + PSendSysMessage(LANG_INVALID_ZONE_MAP,areaEntry->ID,areaEntry->area_name[GetSessionDbcLocale()],map->GetId(),map->GetMapName()); + SetSentErrorMessage(true); + return false; + } + + Zone2MapCoordinates(x,y,zoneEntry->ID); + + if (!MapManager::IsValidMapCoord(zoneEntry->mapid,x,y)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,zoneEntry->mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y)); + _player->TeleportTo(zoneEntry->mapid, x, y, z, _player->GetOrientation()); + + return true; +} + +//teleport to grid +bool ChatHandler::HandleGoGridCommand(const char* args) +{ + if (!*args) return false; + Player* _player = m_session->GetPlayer(); + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + char* pmapid = strtok(NULL, " "); + + if (!px || !py) + return false; + + float grid_x = (float)atof(px); + float grid_y = (float)atof(py); + uint32 mapid; + if (pmapid) + mapid = (uint32)atoi(pmapid); + else mapid = _player->GetMapId(); + + // center of grid + float x = (grid_x-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS; + float y = (grid_y-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS; + + if (!MapManager::IsValidMapCoord(mapid,x,y)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + Map const *map = MapManager::Instance().CreateBaseMap(mapid); + float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y)); + _player->TeleportTo(mapid, x, y, z, _player->GetOrientation()); + + return true; +} + +bool ChatHandler::HandleModifyDrunkCommand(const char* args) +{ + if (!*args) return false; + + uint32 drunklevel = (uint32)atoi(args); + if (drunklevel > 100) + drunklevel = 100; + + uint16 drunkMod = drunklevel * 0xFFFF / 100; + + m_session->GetPlayer()->SetDrunkValue(drunkMod); + + return true; +} + diff --git a/src/server/game/Chat/Level2.cpp b/src/server/game/Chat/Level2.cpp new file mode 100644 index 00000000000..4f299c932e8 --- /dev/null +++ b/src/server/game/Chat/Level2.cpp @@ -0,0 +1,4526 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "DBCStores.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Item.h" +#include "GameObject.h" +#include "Opcodes.h" +#include "Chat.h" +#include "MapManager.h" +#include "Language.h" +#include "World.h" +#include "GameEventMgr.h" +#include "SpellMgr.h" +#include "PoolHandler.h" +#include "AccountMgr.h" +#include "WaypointManager.h" +#include "Util.h" +#include +#include +#include +#include +#include "GlobalEvents.h" +#include "OutdoorPvPMgr.h" + +#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand +#include "CreatureGroups.h" + +//mute player for some times +bool ChatHandler::HandleMuteCommand(const char* args) +{ + char* nameStr; + char* delayStr; + extractOptFirstArg((char*)args,&nameStr,&delayStr); + if (!delayStr) + return false; + + char *mutereason = strtok(NULL, "\r"); + std::string mutereasonstr = "No reason"; + if (mutereason != NULL) + mutereasonstr = mutereason; + + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) + return false; + + uint32 account_id = target ? target->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(target_guid); + + // find only player from same account if any + if (!target) + if (WorldSession* session = sWorld.FindSession(account_id)) + target = session->GetPlayer(); + + uint32 notspeaktime = (uint32) atoi(delayStr); + + // must have strong lesser security level + if (HasLowerSecurity (target,target_guid,true)) + return false; + + time_t mutetime = time(NULL) + notspeaktime*60; + + if (target) + target->GetSession()->m_muteTime = mutetime; + + LoginDatabase.PExecute("UPDATE account SET mutetime = " UI64FMTD " WHERE id = '%u'",uint64(mutetime), account_id); + + if (target) + ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime, mutereasonstr.c_str()); + + std::string nameLink = playerLink(target_name); + + PSendSysMessage(LANG_YOU_DISABLE_CHAT, nameLink.c_str(), notspeaktime, mutereasonstr.c_str()); + + return true; +} + +//unmute player +bool ChatHandler::HandleUnmuteCommand(const char* args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + uint32 account_id = target ? target->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(target_guid); + + // find only player from same account if any + if (!target) + if (WorldSession* session = sWorld.FindSession(account_id)) + target = session->GetPlayer(); + + // must have strong lesser security level + if (HasLowerSecurity (target,target_guid,true)) + return false; + + if (target) + { + if (target->CanSpeak()) + { + SendSysMessage(LANG_CHAT_ALREADY_ENABLED); + SetSentErrorMessage(true); + return false; + } + + target->GetSession()->m_muteTime = 0; + } + + LoginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id); + + if (target) + ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_ENABLED); + + std::string nameLink = playerLink(target_name); + + PSendSysMessage(LANG_YOU_ENABLE_CHAT, nameLink.c_str()); + return true; +} + +bool ChatHandler::HandleGoTicketCommand(const char * args) +{ + if (!*args) + return false; + + char *cstrticket_id = strtok((char*)args, " "); + + if (!cstrticket_id) + return false; + + uint64 ticket_id = atoi(cstrticket_id); + if (!ticket_id) + return false; + + GM_Ticket *ticket = objmgr.GetGMTicket(ticket_id); + if (!ticket) + { + SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + float x, y, z; + int mapid; + + x = ticket->pos_x; + y = ticket->pos_y; + z = ticket->pos_z; + mapid = ticket->map; + + Player* _player = m_session->GetPlayer(); + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + else + _player->SaveRecallPosition(); + + _player->TeleportTo(mapid, x, y, z, 1, 0); + return true; +} + +bool ChatHandler::HandleGoTriggerCommand(const char* args) +{ + Player* _player = m_session->GetPlayer(); + + if (!*args) + return false; + + char *atId = strtok((char*)args, " "); + if (!atId) + return false; + + int32 i_atId = atoi(atId); + + if (!i_atId) + return false; + + AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(i_atId); + if (!at) + { + PSendSysMessage(LANG_COMMAND_GOAREATRNOTFOUND,i_atId); + SetSentErrorMessage(true); + return false; + } + + if (!MapManager::IsValidMapCoord(at->mapid,at->x,at->y,at->z)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,at->x,at->y,at->mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(at->mapid, at->x, at->y, at->z, _player->GetOrientation()); + return true; +} + +bool ChatHandler::HandleGoGraveyardCommand(const char* args) +{ + Player* _player = m_session->GetPlayer(); + + if (!*args) + return false; + + char *gyId = strtok((char*)args, " "); + if (!gyId) + return false; + + int32 i_gyId = atoi(gyId); + + if (!i_gyId) + return false; + + WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(i_gyId); + if (!gy) + { + PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST,i_gyId); + SetSentErrorMessage(true); + return false; + } + + if (!MapManager::IsValidMapCoord(gy->map_id,gy->x,gy->y,gy->z)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,gy->x,gy->y,gy->map_id); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(gy->map_id, gy->x, gy->y, gy->z, _player->GetOrientation()); + return true; +} + +/** \brief Teleport the GM to the specified creature +* +* .gocreature --> TP using creature.guid +* .gocreature azuregos --> TP player to the mob with this name +* Warning: If there is more than one mob with this name +* you will be teleported to the first one that is found. +* .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry +* Warning: If there is more than one mob with this "id" +* you will be teleported to the first one that is found. +*/ +//teleport to creature +bool ChatHandler::HandleGoCreatureCommand(const char* args) +{ + if (!*args) + return false; + Player* _player = m_session->GetPlayer(); + + // "id" or number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r + char* pParam1 = extractKeyFromLink((char*)args,"Hcreature"); + if (!pParam1) + return false; + + std::ostringstream whereClause; + + // User wants to teleport to the NPC's template entry + if (strcmp(pParam1, "id") == 0) + { + //sLog.outError("DEBUG: ID found"); + + // Get the "creature_template.entry" + // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r + char* tail = strtok(NULL,""); + if (!tail) + return false; + char* cId = extractKeyFromLink(tail,"Hcreature_entry"); + if (!cId) + return false; + + int32 tEntry = atoi(cId); + //sLog.outError("DEBUG: ID value: %d", tEntry); + if (!tEntry) + return false; + + whereClause << "WHERE id = '" << tEntry << "'"; + } + else + { + //sLog.outError("DEBUG: ID *not found*"); + + int32 guid = atoi(pParam1); + + // Number is invalid - maybe the user specified the mob's name + if (!guid) + { + std::string name = pParam1; + WorldDatabase.escape_string(name); + whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name "_LIKE_" '" << name << "'"; + } + else + { + whereClause << "WHERE guid = '" << guid << "'"; + } + } + //sLog.outError("DEBUG: %s", whereClause.c_str()); + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM creature %s", whereClause.str().c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND); + SetSentErrorMessage(true); + return false; + } + if (result->GetRowCount() > 1) + SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE); + + Field *fields = result->Fetch(); + float x = fields[0].GetFloat(); + float y = fields[1].GetFloat(); + float z = fields[2].GetFloat(); + float ort = fields[3].GetFloat(); + int mapid = fields[4].GetUInt16(); + + if (!MapManager::IsValidMapCoord(mapid,x,y,z,ort)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(mapid, x, y, z, ort); + return true; +} + +//teleport to gameobject +bool ChatHandler::HandleGoObjectCommand(const char* args) +{ + if (!*args) + return false; + + Player* _player = m_session->GetPlayer(); + + // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if (!cId) + return false; + + int32 guid = atoi(cId); + if (!guid) + return false; + + float x, y, z, ort; + int mapid; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(guid)) + { + x = go_data->posX; + y = go_data->posY; + z = go_data->posZ; + ort = go_data->orientation; + mapid = go_data->mapid; + } + else + { + SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND); + SetSentErrorMessage(true); + return false; + } + + if (!MapManager::IsValidMapCoord(mapid,x,y,z,ort)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + SetSentErrorMessage(true); + return false; + } + + // stop flight if need + if (_player->isInFlight()) + { + _player->GetMotionMaster()->MovementExpired(); + _player->CleanupAfterTaxiFlight(); + } + // save only in non-flight case + else + _player->SaveRecallPosition(); + + _player->TeleportTo(mapid, x, y, z, ort); + return true; +} + +bool ChatHandler::HandleGameObjectTargetCommand(const char* args) +{ + Player* pl = m_session->GetPlayer(); + QueryResult_AutoPtr result; + GameEventMgr::ActiveEvents const& activeEventsList = gameeventmgr.GetActiveEventList(); + if (*args) + { + // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry"); + if (!cId) + return false; + + uint32 id = atol(cId); + + if (id) + result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, phaseMask, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND id = '%u' ORDER BY order_ ASC LIMIT 1", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),id); + else + { + std::string name = cId; + WorldDatabase.escape_string(name); + result = WorldDatabase.PQuery( + "SELECT guid, id, position_x, position_y, position_z, orientation, map, phaseMask, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " + "FROM gameobject,gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" ORDER BY order_ ASC LIMIT 1", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),name.c_str()); + } + } + else + { + std::ostringstream eventFilter; + eventFilter << " AND (event IS NULL "; + bool initString = true; + + for (GameEventMgr::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr) + { + if (initString) + { + eventFilter << "OR event IN (" <<*itr; + initString =false; + } + else + eventFilter << "," << *itr; + } + + if (!initString) + eventFilter << "))"; + else + eventFilter << ")"; + + result = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, phaseMask, " + "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject " + "LEFT OUTER JOIN game_event_gameobject on gameobject.guid=game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 10", + m_session->GetPlayer()->GetPositionX(), m_session->GetPlayer()->GetPositionY(), m_session->GetPlayer()->GetPositionZ(), m_session->GetPlayer()->GetMapId(),eventFilter.str().c_str()); + } + + if (!result) + { + SendSysMessage(LANG_COMMAND_TARGETOBJNOTFOUND); + return true; + } + + bool found = false; + float x, y, z, o; + uint32 lowguid, id; + uint16 mapid, pool_id, phase; + + do + { + Field *fields = result->Fetch(); + lowguid = fields[0].GetUInt32(); + id = fields[1].GetUInt32(); + x = fields[2].GetFloat(); + y = fields[3].GetFloat(); + z = fields[4].GetFloat(); + o = fields[5].GetFloat(); + mapid = fields[6].GetUInt16(); + phase = fields[7].GetUInt16(); + pool_id = poolhandler.IsPartOfAPool(lowguid); + if (!pool_id || poolhandler.IsSpawnedObject(lowguid)) + found = true; + } while (result->NextRow() && (!found)); + + if (!found) + { + PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); + return false; + } + + GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id); + + if (!goI) + { + PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); + return false; + } + + GameObject* target = m_session->GetPlayer()->GetMap()->GetGameObject(MAKE_NEW_GUID(lowguid,id,HIGHGUID_GAMEOBJECT)); + + PSendSysMessage(LANG_GAMEOBJECT_DETAIL, lowguid, goI->name, lowguid, id, x, y, z, mapid, o, phase); + + if (target) + { + int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL); + if (curRespawnDelay < 0) + curRespawnDelay = 0; + + std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true); + std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true); + + PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str()); + } + return true; +} + +//delete object by selection or guid +bool ChatHandler::HandleGameObjectDeleteCommand(const char* args) +{ + // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + GameObject* obj = NULL; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(lowguid)) + obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id); + + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + uint64 owner_guid = obj->GetOwnerGUID(); + if (owner_guid) + { + Unit* owner = ObjectAccessor::GetUnit(*m_session->GetPlayer(),owner_guid); + if (!owner || !IS_PLAYER_GUID(owner_guid)) + { + PSendSysMessage(LANG_COMMAND_DELOBJREFERCREATURE, GUID_LOPART(owner_guid), obj->GetGUIDLow()); + SetSentErrorMessage(true); + return false; + } + + owner->RemoveGameObject(obj,false); + } + + obj->SetRespawnTime(0); // not save respawn time + obj->Delete(); + obj->DeleteFromDB(); + + PSendSysMessage(LANG_COMMAND_DELOBJMESSAGE, obj->GetGUIDLow()); + + return true; +} + +//turn selected object +bool ChatHandler::HandleGameObjectTurnCommand(const char* args) +{ + // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + GameObject* obj = NULL; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(lowguid)) + obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id); + + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + char* po = strtok(NULL, " "); + float o; + + if (po) + { + o = (float)atof(po); + } + else + { + Player *chr = m_session->GetPlayer(); + o = chr->GetOrientation(); + } + + obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o); + obj->UpdateRotationFields(); + obj->DestroyForNearbyPlayers(); + obj->UpdateObjectVisibility(); + + obj->SaveToDB(); + obj->Refresh(); + + PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), obj->GetGOInfo()->name, obj->GetGUIDLow(), o); + + return true; +} + +//move selected object +bool ChatHandler::HandleGameObjectMoveCommand(const char* args) +{ + // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + GameObject* obj = NULL; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(lowguid)) + obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id); + + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + char* px = strtok(NULL, " "); + char* py = strtok(NULL, " "); + char* pz = strtok(NULL, " "); + + if (!px) + { + Player *chr = m_session->GetPlayer(); + obj->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), obj->GetOrientation()); + obj->DestroyForNearbyPlayers(); + obj->UpdateObjectVisibility(); + } + else + { + if (!py || !pz) + return false; + + float x = (float)atof(px); + float y = (float)atof(py); + float z = (float)atof(pz); + + if (!MapManager::IsValidMapCoord(obj->GetMapId(),x,y,z)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,obj->GetMapId()); + SetSentErrorMessage(true); + return false; + } + + obj->Relocate(x, y, z, obj->GetOrientation()); + obj->DestroyForNearbyPlayers(); + obj->UpdateObjectVisibility(); + } + + obj->SaveToDB(); + obj->Refresh(); + + PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow(), obj->GetGOInfo()->name, obj->GetGUIDLow()); + + return true; +} + +//spawn go +bool ChatHandler::HandleGameObjectAddCommand(const char* args) +{ + if (!*args) + return false; + + // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry"); + if (!cId) + return false; + + uint32 id = atol(cId); + if (!id) + return false; + + char* spawntimeSecs = strtok(NULL, " "); + + const GameObjectInfo *gInfo = objmgr.GetGameObjectInfo(id); + + if (!gInfo) + { + PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id); + SetSentErrorMessage(true); + return false; + } + + if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId)) + { + // report to DB errors log as in loading case + sLog.outErrorDb("Gameobject (Entry %u GoType: %u) have invalid displayId (%u), not spawned.",id, gInfo->type, gInfo->displayId); + PSendSysMessage(LANG_GAMEOBJECT_HAVE_INVALID_DATA,id); + SetSentErrorMessage(true); + return false; + } + + Player *chr = m_session->GetPlayer(); + float x = float(chr->GetPositionX()); + float y = float(chr->GetPositionY()); + float z = float(chr->GetPositionZ()); + float o = float(chr->GetOrientation()); + Map *map = chr->GetMap(); + + GameObject* pGameObj = new GameObject; + uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); + + if (!pGameObj->Create(db_lowGUID, gInfo->id, map, chr->GetPhaseMaskForSpawn(), x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY)) + { + delete pGameObj; + return false; + } + + if (spawntimeSecs) + { + uint32 value = atoi((char*)spawntimeSecs); + pGameObj->SetRespawnTime(value); + //sLog.outDebug("*** spawntimeSecs: %d", value); + } + + // fill the gameobject data and save to the db + pGameObj->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()),chr->GetPhaseMaskForSpawn()); + + // this will generate a new guid if the object is in an instance + if (!pGameObj->LoadFromDB(db_lowGUID, map)) + { + delete pGameObj; + return false; + } + + sLog.outDebug(GetTrinityString(LANG_GAMEOBJECT_CURRENT), gInfo->name, db_lowGUID, x, y, z, o); + + map->Add(pGameObj); + + // TODO: is it really necessary to add both the real and DB table guid here ? + objmgr.AddGameobjectToGrid(db_lowGUID, objmgr.GetGOData(db_lowGUID)); + + PSendSysMessage(LANG_GAMEOBJECT_ADD,id,gInfo->name,db_lowGUID,x,y,z); + return true; +} + +//set pahsemask for selected object +bool ChatHandler::HandleGameObjectPhaseCommand(const char* args) +{ + // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + GameObject* obj = NULL; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(lowguid)) + obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id); + + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + char* phaseStr = strtok (NULL, " "); + uint32 phasemask = phaseStr? atoi(phaseStr) : 0; + if (phasemask == 0) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + obj->SetPhaseMask(phasemask,true); + obj->SaveToDB(); + return true; +} + +bool ChatHandler::HandleGameObjectNearCommand(const char* args) +{ + float distance = (!*args) ? 10 : atol(args); + uint32 count = 0; + + Player* pl = m_session->GetPlayer(); + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, " + "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ " + "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), + pl->GetMapId(),pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),distance*distance); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + uint32 entry = fields[1].GetUInt32(); + float x = fields[2].GetFloat(); + float y = fields[3].GetFloat(); + float z = fields[4].GetFloat(); + int mapid = fields[5].GetUInt16(); + + GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(entry); + + if (!gInfo) + continue; + + PSendSysMessage(LANG_GO_LIST_CHAT, guid, guid, gInfo->name, x, y, z, mapid); + + ++count; + } while (result->NextRow()); + } + + PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count); + return true; +} + +bool ChatHandler::HandleGUIDCommand(const char* /*args*/) +{ + uint64 guid = m_session->GetPlayer()->GetSelection(); + + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_OBJECT_GUID, GUID_LOPART(guid), GUID_HIPART(guid)); + return true; +} + +bool ChatHandler::HandleModifyRepCommand(const char * args) +{ + if (!*args) return false; + + Player* target = NULL; + target = getSelectedPlayer(); + + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + char* factionTxt = extractKeyFromLink((char*)args,"Hfaction"); + if (!factionTxt) + return false; + + uint32 factionId = atoi(factionTxt); + + int32 amount = 0; + char *rankTxt = strtok(NULL, " "); + if (!factionTxt || !rankTxt) + return false; + + amount = atoi(rankTxt); + if ((amount == 0) && (rankTxt[0] != '-') && !isdigit(rankTxt[0])) + { + std::string rankStr = rankTxt; + std::wstring wrankStr; + if (!Utf8toWStr(rankStr,wrankStr)) + return false; + wstrToLower(wrankStr); + + int r = 0; + amount = -42000; + for (; r < MAX_REPUTATION_RANK; ++r) + { + std::string rank = GetTrinityString(ReputationRankStrIndex[r]); + if (rank.empty()) + continue; + + std::wstring wrank; + if (!Utf8toWStr(rank,wrank)) + continue; + + wstrToLower(wrank); + + if (wrank.substr(0,wrankStr.size()) == wrankStr) + { + char *deltaTxt = strtok(NULL, " "); + if (deltaTxt) + { + int32 delta = atoi(deltaTxt); + if ((delta < 0) || (delta > ReputationMgr::PointsInRank[r] -1)) + { + PSendSysMessage(LANG_COMMAND_FACTION_DELTA, (ReputationMgr::PointsInRank[r]-1)); + SetSentErrorMessage(true); + return false; + } + amount += delta; + } + break; + } + amount += ReputationMgr::PointsInRank[r]; + } + if (r >= MAX_REPUTATION_RANK) + { + PSendSysMessage(LANG_COMMAND_FACTION_INVPARAM, rankTxt); + SetSentErrorMessage(true); + return false; + } + } + + FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId); + + if (!factionEntry) + { + PSendSysMessage(LANG_COMMAND_FACTION_UNKNOWN, factionId); + SetSentErrorMessage(true); + return false; + } + + if (factionEntry->reputationListID < 0) + { + PSendSysMessage(LANG_COMMAND_FACTION_NOREP_ERROR, factionEntry->name[GetSessionDbcLocale()], factionId); + SetSentErrorMessage(true); + return false; + } + + target->GetReputationMgr().SetReputation(factionEntry,amount); + PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[GetSessionDbcLocale()], factionId, + GetNameLink(target).c_str(), target->GetReputationMgr().GetReputation(factionEntry)); + return true; +} + +//-----------------------Npc Commands----------------------- +//add spawn of creature +bool ChatHandler::HandleNpcAddCommand(const char* args) +{ + if (!*args) + return false; + char* charID = extractKeyFromLink((char*)args,"Hcreature_entry"); + if (!charID) + return false; + + char* team = strtok(NULL, " "); + int32 teamval = 0; + if (team) { teamval = atoi(team); } + if (teamval < 0) { teamval = 0; } + + uint32 id = atoi(charID); + + Player *chr = m_session->GetPlayer(); + float x = chr->GetPositionX(); + float y = chr->GetPositionY(); + float z = chr->GetPositionZ(); + float o = chr->GetOrientation(); + Map *map = chr->GetMap(); + + Creature* pCreature = new Creature; + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, (uint32)teamval, x, y, z, o)) + { + delete pCreature; + return false; + } + + pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + + uint32 db_guid = pCreature->GetDBTableGUIDLow(); + + // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); + pCreature->LoadFromDB(db_guid, map); + + map->Add(pCreature); + objmgr.AddCreatureToGrid(db_guid, objmgr.GetCreatureData(db_guid)); + return true; +} + +//add item in vendorlist +bool ChatHandler::HandleNpcAddVendorItemCommand(const char* args) +{ + if (!*args) + return false; + + char* pitem = extractKeyFromLink((char*)args,"Hitem"); + if (!pitem) + { + SendSysMessage(LANG_COMMAND_NEEDITEMSEND); + SetSentErrorMessage(true); + return false; + } + + uint32 itemId = atol(pitem); + + char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0 + uint32 maxcount = 0; + if (fmaxcount) + maxcount = atol(fmaxcount); + + char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0 + uint32 incrtime = 0; + if (fincrtime) + incrtime = atol(fincrtime); + + char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0 + uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0; + + Creature* vendor = getSelectedCreature(); + + uint32 vendor_entry = vendor ? vendor->GetEntry() : 0; + + if (!objmgr.IsVendorItemValid(vendor_entry,itemId,maxcount,incrtime,extendedcost,m_session->GetPlayer())) + { + SetSentErrorMessage(true); + return false; + } + + objmgr.AddVendorItem(vendor_entry,itemId,maxcount,incrtime,extendedcost); + + ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId); + + PSendSysMessage(LANG_ITEM_ADDED_TO_LIST,itemId,pProto->Name1,maxcount,incrtime,extendedcost); + return true; +} + +//del item from vendor list +bool ChatHandler::HandleNpcDelVendorItemCommand(const char* args) +{ + if (!*args) + return false; + + Creature* vendor = getSelectedCreature(); + if (!vendor || !vendor->isVendor()) + { + SendSysMessage(LANG_COMMAND_VENDORSELECTION); + SetSentErrorMessage(true); + return false; + } + + char* pitem = extractKeyFromLink((char*)args,"Hitem"); + if (!pitem) + { + SendSysMessage(LANG_COMMAND_NEEDITEMSEND); + SetSentErrorMessage(true); + return false; + } + uint32 itemId = atol(pitem); + + if (!objmgr.RemoveVendorItem(vendor->GetEntry(),itemId)) + { + PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId); + SetSentErrorMessage(true); + return false; + } + + ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId); + + PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1); + return true; +} + +//add move for creature +bool ChatHandler::HandleNpcAddMoveCommand(const char* args) +{ + if (!*args) + return false; + + char* guid_str = strtok((char*)args, " "); + char* wait_str = strtok((char*)NULL, " "); + + uint32 lowguid = atoi((char*)guid_str); + + Creature* pCreature = NULL; + + /* FIXME: impossible without entry + if (lowguid) + pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT)); + */ + + // attempt check creature existence by DB data + if (!pCreature) + { + CreatureData const* data = objmgr.GetCreatureData(lowguid); + if (!data) + { + PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + } + else + { + // obtain real GUID for DB operations + lowguid = pCreature->GetDBTableGUIDLow(); + } + + int wait = wait_str ? atoi(wait_str) : 0; + + if (wait < 0) + wait = 0; + + //Player* player = m_session->GetPlayer(); + + //WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0); + + // update movement type + WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid); + if (pCreature && pCreature->GetWaypointPath()) + { + pCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE); + pCreature->GetMotionMaster()->Initialize(); + if (pCreature->isAlive()) // dead creature will reset movement generator at respawn + { + pCreature->setDeathState(JUST_DIED); + pCreature->Respawn(true); + } + pCreature->SaveToDB(); + } + + SendSysMessage(LANG_WAYPOINT_ADDED); + + return true; +} + +//change level of creature or pet +bool ChatHandler::HandleNpcChangeLevelCommand(const char* args) +{ + if (!*args) + return false; + + uint8 lvl = (uint8) atoi((char*)args); + if (lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Creature* pCreature = getSelectedCreature(); + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (pCreature->isPet()) + { + if (((Pet*)pCreature)->getPetType() == HUNTER_PET) + { + pCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(lvl)/4); + pCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); + } + ((Pet*)pCreature)->GivePetLevel(lvl); + } + else + { + pCreature->SetMaxHealth(100 + 30*lvl); + pCreature->SetHealth(100 + 30*lvl); + pCreature->SetLevel(lvl); + pCreature->SaveToDB(); + } + + return true; +} + +//set npcflag of creature +bool ChatHandler::HandleNpcFlagCommand(const char* args) +{ + if (!*args) + return false; + + uint32 npcFlags = (uint32) atoi((char*)args); + + Creature* pCreature = getSelectedCreature(); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags); + + WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry()); + + SendSysMessage(LANG_VALUE_SAVED_REJOIN); + + return true; +} + +bool ChatHandler::HandleNpcDeleteCommand(const char* args) +{ + Creature* unit = NULL; + + if (*args) + { + // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hcreature"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + if (CreatureData const* cr_data = objmgr.GetCreatureData(lowguid)) + unit = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT)); + } + else + unit = getSelectedCreature(); + + if (!unit || unit->isPet() || unit->isTotem()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // Delete the creature + unit->CombatStop(); + unit->DeleteFromDB(); + unit->AddObjectToRemoveList(); + + SendSysMessage(LANG_COMMAND_DELCREATMESSAGE); + + return true; +} + +//move selected creature +bool ChatHandler::HandleNpcMoveCommand(const char* args) +{ + uint32 lowguid = 0; + + Creature* pCreature = getSelectedCreature(); + + if (!pCreature) + { + // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hcreature"); + if (!cId) + return false; + + lowguid = atoi(cId); + + /* FIXME: impossibel without entry + if (lowguid) + pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT)); + */ + + // Attempting creature load from DB data + if (!pCreature) + { + CreatureData const* data = objmgr.GetCreatureData(lowguid); + if (!data) + { + PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + uint32 map_id = data->mapid; + + if (m_session->GetPlayer()->GetMapId() != map_id) + { + PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); + SetSentErrorMessage(true); + return false; + } + } + else + { + lowguid = pCreature->GetDBTableGUIDLow(); + } + } + else + { + lowguid = pCreature->GetDBTableGUIDLow(); + } + + float x = m_session->GetPlayer()->GetPositionX(); + float y = m_session->GetPlayer()->GetPositionY(); + float z = m_session->GetPlayer()->GetPositionZ(); + float o = m_session->GetPlayer()->GetOrientation(); + + if (pCreature) + { + if (CreatureData const* data = objmgr.GetCreatureData(pCreature->GetDBTableGUIDLow())) + { + const_cast(data)->posX = x; + const_cast(data)->posY = y; + const_cast(data)->posZ = z; + const_cast(data)->orientation = o; + } + pCreature->GetMap()->CreatureRelocation(pCreature,x, y, z,o); + pCreature->GetMotionMaster()->Initialize(); + if (pCreature->isAlive()) // dead creature will reset movement generator at respawn + { + pCreature->setDeathState(JUST_DIED); + pCreature->Respawn(); + } + } + + WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid); + PSendSysMessage(LANG_COMMAND_CREATUREMOVED); + return true; +} + +/**HandleNpcSetMoveTypeCommand + * Set the movement type for an NPC.
+ *
+ * Valid movement types are: + *
    + *
  • stay - NPC wont move
  • + *
  • random - NPC will move randomly according to the spawndist
  • + *
  • way - NPC will move with given waypoints set
  • + *
+ * additional parameter: NODEL - so no waypoints are deleted, if you + * change the movement type + */ +bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args) +{ + if (!*args) + return false; + + // 3 arguments: + // GUID (optional - you can also select the creature) + // stay|random|way (determines the kind of movement) + // NODEL (optional - tells the system NOT to delete any waypoints) + // this is very handy if you want to do waypoints, that are + // 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, " "); + + bool doNotDelete = false; + + if (!guid_str) + return false; + + uint32 lowguid = 0; + Creature* pCreature = NULL; + + if (dontdel_str) + { + //sLog.outError("DEBUG: All 3 params are set"); + + // All 3 params are set + // GUID + // type + // doNotDEL + if (stricmp(dontdel_str, "NODEL") == 0) + { + //sLog.outError("DEBUG: doNotDelete = true;"); + doNotDelete = true; + } + } + else + { + // Only 2 params - but maybe NODEL is set + if (type_str) + { + sLog.outError("DEBUG: Only 2 params "); + if (stricmp(type_str, "NODEL") == 0) + { + //sLog.outError("DEBUG: type_str, NODEL "); + doNotDelete = true; + type_str = NULL; + } + } + } + + if (!type_str) // case .setmovetype $move_type (with selected creature) + { + type_str = guid_str; + pCreature = getSelectedCreature(); + if (!pCreature || pCreature->isPet()) + return false; + lowguid = pCreature->GetDBTableGUIDLow(); + } + else // case .setmovetype #creature_guid $move_type (with selected creature) + { + lowguid = atoi((char*)guid_str); + + /* impossible without entry + if (lowguid) + pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT)); + */ + + // attempt check creature existence by DB data + if (!pCreature) + { + CreatureData const* data = objmgr.GetCreatureData(lowguid); + if (!data) + { + PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + } + else + { + lowguid = pCreature->GetDBTableGUIDLow(); + } + } + + // now lowguid is low guid really existed creature + // and pCreature point (maybe) to this creature or NULL + + MovementGeneratorType move_type; + + std::string type = type_str; + + if (type == "stay") + move_type = IDLE_MOTION_TYPE; + else if (type == "random") + move_type = RANDOM_MOTION_TYPE; + else if (type == "way") + move_type = WAYPOINT_MOTION_TYPE; + else + return false; + + // update movement type + //if (doNotDelete == false) + // WaypointMgr.DeletePath(lowguid); + + if (pCreature) + { + // update movement type + if (doNotDelete == false) + pCreature->LoadPath(0); + + pCreature->SetDefaultMovementType(move_type); + pCreature->GetMotionMaster()->Initialize(); + if (pCreature->isAlive()) // dead creature will reset movement generator at respawn + { + pCreature->setDeathState(JUST_DIED); + pCreature->Respawn(); + } + pCreature->SaveToDB(); + } + if (doNotDelete == false) + { + PSendSysMessage(LANG_MOVE_TYPE_SET,type_str); + } + else + { + PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL,type_str); + } + + return true; +} + +//set model of creature +bool ChatHandler::HandleNpcSetModelCommand(const char* args) +{ + if (!*args) + return false; + + uint32 displayId = (uint32) atoi((char*)args); + + Creature *pCreature = getSelectedCreature(); + + if (!pCreature || pCreature->isPet()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->SetDisplayId(displayId); + pCreature->SetNativeDisplayId(displayId); + + pCreature->SaveToDB(); + + return true; +} +//set faction of creature +bool ChatHandler::HandleNpcFactionIdCommand(const char* args) +{ + if (!*args) + return false; + + uint32 factionId = (uint32) atoi((char*)args); + + if (!sFactionTemplateStore.LookupEntry(factionId)) + { + PSendSysMessage(LANG_WRONG_FACTION, factionId); + SetSentErrorMessage(true); + return false; + } + + Creature* pCreature = getSelectedCreature(); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->setFaction(factionId); + + // faction is set in creature_template - not inside creature + + // update in memory + if (CreatureInfo const *cinfo = pCreature->GetCreatureInfo()) + { + const_cast(cinfo)->faction_A = factionId; + const_cast(cinfo)->faction_H = factionId; + } + + // and DB + WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry()); + + return true; +} +//set spawn dist of creature +bool ChatHandler::HandleNpcSpawnDistCommand(const char* args) +{ + if (!*args) + return false; + + float option = atof((char*)args); + if (option < 0.0f) + { + SendSysMessage(LANG_BAD_VALUE); + return false; + } + + MovementGeneratorType mtype = IDLE_MOTION_TYPE; + if (option >0.0f) + mtype = RANDOM_MOTION_TYPE; + + Creature *pCreature = getSelectedCreature(); + uint32 u_guidlow = 0; + + if (pCreature) + u_guidlow = pCreature->GetDBTableGUIDLow(); + else + return false; + + pCreature->SetRespawnRadius((float)option); + pCreature->SetDefaultMovementType(mtype); + pCreature->GetMotionMaster()->Initialize(); + if (pCreature->isAlive()) // dead creature will reset movement generator at respawn + { + pCreature->setDeathState(JUST_DIED); + pCreature->Respawn(); + } + + WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow); + PSendSysMessage(LANG_COMMAND_SPAWNDIST,option); + return true; +} +//spawn time handling +bool ChatHandler::HandleNpcSpawnTimeCommand(const char* args) +{ + if (!*args) + return false; + + char* stime = strtok((char*)args, " "); + + if (!stime) + return false; + + int i_stime = atoi((char*)stime); + + if (i_stime < 0) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Creature *pCreature = getSelectedCreature(); + uint32 u_guidlow = 0; + + if (pCreature) + u_guidlow = pCreature->GetDBTableGUIDLow(); + else + return false; + + WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow); + pCreature->SetRespawnDelay((uint32)i_stime); + PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime); + + return true; +} +//npc follow handling +bool ChatHandler::HandleNpcFollowCommand(const char* /*args*/) +{ + Player *player = m_session->GetPlayer(); + Creature *creature = getSelectedCreature(); + + if (!creature) + { + PSendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // Follow player - Using pet's default dist and angle + creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, creature->GetFollowAngle()); + + PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName()); + return true; +} +//npc unfollow handling +bool ChatHandler::HandleNpcUnFollowCommand(const char* /*args*/) +{ + Player *player = m_session->GetPlayer(); + Creature *creature = getSelectedCreature(); + + if (!creature) + { + PSendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (/*creature->GetMotionMaster()->empty() ||*/ + creature->GetMotionMaster()->GetCurrentMovementGeneratorType () != TARGETED_MOTION_TYPE) + { + PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU); + SetSentErrorMessage(true); + return false; + } + + TargetedMovementGenerator const* mgen + = static_cast const*>((creature->GetMotionMaster()->top())); + + if (mgen->GetTarget() != player) + { + PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU); + SetSentErrorMessage(true); + return false; + } + + // reset movement + creature->GetMotionMaster()->MovementExpired(true); + + PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW, creature->GetName()); + return true; +} +//npc tame handling +bool ChatHandler::HandleNpcTameCommand(const char* /*args*/) +{ + Creature *creatureTarget = getSelectedCreature (); + if (!creatureTarget || creatureTarget->isPet ()) + { + PSendSysMessage (LANG_SELECT_CREATURE); + SetSentErrorMessage (true); + return false; + } + + Player *player = m_session->GetPlayer (); + + if (player->GetPetGUID ()) + { + SendSysMessage (LANG_YOU_ALREADY_HAVE_PET); + SetSentErrorMessage (true); + return false; + } + + CreatureInfo const* cInfo = creatureTarget->GetCreatureInfo(); + + if (!cInfo->isTameable (player->CanTameExoticPets())) + { + PSendSysMessage (LANG_CREATURE_NON_TAMEABLE,cInfo->Entry); + SetSentErrorMessage (true); + return false; + } + + // Everything looks OK, create new pet + Pet* pet = player->CreateTamedPetFrom (creatureTarget); + if (!pet) + { + PSendSysMessage (LANG_CREATURE_NON_TAMEABLE,cInfo->Entry); + SetSentErrorMessage (true); + return false; + } + + // place pet before player + float x,y,z; + player->GetClosePoint (x,y,z,creatureTarget->GetObjectSize (),CONTACT_DISTANCE); + pet->Relocate (x,y,z,M_PI-player->GetOrientation ()); + + // set pet to defensive mode by default (some classes can't control controlled pets in fact). + pet->SetReactState(REACT_DEFENSIVE); + + // calculate proper level + uint8 level = (creatureTarget->getLevel() < (player->getLevel() - 5)) ? (player->getLevel() - 5) : creatureTarget->getLevel(); + + // prepare visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); + + // add to world + pet->GetMap()->Add(pet->ToCreature()); + + // visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); + + // caster have pet now + player->SetMinion(pet, true); + + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + player->PetSpellInitialize(); + + return true; +} +//npc phasemask handling +//change phasemask of creature or pet +bool ChatHandler::HandleNpcSetPhaseCommand(const char* args) +{ + if (!*args) + return false; + + uint32 phasemask = (uint32) atoi((char*)args); + if (phasemask == 0) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + Creature* pCreature = getSelectedCreature(); + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pCreature->SetPhaseMask(phasemask,true); + + if (!pCreature->isPet()) + pCreature->SaveToDB(); + + return true; +} +//npc deathstate handling +bool ChatHandler::HandleNpcSetDeathStateCommand(const char* args) +{ + if (!*args) + return false; + + Creature* pCreature = getSelectedCreature(); + if (!pCreature || pCreature->isPet()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (strncmp(args, "on", 3) == 0) + pCreature->SetDeadByDefault(true); + else if (strncmp(args, "off", 4) == 0) + pCreature->SetDeadByDefault(false); + else + { + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; + } + + pCreature->SaveToDB(); + pCreature->Respawn(); + + return true; +} + +//TODO: NpcCommands that need to be fixed : + +bool ChatHandler::HandleNpcNameCommand(const char* /*args*/) +{ + /* Temp. disabled + if (!*args) + return false; + + if (strlen((char*)args)>75) + { + PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75); + return true; + } + + for (uint8 i = 0; i < strlen(args); ++i) + { + if (!isalpha(args[i]) && args[i] != ' ') + { + SendSysMessage(LANG_CHARS_ONLY); + return false; + } + } + + uint64 guid; + guid = m_session->GetPlayer()->GetSelection(); + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + return true; + } + + Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + return true; + } + + pCreature->SetName(args); + uint32 idname = objmgr.AddCreatureTemplate(pCreature->GetName()); + pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname); + + pCreature->SaveToDB(); + */ + + return true; +} + +bool ChatHandler::HandleNpcSubNameCommand(const char* /*args*/) +{ + /* Temp. disabled + + if (!*args) + args = ""; + + if (strlen((char*)args)>75) + { + + PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75); + return true; + } + + for (uint8 i = 0; i < strlen(args); i++) + { + if (!isalpha(args[i]) && args[i] != ' ') + { + SendSysMessage(LANG_CHARS_ONLY); + return false; + } + } + uint64 guid; + guid = m_session->GetPlayer()->GetSelection(); + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + return true; + } + + Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + return true; + } + + uint32 idname = objmgr.AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID)); + pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname); + + pCreature->SaveToDB(); + */ + return true; +} + +//move item to other slot +bool ChatHandler::HandleItemMoveCommand(const char* args) +{ + if (!*args) + return false; + uint8 srcslot, dstslot; + + char* pParam1 = strtok((char*)args, " "); + if (!pParam1) + return false; + + char* pParam2 = strtok(NULL, " "); + if (!pParam2) + return false; + + srcslot = (uint8)atoi(pParam1); + dstslot = (uint8)atoi(pParam2); + + if (srcslot == dstslot) + return true; + + if (!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot)) + return false; + + if (!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot)) + return false; + + uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot); + uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot); + + m_session->GetPlayer()->SwapItem(src, dst); + + return true; +} + +//demorph player or unit +bool ChatHandler::HandleDeMorphCommand(const char* /*args*/) +{ + Unit *target = getSelectedUnit(); + if (!target) + target = m_session->GetPlayer(); + + // check online security + else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + target->DeMorph(); + + return true; +} + +//morph creature or player +bool ChatHandler::HandleModifyMorphCommand(const char* args) +{ + if (!*args) + return false; + + uint16 display_id = (uint16)atoi((char*)args); + + Unit *target = getSelectedUnit(); + if (!target) + target = m_session->GetPlayer(); + + // check online security + else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + target->SetDisplayId(display_id); + + return true; +} + +//kick player +bool ChatHandler::HandleKickPlayerCommand(const char *args) +{ +/* const char* kickName = strtok((char*)args, " "); + char* kickReason = strtok(NULL, "\n"); + std::string reason = "No Reason"; + std::string kicker = "Console"; + if (kickReason) + reason = kickReason; + if (m_session) + kicker = m_session->GetPlayer()->GetName(); + + if (!kickName) + { + Player* player = getSelectedPlayer(); + if (!player) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + if (player == m_session->GetPlayer()) + { + SendSysMessage(LANG_COMMAND_KICKSELF); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(player, 0)) + return false; + + if (sWorld.getConfig(CONFIG_SHOW_KICK_IN_WORLD) == 1) + { + sWorld.SendWorldText(LANG_COMMAND_KICKMESSAGE, player->GetName(), kicker.c_str(), reason.c_str()); + } + else + { + PSendSysMessage(LANG_COMMAND_KICKMESSAGE, player->GetName(), kicker.c_str(), reason.c_str()); + } + + player->GetSession()->KickPlayer(); + } + else + { + std::string name = extractPlayerNameFromLink((char*)kickName); + if (name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + if (m_session && name == m_session->GetPlayer()->GetName()) + { + SendSysMessage(LANG_COMMAND_KICKSELF); + SetSentErrorMessage(true); + return false; + } + + Player* player = objmgr.GetPlayer(kickName); + if (!player) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + if (HasLowerSecurity(player, 0)) + { + SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); //maybe replacement string for this later on + SetSentErrorMessage(true); + return false; + } + + std::string nameLink = playerLink(name); + + if (sWorld.KickPlayer(name)) + { + if (sWorld.getConfig(CONFIG_SHOW_KICK_IN_WORLD) == 1) + { + sWorld.SendWorldText(LANG_COMMAND_KICKMESSAGE, nameLink.c_str(), kicker.c_str(), reason.c_str()); + } + else + { + PSendSysMessage(LANG_COMMAND_KICKMESSAGE,nameLink.c_str()); + } + } + else + { + PSendSysMessage(LANG_COMMAND_KICKNOTFOUNDPLAYER,nameLink.c_str()); + return false; + } + }*/ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + if (m_session && target == m_session->GetPlayer()) + { + SendSysMessage(LANG_COMMAND_KICKSELF); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + // send before target pointer invalidate + PSendSysMessage(LANG_COMMAND_KICKMESSAGE,GetNameLink(target).c_str()); + target->GetSession()->KickPlayer(); + return true; +} + +//set temporary phase mask for player +bool ChatHandler::HandleModifyPhaseCommand(const char* args) +{ + if (!*args) + return false; + + uint32 phasemask = (uint32)atoi((char*)args); + + Unit *target = getSelectedUnit(); + if (!target) + target = m_session->GetPlayer(); + + // check online security + else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + target->SetPhaseMask(phasemask,true); + + return true; +} +//show info of gameobject +bool ChatHandler::HandleGOInfoCommand(const char* args) +{ + uint32 entry = 0; + uint32 type = 0; + uint32 displayid = 0; + std::string name; + + if (!*args) + { + if (WorldObject * obj = getSelectedObject()) + entry = obj->GetEntry(); + } + else + entry = atoi((char*)args); + + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry); + + if (!goinfo) + return false; + + type = goinfo->type; + displayid = goinfo->displayId; + name = goinfo->name; + + PSendSysMessage(LANG_GOINFO_ENTRY, entry); + PSendSysMessage(LANG_GOINFO_TYPE, type); + PSendSysMessage(LANG_GOINFO_DISPLAYID, displayid); + PSendSysMessage(LANG_GOINFO_NAME, name.c_str()); + + return true; +} + +//show info of player +bool ChatHandler::HandlePInfoCommand(const char* args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + uint32 accId = 0; + uint32 money = 0; + uint32 total_player_time = 0; + uint8 level = 0; + uint32 latency = 0; + uint8 race; + uint8 Class; + + // get additional information from Player object + if (target) + { + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + accId = target->GetSession()->GetAccountId(); + money = target->GetMoney(); + total_player_time = target->GetTotalPlayedTime(); + level = target->getLevel(); + latency = target->GetSession()->GetLatency(); + race = target->getRace(); + Class = target->getClass(); + } + // get additional information from DB + else + { + // check offline security + if (HasLowerSecurity(NULL, target_guid)) + return false; + + // 0 1 2 3 4 5 + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT totaltime, level, money, account, race, class FROM characters WHERE guid = '%u'", GUID_LOPART(target_guid)); + if (!result) + return false; + + Field *fields = result->Fetch(); + total_player_time = fields[0].GetUInt32(); + level = fields[1].GetUInt32(); + money = fields[2].GetUInt32(); + accId = fields[3].GetUInt32(); + race = fields[4].GetUInt8(); + Class = fields[5].GetUInt8(); + } + + std::string username = GetTrinityString(LANG_ERROR); + std::string email = GetTrinityString(LANG_ERROR); + std::string last_ip = GetTrinityString(LANG_ERROR); + uint32 security = 0; + std::string last_login = GetTrinityString(LANG_ERROR); + + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT a.username,aa.gmlevel,a.email,a.last_ip,a.last_login " + "FROM account a " + "LEFT JOIN account_access aa " + "ON (a.id = aa.id) " + "WHERE a.id = '%u'",accId); + if (result) + { + Field* fields = result->Fetch(); + username = fields[0].GetCppString(); + security = fields[1].GetUInt32(); + email = fields[2].GetCppString(); + + if (email.empty()) + email = "-"; + + if (!m_session || m_session->GetSecurity() >= security) + { + last_ip = fields[3].GetCppString(); + last_login = fields[4].GetCppString(); + } + else + { + last_ip = "-"; + last_login = "-"; + } + } + + std::string nameLink = playerLink(target_name); + + PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetTrinityString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(target_guid), username.c_str(), accId, email.c_str(), security, last_ip.c_str(), last_login.c_str(), latency); + + std::string race_s, Class_s; + switch(race) + { + case RACE_HUMAN: race_s = "Human"; break; + case RACE_ORC: race_s = "Orc"; break; + case RACE_DWARF: race_s = "Dwarf"; break; + case RACE_NIGHTELF: race_s = "Night Elf"; break; + case RACE_UNDEAD_PLAYER: race_s = "Undead"; break; + case RACE_TAUREN: race_s = "Tauren"; break; + case RACE_GNOME: race_s = "Gnome"; break; + case RACE_TROLL: race_s = "Troll"; break; + case RACE_BLOODELF: race_s = "Blood Elf"; break; + case RACE_DRAENEI: race_s = "Draenei"; break; + } + switch(Class) + { + case CLASS_WARRIOR: Class_s = "Warrior"; break; + case CLASS_PALADIN: Class_s = "Paladin"; break; + case CLASS_HUNTER: Class_s = "Hunter"; break; + case CLASS_ROGUE: Class_s = "Rogue"; break; + case CLASS_PRIEST: Class_s = "Priest"; break; + case CLASS_DEATH_KNIGHT: Class_s = "Death Knight"; break; + case CLASS_SHAMAN: Class_s = "Shaman"; break; + case CLASS_MAGE: Class_s = "Mage"; break; + case CLASS_WARLOCK: Class_s = "Warlock"; break; + case CLASS_DRUID: Class_s = "Druid"; break; + } + + std::string timeStr = secsToTimeString(total_player_time,true,true); + uint32 gold = money /GOLD; + uint32 silv = (money % GOLD) / SILVER; + uint32 copp = (money % GOLD) % SILVER; + PSendSysMessage(LANG_PINFO_LEVEL, race_s.c_str(), Class_s.c_str(), timeStr.c_str(), level, gold, silv, copp); + + return true; +} + +/////WAYPOINT COMMANDS + +/** + * Add a waypoint to a creature. + * + * The user can either select an npc or provide its GUID. + * + * The user can even select a visual waypoint - then the new waypoint + * is placed *after* the selected one - this makes insertion of new + * waypoints possible. + * + * eg: + * .wp add 12345 + * -> adds a waypoint to the npc with the GUID 12345 + * + * .wp add + * -> adds a waypoint to the currently selected creature + * + * + * @param args if the user did not provide a GUID, it is NULL + * + * @return true - command did succeed, false - something went wrong + */ +bool ChatHandler::HandleWpAddCommand(const char* args) +{ + sLog.outDebug("DEBUG: HandleWpAddCommand"); + + // optional + char* path_number = NULL; + uint32 pathid = 0; + + if (*args) + path_number = strtok((char*)args, " "); + + uint32 point = 0; + Creature* target = getSelectedCreature(); + + if (!path_number) + { + if (target) + pathid = target->GetWaypointPath(); + else + { + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT MAX(id) FROM waypoint_data"); + uint32 maxpathid = result->Fetch()->GetInt32(); + pathid = maxpathid+1; + sLog.outDebug("DEBUG: HandleWpAddCommand - New path started."); + PSendSysMessage("%s%s|r", "|cff00ff00", "New path started."); + } + } + else + pathid = atoi(path_number); + + // path_id -> ID of the Path + // point -> number of the waypoint (if not 0) + + if (!pathid) + { + sLog.outDebug("DEBUG: HandleWpAddCommand - Current creature haven't loaded path."); + PSendSysMessage("%s%s|r", "|cffff33ff", "Current creature haven't loaded path."); + return true; + } + + sLog.outDebug("DEBUG: HandleWpAddCommand - point == 0"); + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT MAX(point) FROM waypoint_data WHERE id = '%u'",pathid); + + if (result) + point = (*result)[0].GetUInt32(); + + Player* player = m_session->GetPlayer(); + //Map *map = player->GetMap(); + + WorldDatabase.PExecuteLog("INSERT INTO waypoint_data (id, point, position_x, position_y, position_z) VALUES ('%u','%u','%f', '%f', '%f')", + pathid, point+1, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); + + PSendSysMessage("%s%s%u%s%u%s|r", "|cff00ff00", "PathID: |r|cff00ffff", pathid, "|r|cff00ff00: Waypoint |r|cff00ffff", point+1,"|r|cff00ff00 created. "); + return true; +} // HandleWpAddCommand + +bool ChatHandler::HandleWpLoadPathCommand(const char *args) +{ + if (!*args) + return false; + + // optional + char* path_number = NULL; + + if (*args) + path_number = strtok((char*)args, " "); + + uint32 pathid = 0; + uint32 guidlow = 0; + Creature* target = getSelectedCreature(); + + // Did player provide a path_id? + if (!path_number) + sLog.outDebug("DEBUG: HandleWpLoadPathCommand - No path number provided"); + + if (!target) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (target->GetEntry() == 1) + { + PSendSysMessage("%s%s|r", "|cffff33ff", "You want to load path to a waypoint? Aren't you?"); + SetSentErrorMessage(true); + return false; + } + + pathid = atoi(path_number); + + if (!pathid) + { + PSendSysMessage("%s%s|r", "|cffff33ff", "No vallid path number provided."); + return true; + } + + guidlow = target->GetDBTableGUIDLow(); + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid FROM creature_addon WHERE guid = '%u'",guidlow); + + if (result) + WorldDatabase.PExecute("UPDATE creature_addon SET path_id = '%u' WHERE guid = '%u'", pathid, guidlow); + else + WorldDatabase.PExecute("INSERT INTO creature_addon(guid,path_id) VALUES ('%u','%u')", guidlow, pathid); + + WorldDatabase.PExecute("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE, guidlow); + + target->LoadPath(pathid); + target->SetDefaultMovementType(WAYPOINT_MOTION_TYPE); + target->GetMotionMaster()->Initialize(); + target->MonsterSay("Path loaded.",0,0); + + return true; +} + +bool ChatHandler::HandleReloadAllPaths(const char* args) +{ + if (!*args) + return false; + + uint32 id = atoi(args); + + if (!id) + return false; + + PSendSysMessage("%s%s|r|cff00ffff%u|r", "|cff00ff00", "Loading Path: ", id); + sWaypointMgr->UpdatePath(id); + return true; +} + +bool ChatHandler::HandleWpUnLoadPathCommand(const char * /*args*/) +{ + uint32 guidlow = 0; + Creature* target = getSelectedCreature(); + + if (!target) + { + PSendSysMessage("%s%s|r", "|cff33ffff", "You must select target."); + return true; + } + + if (target->GetCreatureAddon()) + { + if (target->GetCreatureAddon()->path_id != 0) + { + WorldDatabase.PExecute("DELETE FROM creature_addon WHERE guid = %u", target->GetGUIDLow()); + target->UpdateWaypointID(0); + WorldDatabase.PExecute("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", IDLE_MOTION_TYPE, guidlow); + target->LoadPath(0); + target->SetDefaultMovementType(IDLE_MOTION_TYPE); + target->GetMotionMaster()->MoveTargetedHome(); + target->GetMotionMaster()->Initialize(); + target->MonsterSay("Path unloaded.",0,0); + return true; + } + PSendSysMessage("%s%s|r", "|cffff33ff", "Target have no loaded path."); + } + return true; +} + +bool ChatHandler::HandleWpEventCommand(const char* args) +{ + if (!*args) + return false; + + char* show_str = strtok((char*)args, " "); + std::string show = show_str; + + // Check + if ((show != "add") && (show != "mod") && (show != "del") && (show != "listid")) return false; + + char* arg_id = strtok(NULL, " "); + uint32 id = 0; + + if (show == "add") + { + if (arg_id) + id = atoi(arg_id); + + if (id) + { + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT id FROM waypoint_scripts WHERE guid = %u", id); + + if (!result) + { + WorldDatabase.PExecute("INSERT INTO waypoint_scripts(guid)VALUES(%u)", id); + PSendSysMessage("%s%s%u|r", "|cff00ff00", "Wp Event: New waypoint event added: ", id); + } + else + PSendSysMessage("|cff00ff00Wp Event: You have choosed an existing waypoint script guid: %u|r", id); + } + else + { + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT MAX(guid) FROM waypoint_scripts"); + id = result->Fetch()->GetUInt32(); + WorldDatabase.PExecute("INSERT INTO waypoint_scripts(guid)VALUES(%u)", id+1); + PSendSysMessage("%s%s%u|r", "|cff00ff00","Wp Event: New waypoint event added: |r|cff00ffff", id+1); + } + + return true; + } + + if (show == "listid") + { + if (!arg_id) + { + PSendSysMessage("%s%s|r", "|cff33ffff","Wp Event: You must provide waypoint script id."); + return true; + } + + id = atoi(arg_id); + + uint32 a2, a3, a4, a5, a6; + float a8, a9, a10, a11; + char const* a7; + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = %u", id); + + if (!result) + { + PSendSysMessage("%s%s%u|r", "|cff33ffff", "Wp Event: No waypoint scripts found on id: ", id); + return true; + } + + Field *fields; + + do + { + fields = result->Fetch(); + a2 = fields[0].GetUInt32(); + a3 = fields[1].GetUInt32(); + a4 = fields[2].GetUInt32(); + a5 = fields[3].GetUInt32(); + a6 = fields[4].GetUInt32(); + a7 = fields[5].GetString(); + a8 = fields[6].GetFloat(); + a9 = fields[7].GetFloat(); + a10 = fields[8].GetFloat(); + a11 = fields[9].GetFloat(); + + PSendSysMessage("|cffff33ffid:|r|cff00ffff %u|r|cff00ff00, guid: |r|cff00ffff%u|r|cff00ff00, delay: |r|cff00ffff%u|r|cff00ff00, command: |r|cff00ffff%u|r|cff00ff00, datalong: |r|cff00ffff%u|r|cff00ff00, datalong2: |r|cff00ffff%u|r|cff00ff00, datatext: |r|cff00ffff%s|r|cff00ff00, posx: |r|cff00ffff%f|r|cff00ff00, posy: |r|cff00ffff%f|r|cff00ff00, posz: |r|cff00ffff%f|r|cff00ff00, orientation: |r|cff00ffff%f|r", id, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + } + while (result->NextRow()); + } + + if (show == "del") + { + id = atoi(arg_id); + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid FROM waypoint_scripts WHERE guid = %u", id); + + if (result) + { + WorldDatabase.PExecuteLog("DELETE FROM waypoint_scripts WHERE guid = %u", id); + PSendSysMessage("%s%s%u|r","|cff00ff00","Wp Event: Waypoint script removed: ", id); + } + else + PSendSysMessage("|cffff33ffWp Event: ERROR: you have selected a non existing script: %u|r", id); + + return true; + } + + if (show == "mod") + { + if (!arg_id) + { + SendSysMessage("|cffff33ffERROR: Waypoint script guid not present.|r"); + return true; + } + + id = atoi(arg_id); + + if (!id) + { + SendSysMessage("|cffff33ffERROR: No vallid waypoint script id not present.|r"); + return true; + } + + char* arg_2 = strtok(NULL," "); + + if (!arg_2) + { + SendSysMessage("|cffff33ffERROR: No argument present.|r"); + return true; + } + + std::string arg_string = arg_2; + + if ((arg_string != "setid") && (arg_string != "delay") && (arg_string != "command") + && (arg_string != "datalong") && (arg_string != "datalong2") && (arg_string != "dataint") && (arg_string != "posx") + && (arg_string != "posy") && (arg_string != "posz") && (arg_string != "orientation")) + { + SendSysMessage("|cffff33ffERROR: No valid argument present.|r"); + return true; + } + + char* arg_3; + std::string arg_str_2 = arg_2; + arg_3 = strtok(NULL," "); + + if (!arg_3) + { + SendSysMessage("|cffff33ffERROR: No additional argument present.|r"); + return true; + } + + float coord; + + if (arg_str_2 == "setid") + { + uint32 newid = atoi(arg_3); + PSendSysMessage("%s%s|r|cff00ffff%u|r|cff00ff00%s|r|cff00ffff%u|r","|cff00ff00","Wp Event: Wypoint scipt guid: ", newid," id changed: ", id); + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET id='%u' WHERE guid='%u'", + newid, id); return true; + } + else + { + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT id FROM waypoint_scripts WHERE guid='%u'",id); + + if (!result) + { + SendSysMessage("|cffff33ffERROR: You have selected an non existing waypoint script guid.|r"); + return true; + } + + if (arg_str_2 == "posx") + { + coord = atof(arg_3); + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET x='%f' WHERE guid='%u'", + coord, id); + PSendSysMessage("|cff00ff00Waypoint script:|r|cff00ffff %u|r|cff00ff00 position_x updated.|r", id); + return true; + } + else if (arg_str_2 == "posy") + { + coord = atof(arg_3); + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET y='%f' WHERE guid='%u'", + coord, id); + PSendSysMessage("|cff00ff00Waypoint script: %u position_y updated.|r", id); + return true; + } + else if (arg_str_2 == "posz") + { + coord = atof(arg_3); + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET z='%f' WHERE guid='%u'", + coord, id); + PSendSysMessage("|cff00ff00Waypoint script: |r|cff00ffff%u|r|cff00ff00 position_z updated.|r", id); + return true; + } + else if (arg_str_2 == "orientation") + { + coord = atof(arg_3); + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET o='%f' WHERE guid='%u'", + coord, id); + PSendSysMessage("|cff00ff00Waypoint script: |r|cff00ffff%u|r|cff00ff00 orientation updated.|r", id); + return true; + } + else if (arg_str_2 == "dataint") + { + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET %s='%u' WHERE guid='%u'", + arg_2, atoi(arg_3), id); + PSendSysMessage("|cff00ff00Waypoint script: |r|cff00ffff%u|r|cff00ff00 dataint updated.|r", id); + return true; + } + else + { + std::string arg_str_3 = arg_3; + WorldDatabase.escape_string(arg_str_3); + WorldDatabase.PExecuteLog("UPDATE waypoint_scripts SET %s='%s' WHERE guid='%u'", + arg_2, arg_str_3.c_str(), id); + } + } + PSendSysMessage("%s%s|r|cff00ffff%u:|r|cff00ff00 %s %s|r","|cff00ff00","Waypoint script:", id, arg_2,"updated."); + } + return true; +} + +bool ChatHandler::HandleWpModifyCommand(const char* args) +{ + sLog.outDebug("DEBUG: HandleWpModifyCommand"); + + if (!*args) + return false; + + // first arg: add del text emote spell waittime move + char* show_str = strtok((char*)args, " "); + if (!show_str) + { + return false; + } + + std::string show = show_str; + // Check + // Remember: "show" must also be the name of a column! + if ((show != "delay") && (show != "action") && (show != "action_chance") + && (show != "move_flag") && (show != "del") && (show != "move") && (show != "wpadd") +) + { + return false; + } + + // Next arg is: + char* arg_str = NULL; + + // Did user provide a GUID + // or did the user select a creature? + // -> variable lowguid is filled with the GUID of the NPC + uint32 pathid = 0; + uint32 point = 0; + uint32 wpGuid = 0; + Creature* target = getSelectedCreature(); + + if (!target || target->GetEntry() != VISUAL_WAYPOINT) + { + SendSysMessage("|cffff33ffERROR: You must select a waypoint.|r"); + return false; + } + + sLog.outDebug("DEBUG: HandleWpModifyCommand - User did select an NPC"); + // The visual waypoint + Creature* wpCreature = NULL; + wpGuid = target->GetGUIDLow(); + + // Did the user select a visual spawnpoint? + if (wpGuid) + wpCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT)); + // attempt check creature existence by DB data + else + { + PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, wpGuid); + return false; + } + // User did select a visual waypoint? + // Check the creature + if (wpCreature->GetEntry() == VISUAL_WAYPOINT) + { + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT id, point FROM waypoint_data WHERE wpguid = %u", wpGuid); + + if (!result) + { + sLog.outDebug("DEBUG: HandleWpModifyCommand - No waypoint found - used 'wpguid'"); + + PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUIDLow()); + // Select waypoint number from database + // Since we compare float values, we have to deal with + // some difficulties. + // Here we search for all waypoints that only differ in one from 1 thousand + // (0.001) - There is no other way to compare C++ floats with mySQL floats + // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html + const char* maxDIFF = "0.01"; + result = WorldDatabase.PQuery("SELECT id, point FROM waypoint_data WHERE (abs(position_x - %f) <= %s) and (abs(position_y - %f) <= %s) and (abs(position_z - %f) <= %s)", + wpCreature->GetPositionX(), maxDIFF, wpCreature->GetPositionY(), maxDIFF, wpCreature->GetPositionZ(), maxDIFF); + if (!result) + { + PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, wpGuid); + return true; + } + } + sLog.outDebug("DEBUG: HandleWpModifyCommand - After getting wpGuid"); + + do + { + Field *fields = result->Fetch(); + pathid = fields[0].GetUInt32(); + point = fields[1].GetUInt32(); + } + while (result->NextRow()); + + // We have the waypoint number and the GUID of the "master npc" + // Text is enclosed in "<>", all other arguments not + arg_str = strtok((char*)NULL, " "); + } + + sLog.outDebug("DEBUG: HandleWpModifyCommand - Parameters parsed - now execute the command"); + + // Check for argument + if (show != "del" && show != "move" && arg_str == NULL) + { + PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, show_str); + return false; + } + + if (show == "del" && target) + { + PSendSysMessage("|cff00ff00DEBUG: wp modify del, PathID: |r|cff00ffff%u|r", pathid); + + // wpCreature + Creature* wpCreature = NULL; + + if (wpGuid != 0) + { + wpCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT)); + wpCreature->CombatStop(); + wpCreature->DeleteFromDB(); + wpCreature->AddObjectToRemoveList(); + } + + WorldDatabase.PExecuteLog("DELETE FROM waypoint_data WHERE id='%u' AND point='%u'", + pathid, point); + WorldDatabase.PExecuteLog("UPDATE waypoint_data SET point=point-1 WHERE id='%u' AND point>'%u'", + pathid, point); + + PSendSysMessage(LANG_WAYPOINT_REMOVED); + return true; + } // del + + if (show == "move" && target) + { + PSendSysMessage("|cff00ff00DEBUG: wp move, PathID: |r|cff00ffff%u|r", pathid); + + Player *chr = m_session->GetPlayer(); + Map *map = chr->GetMap(); + { + // wpCreature + Creature* wpCreature = NULL; + // What to do: + // Move the visual spawnpoint + // Respawn the owner of the waypoints + if (wpGuid != 0) + { + wpCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT)); + wpCreature->CombatStop(); + wpCreature->DeleteFromDB(); + wpCreature->AddObjectToRemoveList(); + // re-create + Creature* wpCreature2 = new Creature; + if (!wpCreature2->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, 0, 0, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation())) + { + PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT); + delete wpCreature2; + return false; + } + + wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); + wpCreature2->LoadFromDB(wpCreature2->GetDBTableGUIDLow(), map); + map->Add(wpCreature2); + //MapManager::Instance().GetMap(npcCreature->GetMapId())->Add(wpCreature2); + } + + WorldDatabase.PExecuteLog("UPDATE waypoint_data SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", + chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), pathid, point); + + PSendSysMessage(LANG_WAYPOINT_CHANGED); + } + return true; + } // move + + const char *text = arg_str; + + if (text == 0) + { + // show_str check for present in list of correct values, no sql injection possible + WorldDatabase.PExecuteLog("UPDATE waypoint_data SET %s=NULL WHERE id='%u' AND point='%u'", + show_str, pathid, point); + } + else + { + // show_str check for present in list of correct values, no sql injection possible + std::string text2 = text; + WorldDatabase.escape_string(text2); + WorldDatabase.PExecuteLog("UPDATE waypoint_data SET %s='%s' WHERE id='%u' AND point='%u'", + show_str, text2.c_str(), pathid, point); + } + + PSendSysMessage(LANG_WAYPOINT_CHANGED_NO, show_str); + return true; +} + +bool ChatHandler::HandleWpShowCommand(const char* args) +{ + sLog.outDebug("DEBUG: HandleWpShowCommand"); + + if (!*args) + return false; + + // first arg: on, off, first, last + char* show_str = strtok((char*)args, " "); + if (!show_str) + return false; + + // second arg: GUID (optional, if a creature is selected) + char* guid_str = strtok((char*)NULL, " "); + sLog.outDebug("DEBUG: HandleWpShowCommand: show_str: %s guid_str: %s", show_str, guid_str); + + uint32 pathid = 0; + Creature* target = getSelectedCreature(); + + // Did player provide a PathID? + + if (!guid_str) + { + sLog.outDebug("DEBUG: HandleWpShowCommand: !guid_str"); + // No PathID provided + // -> Player must have selected a creature + + if (!target) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + pathid = target->GetWaypointPath(); + } + else + { + sLog.outDebug("|cff00ff00DEBUG: HandleWpShowCommand: PathID provided|r"); + // PathID provided + // Warn if player also selected a creature + // -> Creature selection is ignored <- + if (target) + SendSysMessage(LANG_WAYPOINT_CREATSELECTED); + + pathid = atoi((char*)guid_str); + } + + sLog.outDebug("DEBUG: HandleWpShowCommand: danach"); + + std::string show = show_str; + uint32 Maxpoint; + + sLog.outDebug("DEBUG: HandleWpShowCommand: PathID: %u", pathid); + + //PSendSysMessage("wpshow - show: %s", show); + + // Show info for the selected waypoint + if (show == "info") + { + // Check if the user did specify a visual waypoint + if (target->GetEntry() != VISUAL_WAYPOINT) + { + PSendSysMessage(LANG_WAYPOINT_VP_SELECT); + SetSentErrorMessage(true); + return false; + } + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT id, point, delay, move_flag, action, action_chance FROM waypoint_data WHERE wpguid = %u", target->GetGUIDLow()); + + if (!result) + { + SendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM); + return true; + } + + SendSysMessage("|cff00ffffDEBUG: wp show info:|r"); + do + { + Field *fields = result->Fetch(); + pathid = fields[0].GetUInt32(); + uint32 point = fields[1].GetUInt32(); + uint32 delay = fields[2].GetUInt32(); + uint32 flag = fields[3].GetUInt32(); + uint32 ev_id = fields[4].GetUInt32(); + uint32 ev_chance = fields[5].GetUInt32(); + + PSendSysMessage("|cff00ff00Show info: for current point: |r|cff00ffff%u|r|cff00ff00, Path ID: |r|cff00ffff%u|r", point, pathid); + PSendSysMessage("|cff00ff00Show info: delay: |r|cff00ffff%u|r", delay); + PSendSysMessage("|cff00ff00Show info: Move flag: |r|cff00ffff%u|r", flag); + PSendSysMessage("|cff00ff00Show info: Waypoint event: |r|cff00ffff%u|r", ev_id); + PSendSysMessage("|cff00ff00Show info: Event chance: |r|cff00ffff%u|r", ev_chance); + } + while (result->NextRow()); + + return true; + } + + if (show == "on") + { + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT point, position_x,position_y,position_z FROM waypoint_data WHERE id = '%u'", pathid); + + if (!result) + { + SendSysMessage("|cffff33ffPath no found.|r"); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage("|cff00ff00DEBUG: wp on, PathID: |cff00ffff%u|r", pathid); + + // Delete all visuals for this NPC + QueryResult_AutoPtr result2 = WorldDatabase.PQuery("SELECT wpguid FROM waypoint_data WHERE id = '%u' and wpguid <> 0", pathid); + + if (result2) + { + bool hasError = false; + do + { + Field *fields = result2->Fetch(); + uint32 wpguid = fields[0].GetUInt32(); + Creature* pCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpguid,VISUAL_WAYPOINT,HIGHGUID_UNIT)); + + if (!pCreature) + { + PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid); + hasError = true; + WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", wpguid); + } + else + { + pCreature->CombatStop(); + pCreature->DeleteFromDB(); + pCreature->AddObjectToRemoveList(); + } + + } + while (result2->NextRow()); + + if (hasError) + { + PSendSysMessage(LANG_WAYPOINT_TOOFAR1); + PSendSysMessage(LANG_WAYPOINT_TOOFAR2); + PSendSysMessage(LANG_WAYPOINT_TOOFAR3); + } + } + + do + { + Field *fields = result->Fetch(); + uint32 point = fields[0].GetUInt32(); + float x = fields[1].GetFloat(); + float y = fields[2].GetFloat(); + float z = fields[3].GetFloat(); + + uint32 id = VISUAL_WAYPOINT; + + Player *chr = m_session->GetPlayer(); + Map *map = chr->GetMap(); + float o = chr->GetOrientation(); + + Creature* wpCreature = new Creature; + if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) + { + PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); + delete wpCreature; + return false; + } + + sLog.outDebug("DEBUG: UPDATE waypoint_data SET wpguid = '%u"); + // set "wpguid" column to the visual waypoint + WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), pathid, point); + + wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); + wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(),map); + map->Add(wpCreature); + + if (target) + { + wpCreature->SetDisplayId(target->GetDisplayId()); + wpCreature->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5); + wpCreature->SetLevel(point > MAX_LEVEL ? MAX_LEVEL : point); + } + } + while (result->NextRow()); + + SendSysMessage("|cff00ff00Showing the current creature's path.|r"); + return true; + } + + if (show == "first") + { + PSendSysMessage("|cff00ff00DEBUG: wp first, GUID: %u|r", pathid); + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z FROM waypoint_data WHERE point='1' AND id = '%u'",pathid); + if (!result) + { + PSendSysMessage(LANG_WAYPOINT_NOTFOUND, pathid); + SetSentErrorMessage(true); + return false; + } + + Field *fields = result->Fetch(); + float x = fields[0].GetFloat(); + float y = fields[1].GetFloat(); + float z = fields[2].GetFloat(); + uint32 id = VISUAL_WAYPOINT; + + Player *chr = m_session->GetPlayer(); + float o = chr->GetOrientation(); + Map *map = chr->GetMap(); + + Creature* pCreature = new Creature; + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT),map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) + { + PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); + delete pCreature; + return false; + } + + pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map); + map->Add(pCreature); + + if (target) + { + pCreature->SetDisplayId(target->GetDisplayId()); + pCreature->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5); + } + + return true; + } + + if (show == "last") + { + PSendSysMessage("|cff00ff00DEBUG: wp last, PathID: |r|cff00ffff%u|r", pathid); + + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT MAX(point) FROM waypoint_data WHERE id = '%u'",pathid); + if (result) + Maxpoint = (*result)[0].GetUInt32(); + else + Maxpoint = 0; + + result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z FROM waypoint_data WHERE point ='%u' AND id = '%u'",Maxpoint, pathid); + if (!result) + { + PSendSysMessage(LANG_WAYPOINT_NOTFOUNDLAST, pathid); + SetSentErrorMessage(true); + return false; + } + Field *fields = result->Fetch(); + float x = fields[0].GetFloat(); + float y = fields[1].GetFloat(); + float z = fields[2].GetFloat(); + uint32 id = VISUAL_WAYPOINT; + + Player *chr = m_session->GetPlayer(); + float o = chr->GetOrientation(); + Map *map = chr->GetMap(); + + Creature* pCreature = new Creature; + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) + { + PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); + delete pCreature; + return false; + } + + pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map); + map->Add(pCreature); + + if (target) + { + pCreature->SetDisplayId(target->GetDisplayId()); + pCreature->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5); + } + + return true; + } + + if (show == "off") + { + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id = '%u'", 1); + if (!result) + { + SendSysMessage(LANG_WAYPOINT_VP_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + bool hasError = false; + do + { + Field *fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + Creature* pCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(guid,VISUAL_WAYPOINT,HIGHGUID_UNIT)); + if (!pCreature) + { + PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); + hasError = true; + WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", guid); + } + else + { + pCreature->CombatStop(); + pCreature->DeleteFromDB(); + pCreature->AddObjectToRemoveList(); + } + } + while (result->NextRow()); + // set "wpguid" column to "empty" - no visual waypoint spawned + WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '0'"); + //WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '0' WHERE wpguid <> '0'"); + + if (hasError) + { + PSendSysMessage(LANG_WAYPOINT_TOOFAR1); + PSendSysMessage(LANG_WAYPOINT_TOOFAR2); + PSendSysMessage(LANG_WAYPOINT_TOOFAR3); + } + + SendSysMessage(LANG_WAYPOINT_VP_ALLREMOVED); + return true; + } + + PSendSysMessage("|cffff33ffDEBUG: wpshow - no valid command found|r"); + return true; +} + +//////////// WAYPOINT COMMANDS // + +//rename characters +bool ChatHandler::HandleCharacterRenameCommand(const char* args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + if (target) + { + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + PSendSysMessage(LANG_RENAME_PLAYER, GetNameLink(target).c_str()); + target->SetAtLoginFlag(AT_LOGIN_RENAME); + } + else + { + // check offline security + if (HasLowerSecurity(NULL, target_guid)) + return false; + + std::string oldNameLink = playerLink(target_name); + + PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(target_guid)); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", GUID_LOPART(target_guid)); + } + + return true; +} + +// customize characters +bool ChatHandler::HandleCharacterCustomizeCommand(const char* args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + if (target) + { + PSendSysMessage(LANG_CUSTOMIZE_PLAYER, GetNameLink(target).c_str()); + target->SetAtLoginFlag(AT_LOGIN_CUSTOMIZE); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '8' WHERE guid = '%u'", target->GetGUIDLow()); + } + else + { + std::string oldNameLink = playerLink(target_name); + + PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(target_guid)); + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '8' WHERE guid = '%u'", GUID_LOPART(target_guid)); + } + + return true; +} + +bool ChatHandler::HandleCharacterReputationCommand(const char* args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + LocaleConstant loc = GetSessionDbcLocale(); + + FactionStateList const& targetFSL = target->GetReputationMgr().GetStateList(); + for (FactionStateList::const_iterator itr = targetFSL.begin(); itr != targetFSL.end(); ++itr) + { + FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID); + char const* factionName = factionEntry ? factionEntry->name[loc] : "#Not found#"; + ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry); + std::string rankName = GetTrinityString(ReputationRankStrIndex[rank]); + std::ostringstream ss; + if (m_session) + ss << itr->second.ID << " - |cffffffff|Hfaction:" << itr->second.ID << "|h[" << factionName << " " << localeNames[loc] << "]|h|r"; + else + ss << itr->second.ID << " - " << factionName << " " << localeNames[loc]; + + ss << " " << rankName << " (" << target->GetReputationMgr().GetReputation(factionEntry) << ")"; + + if (itr->second.Flags & FACTION_FLAG_VISIBLE) + ss << GetTrinityString(LANG_FACTION_VISIBLE); + if (itr->second.Flags & FACTION_FLAG_AT_WAR) + ss << GetTrinityString(LANG_FACTION_ATWAR); + if (itr->second.Flags & FACTION_FLAG_PEACE_FORCED) + ss << GetTrinityString(LANG_FACTION_PEACE_FORCED); + if (itr->second.Flags & FACTION_FLAG_HIDDEN) + ss << GetTrinityString(LANG_FACTION_HIDDEN); + if (itr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED) + ss << GetTrinityString(LANG_FACTION_INVISIBLE_FORCED); + if (itr->second.Flags & FACTION_FLAG_INACTIVE) + ss << GetTrinityString(LANG_FACTION_INACTIVE); + + SendSysMessage(ss.str().c_str()); + } + return true; +} + +//change standstate +bool ChatHandler::HandleModifyStandStateCommand(const char* args) +{ + if (!*args) + return false; + + uint32 anim_id = atoi((char*)args); + m_session->GetPlayer()->SetUInt32Value(UNIT_NPC_EMOTESTATE , anim_id); + + return true; +} + +bool ChatHandler::HandleHonorAddCommand(const char* args) +{ + if (!*args) + return false; + + Player *target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + uint32 amount = (uint32)atoi(args); + target->RewardHonor(NULL, 1, amount); + return true; +} + +bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/) +{ + Unit *target = getSelectedUnit(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0)) + return false; + + m_session->GetPlayer()->RewardHonor(target, 1); + return true; +} + +bool ChatHandler::HandleHonorUpdateCommand(const char* /*args*/) +{ + Player *target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + target->UpdateHonorFields(); + return true; +} + +bool ChatHandler::HandleLookupEventCommand(const char* args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + // converting string that we try to find to lower case + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + wstrToLower(wnamepart); + + bool found = false; + + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + + for (uint32 id = 0; id < events.size(); ++id) + { + GameEventData const& eventData = events[id]; + + std::string descr = eventData.description; + if (descr.empty()) + continue; + + if (Utf8FitTo(descr, wnamepart)) + { + char const* active = activeEvents.find(id) != activeEvents.end() ? GetTrinityString(LANG_ACTIVE) : ""; + + if (m_session) + PSendSysMessage(LANG_EVENT_ENTRY_LIST_CHAT,id,id,eventData.description.c_str(),active); + else + PSendSysMessage(LANG_EVENT_ENTRY_LIST_CONSOLE,id,eventData.description.c_str(),active); + + if (!found) + found = true; + } + } + + if (!found) + SendSysMessage(LANG_NOEVENTFOUND); + + return true; +} + +bool ChatHandler::HandleEventActiveListCommand(const char* /*args*/) +{ + uint32 counter = 0; + + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + + char const* active = GetTrinityString(LANG_ACTIVE); + + for (GameEventMgr::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr) + { + uint32 event_id = *itr; + GameEventData const& eventData = events[event_id]; + + if (m_session) + PSendSysMessage(LANG_EVENT_ENTRY_LIST_CHAT,event_id,event_id,eventData.description.c_str(),active); + else + PSendSysMessage(LANG_EVENT_ENTRY_LIST_CONSOLE,event_id,eventData.description.c_str(),active); + + ++counter; + } + + if (counter == 0) + SendSysMessage(LANG_NOEVENTFOUND); + + return true; +} + +bool ChatHandler::HandleEventInfoCommand(const char* args) +{ + if (!*args) + return false; + + // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameevent"); + if (!cId) + return false; + + uint32 event_id = atoi(cId); + + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + + if (event_id >=events.size()) + { + SendSysMessage(LANG_EVENT_NOT_EXIST); + SetSentErrorMessage(true); + return false; + } + + GameEventData const& eventData = events[event_id]; + if (!eventData.isValid()) + { + SendSysMessage(LANG_EVENT_NOT_EXIST); + SetSentErrorMessage(true); + return false; + } + + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + bool active = activeEvents.find(event_id) != activeEvents.end(); + char const* activeStr = active ? GetTrinityString(LANG_ACTIVE) : ""; + + std::string startTimeStr = TimeToTimestampStr(eventData.start); + std::string endTimeStr = TimeToTimestampStr(eventData.end); + + uint32 delay = gameeventmgr.NextCheck(event_id); + time_t nextTime = time(NULL)+delay; + std::string nextStr = nextTime >= eventData.start && nextTime < eventData.end ? TimeToTimestampStr(time(NULL)+delay) : "-"; + + std::string occurenceStr = secsToTimeString(eventData.occurence * MINUTE); + std::string lengthStr = secsToTimeString(eventData.length * MINUTE); + + PSendSysMessage(LANG_EVENT_INFO,event_id,eventData.description.c_str(),activeStr, + startTimeStr.c_str(),endTimeStr.c_str(),occurenceStr.c_str(),lengthStr.c_str(), + nextStr.c_str()); + return true; +} + +bool ChatHandler::HandleEventStartCommand(const char* args) +{ + if (!*args) + return false; + + // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameevent"); + if (!cId) + return false; + + int32 event_id = atoi(cId); + + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + + if (event_id < 1 || event_id >=events.size()) + { + SendSysMessage(LANG_EVENT_NOT_EXIST); + SetSentErrorMessage(true); + return false; + } + + GameEventData const& eventData = events[event_id]; + if (!eventData.isValid()) + { + SendSysMessage(LANG_EVENT_NOT_EXIST); + SetSentErrorMessage(true); + return false; + } + + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + if (activeEvents.find(event_id) != activeEvents.end()) + { + PSendSysMessage(LANG_EVENT_ALREADY_ACTIVE,event_id); + SetSentErrorMessage(true); + return false; + } + + gameeventmgr.StartEvent(event_id,true); + return true; +} + +bool ChatHandler::HandleEventStopCommand(const char* args) +{ + if (!*args) + return false; + + // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameevent"); + if (!cId) + return false; + + int32 event_id = atoi(cId); + + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + + if (event_id < 1 || event_id >=events.size()) + { + SendSysMessage(LANG_EVENT_NOT_EXIST); + SetSentErrorMessage(true); + return false; + } + + GameEventData const& eventData = events[event_id]; + if (!eventData.isValid()) + { + SendSysMessage(LANG_EVENT_NOT_EXIST); + SetSentErrorMessage(true); + return false; + } + + GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList(); + + if (activeEvents.find(event_id) == activeEvents.end()) + { + PSendSysMessage(LANG_EVENT_NOT_ACTIVE,event_id); + SetSentErrorMessage(true); + return false; + } + + gameeventmgr.StopEvent(event_id,true); + return true; +} + +bool ChatHandler::HandleCombatStopCommand(const char* args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + target->CombatStop(); + target->getHostileRefManager().deleteReferences(); + return true; +} + +void ChatHandler::HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id) +{ + uint32 classmask = player->getClassMask(); + + for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) + { + SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j); + if (!skillLine) + continue; + + // wrong skill + if (skillLine->skillId != skill_id) + continue; + + // not high rank + if (skillLine->forward_spellid) + continue; + + // skip racial skills + if (skillLine->racemask != 0) + continue; + + // skip wrong class skills + if (skillLine->classmask && (skillLine->classmask & classmask) == 0) + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo,player,false)) + continue; + + player->learnSpell(skillLine->spellId, false); + } +} + +bool ChatHandler::HandleLearnAllCraftsCommand(const char* /*args*/) +{ + + for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i) + { + SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i); + if (!skillInfo) + continue; + + if ((skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY) && + skillInfo->canLink) // only prof. with recipes have + { + HandleLearnSkillRecipesHelper(m_session->GetPlayer(),skillInfo->id); + } + } + + SendSysMessage(LANG_COMMAND_LEARN_ALL_CRAFT); + return true; +} + +bool ChatHandler::HandleLearnAllRecipesCommand(const char* args) +{ + // Learns all recipes of specified profession and sets skill to max + // Example: .learn all_recipes enchanting + + Player* target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + return false; + } + + if (!*args) + return false; + + std::wstring wnamepart; + + if (!Utf8toWStr(args,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + std::string name; + + SkillLineEntry const *targetSkillInfo = NULL; + for (uint32 i = 1; i < sSkillLineStore.GetNumRows(); ++i) + { + SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i); + if (!skillInfo) + continue; + + if ((skillInfo->categoryId != SKILL_CATEGORY_PROFESSION && + skillInfo->categoryId != SKILL_CATEGORY_SECONDARY) || + !skillInfo->canLink) // only prof with recipes have set + continue; + + int loc = GetSessionDbcLocale(); + name = skillInfo->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = skillInfo->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + targetSkillInfo = skillInfo; + break; + } + } + + if (!targetSkillInfo) + return false; + + HandleLearnSkillRecipesHelper(target,targetSkillInfo->id); + + uint16 maxLevel = target->GetPureMaxSkillValue(targetSkillInfo->id); + target->SetSkill(targetSkillInfo->id, target->GetSkillStep(targetSkillInfo->id), maxLevel, maxLevel); + PSendSysMessage(LANG_COMMAND_LEARN_ALL_RECIPES, name.c_str()); + return true; +} + +bool ChatHandler::HandleLookupPlayerIpCommand(const char* args) +{ + + if (!*args) + return false; + + std::string ip = strtok ((char*)args, " "); + char* limit_str = strtok (NULL, " "); + int32 limit = limit_str ? atoi (limit_str) : -1; + + LoginDatabase.escape_string (ip); + + QueryResult_AutoPtr result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str ()); + + return LookupPlayerSearchCommand (result,limit); +} + +bool ChatHandler::HandleLookupPlayerAccountCommand(const char* args) +{ + if (!*args) + return false; + + std::string account = strtok ((char*)args, " "); + char* limit_str = strtok (NULL, " "); + int32 limit = limit_str ? atoi (limit_str) : -1; + + if (!AccountMgr::normalizeString (account)) + return false; + + LoginDatabase.escape_string (account); + + QueryResult_AutoPtr result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE username = '%s'", account.c_str ()); + + return LookupPlayerSearchCommand (result,limit); +} + +bool ChatHandler::HandleLookupPlayerEmailCommand(const char* args) +{ + + if (!*args) + return false; + + std::string email = strtok ((char*)args, " "); + char* limit_str = strtok (NULL, " "); + int32 limit = limit_str ? atoi (limit_str) : -1; + + LoginDatabase.escape_string (email); + + QueryResult_AutoPtr result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE email = '%s'", email.c_str ()); + + return LookupPlayerSearchCommand (result,limit); +} + +bool ChatHandler::LookupPlayerSearchCommand(QueryResult_AutoPtr result, int32 limit) +{ + if (!result) + { + PSendSysMessage(LANG_NO_PLAYERS_FOUND); + SetSentErrorMessage(true); + return false; + } + + int i =0; + do + { + Field* fields = result->Fetch(); + uint32 acc_id = fields[0].GetUInt32(); + std::string acc_name = fields[1].GetCppString(); + + QueryResult_AutoPtr chars = CharacterDatabase.PQuery("SELECT guid,name FROM characters WHERE account = '%u'", acc_id); + if (chars) + { + PSendSysMessage(LANG_LOOKUP_PLAYER_ACCOUNT,acc_name.c_str(),acc_id); + + uint64 guid = 0; + std::string name; + + do + { + Field* charfields = chars->Fetch(); + guid = charfields[0].GetUInt64(); + name = charfields[1].GetCppString(); + + PSendSysMessage(LANG_LOOKUP_PLAYER_CHARACTER,name.c_str(),guid); + ++i; + + } while (chars->NextRow() && (limit == -1 || i < limit)); + } + } while (result->NextRow()); + + if (i == 0) // empty accounts only + { + PSendSysMessage(LANG_NO_PLAYERS_FOUND); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +/// Triggering corpses expire check in world +bool ChatHandler::HandleServerCorpsesCommand(const char* /*args*/) +{ + CorpsesErase(); + return true; +} + +bool ChatHandler::HandleRepairitemsCommand(const char* args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + // Repair items + target->DurabilityRepairAll(false, 0, false); + + PSendSysMessage(LANG_YOU_REPAIR_ITEMS, GetNameLink(target).c_str()); + if (needReportToTarget(target)) + ChatHandler(target).PSendSysMessage(LANG_YOUR_ITEMS_REPAIRED, GetNameLink().c_str()); + return true; +} + +bool ChatHandler::HandleWaterwalkCommand(const char* args) +{ + if (!*args) + return false; + + Player *player = getSelectedPlayer(); + + if (!player) + { + PSendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(player, 0)) + return false; + + if (strncmp(args, "on", 3) == 0) + player->SetMovement(MOVE_WATER_WALK); // ON + else if (strncmp(args, "off", 4) == 0) + player->SetMovement(MOVE_LAND_WALK); // OFF + else + { + SendSysMessage(LANG_USE_BOL); + return false; + } + + PSendSysMessage(LANG_YOU_SET_WATERWALK, args, GetNameLink(player).c_str()); + if (needReportToTarget(player)) + ChatHandler(player).PSendSysMessage(LANG_YOUR_WATERWALK_SET, args, GetNameLink().c_str()); + return true; +} + +bool ChatHandler::HandleCreatePetCommand(const char* /*args*/) +{ + Player *player = m_session->GetPlayer(); + Creature *creatureTarget = getSelectedCreature(); + + if (!creatureTarget || creatureTarget->isPet() || creatureTarget->GetTypeId() == TYPEID_PLAYER) + { + PSendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(creatureTarget->GetEntry()); + // Creatures with family 0 crashes the server + if (cInfo->family == 0) + { + PSendSysMessage("This creature cannot be tamed. (family id: 0)."); + SetSentErrorMessage(true); + return false; + } + + if (player->GetPetGUID()) + { + PSendSysMessage("You already have a pet"); + SetSentErrorMessage(true); + return false; + } + + // Everything looks OK, create new pet + Pet* pet = new Pet(player, HUNTER_PET); + + if (!pet) + return false; + + if (!pet->CreateBaseAtCreature(creatureTarget)) + { + delete pet; + PSendSysMessage("Error 1"); + return false; + } + + creatureTarget->setDeathState(JUST_DIED); + creatureTarget->RemoveCorpse(); + creatureTarget->SetHealth(0); // just for nice GM-mode view + + pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, player->GetGUID()); + pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, player->getFaction()); + + if (!pet->InitStatsForLevel(creatureTarget->getLevel())) + { + sLog.outError("ERROR: InitStatsForLevel() in EffectTameCreature failed! Pet deleted."); + PSendSysMessage("Error 2"); + delete pet; + return false; + } + + // prepare visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()-1); + + pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true); + // this enables pet details window (Shift+P) + pet->InitPetCreateSpells(); + pet->SetHealth(pet->GetMaxHealth()); + + pet->GetMap()->Add(pet->ToCreature()); + + // visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()); + + player->SetMinion(pet, true); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + player->PetSpellInitialize(); + + return true; +} + +bool ChatHandler::HandlePetLearnCommand(const char* args) +{ + if (!*args) + return false; + + Player *plr = m_session->GetPlayer(); + Pet *pet = plr->GetPet(); + + if (!pet) + { + PSendSysMessage("You have no pet"); + SetSentErrorMessage(true); + return false; + } + + uint32 spellId = extractSpellIdFromLink((char*)args); + + if (!spellId || !sSpellStore.LookupEntry(spellId)) + return false; + + // Check if pet already has it + if (pet->HasSpell(spellId)) + { + PSendSysMessage("Pet already has spell: %u", spellId); + SetSentErrorMessage(true); + return false; + } + + // Check if spell is valid + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo)) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spellId); + SetSentErrorMessage(true); + return false; + } + + pet->learnSpell(spellId); + + PSendSysMessage("Pet has learned spell %u", spellId); + return true; +} + +bool ChatHandler::HandlePetUnlearnCommand(const char *args) +{ + if (!*args) + return false; + + Player *plr = m_session->GetPlayer(); + Pet *pet = plr->GetPet(); + + if (!pet) + { + PSendSysMessage("You have no pet"); + SetSentErrorMessage(true); + return false; + } + + uint32 spellId = extractSpellIdFromLink((char*)args); + + if (pet->HasSpell(spellId)) + pet->removeSpell(spellId, false); + else + PSendSysMessage("Pet doesn't have that spell"); + + return true; +} + +bool ChatHandler::HandlePetTpCommand(const char *args) +{ + if (!*args) + return false; + + Player *plr = m_session->GetPlayer(); + Pet *pet = plr->GetPet(); + + if (!pet) + { + PSendSysMessage("You have no pet"); + SetSentErrorMessage(true); + return false; + } + + uint32 tp = atol(args); + + //pet->SetTP(tp); + + PSendSysMessage("Pet's tp changed to %u", tp); + return true; +} + +bool ChatHandler::HandleActivateObjectCommand(const char *args) +{ + if (!*args) + return false; + + char* cId = extractKeyFromLink((char*)args,"Hgameobject"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + GameObject* obj = NULL; + + // by DB guid + if (GameObjectData const* go_data = objmgr.GetGOData(lowguid)) + obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id); + + if (!obj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + // Activate + obj->SetLootState(GO_READY); + obj->UseDoorOrButton(10000); + + PSendSysMessage("Object activated!"); + + return true; +} + +// add creature, temp only +bool ChatHandler::HandleTempAddSpwCommand(const char* args) +{ + if (!*args) + return false; + char* charID = strtok((char*)args, " "); + if (!charID) + return false; + + Player *chr = m_session->GetPlayer(); + + uint32 id = atoi(charID); + if (!id) + return false; + + chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120); + + return true; +} + +// add go, temp only +bool ChatHandler::HandleTempGameObjectCommand(const char* args) +{ + if (!*args) + return false; + char* charID = strtok((char*)args, " "); + if (!charID) + return false; + + Player *chr = m_session->GetPlayer(); + + char* spawntime = strtok(NULL, " "); + uint32 spawntm = 300; + + if (spawntime) + spawntm = atoi((char*)spawntime); + + float x = chr->GetPositionX(); + float y = chr->GetPositionY(); + float z = chr->GetPositionZ(); + float ang = chr->GetOrientation(); + + float rot2 = sin(ang/2); + float rot3 = cos(ang/2); + + uint32 id = atoi(charID); + + chr->SummonGameObject(id,x,y,z,ang,0,0,rot2,rot3,spawntm); + + return true; +} + +bool ChatHandler::HandleNpcAddFormationCommand(const char* args) +{ + if (!*args) + return false; + + uint32 leaderGUID = (uint32) atoi((char*)args); + Creature *pCreature = getSelectedCreature(); + + if (!pCreature || !pCreature->GetDBTableGUIDLow()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + uint32 lowguid = pCreature->GetDBTableGUIDLow(); + if (pCreature->GetFormation()) + { + PSendSysMessage("Selected creature is already member of group %u", pCreature->GetFormation()->GetId()); + return false; + } + + if (!lowguid) + return false; + + Player *chr = m_session->GetPlayer(); + FormationInfo *group_member; + + group_member = new FormationInfo; + group_member->follow_angle = (pCreature->GetAngle(chr) - chr->GetOrientation()) * 180 / M_PI; + group_member->follow_dist = sqrtf(pow(chr->GetPositionX() - pCreature->GetPositionX(),int(2))+pow(chr->GetPositionY()-pCreature->GetPositionY(),int(2))); + group_member->leaderGUID = leaderGUID; + group_member->groupAI = 0; + + CreatureGroupMap[lowguid] = group_member; + pCreature->SearchFormation(); + + WorldDatabase.PExecuteLog("INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES ('%u','%u','%f', '%f', '%u')", + leaderGUID, lowguid, group_member->follow_dist, group_member->follow_angle, group_member->groupAI); + + PSendSysMessage("Creature %u added to formation with leader %u", lowguid, leaderGUID); + + return true; + } + +bool ChatHandler::HandleNpcSetLinkCommand(const char* args) +{ + if (!*args) + return false; + + uint32 linkguid = (uint32) atoi((char*)args); + + Creature* pCreature = getSelectedCreature(); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (!pCreature->GetDBTableGUIDLow()) + { + PSendSysMessage("Selected creature isn't in creature table", pCreature->GetGUIDLow()); + SetSentErrorMessage(true); + return false; + } + + if (!objmgr.SetCreatureLinkedRespawn(pCreature->GetDBTableGUIDLow(), linkguid)) + { + PSendSysMessage("Selected creature can't link with guid '%u'", linkguid); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage("LinkGUID '%u' added to creature with DBTableGUID: '%u'", linkguid, pCreature->GetDBTableGUIDLow()); + return true; +} + +bool ChatHandler::HandleLookupTitleCommand(const char* args) +{ + if (!*args) + return false; + + // can be NULL in console call + Player* target = getSelectedPlayer(); + + // title name have single string arg for player name + char const* targetName = target ? target->GetName() : "NAME"; + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + uint32 counter = 0; // Counter for figure out that we found smth. + + // Search in CharTitles.dbc + for (uint32 id = 0; id < sCharTitlesStore.GetNumRows(); id++) + { + CharTitlesEntry const *titleInfo = sCharTitlesStore.LookupEntry(id); + if (titleInfo) + { + int loc = GetSessionDbcLocale(); + std::string name = titleInfo->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = titleInfo->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + char const* knownStr = target && target->HasTitle(titleInfo) ? GetTrinityString(LANG_KNOWN) : ""; + + char const* activeStr = target && target->GetUInt32Value(PLAYER_CHOSEN_TITLE) == titleInfo->bit_index + ? GetTrinityString(LANG_ACTIVE) + : ""; + + char titleNameStr[80]; + snprintf(titleNameStr,80,name.c_str(),targetName); + + // send title in "id (idx:idx) - [namedlink locale]" format + if (m_session) + PSendSysMessage(LANG_TITLE_LIST_CHAT,id,titleInfo->bit_index,id,titleNameStr,localeNames[loc],knownStr,activeStr); + else + PSendSysMessage(LANG_TITLE_LIST_CONSOLE,id,titleInfo->bit_index,titleNameStr,localeNames[loc],knownStr,activeStr); + + ++counter; + } + } + } + if (counter == 0) // if counter == 0 then we found nth + SendSysMessage(LANG_COMMAND_NOTITLEFOUND); + return true; +} + +bool ChatHandler::HandleTitlesAddCommand(const char* args) +{ + // number or [name] Shift-click form |color|Htitle:title_id|h[name]|h|r + char* id_p = extractKeyFromLink((char*)args,"Htitle"); + if (!id_p) + return false; + + int32 id = atoi(id_p); + if (id <= 0) + { + PSendSysMessage(LANG_INVALID_TITLE_ID, id); + SetSentErrorMessage(true); + return false; + } + + Player * target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(id); + if (!titleInfo) + { + PSendSysMessage(LANG_INVALID_TITLE_ID, id); + SetSentErrorMessage(true); + return false; + } + + std::string tNameLink = GetNameLink(target); + + char const* targetName = target->GetName(); + char titleNameStr[80]; + snprintf(titleNameStr,80,titleInfo->name[GetSessionDbcLocale()],targetName); + + target->SetTitle(titleInfo); + PSendSysMessage(LANG_TITLE_ADD_RES, id, titleNameStr, tNameLink.c_str()); + + return true; +} + +bool ChatHandler::HandleTitlesRemoveCommand(const char* args) +{ + // number or [name] Shift-click form |color|Htitle:title_id|h[name]|h|r + char* id_p = extractKeyFromLink((char*)args,"Htitle"); + if (!id_p) + return false; + + int32 id = atoi(id_p); + if (id <= 0) + { + PSendSysMessage(LANG_INVALID_TITLE_ID, id); + SetSentErrorMessage(true); + return false; + } + + Player * target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(id); + if (!titleInfo) + { + PSendSysMessage(LANG_INVALID_TITLE_ID, id); + SetSentErrorMessage(true); + return false; + } + + target->SetTitle(titleInfo,true); + + std::string tNameLink = GetNameLink(target); + + char const* targetName = target->GetName(); + char titleNameStr[80]; + snprintf(titleNameStr,80,titleInfo->name[GetSessionDbcLocale()],targetName); + + PSendSysMessage(LANG_TITLE_REMOVE_RES, id, titleNameStr, tNameLink.c_str()); + + if (!target->HasTitle(target->GetInt32Value(PLAYER_CHOSEN_TITLE))) + { + target->SetUInt32Value(PLAYER_CHOSEN_TITLE,0); + PSendSysMessage(LANG_CURRENT_TITLE_RESET, tNameLink.c_str()); + } + + return true; +} + +//Edit Player KnownTitles +bool ChatHandler::HandleTitlesSetMaskCommand(const char* args) +{ + if (!*args) + return false; + + uint64 titles = 0; + + sscanf((char*)args, UI64FMTD, &titles); + + Player *target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + uint64 titles2 = titles; + + for (uint32 i = 1; i < sCharTitlesStore.GetNumRows(); ++i) + if (CharTitlesEntry const* tEntry = sCharTitlesStore.LookupEntry(i)) + titles2 &= ~(uint64(1) << tEntry->bit_index); + + titles &= ~titles2; // remove not existed titles + + target->SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES, titles); + SendSysMessage(LANG_DONE); + + if (!target->HasTitle(target->GetInt32Value(PLAYER_CHOSEN_TITLE))) + { + target->SetUInt32Value(PLAYER_CHOSEN_TITLE,0); + PSendSysMessage(LANG_CURRENT_TITLE_RESET,GetNameLink(target).c_str()); + } + + return true; +} + +bool ChatHandler::HandleCharacterTitlesCommand(const char* args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + LocaleConstant loc = GetSessionDbcLocale(); + char const* targetName = target->GetName(); + char const* knownStr = GetTrinityString(LANG_KNOWN); + + // Search in CharTitles.dbc + for (uint32 id = 0; id < sCharTitlesStore.GetNumRows(); id++) + { + CharTitlesEntry const *titleInfo = sCharTitlesStore.LookupEntry(id); + if (titleInfo && target->HasTitle(titleInfo)) + { + std::string name = titleInfo->name[loc]; + if (name.empty()) + continue; + + char const* activeStr = target && target->GetUInt32Value(PLAYER_CHOSEN_TITLE) == titleInfo->bit_index + ? GetTrinityString(LANG_ACTIVE) + : ""; + + char titleNameStr[80]; + snprintf(titleNameStr,80,name.c_str(),targetName); + + // send title in "id (idx:idx) - [namedlink locale]" format + if (m_session) + PSendSysMessage(LANG_TITLE_LIST_CHAT,id,titleInfo->bit_index,id,titleNameStr,localeNames[loc],knownStr,activeStr); + else + PSendSysMessage(LANG_TITLE_LIST_CONSOLE,id,titleInfo->bit_index,name.c_str(),localeNames[loc],knownStr,activeStr); + } + } + return true; +} + +bool ChatHandler::HandleTitlesCurrentCommand(const char* args) +{ + // number or [name] Shift-click form |color|Htitle:title_id|h[name]|h|r + char* id_p = extractKeyFromLink((char*)args,"Htitle"); + if (!id_p) + return false; + + int32 id = atoi(id_p); + if (id <= 0) + { + PSendSysMessage(LANG_INVALID_TITLE_ID, id); + SetSentErrorMessage(true); + return false; + } + + Player * target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // check online security + if (HasLowerSecurity(target, 0)) + return false; + + CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(id); + if (!titleInfo) + { + PSendSysMessage(LANG_INVALID_TITLE_ID, id); + SetSentErrorMessage(true); + return false; + } + + std::string tNameLink = GetNameLink(target); + + target->SetTitle(titleInfo); // to be sure that title now known + target->SetUInt32Value(PLAYER_CHOSEN_TITLE,titleInfo->bit_index); + + PSendSysMessage(LANG_TITLE_CURRENT_RES, id, titleInfo->name[GetSessionDbcLocale()], tNameLink.c_str()); + + return true; +} diff --git a/src/server/game/Chat/Level3.cpp b/src/server/game/Chat/Level3.cpp new file mode 100644 index 00000000000..f7ced44922b --- /dev/null +++ b/src/server/game/Chat/Level3.cpp @@ -0,0 +1,7743 @@ +/* +* Copyright (C) 2005-2009 MaNGOS +* +* Copyright (C) 2008-2010 Trinity +* +* 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, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectMgr.h" +#include "AuctionHouseMgr.h" +#include "AccountMgr.h" +#include "PlayerDump.h" +#include "SpellMgr.h" +#include "Player.h" +#include "Opcodes.h" +#include "GameObject.h" +#include "Chat.h" +#include "Log.h" +#include "Guild.h" +#include "ObjectAccessor.h" +#include "MapManager.h" +#include "Language.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" +#include "Weather.h" +#include "PointMovementGenerator.h" +#include "TargetedMovementGenerator.h" +#include "SkillDiscovery.h" +#include "SkillExtraItems.h" +#include "SystemConfig.h" +#include "Config/ConfigEnv.h" +#include "Util.h" +#include "ItemEnchantmentMgr.h" +#include "BattleGroundMgr.h" +#include "InstanceSaveMgr.h" +#include "InstanceData.h" +#include "AuctionHouseBot.h" +#include "CreatureEventAIMgr.h" +#include "SpellAuraEffects.h" +#include "DBCEnums.h" +#include "ConditionMgr.h" + +bool ChatHandler::HandleAHBotOptionsCommand(const char *args) +{ + uint32 ahMapID = 0; + char * opt = strtok((char*)args, " "); + char * ahMapIdStr = strtok(NULL, " "); + if (ahMapIdStr) + { + ahMapID = (uint32) strtoul(ahMapIdStr, NULL, 0); + switch (ahMapID) + { + case 2: + case 6: + case 7: + break; + default: + opt = NULL; + break; + } + } + if (!opt) + { + PSendSysMessage("Syntax is: ahbotoptions $option $ahMapID (2, 6 or 7) $parameter"); + PSendSysMessage("Try ahbotoptions help to see a list of options."); + return false; + } + int l = strlen(opt); + + if (strncmp(opt,"help",l) == 0) + { + PSendSysMessage("AHBot commands:"); + PSendSysMessage("ahexpire"); + PSendSysMessage("minitems"); + PSendSysMessage("maxitems"); + //PSendSysMessage(""); + //PSendSysMessage(""); + PSendSysMessage("percentages"); + PSendSysMessage("minprice"); + PSendSysMessage("maxprice"); + PSendSysMessage("minbidprice"); + PSendSysMessage("maxbidprice"); + PSendSysMessage("maxstack"); + PSendSysMessage("buyerprice"); + PSendSysMessage("bidinterval"); + PSendSysMessage("bidsperinterval"); + return true; + } + else if (strncmp(opt,"ahexpire",l) == 0) + { + if (!ahMapIdStr) + { + PSendSysMessage("Syntax is: ahbotoptions ahexpire $ahMapID (2, 6 or 7)"); + return false; + } + auctionbot.Commands(0, ahMapID, NULL, NULL); + } + else if (strncmp(opt,"minitems",l) == 0) + { + char * param1 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1)) + { + PSendSysMessage("Syntax is: ahbotoptions minitems $ahMapID (2, 6 or 7) $minItems"); + return false; + } + auctionbot.Commands(1, ahMapID, NULL, param1); + } + else if (strncmp(opt,"maxitems",l) == 0) + { + char * param1 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1)) + { + PSendSysMessage("Syntax is: ahbotoptions maxitems $ahMapID (2, 6 or 7) $maxItems"); + return false; + } + auctionbot.Commands(2, ahMapID, NULL, param1); + } + else if (strncmp(opt,"mintime",l) == 0) + { + PSendSysMessage("ahbotoptions mintime has been deprecated"); + return false; + /* + char * param1 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1)) + { + PSendSysMessage("Syntax is: ahbotoptions mintime $ahMapID (2, 6 or 7) $mintime"); + return false; + } + auctionbot.Commands(3, ahMapID, NULL, param1); + */ + } + else if (strncmp(opt,"maxtime",l) == 0) + { + PSendSysMessage("ahbotoptions maxtime has been deprecated"); + return false; + /* + char * param1 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1)) + { + PSendSysMessage("Syntax is: ahbotoptions maxtime $ahMapID (2, 6 or 7) $maxtime"); + return false; + } + auctionbot.Commands(4, ahMapID, NULL, param1); + */ + } + else if (strncmp(opt,"percentages",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + char * param3 = strtok(NULL, " "); + char * param4 = strtok(NULL, " "); + char * param5 = strtok(NULL, " "); + char * param6 = strtok(NULL, " "); + char * param7 = strtok(NULL, " "); + char * param8 = strtok(NULL, " "); + char * param9 = strtok(NULL, " "); + char * param10 = strtok(NULL, " "); + char * param11 = strtok(NULL, " "); + char * param12 = strtok(NULL, " "); + char * param13 = strtok(NULL, " "); + char * param14 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param14)) + { + PSendSysMessage("Syntax is: ahbotoptions percentages $ahMapID (2, 6 or 7) $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14"); + PSendSysMessage("1 GreyTradeGoods 2 WhiteTradeGoods 3 GreenTradeGoods 4 BlueTradeGoods 5 PurpleTradeGoods"); + PSendSysMessage("6 OrangeTradeGoods 7 YellowTradeGoods 8 GreyItems 9 WhiteItems 10 GreenItems 11 BlueItems"); + PSendSysMessage("12 PurpleItems 13 OrangeItems 14 YellowItems"); + PSendSysMessage("The total must add up to 100%"); + return false; + } + uint32 greytg = (uint32) strtoul(param1, NULL, 0); + uint32 whitetg = (uint32) strtoul(param2, NULL, 0); + uint32 greentg = (uint32) strtoul(param3, NULL, 0); + uint32 bluetg = (uint32) strtoul(param3, NULL, 0); + uint32 purpletg = (uint32) strtoul(param5, NULL, 0); + uint32 orangetg = (uint32) strtoul(param6, NULL, 0); + uint32 yellowtg = (uint32) strtoul(param7, NULL, 0); + uint32 greyi = (uint32) strtoul(param8, NULL, 0); + uint32 whitei = (uint32) strtoul(param9, NULL, 0); + uint32 greeni = (uint32) strtoul(param10, NULL, 0); + uint32 bluei = (uint32) strtoul(param11, NULL, 0); + uint32 purplei = (uint32) strtoul(param12, NULL, 0); + uint32 orangei = (uint32) strtoul(param13, NULL, 0); + uint32 yellowi = (uint32) strtoul(param14, NULL, 0); + uint32 totalPercent = greytg + whitetg + greentg + bluetg + purpletg + orangetg + yellowtg + greyi + whitei + greeni + bluei + purplei + orangei + yellowi; + if ((totalPercent == 0) || (totalPercent != 100)) + { + PSendSysMessage("Syntax is: ahbotoptions percentages $ahMapID (2, 6 or 7) $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14"); + PSendSysMessage("1 GreyTradeGoods 2 WhiteTradeGoods 3 GreenTradeGoods 4 BlueTradeGoods 5 PurpleTradeGoods"); + PSendSysMessage("6 OrangeTradeGoods 7 YellowTradeGoods 8 GreyItems 9 WhiteItems 10 GreenItems 11 BlueItems"); + PSendSysMessage("12 PurpleItems 13 OrangeItems 14 YellowItems"); + PSendSysMessage("The total must add up to 100%"); + return false; + } + char param[100]; + param[0] = '\0'; + strcat(param, param1); + strcat(param, " "); + strcat(param, param2); + strcat(param, " "); + strcat(param, param3); + strcat(param, " "); + strcat(param, param4); + strcat(param, " "); + strcat(param, param5); + strcat(param, " "); + strcat(param, param6); + strcat(param, " "); + strcat(param, param7); + strcat(param, " "); + strcat(param, param8); + strcat(param, " "); + strcat(param, param9); + strcat(param, " "); + strcat(param, param10); + strcat(param, " "); + strcat(param, param11); + strcat(param, " "); + strcat(param, param12); + strcat(param, " "); + strcat(param, param13); + strcat(param, " "); + strcat(param, param14); + auctionbot.Commands(5, ahMapID, NULL, param); + } + else if (strncmp(opt,"minprice",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1) || (!param2)) + { + PSendSysMessage("Syntax is: ahbotoptions minprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + if (strncmp(param1,"grey",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_GREY, param2); + } + else if (strncmp(param1,"white",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_WHITE, param2); + } + else if (strncmp(param1,"green",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_GREEN, param2); + } + else if (strncmp(param1,"blue",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_BLUE, param2); + } + else if (strncmp(param1,"purple",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_PURPLE, param2); + } + else if (strncmp(param1,"orange",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_ORANGE, param2); + } + else if (strncmp(param1,"yellow",l) == 0) + { + auctionbot.Commands(6, ahMapID, AHB_YELLOW, param2); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions minprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + } + else if (strncmp(opt,"maxprice",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1) || (!param2)) + { + PSendSysMessage("Syntax is: ahbotoptions maxprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + if (strncmp(param1,"grey",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_GREY, param2); + } + else if (strncmp(param1,"white",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_WHITE, param2); + } + else if (strncmp(param1,"green",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_GREEN, param2); + } + else if (strncmp(param1,"blue",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_BLUE, param2); + } + else if (strncmp(param1,"purple",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_PURPLE, param2); + } + else if (strncmp(param1,"orange",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_ORANGE, param2); + } + else if (strncmp(param1,"yellow",l) == 0) + { + auctionbot.Commands(7, ahMapID, AHB_YELLOW, param2); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions maxprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + } + else if (strncmp(opt,"minbidprice",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1) || (!param2)) + { + PSendSysMessage("Syntax is: ahbotoptions minbidprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + uint32 minBidPrice = (uint32) strtoul(param2, NULL, 0); + if ((minBidPrice < 1) || (minBidPrice > 100)) + { + PSendSysMessage("The min bid price multiplier must be between 1 and 100"); + return false; + } + if (strncmp(param1,"grey",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_GREY, param2); + } + else if (strncmp(param1,"white",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_WHITE, param2); + } + else if (strncmp(param1,"green",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_GREEN, param2); + } + else if (strncmp(param1,"blue",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_BLUE, param2); + } + else if (strncmp(param1,"purple",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_PURPLE, param2); + } + else if (strncmp(param1,"orange",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_ORANGE, param2); + } + else if (strncmp(param1,"yellow",l) == 0) + { + auctionbot.Commands(8, ahMapID, AHB_YELLOW, param2); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions minbidprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + } + else if (strncmp(opt,"maxbidprice",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1) || (!param2)) + { + PSendSysMessage("Syntax is: ahbotoptions maxbidprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + uint32 maxBidPrice = (uint32) strtoul(param2, NULL, 0); + if ((maxBidPrice < 1) || (maxBidPrice > 100)) + { + PSendSysMessage("The max bid price multiplier must be between 1 and 100"); + return false; + } + if (strncmp(param1,"grey",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_GREY, param2); + } + else if (strncmp(param1,"white",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_WHITE, param2); + } + else if (strncmp(param1,"green",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_GREEN, param2); + } + else if (strncmp(param1,"blue",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_BLUE, param2); + } + else if (strncmp(param1,"purple",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_PURPLE, param2); + } + else if (strncmp(param1,"orange",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_ORANGE, param2); + } + else if (strncmp(param1,"yellow",l) == 0) + { + auctionbot.Commands(9, ahMapID, AHB_YELLOW, param2); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions max bidprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $price"); + return false; + } + } + else if (strncmp(opt,"maxstack",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1) || (!param2)) + { + PSendSysMessage("Syntax is: ahbotoptions maxstack $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $value"); + return false; + } + uint32 maxStack = (uint32) strtoul(param2, NULL, 0); + if (maxStack < 0) + { + PSendSysMessage("maxstack can't be a negative number."); + return false; + } + if (strncmp(param1,"grey",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_GREY, param2); + } + else if (strncmp(param1,"white",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_WHITE, param2); + } + else if (strncmp(param1,"green",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_GREEN, param2); + } + else if (strncmp(param1,"blue",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_BLUE, param2); + } + else if (strncmp(param1,"purple",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_PURPLE, param2); + } + else if (strncmp(param1,"orange",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_ORANGE, param2); + } + else if (strncmp(param1,"yellow",l) == 0) + { + auctionbot.Commands(10, ahMapID, AHB_YELLOW, param2); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions maxstack $ahMapID (2, 6 or 7) $color (grey, white, green, blue, purple, orange or yellow) $value"); + return false; + } + } + else if (strncmp(opt,"buyerprice",l) == 0) + { + char * param1 = strtok(NULL, " "); + char * param2 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1) || (!param2)) + { + PSendSysMessage("Syntax is: ahbotoptions buyerprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue or purple) $price"); + return false; + } + if (strncmp(param1,"grey",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_GREY, param2); + } + else if (strncmp(param1,"white",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_WHITE, param2); + } + else if (strncmp(param1,"green",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_GREEN, param2); + } + else if (strncmp(param1,"blue",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_BLUE, param2); + } + else if (strncmp(param1,"purple",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_PURPLE, param2); + } + else if (strncmp(param1,"orange",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_ORANGE, param2); + } + else if (strncmp(param1,"yellow",l) == 0) + { + auctionbot.Commands(11, ahMapID, AHB_YELLOW, param2); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions buyerprice $ahMapID (2, 6 or 7) $color (grey, white, green, blue or purple) $price"); + return false; + } + } + else if (strncmp(opt,"bidinterval",l) == 0) + { + char * param1 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1)) + { + PSendSysMessage("Syntax is: ahbotoptions bidinterval $ahMapID (2, 6 or 7) $interval(in minutes)"); + return false; + } + auctionbot.Commands(12, ahMapID, NULL, param1); + } + else if (strncmp(opt,"bidsperinterval",l) == 0) + { + char * param1 = strtok(NULL, " "); + if ((!ahMapIdStr) || (!param1)) + { + PSendSysMessage("Syntax is: ahbotoptions bidsperinterval $ahMapID (2, 6 or 7) $bids"); + return false; + } + auctionbot.Commands(13, ahMapID, NULL, param1); + } + else + { + PSendSysMessage("Syntax is: ahbotoptions $option $ahMapID (2, 6 or 7) $parameter"); + PSendSysMessage("Try ahbotoptions help to see a list of options."); + return false; + } + return true; +} + +//reload commands +bool ChatHandler::HandleReloadAllCommand(const char*) +{ + HandleReloadSkillFishingBaseLevelCommand(""); + + HandleReloadAllAchievementCommand(""); + HandleReloadAllAreaCommand(""); + HandleReloadAllEventAICommand(""); + HandleReloadAllLootCommand(""); + HandleReloadAllNpcCommand(""); + HandleReloadAllQuestCommand(""); + HandleReloadAllSpellCommand(""); + HandleReloadAllItemCommand(""); + HandleReloadAllLocalesCommand(""); + + HandleReloadAccessRequirementCommand(""); + HandleReloadMailLevelRewardCommand(""); + HandleReloadCommandCommand(""); + HandleReloadReservedNameCommand(""); + HandleReloadTrinityStringCommand(""); + HandleReloadGameTeleCommand(""); + + HandleReloadAutobroadcastCommand(""); + return true; +} + +bool ChatHandler::HandleReloadAllAchievementCommand(const char*) +{ + HandleReloadAchievementCriteriaDataCommand(""); + HandleReloadAchievementRewardCommand(""); + return true; +} + +bool ChatHandler::HandleReloadAllAreaCommand(const char*) +{ + //HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand + HandleReloadAreaTriggerTeleportCommand(""); + HandleReloadAreaTriggerTavernCommand(""); + HandleReloadGameGraveyardZoneCommand(""); + return true; +} + +bool ChatHandler::HandleReloadAllLootCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables..."); + LoadLootTables(); + SendGlobalGMSysMessage("DB tables `*_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/) +{ + HandleReloadNpcGossipCommand("a"); + HandleReloadNpcTrainerCommand("a"); + HandleReloadNpcVendorCommand("a"); + HandleReloadPointsOfInterestCommand("a"); + HandleReloadSpellClickSpellsCommand("a"); + return true; +} + +bool ChatHandler::HandleReloadAllQuestCommand(const char* /*args*/) +{ + HandleReloadQuestAreaTriggersCommand("a"); + HandleReloadQuestTemplateCommand("a"); + + sLog.outString("Re-Loading Quests Relations..."); + objmgr.LoadQuestRelations(); + SendGlobalGMSysMessage("DB tables `*_questrelation` and `*_involvedrelation` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAllScriptsCommand(const char*) +{ + if (sWorld.IsScriptScheduled()) + { + PSendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + sLog.outString("Re-Loading Scripts..."); + HandleReloadGameObjectScriptsCommand("a"); + HandleReloadEventScriptsCommand("a"); + HandleReloadQuestEndScriptsCommand("a"); + HandleReloadQuestStartScriptsCommand("a"); + HandleReloadSpellScriptsCommand("a"); + SendGlobalGMSysMessage("DB tables `*_scripts` reloaded."); + HandleReloadDbScriptStringCommand("a"); + HandleReloadWpScriptsCommand("a"); + return true; +} + +bool ChatHandler::HandleReloadAllEventAICommand(const char*) +{ + HandleReloadEventAITextsCommand("a"); + HandleReloadEventAISummonsCommand("a"); + HandleReloadEventAIScriptsCommand("a"); + return true; +} + +bool ChatHandler::HandleReloadAllSpellCommand(const char*) +{ + HandleReloadSkillDiscoveryTemplateCommand("a"); + HandleReloadSkillExtraItemTemplateCommand("a"); + HandleReloadSpellRequiredCommand("a"); + HandleReloadSpellAreaCommand("a"); + HandleReloadSpellGroupsCommand("a"); + HandleReloadSpellLearnSpellCommand("a"); + HandleReloadSpellLinkedSpellCommand("a"); + HandleReloadSpellProcEventCommand("a"); + HandleReloadSpellBonusesCommand("a"); + HandleReloadSpellTargetPositionCommand("a"); + HandleReloadSpellThreatsCommand("a"); + HandleReloadSpellGroupStackRulesCommand("a"); + HandleReloadSpellPetAurasCommand("a"); + HandleReloadSpellDisabledCommand("a"); + return true; +} + +bool ChatHandler::HandleReloadAllItemCommand(const char*) +{ + HandleReloadPageTextsCommand("a"); + HandleReloadItemEnchantementsCommand("a"); + return true; +} + +bool ChatHandler::HandleReloadAllLocalesCommand(const char* /*args*/) +{ + HandleReloadLocalesAchievementRewardCommand("a"); + HandleReloadLocalesCreatureCommand("a"); + HandleReloadLocalesGameobjectCommand("a"); + HandleReloadLocalesItemCommand("a"); + HandleReloadLocalesNpcTextCommand("a"); + HandleReloadLocalesPageTextCommand("a"); + HandleReloadLocalesPointsOfInterestCommand("a"); + HandleReloadLocalesQuestCommand("a"); + return true; +} + +bool ChatHandler::HandleReloadConfigCommand(const char* /*args*/) +{ + sLog.outString("Re-Loading config settings..."); + sWorld.LoadConfigSettings(true); + MapManager::Instance().InitializeVisibilityDistanceInfo(); + SendGlobalGMSysMessage("World config settings reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAccessRequirementCommand(const char*) +{ + sLog.outString("Re-Loading Access Requirement definitions..."); + objmgr.LoadAccessRequirements(); + SendGlobalGMSysMessage("DB table `access_requirement` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAchievementCriteriaDataCommand(const char*) +{ + sLog.outString("Re-Loading Additional Achievement Criteria Data..."); + achievementmgr.LoadAchievementCriteriaData(); + SendGlobalGMSysMessage("DB table `achievement_criteria_data` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAchievementRewardCommand(const char*) +{ + sLog.outString("Re-Loading Achievement Reward Data..."); + achievementmgr.LoadRewards(); + SendGlobalGMSysMessage("DB table `achievement_reward` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*) +{ + sLog.outString("Re-Loading Tavern Area Triggers..."); + objmgr.LoadTavernAreaTriggers(); + SendGlobalGMSysMessage("DB table `areatrigger_tavern` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAreaTriggerTeleportCommand(const char*) +{ + sLog.outString("Re-Loading AreaTrigger teleport definitions..."); + objmgr.LoadAreaTriggerTeleports(); + SendGlobalGMSysMessage("DB table `areatrigger_teleport` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAutobroadcastCommand(const char*) +{ + sLog.outString("Re-Loading Autobroadcast..."); + sWorld.LoadAutobroadcasts(); + SendGlobalGMSysMessage("DB table `autobroadcast` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadCommandCommand(const char*) +{ + load_command_table = true; + SendGlobalGMSysMessage("DB table `command` will be reloaded at next chat command use."); + return true; +} + +bool ChatHandler::HandleReloadCreatureTemplateCommand(const char* args) +{ + if (!*args) + return false; + + uint32 entry = (uint32) atoi((char*)args); + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT difficulty_entry_1,difficulty_entry_2,difficulty_entry_3,KillCredit1,KillCredit2,modelid1,modelid2,modelid3,modelid4,name,subname,IconName,gossip_menu_id,minlevel,maxlevel,exp,faction_A,faction_H,npcflag,speed_walk,speed_run,scale,rank,mindmg,maxdmg,dmgschool,attackpower,dmg_multiplier,baseattacktime,rangeattacktime,unit_class,unit_flags,dynamicflags,family,trainer_type,trainer_spell,trainer_class,trainer_race,minrangedmg,maxrangedmg,rangedattackpower,type,type_flags,lootid,pickpocketloot,skinloot,resistance1,resistance2,resistance3,resistance4,resistance5,resistance6,spell1,spell2,spell3,spell4,spell5,spell6,spell7,spell8,PetSpellDataId,VehicleId,mingold,maxgold,AIName,MovementType,InhabitType,Health_mod,Mana_mod,Armor_mod,RacialLeader,questItem1,questItem2,questItem3,questItem4,questItem5,questItem6,movementId,RegenHealth,equipment_id,mechanic_immune_mask,flags_extra,ScriptName FROM creature_template WHERE entry = %u", entry); + if (!result) + { + PSendSysMessage(LANG_COMMAND_CREATURETEMPLATE_NOTFOUND, entry); + SetSentErrorMessage(true); + return false; + } + + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(entry); + if (!cInfo) + { + PSendSysMessage(LANG_COMMAND_CREATURESTORAGE_NOTFOUND, entry); + SetSentErrorMessage(true); + return false; + } + + sLog.outString("Reloading creature template entry %u", entry); + + Field *fields = result->Fetch(); + + const_cast(cInfo)->DifficultyEntry[0] = fields[0].GetUInt32(); + const_cast(cInfo)->DifficultyEntry[1] = fields[1].GetUInt32(); + const_cast(cInfo)->DifficultyEntry[2] = fields[2].GetUInt32(); + const_cast(cInfo)->KillCredit[0] = fields[3].GetUInt32(); + const_cast(cInfo)->KillCredit[1] = fields[4].GetUInt32(); + const_cast(cInfo)->Modelid1 = fields[5].GetUInt32(); + const_cast(cInfo)->Modelid2 = fields[6].GetUInt32(); + const_cast(cInfo)->Modelid3 = fields[7].GetUInt32(); + const_cast(cInfo)->Modelid4 = fields[8].GetUInt32(); + size_t len = 0; + if (const char* temp = fields[9].GetString()) + { + if (cInfo->Name) + delete cInfo->Name; + len = strlen(temp)+1; + const_cast(cInfo)->Name = new char[len]; + strncpy(cInfo->Name, temp, len); + } + if (const char* temp = fields[10].GetString()) + { + if (cInfo->SubName) + delete cInfo->SubName; + len = strlen(temp)+1; + const_cast(cInfo)->SubName = new char[len]; + strncpy(cInfo->SubName, temp, len); + } + if (const char* temp = fields[11].GetString()) + { + if (cInfo->IconName) + delete cInfo->IconName; + len = strlen(temp)+1; + const_cast(cInfo)->IconName = new char[len]; + strncpy(cInfo->IconName, temp, len); + } + const_cast(cInfo)->GossipMenuId = fields[12].GetUInt32(); + const_cast(cInfo)->minlevel = fields[13].GetUInt32(); + const_cast(cInfo)->maxlevel = fields[14].GetUInt32(); + const_cast(cInfo)->expansion = fields[15].GetUInt32(); + const_cast(cInfo)->faction_A = fields[16].GetUInt32(); + const_cast(cInfo)->faction_H = fields[17].GetUInt32(); + const_cast(cInfo)->npcflag = fields[18].GetUInt32(); + const_cast(cInfo)->speed_walk = fields[19].GetFloat(); + const_cast(cInfo)->speed_run = fields[20].GetFloat(); + const_cast(cInfo)->scale = fields[21].GetFloat(); + const_cast(cInfo)->rank = fields[22].GetUInt32(); + const_cast(cInfo)->mindmg = fields[23].GetFloat(); + const_cast(cInfo)->maxdmg = fields[24].GetFloat(); + const_cast(cInfo)->dmgschool = fields[25].GetUInt32(); + const_cast(cInfo)->attackpower = fields[26].GetUInt32(); + const_cast(cInfo)->dmg_multiplier = fields[27].GetFloat(); + const_cast(cInfo)->baseattacktime = fields[28].GetUInt32(); + const_cast(cInfo)->rangeattacktime = fields[29].GetUInt32(); + const_cast(cInfo)->unit_class = fields[30].GetUInt32(); + const_cast(cInfo)->unit_flags = fields[31].GetUInt32(); + const_cast(cInfo)->dynamicflags = fields[32].GetUInt32(); + const_cast(cInfo)->family = fields[33].GetUInt32(); + const_cast(cInfo)->trainer_type = fields[34].GetUInt32(); + const_cast(cInfo)->trainer_spell = fields[35].GetUInt32(); + const_cast(cInfo)->trainer_class = fields[36].GetUInt32(); + const_cast(cInfo)->trainer_race = fields[37].GetUInt32(); + const_cast(cInfo)->minrangedmg = fields[38].GetFloat(); + const_cast(cInfo)->maxrangedmg = fields[39].GetFloat(); + const_cast(cInfo)->rangedattackpower = fields[40].GetUInt32(); + const_cast(cInfo)->type = fields[41].GetUInt32(); + const_cast(cInfo)->type_flags = fields[42].GetUInt32(); + const_cast(cInfo)->lootid = fields[43].GetUInt32(); + const_cast(cInfo)->pickpocketLootId = fields[44].GetUInt32(); + const_cast(cInfo)->SkinLootId = fields[45].GetUInt32(); + const_cast(cInfo)->resistance1 = fields[46].GetUInt32(); + const_cast(cInfo)->resistance2 = fields[47].GetUInt32(); + const_cast(cInfo)->resistance3 = fields[48].GetUInt32(); + const_cast(cInfo)->resistance4 = fields[49].GetUInt32(); + const_cast(cInfo)->resistance5 = fields[50].GetUInt32(); + const_cast(cInfo)->resistance6 = fields[51].GetUInt32(); + const_cast(cInfo)->spells[0] = fields[52].GetUInt32(); + const_cast(cInfo)->spells[1] = fields[53].GetUInt32(); + const_cast(cInfo)->spells[2] = fields[54].GetUInt32(); + const_cast(cInfo)->spells[3] = fields[55].GetUInt32(); + const_cast(cInfo)->spells[4] = fields[56].GetUInt32(); + const_cast(cInfo)->spells[5] = fields[57].GetUInt32(); + const_cast(cInfo)->spells[6] = fields[58].GetUInt32(); + const_cast(cInfo)->spells[7] = fields[59].GetUInt32(); + const_cast(cInfo)->PetSpellDataId = fields[60].GetUInt32(); + const_cast(cInfo)->VehicleId = fields[61].GetUInt32(); + const_cast(cInfo)->mingold = fields[62].GetUInt32(); + const_cast(cInfo)->maxgold = fields[63].GetUInt32(); + if (const char* temp = fields[64].GetString()) + { + if (cInfo->AIName) + delete cInfo->AIName; + len = strlen(temp)+1; + const_cast(cInfo)->AIName = new char[len]; + strncpy(const_cast(cInfo->AIName), temp, len); + } + const_cast(cInfo)->MovementType = fields[65].GetUInt32(); + const_cast(cInfo)->InhabitType = fields[66].GetUInt32(); + const_cast(cInfo)->ModHealth = fields[67].GetFloat(); + const_cast(cInfo)->ModMana = fields[68].GetFloat(); + const_cast(cInfo)->ModArmor = fields[69].GetFloat(); + const_cast(cInfo)->RacialLeader = fields[70].GetBool(); + const_cast(cInfo)->questItems[0] = fields[71].GetUInt32(); + const_cast(cInfo)->questItems[1] = fields[72].GetUInt32(); + const_cast(cInfo)->questItems[2] = fields[73].GetUInt32(); + const_cast(cInfo)->questItems[3] = fields[74].GetUInt32(); + const_cast(cInfo)->questItems[4] = fields[75].GetUInt32(); + const_cast(cInfo)->questItems[5] = fields[76].GetUInt32(); + const_cast(cInfo)->movementId = fields[77].GetUInt32(); + const_cast(cInfo)->RegenHealth = fields[78].GetBool(); + const_cast(cInfo)->equipmentId = fields[79].GetUInt32(); + const_cast(cInfo)->MechanicImmuneMask = fields[80].GetUInt32(); + const_cast(cInfo)->flags_extra = fields[81].GetUInt32(); + const_cast(cInfo)->ScriptID = objmgr.GetScriptId(fields[82].GetString()); + + objmgr.CheckCreatureTemplate(cInfo); + + SendGlobalGMSysMessage("Creature template reloaded."); + return true; +} + +bool ChatHandler::HandleReloadCreatureQuestRelationsCommand(const char*) +{ + sLog.outString("Loading Quests Relations... (`creature_questrelation`)"); + objmgr.LoadCreatureQuestRelations(); + SendGlobalGMSysMessage("DB table `creature_questrelation` (creature quest givers) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadCreatureLinkedRespawnCommand(const char * /*args*/) +{ + sLog.outString("Loading Linked Respawns... (`creature_linked_respawn`)"); + objmgr.LoadCreatureLinkedRespawn(); + SendGlobalGMSysMessage("DB table `creature_linked_respawn` (creature linked respawns) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*) +{ + sLog.outString("Loading Quests Relations... (`creature_involvedrelation`)"); + objmgr.LoadCreatureInvolvedRelations(); + SendGlobalGMSysMessage("DB table `creature_involvedrelation` (creature quest takers) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadGossipMenuCommand(const char*) +{ + sLog.outString("Re-Loading `gossip_menu` Table!"); + objmgr.LoadGossipMenu(); + SendGlobalGMSysMessage("DB table `gossip_menu` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadGossipMenuOptionCommand(const char*) +{ + sLog.outString("Re-Loading `gossip_menu_option` Table!"); + objmgr.LoadGossipMenuItems(); + SendGlobalGMSysMessage("DB table `gossip_menu_option` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*) +{ + sLog.outString("Loading Quests Relations... (`gameobject_questrelation`)"); + objmgr.LoadGameobjectQuestRelations(); + SendGlobalGMSysMessage("DB table `gameobject_questrelation` (gameobject quest givers) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadGOQuestInvRelationsCommand(const char*) +{ + sLog.outString("Loading Quests Relations... (`gameobject_involvedrelation`)"); + objmgr.LoadGameobjectInvolvedRelations(); + SendGlobalGMSysMessage("DB table `gameobject_involvedrelation` (gameobject quest takers) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadQuestAreaTriggersCommand(const char*) +{ + sLog.outString("Re-Loading Quest Area Triggers..."); + objmgr.LoadQuestAreaTriggers(); + SendGlobalGMSysMessage("DB table `areatrigger_involvedrelation` (quest area triggers) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadQuestTemplateCommand(const char*) +{ + sLog.outString("Re-Loading Quest Templates..."); + objmgr.LoadQuests(); + SendGlobalGMSysMessage("DB table `quest_template` (quest definitions) reloaded."); + + /// dependent also from `gameobject` but this table not reloaded anyway + sLog.outString("Re-Loading GameObjects for quests..."); + objmgr.LoadGameObjectForQuests(); + SendGlobalGMSysMessage("Data GameObjects for quests reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`creature_loot_template`)"); + LoadLootTemplates_Creature(); + LootTemplates_Creature.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `creature_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`disenchant_loot_template`)"); + LoadLootTemplates_Disenchant(); + LootTemplates_Disenchant.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `disenchant_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`fishing_loot_template`)"); + LoadLootTemplates_Fishing(); + LootTemplates_Fishing.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `fishing_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`gameobject_loot_template`)"); + LoadLootTemplates_Gameobject(); + LootTemplates_Gameobject.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `gameobject_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`item_loot_template`)"); + LoadLootTemplates_Item(); + LootTemplates_Item.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `item_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesMillingCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`milling_loot_template`)"); + LoadLootTemplates_Milling(); + LootTemplates_Milling.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `milling_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`pickpocketing_loot_template`)"); + LoadLootTemplates_Pickpocketing(); + LootTemplates_Pickpocketing.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `pickpocketing_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`prospecting_loot_template`)"); + LoadLootTemplates_Prospecting(); + LootTemplates_Prospecting.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `prospecting_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesMailCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`mail_loot_template`)"); + LoadLootTemplates_Mail(); + LootTemplates_Mail.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `mail_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`reference_loot_template`)"); + LoadLootTemplates_Reference(); + SendGlobalGMSysMessage("DB table `reference_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`skinning_loot_template`)"); + LoadLootTemplates_Skinning(); + LootTemplates_Skinning.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `skinning_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadLootTemplatesSpellCommand(const char*) +{ + sLog.outString("Re-Loading Loot Tables... (`spell_loot_template`)"); + LoadLootTemplates_Spell(); + LootTemplates_Spell.CheckLootRefs(); + SendGlobalGMSysMessage("DB table `spell_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); + return true; +} + +bool ChatHandler::HandleReloadTrinityStringCommand(const char*) +{ + sLog.outString("Re-Loading trinity_string Table!"); + objmgr.LoadTrinityStrings(); + SendGlobalGMSysMessage("DB table `trinity_string` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadNpcGossipCommand(const char*) +{ + sLog.outString("Re-Loading `npc_gossip` Table!"); + objmgr.LoadNpcTextId(); + SendGlobalGMSysMessage("DB table `npc_gossip` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadNpcTrainerCommand(const char*) +{ + sLog.outString("Re-Loading `npc_trainer` Table!"); + objmgr.LoadTrainerSpell(); + SendGlobalGMSysMessage("DB table `npc_trainer` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadNpcVendorCommand(const char*) +{ + sLog.outString("Re-Loading `npc_vendor` Table!"); + objmgr.LoadVendors(); + SendGlobalGMSysMessage("DB table `npc_vendor` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadPointsOfInterestCommand(const char*) +{ + sLog.outString("Re-Loading `points_of_interest` Table!"); + objmgr.LoadPointsOfInterest(); + SendGlobalGMSysMessage("DB table `points_of_interest` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellClickSpellsCommand(const char*) +{ + sLog.outString("Re-Loading `npc_spellclick_spells` Table!"); + objmgr.LoadNPCSpellClickSpells(); + SendGlobalGMSysMessage("DB table `npc_spellclick_spells` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadReservedNameCommand(const char*) +{ + sLog.outString("Loading ReservedNames... (`reserved_name`)"); + objmgr.LoadReservedPlayersNames(); + SendGlobalGMSysMessage("DB table `reserved_name` (player reserved names) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSkillDiscoveryTemplateCommand(const char* /*args*/) +{ + sLog.outString("Re-Loading Skill Discovery Table..."); + LoadSkillDiscoveryTable(); + SendGlobalGMSysMessage("DB table `skill_discovery_template` (recipes discovered at crafting) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSkillExtraItemTemplateCommand(const char* /*args*/) +{ + sLog.outString("Re-Loading Skill Extra Item Table..."); + LoadSkillExtraItemTable(); + SendGlobalGMSysMessage("DB table `skill_extra_item_template` (extra item creation when crafting) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSkillFishingBaseLevelCommand(const char* /*args*/) +{ + sLog.outString("Re-Loading Skill Fishing base level requirements..."); + objmgr.LoadFishingBaseSkillLevel(); + SendGlobalGMSysMessage("DB table `skill_fishing_base_level` (fishing base level for zone/subzone) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellAreaCommand(const char*) +{ + sLog.outString("Re-Loading SpellArea Data..."); + spellmgr.LoadSpellAreas(); + SendGlobalGMSysMessage("DB table `spell_area` (spell dependences from area/quest/auras state) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellRequiredCommand(const char*) +{ + sLog.outString("Re-Loading Spell Required Data... "); + spellmgr.LoadSpellRequired(); + SendGlobalGMSysMessage("DB table `spell_required` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellGroupsCommand(const char*) +{ + sLog.outString("Re-Loading Spell Groups..."); + spellmgr.LoadSpellGroups(); + SendGlobalGMSysMessage("DB table `spell_group` (spell groups) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellLearnSpellCommand(const char*) +{ + sLog.outString("Re-Loading Spell Learn Spells..."); + spellmgr.LoadSpellLearnSpells(); + SendGlobalGMSysMessage("DB table `spell_learn_spell` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellLinkedSpellCommand(const char*) +{ + sLog.outString("Re-Loading Spell Linked Spells..."); + spellmgr.LoadSpellLinked(); + SendGlobalGMSysMessage("DB table `spell_linked_spell` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellProcEventCommand(const char*) +{ + sLog.outString("Re-Loading Spell Proc Event conditions..."); + spellmgr.LoadSpellProcEvents(); + SendGlobalGMSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellBonusesCommand(const char*) +{ + sLog.outString("Re-Loading Spell Bonus Data..."); + spellmgr.LoadSpellBonusess(); + SendGlobalGMSysMessage("DB table `spell_bonus_data` (spell damage/healing coefficients) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellTargetPositionCommand(const char*) +{ + sLog.outString("Re-Loading Spell target coordinates..."); + spellmgr.LoadSpellTargetPositions(); + SendGlobalGMSysMessage("DB table `spell_target_position` (destination coordinates for spell targets) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellThreatsCommand(const char*) +{ + sLog.outString("Re-Loading Aggro Spells Definitions..."); + spellmgr.LoadSpellThreats(); + SendGlobalGMSysMessage("DB table `spell_threat` (spell aggro definitions) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellGroupStackRulesCommand(const char*) +{ + sLog.outString("Re-Loading Spell Group Stack Rules..."); + spellmgr.LoadSpellGroupStackRules(); + SendGlobalGMSysMessage("DB table `spell_group_stack_rules` (spell stacking definitions) reloaded."); + return true; +} + +bool ChatHandler::HandleReloadSpellPetAurasCommand(const char*) +{ + sLog.outString("Re-Loading Spell pet auras..."); + spellmgr.LoadSpellPetAuras(); + SendGlobalGMSysMessage("DB table `spell_pet_auras` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadPageTextsCommand(const char*) +{ + sLog.outString("Re-Loading Page Texts..."); + objmgr.LoadPageTexts(); + SendGlobalGMSysMessage("DB table `page_texts` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*) +{ + sLog.outString("Re-Loading Item Random Enchantments Table..."); + LoadRandomEnchantmentsTable(); + SendGlobalGMSysMessage("DB table `item_enchantment_template` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg) +{ + if (sWorld.IsScriptScheduled()) + { + SendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + if (*arg != 'a') + sLog.outString("Re-Loading Scripts from `gameobject_scripts`..."); + + objmgr.LoadGameObjectScripts(); + + if (*arg != 'a') + SendGlobalGMSysMessage("DB table `gameobject_scripts` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadEventScriptsCommand(const char* arg) +{ + if (sWorld.IsScriptScheduled()) + { + SendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + if (*arg != 'a') + sLog.outString("Re-Loading Scripts from `event_scripts`..."); + + objmgr.LoadEventScripts(); + + if (*arg != 'a') + SendGlobalGMSysMessage("DB table `event_scripts` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadWpScriptsCommand(const char* arg) +{ + if (sWorld.IsScriptScheduled()) + { + SendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + if (*arg != 'a') + sLog.outString("Re-Loading Scripts from `waypoint_scripts`..."); + + objmgr.LoadWaypointScripts(); + + if (*arg != 'a') + SendGlobalGMSysMessage("DB table `waypoint_scripts` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadEventAITextsCommand(const char* /*args*/) +{ + + sLog.outString("Re-Loading Texts from `creature_ai_texts`..."); + CreatureEAI_Mgr.LoadCreatureEventAI_Texts(); + SendGlobalGMSysMessage("DB table `creature_ai_texts` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadEventAISummonsCommand(const char* /*args*/) +{ + sLog.outString("Re-Loading Summons from `creature_ai_summons`..."); + CreatureEAI_Mgr.LoadCreatureEventAI_Summons(); + SendGlobalGMSysMessage("DB table `creature_ai_summons` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadEventAIScriptsCommand(const char* /*args*/) +{ + sLog.outString("Re-Loading Scripts from `creature_ai_scripts`..."); + CreatureEAI_Mgr.LoadCreatureEventAI_Scripts(); + SendGlobalGMSysMessage("DB table `creature_ai_scripts` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadQuestEndScriptsCommand(const char* arg) +{ + if (sWorld.IsScriptScheduled()) + { + SendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + if (*arg != 'a') + sLog.outString("Re-Loading Scripts from `quest_end_scripts`..."); + + objmgr.LoadQuestEndScripts(); + + if (*arg != 'a') + SendGlobalGMSysMessage("DB table `quest_end_scripts` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadQuestStartScriptsCommand(const char* arg) +{ + if (sWorld.IsScriptScheduled()) + { + SendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + if (*arg != 'a') + sLog.outString("Re-Loading Scripts from `quest_start_scripts`..."); + + objmgr.LoadQuestStartScripts(); + + if (*arg != 'a') + SendGlobalGMSysMessage("DB table `quest_start_scripts` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadSpellScriptsCommand(const char* arg) +{ + if (sWorld.IsScriptScheduled()) + { + SendSysMessage("DB scripts used currently, please attempt reload later."); + SetSentErrorMessage(true); + return false; + } + + if (*arg != 'a') + sLog.outString("Re-Loading Scripts from `spell_scripts`..."); + + objmgr.LoadSpellScripts(); + + if (*arg != 'a') + SendGlobalGMSysMessage("DB table `spell_scripts` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadDbScriptStringCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Script strings from `db_script_string`..."); + objmgr.LoadDbScriptStrings(); + SendGlobalGMSysMessage("DB table `db_script_string` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadGameGraveyardZoneCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Graveyard-zone links..."); + + objmgr.LoadGraveyardZones(); + + SendGlobalGMSysMessage("DB table `game_graveyard_zone` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadGameTeleCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Game Tele coordinates..."); + + objmgr.LoadGameTele(); + + SendGlobalGMSysMessage("DB table `game_tele` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadSpellDisabledCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading spell disabled table..."); + + objmgr.LoadSpellDisabledEntrys(); + + SendGlobalGMSysMessage("DB table `spell_disabled` reloaded."); + + return true; +} + +bool ChatHandler::HandleReloadLocalesAchievementRewardCommand(const char*) +{ + sLog.outString("Re-Loading Locales Achievement Reward Data..."); + achievementmgr.LoadRewardLocales(); + SendGlobalGMSysMessage("DB table `locales_achievement_reward` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesCreatureCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales Creature ..."); + objmgr.LoadCreatureLocales(); + SendGlobalGMSysMessage("DB table `locales_creature` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesGameobjectCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales Gameobject ... "); + objmgr.LoadGameObjectLocales(); + SendGlobalGMSysMessage("DB table `locales_gameobject` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesItemCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales Item ... "); + objmgr.LoadItemLocales(); + SendGlobalGMSysMessage("DB table `locales_item` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesNpcTextCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales NPC Text ... "); + objmgr.LoadNpcTextLocales(); + SendGlobalGMSysMessage("DB table `locales_npc_text` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesPageTextCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales Page Text ... "); + objmgr.LoadPageTextLocales(); + SendGlobalGMSysMessage("DB table `locales_page_text` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesPointsOfInterestCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales Points Of Interest ... "); + objmgr.LoadPointOfInterestLocales(); + SendGlobalGMSysMessage("DB table `locales_points_of_interest` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadLocalesQuestCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Locales Quest ... "); + objmgr.LoadQuestLocales(); + SendGlobalGMSysMessage("DB table `locales_quest` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadMailLevelRewardCommand(const char* /*arg*/) +{ + sLog.outString("Re-Loading Player level dependent mail rewards..."); + objmgr.LoadMailLevelRewards(); + SendGlobalGMSysMessage("DB table `mail_level_reward` reloaded."); + return true; +} + +bool ChatHandler::HandleReloadAuctionsCommand(const char * /*args*/) +{ + ///- Reload dynamic data tables from the database + sLog.outString("Re-Loading Auctions..."); + auctionmgr.LoadAuctionItems(); + auctionmgr.LoadAuctions(); + SendGlobalGMSysMessage("Auctions reloaded."); + return true; +} + +bool ChatHandler::HandleReloadConditions(const char* args) +{ + sLog.outString("Re-Loading Conditions..."); + sConditionMgr.LoadConditions(true); + SendGlobalGMSysMessage("Conditions reloaded."); + return true; +} + +bool ChatHandler::HandleAccountSetGmLevelCommand(const char *args) +{ + if (!*args) + return false; + + std::string targetAccountName; + uint32 targetAccountId = 0; + uint32 targetSecurity = 0; + uint32 gm = 0; + char* arg1 = strtok((char*)args, " "); + char* arg2 = strtok(NULL, " "); + char* arg3 = strtok(NULL, " "); + bool isAccountNameGiven = true; + + if (arg1 && !arg3) + { + if (!getSelectedPlayer()) + return false; + isAccountNameGiven = false; + } + + // Check for second parameter + if (!isAccountNameGiven && !arg2) + return false; + + // Check for account + if (isAccountNameGiven) + { + targetAccountName = arg1; + if (!AccountMgr::normalizeString(targetAccountName)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,targetAccountName.c_str()); + SetSentErrorMessage(true); + return false; + } + } + + // Check for invalid specified GM level. + gm = (isAccountNameGiven) ? atoi(arg2) : atoi(arg1); + if (gm < SEC_PLAYER) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + // m_session == NULL only for console + targetAccountId = (isAccountNameGiven) ? accmgr.GetId(targetAccountName) : getSelectedPlayer()->GetSession()->GetAccountId(); + int32 gmRealmID = (isAccountNameGiven) ? atoi(arg3) : atoi(arg2); + uint32 plSecurity = m_session ? accmgr.GetSecurity(m_session->GetAccountId(), gmRealmID) : SEC_CONSOLE; + + // can set security level only for target with less security and to less security that we have + // This is also reject self apply in fact + targetSecurity = accmgr.GetSecurity(targetAccountId, gmRealmID); + if (targetSecurity >= plSecurity || gm >= plSecurity) + { + SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); + SetSentErrorMessage(true); + return false; + } + + // Check and abort if the target gm has a higher rank on one of the realms and the new realm is -1 + if (gmRealmID == -1) + { + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT * FROM account_access WHERE id = '%u' AND gmlevel > '%d'", targetAccountId, gm); + if (result) + { + SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); + SetSentErrorMessage(true); + return false; + } + } + + // Check if provided realmID has a negative value other than -1 + if (gmRealmID < -1) + { + SendSysMessage(LANG_INVALID_REALMID); + SetSentErrorMessage(true); + return false; + } + + // If gmRealmID is -1, delete all values for the account id, else, insert values for the specific realmID + if (gmRealmID == -1) + LoginDatabase.PExecute("DELETE FROM account_access WHERE id = '%u'", targetAccountId); + else + LoginDatabase.PExecute("DELETE FROM account_access WHERE id = '%u' AND (RealmID = '%d' OR RealmID = '-1')", targetAccountId, realmID); + + if (gm != 0) + LoginDatabase.PExecute("INSERT INTO account_access VALUES ('%u','%d','%d')", targetAccountId, gm, realmID); + PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm); + return true; +} + +/// Set password for account +bool ChatHandler::HandleAccountSetPasswordCommand(const char *args) +{ + if (!*args) + return false; + + ///- Get the command line arguments + char *szAccount = strtok ((char*)args," "); + char *szPassword1 = strtok (NULL," "); + char *szPassword2 = strtok (NULL," "); + + if (!szAccount||!szPassword1 || !szPassword2) + return false; + + std::string account_name = szAccount; + if (!AccountMgr::normalizeString(account_name)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + uint32 targetAccountId = accmgr.GetId(account_name); + if (!targetAccountId) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + /// can set password only for target with less security + /// This is also reject self apply in fact + if (HasLowerSecurityAccount (NULL,targetAccountId,true)) + return false; + + if (strcmp(szPassword1,szPassword2)) + { + SendSysMessage (LANG_NEW_PASSWORDS_NOT_MATCH); + SetSentErrorMessage (true); + return false; + } + + AccountOpResult result = accmgr.ChangePassword(targetAccountId, szPassword1); + + switch (result) + { + case AOR_OK: + SendSysMessage(LANG_COMMAND_PASSWORD); + break; + case AOR_NAME_NOT_EXIST: + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + case AOR_PASS_TOO_LONG: + SendSysMessage(LANG_PASSWORD_TOO_LONG); + SetSentErrorMessage(true); + return false; + default: + SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/) +{ + Player* SelectedPlayer = getSelectedPlayer(); + if (!SelectedPlayer) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // each skills that have max skill value dependent from level seted to current level max skill value + SelectedPlayer->UpdateSkillsToMaxSkillsForLevel(); + return true; +} + +bool ChatHandler::HandleSetSkillCommand(const char *args) +{ + // number or [name] Shift-click form |color|Hskill:skill_id|h[name]|h|r + char* skill_p = extractKeyFromLink((char*)args,"Hskill"); + if (!skill_p) + return false; + + char *level_p = strtok (NULL, " "); + + if (!level_p) + return false; + + char *max_p = strtok (NULL, " "); + + int32 skill = atoi(skill_p); + if (skill <= 0) + { + PSendSysMessage(LANG_INVALID_SKILL_ID, skill); + SetSentErrorMessage(true); + return false; + } + + int32 level = atol (level_p); + + Player * target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + SkillLineEntry const* sl = sSkillLineStore.LookupEntry(skill); + if (!sl) + { + PSendSysMessage(LANG_INVALID_SKILL_ID, skill); + SetSentErrorMessage(true); + return false; + } + + std::string tNameLink = GetNameLink(target); + + if (!target->GetSkillValue(skill)) + { + PSendSysMessage(LANG_SET_SKILL_ERROR, tNameLink.c_str(), skill, sl->name[GetSessionDbcLocale()]); + SetSentErrorMessage(true); + return false; + } + + int32 max = max_p ? atol (max_p) : target->GetPureMaxSkillValue(skill); + + if (level <= 0 || level > max || max <= 0) + return false; + + target->SetSkill(skill, target->GetSkillStep(skill), level, max); + PSendSysMessage(LANG_SET_SKILL, skill, sl->name[GetSessionDbcLocale()], tNameLink.c_str(), level, max); + + return true; +} + +bool ChatHandler::HandleUnLearnCommand(const char *args) +{ + if (!*args) + return false; + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r + uint32 spell_id = extractSpellIdFromLink((char*)args); + if (!spell_id) + return false; + + char const* allStr = strtok(NULL," "); + bool allRanks = allStr ? (strncmp(allStr, "all", strlen(allStr)) == 0) : false; + + Player* target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + if (allRanks) + spell_id = spellmgr.GetFirstSpellInChain (spell_id); + + if (target->HasSpell(spell_id)) + target->removeSpell(spell_id,false,!allRanks); + else + SendSysMessage(LANG_FORGET_SPELL); + + if (GetTalentSpellCost(spell_id)) + target->SendTalentsInfoData(false); + + return true; +} + +bool ChatHandler::HandleCooldownCommand(const char *args) +{ + Player* target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + std::string tNameLink = GetNameLink(target); + + if (!*args) + { + target->RemoveAllSpellCooldown(); + PSendSysMessage(LANG_REMOVEALL_COOLDOWN, tNameLink.c_str()); + } + else + { + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell_id = extractSpellIdFromLink((char*)args); + if (!spell_id) + return false; + + if (!sSpellStore.LookupEntry(spell_id)) + { + PSendSysMessage(LANG_UNKNOWN_SPELL, target == m_session->GetPlayer() ? GetTrinityString(LANG_YOU) : tNameLink.c_str()); + SetSentErrorMessage(true); + return false; + } + + target->RemoveSpellCooldown(spell_id,true); + PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target == m_session->GetPlayer() ? GetTrinityString(LANG_YOU) : tNameLink.c_str()); + } + return true; +} + +bool ChatHandler::HandleLearnAllCommand(const char* /*args*/) +{ + static const char *allSpellList[] = + { + "3365", + "6233", + "6247", + "6246", + "6477", + "6478", + "22810", + "8386", + "21651", + "21652", + "522", + "7266", + "8597", + "2479", + "22027", + "6603", + "5019", + "133", + "168", + "227", + "5009", + "9078", + "668", + "203", + "20599", + "20600", + "81", + "20597", + "20598", + "20864", + "1459", + "5504", + "587", + "5143", + "118", + "5505", + "597", + "604", + "1449", + "1460", + "2855", + "1008", + "475", + "5506", + "1463", + "12824", + "8437", + "990", + "5145", + "8450", + "1461", + "759", + "8494", + "8455", + "8438", + "6127", + "8416", + "6129", + "8451", + "8495", + "8439", + "3552", + "8417", + "10138", + "12825", + "10169", + "10156", + "10144", + "10191", + "10201", + "10211", + "10053", + "10173", + "10139", + "10145", + "10192", + "10170", + "10202", + "10054", + "10174", + "10193", + "12826", + "2136", + "143", + "145", + "2137", + "2120", + "3140", + "543", + "2138", + "2948", + "8400", + "2121", + "8444", + "8412", + "8457", + "8401", + "8422", + "8445", + "8402", + "8413", + "8458", + "8423", + "8446", + "10148", + "10197", + "10205", + "10149", + "10215", + "10223", + "10206", + "10199", + "10150", + "10216", + "10207", + "10225", + "10151", + "116", + "205", + "7300", + "122", + "837", + "10", + "7301", + "7322", + "6143", + "120", + "865", + "8406", + "6141", + "7302", + "8461", + "8407", + "8492", + "8427", + "8408", + "6131", + "7320", + "10159", + "8462", + "10185", + "10179", + "10160", + "10180", + "10219", + "10186", + "10177", + "10230", + "10181", + "10161", + "10187", + "10220", + "2018", + "2663", + "12260", + "2660", + "3115", + "3326", + "2665", + "3116", + "2738", + "3293", + "2661", + "3319", + "2662", + "9983", + "8880", + "2737", + "2739", + "7408", + "3320", + "2666", + "3323", + "3324", + "3294", + "22723", + "23219", + "23220", + "23221", + "23228", + "23338", + "10788", + "10790", + "5611", + "5016", + "5609", + "2060", + "10963", + "10964", + "10965", + "22593", + "22594", + "596", + "996", + "499", + "768", + "17002", + "1448", + "1082", + "16979", + "1079", + "5215", + "20484", + "5221", + "15590", + "17007", + "6795", + "6807", + "5487", + "1446", + "1066", + "5421", + "3139", + "779", + "6811", + "6808", + "1445", + "5216", + "1737", + "5222", + "5217", + "1432", + "6812", + "9492", + "5210", + "3030", + "1441", + "783", + "6801", + "20739", + "8944", + "9491", + "22569", + "5226", + "6786", + "1433", + "8973", + "1828", + "9495", + "9006", + "6794", + "8993", + "5203", + "16914", + "6784", + "9635", + "22830", + "20722", + "9748", + "6790", + "9753", + "9493", + "9752", + "9831", + "9825", + "9822", + "5204", + "5401", + "22831", + "6793", + "9845", + "17401", + "9882", + "9868", + "20749", + "9893", + "9899", + "9895", + "9832", + "9902", + "9909", + "22832", + "9828", + "9851", + "9883", + "9869", + "17406", + "17402", + "9914", + "20750", + "9897", + "9848", + "3127", + "107", + "204", + "9116", + "2457", + "78", + "18848", + "331", + "403", + "2098", + "1752", + "11278", + "11288", + "11284", + "6461", + "2344", + "2345", + "6463", + "2346", + "2352", + "775", + "1434", + "1612", + "71", + "2468", + "2458", + "2467", + "7164", + "7178", + "7367", + "7376", + "7381", + "21156", + "5209", + "3029", + "5201", + "9849", + "9850", + "20719", + "22568", + "22827", + "22828", + "22829", + "6809", + "8972", + "9005", + "9823", + "9827", + "6783", + "9913", + "6785", + "6787", + "9866", + "9867", + "9894", + "9896", + "6800", + "8992", + "9829", + "9830", + "780", + "769", + "6749", + "6750", + "9755", + "9754", + "9908", + "20745", + "20742", + "20747", + "20748", + "9746", + "9745", + "9880", + "9881", + "5391", + "842", + "3025", + "3031", + "3287", + "3329", + "1945", + "3559", + "4933", + "4934", + "4935", + "4936", + "5142", + "5390", + "5392", + "5404", + "5420", + "6405", + "7293", + "7965", + "8041", + "8153", + "9033", + "9034", + //"9036", problems with ghost state + "16421", + "21653", + "22660", + "5225", + "9846", + "2426", + "5916", + "6634", + //"6718", phasing stealth, annoying for learn all case. + "6719", + "8822", + "9591", + "9590", + "10032", + "17746", + "17747", + "8203", + "11392", + "12495", + "16380", + "23452", + "4079", + "4996", + "4997", + "4998", + "4999", + "5000", + "6348", + "6349", + "6481", + "6482", + "6483", + "6484", + "11362", + "11410", + "11409", + "12510", + "12509", + "12885", + "13142", + "21463", + "23460", + "11421", + "11416", + "11418", + "1851", + "10059", + "11423", + "11417", + "11422", + "11419", + "11424", + "11420", + "27", + "31", + "33", + "34", + "35", + "15125", + "21127", + "22950", + "1180", + "201", + "12593", + "16770", + "6057", + "12051", + "18468", + "12606", + "12605", + "18466", + "12502", + "12043", + "15060", + "12042", + "12341", + "12848", + "12344", + "12353", + "18460", + "11366", + "12350", + "12352", + "13043", + "11368", + "11113", + "12400", + "11129", + "16766", + "12573", + "12580", + "12472", + "12953", + "12488", + "11189", + "12985", + "12519", + "16758", + "11958", + "12490", + "11426", + "3565", + "3562", + "18960", + "3567", + "3561", + "3566", + "3563", + "1953", + "2139", + "12505", + "13018", + "12522", + "12523", + "5146", + "5144", + "5148", + "8419", + "8418", + "10213", + "10212", + "10157", + "12524", + "13019", + "12525", + "13020", + "12526", + "13021", + "18809", + "13031", + "13032", + "13033", + "4036", + "3920", + "3919", + "3918", + "7430", + "3922", + "3923", + "7411", + "7418", + "7421", + "13262", + "7412", + "7415", + "7413", + "7416", + "13920", + "13921", + "7745", + "7779", + "7428", + "7457", + "7857", + "7748", + "7426", + "13421", + "7454", + "13378", + "7788", + "14807", + "14293", + "7795", + "6296", + "20608", + "755", + "444", + "427", + "428", + "442", + "447", + "3578", + "3581", + "19027", + "3580", + "665", + "3579", + "3577", + "6755", + "3576", + "2575", + "2577", + "2578", + "2579", + "2580", + "2656", + "2657", + "2576", + "3564", + "10248", + "8388", + "2659", + "14891", + "3308", + "3307", + "10097", + "2658", + "3569", + "16153", + "3304", + "10098", + "4037", + "3929", + "3931", + "3926", + "3924", + "3930", + "3977", + "3925", + "136", + "228", + "5487", + "43", + "202", + "0" + }; + + int loop = 0; + while (strcmp(allSpellList[loop], "0")) + { + uint32 spell = atol((char*)allSpellList[loop++]); + + if (m_session->GetPlayer()->HasSpell(spell)) + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + continue; + } + + m_session->GetPlayer()->learnSpell(spell, false); + } + + SendSysMessage(LANG_COMMAND_LEARN_MANY_SPELLS); + + return true; +} + +bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/) +{ + static const char *gmSpellList[] = + { + "24347", // Become A Fish, No Breath Bar + "35132", // Visual Boom + "38488", // Attack 4000-8000 AOE + "38795", // Attack 2000 AOE + Slow Down 90% + "15712", // Attack 200 + "1852", // GM Spell Silence + "31899", // Kill + "31924", // Kill + "29878", // Kill My Self + "26644", // More Kill + + "28550", //Invisible 24 + "23452", //Invisible + Target + "0" + }; + + uint16 gmSpellIter = 0; + while (strcmp(gmSpellList[gmSpellIter], "0")) + { + uint32 spell = atol((char*)gmSpellList[gmSpellIter++]); + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + continue; + } + + m_session->GetPlayer()->learnSpell(spell, false); + } + + SendSysMessage(LANG_LEARNING_GM_SKILLS); + return true; +} + +bool ChatHandler::HandleLearnAllMyClassCommand(const char* /*args*/) +{ + HandleLearnAllMySpellsCommand(""); + HandleLearnAllMyTalentsCommand(""); + return true; +} + +bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/) +{ + ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(m_session->GetPlayer()->getClass()); + if (!clsEntry) + return true; + uint32 family = clsEntry->spellfamily; + + for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i) + { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(i); + if (!spellInfo) + continue; + + // skip server-side/triggered spells + if (spellInfo->spellLevel == 0) + continue; + + // skip wrong class/race skills + if (!m_session->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id)) + continue; + + // skip other spell families + if (spellInfo->SpellFamilyName != family) + continue; + + // skip spells with first rank learned as talent (and all talents then also) + uint32 first_rank = spellmgr.GetFirstSpellInChain(spellInfo->Id); + if (GetTalentSpellCost(first_rank) > 0) + continue; + + // skip broken spells + if (!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) + continue; + + m_session->GetPlayer()->learnSpell(i, false); + } + + SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS); + return true; +} + +bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/) +{ + Player* player = m_session->GetPlayer(); + uint32 classMask = player->getClassMask(); + + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) + continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); + if (!talentTabInfo) + continue; + + if ((classMask & talentTabInfo->ClassMask) == 0) + continue; + + // search highest talent rank + uint32 spellId = 0; + for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) + { + if (talentInfo->RankID[rank] != 0) + { + spellId = talentInfo->RankID[rank]; + break; + } + } + + if (!spellId) // ??? none spells in talent + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) + continue; + + // learn highest rank of talent and learn all non-talent spell ranks (recursive by tree) + player->learnSpellHighRank(spellId); + player->AddTalent(spellId, player->GetActiveSpec(), true); + } + + player->SetFreeTalentPoints(0); + + SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS); + return true; +} + +bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/) +{ + Player* player = m_session->GetPlayer(); + + Pet* pet = player->GetPet(); + if (!pet) + { + SendSysMessage(LANG_NO_PET_FOUND); + SetSentErrorMessage(true); + return false; + } + + CreatureInfo const *ci = pet->GetCreatureInfo(); + if (!ci) + { + SendSysMessage(LANG_WRONG_PET_TYPE); + SetSentErrorMessage(true); + return false; + } + + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family); + if (!pet_family) + { + SendSysMessage(LANG_WRONG_PET_TYPE); + SetSentErrorMessage(true); + return false; + } + + if (pet_family->petTalentType < 0) // not hunter pet + { + SendSysMessage(LANG_WRONG_PET_TYPE); + SetSentErrorMessage(true); + return false; + } + + for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) + continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab); + if (!talentTabInfo) + continue; + + // prevent learn talent for different family (cheating) + if (((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask) == 0) + continue; + + // search highest talent rank + uint32 spellid = 0; + + for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) + { + if (talentInfo->RankID[rank] != 0) + { + spellid = talentInfo->RankID[rank]; + break; + } + } + + if (!spellid) // ??? none spells in talent + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false)) + continue; + + // learn highest rank of talent and learn all non-talent spell ranks (recursive by tree) + pet->learnSpellHighRank(spellid); + } + + pet->SetFreeTalentPoints(0); + + SendSysMessage(LANG_COMMAND_LEARN_PET_TALENTS); + return true; +} + +bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/) +{ + // skipping UNIVERSAL language (0) + for (uint8 i = 1; i < LANGUAGES_COUNT; ++i) + m_session->GetPlayer()->learnSpell(lang_description[i].spell_id, false); + + SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG); + return true; +} + +bool ChatHandler::HandleLearnAllDefaultCommand(const char *args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + target->learnDefaultSpells(); + target->learnQuestRewardedSpells(); + + PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,GetNameLink(target).c_str()); + return true; +} + +bool ChatHandler::HandleLearnCommand(const char *args) +{ + Player* targetPlayer = getSelectedPlayer(); + + if (!targetPlayer) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell = extractSpellIdFromLink((char*)args); + if (!spell || !sSpellStore.LookupEntry(spell)) + return false; + + char const* allStr = strtok(NULL," "); + bool allRanks = allStr ? (strncmp(allStr, "all", strlen(allStr)) == 0) : false; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + SetSentErrorMessage(true); + return false; + } + + if (!allRanks && targetPlayer->HasSpell(spell)) + { + if (targetPlayer == m_session->GetPlayer()) + SendSysMessage(LANG_YOU_KNOWN_SPELL); + else + PSendSysMessage(LANG_TARGET_KNOWN_SPELL,GetNameLink(targetPlayer).c_str()); + SetSentErrorMessage(true); + return false; + } + + if (allRanks) + targetPlayer->learnSpellHighRank(spell); + else + targetPlayer->learnSpell(spell, false); + + uint32 first_spell = spellmgr.GetFirstSpellInChain(spell); + if (GetTalentSpellCost(first_spell)) + targetPlayer->SendTalentsInfoData(false); + + return true; +} + +bool ChatHandler::HandleAddItemCommand(const char *args) +{ + if (!*args) + return false; + + uint32 itemId = 0; + + if (args[0] == '[') // [name] manual form + { + char* citemName = strtok((char*)args, "]"); + + if (citemName && citemName[0]) + { + std::string itemName = citemName+1; + WorldDatabase.escape_string(itemName); + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE name = '%s'", itemName.c_str()); + if (!result) + { + PSendSysMessage(LANG_COMMAND_COULDNOTFIND, citemName+1); + SetSentErrorMessage(true); + return false; + } + itemId = result->Fetch()->GetUInt16(); + } + else + return false; + } + else // item_id or [name] Shift-click form |color|Hitem:item_id:0:0:0|h[name]|h|r + { + char* cId = extractKeyFromLink((char*)args,"Hitem"); + if (!cId) + return false; + itemId = atol(cId); + } + + char* ccount = strtok(NULL, " "); + + int32 count = 1; + + if (ccount) + count = strtol(ccount, NULL, 10); + + if (count == 0) + count = 1; + + Player* pl = m_session->GetPlayer(); + Player* plTarget = getSelectedPlayer(); + if (!plTarget) + plTarget = pl; + + sLog.outDetail(GetTrinityString(LANG_ADDITEM), itemId, count); + + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId); + if (!pProto) + { + PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemId); + SetSentErrorMessage(true); + return false; + } + + //Subtract + if (count < 0) + { + plTarget->DestroyItemCount(itemId, -count, true, false); + PSendSysMessage(LANG_REMOVEITEM, itemId, -count, GetNameLink(plTarget).c_str()); + return true; + } + + //Adding items + uint32 noSpaceForCount = 0; + + // check space and find places + ItemPosCountVec dest; + uint8 msg = plTarget->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount); + if (msg != EQUIP_ERR_OK) // convert to possible store amount + count -= noSpaceForCount; + + if (count == 0 || dest.empty()) // can't add any + { + PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount); + SetSentErrorMessage(true); + return false; + } + + Item* item = plTarget->StoreNewItem(dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId)); + + // remove binding (let GM give it to another player later) + if (pl == plTarget) + for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr) + if (Item* item1 = pl->GetItemByPos(itr->pos)) + item1->SetBinding(false); + + if (count > 0 && item) + { + pl->SendNewItem(item,count,false,true); + if (pl != plTarget) + plTarget->SendNewItem(item,count,true,false); + } + + if (noSpaceForCount > 0) + PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount); + + return true; +} + +bool ChatHandler::HandleAddItemSetCommand(const char *args) +{ + if (!*args) + return false; + + char* cId = extractKeyFromLink((char*)args,"Hitemset"); // number or [name] Shift-click form |color|Hitemset:itemset_id|h[name]|h|r + if (!cId) + return false; + + uint32 itemsetId = atol(cId); + + // prevent generation all items with itemset field value '0' + if (itemsetId == 0) + { + PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId); + SetSentErrorMessage(true); + return false; + } + + Player* pl = m_session->GetPlayer(); + Player* plTarget = getSelectedPlayer(); + if (!plTarget) + plTarget = pl; + + sLog.outDetail(GetTrinityString(LANG_ADDITEMSET), itemsetId); + + bool found = false; + for (uint32 id = 0; id < sItemStorage.MaxEntry; id++) + { + ItemPrototype const *pProto = sItemStorage.LookupEntry(id); + if (!pProto) + continue; + + if (pProto->ItemSet == itemsetId) + { + found = true; + ItemPosCountVec dest; + uint8 msg = plTarget->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, pProto->ItemId, 1); + if (msg == EQUIP_ERR_OK) + { + Item* item = plTarget->StoreNewItem(dest, pProto->ItemId, true); + + // remove binding (let GM give it to another player later) + if (pl == plTarget) + item->SetBinding(false); + + pl->SendNewItem(item,1,false,true); + if (pl != plTarget) + plTarget->SendNewItem(item,1,true,false); + } + else + { + pl->SendEquipError(msg, NULL, NULL); + PSendSysMessage(LANG_ITEM_CANNOT_CREATE, pProto->ItemId, 1); + } + } + } + + if (!found) + { + PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId); + + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleListItemCommand(const char *args) +{ + if (!*args) + return false; + + char* cId = extractKeyFromLink((char*)args,"Hitem"); + if (!cId) + return false; + + uint32 item_id = atol(cId); + if (!item_id) + { + PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id); + SetSentErrorMessage(true); + return false; + } + + ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_id); + if (!itemProto) + { + PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id); + SetSentErrorMessage(true); + return false; + } + + char* c_count = strtok(NULL, " "); + int count = c_count ? atol(c_count) : 10; + + if (count < 0) + return false; + + QueryResult_AutoPtr result; + + // inventory case + uint32 inv_count = 0; + result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM character_inventory WHERE item_template='%u'",item_id); + if (result) + inv_count = (*result)[0].GetUInt32(); + + result=CharacterDatabase.PQuery( + // 0 1 2 3 4 5 + "SELECT ci.item, cibag.slot AS bag, ci.slot, ci.guid, characters.account,characters.name " + "FROM character_inventory AS ci LEFT JOIN character_inventory AS cibag ON (cibag.item=ci.bag),characters " + "WHERE ci.item_template='%u' AND ci.guid = characters.guid LIMIT %u ", + item_id,uint32(count)); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 item_guid = fields[0].GetUInt32(); + uint32 item_bag = fields[1].GetUInt32(); + uint32 item_slot = fields[2].GetUInt32(); + uint32 owner_guid = fields[3].GetUInt32(); + uint32 owner_acc = fields[4].GetUInt32(); + std::string owner_name = fields[5].GetCppString(); + + char const* item_pos = 0; + if (Player::IsEquipmentPos(item_bag,item_slot)) + item_pos = "[equipped]"; + else if (Player::IsInventoryPos(item_bag,item_slot)) + item_pos = "[in inventory]"; + else if (Player::IsBankPos(item_bag,item_slot)) + item_pos = "[in bank]"; + else + item_pos = ""; + + PSendSysMessage(LANG_ITEMLIST_SLOT, + item_guid,owner_name.c_str(),owner_guid,owner_acc,item_pos); + } while (result->NextRow()); + + int64 res_count = result->GetRowCount(); + + if (count > res_count) + count-=res_count; + else if (count) + count = 0; + } + + // mail case + uint32 mail_count = 0; + result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM mail_items WHERE item_template='%u'", item_id); + if (result) + mail_count = (*result)[0].GetUInt32(); + + if (count > 0) + { + result=CharacterDatabase.PQuery( + // 0 1 2 3 4 5 6 + "SELECT mail_items.item_guid, mail.sender, mail.receiver, char_s.account, char_s.name, char_r.account, char_r.name " + "FROM mail,mail_items,characters as char_s,characters as char_r " + "WHERE mail_items.item_template='%u' AND char_s.guid = mail.sender AND char_r.guid = mail.receiver AND mail.id=mail_items.mail_id LIMIT %u", + item_id,uint32(count)); + } + else + result = QueryResult_AutoPtr(NULL); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 item_guid = fields[0].GetUInt32(); + uint32 item_s = fields[1].GetUInt32(); + uint32 item_r = fields[2].GetUInt32(); + uint32 item_s_acc = fields[3].GetUInt32(); + std::string item_s_name = fields[4].GetCppString(); + uint32 item_r_acc = fields[5].GetUInt32(); + std::string item_r_name = fields[6].GetCppString(); + + char const* item_pos = "[in mail]"; + + PSendSysMessage(LANG_ITEMLIST_MAIL, + item_guid,item_s_name.c_str(),item_s,item_s_acc,item_r_name.c_str(),item_r,item_r_acc,item_pos); + } while (result->NextRow()); + + int64 res_count = result->GetRowCount(); + + if (count > res_count) + count-=res_count; + else if (count) + count = 0; + } + + // auction case + uint32 auc_count = 0; + result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM auctionhouse WHERE item_template='%u'",item_id); + if (result) + auc_count = (*result)[0].GetUInt32(); + + if (count > 0) + { + result=CharacterDatabase.PQuery( + // 0 1 2 3 + "SELECT auctionhouse.itemguid, auctionhouse.itemowner, characters.account, characters.name " + "FROM auctionhouse,characters WHERE auctionhouse.item_template='%u' AND characters.guid = auctionhouse.itemowner LIMIT %u", + item_id,uint32(count)); + } + else + result = QueryResult_AutoPtr(NULL); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 item_guid = fields[0].GetUInt32(); + uint32 owner = fields[1].GetUInt32(); + uint32 owner_acc = fields[2].GetUInt32(); + std::string owner_name = fields[3].GetCppString(); + + char const* item_pos = "[in auction]"; + + PSendSysMessage(LANG_ITEMLIST_AUCTION, item_guid, owner_name.c_str(), owner, owner_acc,item_pos); + } while (result->NextRow()); + } + + // guild bank case + uint32 guild_count = 0; + result=CharacterDatabase.PQuery("SELECT COUNT(item_entry) FROM guild_bank_item WHERE item_entry='%u'",item_id); + if (result) + guild_count = (*result)[0].GetUInt32(); + + result=CharacterDatabase.PQuery( + // 0 1 2 + "SELECT gi.item_guid, gi.guildid, guild.name " + "FROM guild_bank_item AS gi, guild WHERE gi.item_entry='%u' AND gi.guildid = guild.guildid LIMIT %u ", + item_id,uint32(count)); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 item_guid = fields[0].GetUInt32(); + uint32 guild_guid = fields[1].GetUInt32(); + std::string guild_name = fields[2].GetCppString(); + + char const* item_pos = "[in guild bank]"; + + PSendSysMessage(LANG_ITEMLIST_GUILD,item_guid,guild_name.c_str(),guild_guid,item_pos); + } while (result->NextRow()); + + int64 res_count = result->GetRowCount(); + + if (count > res_count) + count-=res_count; + else if (count) + count = 0; + } + + if (inv_count+mail_count+auc_count+guild_count == 0) + { + SendSysMessage(LANG_COMMAND_NOITEMFOUND); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_COMMAND_LISTITEMMESSAGE,item_id,inv_count+mail_count+auc_count+guild_count,inv_count,mail_count,auc_count,guild_count); + + return true; +} + +bool ChatHandler::HandleListObjectCommand(const char *args) +{ + if (!*args) + return false; + + // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry"); + if (!cId) + return false; + + uint32 go_id = atol(cId); + if (!go_id) + { + PSendSysMessage(LANG_COMMAND_LISTOBJINVALIDID, go_id); + SetSentErrorMessage(true); + return false; + } + + GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(go_id); + if (!gInfo) + { + PSendSysMessage(LANG_COMMAND_LISTOBJINVALIDID, go_id); + SetSentErrorMessage(true); + return false; + } + + char* c_count = strtok(NULL, " "); + int count = c_count ? atol(c_count) : 10; + + if (count < 0) + return false; + + QueryResult_AutoPtr result; + + uint32 obj_count = 0; + result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM gameobject WHERE id='%u'",go_id); + if (result) + obj_count = (*result)[0].GetUInt32(); + + if (m_session) + { + Player* pl = m_session->GetPlayer(); + result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE id = '%u' ORDER BY order_ ASC LIMIT %u", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),go_id,uint32(count)); + } + else + result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map FROM gameobject WHERE id = '%u' LIMIT %u", + go_id,uint32(count)); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + float x = fields[1].GetFloat(); + float y = fields[2].GetFloat(); + float z = fields[3].GetFloat(); + int mapid = fields[4].GetUInt16(); + + if (m_session) + PSendSysMessage(LANG_GO_LIST_CHAT, guid, guid, gInfo->name, x, y, z, mapid); + else + PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name, x, y, z, mapid); + } while (result->NextRow()); + } + + PSendSysMessage(LANG_COMMAND_LISTOBJMESSAGE,go_id,obj_count); + return true; +} + +bool ChatHandler::HandleGameObjectStateCommand(const char *args) +{ + // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args, "Hgameobject"); + if (!cId) + return false; + + uint32 lowguid = atoi(cId); + if (!lowguid) + return false; + + GameObject* gobj = NULL; + + if (GameObjectData const* goData = objmgr.GetGOData(lowguid)) + gobj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid, goData->id); + + if (!gobj) + { + PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid); + SetSentErrorMessage(true); + return false; + } + + char* ctype = strtok(NULL, " "); + if (!ctype) + return false; + + int32 type = atoi(ctype); + if (type < 0) + { + if (type == -1) + gobj->SendObjectDeSpawnAnim(gobj->GetGUID()); + else if (type == -2) + { + return false; + } + return true; + } + + char* cstate = strtok(NULL, " "); + if (!cstate) + return false; + + int32 state = atoi(cstate); + + if (type < 4) + gobj->SetByteValue(GAMEOBJECT_BYTES_1, type, state); + else if (type == 4) + { + WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4); + data << gobj->GetGUID(); + data << (uint32)(state); + gobj->SendMessageToSet(&data, true); + } + PSendSysMessage("Set gobject type %d state %d", type, state); + + return true; +} + +bool ChatHandler::HandleListCreatureCommand(const char *args) +{ + if (!*args) + return false; + + // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hcreature_entry"); + if (!cId) + return false; + + uint32 cr_id = atol(cId); + if (!cr_id) + { + PSendSysMessage(LANG_COMMAND_INVALIDCREATUREID, cr_id); + SetSentErrorMessage(true); + return false; + } + + CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(cr_id); + if (!cInfo) + { + PSendSysMessage(LANG_COMMAND_INVALIDCREATUREID, cr_id); + SetSentErrorMessage(true); + return false; + } + + char* c_count = strtok(NULL, " "); + int count = c_count ? atol(c_count) : 10; + + if (count < 0) + return false; + + QueryResult_AutoPtr result; + + uint32 cr_count = 0; + result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id='%u'",cr_id); + if (result) + cr_count = (*result)[0].GetUInt32(); + + if (m_session) + { + Player* pl = m_session->GetPlayer(); + result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM creature WHERE id = '%u' ORDER BY order_ ASC LIMIT %u", + pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), cr_id,uint32(count)); + } + else + result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map FROM creature WHERE id = '%u' LIMIT %u", + cr_id,uint32(count)); + + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + float x = fields[1].GetFloat(); + float y = fields[2].GetFloat(); + float z = fields[3].GetFloat(); + int mapid = fields[4].GetUInt16(); + + if (m_session) + PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name, x, y, z, mapid); + else + PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name, x, y, z, mapid); + } while (result->NextRow()); + } + + PSendSysMessage(LANG_COMMAND_LISTCREATUREMESSAGE,cr_id,cr_count); + return true; +} + +bool ChatHandler::HandleLookupItemCommand(const char *args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + // converting string that we try to find to lower case + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + wstrToLower(wnamepart); + + bool found = false; + + // Search in `item_template` + for (uint32 id = 0; id < sItemStorage.MaxEntry; id++) + { + ItemPrototype const *pProto = sItemStorage.LookupEntry(id); + if (!pProto) + continue; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); + if (il) + { + if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) + { + std::string name = il->Name[loc_idx]; + + if (Utf8FitTo(name, wnamepart)) + { + if (m_session) + PSendSysMessage(LANG_ITEM_LIST_CHAT, id, id, name.c_str()); + else + PSendSysMessage(LANG_ITEM_LIST_CONSOLE, id, name.c_str()); + + if (!found) + found = true; + + continue; + } + } + } + } + + std::string name = pProto->Name1; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + { + if (m_session) + PSendSysMessage(LANG_ITEM_LIST_CHAT, id, id, name.c_str()); + else + PSendSysMessage(LANG_ITEM_LIST_CONSOLE, id, name.c_str()); + + if (!found) + found = true; + } + } + + if (!found) + SendSysMessage(LANG_COMMAND_NOITEMFOUND); + + return true; +} + +bool ChatHandler::HandleLookupItemSetCommand(const char *args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + bool found = false; + + // Search in ItemSet.dbc + for (uint32 id = 0; id < sItemSetStore.GetNumRows(); id++) + { + ItemSetEntry const *set = sItemSetStore.LookupEntry(id); + if (set) + { + int loc = GetSessionDbcLocale(); + std::string name = set->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = set->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + // send item set in "id - [namedlink locale]" format + if (m_session) + PSendSysMessage(LANG_ITEMSET_LIST_CHAT,id,id,name.c_str(),localeNames[loc]); + else + PSendSysMessage(LANG_ITEMSET_LIST_CONSOLE,id,name.c_str(),localeNames[loc]); + + if (!found) + found = true; + } + } + } + if (!found) + SendSysMessage(LANG_COMMAND_NOITEMSETFOUND); + return true; +} + +bool ChatHandler::HandleLookupSkillCommand(const char *args) +{ + if (!*args) + return false; + + // can be NULL in console call + Player* target = getSelectedPlayer(); + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + bool found = false; + + // Search in SkillLine.dbc + for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); id++) + { + SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(id); + if (skillInfo) + { + int loc = GetSessionDbcLocale(); + std::string name = skillInfo->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = skillInfo->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + char valStr[50] = ""; + char const* knownStr = ""; + if (target && target->HasSkill(id)) + { + knownStr = GetTrinityString(LANG_KNOWN); + uint32 curValue = target->GetPureSkillValue(id); + uint32 maxValue = target->GetPureMaxSkillValue(id); + uint32 permValue = target->GetSkillPermBonusValue(id); + uint32 tempValue = target->GetSkillTempBonusValue(id); + + char const* valFormat = GetTrinityString(LANG_SKILL_VALUES); + snprintf(valStr,50,valFormat,curValue,maxValue,permValue,tempValue); + } + + // send skill in "id - [namedlink locale]" format + if (m_session) + PSendSysMessage(LANG_SKILL_LIST_CHAT,id,id,name.c_str(),localeNames[loc],knownStr,valStr); + else + PSendSysMessage(LANG_SKILL_LIST_CONSOLE,id,name.c_str(),localeNames[loc],knownStr,valStr); + + if (!found) + found = true; + } + } + } + if (!found) + SendSysMessage(LANG_COMMAND_NOSKILLFOUND); + return true; +} + +bool ChatHandler::HandleLookupSpellCommand(const char *args) +{ + if (!*args) + return false; + + // can be NULL at console call + Player* target = getSelectedPlayer(); + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + bool found = false; + + // Search in Spell.dbc + for (uint32 id = 0; id < sSpellStore.GetNumRows(); id++) + { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(id); + if (spellInfo) + { + int loc = GetSessionDbcLocale(); + std::string name = spellInfo->SpellName[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = spellInfo->SpellName[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + bool known = target && target->HasSpell(id); + bool learn = (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL); + + uint32 talentCost = GetTalentSpellCost(id); + + bool talent = (talentCost > 0); + bool passive = IsPassiveSpell(id); + bool active = target && target->HasAura(id); + + // unit32 used to prevent interpreting uint8 as char at output + // find rank of learned spell for learning spell, or talent rank + uint32 rank = talentCost ? talentCost : spellmgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[0] : id); + + // send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format + std::ostringstream ss; + if (m_session) + ss << id << " - |cffffffff|Hspell:" << id << "|h[" << name; + else + ss << id << " - " << name; + + // include rank in link name + if (rank) + ss << GetTrinityString(LANG_SPELL_RANK) << rank; + + if (m_session) + ss << " " << localeNames[loc] << "]|h|r"; + else + ss << " " << localeNames[loc]; + + if (talent) + ss << GetTrinityString(LANG_TALENT); + if (passive) + ss << GetTrinityString(LANG_PASSIVE); + if (learn) + ss << GetTrinityString(LANG_LEARN); + if (known) + ss << GetTrinityString(LANG_KNOWN); + if (active) + ss << GetTrinityString(LANG_ACTIVE); + + SendSysMessage(ss.str().c_str()); + + if (!found) + found = true; + } + } + } + if (!found) + SendSysMessage(LANG_COMMAND_NOSPELLFOUND); + return true; +} + +bool ChatHandler::HandleLookupQuestCommand(const char *args) +{ + if (!*args) + return false; + + // can be NULL at console call + Player* target = getSelectedPlayer(); + + std::string namepart = args; + std::wstring wnamepart; + + // converting string that we try to find to lower case + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + wstrToLower(wnamepart); + + bool found = false; + + ObjectMgr::QuestMap const& qTemplates = objmgr.GetQuestTemplates(); + for (ObjectMgr::QuestMap::const_iterator iter = qTemplates.begin(); iter != qTemplates.end(); ++iter) + { + Quest * qinfo = iter->second; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + QuestLocale const *il = objmgr.GetQuestLocale(qinfo->GetQuestId()); + if (il) + { + if (il->Title.size() > loc_idx && !il->Title[loc_idx].empty()) + { + std::string title = il->Title[loc_idx]; + + if (Utf8FitTo(title, wnamepart)) + { + char const* statusStr = ""; + + if (target) + { + QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId()); + + if (status == QUEST_STATUS_COMPLETE) + { + if (target->GetQuestRewardStatus(qinfo->GetQuestId())) + statusStr = GetTrinityString(LANG_COMMAND_QUEST_REWARDED); + else + statusStr = GetTrinityString(LANG_COMMAND_QUEST_COMPLETE); + } + else if (status == QUEST_STATUS_INCOMPLETE) + statusStr = GetTrinityString(LANG_COMMAND_QUEST_ACTIVE); + } + + if (m_session) + PSendSysMessage(LANG_QUEST_LIST_CHAT,qinfo->GetQuestId(),qinfo->GetQuestId(),qinfo->GetQuestLevel(),title.c_str(),statusStr); + else + PSendSysMessage(LANG_QUEST_LIST_CONSOLE,qinfo->GetQuestId(),title.c_str(),statusStr); + + if (!found) + found = true; + + continue; + } + } + } + } + + std::string title = qinfo->GetTitle(); + if (title.empty()) + continue; + + if (Utf8FitTo(title, wnamepart)) + { + char const* statusStr = ""; + + if (target) + { + QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId()); + + if (status == QUEST_STATUS_COMPLETE) + { + if (target->GetQuestRewardStatus(qinfo->GetQuestId())) + statusStr = GetTrinityString(LANG_COMMAND_QUEST_REWARDED); + else + statusStr = GetTrinityString(LANG_COMMAND_QUEST_COMPLETE); + } + else if (status == QUEST_STATUS_INCOMPLETE) + statusStr = GetTrinityString(LANG_COMMAND_QUEST_ACTIVE); + } + + if (m_session) + PSendSysMessage(LANG_QUEST_LIST_CHAT,qinfo->GetQuestId(),qinfo->GetQuestId(),qinfo->GetQuestLevel(),title.c_str(),statusStr); + else + PSendSysMessage(LANG_QUEST_LIST_CONSOLE,qinfo->GetQuestId(),title.c_str(),statusStr); + + if (!found) + found = true; + } + } + + if (!found) + SendSysMessage(LANG_COMMAND_NOQUESTFOUND); + + return true; +} + +bool ChatHandler::HandleLookupCreatureCommand(const char *args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + // converting string that we try to find to lower case + if (!Utf8toWStr (namepart,wnamepart)) + return false; + + wstrToLower (wnamepart); + + bool found = false; + + for (uint32 id = 0; id< sCreatureStorage.MaxEntry; ++id) + { + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry (id); + if (!cInfo) + continue; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + CreatureLocale const *cl = objmgr.GetCreatureLocale (id); + if (cl) + { + if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty ()) + { + std::string name = cl->Name[loc_idx]; + + if (Utf8FitTo (name, wnamepart)) + { + if (m_session) + PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CHAT, id, id, name.c_str ()); + else + PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CONSOLE, id, name.c_str ()); + + if (!found) + found = true; + + continue; + } + } + } + } + + std::string name = cInfo->Name; + if (name.empty ()) + continue; + + if (Utf8FitTo(name, wnamepart)) + { + if (m_session) + PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CHAT, id, id, name.c_str ()); + else + PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CONSOLE, id, name.c_str ()); + + if (!found) + found = true; + } + } + + if (!found) + SendSysMessage (LANG_COMMAND_NOCREATUREFOUND); + + return true; +} + +bool ChatHandler::HandleLookupObjectCommand(const char *args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + // converting string that we try to find to lower case + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + wstrToLower(wnamepart); + + bool found = false; + + for (uint32 id = 0; id< sGOStorage.MaxEntry; id++) + { + GameObjectInfo const* gInfo = sGOStorage.LookupEntry(id); + if (!gInfo) + continue; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + GameObjectLocale const *gl = objmgr.GetGameObjectLocale(id); + if (gl) + { + if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty()) + { + std::string name = gl->Name[loc_idx]; + + if (Utf8FitTo(name, wnamepart)) + { + if (m_session) + PSendSysMessage(LANG_GO_ENTRY_LIST_CHAT, id, id, name.c_str()); + else + PSendSysMessage(LANG_GO_ENTRY_LIST_CONSOLE, id, name.c_str()); + + if (!found) + found = true; + + continue; + } + } + } + } + + std::string name = gInfo->name; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + { + if (m_session) + PSendSysMessage(LANG_GO_ENTRY_LIST_CHAT, id, id, name.c_str()); + else + PSendSysMessage(LANG_GO_ENTRY_LIST_CONSOLE, id, name.c_str()); + + if (!found) + found = true; + } + } + + if (!found) + SendSysMessage(LANG_COMMAND_NOGAMEOBJECTFOUND); + + return true; +} + +bool ChatHandler::HandleLookupFactionCommand(const char *args) +{ + if (!*args) + return false; + + // Can be NULL at console call + Player *target = getSelectedPlayer (); + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr (namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower (wnamepart); + + bool found = false; + + for (uint32 id = 0; id < sFactionStore.GetNumRows(); ++id) + { + FactionEntry const *factionEntry = sFactionStore.LookupEntry (id); + if (factionEntry) + { + FactionState const* repState = target ? target->GetReputationMgr().GetState(factionEntry) : NULL; + + int loc = GetSessionDbcLocale(); + std::string name = factionEntry->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = factionEntry->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format + // or "id - [faction] [no reputation]" format + std::ostringstream ss; + if (m_session) + ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r"; + else + ss << id << " - " << name << " " << localeNames[loc]; + + if (repState) // and then target != NULL also + { + ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry); + std::string rankName = GetTrinityString(ReputationRankStrIndex[rank]); + + ss << " " << rankName << "|h|r (" << target->GetReputationMgr().GetReputation(factionEntry) << ")"; + + if (repState->Flags & FACTION_FLAG_VISIBLE) + ss << GetTrinityString(LANG_FACTION_VISIBLE); + if (repState->Flags & FACTION_FLAG_AT_WAR) + ss << GetTrinityString(LANG_FACTION_ATWAR); + if (repState->Flags & FACTION_FLAG_PEACE_FORCED) + ss << GetTrinityString(LANG_FACTION_PEACE_FORCED); + if (repState->Flags & FACTION_FLAG_HIDDEN) + ss << GetTrinityString(LANG_FACTION_HIDDEN); + if (repState->Flags & FACTION_FLAG_INVISIBLE_FORCED) + ss << GetTrinityString(LANG_FACTION_INVISIBLE_FORCED); + if (repState->Flags & FACTION_FLAG_INACTIVE) + ss << GetTrinityString(LANG_FACTION_INACTIVE); + } + else + ss << GetTrinityString(LANG_FACTION_NOREPUTATION); + + SendSysMessage(ss.str().c_str()); + + if (!found) + found = true; + } + } + } + + if (!found) + SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND); + return true; +} + +bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args) +{ + if (!*args) + return false; + + std::string namepart = args; + std::wstring wnamepart; + + if (!Utf8toWStr(namepart,wnamepart)) + return false; + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + bool found = false; + + // Search in TaxiNodes.dbc + for (uint32 id = 0; id < sTaxiNodesStore.GetNumRows(); id++) + { + TaxiNodesEntry const *nodeEntry = sTaxiNodesStore.LookupEntry(id); + if (nodeEntry) + { + int loc = GetSessionDbcLocale(); + std::string name = nodeEntry->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == GetSessionDbcLocale()) + continue; + + name = nodeEntry->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + // send taxinode in "id - [name] (Map:m X:x Y:y Z:z)" format + if (m_session) + PSendSysMessage (LANG_TAXINODE_ENTRY_LIST_CHAT, id, id, name.c_str(),localeNames[loc], + nodeEntry->map_id,nodeEntry->x,nodeEntry->y,nodeEntry->z); + else + PSendSysMessage (LANG_TAXINODE_ENTRY_LIST_CONSOLE, id, name.c_str(), localeNames[loc], + nodeEntry->map_id,nodeEntry->x,nodeEntry->y,nodeEntry->z); + + if (!found) + found = true; + } + } + } + if (!found) + SendSysMessage(LANG_COMMAND_NOTAXINODEFOUND); + return true; +} + +bool ChatHandler::HandleLookupMapCommand(const char *args) +{ + if (!*args) + return false; + + /*std::string namepart = args; + std::wstring wnamepart; + + // converting string that we try to find to lower case + if (!Utf8toWStr(namepart, wnamepart)) + return false; + + wstrToLower(wnamepart); + + bool found = false; + + // search in Map.dbc + for (uint32 id = 0; id < sMapStore.GetNumRows(); id++) + { + MapEntry const* MapInfo = sMapStore.LookupEntry(id); + if (MapInfo) + { + uint8 loc = m_session ? m_session->GetSessionDbcLocale() : sWorld.GetDefaultDbcLocale(); + + std::string name = MapInfo->name[loc]; + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = LOCALE_enUS; + for (; loc < MAX_LOCALE; loc++) + { + if (m_session && loc == m_session->GetSessionDbcLocale()) + continue; + + name = MapInfo->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + + if (loc < MAX_LOCALE) + { + // send map in "id - [name][Continent][Instance/Battleground/Arena][Raid reset time:][Heroic reset time:][Mountable]" format + std::ostringstream ss; + + if (m_session) + ss << id << " - |cffffffff|Hmap:" << id << "|h[" << name << "]"; + else // console + ss << id << " - [" << name << "]"; + + if (MapInfo->IsContinent()) + ss << GetTrinityString(LANG_CONTINENT); + + switch(MapInfo->map_type) + { + case MAP_INSTANCE: ss << GetTrinityString(LANG_INSTANCE); break; + case MAP_BATTLEGROUND: ss << GetTrinityString(LANG_BATTLEGROUND); break; + case MAP_ARENA: ss << GetTrinityString(LANG_ARENA); break; + } + + if (MapInfo->IsRaid()) + ss << GetTrinityString(LANG_RAID); + + if (MapInfo->SupportsHeroicMode()) + ss << GetTrinityString(LANG_HEROIC); + + uint32 ResetTimeRaid = MapInfo->resetTimeRaid; + + std::string ResetTimeRaidStr; + if (ResetTimeRaid) + ResetTimeRaidStr = secsToTimeString(ResetTimeRaid, true, false); + + uint32 ResetTimeHeroic = MapInfo->resetTimeHeroic; + std::string ResetTimeHeroicStr; + if (ResetTimeHeroic) + ResetTimeHeroicStr = secsToTimeString(ResetTimeHeroic, true, false); + + if (MapInfo->IsMountAllowed()) + ss << GetTrinityString(LANG_MOUNTABLE); + + if (ResetTimeRaid && !ResetTimeHeroic) + PSendSysMessage(ss.str().c_str(), ResetTimeRaidStr.c_str()); + else if (!ResetTimeRaid && ResetTimeHeroic) + PSendSysMessage(ss.str().c_str(), ResetTimeHeroicStr.c_str()); + else if (ResetTimeRaid && ResetTimeHeroic) + PSendSysMessage(ss.str().c_str(), ResetTimeRaidStr.c_str(), ResetTimeHeroicStr.c_str()); + else + SendSysMessage(ss.str().c_str()); + + if (!found) + found = true; + } + } + } + + if (!found) + SendSysMessage(LANG_COMMAND_NOMAPFOUND); + */ + return true; +} + +/** \brief GM command level 3 - Create a guild. + * + * This command allows a GM (level 3) to create a guild. + * + * The "args" parameter contains the name of the guild leader + * and then the name of the guild. + * + */ +bool ChatHandler::HandleGuildCreateCommand(const char *args) +{ + if (!*args) + return false; + + // if not guild name only (in "") then player name + Player* target; + if (!extractPlayerTarget(*args != '"' ? (char*)args : NULL, &target)) + return false; + + char* tailStr = *args != '"' ? strtok(NULL, "") : (char*)args; + if (!tailStr) + return false; + + char* guildStr = extractQuotedArg(tailStr); + if (!guildStr) + return false; + + std::string guildname = guildStr; + + if (target->GetGuildId()) + { + SendSysMessage (LANG_PLAYER_IN_GUILD); + return true; + } + + Guild *guild = new Guild; + if (!guild->Create (target,guildname)) + { + delete guild; + SendSysMessage (LANG_GUILD_NOT_CREATED); + SetSentErrorMessage (true); + return false; + } + + objmgr.AddGuild (guild); + return true; +} + +bool ChatHandler::HandleGuildInviteCommand(const char *args) +{ + if (!*args) + return false; + + // if not guild name only (in "") then player name + uint64 target_guid; + if (!extractPlayerTarget(*args != '"' ? (char*)args : NULL, NULL, &target_guid)) + return false; + + char* tailStr = *args != '"' ? strtok(NULL, "") : (char*)args; + if (!tailStr) + return false; + + char* guildStr = extractQuotedArg(tailStr); + if (!guildStr) + return false; + + std::string glName = guildStr; + Guild* targetGuild = objmgr.GetGuildByName (glName); + if (!targetGuild) + return false; + + // player's guild membership checked in AddMember before add + if (!targetGuild->AddMember (target_guid,targetGuild->GetLowestRank ())) + return false; + + return true; +} + +bool ChatHandler::HandleGuildUninviteCommand(const char *args) +{ + Player* target; + uint64 target_guid; + if (!extractPlayerTarget((char*)args,&target,&target_guid)) + return false; + + uint32 glId = target ? target->GetGuildId () : Player::GetGuildIdFromDB (target_guid); + if (!glId) + return false; + + Guild* targetGuild = objmgr.GetGuildById (glId); + if (!targetGuild) + return false; + + targetGuild->DelMember (target_guid); + return true; +} + +bool ChatHandler::HandleGuildRankCommand(const char *args) +{ + char* nameStr; + char* rankStr; + extractOptFirstArg((char*)args,&nameStr,&rankStr); + if (!rankStr) + return false; + + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) + return false; + + uint32 glId = target ? target->GetGuildId () : Player::GetGuildIdFromDB (target_guid); + if (!glId) + return false; + + Guild* targetGuild = objmgr.GetGuildById (glId); + if (!targetGuild) + return false; + + uint32 newrank = uint32 (atoi (rankStr)); + if (newrank > targetGuild->GetLowestRank ()) + return false; + + targetGuild->ChangeRank (target_guid,newrank); + return true; +} + +bool ChatHandler::HandleGuildDeleteCommand(const char *args) +{ + if (!*args) + return false; + + char* guildStr = extractQuotedArg((char*)args); + if (!guildStr) + return false; + + std::string gld = guildStr; + + Guild* targetGuild = objmgr.GetGuildByName (gld); + if (!targetGuild) + return false; + + targetGuild->Disband (); + + return true; +} + +bool ChatHandler::HandleGetDistanceCommand(const char *args) +{ + WorldObject* obj = NULL; + + if (*args) + { + uint64 guid = extractGuidFromLink((char*)args); + if (guid) + obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); + + if (!obj) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + } + else + { + obj = getSelectedUnit(); + + if (!obj) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + } + + PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(obj), m_session->GetPlayer()->GetDistance2d(obj), m_session->GetPlayer()->GetExactDist(obj), m_session->GetPlayer()->GetExactDist2d(obj)); + return true; +} + +bool ChatHandler::HandleDieCommand(const char* /*args*/) +{ + Unit* target = getSelectedUnit(); + + if (!target || !m_session->GetPlayer()->GetSelection()) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (target->GetTypeId() == TYPEID_PLAYER) + { + if (HasLowerSecurity((Player*)target,0,false)) + return false; + } + + if (target->isAlive()) + { + if (sWorld.getConfig(CONFIG_DIE_COMMAND_MODE)) + m_session->GetPlayer()->Kill(target); + else + m_session->GetPlayer()->DealDamage(target, target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + return true; +} + +bool ChatHandler::HandleDamageCommand(const char * args) +{ + if (!*args) + return false; + + Unit* target = getSelectedUnit(); + + if (!target || !m_session->GetPlayer()->GetSelection()) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (!target->isAlive()) + return true; + + char* damageStr = strtok((char*)args, " "); + if (!damageStr) + return false; + + int32 damage_int = atoi((char*)damageStr); + if (damage_int <= 0) + return true; + + uint32 damage = damage_int; + + char* schoolStr = strtok((char*)NULL, " "); + + // flat melee damage without resistence/etc reduction + if (!schoolStr) + { + m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + if (target != m_session->GetPlayer()) + m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, SPELL_SCHOOL_MASK_NORMAL, damage, 0, 0, VICTIMSTATE_NORMAL, 0); + return true; + } + + uint32 school = schoolStr ? atoi((char*)schoolStr) : SPELL_SCHOOL_NORMAL; + if (school >= MAX_SPELL_SCHOOL) + return false; + + SpellSchoolMask schoolmask = SpellSchoolMask(1 << school); + + if (schoolmask & SPELL_SCHOOL_MASK_NORMAL) + damage = m_session->GetPlayer()->CalcArmorReducedDamage(target, damage, NULL, BASE_ATTACK); + + char* spellStr = strtok((char*)NULL, " "); + + // melee damage by specific school + if (!spellStr) + { + uint32 absorb = 0; + uint32 resist = 0; + + m_session->GetPlayer()->CalcAbsorbResist(target,schoolmask, SPELL_DIRECT_DAMAGE, damage, &absorb, &resist); + + if (damage <= absorb + resist) + return true; + + damage -= absorb + resist; + + m_session->GetPlayer()->DealDamageMods(target,damage,&absorb); + m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, schoolmask, NULL, false); + m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, schoolmask, damage, absorb, resist, VICTIMSTATE_NORMAL, 0); + return true; + } + + // non-melee damage + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spellid = extractSpellIdFromLink((char*)args); + if (!spellid || !sSpellStore.LookupEntry(spellid)) + return false; + + m_session->GetPlayer()->SpellNonMeleeDamageLog(target, spellid, damage); + return true; +} + +bool ChatHandler::HandleModifyArenaCommand(const char * args) +{ + if (!*args) + return false; + + Player *target = getSelectedPlayer(); + if (!target) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + int32 amount = (uint32)atoi(args); + + target->ModifyArenaPoints(amount); + + PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, GetNameLink(target).c_str(), target->GetArenaPoints()); + + return true; +} + +bool ChatHandler::HandleReviveCommand(const char *args) +{ + Player* target; + uint64 target_guid; + if (!extractPlayerTarget((char*)args,&target,&target_guid)) + return false; + + if (target) + { + target->ResurrectPlayer(target->GetSession()->GetSecurity() > SEC_PLAYER ? 1.0f : 0.5f); + target->SpawnCorpseBones(); + target->SaveToDB(); + } + else + // will resurrected at login without corpse + ObjectAccessor::Instance().ConvertCorpseForPlayer(target_guid); + + return true; +} + +bool ChatHandler::HandleAuraCommand(const char *args) +{ + Unit *target = getSelectedUnit(); + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spellID = extractSpellIdFromLink((char*)args); + + if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID)) + Aura::TryCreate(spellInfo, target, target); + + return true; +} + +bool ChatHandler::HandleUnAuraCommand(const char *args) +{ + Unit *target = getSelectedUnit(); + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + std::string argstr = args; + if (argstr == "all") + { + target->RemoveAllAuras(); + return true; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spellID = extractSpellIdFromLink((char*)args); + if (!spellID) + return false; + + target->RemoveAurasDueToSpell(spellID); + + return true; +} + +bool ChatHandler::HandleLinkGraveCommand(const char *args) +{ + if (!*args) + return false; + + char* px = strtok((char*)args, " "); + if (!px) + return false; + + uint32 g_id = (uint32)atoi(px); + + uint32 g_team; + + char* px2 = strtok(NULL, " "); + + if (!px2) + g_team = 0; + else if (strncmp(px2,"horde",6) == 0) + g_team = HORDE; + else if (strncmp(px2,"alliance",9) == 0) + g_team = ALLIANCE; + else + return false; + + WorldSafeLocsEntry const* graveyard = sWorldSafeLocsStore.LookupEntry(g_id); + + if (!graveyard) + { + PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, g_id); + SetSentErrorMessage(true); + return false; + } + + Player* player = m_session->GetPlayer(); + + uint32 zoneId = player->GetZoneId(); + + AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId); + if (!areaEntry || areaEntry->zone !=0) + { + PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, g_id,zoneId); + SetSentErrorMessage(true); + return false; + } + + if (objmgr.AddGraveYardLink(g_id,zoneId,g_team)) + PSendSysMessage(LANG_COMMAND_GRAVEYARDLINKED, g_id,zoneId); + else + PSendSysMessage(LANG_COMMAND_GRAVEYARDALRLINKED, g_id,zoneId); + + return true; +} + +bool ChatHandler::HandleNearGraveCommand(const char *args) +{ + uint32 g_team; + + size_t argslen = strlen(args); + + if (!*args) + g_team = 0; + else if (strncmp((char*)args,"horde",argslen) == 0) + g_team = HORDE; + else if (strncmp((char*)args,"alliance",argslen) == 0) + g_team = ALLIANCE; + else + return false; + + Player* player = m_session->GetPlayer(); + uint32 zone_id = player->GetZoneId(); + + WorldSafeLocsEntry const* graveyard = objmgr.GetClosestGraveYard( + player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(),player->GetMapId(),g_team); + + if (graveyard) + { + uint32 g_id = graveyard->ID; + + GraveYardData const* data = objmgr.FindGraveYardData(g_id,zone_id); + if (!data) + { + PSendSysMessage(LANG_COMMAND_GRAVEYARDERROR,g_id); + SetSentErrorMessage(true); + return false; + } + + g_team = data->team; + + std::string team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_NOTEAM); + + if (g_team == 0) + team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ANY); + else if (g_team == HORDE) + team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_HORDE); + else if (g_team == ALLIANCE) + team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ALLIANCE); + + PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),zone_id); + } + else + { + std::string team_name; + + if (g_team == 0) + team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ANY); + else if (g_team == HORDE) + team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_HORDE); + else if (g_team == ALLIANCE) + team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ALLIANCE); + + if (g_team == ~uint32(0)) + PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, zone_id); + else + PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, zone_id,team_name.c_str()); + } + + return true; +} + +//-----------------------Npc Commands----------------------- +bool ChatHandler::HandleNpcAllowMovementCommand(const char* /*args*/) +{ + if (sWorld.getAllowMovement()) + { + sWorld.SetAllowMovement(false); + SendSysMessage(LANG_CREATURE_MOVE_DISABLED); + } + else + { + sWorld.SetAllowMovement(true); + SendSysMessage(LANG_CREATURE_MOVE_ENABLED); + } + return true; +} + +bool ChatHandler::HandleNpcChangeEntryCommand(const char *args) +{ + if (!*args) + return false; + + uint32 newEntryNum = atoi(args); + if (!newEntryNum) + return false; + + Unit* unit = getSelectedUnit(); + if (!unit || unit->GetTypeId() != TYPEID_UNIT) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + Creature* creature = unit->ToCreature(); + if (creature->UpdateEntry(newEntryNum)) + SendSysMessage(LANG_DONE); + else + SendSysMessage(LANG_ERROR); + return true; +} + +bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/) +{ + Creature* target = getSelectedCreature(); + + if (!target) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + uint32 faction = target->getFaction(); + uint32 npcflags = target->GetUInt32Value(UNIT_NPC_FLAGS); + uint32 displayid = target->GetDisplayId(); + uint32 nativeid = target->GetNativeDisplayId(); + uint32 Entry = target->GetEntry(); + CreatureInfo const* cInfo = target->GetCreatureInfo(); + + int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL); + if (curRespawnDelay < 0) + curRespawnDelay = 0; + std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true); + std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true); + + PSendSysMessage(LANG_NPCINFO_CHAR, target->GetDBTableGUIDLow(), faction, npcflags, Entry, displayid, nativeid); + PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel()); + PSendSysMessage(LANG_NPCINFO_HEALTH,target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth()); + PSendSysMessage(LANG_NPCINFO_FLAGS, target->GetUInt32Value(UNIT_FIELD_FLAGS), target->GetUInt32Value(UNIT_DYNAMIC_FLAGS), target->getFaction()); + PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str()); + PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid,cInfo->pickpocketLootId,cInfo->SkinLootId); + PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId()); + PSendSysMessage(LANG_NPCINFO_PHASEMASK, target->GetPhaseMask()); + PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor()); + PSendSysMessage(LANG_NPCINFO_POSITION,float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ())); + if (const CreatureData* const linked = target->GetLinkedRespawnCreatureData()) + if (CreatureInfo const *master = GetCreatureInfo(linked->id)) + PSendSysMessage(LANG_NPCINFO_LINKGUID, objmgr.GetLinkedRespawnGuid(target->GetDBTableGUIDLow()), linked->id, master->Name); + + if ((npcflags & UNIT_NPC_FLAG_VENDOR)) + { + SendSysMessage(LANG_NPCINFO_VENDOR); + } + if ((npcflags & UNIT_NPC_FLAG_TRAINER)) + { + SendSysMessage(LANG_NPCINFO_TRAINER); + } + + return true; +} + +//play npc emote +bool ChatHandler::HandleNpcPlayEmoteCommand(const char *args) +{ + uint32 emote = atoi((char*)args); + + Creature* target = getSelectedCreature(); + if (!target) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote); + + return true; +} + +//TODO: NpcCommands that needs to be fixed : + +bool ChatHandler::HandleNpcAddWeaponCommand(const char* /*args*/) +{ + /*if (!*args) + return false; + + uint64 guid = m_session->GetPlayer()->GetSelection(); + if (guid == 0) + { + SendSysMessage(LANG_NO_SELECTION); + return true; + } + + Creature *pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid); + + if (!pCreature) + { + SendSysMessage(LANG_SELECT_CREATURE); + return true; + } + + char* pSlotID = strtok((char*)args, " "); + if (!pSlotID) + return false; + + char* pItemID = strtok(NULL, " "); + if (!pItemID) + return false; + + uint32 ItemID = atoi(pItemID); + uint32 SlotID = atoi(pSlotID); + + ItemPrototype* tmpItem = objmgr.GetItemPrototype(ItemID); + + bool added = false; + if (tmpItem) + { + switch(SlotID) + { + case 1: + pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID); + added = true; + break; + case 2: + pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID); + added = true; + break; + case 3: + pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID); + added = true; + break; + default: + PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID); + added = false; + break; + } + + if (added) + PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID); + } + else + { + PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID); + return true; + } + */ + return true; +} +//---------------------------------------------------------- + +bool ChatHandler::HandleExploreCheatCommand(const char *args) +{ + if (!*args) + return false; + + int flag = atoi((char*)args); + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + if (flag != 0) + { + PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_ALL,GetNameLink().c_str()); + } + else + { + PSendSysMessage(LANG_YOU_SET_EXPLORE_NOTHING, GetNameLink(chr).c_str()); + if (needReportToTarget(chr)) + ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,GetNameLink().c_str()); + } + + for (uint8 i=0; iGetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF); + } + else + { + m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0); + } + } + + return true; +} + +bool ChatHandler::HandleHoverCommand(const char *args) +{ + char* px = strtok((char*)args, " "); + uint32 flag; + if (!px) + flag = 1; + else + flag = atoi(px); + + m_session->GetPlayer()->SetHover(flag); + + if (flag) + SendSysMessage(LANG_HOVER_ENABLED); + else + SendSysMessage(LANG_HOVER_DISABLED); + + return true; +} + +void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel) +{ + if (player) + { + player->GiveLevel(newlevel); + player->InitTalentForLevel(); + player->SetUInt32Value(PLAYER_XP,0); + + if (needReportToTarget(player)) + { + if (oldlevel == newlevel) + ChatHandler(player).PSendSysMessage(LANG_YOURS_LEVEL_PROGRESS_RESET,GetNameLink().c_str()); + else if (oldlevel < newlevel) + ChatHandler(player).PSendSysMessage(LANG_YOURS_LEVEL_UP,GetNameLink().c_str(),newlevel); + else // if (oldlevel > newlevel) + ChatHandler(player).PSendSysMessage(LANG_YOURS_LEVEL_DOWN,GetNameLink().c_str(),newlevel); + } + } + else + { + // update level and XP at level, all other will be updated at loading + CharacterDatabase.PExecute("UPDATE characters SET level = '%u', xp = 0 WHERE guid = '%u'", newlevel, GUID_LOPART(player_guid)); + } +} + +bool ChatHandler::HandleCharacterLevelCommand(const char *args) +{ + char* nameStr; + char* levelStr; + extractOptFirstArg((char*)args,&nameStr,&levelStr); + if (!levelStr) + return false; + + // exception opt second arg: .character level $name + if (isalpha(levelStr[0])) + { + nameStr = levelStr; + levelStr = NULL; // current level will used + } + + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) + return false; + + int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid); + int32 newlevel = levelStr ? atoi(levelStr) : oldlevel; + + if (newlevel < 1) + return false; // invalid level + + if (newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level + newlevel = STRONG_MAX_LEVEL; + + HandleCharacterLevel(target,target_guid,oldlevel,newlevel); + + if (!m_session || m_session->GetPlayer() != target) // including player == NULL + { + std::string nameLink = playerLink(target_name); + PSendSysMessage(LANG_YOU_CHANGE_LVL,nameLink.c_str(),newlevel); + } + + return true; +} + +bool ChatHandler::HandleLevelUpCommand(const char *args) +{ + char* nameStr; + char* levelStr; + extractOptFirstArg((char*)args,&nameStr,&levelStr); + + // exception opt second arg: .character level $name + if (levelStr && isalpha(levelStr[0])) + { + nameStr = levelStr; + levelStr = NULL; // current level will used + } + + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget(nameStr,&target,&target_guid,&target_name)) + return false; + + int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid); + int32 addlevel = levelStr ? atoi(levelStr) : 1; + int32 newlevel = oldlevel + addlevel; + + if (newlevel < 1) + newlevel = 1; + + if (newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level + newlevel = STRONG_MAX_LEVEL; + + HandleCharacterLevel(target,target_guid,oldlevel,newlevel); + + if (!m_session || m_session->GetPlayer() != target) // including chr == NULL + { + std::string nameLink = playerLink(target_name); + PSendSysMessage(LANG_YOU_CHANGE_LVL,nameLink.c_str(),newlevel); + } + + return true; +} + +bool ChatHandler::HandleShowAreaCommand(const char *args) +{ + if (!*args) + return false; + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + int area = GetAreaFlagByAreaID(atoi((char*)args)); + int offset = area / 32; + uint32 val = (uint32)(1 << (area % 32)); + + if (area<0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); + chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val)); + + SendSysMessage(LANG_EXPLORE_AREA); + return true; +} + +bool ChatHandler::HandleHideAreaCommand(const char *args) +{ + if (!*args) + return false; + + Player *chr = getSelectedPlayer(); + if (chr == NULL) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + int area = GetAreaFlagByAreaID(atoi((char*)args)); + int offset = area / 32; + uint32 val = (uint32)(1 << (area % 32)); + + if (area<0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + { + SendSysMessage(LANG_BAD_VALUE); + SetSentErrorMessage(true); + return false; + } + + uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); + chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields ^ val)); + + SendSysMessage(LANG_UNEXPLORE_AREA); + return true; +} + +bool ChatHandler::HandleBankCommand(const char* /*args*/) +{ + m_session->SendShowBank(m_session->GetPlayer()->GetGUID()); + + return true; +} + +bool ChatHandler::HandleChangeWeather(const char *args) +{ + if (!*args) + return false; + + //Weather is OFF + if (!sWorld.getConfig(CONFIG_WEATHER)) + { + SendSysMessage(LANG_WEATHER_DISABLED); + SetSentErrorMessage(true); + return false; + } + + //*Change the weather of a cell + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + + if (!px || !py) + return false; + + uint32 type = (uint32)atoi(px); //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand + float grade = (float)atof(py); //0 to 1, sending -1 is instand good weather + + Player *player = m_session->GetPlayer(); + uint32 zoneid = player->GetZoneId(); + + Weather* wth = sWorld.FindWeather(zoneid); + + if (!wth) + wth = sWorld.AddWeather(zoneid); + if (!wth) + { + SendSysMessage(LANG_NO_WEATHER); + SetSentErrorMessage(true); + return false; + } + + wth->SetWeather(WeatherType(type), grade); + + return true; +} + +bool ChatHandler::HandleDebugSet32Bit(const char *args) +{ + if (!*args) + return false; + + WorldObject* target = getSelectedObject(); + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + char* px = strtok((char*)args, " "); + char* py = strtok(NULL, " "); + + if (!px || !py) + return false; + + uint32 Opcode = (uint32)atoi(px); + uint32 Value = (uint32)atoi(py); + if (Value > 32) //uint32 = 32 bits + return false; + + sLog.outDebug(GetTrinityString(LANG_SET_32BIT), Opcode, Value); + + uint32 iValue = Value ? 1 << (Value - 1) : 0; + target->SetUInt32Value(Opcode , iValue); + + PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode, iValue); + return true; +} + +bool ChatHandler::HandleTeleAddCommand(const char * args) +{ + if (!*args) + return false; + + Player *player=m_session->GetPlayer(); + if (!player) + return false; + + std::string name = args; + + if (objmgr.GetGameTele(name)) + { + SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST); + SetSentErrorMessage(true); + return false; + } + + GameTele tele; + tele.position_x = player->GetPositionX(); + tele.position_y = player->GetPositionY(); + tele.position_z = player->GetPositionZ(); + tele.orientation = player->GetOrientation(); + tele.mapId = player->GetMapId(); + tele.name = name; + + if (objmgr.AddGameTele(tele)) + { + SendSysMessage(LANG_COMMAND_TP_ADDED); + } + else + { + SendSysMessage(LANG_COMMAND_TP_ADDEDERR); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleTeleDelCommand(const char * args) +{ + if (!*args) + return false; + + std::string name = args; + + if (!objmgr.DeleteGameTele(name)) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + + SendSysMessage(LANG_COMMAND_TP_DELETED); + return true; +} + +bool ChatHandler::HandleListAurasCommand (const char * /*args*/) +{ + Unit *unit = getSelectedUnit(); + if (!unit) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + char const* talentStr = GetTrinityString(LANG_TALENT); + char const* passiveStr = GetTrinityString(LANG_PASSIVE); + + Unit::AuraApplicationMap const& uAuras = unit->GetAppliedAuras(); + PSendSysMessage(LANG_COMMAND_TARGET_LISTAURAS, uAuras.size()); + for (Unit::AuraApplicationMap::const_iterator itr = uAuras.begin(); itr != uAuras.end(); ++itr) + { + bool talent = GetTalentSpellCost(itr->second->GetBase()->GetId()) > 0; + + AuraApplication const * aurApp = itr->second; + Aura const * aura = aurApp->GetBase(); + char const* name = aura->GetSpellProto()->SpellName[GetSessionDbcLocale()]; + + if (m_session) + { + std::ostringstream ss_name; + ss_name << "|cffffffff|Hspell:" << aura->GetId() << "|h[" << name << "]|h|r"; + + PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, aura->GetId(), aurApp->GetEffectMask(), + aura->GetCharges(), aura->GetStackAmount(), aurApp->GetSlot(), + aura->GetDuration(), aura->GetMaxDuration(), + ss_name.str().c_str(), + (aura->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), + IS_PLAYER_GUID(aura->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(aura->GetCasterGUID())); + } + else + { + PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, aura->GetId(), aurApp->GetEffectMask(), + aura->GetCharges(), aura->GetStackAmount(), aurApp->GetSlot(), + aura->GetDuration(), aura->GetMaxDuration(), + name, + (aura->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""), + IS_PLAYER_GUID(aura->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(aura->GetCasterGUID())); + } + } + for (uint16 i = 0; i < TOTAL_AURAS; ++i) + { + Unit::AuraEffectList const& uAuraList = unit->GetAuraEffectsByType(AuraType(i)); + if (uAuraList.empty()) continue; + PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, uAuraList.size(), i); + for (Unit::AuraEffectList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr) + { + //bool talent = GetTalentSpellCost((*itr)->GetId()) > 0; + + char const* name = (*itr)->GetSpellProto()->SpellName[GetSessionDbcLocale()]; + + std::ostringstream ss_name; + ss_name << "|cffffffff|Hspell:" << (*itr)->GetId() << "|h[" << name << "]|h|r"; + + PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(), + (*itr)->GetAmount()); + } + } + return true; +} + +bool ChatHandler::HandleResetAchievementsCommand (const char * args) +{ + Player* target; + uint64 target_guid; + if (!extractPlayerTarget((char*)args,&target,&target_guid)) + return false; + + if (target) + target->GetAchievementMgr().Reset(); + else + AchievementMgr::DeleteFromDB(GUID_LOPART(target_guid)); + + return true; +} + +bool ChatHandler::HandleResetHonorCommand (const char * args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + target->SetUInt32Value(PLAYER_FIELD_KILLS, 0); + target->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0); + target->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, 0); + target->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0); + target->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0); + target->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL); + + return true; +} + +static bool HandleResetStatsOrLevelHelper(Player* player) +{ + ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(player->getClass()); + if (!cEntry) + { + sLog.outError("Class %u not found in DBC (Wrong DBC files?)",player->getClass()); + return false; + } + + uint8 powertype = cEntry->powerType; + + // reset m_form if no aura + if (!player->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) + player->m_form = FORM_NONE; + + player->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); + player->SetFloatValue(UNIT_FIELD_COMBATREACH, DEFAULT_COMBAT_REACH); + + player->setFactionForRace(player->getRace()); + + player->SetUInt32Value(UNIT_FIELD_BYTES_0, ((player->getRace()) | (player->getClass() << 8) | (player->getGender() << 16) | (powertype << 24))); + + // reset only if player not in some form; + if (player->m_form == FORM_NONE) + player->InitDisplayIds(); + + player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP); + player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form); + + player->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + + //-1 is default value + player->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); + + //player->SetUInt32Value(PLAYER_FIELD_BYTES, 0xEEE00000); + return true; +} + +bool ChatHandler::HandleResetLevelCommand(const char * args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + if (!HandleResetStatsOrLevelHelper(target)) + return false; + + // set starting level + uint32 start_level = target->getClass() != CLASS_DEATH_KNIGHT + ? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) + : sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); + + target->_ApplyAllLevelScaleItemMods(false); + + target->SetLevel(start_level); + target->InitRunes(); + target->InitStatsForLevel(true); + target->InitTaxiNodesForLevel(); + target->InitGlyphsForLevel(); + target->InitTalentForLevel(); + target->SetUInt32Value(PLAYER_XP,0); + + target->_ApplyAllLevelScaleItemMods(true); + + // reset level for pet + if (Pet* pet = target->GetPet()) + pet->SynchronizeLevelWithOwner(); + + return true; +} + +bool ChatHandler::HandleResetStatsCommand(const char * args) +{ + Player* target; + if (!extractPlayerTarget((char*)args,&target)) + return false; + + if (!HandleResetStatsOrLevelHelper(target)) + return false; + + target->InitRunes(); + target->InitStatsForLevel(true); + target->InitTaxiNodesForLevel(); + target->InitGlyphsForLevel(); + target->InitTalentForLevel(); + + return true; +} + +bool ChatHandler::HandleResetSpellsCommand(const char * args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + return false; + + if (target) + { + target->resetSpells(/* bool myClassOnly */); + + ChatHandler(target).SendSysMessage(LANG_RESET_SPELLS); + if (!m_session || m_session->GetPlayer() != target) + PSendSysMessage(LANG_RESET_SPELLS_ONLINE,GetNameLink(target).c_str()); + } + else + { + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_SPELLS), GUID_LOPART(target_guid)); + PSendSysMessage(LANG_RESET_SPELLS_OFFLINE,target_name.c_str()); + } + + return true; +} + +bool ChatHandler::HandleResetTalentsCommand(const char * args) +{ + Player* target; + uint64 target_guid; + std::string target_name; + if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name)) + { + // Try reset talents as Hunter Pet + Creature* creature = getSelectedCreature(); + if (!*args && creature && creature->isPet()) + { + Unit *owner = creature->GetOwner(); + if (owner && owner->GetTypeId() == TYPEID_PLAYER && ((Pet *)creature)->IsPermanentPetFor(owner->ToPlayer())) + { + ((Pet *)creature)->resetTalents(true); + owner->ToPlayer()->SendTalentsInfoData(true); + + ChatHandler(owner->ToPlayer()).SendSysMessage(LANG_RESET_PET_TALENTS); + if (!m_session || m_session->GetPlayer() != owner->ToPlayer()) + PSendSysMessage(LANG_RESET_PET_TALENTS_ONLINE,GetNameLink(owner->ToPlayer()).c_str()); + } + return true; + } + + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + if (target) + { + target->resetTalents(true); + target->SendTalentsInfoData(false); + ChatHandler(target).SendSysMessage(LANG_RESET_TALENTS); + if (!m_session || m_session->GetPlayer() != target) + PSendSysMessage(LANG_RESET_TALENTS_ONLINE,GetNameLink(target).c_str()); + + Pet* pet = target->GetPet(); + Pet::resetTalentsForAllPetsOf(target,pet); + if (pet) + target->SendTalentsInfoData(true); + return true; + } + else if (target_guid) + { + uint32 at_flags = AT_LOGIN_NONE | AT_LOGIN_RESET_PET_TALENTS; + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",at_flags, GUID_LOPART(target_guid)); + std::string nameLink = playerLink(target_name); + PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,nameLink.c_str()); + return true; + } + + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; +} + +bool ChatHandler::HandleResetAllCommand(const char * args) +{ + if (!*args) + return false; + + std::string casename = args; + + AtLoginFlags atLogin; + + // Command specially created as single command to prevent using short case names + if (casename == "spells") + { + atLogin = AT_LOGIN_RESET_SPELLS; + sWorld.SendWorldText(LANG_RESETALL_SPELLS); + if (!m_session) + SendSysMessage(LANG_RESETALL_SPELLS); + } + else if (casename == "talents") + { + atLogin = AtLoginFlags(AT_LOGIN_RESET_TALENTS | AT_LOGIN_RESET_PET_TALENTS); + sWorld.SendWorldText(LANG_RESETALL_TALENTS); + if (!m_session) + SendSysMessage(LANG_RESETALL_TALENTS); + } + else + { + PSendSysMessage(LANG_RESETALL_UNKNOWN_CASE,args); + SetSentErrorMessage(true); + return false; + } + + CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE (at_login & '%u') = '0'",atLogin,atLogin); + + ObjectAccessor::Guard guard(*HashMapHolder::GetLock()); + HashMapHolder::MapType const& plist = ObjectAccessor::Instance().GetPlayers(); + for (HashMapHolder::MapType::const_iterator itr = plist.begin(); itr != plist.end(); ++itr) + itr->second->SetAtLoginFlag(atLogin); + + return true; +} + +bool ChatHandler::HandleServerShutDownCancelCommand(const char* /*args*/) +{ + sWorld.ShutdownCancel(); + return true; +} + +bool ChatHandler::HandleServerShutDownCommand(const char *args) +{ + if (!*args) + return false; + + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if ((time == 0 && (time_str[0] != '0' || time_str[1] != '\0')) || time < 0) + return false; + + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, 0, exitcode); + } + else + sWorld.ShutdownServ(time,0,SHUTDOWN_EXIT_CODE); + return true; +} + +bool ChatHandler::HandleServerRestartCommand(const char *args) +{ + if (!*args) + return false; + + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if ((time == 0 && (time_str[0] != '0' || time_str[1] != '\0') || time < 0)) + return false; + + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, SHUTDOWN_MASK_RESTART, exitcode); + } + else + sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); + return true; +} + +bool ChatHandler::HandleServerIdleRestartCommand(const char *args) +{ + if (!*args) + return false; + + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if ((time == 0 && (time_str[0] != '0' || time_str[1] != '\0') || time < 0)) + return false; + + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE, exitcode); + } + else + sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE,RESTART_EXIT_CODE); + return true; +} + +bool ChatHandler::HandleServerIdleShutDownCommand(const char *args) +{ + if (!*args) + return false; + + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if (time == 0 && (time_str[0] != '0' || time_str[1] != '\0') || time < 0) + return false; + + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, SHUTDOWN_MASK_IDLE, exitcode); + } + else + sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE,SHUTDOWN_EXIT_CODE); + return true; +} + +bool ChatHandler::HandleQuestAdd(const char *args) +{ + Player* player = getSelectedPlayer(); + if (!player) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // .addquest #entry' + // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hquest"); + if (!cId) + return false; + + uint32 entry = atol(cId); + + Quest const* pQuest = objmgr.GetQuestTemplate(entry); + + if (!pQuest) + { + PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND,entry); + SetSentErrorMessage(true); + return false; + } + + // check item starting quest (it can work incorrectly if added without item in inventory) + for (uint32 id = 0; id < sItemStorage.MaxEntry; id++) + { + ItemPrototype const *pProto = sItemStorage.LookupEntry(id); + if (!pProto) + continue; + + if (pProto->StartQuest == entry) + { + PSendSysMessage(LANG_COMMAND_QUEST_STARTFROMITEM, entry, pProto->ItemId); + SetSentErrorMessage(true); + return false; + } + } + + // ok, normal (creature/GO starting) quest + if (player->CanAddQuest(pQuest, true)) + { + player->AddQuest(pQuest, NULL); + + if (player->CanCompleteQuest(entry)) + player->CompleteQuest(entry); + } + + return true; +} + +bool ChatHandler::HandleQuestRemove(const char *args) +{ + Player* player = getSelectedPlayer(); + if (!player) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // .removequest #entry' + // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hquest"); + if (!cId) + return false; + + uint32 entry = atol(cId); + + Quest const* pQuest = objmgr.GetQuestTemplate(entry); + + if (!pQuest) + { + PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry); + SetSentErrorMessage(true); + return false; + } + + // remove all quest entries for 'entry' from quest log + for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) + { + uint32 quest = player->GetQuestSlotQuestId(slot); + if (quest == entry) + { + player->SetQuestSlot(slot,0); + + // we ignore unequippable quest items in this case, its' still be equipped + player->TakeQuestSourceItem(quest, false); + } + } + + // set quest status to not started (will updated in DB at next save) + player->SetQuestStatus(entry, QUEST_STATUS_NONE); + + // reset rewarded for restart repeatable quest + player->getQuestStatusMap()[entry].m_rewarded = false; + + SendSysMessage(LANG_COMMAND_QUEST_REMOVED); + return true; +} + +bool ChatHandler::HandleQuestComplete(const char *args) +{ + Player* player = getSelectedPlayer(); + if (!player) + { + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; + } + + // .quest complete #entry + // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r + char* cId = extractKeyFromLink((char*)args,"Hquest"); + if (!cId) + return false; + + uint32 entry = atol(cId); + + Quest const* pQuest = objmgr.GetQuestTemplate(entry); + + // If player doesn't have the quest + if (!pQuest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE) + { + PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry); + SetSentErrorMessage(true); + return false; + } + + // Add quest items for quests that require items + for (uint8 x = 0; x < QUEST_ITEM_OBJECTIVES_COUNT; ++x) + { + uint32 id = pQuest->ReqItemId[x]; + uint32 count = pQuest->ReqItemCount[x]; + if (!id || !count) + continue; + + uint32 curItemCount = player->GetItemCount(id,true); + + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, id, count-curItemCount); + if (msg == EQUIP_ERR_OK) + { + Item* item = player->StoreNewItem(dest, id, true); + player->SendNewItem(item,count-curItemCount,true,false); + } + } + + // All creature/GO slain/casted (not required, but otherwise it will display "Creature slain 0/10") + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + { + uint32 creature = pQuest->ReqCreatureOrGOId[i]; + uint32 creaturecount = pQuest->ReqCreatureOrGOCount[i]; + + if (uint32 spell_id = pQuest->ReqSpell[i]) + { + for (uint16 z = 0; z < creaturecount; ++z) + player->CastedCreatureOrGO(creature,0,spell_id); + } + else if (creature > 0) + { + if (CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(creature)) + for (uint16 z = 0; z < creaturecount; ++z) + player->KilledMonster(cInfo,0); + } + else if (creature < 0) + { + for (uint16 z = 0; z < creaturecount; ++z) + player->CastedCreatureOrGO(creature,0,0); + } + } + + // If the quest requires reputation to complete + if (uint32 repFaction = pQuest->GetRepObjectiveFaction()) + { + uint32 repValue = pQuest->GetRepObjectiveValue(); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); + if (curRep < repValue) + if (FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry,repValue); + } + + // If the quest requires a SECOND reputation to complete + if (uint32 repFaction = pQuest->GetRepObjectiveFaction2()) + { + uint32 repValue2 = pQuest->GetRepObjectiveValue2(); + uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); + if (curRep < repValue2) + if (FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction)) + player->GetReputationMgr().SetReputation(factionEntry,repValue2); + } + + // If the quest requires money + int32 ReqOrRewMoney = pQuest->GetRewOrReqMoney(); + if (ReqOrRewMoney < 0) + player->ModifyMoney(-ReqOrRewMoney); + + player->CompleteQuest(entry); + return true; +} + +bool ChatHandler::HandleBanAccountCommand(const char *args) +{ + return HandleBanHelper(BAN_ACCOUNT,args); +} + +bool ChatHandler::HandleBanCharacterCommand(const char *args) +{ + return HandleBanHelper(BAN_CHARACTER,args); +} + +bool ChatHandler::HandleBanIPCommand(const char *args) +{ + return HandleBanHelper(BAN_IP,args); +} + +bool ChatHandler::HandleBanHelper(BanMode mode, const char *args) +{ + if (!*args) + return false; + + char* cnameOrIP = strtok ((char*)args, " "); + if (!cnameOrIP) + return false; + + std::string nameOrIP = cnameOrIP; + + char* duration = strtok (NULL," "); + if (!duration || !atoi(duration)) + return false; + + char* reason = strtok (NULL,""); + if (!reason) + return false; + + switch(mode) + { + case BAN_ACCOUNT: + if (!AccountMgr::normalizeString(nameOrIP)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,nameOrIP.c_str()); + SetSentErrorMessage(true); + return false; + } + break; + case BAN_CHARACTER: + if (!normalizePlayerName(nameOrIP)) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + break; + case BAN_IP: + if (!IsIPAddress(nameOrIP.c_str())) + return false; + break; + } + + switch(sWorld.BanAccount(mode, nameOrIP, duration, reason,m_session ? m_session->GetPlayerName() : "")) + { + case BAN_SUCCESS: + if (atoi(duration)>0) + PSendSysMessage(LANG_BAN_YOUBANNED,nameOrIP.c_str(),secsToTimeString(TimeStringToSecs(duration),true).c_str(),reason); + else + PSendSysMessage(LANG_BAN_YOUPERMBANNED,nameOrIP.c_str(),reason); + break; + case BAN_SYNTAX_ERROR: + return false; + case BAN_NOTFOUND: + switch(mode) + { + default: + PSendSysMessage(LANG_BAN_NOTFOUND,"account",nameOrIP.c_str()); + break; + case BAN_CHARACTER: + PSendSysMessage(LANG_BAN_NOTFOUND,"character",nameOrIP.c_str()); + break; + case BAN_IP: + PSendSysMessage(LANG_BAN_NOTFOUND,"ip",nameOrIP.c_str()); + break; + } + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleUnBanAccountCommand(const char *args) +{ + return HandleUnBanHelper(BAN_ACCOUNT,args); +} + +bool ChatHandler::HandleUnBanCharacterCommand(const char *args) +{ + return HandleUnBanHelper(BAN_CHARACTER,args); +} + +bool ChatHandler::HandleUnBanIPCommand(const char *args) +{ + return HandleUnBanHelper(BAN_IP,args); +} + +bool ChatHandler::HandleUnBanHelper(BanMode mode, const char *args) +{ + if (!*args) + return false; + + char* cnameOrIP = strtok ((char*)args, " "); + if (!cnameOrIP) + return false; + + std::string nameOrIP = cnameOrIP; + + switch(mode) + { + case BAN_ACCOUNT: + if (!AccountMgr::normalizeString(nameOrIP)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,nameOrIP.c_str()); + SetSentErrorMessage(true); + return false; + } + break; + case BAN_CHARACTER: + if (!normalizePlayerName(nameOrIP)) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + break; + case BAN_IP: + if (!IsIPAddress(nameOrIP.c_str())) + return false; + break; + } + + if (sWorld.RemoveBanAccount(mode,nameOrIP)) + PSendSysMessage(LANG_UNBAN_UNBANNED,nameOrIP.c_str()); + else + PSendSysMessage(LANG_UNBAN_ERROR,nameOrIP.c_str()); + + return true; +} + +bool ChatHandler::HandleBanInfoAccountCommand(const char *args) +{ + if (!*args) + return false; + + char* cname = strtok((char*)args, ""); + if (!cname) + return false; + + std::string account_name = cname; + if (!AccountMgr::normalizeString(account_name)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + uint32 accountid = accmgr.GetId(account_name); + if (!accountid) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + return true; + } + + return HandleBanInfoHelper(accountid,account_name.c_str()); +} + +bool ChatHandler::HandleBanInfoCharacterCommand(const char *args) +{ + Player* target; + uint64 target_guid; + if (!extractPlayerTarget((char*)args,&target,&target_guid)) + return false; + + uint32 accountid = target ? target->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(target_guid); + + std::string accountname; + if (!accmgr.GetName(accountid,accountname)) + { + PSendSysMessage(LANG_BANINFO_NOCHARACTER); + return true; + } + + return HandleBanInfoHelper(accountid,accountname.c_str()); +} + +bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname) +{ + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid); + if (!result) + { + PSendSysMessage(LANG_BANINFO_NOACCOUNTBAN, accountname); + return true; + } + + PSendSysMessage(LANG_BANINFO_BANHISTORY,accountname); + do + { + Field* fields = result->Fetch(); + + time_t unbandate = time_t(fields[3].GetUInt64()); + bool active = false; + if (fields[2].GetBool() && (fields[1].GetUInt64() == (uint64)0 ||unbandate >= time(NULL))) + active = true; + bool permanent = (fields[1].GetUInt64() == (uint64)0); + std::string bantime = permanent?GetTrinityString(LANG_BANINFO_INFINITE):secsToTimeString(fields[1].GetUInt64(), true); + PSendSysMessage(LANG_BANINFO_HISTORYENTRY, + fields[0].GetString(), bantime.c_str(), active ? GetTrinityString(LANG_BANINFO_YES):GetTrinityString(LANG_BANINFO_NO), fields[4].GetString(), fields[5].GetString()); + }while (result->NextRow()); + + return true; +} + +bool ChatHandler::HandleBanInfoIPCommand(const char *args) +{ + if (!*args) + return false; + + char* cIP = strtok ((char*)args, ""); + if (!cIP) + return false; + + if (!IsIPAddress(cIP)) + return false; + + std::string IP = cIP; + + LoginDatabase.escape_string(IP); + QueryResult_AutoPtr result = LoginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",IP.c_str()); + if (!result) + { + PSendSysMessage(LANG_BANINFO_NOIP); + return true; + } + + Field *fields = result->Fetch(); + bool permanent = !fields[6].GetUInt64(); + PSendSysMessage(LANG_BANINFO_IPENTRY, + fields[0].GetString(), fields[1].GetString(), permanent ? GetTrinityString(LANG_BANINFO_NEVER):fields[2].GetString(), + permanent ? GetTrinityString(LANG_BANINFO_INFINITE):secsToTimeString(fields[3].GetUInt64(), true).c_str(), fields[4].GetString(), fields[5].GetString()); + + return true; +} + +bool ChatHandler::HandleBanListCharacterCommand(const char *args) +{ + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate<>bandate"); + + char* cFilter = strtok ((char*)args, " "); + if (!cFilter) + return false; + + std::string filter = cFilter; + LoginDatabase.escape_string(filter); + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),filter.c_str()); + if (!result) + { + PSendSysMessage(LANG_BANLIST_NOCHARACTER); + return true; + } + + return HandleBanListHelper(result); +} + +bool ChatHandler::HandleBanListAccountCommand(const char *args) +{ + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate<>bandate"); + + char* cFilter = strtok((char*)args, " "); + std::string filter = cFilter ? cFilter : ""; + LoginDatabase.escape_string(filter); + + QueryResult_AutoPtr result; + + if (filter.empty()) + { + result = LoginDatabase.Query("SELECT account.id, username FROM account, account_banned" + " WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id"); + } + else + { + result = LoginDatabase.PQuery("SELECT account.id, username FROM account, account_banned" + " WHERE account.id = account_banned.id AND active = 1 AND username "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" GROUP BY account.id", + filter.c_str()); + } + + if (!result) + { + PSendSysMessage(LANG_BANLIST_NOACCOUNT); + return true; + } + + return HandleBanListHelper(result); +} + +bool ChatHandler::HandleBanListHelper(QueryResult_AutoPtr result) +{ + PSendSysMessage(LANG_BANLIST_MATCHINGACCOUNT); + + // Chat short output + if (m_session) + { + do + { + Field* fields = result->Fetch(); + uint32 accountid = fields[0].GetUInt32(); + + QueryResult_AutoPtr banresult = LoginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.id=account.id",accountid); + if (banresult) + { + Field* fields2 = banresult->Fetch(); + PSendSysMessage("%s",fields2[0].GetString()); + } + } while (result->NextRow()); + } + // Console wide output + else + { + SendSysMessage(LANG_BANLIST_ACCOUNTS); + SendSysMessage(" ==============================================================================="); + SendSysMessage(LANG_BANLIST_ACCOUNTS_HEADER); + do + { + SendSysMessage("-------------------------------------------------------------------------------"); + Field *fields = result->Fetch(); + uint32 account_id = fields[0].GetUInt32 (); + + std::string account_name; + + // "account" case, name can be get in same query + if (result->GetFieldCount() > 1) + account_name = fields[1].GetCppString(); + // "character" case, name need extract from another DB + else + accmgr.GetName (account_id,account_name); + + // No SQL injection. id is uint32. + QueryResult_AutoPtr banInfo = LoginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u ORDER BY unbandate", account_id); + if (banInfo) + { + Field *fields2 = banInfo->Fetch(); + do + { + time_t t_ban = fields2[0].GetUInt64(); + tm* aTm_ban = localtime(&t_ban); + + if (fields2[0].GetUInt64() == fields2[1].GetUInt64()) + { + PSendSysMessage("|%-15.15s|%02d-%02d-%02d %02d:%02d| permanent |%-15.15s|%-15.15s|", + account_name.c_str(),aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min, + fields2[2].GetString(),fields2[3].GetString()); + } + else + { + time_t t_unban = fields2[1].GetUInt64(); + tm* aTm_unban = localtime(&t_unban); + PSendSysMessage("|%-15.15s|%02d-%02d-%02d %02d:%02d|%02d-%02d-%02d %02d:%02d|%-15.15s|%-15.15s|", + account_name.c_str(),aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min, + aTm_unban->tm_year%100, aTm_unban->tm_mon+1, aTm_unban->tm_mday, aTm_unban->tm_hour, aTm_unban->tm_min, + fields2[2].GetString(),fields2[3].GetString()); + } + }while (banInfo->NextRow()); + } + }while (result->NextRow()); + SendSysMessage(" ==============================================================================="); + } + return true; +} + +bool ChatHandler::HandleBanListIPCommand(const char *args) +{ + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate<>bandate"); + + char* cFilter = strtok((char*)args, " "); + std::string filter = cFilter ? cFilter : ""; + LoginDatabase.escape_string(filter); + + QueryResult_AutoPtr result; + + if (filter.empty()) + { + result = LoginDatabase.Query ("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" + " WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP())" + " ORDER BY unbandate"); + } + else + { + result = LoginDatabase.PQuery("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" + " WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP()) AND ip "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'") + " ORDER BY unbandate",filter.c_str()); + } + + if (!result) + { + PSendSysMessage(LANG_BANLIST_NOIP); + return true; + } + + PSendSysMessage(LANG_BANLIST_MATCHINGIP); + // Chat short output + if (m_session) + { + do + { + Field* fields = result->Fetch(); + PSendSysMessage("%s",fields[0].GetString()); + } while (result->NextRow()); + } + // Console wide output + else + { + SendSysMessage(LANG_BANLIST_IPS); + SendSysMessage(" ==============================================================================="); + SendSysMessage(LANG_BANLIST_IPS_HEADER); + do + { + SendSysMessage("-------------------------------------------------------------------------------"); + Field *fields = result->Fetch(); + time_t t_ban = fields[1].GetUInt64(); + tm* aTm_ban = localtime(&t_ban); + if (fields[1].GetUInt64() == fields[2].GetUInt64()) + { + PSendSysMessage("|%-15.15s|%02d-%02d-%02d %02d:%02d| permanent |%-15.15s|%-15.15s|", + fields[0].GetString(), aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min, + fields[3].GetString(), fields[4].GetString()); + } + else + { + time_t t_unban = fields[2].GetUInt64(); + tm* aTm_unban = localtime(&t_unban); + PSendSysMessage("|%-15.15s|%02d-%02d-%02d %02d:%02d|%02d-%02d-%02d %02d:%02d|%-15.15s|%-15.15s|", + fields[0].GetString(), aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min, + aTm_unban->tm_year%100, aTm_unban->tm_mon+1, aTm_unban->tm_mday, aTm_unban->tm_hour, aTm_unban->tm_min, + fields[3].GetString(), fields[4].GetString()); + } + }while (result->NextRow()); + SendSysMessage(" ==============================================================================="); + } + + return true; +} + +bool ChatHandler::HandleRespawnCommand(const char* /*args*/) +{ + Player* pl = m_session->GetPlayer(); + + // accept only explicitly selected target (not implicitly self targeting case) + Unit* target = getSelectedUnit(); + if (pl->GetSelection() && target) + { + if (target->GetTypeId() != TYPEID_UNIT || target->isPet()) + { + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (target->isDead()) + target->ToCreature()->Respawn(); + return true; + } + + CellPair p(Trinity::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + Trinity::RespawnDo u_do; + Trinity::WorldObjectWorker worker(pl,u_do); + + TypeContainerVisitor, GridTypeMapContainer > obj_worker(worker); + cell.Visit(p, obj_worker, *pl->GetMap()); + + return true; +} + +bool ChatHandler::HandleGMFlyCommand(const char *args) +{ + if (!*args) + return false; + + Player *target = getSelectedPlayer(); + if (!target) + target = m_session->GetPlayer(); + + WorldPacket data(12); + if (strncmp(args, "on", 3) == 0) + data.SetOpcode(SMSG_MOVE_SET_CAN_FLY); + else if (strncmp(args, "off", 4) == 0) + data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY); + else + { + SendSysMessage(LANG_USE_BOL); + return false; + } + data.append(target->GetPackGUID()); + data << uint32(0); // unknown + target->SendMessageToSet(&data, true); + PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, GetNameLink(target).c_str(), args); + return true; +} + +bool ChatHandler::HandlePDumpLoadCommand(const char *args) +{ + if (!*args) + return false; + + char * file = strtok((char*)args, " "); + if (!file) + return false; + + char * account = strtok(NULL, " "); + if (!account) + return false; + + std::string account_name = account; + if (!AccountMgr::normalizeString(account_name)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + uint32 account_id = accmgr.GetId(account_name); + if (!account_id) + { + account_id = atoi(account); // use original string + if (!account_id) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + } + + if (!accmgr.GetName(account_id,account_name)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + char* guid_str = NULL; + char* name_str = strtok(NULL, " "); + + std::string name; + if (name_str) + { + name = name_str; + // normalize the name if specified and check if it exists + if (!normalizePlayerName(name)) + { + PSendSysMessage(LANG_INVALID_CHARACTER_NAME); + SetSentErrorMessage(true); + return false; + } + + if (ObjectMgr::CheckPlayerName(name,true) != CHAR_NAME_SUCCESS) + { + PSendSysMessage(LANG_INVALID_CHARACTER_NAME); + SetSentErrorMessage(true); + return false; + } + + guid_str = strtok(NULL, " "); + } + + uint32 guid = 0; + + if (guid_str) + { + guid = atoi(guid_str); + if (!guid) + { + PSendSysMessage(LANG_INVALID_CHARACTER_GUID); + SetSentErrorMessage(true); + return false; + } + + if (objmgr.GetPlayerAccountIdByGUID(guid)) + { + PSendSysMessage(LANG_CHARACTER_GUID_IN_USE,guid); + SetSentErrorMessage(true); + return false; + } + } + + switch(PlayerDumpReader().LoadDump(file, account_id, name, guid)) + { + case DUMP_SUCCESS: + PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS); + break; + case DUMP_FILE_OPEN_ERROR: + PSendSysMessage(LANG_FILE_OPEN_FAIL,file); + SetSentErrorMessage(true); + return false; + case DUMP_FILE_BROKEN: + PSendSysMessage(LANG_DUMP_BROKEN,file); + SetSentErrorMessage(true); + return false; + case DUMP_TOO_MANY_CHARS: + PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL,account_name.c_str(),account_id); + SetSentErrorMessage(true); + return false; + default: + PSendSysMessage(LANG_COMMAND_IMPORT_FAILED); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandlePDumpWriteCommand(const char *args) +{ + if (!*args) + return false; + + char* file = strtok((char*)args, " "); + char* p2 = strtok(NULL, " "); + + if (!file || !p2) + return false; + + uint32 guid; + // character name can't start from number + if (isNumeric(p2[0])) + guid = atoi(p2); + else + { + std::string name = extractPlayerNameFromLink(p2); + if (name.empty()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + guid = objmgr.GetPlayerGUIDByName(name); + } + + if (!objmgr.GetPlayerAccountIdByGUID(guid)) + { + PSendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + switch(PlayerDumpWriter().WriteDump(file, guid)) + { + case DUMP_SUCCESS: + PSendSysMessage(LANG_COMMAND_EXPORT_SUCCESS); + break; + case DUMP_FILE_OPEN_ERROR: + PSendSysMessage(LANG_FILE_OPEN_FAIL,file); + SetSentErrorMessage(true); + return false; + default: + PSendSysMessage(LANG_COMMAND_EXPORT_FAILED); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleMovegensCommand(const char* /*args*/) +{ + Unit* unit = getSelectedUnit(); + if (!unit) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + PSendSysMessage(LANG_MOVEGENS_LIST,(unit->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"),unit->GetGUIDLow()); + + MotionMaster* mm = unit->GetMotionMaster(); + for (uint8 i = 0; i < MAX_MOTION_SLOT; ++i) + { + MovementGenerator* mg = mm->GetMotionSlot(i); + if (!mg) + { + SendSysMessage("Empty"); + continue; + } + switch(mg->GetMovementGeneratorType()) + { + case IDLE_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_IDLE); break; + case RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_RANDOM); break; + case WAYPOINT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_WAYPOINT); break; + case ANIMAL_RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_ANIMAL_RANDOM); break; + case CONFUSED_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_CONFUSED); break; + case TARGETED_MOTION_TYPE: + { + if (unit->GetTypeId() == TYPEID_PLAYER) + { + TargetedMovementGenerator const* mgen = static_cast const*>(mg); + Unit* target = mgen->GetTarget(); + if (target) + PSendSysMessage(LANG_MOVEGENS_TARGETED_PLAYER,target->GetName(),target->GetGUIDLow()); + else + SendSysMessage(LANG_MOVEGENS_TARGETED_NULL); + } + else + { + TargetedMovementGenerator const* mgen = static_cast const*>(mg); + Unit* target = mgen->GetTarget(); + if (target) + PSendSysMessage(LANG_MOVEGENS_TARGETED_CREATURE,target->GetName(),target->GetGUIDLow()); + else + SendSysMessage(LANG_MOVEGENS_TARGETED_NULL); + } + break; + } + case HOME_MOTION_TYPE: + if (unit->GetTypeId() == TYPEID_UNIT) + { + float x,y,z; + mg->GetDestination(x,y,z); + PSendSysMessage(LANG_MOVEGENS_HOME_CREATURE,x,y,z); + } + else + SendSysMessage(LANG_MOVEGENS_HOME_PLAYER); + break; + case FLIGHT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FLIGHT); break; + case POINT_MOTION_TYPE: + { + float x,y,z; + mg->GetDestination(x,y,z); + PSendSysMessage(LANG_MOVEGENS_POINT,x,y,z); + break; + } + case FLEEING_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FEAR); break; + case DISTRACT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_DISTRACT); break; + default: + PSendSysMessage(LANG_MOVEGENS_UNKNOWN,mg->GetMovementGeneratorType()); + break; + } + } + return true; +} + +bool ChatHandler::HandleServerPLimitCommand(const char *args) +{ + if (*args) + { + char* param = strtok((char*)args, " "); + if (!param) + return false; + + int l = strlen(param); + + if (strncmp(param,"player",l) == 0) + sWorld.SetPlayerSecurityLimit(SEC_PLAYER); + else if (strncmp(param,"moderator",l) == 0) + sWorld.SetPlayerSecurityLimit(SEC_MODERATOR); + else if (strncmp(param,"gamemaster",l) == 0) + sWorld.SetPlayerSecurityLimit(SEC_GAMEMASTER); + else if (strncmp(param,"administrator",l) == 0) + sWorld.SetPlayerSecurityLimit(SEC_ADMINISTRATOR); + else if (strncmp(param,"reset",l) == 0) + sWorld.SetPlayerLimit(sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT)); + else + { + int val = atoi(param); + if (val < 0) + sWorld.SetPlayerSecurityLimit(AccountTypes(uint32(-val))); + else + sWorld.SetPlayerLimit(val); + } + + // kick all low security level players + if (sWorld.GetPlayerSecurityLimit() > SEC_PLAYER) + sWorld.KickAllLess(sWorld.GetPlayerSecurityLimit()); + } + + uint32 pLimit = sWorld.GetPlayerAmountLimit(); + AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit(); + char const* secName = ""; + switch(allowedAccountType) + { + case SEC_PLAYER: secName = "Player"; break; + case SEC_MODERATOR: secName = "Moderator"; break; + case SEC_GAMEMASTER: secName = "Gamemaster"; break; + case SEC_ADMINISTRATOR: secName = "Administrator"; break; + default: secName = ""; break; + } + + PSendSysMessage("Player limits: amount %u, min. security level %s.",pLimit,secName); + + return true; +} + +bool ChatHandler::HandleCastCommand(const char *args) +{ + if (!*args) + return false; + + Unit* target = getSelectedUnit(); + + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell = extractSpellIdFromLink((char*)args); + if (!spell) + return false; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); + return false; + } + + if (!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + SetSentErrorMessage(true); + return false; + } + + char* trig_str = strtok(NULL, " "); + if (trig_str) + { + int l = strlen(trig_str); + if (strncmp(trig_str,"triggered",l) != 0) + return false; + } + + bool triggered = (trig_str != NULL); + + m_session->GetPlayer()->CastSpell(target,spell,triggered); + + return true; +} + +bool ChatHandler::HandleCastBackCommand(const char *args) +{ + Creature* caster = getSelectedCreature(); + + if (!caster) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell = extractSpellIdFromLink((char*)args); + if (!spell || !sSpellStore.LookupEntry(spell)) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); + return false; + } + + char* trig_str = strtok(NULL, " "); + if (trig_str) + { + int l = strlen(trig_str); + if (strncmp(trig_str,"triggered",l) != 0) + return false; + } + + bool triggered = (trig_str != NULL); + + caster->SetFacingToObject(m_session->GetPlayer()); + + caster->CastSpell(m_session->GetPlayer(),spell,triggered); + + return true; +} + +bool ChatHandler::HandleCastDistCommand(const char *args) +{ + if (!*args) + return false; + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell = extractSpellIdFromLink((char*)args); + if (!spell) + return false; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); + return false; + } + + if (!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + SetSentErrorMessage(true); + return false; + } + + char *distStr = strtok(NULL, " "); + + float dist = 0; + + if (distStr) + sscanf(distStr, "%f", &dist); + + char* trig_str = strtok(NULL, " "); + if (trig_str) + { + int l = strlen(trig_str); + if (strncmp(trig_str,"triggered",l) != 0) + return false; + } + + bool triggered = (trig_str != NULL); + + float x,y,z; + m_session->GetPlayer()->GetClosePoint(x,y,z,dist); + + m_session->GetPlayer()->CastSpell(x,y,z,spell,triggered); + return true; +} + +bool ChatHandler::HandleCastTargetCommand(const char *args) +{ + Creature* caster = getSelectedCreature(); + + if (!caster) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + if (!caster->getVictim()) + { + SendSysMessage(LANG_SELECTED_TARGET_NOT_HAVE_VICTIM); + SetSentErrorMessage(true); + return false; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell = extractSpellIdFromLink((char*)args); + if (!spell || !sSpellStore.LookupEntry(spell)) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); + return false; + } + + char* trig_str = strtok(NULL, " "); + if (trig_str) + { + int l = strlen(trig_str); + if (strncmp(trig_str,"triggered",l) != 0) + return false; + } + + bool triggered = (trig_str != NULL); + + caster->SetFacingToObject(m_session->GetPlayer()); + + caster->CastSpell(caster->getVictim(),spell,triggered); + + return true; +} + +/* +ComeToMe command REQUIRED for 3rd party scripting library to have access to PointMovementGenerator +Without this function 3rd party scripting library will get linking errors (unresolved external) +when attempting to use the PointMovementGenerator +*/ +bool ChatHandler::HandleComeToMeCommand(const char *args) +{ + char* newFlagStr = strtok((char*)args, " "); + + if (!newFlagStr) + return false; + + uint32 newFlags = (uint32)strtoul(newFlagStr, NULL, 0); + + Creature* caster = getSelectedCreature(); + if (!caster) + { + m_session->GetPlayer()->SetUnitMovementFlags(newFlags); + SendSysMessage(LANG_SELECT_CREATURE); + SetSentErrorMessage(true); + return false; + } + + caster->SetUnitMovementFlags(newFlags); + + Player* pl = m_session->GetPlayer(); + + caster->GetMotionMaster()->MovePoint(0, pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ()); + return true; +} + +bool ChatHandler::HandleCastSelfCommand(const char *args) +{ + if (!*args) + return false; + + Unit* target = getSelectedUnit(); + + if (!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + SetSentErrorMessage(true); + return false; + } + + // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form + uint32 spell = extractSpellIdFromLink((char*)args); + if (!spell) + return false; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo) + return false; + + if (!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) + { + PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell); + SetSentErrorMessage(true); + return false; + } + + target->CastSpell(target,spell,false); + + return true; +} + +std::string GetTimeString(uint32 time) +{ + uint16 days = time / DAY, hours = (time % DAY) / HOUR, minute = (time % HOUR) / MINUTE; + std::ostringstream ss; + if (days) ss << days << "d "; + if (hours) ss << hours << "h "; + ss << minute << "m"; + return ss.str(); +} + +bool ChatHandler::HandleInstanceListBindsCommand(const char* /*args*/) +{ + Player* player = getSelectedPlayer(); + if (!player) player = m_session->GetPlayer(); + uint32 counter = 0; + for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) + { + Player::BoundInstancesMap &binds = player->GetBoundInstances(Difficulty(i)); + for (Player::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr) + { + InstanceSave *save = itr->second.save; + std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); + PSendSysMessage("map: %d inst: %d perm: %s diff: %d canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + counter++; + } + } + PSendSysMessage("player binds: %d", counter); + counter = 0; + Group *group = player->GetGroup(); + if (group) + { + for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) + { + Group::BoundInstancesMap &binds = group->GetBoundInstances(Difficulty(i)); + for (Group::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr) + { + InstanceSave *save = itr->second.save; + std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); + PSendSysMessage("map: %d inst: %d perm: %s diff: %d canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); counter++; + } + } + } + PSendSysMessage("group binds: %d", counter); + + return true; +} + +bool ChatHandler::HandleInstanceUnbindCommand(const char *args) +{ + if (!*args) + return false; + + std::string cmd = args; + if (cmd == "all") + { + Player* player = getSelectedPlayer(); + if (!player) player = m_session->GetPlayer(); + uint32 counter = 0; + for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) + { + Player::BoundInstancesMap &binds = player->GetBoundInstances(Difficulty(i)); + for (Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end();) + { + if (itr->first != player->GetMapId()) + { + InstanceSave *save = itr->second.save; + std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); + PSendSysMessage("unbinding map: %d inst: %d perm: %s diff: %d canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + player->UnbindInstance(itr, Difficulty(i)); + counter++; + } + else + ++itr; + } + } + PSendSysMessage("instances unbound: %d", counter); + } + return true; +} + +bool ChatHandler::HandleInstanceStatsCommand(const char* /*args*/) +{ + PSendSysMessage("instances loaded: %d", MapManager::Instance().GetNumInstances()); + PSendSysMessage("players in instances: %d", MapManager::Instance().GetNumPlayersInInstances()); + PSendSysMessage("instance saves: %d", sInstanceSaveManager.GetNumInstanceSaves()); + PSendSysMessage("players bound: %d", sInstanceSaveManager.GetNumBoundPlayersTotal()); + PSendSysMessage("groups bound: %d", sInstanceSaveManager.GetNumBoundGroupsTotal()); + return true; +} + +bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/) +{ + Player* pl = m_session->GetPlayer(); + + Map* map = pl->GetMap(); + if (!map->IsDungeon()) + { + PSendSysMessage("Map is not a dungeon."); + SetSentErrorMessage(true); + return false; + } + + if (!((InstanceMap*)map)->GetInstanceData()) + { + PSendSysMessage("Map has no instance data."); + SetSentErrorMessage(true); + return false; + } + + ((InstanceMap*)map)->GetInstanceData()->SaveToDB(); + return true; +} + +bool ChatHandler::HandleInstanceOpenCommand(const char *args) +{ + return HandleInstanceOpenCloseCommand(args,true); +} + +bool ChatHandler::HandleInstanceCloseCommand(const char *args) +{ + return HandleInstanceOpenCloseCommand(args,false); +} + +bool ChatHandler::HandleInstanceOpenCloseCommand(const char *args,bool open) +{ + char *mapIdStr; + char *instanceModeStr; + extractOptFirstArg((char*)args,&mapIdStr,&instanceModeStr); + if (!mapIdStr || !instanceModeStr) + return false; + + uint32 mapid = atoi(mapIdStr); + + InstanceTemplate const* instance = objmgr.GetInstanceTemplate(mapid); + if (!instance) + { + PSendSysMessage("Invalid map id"); + SetSentErrorMessage(true); + return false; + } + + uint8 status = objmgr.GetAccessRequirement(instance->access_id)->status; + uint8 flag = 0; + + if (strcmp(instanceModeStr,"normal") || strcmp(instanceModeStr,"10normal")) + flag = DUNGEON_STATUSFLAG_NORMAL; + else if (strcmp(instanceModeStr,"heroic") || strcmp(instanceModeStr,"25normal")) + flag = DUNGEON_STATUSFLAG_HEROIC; + else if (strcmp(instanceModeStr,"10heroic")) + flag = RAID_STATUSFLAG_10MAN_HEROIC; + else if (strcmp(instanceModeStr,"25heroic")) + flag = RAID_STATUSFLAG_25MAN_HEROIC; + else + { + PSendSysMessage("Unrecognized difficulty string"); + SetSentErrorMessage(true); + return false; + } + if (open) + status |= flag; + else + status &= ~flag; + + WorldDatabase.PExecute("UPDATE access_requirement SET status = '%u' WHERE id = '%u'", status, instance->access_id); + PSendSysMessage("Instance status changed. Don't forget to reload access_requirement table"); + return true; +} + +/// Display the list of GMs +bool ChatHandler::HandleGMListFullCommand(const char* /*args*/) +{ + ///- Get the accounts with GM Level >0 + QueryResult_AutoPtr result = LoginDatabase.Query("SELECT a.username,aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel > 0"); + if (result) + { + SendSysMessage(LANG_GMLIST); + SendSysMessage(" ======================== "); + SendSysMessage(LANG_GMLIST_HEADER); + SendSysMessage(" ======================== "); + + ///- Circle through them. Display username and GM level + do + { + Field *fields = result->Fetch(); + PSendSysMessage("|%15s|%6s|", fields[0].GetString(),fields[1].GetString()); + }while (result->NextRow()); + + PSendSysMessage(" ======================== "); + } + else + PSendSysMessage(LANG_GMLIST_EMPTY); + return true; +} + +/// Define the 'Message of the day' for the realm +bool ChatHandler::HandleServerSetMotdCommand(const char *args) +{ + sWorld.SetMotd(args); + PSendSysMessage(LANG_MOTD_NEW, args); + return true; +} + +/// Set whether we accept new clients +bool ChatHandler::HandleServerSetClosedCommand(const char *args) +{ + std::string arg = args; + + if (strncmp(args, "on", 3) == 0) + { + SendSysMessage(LANG_WORLD_CLOSED); + sWorld.SetClosed(true); + return true; + } + else if (strncmp(args, "off", 4) == 0) + { + SendSysMessage(LANG_WORLD_OPENED); + sWorld.SetClosed(false); + return true; + } + + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; +} + +/// Set/Unset the expansion level for an account +bool ChatHandler::HandleAccountSetAddonCommand(const char *args) +{ + ///- Get the command line arguments + char *szAcc = strtok((char*)args," "); + char *szExp = strtok(NULL," "); + + if (!szAcc) + return false; + + std::string account_name; + uint32 account_id; + + if (!szExp) + { + Player* player = getSelectedPlayer(); + if (!player) + return false; + + account_id = player->GetSession()->GetAccountId(); + accmgr.GetName(account_id,account_name); + szExp = szAcc; + } + else + { + ///- Convert Account name to Upper Format + account_name = szAcc; + if (!AccountMgr::normalizeString(account_name)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + account_id = accmgr.GetId(account_name); + if (!account_id) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + } + + // Let set addon state only for lesser (strong) security level + // or to self account + if (m_session && m_session->GetAccountId () != account_id && + HasLowerSecurityAccount (NULL,account_id,true)) + return false; + + int expansion = atoi(szExp); //get int anyway (0 if error) + if (expansion < 0 || expansion > sWorld.getConfig(CONFIG_EXPANSION)) + return false; + + // No SQL injection + LoginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'",expansion,account_id); + PSendSysMessage(LANG_ACCOUNT_SETADDON,account_name.c_str(),account_id,expansion); + return true; +} + +//Send items by mail +bool ChatHandler::HandleSendItemsCommand(const char *args) +{ + // format: name "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] + Player* receiver; + uint64 receiver_guid; + std::string receiver_name; + if (!extractPlayerTarget((char*)args,&receiver,&receiver_guid,&receiver_name)) + return false; + + char* tail1 = strtok(NULL, ""); + if (!tail1) + return false; + + char* msgSubject = extractQuotedArg(tail1); + if (!msgSubject) + return false; + + char* tail2 = strtok(NULL, ""); + if (!tail2) + return false; + + char* msgText = extractQuotedArg(tail2); + if (!msgText) + return false; + + // msgSubject, msgText isn't NUL after prev. check + std::string subject = msgSubject; + std::string text = msgText; + + // extract items + typedef std::pair ItemPair; + typedef std::list< ItemPair > ItemPairs; + ItemPairs items; + + // get all tail string + char* tail = strtok(NULL, ""); + + // get from tail next item str + while (char* itemStr = strtok(tail, " ")) + { + // and get new tail + tail = strtok(NULL, ""); + + // parse item str + char* itemIdStr = strtok(itemStr, ":"); + char* itemCountStr = strtok(NULL, " "); + + uint32 item_id = atoi(itemIdStr); + if (!item_id) + return false; + + ItemPrototype const* item_proto = objmgr.GetItemPrototype(item_id); + if (!item_proto) + { + PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id); + SetSentErrorMessage(true); + return false; + } + + uint32 item_count = itemCountStr ? atoi(itemCountStr) : 1; + if (item_count < 1 || (item_proto->MaxCount > 0 && item_count > uint32(item_proto->MaxCount))) + { + PSendSysMessage(LANG_COMMAND_INVALID_ITEM_COUNT, item_count,item_id); + SetSentErrorMessage(true); + return false; + } + + while (item_count > item_proto->GetMaxStackSize()) + { + items.push_back(ItemPair(item_id,item_proto->GetMaxStackSize())); + item_count -= item_proto->GetMaxStackSize(); + } + + items.push_back(ItemPair(item_id,item_count)); + + if (items.size() > MAX_MAIL_ITEMS) + { + PSendSysMessage(LANG_COMMAND_MAIL_ITEMS_LIMIT, MAX_MAIL_ITEMS); + SetSentErrorMessage(true); + return false; + } + } + + // from console show not existed sender + MailSender sender(MAIL_NORMAL,m_session ? m_session->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM); + + // fill mail + MailDraft draft(subject, text); + + for (ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr) + { + if (Item* item = Item::CreateItem(itr->first,itr->second,m_session ? m_session->GetPlayer() : 0)) + { + item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted + draft.AddItem(item); + } + } + + draft.SendMailTo(MailReceiver(receiver,GUID_LOPART(receiver_guid)), sender); + + std::string nameLink = playerLink(receiver_name); + PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); + return true; +} + +///Send money by mail +bool ChatHandler::HandleSendMoneyCommand(const char *args) +{ + /// format: name "subject text" "mail text" money + + Player* receiver; + uint64 receiver_guid; + std::string receiver_name; + if (!extractPlayerTarget((char*)args,&receiver,&receiver_guid,&receiver_name)) + return false; + + char* tail1 = strtok(NULL, ""); + if (!tail1) + return false; + + char* msgSubject = extractQuotedArg(tail1); + if (!msgSubject) + return false; + + char* tail2 = strtok(NULL, ""); + if (!tail2) + return false; + + char* msgText = extractQuotedArg(tail2); + if (!msgText) + return false; + + char* money_str = strtok(NULL, ""); + int32 money = money_str ? atoi(money_str) : 0; + if (money <= 0) + return false; + + // msgSubject, msgText isn't NUL after prev. check + std::string subject = msgSubject; + std::string text = msgText; + + // from console show not existed sender + MailSender sender(MAIL_NORMAL,m_session ? m_session->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM); + + MailDraft(subject, text) + .AddMoney(money) + .SendMailTo(MailReceiver(receiver,GUID_LOPART(receiver_guid)),sender); + + std::string nameLink = playerLink(receiver_name); + PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); + return true; +} + +/// Send a message to a player in game +bool ChatHandler::HandleSendMessageCommand(const char *args) +{ + ///- Find the player + Player *rPlayer; + if (!extractPlayerTarget((char*)args, &rPlayer)) + return false; + + char* msg_str = strtok(NULL, ""); + if (!msg_str) + return false; + + ///- Check that he is not logging out. + if (rPlayer->GetSession()->isLogingOut()) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + ///- Send the message + //Use SendAreaTriggerMessage for fastest delivery. + rPlayer->GetSession()->SendAreaTriggerMessage("%s", msg_str); + rPlayer->GetSession()->SendAreaTriggerMessage("|cffff0000[Message from administrator]:|r"); + + //Confirmation message + std::string nameLink = GetNameLink(rPlayer); + PSendSysMessage(LANG_SENDMESSAGE,nameLink.c_str(),msg_str); + return true; +} + +bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/) +{ + sBattleGroundMgr.DistributeArenaPoints(); + return true; +} + +bool ChatHandler::HandleModifyGenderCommand(const char *args) +{ + if (!*args) + return false; + + Player *player = getSelectedPlayer(); + + if (!player) + { + PSendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + PlayerInfo const* info = objmgr.GetPlayerInfo(player->getRace(), player->getClass()); + if (!info) + return false; + + char const* gender_str = (char*)args; + int gender_len = strlen(gender_str); + + Gender gender; + + if (!strncmp(gender_str, "male", gender_len)) // MALE + { + if (player->getGender() == GENDER_MALE) + return true; + + gender = GENDER_MALE; + } + else if (!strncmp(gender_str, "female", gender_len)) // FEMALE + { + if (player->getGender() == GENDER_FEMALE) + return true; + + gender = GENDER_FEMALE; + } + else + { + SendSysMessage(LANG_MUST_MALE_OR_FEMALE); + SetSentErrorMessage(true); + return false; + } + + // Set gender + player->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender); + player->SetByteValue(PLAYER_BYTES_3, 0, gender); + + // Change display ID + player->InitDisplayIds(); + + char const* gender_full = gender ? "female" : "male"; + + PSendSysMessage(LANG_YOU_CHANGE_GENDER, GetNameLink(player).c_str(), gender_full); + + if (needReportToTarget(player)) + ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full, GetNameLink().c_str()); + + return true; +} + +bool ChatHandler::HandleChannelSetPublic(const char *args) +{ + if (!*args) + return false; + std::string channel = strtok((char*)args, " "); + uint32 val = atoi((char*)args); + + if (val) + { + CharacterDatabase.PExecute("UPDATE channels SET m_public = 1 WHERE m_name LIKE '%s'", channel.c_str()); + val = 1; + } + else + { + CharacterDatabase.PExecute("UPDATE channels SET m_public = 0 WHERE m_name LIKE '%s'", channel.c_str()); + val = 0; + } + + PSendSysMessage(LANG_CHANNEL_PUBLIC_CHANGED, channel.c_str(), val); + + return true; +} + + +/*------------------------------------------ + *-------------TRINITY---------------------- + *-------------------------------------*/ + +bool ChatHandler::HandlePlayAllCommand(const char *args) +{ + if (!*args) + return false; + + uint32 soundId = atoi((char*)args); + + if (!sSoundEntriesStore.LookupEntry(soundId)) + { + PSendSysMessage(LANG_SOUND_NOT_EXIST, soundId); + SetSentErrorMessage(true); + return false; + } + + WorldPacket data(SMSG_PLAY_SOUND, 4); + data << uint32(soundId) << m_session->GetPlayer()->GetGUID(); + sWorld.SendGlobalMessage(&data); + + PSendSysMessage(LANG_COMMAND_PLAYED_TO_ALL, soundId); + return true; +} + +bool ChatHandler::HandleFreezeCommand(const char *args) +{ + std::string name; + Player *player; + char *TargetName = strtok((char*)args, " "); //get entered name + if (!TargetName) //if no name entered use target + { + player = getSelectedPlayer(); + if (player) //prevent crash with creature as target + { + name = player->GetName(); + normalizePlayerName(name); + } + } + else // if name entered + { + name = TargetName; + normalizePlayerName(name); + player = objmgr.GetPlayer(name.c_str()); //get player by name + } + + if (!player) + { + SendSysMessage(LANG_COMMAND_FREEZE_WRONG); + return true; + } + + if (player == m_session->GetPlayer()) + { + SendSysMessage(LANG_COMMAND_FREEZE_ERROR); + return true; + } + + //effect + if (player && player != m_session->GetPlayer()) + { + PSendSysMessage(LANG_COMMAND_FREEZE,name.c_str()); + + //stop combat + make player unattackable + duel stop + stop some spells + player->setFaction(35); + player->CombatStop(); + if (player->IsNonMeleeSpellCasted(true)) + player->InterruptNonMeleeSpells(true); + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + player->SetUInt32Value(PLAYER_DUEL_TEAM, 1); + + //if player class = hunter || warlock remove pet if alive + if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK)) + { + if (Pet *pet = player->GetPet()) + { + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + // not let dismiss dead pet + if (pet && pet->isAlive()) + player->RemovePet(pet,PET_SAVE_NOT_IN_SLOT); + } + } + + //m_session->GetPlayer()->CastSpell(player,spellID,false); + if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(9454)) + Aura::TryCreate(spellInfo, player, player); + + //save player + player->SaveToDB(); + } + return true; +} + +bool ChatHandler::HandleUnFreezeCommand(const char *args) +{ + std::string name; + Player *player; + char *TargetName = strtok((char*)args, " "); //get entered name + if (!TargetName) //if no name entered use target + { + player = getSelectedPlayer(); + if (player) //prevent crash with creature as target + name = player->GetName(); + } + + else // if name entered + { + name = TargetName; + normalizePlayerName(name); + player = objmgr.GetPlayer(name.c_str()); //get player by name + } + + //effect + if (player) + { + PSendSysMessage(LANG_COMMAND_UNFREEZE,name.c_str()); + + //Reset player faction + allow combat + allow duels + player->setFactionForRace(player->getRace()); + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + //allow movement and spells + player->RemoveAurasDueToSpell(9454); + + //save player + player->SaveToDB(); + } + + if (!player) + { + if (TargetName) + { + //check for offline players + QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT characters.guid FROM characters WHERE characters.name = '%s'",name.c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_FREEZE_WRONG); + return true; + } + //if player found: delete his freeze aura + Field *fields=result->Fetch(); + uint64 pguid = fields[0].GetUInt64(); + + CharacterDatabase.PQuery("DELETE FROM character_aura WHERE character_aura.spell = 9454 AND character_aura.guid = '%u'",pguid); + PSendSysMessage(LANG_COMMAND_UNFREEZE,name.c_str()); + return true; + } + else + { + SendSysMessage(LANG_COMMAND_FREEZE_WRONG); + return true; + } + } + + return true; +} + +bool ChatHandler::HandleListFreezeCommand(const char * /*args*/) +{ + //Get names from DB + QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT characters.name FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454"); + if (!result) + { + SendSysMessage(LANG_COMMAND_NO_FROZEN_PLAYERS); + return true; + } + //Header of the names + PSendSysMessage(LANG_COMMAND_LIST_FREEZE); + + //Output of the results + do + { + Field *fields = result->Fetch(); + std::string fplayers = fields[0].GetCppString(); + PSendSysMessage(LANG_COMMAND_FROZEN_PLAYERS,fplayers.c_str()); + } while (result->NextRow()); + + return true; +} + +bool ChatHandler::HandleGroupLeaderCommand(const char *args) +{ + Player* plr = NULL; + Group* group = NULL; + uint64 guid = 0; + char* cname = strtok((char*)args, " "); + + if (GetPlayerGroupAndGUIDByName(cname, plr, group, guid)) + if (group && group->GetLeaderGUID() != guid) + group->ChangeLeader(guid); + + return true; +} + +bool ChatHandler::HandleGroupDisbandCommand(const char *args) +{ + Player* plr = NULL; + Group* group = NULL; + uint64 guid = 0; + char* cname = strtok((char*)args, " "); + + if (GetPlayerGroupAndGUIDByName(cname, plr, group, guid)) + if (group) + group->Disband(); + + return true; +} + +bool ChatHandler::HandleGroupRemoveCommand(const char *args) +{ + Player* plr = NULL; + Group* group = NULL; + uint64 guid = 0; + char* cname = strtok((char*)args, " "); + + if (GetPlayerGroupAndGUIDByName(cname, plr, group, guid, true)) + if (group) + group->RemoveMember(guid, 0); + + return true; +} + +bool ChatHandler::HandlePossessCommand(const char * /*args*/) +{ + Unit *pUnit = getSelectedUnit(); + if (!pUnit) + return false; + + m_session->GetPlayer()->CastSpell(pUnit, 530, true); + return true; +} + +bool ChatHandler::HandleUnPossessCommand(const char * /*args*/) +{ + Unit *pUnit = getSelectedUnit(); + if (!pUnit) + pUnit = m_session->GetPlayer(); + + pUnit->RemoveCharmAuras(); + + return true; +} + +bool ChatHandler::HandleBindSightCommand(const char * /*args*/) +{ + Unit *pUnit = getSelectedUnit(); + if (!pUnit) + return false; + + m_session->GetPlayer()->CastSpell(pUnit, 6277, true); + return true; +} + +bool ChatHandler::HandleUnbindSightCommand(const char * /*args*/) +{ + if (m_session->GetPlayer()->isPossessing()) + return false; + + m_session->GetPlayer()->StopCastingBindSight(); + return true; +} diff --git a/src/server/game/ChatHandler.cpp b/src/server/game/ChatHandler.cpp deleted file mode 100644 index 88e2b5473a5..00000000000 --- a/src/server/game/ChatHandler.cpp +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "ObjectAccessor.h" -#include "ObjectMgr.h" -#include "World.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Database/DatabaseEnv.h" - -#include "CellImpl.h" -#include "Chat.h" -#include "ChannelMgr.h" -#include "GridNotifiersImpl.h" -#include "Group.h" -#include "Guild.h" -#include "Language.h" -#include "Log.h" -#include "Opcodes.h" -#include "Player.h" -#include "SpellAuras.h" -#include "SpellAuraEffects.h" -#include "Util.h" - -bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang) -{ - if (lang != LANG_ADDON) - { - // strip invisible characters for non-addon messages - if (sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) - stripLineInvisibleChars(msg); - - if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR - && !ChatHandler(this).isValidChatMessage(msg.c_str())) - { - sLog.outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(), - GetPlayer()->GetGUIDLow(), msg.c_str()); - if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) - KickPlayer(); - return false; - } - } - - return true; -} - -void WorldSession::HandleMessagechatOpcode(WorldPacket & recv_data) -{ - uint32 type; - uint32 lang; - - recv_data >> type; - recv_data >> lang; - - if (type >= MAX_CHAT_MSG_TYPE) - { - sLog.outError("CHAT: Wrong message type received: %u", type); - return; - } - - //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang); - - // prevent talking at unknown language (cheating) - LanguageDesc const* langDesc = GetLanguageDescByID(lang); - if (!langDesc) - { - SendNotification(LANG_UNKNOWN_LANGUAGE); - return; - } - if (langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id)) - { - // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language) - Unit::AuraEffectList const& langAuras = _player->GetAuraEffectsByType(SPELL_AURA_COMPREHEND_LANGUAGE); - bool foundAura = false; - for (Unit::AuraEffectList::const_iterator i = langAuras.begin(); i != langAuras.end(); ++i) - { - if ((*i)->GetMiscValue() == int32(lang)) - { - foundAura = true; - break; - } - } - if (!foundAura) - { - SendNotification(LANG_NOT_LEARNED_LANGUAGE); - return; - } - } - - if (lang == LANG_ADDON) - { - if (sWorld.getConfig(CONFIG_CHATLOG_ADDON)) - { - std::string msg = ""; - recv_data >> msg; - - if (msg.empty()) - { - sLog.outDebug("Player %s send empty addon msg", GetPlayer()->GetName()); - return; - } - - sLog.outChat("[ADDON] Player %s sends: %s", - GetPlayer()->GetName(), msg.c_str()); - } - - // Disabled addon channel? - if (!sWorld.getConfig(CONFIG_ADDON_CHANNEL)) - return; - } - // LANG_ADDON should not be changed nor be affected by flood control - else - { - // send in universal language if player in .gmon mode (ignore spell effects) - if (_player->isGameMaster()) - lang = LANG_UNIVERSAL; - else - { - // send in universal language in two side iteration allowed mode - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)) - lang = LANG_UNIVERSAL; - else - { - switch(type) - { - case CHAT_MSG_PARTY: - case CHAT_MSG_RAID: - case CHAT_MSG_RAID_LEADER: - case CHAT_MSG_RAID_WARNING: - // allow two side chat at group channel if two side group allowed - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) - lang = LANG_UNIVERSAL; - break; - case CHAT_MSG_GUILD: - case CHAT_MSG_OFFICER: - // allow two side chat at guild channel if two side guild allowed - if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) - lang = LANG_UNIVERSAL; - break; - } - } - - // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used) - Unit::AuraEffectList const& ModLangAuras = _player->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE); - if (!ModLangAuras.empty()) - lang = ModLangAuras.front()->GetMiscValue(); - } - - if (!_player->CanSpeak()) - { - std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); - SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); - return; - } - - if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND) - GetPlayer()->UpdateSpeakTime(); - } - - if (GetPlayer()->HasAura(1852) && type != CHAT_MSG_WHISPER) - { - std::string msg=""; - recv_data >> msg; - - SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName()); - return; - } - - switch(type) - { - case CHAT_MSG_SAY: - case CHAT_MSG_EMOTE: - case CHAT_MSG_YELL: - { - std::string msg; - recv_data >> msg; - - if (msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_SAY_LEVEL_REQ)) - { - SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld.getConfig(CONFIG_CHAT_SAY_LEVEL_REQ)); - return; - } - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - if (type == CHAT_MSG_SAY) - GetPlayer()->Say(msg, lang); - else if (type == CHAT_MSG_EMOTE) - GetPlayer()->TextEmote(msg); - else if (type == CHAT_MSG_YELL) - GetPlayer()->Yell(msg, lang); - } break; - - case CHAT_MSG_WHISPER: - { - std::string to, msg; - recv_data >> to; - recv_data >> msg; - - if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ)) - { - SendNotification(GetTrinityString(LANG_WHISPER_REQ), sWorld.getConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ)); - return; - } - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - if (!normalizePlayerName(to)) - { - SendPlayerNotFoundNotice(to); - break; - } - - Player *player = objmgr.GetPlayer(to.c_str()); - uint32 tSecurity = GetSecurity(); - uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER; - if (!player || (tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers())) - { - SendPlayerNotFoundNotice(to); - return; - } - - if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER) - { - uint32 sidea = GetPlayer()->GetTeam(); - uint32 sideb = player->GetTeam(); - if (sidea != sideb) - { - SendWrongFactionNotice(); - return; - } - } - - if (GetPlayer()->HasAura(1852) && !player->isGameMaster()) - { - SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName()); - return; - } - - GetPlayer()->Whisper(msg, lang, player->GetGUID()); - } break; - - case CHAT_MSG_PARTY: - case CHAT_MSG_PARTY_LEADER: - { - std::string msg; - recv_data >> msg; - - if (msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - // if player is in battleground, he cannot say to battleground members by /p - Group *group = GetPlayer()->GetOriginalGroup(); - if (!group) - { - group = _player->GetGroup(); - if (!group || group->isBGGroup()) - return; - } - - if ((type == CHAT_MSG_PARTY_LEADER) && !group->IsLeader(_player->GetGUID())) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, type, lang, NULL, 0, msg.c_str(), NULL); - group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID())); - - if (sWorld.getConfig(CONFIG_CHATLOG_PARTY)) - sLog.outChat("[PARTY] Player %s tells group with leader %s: %s", - GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); - } break; - - case CHAT_MSG_GUILD: - { - std::string msg; - recv_data >> msg; - - if (msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - if (GetPlayer()->GetGuildId()) - { - Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); - - if (guild) - guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); - - if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_GUILD)) - { - sLog.outChat("[GUILD] Player %s tells guild %s: %s", - GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); - } - else if (lang == LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_ADDON)) - { - sLog.outChat("[ADDON] Player %s sends to guild %s: %s", - GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); - } - } - - break; - } - case CHAT_MSG_OFFICER: - { - std::string msg; - recv_data >> msg; - - if (msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - if (GetPlayer()->GetGuildId()) - { - Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); - - if (guild) - guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); - - if (sWorld.getConfig(CONFIG_CHATLOG_GUILD)) - sLog.outChat("[OFFICER] Player %s tells guild %s officers: %s", - GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str()); - } - break; - } - case CHAT_MSG_RAID: - { - std::string msg; - recv_data >> msg; - - if (msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - // if player is in battleground, he cannot say to battleground members by /ra - Group *group = GetPlayer()->GetOriginalGroup(); - if (!group) - { - group = GetPlayer()->GetGroup(); - if (!group || group->isBGGroup() || !group->isRaidGroup()) - return; - } - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(), NULL); - group->BroadcastPacket(&data, false); - - if (sWorld.getConfig(CONFIG_CHATLOG_RAID)) - sLog.outChat("[RAID] Player %s tells raid with leader %s: %s", - GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); - } break; - case CHAT_MSG_RAID_LEADER: - { - std::string msg; - recv_data >> msg; - - if (msg.empty()) - break; - - if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) - break; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - // if player is in battleground, he cannot say to battleground members by /ra - Group *group = GetPlayer()->GetOriginalGroup(); - if (!group) - { - group = GetPlayer()->GetGroup(); - if (!group || group->isBGGroup() || !group->isRaidGroup() || !group->IsLeader(_player->GetGUID())) - return; - } - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(), NULL); - group->BroadcastPacket(&data, false); - - if (sWorld.getConfig(CONFIG_CHATLOG_RAID)) - sLog.outChat("[RAID] Leader player %s tells raid: %s", - GetPlayer()->GetName(), msg.c_str()); - } break; - case CHAT_MSG_RAID_WARNING: - { - std::string msg; - recv_data >> msg; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - Group *group = GetPlayer()->GetGroup(); - if (!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup()) - return; - - WorldPacket data; - //in battleground, raid warning is sent only to players in battleground - code is ok - ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(), NULL); - group->BroadcastPacket(&data, false); - - if (sWorld.getConfig(CONFIG_CHATLOG_RAID)) - sLog.outChat("[RAID] Leader player %s warns raid with: %s", - GetPlayer()->GetName(), msg.c_str()); - } break; - - case CHAT_MSG_BATTLEGROUND: - { - std::string msg; - recv_data >> msg; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() - Group *group = GetPlayer()->GetGroup(); - if (!group || !group->isBGGroup()) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(), NULL); - group->BroadcastPacket(&data, false); - - if (sWorld.getConfig(CONFIG_CHATLOG_BGROUND)) - sLog.outChat("[BATTLEGROUND] Player %s tells battleground with leader %s: %s", - GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str()); - } break; - - case CHAT_MSG_BATTLEGROUND_LEADER: - { - std::string msg; - recv_data >> msg; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (msg.empty()) - break; - - // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() - Group *group = GetPlayer()->GetGroup(); - if (!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID())) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(), NULL); - group->BroadcastPacket(&data, false); - - if (sWorld.getConfig(CONFIG_CHATLOG_BGROUND)) - sLog.outChat("[RAID] Leader player %s tells battleground: %s", - GetPlayer()->GetName(), msg.c_str()); - } break; - - case CHAT_MSG_CHANNEL: - { - std::string channel, msg; - recv_data >> channel; - recv_data >> msg; - - if (!processChatmessageFurtherAfterSecurityChecks(msg, lang)) - return; - - if (_player->getLevel() < sWorld.getConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ)) - { - SendNotification(GetTrinityString(LANG_CHANNEL_REQ), sWorld.getConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ)); - return; - } - - if (msg.empty()) - break; - - if (ChannelMgr* cMgr = channelMgr(_player->GetTeam())) - { - - if (Channel *chn = cMgr->GetChannel(channel, _player)) - { - chn->Say(_player->GetGUID(), msg.c_str(), lang); - - if ((chn->HasFlag(CHANNEL_FLAG_TRADE) || - chn->HasFlag(CHANNEL_FLAG_GENERAL) || - chn->HasFlag(CHANNEL_FLAG_CITY) || - chn->HasFlag(CHANNEL_FLAG_LFG)) && - sWorld.getConfig(CONFIG_CHATLOG_SYSCHAN)) - sLog.outChat("[SYSCHAN] Player %s tells channel %s: %s", - GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str()); - else if (sWorld.getConfig(CONFIG_CHATLOG_CHANNEL)) - sLog.outChat("[CHANNEL] Player %s tells channel %s: %s", - GetPlayer()->GetName(), chn->GetName().c_str(), msg.c_str()); - } - } - } break; - - case CHAT_MSG_AFK: - { - std::string msg; - recv_data >> msg; - - if ((msg.empty() || !_player->isAFK()) && !_player->isInCombat()) - { - if (!_player->isAFK()) - { - if (msg.empty()) - msg = GetTrinityString(LANG_PLAYER_AFK_DEFAULT); - _player->afkMsg = msg; - } - _player->ToggleAFK(); - if (_player->isAFK() && _player->isDND()) - _player->ToggleDND(); - } - } break; - - case CHAT_MSG_DND: - { - std::string msg; - recv_data >> msg; - - if (msg.empty() || !_player->isDND()) - { - if (!_player->isDND()) - { - if (msg.empty()) - msg = GetTrinityString(LANG_PLAYER_DND_DEFAULT); - _player->dndMsg = msg; - } - _player->ToggleDND(); - if (_player->isDND() && _player->isAFK()) - _player->ToggleAFK(); - } - } break; - - default: - sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang); - break; - } -} - -void WorldSession::HandleEmoteOpcode(WorldPacket & recv_data) -{ - if (!GetPlayer()->isAlive()) - return; - - uint32 emote; - recv_data >> emote; - GetPlayer()->HandleEmoteCommand(emote); -} - -namespace Trinity -{ - class EmoteChatBuilder - { - public: - EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target) - : i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {} - - void operator()(WorldPacket& data, int32 loc_idx) - { - char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL; - uint32 namlen = (nam ? strlen(nam) : 0) + 1; - - data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); - data << i_player.GetGUID(); - data << (uint32)i_text_emote; - data << i_emote_num; - data << (uint32)namlen; - if (namlen > 1) - data.append(nam, namlen); - else - data << (uint8)0x00; - } - - private: - Player const& i_player; - uint32 i_text_emote; - uint32 i_emote_num; - Unit const* i_target; - }; -} // namespace Trinity - -void WorldSession::HandleTextEmoteOpcode(WorldPacket & recv_data) -{ - if (!GetPlayer()->isAlive()) - return; - - if (!GetPlayer()->CanSpeak()) - { - std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); - SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); - return; - } - - uint32 text_emote, emoteNum; - uint64 guid; - - recv_data >> text_emote; - recv_data >> emoteNum; - recv_data >> guid; - - EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote); - if (!em) - return; - - uint32 emote_anim = em->textid; - - switch(emote_anim) - { - case EMOTE_STATE_SLEEP: - case EMOTE_STATE_SIT: - case EMOTE_STATE_KNEEL: - case EMOTE_ONESHOT_NONE: - break; - default: - GetPlayer()->HandleEmoteCommand(emote_anim); - break; - } - - Unit* unit = ObjectAccessor::GetUnit(*_player, guid); - - CellPair p = Trinity::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); - - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Trinity::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit); - Trinity::LocalizedPacketDo emote_do(emote_builder); - Trinity::PlayerDistWorker > emote_worker(GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), emote_do); - TypeContainerVisitor >, WorldTypeMapContainer> message(emote_worker); - cell.Visit(p, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE)); - - GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit); - - //Send scripted event call - if (unit && unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->AI()) - ((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(), text_emote); -} - -void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data) -{ - uint64 iguid; - uint8 unk; - //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED"); - - recv_data >> iguid; - recv_data >> unk; // probably related to spam reporting - - Player *player = objmgr.GetPlayer(iguid); - if (!player || !player->GetSession()) - return; - - WorldPacket data; - ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(), NULL); - player->GetSession()->SendPacket(&data); -} - -void WorldSession::HandleChannelDeclineInvite(WorldPacket &recvPacket) -{ - sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); -} - -void WorldSession::SendPlayerNotFoundNotice(std::string name) -{ - WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, name.size()+1); - data << name; - SendPacket(&data); -} - -void WorldSession::SendPlayerAmbiguousNotice(std::string name) -{ - WorldPacket data(SMSG_CHAT_PLAYER_AMBIGUOUS, name.size()+1); - data << name; - SendPacket(&data); -} - -void WorldSession::SendWrongFactionNotice() -{ - WorldPacket data(SMSG_CHAT_WRONG_FACTION, 0); - SendPacket(&data); -} - -void WorldSession::SendChatRestrictedNotice(ChatRestrictionType restriction) -{ - WorldPacket data(SMSG_CHAT_RESTRICTED, 1); - data << uint8(restriction); - SendPacket(&data); -} diff --git a/src/server/game/Combat/CombatHandler.cpp b/src/server/game/Combat/CombatHandler.cpp new file mode 100644 index 00000000000..404b70c1c84 --- /dev/null +++ b/src/server/game/Combat/CombatHandler.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "ObjectAccessor.h" +#include "CreatureAI.h" +#include "ObjectDefines.h" + +void WorldSession::HandleAttackSwingOpcode(WorldPacket & recv_data) +{ + uint64 guid; + recv_data >> guid; + + DEBUG_LOG("WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid)); + + Unit *pEnemy = ObjectAccessor::GetUnit(*_player, guid); + + if (!pEnemy) + { + if (!IS_UNIT_GUID(guid)) + sLog.outError("WORLD: Object %u (TypeID: %u) isn't player, pet or creature",GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); + else + sLog.outError("WORLD: Enemy %s %u not found",GetLogNameForGuid(guid),GUID_LOPART(guid)); + + // stop attack state at client + SendAttackStop(NULL); + return; + } + + if (!_player->canAttack(pEnemy)) + { + sLog.outError("WORLD: Enemy %s %u is friendly",(IS_PLAYER_GUID(guid) ? "player" : "creature"),GUID_LOPART(guid)); + + // stop attack state at client + SendAttackStop(pEnemy); + return; + } + + _player->Attack(pEnemy,true); +} + +void WorldSession::HandleAttackStopOpcode(WorldPacket & /*recv_data*/) +{ + GetPlayer()->AttackStop(); +} + +void WorldSession::HandleSetSheathedOpcode(WorldPacket & recv_data) +{ + uint32 sheathed; + recv_data >> sheathed; + + //sLog.outDebug("WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed); + + if (sheathed >= MAX_SHEATH_STATE) + { + sLog.outError("Unknown sheath state %u ??",sheathed); + return; + } + + GetPlayer()->SetSheath(SheathState(sheathed)); +} + +void WorldSession::SendAttackStop(Unit const* enemy) +{ + WorldPacket data(SMSG_ATTACKSTOP, (4+20)); // we guess size + data.append(GetPlayer()->GetPackGUID()); + data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid + data << uint32(0); // unk, can be 1 also + SendPacket(&data); +} + diff --git a/src/server/game/Combat/HostileRefManager.cpp b/src/server/game/Combat/HostileRefManager.cpp new file mode 100644 index 00000000000..64fc19c78d4 --- /dev/null +++ b/src/server/game/Combat/HostileRefManager.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "HostileRefManager.h" +#include "ThreatManager.h" +#include "Unit.h" +#include "DBCStructure.h" +#include "SpellMgr.h" + +HostileRefManager::~HostileRefManager() +{ + deleteReferences(); +} + +//================================================= +// send threat to all my hateres for the pVictim +// The pVictim is hated than by them as well +// use for buffs and healing threat functionality + +void HostileRefManager::threatAssist(Unit *pVictim, float fThreat, SpellEntry const *pThreatSpell, bool pSingleTarget) +{ + HostileReference* ref; + + float size = pSingleTarget ? 1.0f : getSize(); // if pSingleTarget do not divide threat + ref = getFirst(); + while (ref != NULL) + { + float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, fThreat, (pThreatSpell ? GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell); + if (pVictim == getOwner()) + ref->addThreat(threat / size); // It is faster to modify the threat durectly if possible + else + ref->getSource()->addThreat(pVictim, threat / size); + ref = ref->next(); + } +} + +//================================================= + +void HostileRefManager::addTempThreat(float fThreat, bool apply) +{ + HostileReference* ref = getFirst(); + + while (ref != NULL) + { + if (apply) + { + if (ref->getTempThreatModifier() == 0.0f) + ref->addTempThreat(fThreat); + } + else + ref->resetTempThreat(); + + ref = ref->next(); + } +} + + +//================================================= + +void HostileRefManager::addThreatPercent(int32 iPercent) +{ + HostileReference* ref; + + ref = getFirst(); + while (ref != NULL) + { + ref->addThreatPercent(iPercent); + ref = ref->next(); + } +} + +//================================================= +// The online / offline status is given to the method. The calculation has to be done before + +void HostileRefManager::setOnlineOfflineState(bool bIsOnline) +{ + HostileReference* ref; + + ref = getFirst(); + while (ref != NULL) + { + ref->setOnlineOfflineState(bIsOnline); + ref = ref->next(); + } +} + +//================================================= +// The online / offline status is calculated and set + +void HostileRefManager::updateThreatTables() +{ + HostileReference* ref = getFirst(); + while (ref) + { + ref->updateOnlineStatus(); + ref = ref->next(); + } +} + +//================================================= +// The references are not needed anymore +// tell the source to remove them from the list and free the mem + +void HostileRefManager::deleteReferences() +{ + HostileReference* ref = getFirst(); + while (ref) + { + HostileReference* nextRef = ref->next(); + ref->removeReference(); + delete ref; + ref = nextRef; + } +} + +//================================================= +// delete one reference, defined by faction + +void HostileRefManager::deleteReferencesForFaction(uint32 faction) +{ + HostileReference* ref = getFirst(); + while (ref) + { + HostileReference* nextRef = ref->next(); + if (ref->getSource()->getOwner()->getFactionTemplateEntry()->faction == faction) + { + ref->removeReference(); + delete ref; + } + ref = nextRef; + } +} + +//================================================= +// delete one reference, defined by Unit + +void HostileRefManager::deleteReference(Unit *pCreature) +{ + HostileReference* ref = getFirst(); + while (ref) + { + HostileReference* nextRef = ref->next(); + if (ref->getSource()->getOwner() == pCreature) + { + ref->removeReference(); + delete ref; + break; + } + ref = nextRef; + } +} + +//================================================= +// set state for one reference, defined by Unit + +void HostileRefManager::setOnlineOfflineState(Unit *pCreature, bool bIsOnline) +{ + HostileReference* ref = getFirst(); + while (ref) + { + HostileReference* nextRef = ref->next(); + if (ref->getSource()->getOwner() == pCreature) + { + ref->setOnlineOfflineState(bIsOnline); + break; + } + ref = nextRef; + } +} + +//================================================= + diff --git a/src/server/game/Combat/HostileRefManager.h b/src/server/game/Combat/HostileRefManager.h new file mode 100644 index 00000000000..80b676312a1 --- /dev/null +++ b/src/server/game/Combat/HostileRefManager.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HOSTILEREFMANAGER +#define _HOSTILEREFMANAGER + +#include "Common.h" +#include "Utilities/LinkedReference/RefManager.h" + +class Unit; +class ThreatManager; +class HostileReference; +struct SpellEntry; + +//================================================= + +class HostileRefManager : public RefManager +{ + private: + Unit *iOwner; + public: + explicit HostileRefManager(Unit *pOwner) { iOwner = pOwner; } + ~HostileRefManager(); + + Unit* getOwner() { return iOwner; } + + // send threat to all my hateres for the pVictim + // The pVictim is hated than by them as well + // use for buffs and healing threat functionality + void threatAssist(Unit *pVictim, float fThreat, SpellEntry const *threatSpell = 0, bool pSingleTarget = false); + + void addTempThreat(float fThreat, bool apply); + + void addThreatPercent(int32 iPercent); + + // The references are not needed anymore + // tell the source to remove them from the list and free the mem + void deleteReferences(); + + // Remove specific faction references + void deleteReferencesForFaction(uint32 faction); + + HostileReference* getFirst() { return ((HostileReference*) RefManager::getFirst()); } + + void updateThreatTables(); + + void setOnlineOfflineState(bool bIsOnline); + + // set state for one reference, defined by Unit + void setOnlineOfflineState(Unit *pCreature, bool bIsOnline); + + // delete one reference, defined by Unit + void deleteReference(Unit *pCreature); +}; +//================================================= +#endif + diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp new file mode 100644 index 00000000000..52f02f0f66d --- /dev/null +++ b/src/server/game/Combat/ThreatManager.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ThreatManager.h" +#include "Unit.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "Map.h" +#include "Player.h" +#include "ObjectAccessor.h" +#include "UnitEvents.h" +#include "SpellAuras.h" + +//============================================================== +//================= ThreatCalcHelper =========================== +//============================================================== + +// The pHatingUnit is not used yet +float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* /*pHatingUnit*/, float fThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell) +{ + if (pThreatSpell) + if (Player* modOwner = pHatedUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, fThreat); + + return pHatedUnit->ApplyTotalThreatModifier(fThreat, schoolMask); +} + +//============================================================ +//================= HostileReference ========================== +//============================================================ + +HostileReference::HostileReference(Unit* pUnit, ThreatManager *pThreatManager, float fThreat) +{ + iThreat = fThreat; + iTempThreatModifier = 0.0f; + link(pUnit, pThreatManager); + iUnitGuid = pUnit->GetGUID(); + iOnline = true; + iAccessible = true; +} + +//============================================================ +// Tell our refTo (target) object that we have a link +void HostileReference::targetObjectBuildLink() +{ + getTarget()->addHatedBy(this); +} + +//============================================================ +// Tell our refTo (taget) object, that the link is cut +void HostileReference::targetObjectDestroyLink() +{ + getTarget()->removeHatedBy(this); +} + +//============================================================ +// Tell our refFrom (source) object, that the link is cut (Target destroyed) + +void HostileReference::sourceObjectDestroyLink() +{ + setOnlineOfflineState(false); +} + +//============================================================ +// Inform the source, that the status of the reference changed + +void HostileReference::fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent) +{ + if (getSource()) + getSource()->processThreatEvent(&pThreatRefStatusChangeEvent); +} + +//============================================================ + +void HostileReference::addThreat(float fModThreat) +{ + iThreat += fModThreat; + // the threat is changed. Source and target unit have to be available + // if the link was cut before relink it again + if (!isOnline()) + updateOnlineStatus(); + if (fModThreat != 0.0f) + { + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, fModThreat); + fireStatusChanged(event); + } + + if (isValid() && fModThreat >= 0.0f) + { + Unit* victim_owner = getTarget()->GetCharmerOrOwner(); + if (victim_owner && victim_owner->isAlive()) + getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks + } +} + +//============================================================ +// check, if source can reach target and set the status + +void HostileReference::updateOnlineStatus() +{ + bool online = false; + bool accessible = false; + + if (!isValid()) + if (Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid())) + link(target, getSource()); + + // only check for online status if + // ref is valid + // target is no player or not gamemaster + // target is not in flight + if (isValid() && + ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) || + !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT))) + { + Creature* creature = getSourceUnit()->ToCreature(); + online = getTarget()->isInAccessiblePlaceFor(creature); + if (!online) + { + if (creature->IsWithinCombatRange(getTarget(), creature->m_CombatDistance)) + online = true; // not accessible but stays online + } + else + accessible = true; + + } + setAccessibleState(accessible); + setOnlineOfflineState(online); +} + +//============================================================ +// set the status and fire the event on status change + +void HostileReference::setOnlineOfflineState(bool pIsOnline) +{ + if (iOnline != pIsOnline) + { + iOnline = pIsOnline; + if (!iOnline) + setAccessibleState(false); // if not online that not accessable as well + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this); + fireStatusChanged(event); + } +} + +//============================================================ + +void HostileReference::setAccessibleState(bool pIsAccessible) +{ + if (iAccessible != pIsAccessible) + { + iAccessible = pIsAccessible; + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this); + fireStatusChanged(event); + } +} + +//============================================================ +// prepare the reference for deleting +// this is called be the target + +void HostileReference::removeReference() +{ + invalidate(); + + ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this); + fireStatusChanged(event); +} + +//============================================================ + +Unit* HostileReference::getSourceUnit() +{ + return (getSource()->getOwner()); +} + +//============================================================ +//================ ThreatContainer =========================== +//============================================================ + +void ThreatContainer::clearReferences() +{ + for (std::list::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i) + { + (*i)->unlink(); + delete (*i); + } + iThreatList.clear(); +} + +//============================================================ +// Return the HostileReference of NULL, if not found +HostileReference* ThreatContainer::getReferenceByTarget(Unit* pVictim) +{ + HostileReference* result = NULL; + + uint64 guid = pVictim->GetGUID(); + for (std::list::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i) + { + if ((*i)->getUnitGuid() == guid) + { + result = (*i); + break; + } + } + + return result; +} + +//============================================================ +// Add the threat, if we find the reference + +HostileReference* ThreatContainer::addThreat(Unit* pVictim, float fThreat) +{ + HostileReference* ref = getReferenceByTarget(pVictim); + if (ref) + ref->addThreat(fThreat); + return ref; +} + +//============================================================ + +void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 iPercent) +{ + if (HostileReference* ref = getReferenceByTarget(pVictim)) + ref->addThreatPercent(iPercent); +} + +//============================================================ +// Check if the list is dirty and sort if necessary + +void ThreatContainer::update() +{ + if (iDirty && iThreatList.size() >1) + { + iThreatList.sort(Trinity::ThreatOrderPred()); + } + iDirty = false; +} + +//============================================================ +// return the next best victim +// could be the current victim + +HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim) +{ + HostileReference* currentRef = NULL; + bool found = false; + bool noPriorityTargetFound = false; + + std::list::const_iterator lastRef = iThreatList.end(); + lastRef--; + + for (std::list::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) + { + currentRef = (*iter); + + Unit* target = currentRef->getTarget(); + assert(target); // if the ref has status online the target must be there ! + + // some units are prefered in comparison to others + if (!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE))) + { + if (iter != lastRef) + { + // current victim is a second choice target, so don't compare threat with it below + if (currentRef == pCurrentVictim) + pCurrentVictim = NULL; + ++iter; + continue; + } + else + { + // if we reached to this point, everyone in the threatlist is a second choice target. In such a situation the target with the highest threat should be attacked. + noPriorityTargetFound = true; + iter = iThreatList.begin(); + continue; + } + } + + if (pAttacker->canCreatureAttack(target)) // skip non attackable currently targets + { + if (pCurrentVictim) // select 1.3/1.1 better target in comparison current target + { + // list sorted and and we check current target, then this is best case + if (pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat()) + { + currentRef = pCurrentVictim; // for second case + found = true; + break; + } + + if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || + currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && + pAttacker->IsWithinMeleeRange(target)) + { //implement 110% threat rule for targets in melee range + found = true; //and 130% rule for targets in ranged distances + break; //for selecting alive targets + } + } + else // select any + { + found = true; + break; + } + } + ++iter; + } + if (!found) + currentRef = NULL; + + return currentRef; +} + +//============================================================ +//=================== ThreatManager ========================== +//============================================================ + +ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner), iUpdateTimer(THREAT_UPDATE_INTERVAL) +{ +} + +//============================================================ + +void ThreatManager::clearReferences() +{ + iThreatContainer.clearReferences(); + iThreatOfflineContainer.clearReferences(); + iCurrentVictim = NULL; + iUpdateTimer = THREAT_UPDATE_INTERVAL; +} + +//============================================================ + +void ThreatManager::addThreat(Unit* pVictim, float fThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell) +{ + //function deals with adding threat and adding players and pets into ThreatList + //mobs, NPCs, guards have ThreatList and HateOfflineList + //players and pets have only InHateListOf + //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.) + + // not to self + if (pVictim == getOwner()) + return; + + // not to GM + if (!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && pVictim->ToPlayer()->isGameMaster())) + return; + + // not to dead and not for dead + if (!pVictim->isAlive() || !getOwner()->isAlive()) + return; + + assert(getOwner()->GetTypeId() == TYPEID_UNIT); + + float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, fThreat, schoolMask, pThreatSpell); + + // must check > 0.0f, otherwise dead loop + if (threat > 0.0f && pVictim->GetReducedThreatPercent()) + { + uint32 reducedThreadPercent = pVictim->GetReducedThreatPercent(); + + Unit *unit = pVictim->GetMisdirectionTarget(); + if (unit) + if (Aura* pAura = unit->GetAura(63326)) // Glyph of Vigilance + reducedThreadPercent += pAura->GetSpellProto()->EffectBasePoints[0]; + + float reducedThreat = threat * reducedThreadPercent / 100; + threat -= reducedThreat; + if (unit) + _addThreat(unit, reducedThreat); + } + + _addThreat(pVictim, threat); +} + +void ThreatManager::_addThreat(Unit *pVictim, float fThreat) +{ + HostileReference* ref = iThreatContainer.addThreat(pVictim, fThreat); + // Ref is not in the online refs, search the offline refs next + if (!ref) + ref = iThreatOfflineContainer.addThreat(pVictim, fThreat); + + if (!ref) // there was no ref => create a new one + { + // threat has to be 0 here + HostileReference* hostilReference = new HostileReference(pVictim, this, 0); + iThreatContainer.addReference(hostilReference); + hostilReference->addThreat(fThreat); // now we add the real threat + if (pVictim->GetTypeId() == TYPEID_PLAYER && pVictim->ToPlayer()->isGameMaster()) + hostilReference->setOnlineOfflineState(false); // GM is always offline + } +} + +//============================================================ + +void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 iPercent) +{ + iThreatContainer.modifyThreatPercent(pVictim, iPercent); +} + +//============================================================ + +Unit* ThreatManager::getHostilTarget() +{ + iThreatContainer.update(); + HostileReference* nextVictim = iThreatContainer.selectNextVictim(getOwner()->ToCreature(), getCurrentVictim()); + setCurrentVictim(nextVictim); + return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL; +} + +//============================================================ + +float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList) +{ + float threat = 0.0f; + HostileReference* ref = iThreatContainer.getReferenceByTarget(pVictim); + if (!ref && pAlsoSearchOfflineList) + ref = iThreatOfflineContainer.getReferenceByTarget(pVictim); + if (ref) + threat = ref->getThreat(); + return threat; +} + +//============================================================ + +void ThreatManager::tauntApply(Unit* pTaunter) +{ + HostileReference* ref = iThreatContainer.getReferenceByTarget(pTaunter); + if (getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat())) + { + if (ref->getTempThreatModifier() == 0.0f) // Ok, temp threat is unused + ref->setTempThreat(getCurrentVictim()->getThreat()); + } +} + +//============================================================ + +void ThreatManager::tauntFadeOut(Unit *pTaunter) +{ + HostileReference* ref = iThreatContainer.getReferenceByTarget(pTaunter); + if (ref) + ref->resetTempThreat(); +} + +//============================================================ + +void ThreatManager::setCurrentVictim(HostileReference* pHostileReference) +{ + if (pHostileReference && pHostileReference != iCurrentVictim) + { + iOwner->SendChangeCurrentVictimOpcode(pHostileReference); + } + iCurrentVictim = pHostileReference; +} + +//============================================================ +// The hated unit is gone, dead or deleted +// return true, if the event is consumed + +void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent) +{ + threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager + + HostileReference* hostilReference = threatRefStatusChangeEvent->getReference(); + + switch(threatRefStatusChangeEvent->getType()) + { + case UEV_THREAT_REF_THREAT_CHANGE: + if ((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) || + (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f)) + setDirty(true); // the order in the threat list might have changed + break; + case UEV_THREAT_REF_ONLINE_STATUS: + if (!hostilReference->isOnline()) + { + if (hostilReference == getCurrentVictim()) + { + setCurrentVictim(NULL); + setDirty(true); + } + iThreatContainer.remove(hostilReference); + iThreatOfflineContainer.addReference(hostilReference); + } + else + { + if (getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat())) + setDirty(true); + iThreatContainer.addReference(hostilReference); + iThreatOfflineContainer.remove(hostilReference); + } + break; + case UEV_THREAT_REF_REMOVE_FROM_LIST: + if (hostilReference == getCurrentVictim()) + { + setCurrentVictim(NULL); + setDirty(true); + } + iOwner->SendRemoveFromThreatListOpcode(hostilReference); + if (hostilReference->isOnline()) + iThreatContainer.remove(hostilReference); + else + iThreatOfflineContainer.remove(hostilReference); + break; + } +} + +bool ThreatManager::isNeedUpdateToClient(uint32 time) +{ + if (isThreatListEmpty()) + return false; + if (time >= iUpdateTimer) + { + iUpdateTimer = THREAT_UPDATE_INTERVAL; + return true; + } + iUpdateTimer -= time; + return false; +} + +// Reset all aggro without modifying the threadlist. +void ThreatManager::resetAllAggro() +{ + std::list &threatlist = getThreatList(); + if (threatlist.empty()) + return; + + for (std::list::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + (*itr)->setThreat(0); + } + + setDirty(true); +} diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h new file mode 100644 index 00000000000..723a553e9d7 --- /dev/null +++ b/src/server/game/Combat/ThreatManager.h @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _THREATMANAGER +#define _THREATMANAGER + +#include "Common.h" +#include "SharedDefines.h" +#include "Utilities/LinkedReference/Reference.h" +#include "UnitEvents.h" + +#include + +//============================================================== + +class Unit; +class Creature; +class ThreatManager; +struct SpellEntry; + +#define THREAT_UPDATE_INTERVAL 1 * IN_MILISECONDS // Server should send threat update to client periodically each second + +//============================================================== +// Class to calculate the real threat based + +class ThreatCalcHelper +{ + public: + static float calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL); +}; + +//============================================================== +class HostileReference : public Reference +{ + public: + HostileReference(Unit* pUnit, ThreatManager *pThreatManager, float fThreat); + + //================================================= + void addThreat(float fModThreat); + + void setThreat(float fThreat) { addThreat(fThreat - getThreat()); } + + void addThreatPercent(int32 pPercent) + { + float tmpThreat = iThreat; + tmpThreat = tmpThreat * (pPercent+100.0f) / 100.0f; + addThreat(tmpThreat-iThreat); + } + + float getThreat() const { return iThreat; } + + bool isOnline() const { return iOnline; } + + // The Unit might be in water and the creature can not enter the water, but has range attack + // in this case online = true, but accessible = false + bool isAccessible() const { return iAccessible; } + + // used for temporary setting a threat and reducting it later again. + // the threat modification is stored + void setTempThreat(float fThreat) + { + addTempThreat(fThreat - getThreat()); + } + + void addTempThreat(float fThreat) + { + iTempThreatModifier = fThreat; + if (iTempThreatModifier != 0.0f) + addThreat(iTempThreatModifier); + } + + void resetTempThreat() + { + if (iTempThreatModifier != 0.0f) + { + addThreat(-iTempThreatModifier); + iTempThreatModifier = 0.0f; + } + } + + float getTempThreatModifier() { return iTempThreatModifier; } + + //================================================= + // check, if source can reach target and set the status + void updateOnlineStatus(); + + void setOnlineOfflineState(bool pIsOnline); + + void setAccessibleState(bool pIsAccessible); + //================================================= + + bool operator == (const HostileReference& pHostileReference) const { return pHostileReference.getUnitGuid() == getUnitGuid(); } + + //================================================= + + uint64 getUnitGuid() const { return iUnitGuid; } + + //================================================= + // reference is not needed anymore. realy delete it ! + + void removeReference(); + + //================================================= + + HostileReference* next() { return ((HostileReference*) Reference::next()); } + + //================================================= + + // Tell our refTo (target) object that we have a link + void targetObjectBuildLink(); + + // Tell our refTo (taget) object, that the link is cut + void targetObjectDestroyLink(); + + // Tell our refFrom (source) object, that the link is cut (Target destroyed) + void sourceObjectDestroyLink(); + private: + // Inform the source, that the status of that reference was changed + void fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent); + + Unit* getSourceUnit(); + private: + float iThreat; + float iTempThreatModifier; // used for taunt + uint64 iUnitGuid; + bool iOnline; + bool iAccessible; +}; + +//============================================================== +class ThreatManager; + +class ThreatContainer +{ + private: + std::list iThreatList; + bool iDirty; + protected: + friend class ThreatManager; + + void remove(HostileReference* pRef) { iThreatList.remove(pRef); } + void addReference(HostileReference* pHostileReference) { iThreatList.push_back(pHostileReference); } + void clearReferences(); + // Sort the list if necessary + void update(); + public: + ThreatContainer() { iDirty = false; } + ~ThreatContainer() { clearReferences(); } + + HostileReference* addThreat(Unit* pVictim, float fThreat); + + void modifyThreatPercent(Unit *pVictim, int32 iPercent); + + HostileReference* selectNextVictim(Creature* pAttacker, HostileReference* pCurrentVictim); + + void setDirty(bool pDirty) { iDirty = pDirty; } + + bool isDirty() { return iDirty; } + + bool empty() { return(iThreatList.empty()); } + + HostileReference* getMostHated() { return iThreatList.empty() ? NULL : iThreatList.front(); } + + HostileReference* getReferenceByTarget(Unit* pVictim); + + std::list& getThreatList() { return iThreatList; } +}; + +//================================================= + +class ThreatManager +{ + public: + friend class HostileReference; + + explicit ThreatManager(Unit *pOwner); + + ~ThreatManager() { clearReferences(); } + + void clearReferences(); + + void addThreat(Unit* pVictim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL); + void modifyThreatPercent(Unit *pVictim, int32 iPercent); + + float getThreat(Unit *pVictim, bool pAlsoSearchOfflineList = false); + + bool isThreatListEmpty() { return iThreatContainer.empty(); } + + void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent); + + bool isNeedUpdateToClient(uint32 time); + + HostileReference* getCurrentVictim() { return iCurrentVictim; } + + Unit* getOwner() { return iOwner; } + + Unit* getHostilTarget(); + + void tauntApply(Unit* pTaunter); + void tauntFadeOut(Unit *pTaunter); + + void setCurrentVictim(HostileReference* pHostileReference); + + void setDirty(bool bDirty) { iThreatContainer.setDirty(bDirty); } + + // Reset all aggro without modifying the threadlist. + void resetAllAggro(); + + // Reset all aggro of unit in threadlist satisfying the predicate. + template void resetAggro(PREDICATE predicate) + { + std::list &threatlist = getThreatList(); + if (threatlist.empty()) + return; + + for (std::list::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + HostileReference* ref = (*itr); + + if (predicate(ref->getTarget())) + { + ref->setThreat(0); + setDirty(true); + } + } + } + + // methods to access the lists from the outside to do some dirty manipulation (scriping and such) + // I hope they are used as little as possible. + std::list& getThreatList() { return iThreatContainer.getThreatList(); } + std::list& getOfflieThreatList() { return iThreatOfflineContainer.getThreatList(); } + ThreatContainer& getOnlineContainer() { return iThreatContainer; } + ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; } + private: + void _addThreat(Unit *pVictim, float fThreat); + + HostileReference* iCurrentVictim; + Unit* iOwner; + uint32 iUpdateTimer; + ThreatContainer iThreatContainer; + ThreatContainer iThreatOfflineContainer; +}; + +//================================================= + +namespace Trinity +{ + // Binary predicate for sorting HostileReferences based on threat value + class ThreatOrderPred + { + public: + ThreatOrderPred(bool ascending = false) : m_ascending(ascending) {} + bool operator() (const HostileReference *a, const HostileReference *b) const + { + return m_ascending ? a->getThreat() < b->getThreat() : a->getThreat() > b->getThreat(); + } + private: + const bool m_ascending; + }; +} +#endif + diff --git a/src/server/game/CombatAI.cpp b/src/server/game/CombatAI.cpp deleted file mode 100644 index 0d0ff17ffd7..00000000000 --- a/src/server/game/CombatAI.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "CombatAI.h" -#include "SpellMgr.h" -#include "Vehicle.h" - -int AggressorAI::Permissible(const Creature *creature) -{ - // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight - if (!creature->isCivilian() && !creature->IsNeutralToAll()) - return PERMIT_BASE_PROACTIVE; - - return PERMIT_BASE_NO; -} - -void AggressorAI::UpdateAI(const uint32 /*diff*/) -{ - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); -} - -// some day we will delete these useless things -int CombatAI::Permissible(const Creature * /*creature*/) -{ - return PERMIT_BASE_NO; -} - -int ArchorAI::Permissible(const Creature * /*creature*/) -{ - return PERMIT_BASE_NO; -} - -int TurretAI::Permissible(const Creature * /*creature*/) -{ - return PERMIT_BASE_NO; -} - -int AOEAI::Permissible(const Creature * /*creature*/) -{ - return PERMIT_BASE_NO; -} - -int VehicleAI::Permissible(const Creature * /*creature*/) -{ - return PERMIT_BASE_NO; -} - -void CombatAI::InitializeAI() -{ - for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) - if (me->m_spells[i] && GetSpellStore()->LookupEntry(me->m_spells[i])) - spells.push_back(me->m_spells[i]); - - CreatureAI::InitializeAI(); -} - -void CombatAI::Reset() -{ - events.Reset(); -} - -void CombatAI::JustDied(Unit *killer) -{ - for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i) - if (AISpellInfo[*i].condition == AICOND_DIE) - me->CastSpell(killer, *i, true); -} - -void CombatAI::EnterCombat(Unit *who) -{ - for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i) - { - if (AISpellInfo[*i].condition == AICOND_AGGRO) - me->CastSpell(who, *i, false); - else if (AISpellInfo[*i].condition == AICOND_COMBAT) - events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown); - } -} - -void CombatAI::UpdateAI(const uint32 diff) -{ - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->hasUnitState(UNIT_STAT_CASTING)) - return; - - if (uint32 spellId = events.ExecuteEvent()) - { - DoCast(spellId); - events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown); - } - else - DoMeleeAttackIfReady(); -} - -///////////////// -//CasterAI -///////////////// - -void CasterAI::InitializeAI() -{ - CombatAI::InitializeAI(); - - float m_attackDist = 30.0f; - for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr) - if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange) - m_attackDist = GetAISpellInfo(*itr)->maxRange; - if (m_attackDist == 30.0f) - m_attackDist = MELEE_RANGE; -} - -void CasterAI::EnterCombat(Unit *who) -{ - if (spells.empty()) - return; - - uint32 spell = rand()%spells.size(); - uint32 count = 0; - for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count) - { - if (AISpellInfo[*itr].condition == AICOND_AGGRO) - me->CastSpell(who, *itr, false); - else if (AISpellInfo[*itr].condition == AICOND_COMBAT) - { - uint32 cooldown = GetAISpellInfo(*itr)->realCooldown; - if (count == spell) - { - DoCast(spells[spell]); - cooldown += me->GetCurrentSpellCastTime(*itr); - } - events.ScheduleEvent(*itr, cooldown); - } - } -} - -void CasterAI::UpdateAI(const uint32 diff) -{ - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->hasUnitState(UNIT_STAT_CASTING)) - return; - - if (uint32 spellId = events.ExecuteEvent()) - { - DoCast(spellId); - uint32 casttime = me->GetCurrentSpellCastTime(spellId); - events.ScheduleEvent(spellId, (casttime ? casttime : 500) + GetAISpellInfo(spellId)->realCooldown); - } -} - -////////////// -//ArchorAI -////////////// - -ArchorAI::ArchorAI(Creature *c) : CreatureAI(c) -{ - if (!me->m_spells[0]) - sLog.outError("ArchorAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); - - m_minRange = GetSpellMinRange(me->m_spells[0], false); - if (!m_minRange) - m_minRange = MELEE_RANGE; - me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false); - me->m_SightDistance = me->m_CombatDistance; -} - -void ArchorAI::AttackStart(Unit *who) -{ - if (!who) - return; - - if (me->IsWithinCombatRange(who, m_minRange)) - { - if (me->Attack(who, true) && !who->IsFlying()) - me->GetMotionMaster()->MoveChase(who); - } - else - { - if (me->Attack(who, false) && !who->IsFlying()) - me->GetMotionMaster()->MoveChase(who, me->m_CombatDistance); - } - - if (who->IsFlying()) - me->GetMotionMaster()->MoveIdle(); -} - -void ArchorAI::UpdateAI(const uint32 /*diff*/) -{ - if (!UpdateVictim()) - return; - - if (!me->IsWithinCombatRange(me->getVictim(), m_minRange)) - DoSpellAttackIfReady(me->m_spells[0]); - else - DoMeleeAttackIfReady(); -} - -////////////// -//TurretAI -////////////// - -TurretAI::TurretAI(Creature *c) : CreatureAI(c) -{ - if (!me->m_spells[0]) - sLog.outError("TurretAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); - - m_minRange = GetSpellMinRange(me->m_spells[0], false); - me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false); - me->m_SightDistance = me->m_CombatDistance; -} - -bool TurretAI::CanAIAttack(const Unit * /*who*/) const -{ - // TODO: use one function to replace it - if (!me->IsWithinCombatRange(me->getVictim(), me->m_CombatDistance) - || m_minRange && me->IsWithinCombatRange(me->getVictim(), m_minRange)) - return false; - return true; -} - -void TurretAI::AttackStart(Unit *who) -{ - if (who) - me->Attack(who, false); -} - -void TurretAI::UpdateAI(const uint32 /*diff*/) -{ - if (!UpdateVictim()) - return; - - DoSpellAttackIfReady(me->m_spells[0]); -} - -////////////// -//AOEAI -////////////// - -AOEAI::AOEAI(Creature *c) : CreatureAI(c) -{ - if (!me->m_spells[0]) - sLog.outError("AOEAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); - - me->SetVisibility(VISIBILITY_ON);//visible to see all spell anims - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);//can't be targeted - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_1);//can't be damaged - me->SetDisplayId(11686);//invisible model,around a size of a player -} - -bool AOEAI::CanAIAttack(const Unit * /*who*/) const -{ - return false; -} - -void AOEAI::AttackStart(Unit * /*who*/) -{ -} - -void AOEAI::UpdateAI(const uint32 /*diff*/) -{ - if (!me->HasAura(me->m_spells[0])) - me->CastSpell(me, me->m_spells[0],false); -} - -////////////// -//VehicleAI -////////////// - -VehicleAI::VehicleAI(Creature *c) : CreatureAI(c), m_vehicle(c->GetVehicleKit()), m_IsVehicleInUse(false), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME) -{ - LoadConditions(); - m_DoDismiss = false; - m_DismissTimer = VEHICLE_DISMISS_TIME; -} - - -//NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted -void VehicleAI::UpdateAI(const uint32 diff) -{ - CheckConditions(diff); - - if (m_DoDismiss) - { - if (m_DismissTimer < diff) - { - m_DoDismiss = false; - me->SetVisibility(VISIBILITY_OFF); - me->ForcedDespawn(); - }else m_DismissTimer -= diff; - } -} - -void VehicleAI::Reset() -{ - me->SetVisibility(VISIBILITY_ON); - - m_vehicle->Reset(); -} - -void VehicleAI::OnCharmed(bool apply) -{ - if (m_IsVehicleInUse && !apply && !conditions.empty())//was used and has conditions - { - m_DoDismiss = true;//needs reset - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } - else if (apply) - m_DoDismiss = false;//in use again - m_DismissTimer = VEHICLE_DISMISS_TIME;//reset timer - m_IsVehicleInUse = apply; -} - -void VehicleAI::LoadConditions() -{ - conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry()); - if (!conditions.empty()) - { - sLog.outDebug("VehicleAI::LoadConditions: loaded %u conditions", uint32(conditions.size())); - } -} - -void VehicleAI::CheckConditions(const uint32 diff) -{ - if(m_ConditionsTimer < diff) - { - if (!conditions.empty()) - { - for (SeatMap::iterator itr = m_vehicle->m_Seats.begin(); itr != m_vehicle->m_Seats.end(); ++itr) - if (Unit *passenger = itr->second.passenger) - { - if (Player* plr = passenger->ToPlayer()) - { - if (!sConditionMgr.IsPlayerMeetToConditions(plr, conditions)) - { - plr->ExitVehicle(); - return;//check other pessanger in next tick - } - } - } - } - m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME; - } else m_ConditionsTimer -= diff; -} \ No newline at end of file diff --git a/src/server/game/CombatAI.h b/src/server/game/CombatAI.h deleted file mode 100644 index 8626b38dd37..00000000000 --- a/src/server/game/CombatAI.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_COMBATAI_H -#define TRINITY_COMBATAI_H - -#include "CreatureAI.h" -#include "CreatureAIImpl.h" -#include "ConditionMgr.h" - -class Creature; - -class AggressorAI : public CreatureAI -{ - public: - explicit AggressorAI(Creature *c) : CreatureAI(c) {} - - void UpdateAI(const uint32); - static int Permissible(const Creature *); -}; - -typedef std::vector SpellVct; - -class CombatAI : public CreatureAI -{ - public: - explicit CombatAI(Creature *c) : CreatureAI(c) {} - - void InitializeAI(); - void Reset(); - void EnterCombat(Unit* who); - void JustDied(Unit *killer); - void UpdateAI(const uint32 diff); - static int Permissible(const Creature *); - protected: - EventMap events; - SpellVct spells; -}; - -class CasterAI : public CombatAI -{ - public: - explicit CasterAI(Creature *c) : CombatAI(c) { m_attackDist = MELEE_RANGE; } - void InitializeAI(); - void AttackStart(Unit * victim) { AttackStartCaster(victim, m_attackDist); } - void UpdateAI(const uint32 diff); - void EnterCombat(Unit * /*who*/); - private: - float m_attackDist; -}; - -struct ArchorAI : public CreatureAI -{ - public: - explicit ArchorAI(Creature *c); - void AttackStart(Unit *who); - void UpdateAI(const uint32 diff); - - static int Permissible(const Creature *); - protected: - float m_minRange; -}; - -struct TurretAI : public CreatureAI -{ - public: - explicit TurretAI(Creature *c); - bool CanAIAttack(const Unit *who) const; - void AttackStart(Unit *who); - void UpdateAI(const uint32 diff); - - static int Permissible(const Creature *); - protected: - float m_minRange; -}; - -struct AOEAI : public CreatureAI -{ - public: - explicit AOEAI(Creature *c); - bool CanAIAttack(const Unit *who) const; - void AttackStart(Unit *who); - void UpdateAI(const uint32 diff); - - static int Permissible(const Creature *); -}; -#define VEHICLE_CONDITION_CHECK_TIME 1000 -#define VEHICLE_DISMISS_TIME 5000 -struct VehicleAI : public CreatureAI -{ - public: - explicit VehicleAI(Creature *c); - - void UpdateAI(const uint32 diff); - static int Permissible(const Creature *); - void Reset(); - void MoveInLineOfSight(Unit *) {} - void AttackStart(Unit *) {} - void OnCharmed(bool apply); - - private: - Vehicle* m_vehicle; - bool m_IsVehicleInUse; - void LoadConditions(); - void CheckConditions(const uint32 diff); - ConditionList conditions; - uint32 m_ConditionsTimer; - bool m_DoDismiss; - uint32 m_DismissTimer; -}; - -#endif diff --git a/src/server/game/CombatHandler.cpp b/src/server/game/CombatHandler.cpp deleted file mode 100644 index 404b70c1c84..00000000000 --- a/src/server/game/CombatHandler.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Log.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "ObjectAccessor.h" -#include "CreatureAI.h" -#include "ObjectDefines.h" - -void WorldSession::HandleAttackSwingOpcode(WorldPacket & recv_data) -{ - uint64 guid; - recv_data >> guid; - - DEBUG_LOG("WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid)); - - Unit *pEnemy = ObjectAccessor::GetUnit(*_player, guid); - - if (!pEnemy) - { - if (!IS_UNIT_GUID(guid)) - sLog.outError("WORLD: Object %u (TypeID: %u) isn't player, pet or creature",GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); - else - sLog.outError("WORLD: Enemy %s %u not found",GetLogNameForGuid(guid),GUID_LOPART(guid)); - - // stop attack state at client - SendAttackStop(NULL); - return; - } - - if (!_player->canAttack(pEnemy)) - { - sLog.outError("WORLD: Enemy %s %u is friendly",(IS_PLAYER_GUID(guid) ? "player" : "creature"),GUID_LOPART(guid)); - - // stop attack state at client - SendAttackStop(pEnemy); - return; - } - - _player->Attack(pEnemy,true); -} - -void WorldSession::HandleAttackStopOpcode(WorldPacket & /*recv_data*/) -{ - GetPlayer()->AttackStop(); -} - -void WorldSession::HandleSetSheathedOpcode(WorldPacket & recv_data) -{ - uint32 sheathed; - recv_data >> sheathed; - - //sLog.outDebug("WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed); - - if (sheathed >= MAX_SHEATH_STATE) - { - sLog.outError("Unknown sheath state %u ??",sheathed); - return; - } - - GetPlayer()->SetSheath(SheathState(sheathed)); -} - -void WorldSession::SendAttackStop(Unit const* enemy) -{ - WorldPacket data(SMSG_ATTACKSTOP, (4+20)); // we guess size - data.append(GetPlayer()->GetPackGUID()); - data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid - data << uint32(0); // unk, can be 1 also - SendPacket(&data); -} - diff --git a/src/server/game/ConditionMgr.cpp b/src/server/game/ConditionMgr.cpp deleted file mode 100644 index 8e5a7e5677e..00000000000 --- a/src/server/game/ConditionMgr.cpp +++ /dev/null @@ -1,1145 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "Policies/SingletonImp.h" -#include "Player.h" -#include "SpellAuras.h" -#include "SpellMgr.h" -#include "GameEventMgr.h" -#include "ObjectMgr.h" -#include "ProgressBar.h" -#include "InstanceData.h" -#include "ConditionMgr.h" - -INSTANTIATE_SINGLETON_1(ConditionMgr); - -// Checks if player meets the condition -// Can have CONDITION_SOURCE_TYPE_NONE && !mReferenceId if called from a special event (ie: eventAI) -bool Condition::Meets(Player * player, Unit* targetOverride) -{ - if (!player) - { - sLog.outDebug("Condition player not found"); - return false; // player not present, return false - } - uint32 refId = 0; - bool condMeets = false; - bool sendErrorMsg = false; - refId = mConditionValue3;//value 3 can be a 'quick' reference - switch (mConditionType) - { - case CONDITION_NONE: - condMeets = true; // empty condition, always met - break; - case CONDITION_AURA: - condMeets = player->HasAuraEffect(mConditionValue1, mConditionValue2); - break; - case CONDITION_ITEM: - condMeets = player->HasItemCount(mConditionValue1, mConditionValue2); - break; - case CONDITION_ITEM_EQUIPPED: - condMeets = player->HasItemOrGemWithIdEquipped(mConditionValue1,1); - break; - case CONDITION_ZONEID: - condMeets = player->GetZoneId() == mConditionValue1; - break; - case CONDITION_REPUTATION_RANK: - { - FactionEntry const* faction = sFactionStore.LookupEntry(mConditionValue1); - condMeets = faction && uint32(player->GetReputationMgr().GetRank(faction)) >= int32(mConditionValue2); - break; - } - case CONDITION_ACHIEVEMENT: - { - AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(mConditionValue1); - condMeets = player->GetAchievementMgr().HasAchieved(achievement); - break; - } - case CONDITION_TEAM: - condMeets = player->GetTeam() == mConditionValue1; - break; - case CONDITION_CLASS: - condMeets = player->getClass() == mConditionValue1; - break; - case CONDITION_RACE: - condMeets = player->getRace() == mConditionValue1; - break; - case CONDITION_SKILL: - condMeets = player->HasSkill(mConditionValue1) && player->GetBaseSkillValue(mConditionValue1) >= mConditionValue2; - break; - case CONDITION_QUESTREWARDED: - condMeets = player->GetQuestRewardStatus(mConditionValue1); - break; - case CONDITION_QUESTTAKEN: - { - QuestStatus status = player->GetQuestStatus(mConditionValue1); - condMeets = (status == QUEST_STATUS_INCOMPLETE); - break; - } - case CONDITION_QUEST_NONE: - { - QuestStatus status = player->GetQuestStatus(mConditionValue1); - condMeets = (status == QUEST_STATUS_NONE); - break; - } - case CONDITION_AD_COMMISSION_AURA: - { - Unit::AuraApplicationMap const& auras = player->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - if ((itr->second->GetBase()->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetBase()->GetSpellProto()->SpellVisual[0] == 3580) - { - condMeets = true; - break; - } - condMeets = false; - break; - } - case CONDITION_NO_AURA: - condMeets = !player->HasAuraEffect(mConditionValue1, mConditionValue2); - break; - case CONDITION_ACTIVE_EVENT: - condMeets = gameeventmgr.IsActiveEvent(mConditionValue1); - break; - case CONDITION_INSTANCE_DATA: - { - Map *map = player->GetMap(); - if (map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData()) - condMeets = ((InstanceMap*)map)->GetInstanceData()->GetData(mConditionValue1) == mConditionValue2; - break; - } - case CONDITION_SPELL_SCRIPT_TARGET: - condMeets = true;//spell target condition is handled in spellsystem, here it is always true - refId = 0;//cant have references! use CONDITION_SOURCE_TYPE_SPELL for it - break; - case CONDITION_CREATURE_TARGET: - { - Unit* target = player->GetSelectedUnit(); - if (targetOverride) - target = targetOverride; - if (target) - if (Creature* cTarget = target->ToCreature()) - if (cTarget->GetEntry() == mConditionValue1) - condMeets = true; - break; - } - case CONDITION_TARGET_HEALTH_BELOW_PCT: - { - Unit* target = player->GetSelectedUnit(); - if (targetOverride) - target = targetOverride; - if (target) - if ((target->GetHealth()*100 / target->GetMaxHealth()) <= mConditionValue1) - condMeets = true; - break; - } - case CONDITION_TARGET_RANGE: - { - if (Unit* target = player->GetSelectedUnit()) - if (player->GetDistance(target) >= mConditionValue1 && (!mConditionValue2 || player->GetDistance(target) <= mConditionValue2)) - condMeets = true; - break; - } - case CONDITION_MAPID: - condMeets = player->GetMapId() == mConditionValue1; - break; - case CONDITION_AREAID: - condMeets = player->GetAreaId() == mConditionValue1; - break; - case CONDITION_ITEM_TARGET: - { - condMeets = true;//handled in Item::IsTargetValidForItemUse - refId = 0;//cant have references for now - break; - } - default: - condMeets = false; - refId = 0; - break; - } - switch (mSourceType) - { - case CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET: - case CONDITION_SOURCE_TYPE_SPELL: - sendErrorMsg = true; - break; - } - - bool refMeets = false; - if (condMeets && refId)//only have to check references if 'this' is met - { - ConditionList ref = sConditionMgr.GetConditionReferences(refId); - refMeets = sConditionMgr.IsPlayerMeetToConditions(player, ref); - }else refMeets = true; - - if (sendErrorMsg && ErrorTextd && (!condMeets || !refMeets))//send special error from DB - player->m_ConditionErrorMsgId = ErrorTextd; - - return condMeets && refMeets; -} - -ConditionMgr::ConditionMgr() -{ -} - -ConditionMgr::~ConditionMgr() -{ -} - -ConditionList ConditionMgr::GetConditionReferences(uint32 refId) -{ - ConditionList conditions; - ConditionReferenceMap::const_iterator ref = m_ConditionReferenceMap.find(refId); - if (ref != m_ConditionReferenceMap.end()) - conditions = (*ref).second; - return conditions; -} - -bool ConditionMgr::IsPlayerMeetToConditionList(Player* player,const ConditionList& conditions, Unit* targetOverride) -{ - std::mapElseGroupMap; - for (ConditionList::const_iterator i = conditions.begin(); i != conditions.end(); ++i) - { - sLog.outDebug("ConditionMgr::IsPlayerMeetToConditionList condType: %u val1: %u",(*i)->mConditionType,(*i)->mConditionValue1); - if ((*i)->isLoaded()) - { - std::map::const_iterator itr = ElseGroupMap.find((*i)->mElseGroup); - if (itr == ElseGroupMap.end()) - ElseGroupMap[(*i)->mElseGroup] = true; - else if (!(*itr).second) - continue; - - if ((*i)->mReferenceId)//handle reference - { - ConditionReferenceMap::const_iterator ref = m_ConditionReferenceMap.find((*i)->mReferenceId); - if (ref != m_ConditionReferenceMap.end()) - { - if(!IsPlayerMeetToConditionList(player, (*ref).second, targetOverride)) - ElseGroupMap[(*i)->mElseGroup] = false; - }else{ - sLog.outDebug("IsPlayerMeetToConditionList: Reference template -%u not found", (*i)->mReferenceId);//checked at loading, should never happen - } - - } else//handle normal condition - { - if (!(*i)->Meets(player, targetOverride)) - ElseGroupMap[(*i)->mElseGroup] = false; - } - } - } - for (std::map::const_iterator i = ElseGroupMap.begin(); i != ElseGroupMap.end(); ++i) - if (i->second) - return true; - return false; -} - -bool ConditionMgr::IsPlayerMeetToConditions(Player* player, ConditionList conditions, Unit* targetOverride) -{ - if (conditions.empty()) - return true; - if(player) - player->m_ConditionErrorMsgId = 0; - - sLog.outDebug("ConditionMgr::IsPlayerMeetToConditions"); - bool result = IsPlayerMeetToConditionList(player, conditions, targetOverride); - - if (player && player->m_ConditionErrorMsgId && player->GetSession()) - player->GetSession()->SendNotification(player->m_ConditionErrorMsgId);//m_ConditionErrorMsgId is set only if a condition was not met - - return result; -} - -ConditionList ConditionMgr::GetConditionsForNotGroupedEntry(ConditionSourceType sType, uint32 uEntry) -{ - ConditionList spellCond; - if (sType > CONDITION_SOURCE_TYPE_NONE && sType < MAX_CONDITIONSOURCETYPE) - { - ConditionMap::const_iterator itr = m_ConditionMap.find(sType); - if (itr != m_ConditionMap.end()) - { - ConditionTypeMap::const_iterator i = (*itr).second.find(uEntry); - if (i != (*itr).second.end()) - { - spellCond = (*i).second; - sLog.outDebug("GetConditionsForNotGroupedEntry: found conditions for type %u and entry %u", uint32(sType), uEntry); - } - } - } - return spellCond; -} - -void ConditionMgr::LoadConditions(bool isReload) -{ - m_ConditionMap.clear(); // for reload case - m_ConditionReferenceMap.clear(); // for reload case - //must clear all custom handled cases (groupped types) before reload - if (isReload) - { - sLog.outString("Reseting Loot Conditions..."); - LootTemplates_Creature.ResetConditions(); - LootTemplates_Fishing.ResetConditions(); - LootTemplates_Gameobject.ResetConditions(); - LootTemplates_Item.ResetConditions(); - LootTemplates_Mail.ResetConditions(); - LootTemplates_Milling.ResetConditions(); - LootTemplates_Pickpocketing.ResetConditions(); - LootTemplates_Reference.ResetConditions(); - LootTemplates_Skinning.ResetConditions(); - LootTemplates_Disenchant.ResetConditions(); - LootTemplates_Prospecting.ResetConditions(); - LootTemplates_Spell.ResetConditions(); - - sLog.outString("Re-Loading `gossip_menu` Table for Conditions!"); - objmgr.LoadGossipMenu(); - - sLog.outString("Re-Loading `gossip_menu_option` Table for Conditions!"); - objmgr.LoadGossipMenuItems(); - } - - uint32 count = 0; - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT SourceTypeOrReferenceId, SourceGroup, SourceEntry, ElseGroup, ConditionTypeOrReference, ConditionValue1, ConditionValue2, ConditionValue3, ErrorTextId FROM conditions"); - - if (!result) - { - barGoLink bar(1); - - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded `conditions`, table is empty!"); - return; - } - - barGoLink bar(result->GetRowCount()); - - do - { - bar.step(); - - Field *fields = result->Fetch(); - - Condition* cond = new Condition(); - int32 iSourceTypeOrReferenceId = fields[0].GetInt32(); - cond->mSourceGroup = fields[1].GetUInt32(); - cond->mSourceEntry = fields[2].GetUInt32(); - cond->mElseGroup = fields[3].GetUInt32(); - int32 iConditionTypeOrReference = fields[4].GetInt32(); - cond->mConditionValue1 = fields[5].GetUInt32(); - cond->mConditionValue2 = fields[6].GetUInt32(); - cond->mConditionValue3 = fields[7].GetUInt32(); - cond->ErrorTextd = fields[8].GetUInt32(); - - if (iConditionTypeOrReference >= 0) - cond->mConditionType = ConditionType(iConditionTypeOrReference); - - if (iConditionTypeOrReference < 0)//it has a reference - { - if (iConditionTypeOrReference == iSourceTypeOrReferenceId)//self referencing, skipp - { - sLog.outErrorDb("Condition reference %i is referencing self, skipped", iSourceTypeOrReferenceId); - continue; - } - cond->mReferenceId = uint32(abs(iConditionTypeOrReference)); - - const char* rowType = "reference template"; - if (iSourceTypeOrReferenceId >= 0) - rowType = "reference"; - //check for useless data - if (cond->mConditionValue1) - sLog.outErrorDb("Condition %s %i has useless data in value1 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue1); - if (cond->mConditionValue2) - sLog.outErrorDb("Condition %s %i has useless data in value2 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue2); - if (cond->mConditionValue3) - sLog.outErrorDb("Condition %s %i has useless data in value3 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue3); - if (cond->mSourceGroup && iSourceTypeOrReferenceId < 0) - sLog.outErrorDb("Condition %s %i has useless data in SourceGroup (%u)!", rowType, iSourceTypeOrReferenceId, cond->mSourceGroup); - if (cond->mSourceEntry && iSourceTypeOrReferenceId < 0) - sLog.outErrorDb("Condition %s %i has useless data in SourceEntry (%u)!", rowType, iSourceTypeOrReferenceId, cond->mSourceEntry); - }else if (!isConditionTypeValid(cond))//doesn't have reference, validate ConditionType - continue; - - - if (iSourceTypeOrReferenceId < 0)//it is a reference template - { - uint32 uRefId = abs(iSourceTypeOrReferenceId); - if (m_ConditionReferenceMap.find(uRefId) == m_ConditionReferenceMap.end())//make sure we have a list for our conditions, based on reference id - { - ConditionList mCondList; - m_ConditionReferenceMap[uRefId] = mCondList; - } - m_ConditionReferenceMap[uRefId].push_back(cond);//add to reference storage - count++; - continue; - }//end of reference templates - - cond->mSourceType = ConditionSourceType(iSourceTypeOrReferenceId); - - //if not a reference and SourceType is invalid, skip - if (iConditionTypeOrReference >= 0 && !isSourceTypeValid(cond)) - continue; - - //Grouping is only allowed for some types (loot templates, gossip menus, gossip items) - if (cond->mSourceGroup && !isGroupable(cond->mSourceType)) - { - sLog.outErrorDb("Condition type %u has not allowed grouping %u!", uint32(cond->mSourceType), cond->mSourceGroup); - continue; - }else if (cond->mSourceGroup) - { - bool bIsDone = false; - //handle grouped conditions - switch (cond->mSourceType) - { - case CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Creature.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Disenchant.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Fishing.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Gameobject.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Item.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Mail.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Milling.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Pickpocketing.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Prospecting.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Reference.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Skinning.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE: - bIsDone = addToLootTemplate(cond, LootTemplates_Spell.GetLootForConditionFill(cond->mSourceGroup)); - break; - case CONDITION_SOURCE_TYPE_GOSSIP_MENU: - bIsDone = addToGossipMenus(cond); - break; - case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: - bIsDone = addToGossipMenuItems(cond); - break; - } - if (!bIsDone) - sLog.outErrorDb("Not handled grouped condition, SourceGroup %u", cond->mSourceGroup); - else - ++count; - continue; - } - - //handle not grouped conditions - //make sure we have a storage list for our SourceType - if (m_ConditionMap.find(cond->mSourceType) == m_ConditionMap.end()) - { - ConditionTypeMap mTypeMap; - m_ConditionMap[cond->mSourceType] = mTypeMap;//add new empty list for SourceType - } - - //make sure we have a condition list for our SourceType's entry - if (m_ConditionMap[cond->mSourceType].find(cond->mSourceEntry) == m_ConditionMap[cond->mSourceType].end()) - { - ConditionList mCondList; - m_ConditionMap[cond->mSourceType][cond->mSourceEntry] = mCondList; - } - - //add new Condition to storage based on Type/Entry - m_ConditionMap[cond->mSourceType][cond->mSourceEntry].push_back(cond); - ++count; - } - while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u conditions", count); -} - -bool ConditionMgr::addToLootTemplate(Condition* cond, LootTemplate* loot) -{ - if (!loot) - { - sLog.outErrorDb("ConditionMgr: LootTemplate %u not found", cond->mSourceGroup); - return false; - } - if (loot->addConditionItem(cond)) - return true; - sLog.outErrorDb("ConditionMgr: Item %u not found in LootTemplate %u", cond->mSourceEntry, cond->mSourceGroup); - return false; -} - -bool ConditionMgr::addToGossipMenus(Condition* cond) -{ - GossipMenusMapBoundsNonConst pMenuBounds = objmgr.GetGossipMenusMapBoundsNonConst(cond->mSourceGroup); - - if (pMenuBounds.first != pMenuBounds.second) - { - for (GossipMenusMap::iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) - { - if ((*itr).second.entry == cond->mSourceGroup && (*itr).second.text_id == cond->mSourceEntry) - { - (*itr).second.conditions.push_back(cond); - sLog.outDebug("addToGossipMenus: entry %u textId %u", cond->mSourceGroup, cond->mSourceEntry); - return true; - } - } - } - sLog.outErrorDb("addToGossipMenus: GossipMenu %u not found", cond->mSourceGroup); - return false; -} - -bool ConditionMgr::addToGossipMenuItems(Condition* cond) -{ - GossipMenuItemsMapBoundsNonConst pMenuItemBounds = objmgr.GetGossipMenuItemsMapBoundsNonConst(cond->mSourceGroup); - if (pMenuItemBounds.first != pMenuItemBounds.second) - { - for (GossipMenuItemsMap::iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr) - { - if ((*itr).second.menu_id == cond->mSourceGroup && (*itr).second.id == cond->mSourceEntry) - { - (*itr).second.conditions.push_back(cond); - //sLog.outDebug("addToGossipMenuItems: menuId %u id %u", cond->mSourceGroup, cond->mSourceEntry); - return true; - } - } - } - sLog.outErrorDb("addToGossipMenuItems: GossipMenuIt %u Item %u not found", cond->mSourceGroup, cond->mSourceEntry); - return false; -} - -bool ConditionMgr::isSourceTypeValid(Condition* cond) -{ - if (cond->mSourceType == CONDITION_SOURCE_TYPE_NONE || cond->mSourceType >= MAX_CONDITIONSOURCETYPE) - { - sLog.outErrorDb("Invalid ConditionSourceType %u in `condition` table, ignoring.", uint32(cond->mSourceType)); - return false; - } - switch (cond->mSourceType) - { - case CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE: - { - if (!LootTemplates_Creature.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `creature_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Creature.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE: - { - if (!LootTemplates_Disenchant.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `disenchant_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Disenchant.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE: - { - if (!LootTemplates_Fishing.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `fishing_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Fishing.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE: - { - if (!LootTemplates_Gameobject.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `gameobject_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Gameobject.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE: - { - if (!LootTemplates_Item.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `item_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Item.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE: - { - if (!LootTemplates_Mail.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `mail_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Mail.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE: - { - if (!LootTemplates_Milling.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `milling_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Milling.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE: - { - if (!LootTemplates_Pickpocketing.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `pickpocketing_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Pickpocketing.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE: - { - if (!LootTemplates_Prospecting.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `prospecting_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Prospecting.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE: - { - if (!LootTemplates_Reference.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `reference_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Reference.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE: - { - if (!LootTemplates_Skinning.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `skinning_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Skinning.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE: - { - if (!LootTemplates_Spell.HaveLootFor(cond->mSourceGroup)) - { - sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `spell_loot_template`, ignoring.", cond->mSourceGroup); - return false; - } - LootTemplate* loot = LootTemplates_Spell.GetLootForConditionFill(cond->mSourceGroup); - ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); - if (!pItemProto && !loot->isReference(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET: - { - if (cond->mConditionType != CONDITION_SPELL_SCRIPT_TARGET) - { - sLog.outErrorDb("SourceEntry %u in `condition` table, has ConditionType %u. Only CONDITION_SPELL_SCRIPT_TARGET(18) is valid for CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET(14), ignoring.", cond->mSourceEntry, uint32(cond->mConditionType)); - return false; - } - SpellEntry const* spellProto = sSpellStore.LookupEntry(cond->mSourceEntry); - - if (!spellProto) - { - sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `spell.dbc`, ignoring.", cond->mSourceEntry); - return false; - } - - bool targetfound = false; - for (uint8 i = 0; i < 3; ++i) - { - if (spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_SRC || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_SRC || - spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_DST || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_DST || - spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i] == TARGET_DST_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_DST_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_CONE_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_CONE_ENTRY) - { - targetfound = true; - break; - } - } - if (!targetfound) - { - sLog.outErrorDb("SourceEntry %u in `condition` table does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46)\ - ,TARGET_UNIT_AREA_ENTRY_SRC(7), TARGET_UNIT_AREA_ENTRY_DST(8), TARGET_UNIT_CONE_ENTRY(60), TARGET_GAMEOBJECT_NEARBY_ENTRY(40)",cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE: - { - if (!sCreatureStorage.LookupEntry(cond->mSourceEntry)) - { - sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `creature_template`, ignoring.", cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_SPELL: - { - SpellEntry const* spellProto = sSpellStore.LookupEntry(cond->mSourceEntry); - if (!spellProto) - { - sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `spell.dbc`, ignoring.", cond->mSourceEntry); - return false; - } - break; - } - case CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET: - { - if (cond->mConditionType != CONDITION_ITEM_TARGET) - { - sLog.outErrorDb("SourceEntry %u in `condition` table, has ConditionType %u. Only CONDITION_ITEM_TARGET(24) is valid for CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET(18), ignoring.", cond->mSourceEntry, uint32(cond->mConditionType)); - return false; - } - ItemPrototype const *pItemProto = objmgr.GetItemPrototype(cond->mSourceEntry); - if (!pItemProto) - { - sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `item_tamplate`, ignoring.", cond->mSourceEntry); - return false; - } - bool bIsItemSpellValid = false; - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId)) - { - if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || - pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) - { - ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, pSpellInfo->Id);//script loading is done before item target loading - if (!conditions.empty()) - break; - - for (int j = 0; j < 3; ++j) - { - if (pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY || - pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ENEMY || - pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ANY || - pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ANY) - { - bIsItemSpellValid = true; - break; - } - } - if (bIsItemSpellValid) - break; - } - } - } - - if (!bIsItemSpellValid) - { - sLog.outErrorDb("Conditions:ITEM_REQUIRED_TARGET used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in scriptTargets or doesn't have item spelltrigger.", cond->mSourceEntry); - break; - } - break; - } - case CONDITION_SOURCE_TYPE_GOSSIP_MENU: - case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: - case CONDITION_SOURCE_TYPE_NONE: - break; - } - return true; -} -bool ConditionMgr::isConditionTypeValid(Condition* cond) -{ - if (cond->mConditionType == CONDITION_NONE || cond->mConditionType >= MAX_CONDITION) - { - sLog.outErrorDb("Invalid ConditionType %u at SourceEntry %u in `condition` table, ignoring.", uint32(cond->mConditionType),cond->mSourceEntry); - return false; - } - switch (cond->mConditionType) - { - case CONDITION_AURA: - { - if (!sSpellStore.LookupEntry(cond->mConditionValue1)) - { - sLog.outErrorDb("Aura condition has non existing spell (Id: %d), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2 > 2) - { - sLog.outErrorDb("Aura condition has non existing effect index (%u) (must be 0..2), skipped", cond->mConditionValue2); - return false; - } - break; - } - case CONDITION_ITEM: - { - ItemPrototype const *proto = objmgr.GetItemPrototype(cond->mConditionValue1); - if (!proto) - { - sLog.outErrorDb("Item condition has non existing item (%u), skipped", cond->mConditionValue1); - return false; - } - if (!cond->mConditionValue2) - { - sLog.outErrorDb("Item condition has 0 set for item count in value2 (%u), skipped", cond->mConditionValue2); - return false; - } - break; - } - case CONDITION_ITEM_EQUIPPED: - { - ItemPrototype const *proto = objmgr.GetItemPrototype(cond->mConditionValue1); - if (!proto) - { - sLog.outErrorDb("ItemEquipped condition has non existing item (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("ItemEquipped condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_ZONEID: - { - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->mConditionValue1); - if (!areaEntry) - { - sLog.outErrorDb("Zone condition has non existing area (%u), skipped", cond->mConditionValue1); - return false; - } - if (areaEntry->zone != 0) - { - sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Zone condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_REPUTATION_RANK: - { - FactionEntry const* factionEntry = sFactionStore.LookupEntry(cond->mConditionValue1); - if (!factionEntry) - { - sLog.outErrorDb("Reputation condition has non existing faction (%u), skipped", cond->mConditionValue1); - return false; - } - break; - } - case CONDITION_TEAM: - { - if (cond->mConditionValue1 != ALLIANCE && cond->mConditionValue1 != HORDE) - { - sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Team condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_SKILL: - { - SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(cond->mConditionValue1); - if (!pSkill) - { - sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2 < 1 || cond->mConditionValue2 > sWorld.GetConfigMaxSkillValue()) - { - sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", cond->mConditionValue2); - return false; - } - break; - } - case CONDITION_QUESTREWARDED: - case CONDITION_QUESTTAKEN: - case CONDITION_QUEST_NONE: - { - Quest const *Quest = objmgr.GetQuestTemplate(cond->mConditionValue1); - if (!Quest) - { - sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_AD_COMMISSION_AURA: - { - if (cond->mConditionValue1) - sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", cond->mConditionValue1); - if (cond->mConditionValue2) - sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_NO_AURA: - { - if (!sSpellStore.LookupEntry(cond->mConditionValue1)) - { - sLog.outErrorDb("Aura condition has non existing spell (Id: %d), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2 > 2) - { - sLog.outErrorDb("Aura condition has non existing effect index (%u) in value2 (must be 0..2), skipped", cond->mConditionValue2); - return false; - } - break; - } - case CONDITION_ACTIVE_EVENT: - { - GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); - if (cond->mConditionValue1 >=events.size() || !events[cond->mConditionValue1].isValid()) - { - sLog.outErrorDb("Active event condition has non existing event id (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Active event condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_ACHIEVEMENT: - { - AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(cond->mConditionValue1); - if (!achievement) - { - sLog.outErrorDb("Achivemen condition has non existing achivement id (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Achivemen condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_CLASS: - { - if (cond->mConditionValue1 >= MAX_CLASSES) - { - sLog.outErrorDb("Class condition has non existing class (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Class condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_RACE: - { - if (cond->mConditionValue1 >= MAX_RACES) - { - sLog.outErrorDb("Race condition has non existing race (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Race condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_SPELL_SCRIPT_TARGET: - { - if (cond->mConditionValue1 >= MAX_SPELL_TARGET_TYPE) - { - sLog.outErrorDb("SpellTarget condition has non existing spell target type (%u), skipped", cond->mConditionValue1); - return false; - } - switch(cond->mConditionValue1) - { - case SPELL_TARGET_TYPE_GAMEOBJECT: - { - if (cond->mConditionValue2 && !sGOStorage.LookupEntry(cond->mConditionValue2)) - { - sLog.outErrorDb("SpellTarget condition has non existing gameobject (%u) as target, skipped", cond->mConditionValue2); - return false; - } - break; - } - case SPELL_TARGET_TYPE_CONTROLLED: - case SPELL_TARGET_TYPE_CREATURE: - case SPELL_TARGET_TYPE_DEAD: - { - if (cond->mConditionValue2 && !sCreatureStorage.LookupEntry(cond->mConditionValue2)) - { - sLog.outErrorDb("SpellTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue2); - return false; - } - const CreatureInfo* cInfo = sCreatureStorage.LookupEntry(cond->mConditionValue2); - - if (cond->mSourceEntry == 30427 && !cInfo->SkinLootId) - { - sLog.outErrorDb("SpellTarget condition has creature entry %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!, skipped", cond->mConditionValue2); - return false; - } - break; - } - } - if (cond->mConditionValue3) - sLog.outErrorDb("SpellTarget condition has useless data in value3 (%u)!", cond->mConditionValue3); - break; - } - case CONDITION_CREATURE_TARGET: - { - if (!cond->mConditionValue1 && !sCreatureStorage.LookupEntry(cond->mConditionValue1)) - { - sLog.outErrorDb("CreatureTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("CreatureTarget condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_TARGET_HEALTH_BELOW_PCT: - { - if (cond->mConditionValue1 > 100) - { - sLog.outErrorDb("TargetHealthBelowPct condition has invalid data in value1 (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("TargetHealthBelowPct condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_TARGET_RANGE: - { - if (cond->mConditionValue2 && cond->mConditionValue2 < cond->mConditionValue1)//maxDist can be 0 for infinit max range - { - sLog.outErrorDb("TargetRange condition has max distance closer then min distance, skipped"); - return false; - } - break; - } - case CONDITION_MAPID: - { - MapEntry const * me = sMapStore.LookupEntry(cond->mConditionValue1); - if (!me) - { - sLog.outErrorDb("Map condition has non existing map (%u), skipped", cond->mConditionValue1); - return false; - } - if (cond->mConditionValue2) - sLog.outErrorDb("Map condition has useless data in value2 (%u)!", cond->mConditionValue2); - break; - } - case CONDITION_ITEM_TARGET: - { - if (!cond->mConditionValue1 || cond->mConditionValue1 > MAX_ITEM_REQ_TARGET_TYPE) - { - sLog.outErrorDb("ItemTarget condition has incorrect target type (%u), skipped", cond->mConditionValue1); - return false; - } - if (!cond->mConditionValue2 && !sCreatureStorage.LookupEntry(cond->mConditionValue2)) - { - sLog.outErrorDb("ItemTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue2); - return false; - } - if (cond->mConditionValue3) - sLog.outErrorDb("ItemTarget condition has useless data in value3 (%u)!", cond->mConditionValue3); - break; - } - case CONDITION_AREAID: - case CONDITION_INSTANCE_DATA: - break; - } - return true; -} diff --git a/src/server/game/ConditionMgr.h b/src/server/game/ConditionMgr.h deleted file mode 100644 index bc2ce8d01a2..00000000000 --- a/src/server/game/ConditionMgr.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CONDITIONMGR_H -#define TRINITY_CONDITIONMGR_H - -#include "LootMgr.h" - -class Player; -class Unit; -class LootTemplate; - -enum ConditionType -{ // value1 value2 value3 - CONDITION_NONE = 0, // 0 0 0 always true - CONDITION_AURA = 1, // spell_id effindex +referenceID true if has aura of spell_id with effect effindex - CONDITION_ITEM = 2, // item_id count +referenceID true if has #count of item_ids - CONDITION_ITEM_EQUIPPED = 3, // item_id 0 +referenceID true if has item_id equipped - CONDITION_ZONEID = 4, // zone_id 0 +referenceID true if in zone_id - CONDITION_REPUTATION_RANK = 5, // faction_id min_rank +referenceID true if has min_rank for faction_id - CONDITION_TEAM = 6, // player_team 0, +referenceID 469 - Alliance, 67 - Horde) - CONDITION_SKILL = 7, // skill_id skill_value +referenceID true if has skill_value for skill_id - CONDITION_QUESTREWARDED = 8, // quest_id 0 +referenceID true if quest_id was rewarded before - CONDITION_QUESTTAKEN = 9, // quest_id 0, +referenceID true while quest active - CONDITION_AD_COMMISSION_AURA = 10, // 0 0, +referenceID true while one from AD commission aura active - CONDITION_NO_AURA = 11, // spell_id effindex +referenceID true if does not have aura of spell_id with effect effindex - CONDITION_ACTIVE_EVENT = 12, // event_id 0 +referenceID true if event is active - CONDITION_INSTANCE_DATA = 13, // entry data +referenceID true if data is set in current instance - CONDITION_QUEST_NONE = 14, // quest_id 0 +referenceID true if doesn't have quest saved - CONDITION_CLASS = 15, // class 0 +referenceID true if player's class is equal to class - CONDITION_RACE = 16, // race 0 +referenceID true if player's race is equal to race - CONDITION_ACHIEVEMENT = 17, // achievement_id 0 +referenceID true if achievement is complete - CONDITION_SPELL_SCRIPT_TARGET = 18, // SpellScriptTargetType, TargetEntry, 0 - CONDITION_CREATURE_TARGET = 19, // creature entry 0 +referenceID true if current target is creature with value1 entry - CONDITION_TARGET_HEALTH_BELOW_PCT = 20, // 0-100 0 +referenceID true if target's health is below value1 percent, false if over or no target - CONDITION_TARGET_RANGE = 21, // minDistance maxDist +referenceID true if target is closer then minDist and further then maxDist or if max is 0 then max dist is infinit - CONDITION_MAPID = 22, // map_id 0 +referenceID true if in map_id - CONDITION_AREAID = 23, // area_id 0 +referenceID true if in area_id - CONDITION_ITEM_TARGET = 24 // ItemRequiredTargetType, TargetEntry, 0 -}; - -#define MAX_CONDITION 25 // maximum value in ConditionType enum - -enum ConditionSourceType -{ - CONDITION_SOURCE_TYPE_NONE = 0,//DONE - CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE = 1,//DONE - CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE = 2,//DONE - CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE = 3,//DONE - CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE = 4,//DONE - CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE = 5,//DONE - CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE = 6,//DONE - CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE = 7,//DONE - CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE = 8,//DONE - CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE = 9,//DONE - CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE = 10,//DONE - CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE = 11,//DONE - CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE = 12,//DONE - CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET = 13,//DONE - CONDITION_SOURCE_TYPE_GOSSIP_MENU = 14,//DONE - CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION = 15,//DONE - CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE = 16,//DONE - CONDITION_SOURCE_TYPE_SPELL = 17,//DONE - CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET = 18//DONE -}; - -#define MAX_CONDITIONSOURCETYPE 19 - -struct Condition -{ - ConditionSourceType mSourceType; //SourceTypeOrReferenceId - uint32 mSourceGroup; - uint32 mSourceEntry; - uint32 mElseGroup; - ConditionType mConditionType; //ConditionTypeOrReference - uint32 mConditionValue1; - uint32 mConditionValue2; - uint32 mConditionValue3; - uint32 ErrorTextd; - uint32 mReferenceId; - - Condition() - { - mSourceType = CONDITION_SOURCE_TYPE_NONE; - mSourceGroup = 0; - mSourceEntry = 0; - mElseGroup = 0; - mConditionType = CONDITION_NONE; - mConditionValue1 = 0; - mConditionValue2 = 0; - mConditionValue3 = 0; - mReferenceId = 0; - ErrorTextd = 0; - } - bool Meets(Player * player, Unit* targetOverride = NULL); - bool isLoaded() { return mConditionType > CONDITION_NONE || mReferenceId; } -}; - -typedef std::list ConditionList; -typedef std::map ConditionTypeMap; -typedef std::map ConditionMap;//used for all conditions, except references - -typedef std::map ConditionReferenceMap;//only used for references - -class ConditionMgr -{ - public: - ConditionMgr(); - ~ConditionMgr(); - - void LoadConditions(bool isReload = false); - bool isConditionTypeValid(Condition* cond); - ConditionList GetConditionReferences(uint32 refId); - - bool IsPlayerMeetToConditions(Player* player, ConditionList conditions, Unit* targetOverride = NULL); - ConditionList GetConditionsForNotGroupedEntry(ConditionSourceType sType, uint32 uEntry); - - protected: - ConditionMap m_ConditionMap; - ConditionReferenceMap m_ConditionReferenceMap; - - private: - bool isSourceTypeValid(Condition* cond); - bool addToLootTemplate(Condition* cond, LootTemplate* loot); - bool addToGossipMenus(Condition* cond); - bool addToGossipMenuItems(Condition* cond); - bool IsPlayerMeetToConditionList(Player* player,const ConditionList& conditions, Unit* targetOverride = NULL); - - bool isGroupable(ConditionSourceType sourceType) - { - return (sourceType == CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE || - sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU || - sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION); - } -}; - -#define sConditionMgr Trinity::Singleton::Instance() - -#endif diff --git a/src/server/game/ConditionMgr/ConditionMgr.cpp b/src/server/game/ConditionMgr/ConditionMgr.cpp new file mode 100644 index 00000000000..8e5a7e5677e --- /dev/null +++ b/src/server/game/ConditionMgr/ConditionMgr.cpp @@ -0,0 +1,1145 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "Policies/SingletonImp.h" +#include "Player.h" +#include "SpellAuras.h" +#include "SpellMgr.h" +#include "GameEventMgr.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "InstanceData.h" +#include "ConditionMgr.h" + +INSTANTIATE_SINGLETON_1(ConditionMgr); + +// Checks if player meets the condition +// Can have CONDITION_SOURCE_TYPE_NONE && !mReferenceId if called from a special event (ie: eventAI) +bool Condition::Meets(Player * player, Unit* targetOverride) +{ + if (!player) + { + sLog.outDebug("Condition player not found"); + return false; // player not present, return false + } + uint32 refId = 0; + bool condMeets = false; + bool sendErrorMsg = false; + refId = mConditionValue3;//value 3 can be a 'quick' reference + switch (mConditionType) + { + case CONDITION_NONE: + condMeets = true; // empty condition, always met + break; + case CONDITION_AURA: + condMeets = player->HasAuraEffect(mConditionValue1, mConditionValue2); + break; + case CONDITION_ITEM: + condMeets = player->HasItemCount(mConditionValue1, mConditionValue2); + break; + case CONDITION_ITEM_EQUIPPED: + condMeets = player->HasItemOrGemWithIdEquipped(mConditionValue1,1); + break; + case CONDITION_ZONEID: + condMeets = player->GetZoneId() == mConditionValue1; + break; + case CONDITION_REPUTATION_RANK: + { + FactionEntry const* faction = sFactionStore.LookupEntry(mConditionValue1); + condMeets = faction && uint32(player->GetReputationMgr().GetRank(faction)) >= int32(mConditionValue2); + break; + } + case CONDITION_ACHIEVEMENT: + { + AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(mConditionValue1); + condMeets = player->GetAchievementMgr().HasAchieved(achievement); + break; + } + case CONDITION_TEAM: + condMeets = player->GetTeam() == mConditionValue1; + break; + case CONDITION_CLASS: + condMeets = player->getClass() == mConditionValue1; + break; + case CONDITION_RACE: + condMeets = player->getRace() == mConditionValue1; + break; + case CONDITION_SKILL: + condMeets = player->HasSkill(mConditionValue1) && player->GetBaseSkillValue(mConditionValue1) >= mConditionValue2; + break; + case CONDITION_QUESTREWARDED: + condMeets = player->GetQuestRewardStatus(mConditionValue1); + break; + case CONDITION_QUESTTAKEN: + { + QuestStatus status = player->GetQuestStatus(mConditionValue1); + condMeets = (status == QUEST_STATUS_INCOMPLETE); + break; + } + case CONDITION_QUEST_NONE: + { + QuestStatus status = player->GetQuestStatus(mConditionValue1); + condMeets = (status == QUEST_STATUS_NONE); + break; + } + case CONDITION_AD_COMMISSION_AURA: + { + Unit::AuraApplicationMap const& auras = player->GetAppliedAuras(); + for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + if ((itr->second->GetBase()->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetBase()->GetSpellProto()->SpellVisual[0] == 3580) + { + condMeets = true; + break; + } + condMeets = false; + break; + } + case CONDITION_NO_AURA: + condMeets = !player->HasAuraEffect(mConditionValue1, mConditionValue2); + break; + case CONDITION_ACTIVE_EVENT: + condMeets = gameeventmgr.IsActiveEvent(mConditionValue1); + break; + case CONDITION_INSTANCE_DATA: + { + Map *map = player->GetMap(); + if (map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData()) + condMeets = ((InstanceMap*)map)->GetInstanceData()->GetData(mConditionValue1) == mConditionValue2; + break; + } + case CONDITION_SPELL_SCRIPT_TARGET: + condMeets = true;//spell target condition is handled in spellsystem, here it is always true + refId = 0;//cant have references! use CONDITION_SOURCE_TYPE_SPELL for it + break; + case CONDITION_CREATURE_TARGET: + { + Unit* target = player->GetSelectedUnit(); + if (targetOverride) + target = targetOverride; + if (target) + if (Creature* cTarget = target->ToCreature()) + if (cTarget->GetEntry() == mConditionValue1) + condMeets = true; + break; + } + case CONDITION_TARGET_HEALTH_BELOW_PCT: + { + Unit* target = player->GetSelectedUnit(); + if (targetOverride) + target = targetOverride; + if (target) + if ((target->GetHealth()*100 / target->GetMaxHealth()) <= mConditionValue1) + condMeets = true; + break; + } + case CONDITION_TARGET_RANGE: + { + if (Unit* target = player->GetSelectedUnit()) + if (player->GetDistance(target) >= mConditionValue1 && (!mConditionValue2 || player->GetDistance(target) <= mConditionValue2)) + condMeets = true; + break; + } + case CONDITION_MAPID: + condMeets = player->GetMapId() == mConditionValue1; + break; + case CONDITION_AREAID: + condMeets = player->GetAreaId() == mConditionValue1; + break; + case CONDITION_ITEM_TARGET: + { + condMeets = true;//handled in Item::IsTargetValidForItemUse + refId = 0;//cant have references for now + break; + } + default: + condMeets = false; + refId = 0; + break; + } + switch (mSourceType) + { + case CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET: + case CONDITION_SOURCE_TYPE_SPELL: + sendErrorMsg = true; + break; + } + + bool refMeets = false; + if (condMeets && refId)//only have to check references if 'this' is met + { + ConditionList ref = sConditionMgr.GetConditionReferences(refId); + refMeets = sConditionMgr.IsPlayerMeetToConditions(player, ref); + }else refMeets = true; + + if (sendErrorMsg && ErrorTextd && (!condMeets || !refMeets))//send special error from DB + player->m_ConditionErrorMsgId = ErrorTextd; + + return condMeets && refMeets; +} + +ConditionMgr::ConditionMgr() +{ +} + +ConditionMgr::~ConditionMgr() +{ +} + +ConditionList ConditionMgr::GetConditionReferences(uint32 refId) +{ + ConditionList conditions; + ConditionReferenceMap::const_iterator ref = m_ConditionReferenceMap.find(refId); + if (ref != m_ConditionReferenceMap.end()) + conditions = (*ref).second; + return conditions; +} + +bool ConditionMgr::IsPlayerMeetToConditionList(Player* player,const ConditionList& conditions, Unit* targetOverride) +{ + std::mapElseGroupMap; + for (ConditionList::const_iterator i = conditions.begin(); i != conditions.end(); ++i) + { + sLog.outDebug("ConditionMgr::IsPlayerMeetToConditionList condType: %u val1: %u",(*i)->mConditionType,(*i)->mConditionValue1); + if ((*i)->isLoaded()) + { + std::map::const_iterator itr = ElseGroupMap.find((*i)->mElseGroup); + if (itr == ElseGroupMap.end()) + ElseGroupMap[(*i)->mElseGroup] = true; + else if (!(*itr).second) + continue; + + if ((*i)->mReferenceId)//handle reference + { + ConditionReferenceMap::const_iterator ref = m_ConditionReferenceMap.find((*i)->mReferenceId); + if (ref != m_ConditionReferenceMap.end()) + { + if(!IsPlayerMeetToConditionList(player, (*ref).second, targetOverride)) + ElseGroupMap[(*i)->mElseGroup] = false; + }else{ + sLog.outDebug("IsPlayerMeetToConditionList: Reference template -%u not found", (*i)->mReferenceId);//checked at loading, should never happen + } + + } else//handle normal condition + { + if (!(*i)->Meets(player, targetOverride)) + ElseGroupMap[(*i)->mElseGroup] = false; + } + } + } + for (std::map::const_iterator i = ElseGroupMap.begin(); i != ElseGroupMap.end(); ++i) + if (i->second) + return true; + return false; +} + +bool ConditionMgr::IsPlayerMeetToConditions(Player* player, ConditionList conditions, Unit* targetOverride) +{ + if (conditions.empty()) + return true; + if(player) + player->m_ConditionErrorMsgId = 0; + + sLog.outDebug("ConditionMgr::IsPlayerMeetToConditions"); + bool result = IsPlayerMeetToConditionList(player, conditions, targetOverride); + + if (player && player->m_ConditionErrorMsgId && player->GetSession()) + player->GetSession()->SendNotification(player->m_ConditionErrorMsgId);//m_ConditionErrorMsgId is set only if a condition was not met + + return result; +} + +ConditionList ConditionMgr::GetConditionsForNotGroupedEntry(ConditionSourceType sType, uint32 uEntry) +{ + ConditionList spellCond; + if (sType > CONDITION_SOURCE_TYPE_NONE && sType < MAX_CONDITIONSOURCETYPE) + { + ConditionMap::const_iterator itr = m_ConditionMap.find(sType); + if (itr != m_ConditionMap.end()) + { + ConditionTypeMap::const_iterator i = (*itr).second.find(uEntry); + if (i != (*itr).second.end()) + { + spellCond = (*i).second; + sLog.outDebug("GetConditionsForNotGroupedEntry: found conditions for type %u and entry %u", uint32(sType), uEntry); + } + } + } + return spellCond; +} + +void ConditionMgr::LoadConditions(bool isReload) +{ + m_ConditionMap.clear(); // for reload case + m_ConditionReferenceMap.clear(); // for reload case + //must clear all custom handled cases (groupped types) before reload + if (isReload) + { + sLog.outString("Reseting Loot Conditions..."); + LootTemplates_Creature.ResetConditions(); + LootTemplates_Fishing.ResetConditions(); + LootTemplates_Gameobject.ResetConditions(); + LootTemplates_Item.ResetConditions(); + LootTemplates_Mail.ResetConditions(); + LootTemplates_Milling.ResetConditions(); + LootTemplates_Pickpocketing.ResetConditions(); + LootTemplates_Reference.ResetConditions(); + LootTemplates_Skinning.ResetConditions(); + LootTemplates_Disenchant.ResetConditions(); + LootTemplates_Prospecting.ResetConditions(); + LootTemplates_Spell.ResetConditions(); + + sLog.outString("Re-Loading `gossip_menu` Table for Conditions!"); + objmgr.LoadGossipMenu(); + + sLog.outString("Re-Loading `gossip_menu_option` Table for Conditions!"); + objmgr.LoadGossipMenuItems(); + } + + uint32 count = 0; + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT SourceTypeOrReferenceId, SourceGroup, SourceEntry, ElseGroup, ConditionTypeOrReference, ConditionValue1, ConditionValue2, ConditionValue3, ErrorTextId FROM conditions"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded `conditions`, table is empty!"); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + bar.step(); + + Field *fields = result->Fetch(); + + Condition* cond = new Condition(); + int32 iSourceTypeOrReferenceId = fields[0].GetInt32(); + cond->mSourceGroup = fields[1].GetUInt32(); + cond->mSourceEntry = fields[2].GetUInt32(); + cond->mElseGroup = fields[3].GetUInt32(); + int32 iConditionTypeOrReference = fields[4].GetInt32(); + cond->mConditionValue1 = fields[5].GetUInt32(); + cond->mConditionValue2 = fields[6].GetUInt32(); + cond->mConditionValue3 = fields[7].GetUInt32(); + cond->ErrorTextd = fields[8].GetUInt32(); + + if (iConditionTypeOrReference >= 0) + cond->mConditionType = ConditionType(iConditionTypeOrReference); + + if (iConditionTypeOrReference < 0)//it has a reference + { + if (iConditionTypeOrReference == iSourceTypeOrReferenceId)//self referencing, skipp + { + sLog.outErrorDb("Condition reference %i is referencing self, skipped", iSourceTypeOrReferenceId); + continue; + } + cond->mReferenceId = uint32(abs(iConditionTypeOrReference)); + + const char* rowType = "reference template"; + if (iSourceTypeOrReferenceId >= 0) + rowType = "reference"; + //check for useless data + if (cond->mConditionValue1) + sLog.outErrorDb("Condition %s %i has useless data in value1 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue1); + if (cond->mConditionValue2) + sLog.outErrorDb("Condition %s %i has useless data in value2 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue2); + if (cond->mConditionValue3) + sLog.outErrorDb("Condition %s %i has useless data in value3 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue3); + if (cond->mSourceGroup && iSourceTypeOrReferenceId < 0) + sLog.outErrorDb("Condition %s %i has useless data in SourceGroup (%u)!", rowType, iSourceTypeOrReferenceId, cond->mSourceGroup); + if (cond->mSourceEntry && iSourceTypeOrReferenceId < 0) + sLog.outErrorDb("Condition %s %i has useless data in SourceEntry (%u)!", rowType, iSourceTypeOrReferenceId, cond->mSourceEntry); + }else if (!isConditionTypeValid(cond))//doesn't have reference, validate ConditionType + continue; + + + if (iSourceTypeOrReferenceId < 0)//it is a reference template + { + uint32 uRefId = abs(iSourceTypeOrReferenceId); + if (m_ConditionReferenceMap.find(uRefId) == m_ConditionReferenceMap.end())//make sure we have a list for our conditions, based on reference id + { + ConditionList mCondList; + m_ConditionReferenceMap[uRefId] = mCondList; + } + m_ConditionReferenceMap[uRefId].push_back(cond);//add to reference storage + count++; + continue; + }//end of reference templates + + cond->mSourceType = ConditionSourceType(iSourceTypeOrReferenceId); + + //if not a reference and SourceType is invalid, skip + if (iConditionTypeOrReference >= 0 && !isSourceTypeValid(cond)) + continue; + + //Grouping is only allowed for some types (loot templates, gossip menus, gossip items) + if (cond->mSourceGroup && !isGroupable(cond->mSourceType)) + { + sLog.outErrorDb("Condition type %u has not allowed grouping %u!", uint32(cond->mSourceType), cond->mSourceGroup); + continue; + }else if (cond->mSourceGroup) + { + bool bIsDone = false; + //handle grouped conditions + switch (cond->mSourceType) + { + case CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Creature.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Disenchant.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Fishing.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Gameobject.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Item.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Mail.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Milling.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Pickpocketing.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Prospecting.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Reference.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Skinning.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Spell.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_GOSSIP_MENU: + bIsDone = addToGossipMenus(cond); + break; + case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: + bIsDone = addToGossipMenuItems(cond); + break; + } + if (!bIsDone) + sLog.outErrorDb("Not handled grouped condition, SourceGroup %u", cond->mSourceGroup); + else + ++count; + continue; + } + + //handle not grouped conditions + //make sure we have a storage list for our SourceType + if (m_ConditionMap.find(cond->mSourceType) == m_ConditionMap.end()) + { + ConditionTypeMap mTypeMap; + m_ConditionMap[cond->mSourceType] = mTypeMap;//add new empty list for SourceType + } + + //make sure we have a condition list for our SourceType's entry + if (m_ConditionMap[cond->mSourceType].find(cond->mSourceEntry) == m_ConditionMap[cond->mSourceType].end()) + { + ConditionList mCondList; + m_ConditionMap[cond->mSourceType][cond->mSourceEntry] = mCondList; + } + + //add new Condition to storage based on Type/Entry + m_ConditionMap[cond->mSourceType][cond->mSourceEntry].push_back(cond); + ++count; + } + while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u conditions", count); +} + +bool ConditionMgr::addToLootTemplate(Condition* cond, LootTemplate* loot) +{ + if (!loot) + { + sLog.outErrorDb("ConditionMgr: LootTemplate %u not found", cond->mSourceGroup); + return false; + } + if (loot->addConditionItem(cond)) + return true; + sLog.outErrorDb("ConditionMgr: Item %u not found in LootTemplate %u", cond->mSourceEntry, cond->mSourceGroup); + return false; +} + +bool ConditionMgr::addToGossipMenus(Condition* cond) +{ + GossipMenusMapBoundsNonConst pMenuBounds = objmgr.GetGossipMenusMapBoundsNonConst(cond->mSourceGroup); + + if (pMenuBounds.first != pMenuBounds.second) + { + for (GossipMenusMap::iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) + { + if ((*itr).second.entry == cond->mSourceGroup && (*itr).second.text_id == cond->mSourceEntry) + { + (*itr).second.conditions.push_back(cond); + sLog.outDebug("addToGossipMenus: entry %u textId %u", cond->mSourceGroup, cond->mSourceEntry); + return true; + } + } + } + sLog.outErrorDb("addToGossipMenus: GossipMenu %u not found", cond->mSourceGroup); + return false; +} + +bool ConditionMgr::addToGossipMenuItems(Condition* cond) +{ + GossipMenuItemsMapBoundsNonConst pMenuItemBounds = objmgr.GetGossipMenuItemsMapBoundsNonConst(cond->mSourceGroup); + if (pMenuItemBounds.first != pMenuItemBounds.second) + { + for (GossipMenuItemsMap::iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr) + { + if ((*itr).second.menu_id == cond->mSourceGroup && (*itr).second.id == cond->mSourceEntry) + { + (*itr).second.conditions.push_back(cond); + //sLog.outDebug("addToGossipMenuItems: menuId %u id %u", cond->mSourceGroup, cond->mSourceEntry); + return true; + } + } + } + sLog.outErrorDb("addToGossipMenuItems: GossipMenuIt %u Item %u not found", cond->mSourceGroup, cond->mSourceEntry); + return false; +} + +bool ConditionMgr::isSourceTypeValid(Condition* cond) +{ + if (cond->mSourceType == CONDITION_SOURCE_TYPE_NONE || cond->mSourceType >= MAX_CONDITIONSOURCETYPE) + { + sLog.outErrorDb("Invalid ConditionSourceType %u in `condition` table, ignoring.", uint32(cond->mSourceType)); + return false; + } + switch (cond->mSourceType) + { + case CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE: + { + if (!LootTemplates_Creature.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `creature_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Creature.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE: + { + if (!LootTemplates_Disenchant.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `disenchant_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Disenchant.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE: + { + if (!LootTemplates_Fishing.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `fishing_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Fishing.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE: + { + if (!LootTemplates_Gameobject.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `gameobject_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Gameobject.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE: + { + if (!LootTemplates_Item.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `item_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Item.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE: + { + if (!LootTemplates_Mail.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `mail_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Mail.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE: + { + if (!LootTemplates_Milling.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `milling_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Milling.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE: + { + if (!LootTemplates_Pickpocketing.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `pickpocketing_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Pickpocketing.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE: + { + if (!LootTemplates_Prospecting.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `prospecting_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Prospecting.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE: + { + if (!LootTemplates_Reference.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `reference_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Reference.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE: + { + if (!LootTemplates_Skinning.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `skinning_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Skinning.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE: + { + if (!LootTemplates_Spell.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `spell_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Spell.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET: + { + if (cond->mConditionType != CONDITION_SPELL_SCRIPT_TARGET) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, has ConditionType %u. Only CONDITION_SPELL_SCRIPT_TARGET(18) is valid for CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET(14), ignoring.", cond->mSourceEntry, uint32(cond->mConditionType)); + return false; + } + SpellEntry const* spellProto = sSpellStore.LookupEntry(cond->mSourceEntry); + + if (!spellProto) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `spell.dbc`, ignoring.", cond->mSourceEntry); + return false; + } + + bool targetfound = false; + for (uint8 i = 0; i < 3; ++i) + { + if (spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_SRC || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_SRC || + spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_DST || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_DST || + spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i] == TARGET_DST_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_DST_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_CONE_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_CONE_ENTRY) + { + targetfound = true; + break; + } + } + if (!targetfound) + { + sLog.outErrorDb("SourceEntry %u in `condition` table does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46)\ + ,TARGET_UNIT_AREA_ENTRY_SRC(7), TARGET_UNIT_AREA_ENTRY_DST(8), TARGET_UNIT_CONE_ENTRY(60), TARGET_GAMEOBJECT_NEARBY_ENTRY(40)",cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE: + { + if (!sCreatureStorage.LookupEntry(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `creature_template`, ignoring.", cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SPELL: + { + SpellEntry const* spellProto = sSpellStore.LookupEntry(cond->mSourceEntry); + if (!spellProto) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `spell.dbc`, ignoring.", cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET: + { + if (cond->mConditionType != CONDITION_ITEM_TARGET) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, has ConditionType %u. Only CONDITION_ITEM_TARGET(24) is valid for CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET(18), ignoring.", cond->mSourceEntry, uint32(cond->mConditionType)); + return false; + } + ItemPrototype const *pItemProto = objmgr.GetItemPrototype(cond->mSourceEntry); + if (!pItemProto) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `item_tamplate`, ignoring.", cond->mSourceEntry); + return false; + } + bool bIsItemSpellValid = false; + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId)) + { + if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || + pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + { + ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, pSpellInfo->Id);//script loading is done before item target loading + if (!conditions.empty()) + break; + + for (int j = 0; j < 3; ++j) + { + if (pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY || + pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ENEMY || + pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ANY || + pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ANY) + { + bIsItemSpellValid = true; + break; + } + } + if (bIsItemSpellValid) + break; + } + } + } + + if (!bIsItemSpellValid) + { + sLog.outErrorDb("Conditions:ITEM_REQUIRED_TARGET used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in scriptTargets or doesn't have item spelltrigger.", cond->mSourceEntry); + break; + } + break; + } + case CONDITION_SOURCE_TYPE_GOSSIP_MENU: + case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: + case CONDITION_SOURCE_TYPE_NONE: + break; + } + return true; +} +bool ConditionMgr::isConditionTypeValid(Condition* cond) +{ + if (cond->mConditionType == CONDITION_NONE || cond->mConditionType >= MAX_CONDITION) + { + sLog.outErrorDb("Invalid ConditionType %u at SourceEntry %u in `condition` table, ignoring.", uint32(cond->mConditionType),cond->mSourceEntry); + return false; + } + switch (cond->mConditionType) + { + case CONDITION_AURA: + { + if (!sSpellStore.LookupEntry(cond->mConditionValue1)) + { + sLog.outErrorDb("Aura condition has non existing spell (Id: %d), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2 > 2) + { + sLog.outErrorDb("Aura condition has non existing effect index (%u) (must be 0..2), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_ITEM: + { + ItemPrototype const *proto = objmgr.GetItemPrototype(cond->mConditionValue1); + if (!proto) + { + sLog.outErrorDb("Item condition has non existing item (%u), skipped", cond->mConditionValue1); + return false; + } + if (!cond->mConditionValue2) + { + sLog.outErrorDb("Item condition has 0 set for item count in value2 (%u), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_ITEM_EQUIPPED: + { + ItemPrototype const *proto = objmgr.GetItemPrototype(cond->mConditionValue1); + if (!proto) + { + sLog.outErrorDb("ItemEquipped condition has non existing item (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("ItemEquipped condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_ZONEID: + { + AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->mConditionValue1); + if (!areaEntry) + { + sLog.outErrorDb("Zone condition has non existing area (%u), skipped", cond->mConditionValue1); + return false; + } + if (areaEntry->zone != 0) + { + sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Zone condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_REPUTATION_RANK: + { + FactionEntry const* factionEntry = sFactionStore.LookupEntry(cond->mConditionValue1); + if (!factionEntry) + { + sLog.outErrorDb("Reputation condition has non existing faction (%u), skipped", cond->mConditionValue1); + return false; + } + break; + } + case CONDITION_TEAM: + { + if (cond->mConditionValue1 != ALLIANCE && cond->mConditionValue1 != HORDE) + { + sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Team condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_SKILL: + { + SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(cond->mConditionValue1); + if (!pSkill) + { + sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2 < 1 || cond->mConditionValue2 > sWorld.GetConfigMaxSkillValue()) + { + sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_QUESTREWARDED: + case CONDITION_QUESTTAKEN: + case CONDITION_QUEST_NONE: + { + Quest const *Quest = objmgr.GetQuestTemplate(cond->mConditionValue1); + if (!Quest) + { + sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_AD_COMMISSION_AURA: + { + if (cond->mConditionValue1) + sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", cond->mConditionValue1); + if (cond->mConditionValue2) + sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_NO_AURA: + { + if (!sSpellStore.LookupEntry(cond->mConditionValue1)) + { + sLog.outErrorDb("Aura condition has non existing spell (Id: %d), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2 > 2) + { + sLog.outErrorDb("Aura condition has non existing effect index (%u) in value2 (must be 0..2), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_ACTIVE_EVENT: + { + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + if (cond->mConditionValue1 >=events.size() || !events[cond->mConditionValue1].isValid()) + { + sLog.outErrorDb("Active event condition has non existing event id (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Active event condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_ACHIEVEMENT: + { + AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(cond->mConditionValue1); + if (!achievement) + { + sLog.outErrorDb("Achivemen condition has non existing achivement id (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Achivemen condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_CLASS: + { + if (cond->mConditionValue1 >= MAX_CLASSES) + { + sLog.outErrorDb("Class condition has non existing class (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Class condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_RACE: + { + if (cond->mConditionValue1 >= MAX_RACES) + { + sLog.outErrorDb("Race condition has non existing race (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Race condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_SPELL_SCRIPT_TARGET: + { + if (cond->mConditionValue1 >= MAX_SPELL_TARGET_TYPE) + { + sLog.outErrorDb("SpellTarget condition has non existing spell target type (%u), skipped", cond->mConditionValue1); + return false; + } + switch(cond->mConditionValue1) + { + case SPELL_TARGET_TYPE_GAMEOBJECT: + { + if (cond->mConditionValue2 && !sGOStorage.LookupEntry(cond->mConditionValue2)) + { + sLog.outErrorDb("SpellTarget condition has non existing gameobject (%u) as target, skipped", cond->mConditionValue2); + return false; + } + break; + } + case SPELL_TARGET_TYPE_CONTROLLED: + case SPELL_TARGET_TYPE_CREATURE: + case SPELL_TARGET_TYPE_DEAD: + { + if (cond->mConditionValue2 && !sCreatureStorage.LookupEntry(cond->mConditionValue2)) + { + sLog.outErrorDb("SpellTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue2); + return false; + } + const CreatureInfo* cInfo = sCreatureStorage.LookupEntry(cond->mConditionValue2); + + if (cond->mSourceEntry == 30427 && !cInfo->SkinLootId) + { + sLog.outErrorDb("SpellTarget condition has creature entry %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!, skipped", cond->mConditionValue2); + return false; + } + break; + } + } + if (cond->mConditionValue3) + sLog.outErrorDb("SpellTarget condition has useless data in value3 (%u)!", cond->mConditionValue3); + break; + } + case CONDITION_CREATURE_TARGET: + { + if (!cond->mConditionValue1 && !sCreatureStorage.LookupEntry(cond->mConditionValue1)) + { + sLog.outErrorDb("CreatureTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("CreatureTarget condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_TARGET_HEALTH_BELOW_PCT: + { + if (cond->mConditionValue1 > 100) + { + sLog.outErrorDb("TargetHealthBelowPct condition has invalid data in value1 (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("TargetHealthBelowPct condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_TARGET_RANGE: + { + if (cond->mConditionValue2 && cond->mConditionValue2 < cond->mConditionValue1)//maxDist can be 0 for infinit max range + { + sLog.outErrorDb("TargetRange condition has max distance closer then min distance, skipped"); + return false; + } + break; + } + case CONDITION_MAPID: + { + MapEntry const * me = sMapStore.LookupEntry(cond->mConditionValue1); + if (!me) + { + sLog.outErrorDb("Map condition has non existing map (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Map condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_ITEM_TARGET: + { + if (!cond->mConditionValue1 || cond->mConditionValue1 > MAX_ITEM_REQ_TARGET_TYPE) + { + sLog.outErrorDb("ItemTarget condition has incorrect target type (%u), skipped", cond->mConditionValue1); + return false; + } + if (!cond->mConditionValue2 && !sCreatureStorage.LookupEntry(cond->mConditionValue2)) + { + sLog.outErrorDb("ItemTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue2); + return false; + } + if (cond->mConditionValue3) + sLog.outErrorDb("ItemTarget condition has useless data in value3 (%u)!", cond->mConditionValue3); + break; + } + case CONDITION_AREAID: + case CONDITION_INSTANCE_DATA: + break; + } + return true; +} diff --git a/src/server/game/ConditionMgr/ConditionMgr.h b/src/server/game/ConditionMgr/ConditionMgr.h new file mode 100644 index 00000000000..bc2ce8d01a2 --- /dev/null +++ b/src/server/game/ConditionMgr/ConditionMgr.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005-2009 MaNGOS + * + * Copyright (C) 2008-2010 Trinity + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CONDITIONMGR_H +#define TRINITY_CONDITIONMGR_H + +#include "LootMgr.h" + +class Player; +class Unit; +class LootTemplate; + +enum ConditionType +{ // value1 value2 value3 + CONDITION_NONE = 0, // 0 0 0 always true + CONDITION_AURA = 1, // spell_id effindex +referenceID true if has aura of spell_id with effect effindex + CONDITION_ITEM = 2, // item_id count +referenceID true if has #count of item_ids + CONDITION_ITEM_EQUIPPED = 3, // item_id 0 +referenceID true if has item_id equipped + CONDITION_ZONEID = 4, // zone_id 0 +referenceID true if in zone_id + CONDITION_REPUTATION_RANK = 5, // faction_id min_rank +referenceID true if has min_rank for faction_id + CONDITION_TEAM = 6, // player_team 0, +referenceID 469 - Alliance, 67 - Horde) + CONDITION_SKILL = 7, // skill_id skill_value +referenceID true if has skill_value for skill_id + CONDITION_QUESTREWARDED = 8, // quest_id 0 +referenceID true if quest_id was rewarded before + CONDITION_QUESTTAKEN = 9, // quest_id 0, +referenceID true while quest active + CONDITION_AD_COMMISSION_AURA = 10, // 0 0, +referenceID true while one from AD commission aura active + CONDITION_NO_AURA = 11, // spell_id effindex +referenceID true if does not have aura of spell_id with effect effindex + CONDITION_ACTIVE_EVENT = 12, // event_id 0 +referenceID true if event is active + CONDITION_INSTANCE_DATA = 13, // entry data +referenceID true if data is set in current instance + CONDITION_QUEST_NONE = 14, // quest_id 0 +referenceID true if doesn't have quest saved + CONDITION_CLASS = 15, // class 0 +referenceID true if player's class is equal to class + CONDITION_RACE = 16, // race 0 +referenceID true if player's race is equal to race + CONDITION_ACHIEVEMENT = 17, // achievement_id 0 +referenceID true if achievement is complete + CONDITION_SPELL_SCRIPT_TARGET = 18, // SpellScriptTargetType, TargetEntry, 0 + CONDITION_CREATURE_TARGET = 19, // creature entry 0 +referenceID true if current target is creature with value1 entry + CONDITION_TARGET_HEALTH_BELOW_PCT = 20, // 0-100 0 +referenceID true if target's health is below value1 percent, false if over or no target + CONDITION_TARGET_RANGE = 21, // minDistance maxDist +referenceID true if target is closer then minDist and further then maxDist or if max is 0 then max dist is infinit + CONDITION_MAPID = 22, // map_id 0 +referenceID true if in map_id + CONDITION_AREAID = 23, // area_id 0 +referenceID true if in area_id + CONDITION_ITEM_TARGET = 24 // ItemRequiredTargetType, TargetEntry, 0 +}; + +#define MAX_CONDITION 25 // maximum value in ConditionType enum + +enum ConditionSourceType +{ + CONDITION_SOURCE_TYPE_NONE = 0,//DONE + CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE = 1,//DONE + CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE = 2,//DONE + CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE = 3,//DONE + CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE = 4,//DONE + CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE = 5,//DONE + CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE = 6,//DONE + CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE = 7,//DONE + CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE = 8,//DONE + CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE = 9,//DONE + CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE = 10,//DONE + CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE = 11,//DONE + CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE = 12,//DONE + CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET = 13,//DONE + CONDITION_SOURCE_TYPE_GOSSIP_MENU = 14,//DONE + CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION = 15,//DONE + CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE = 16,//DONE + CONDITION_SOURCE_TYPE_SPELL = 17,//DONE + CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET = 18//DONE +}; + +#define MAX_CONDITIONSOURCETYPE 19 + +struct Condition +{ + ConditionSourceType mSourceType; //SourceTypeOrReferenceId + uint32 mSourceGroup; + uint32 mSourceEntry; + uint32 mElseGroup; + ConditionType mConditionType; //ConditionTypeOrReference + uint32 mConditionValue1; + uint32 mConditionValue2; + uint32 mConditionValue3; + uint32 ErrorTextd; + uint32 mReferenceId; + + Condition() + { + mSourceType = CONDITION_SOURCE_TYPE_NONE; + mSourceGroup = 0; + mSourceEntry = 0; + mElseGroup = 0; + mConditionType = CONDITION_NONE; + mConditionValue1 = 0; + mConditionValue2 = 0; + mConditionValue3 = 0; + mReferenceId = 0; + ErrorTextd = 0; + } + bool Meets(Player * player, Unit* targetOverride = NULL); + bool isLoaded() { return mConditionType > CONDITION_NONE || mReferenceId; } +}; + +typedef std::list ConditionList; +typedef std::map ConditionTypeMap; +typedef std::map ConditionMap;//used for all conditions, except references + +typedef std::map ConditionReferenceMap;//only used for references + +class ConditionMgr +{ + public: + ConditionMgr(); + ~ConditionMgr(); + + void LoadConditions(bool isReload = false); + bool isConditionTypeValid(Condition* cond); + ConditionList GetConditionReferences(uint32 refId); + + bool IsPlayerMeetToConditions(Player* player, ConditionList conditions, Unit* targetOverride = NULL); + ConditionList GetConditionsForNotGroupedEntry(ConditionSourceType sType, uint32 uEntry); + + protected: + ConditionMap m_ConditionMap; + ConditionReferenceMap m_ConditionReferenceMap; + + private: + bool isSourceTypeValid(Condition* cond); + bool addToLootTemplate(Condition* cond, LootTemplate* loot); + bool addToGossipMenus(Condition* cond); + bool addToGossipMenuItems(Condition* cond); + bool IsPlayerMeetToConditionList(Player* player,const ConditionList& conditions, Unit* targetOverride = NULL); + + bool isGroupable(ConditionSourceType sourceType) + { + return (sourceType == CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU || + sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION); + } +}; + +#define sConditionMgr Trinity::Singleton::Instance() + +#endif diff --git a/src/server/game/ConfusedMovementGenerator.cpp b/src/server/game/ConfusedMovementGenerator.cpp deleted file mode 100644 index 43c6052d2d3..00000000000 --- a/src/server/game/ConfusedMovementGenerator.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Creature.h" -#include "MapManager.h" -#include "Opcodes.h" -#include "ConfusedMovementGenerator.h" -#include "DestinationHolderImp.h" -#include "VMapFactory.h" - -#ifdef MAP_BASED_RAND_GEN -#define rand_norm() unit.rand_norm() -#define urand(a,b) unit.urand(a,b) -#endif - -template -void -ConfusedMovementGenerator::Initialize(T &unit) -{ - const float wander_distance = 11; - float x,y,z; - x = unit.GetPositionX(); - y = unit.GetPositionY(); - z = unit.GetPositionZ(); - - Map const* map = unit.GetBaseMap(); - - i_nextMove = 1; - - bool is_water_ok, is_land_ok; - _InitSpecific(unit, is_water_ok, is_land_ok); - - VMAP::IVMapManager *vMaps = VMAP::VMapFactory::createOrGetVMapManager(); - - for (uint8 idx = 0; idx <= MAX_CONF_WAYPOINTS; ++idx) - { - const bool isInLoS = vMaps->isInLineOfSight(unit.GetMapId(), x, y, z + 2.0f, i_waypoints[idx][0], i_waypoints[idx][1], z + 2.0f); - if (isInLoS) - { - const float wanderX = wander_distance*rand_norm() - wander_distance/2; - const float wanderY = wander_distance*rand_norm() - wander_distance/2; - i_waypoints[idx][0] = x + wanderX; - i_waypoints[idx][1] = y + wanderY; - } - else - { - i_waypoints[idx][0] = x; - i_waypoints[idx][1] = y; - } - - // prevent invalid coordinates generation - Trinity::NormalizeMapCoord(i_waypoints[idx][0]); - Trinity::NormalizeMapCoord(i_waypoints[idx][1]); - - bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z); - // if generated wrong path just ignore - if ((is_water && !is_water_ok) || (!is_water && !is_land_ok)) - { - i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x; - i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y; - } - - unit.UpdateGroundPositionZ(i_waypoints[idx][0],i_waypoints[idx][1],z); - i_waypoints[idx][2] = z; - } - - unit.SetUInt64Value(UNIT_FIELD_TARGET, 0); - unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); - unit.CastStop(); - unit.StopMoving(); - unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - unit.addUnitState(UNIT_STAT_CONFUSED); -} - -template<> -void -ConfusedMovementGenerator::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok) -{ - is_water_ok = creature.canSwim(); - is_land_ok = creature.canWalk(); -} - -template<> -void -ConfusedMovementGenerator::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok) -{ - is_water_ok = true; - is_land_ok = true; -} - -template -void -ConfusedMovementGenerator::Reset(T &unit) -{ - i_nextMove = 1; - i_nextMoveTime.Reset(0); - i_destinationHolder.ResetUpdate(); - unit.StopMoving(); -} - -template -bool -ConfusedMovementGenerator::Update(T &unit, const uint32 &diff) -{ - if (!&unit) - return true; - - if (unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED)) - return true; - - if (i_nextMoveTime.Passed()) - { - // currently moving, update location - Traveller traveller(unit); - if (i_destinationHolder.UpdateTraveller(traveller, diff)) - { - if (i_destinationHolder.HasArrived()) - { - // arrived, stop and wait a bit - unit.clearUnitState(UNIT_STAT_MOVE); - - i_nextMove = urand(1,MAX_CONF_WAYPOINTS); - i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher - } - } - } - else - { - // waiting for next move - i_nextMoveTime.Update(diff); - if (i_nextMoveTime.Passed()) - { - // start moving - assert(i_nextMove <= MAX_CONF_WAYPOINTS); - const float x = i_waypoints[i_nextMove][0]; - const float y = i_waypoints[i_nextMove][1]; - const float z = i_waypoints[i_nextMove][2]; - Traveller traveller(unit); - i_destinationHolder.SetDestination(traveller, x, y, z); - } - } - return true; -} - -template -void -ConfusedMovementGenerator::Finalize(T &unit) -{ - unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); - unit.clearUnitState(UNIT_STAT_CONFUSED); - if (unit.GetTypeId() == TYPEID_UNIT && unit.getVictim()) - unit.SetUInt64Value(UNIT_FIELD_TARGET, unit.getVictim()->GetGUID()); -} - -template void ConfusedMovementGenerator::Initialize(Player &player); -template void ConfusedMovementGenerator::Initialize(Creature &creature); -template void ConfusedMovementGenerator::Finalize(Player &player); -template void ConfusedMovementGenerator::Finalize(Creature &creature); -template void ConfusedMovementGenerator::Reset(Player &player); -template void ConfusedMovementGenerator::Reset(Creature &creature); -template bool ConfusedMovementGenerator::Update(Player &player, const uint32 &diff); -template bool ConfusedMovementGenerator::Update(Creature &creature, const uint32 &diff); - - diff --git a/src/server/game/ConfusedMovementGenerator.h b/src/server/game/ConfusedMovementGenerator.h deleted file mode 100644 index e1a71151d37..00000000000 --- a/src/server/game/ConfusedMovementGenerator.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CONFUSEDGENERATOR_H -#define TRINITY_CONFUSEDGENERATOR_H - -#include "MovementGenerator.h" -#include "DestinationHolder.h" -#include "Traveller.h" - -#define MAX_CONF_WAYPOINTS 24 - -template -class ConfusedMovementGenerator -: public MovementGeneratorMedium< T, ConfusedMovementGenerator > -{ - public: - explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {} - - void Initialize(T &); - void Finalize(T &); - void Reset(T &); - bool Update(T &, const uint32 &); - - bool GetDestination(float &x, float &y, float &z) const - { - if (i_destinationHolder.HasArrived()) return false; - i_destinationHolder.GetDestination(x,y,z); - return true; - } - - MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; } - private: - void _InitSpecific(T &, bool &, bool &); - TimeTracker i_nextMoveTime; - float i_waypoints[MAX_CONF_WAYPOINTS+1][3]; - DestinationHolder< Traveller > i_destinationHolder; - uint32 i_nextMove; -}; -#endif - - diff --git a/src/server/game/Corpse.cpp b/src/server/game/Corpse.cpp deleted file mode 100644 index 510ea13e78b..00000000000 --- a/src/server/game/Corpse.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Corpse.h" -#include "Player.h" -#include "UpdateMask.h" -#include "ObjectAccessor.h" -#include "Database/DatabaseEnv.h" -#include "Opcodes.h" -#include "GossipDef.h" -#include "World.h" - -Corpse::Corpse(CorpseType type) : WorldObject() -, m_type(type) -{ - m_objectType |= TYPEMASK_CORPSE; - m_objectTypeId = TYPEID_CORPSE; - - m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_POSITION); - - m_valuesCount = CORPSE_END; - - m_time = time(NULL); - - lootForBody = false; - - if (type != CORPSE_BONES) - m_isWorldObject = true; -} - -Corpse::~Corpse() -{ -} - -void Corpse::AddToWorld() -{ - ///- Register the corpse for guid lookup - if (!IsInWorld()) - ObjectAccessor::Instance().AddObject(this); - - Object::AddToWorld(); -} - -void Corpse::RemoveFromWorld() -{ - ///- Remove the corpse from the accessor - if (IsInWorld()) - ObjectAccessor::Instance().RemoveObject(this); - - Object::RemoveFromWorld(); -} - -bool Corpse::Create(uint32 guidlow, Map *map) -{ - SetMap(map); - Object::_Create(guidlow, 0, HIGHGUID_CORPSE); - return true; -} - -bool Corpse::Create(uint32 guidlow, Player *owner) -{ - ASSERT(owner); - - Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation()); - - if (!IsPositionValid()) - { - sLog.outError("Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %f Y: %f)", - guidlow, owner->GetName(), owner->GetPositionX(), owner->GetPositionY()); - return false; - } - - //we need to assign owner's map for corpse - //in other way we will get a crash in Corpse::SaveToDB() - SetMap(owner->GetMap()); - - WorldObject::_Create(guidlow, HIGHGUID_CORPSE, owner->GetPhaseMask()); - - SetFloatValue(OBJECT_FIELD_SCALE_X, 1); - SetUInt64Value(CORPSE_FIELD_OWNER, owner->GetGUID()); - - m_grid = Trinity::ComputeGridPair(GetPositionX(), GetPositionY()); - - return true; -} - -void Corpse::SaveToDB() -{ - // prevent DB data inconsistence problems and duplicates - CharacterDatabase.BeginTransaction(); - DeleteFromDB(); - - std::ostringstream ss; - ss << "INSERT INTO corpse (guid,player,position_x,position_y,position_z,orientation,zone,map,data,time,corpse_type,instance,phaseMask) VALUES (" - << GetGUIDLow() << ", " - << GUID_LOPART(GetOwnerGUID()) << ", " - << GetPositionX() << ", " - << GetPositionY() << ", " - << GetPositionZ() << ", " - << GetOrientation() << ", " - << GetZoneId() << ", " - << GetMapId() << ", '"; - for (uint16 i = 0; i < m_valuesCount; ++i) - ss << GetUInt32Value(i) << " "; - ss << "'," - << uint64(m_time) <<", " - << uint32(GetType()) << ", " - << int(GetInstanceId()) << ", " - << uint16(GetPhaseMask()) << ")"; // prevent out of range error - CharacterDatabase.Execute(ss.str().c_str()); - CharacterDatabase.CommitTransaction(); -} - -void Corpse::DeleteBonesFromWorld() -{ - assert(GetType() == CORPSE_BONES); - Corpse* corpse = ObjectAccessor::GetCorpse(*this, GetGUID()); - - if (!corpse) - { - sLog.outError("Bones %u not found in world.", GetGUIDLow()); - return; - } - - AddObjectToRemoveList(); -} - -void Corpse::DeleteFromDB() -{ - if (GetType() == CORPSE_BONES) - // only specific bones - CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%d'", GetGUIDLow()); - else - // all corpses (not bones) - CharacterDatabase.PExecute("DELETE FROM corpse WHERE player = '%d' AND corpse_type <> '0'", GUID_LOPART(GetOwnerGUID())); -} - -/* -bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId) -{ - bool external = (result != NULL); - if (!external) - // 0 1 2 3 4 5 6 7 8 9 - result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance,phaseMask FROM corpse WHERE guid = '%u'",guid); - - if (!result) - { - sLog.outError("Corpse (GUID: %u) not found in table `corpse`, can't load. ",guid); - return false; - } - - Field *fields = result->Fetch(); - - if (!LoadFromDB(guid, fields)) - { - if (!external) - delete result; - - return false; - } - - if (!external) - delete result; - - return true; -}*/ - -bool Corpse::LoadFromDB(uint32 guid, Field *fields) -{ - float positionX = fields[0].GetFloat(); - float positionY = fields[1].GetFloat(); - float positionZ = fields[2].GetFloat(); - float ort = fields[3].GetFloat(); - uint32 mapid = fields[4].GetUInt32(); - - Object::_Create(guid, 0, HIGHGUID_CORPSE); - - if (!LoadValues(fields[5].GetString())) - { - sLog.outError("Corpse #%d have broken data in `data` field. Can't be loaded.",guid); - return false; - } - - m_time = time_t(fields[6].GetUInt64()); - m_type = CorpseType(fields[7].GetUInt32()); - - if (m_type >= MAX_CORPSE_TYPE) - { - sLog.outError("Corpse (guidlow %d, owner %d) have wrong corpse type, not load.",GetGUIDLow(),GUID_LOPART(GetOwnerGUID())); - return false; - } - - if (m_type != CORPSE_BONES) - m_isWorldObject = true; - - uint32 instanceid = fields[8].GetUInt32(); - - uint32 phaseMask = fields[9].GetUInt32(); - - // overwrite possible wrong/corrupted guid - SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_CORPSE)); - - // place - SetLocationInstanceId(instanceid); - SetLocationMapId(mapid); - SetPhaseMask(phaseMask, false); - Relocate(positionX, positionY, positionZ, ort); - - if (!IsPositionValid()) - { - sLog.outError("Corpse (guidlow %d, owner %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)", - GetGUIDLow(), GUID_LOPART(GetOwnerGUID()), GetPositionX(), GetPositionY()); - return false; - } - - m_grid = Trinity::ComputeGridPair(GetPositionX(), GetPositionY()); - - return true; -} - -bool Corpse::isVisibleForInState(Player const* u, bool inVisibleList) const -{ - return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u->m_seer, World::GetMaxVisibleDistanceForObject() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); -} - diff --git a/src/server/game/Corpse.h b/src/server/game/Corpse.h deleted file mode 100644 index bab95e99d14..00000000000 --- a/src/server/game/Corpse.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITYCORE_CORPSE_H -#define TRINITYCORE_CORPSE_H - -#include "Object.h" -#include "Database/DatabaseEnv.h" -#include "GridDefines.h" -#include "LootMgr.h" - -enum CorpseType -{ - CORPSE_BONES = 0, - CORPSE_RESURRECTABLE_PVE = 1, - CORPSE_RESURRECTABLE_PVP = 2 -}; -#define MAX_CORPSE_TYPE 3 - -// Value equal client resurrection dialog show radius. -#define CORPSE_RECLAIM_RADIUS 39 - -enum CorpseFlags -{ - CORPSE_FLAG_NONE = 0x00, - CORPSE_FLAG_BONES = 0x01, - CORPSE_FLAG_UNK1 = 0x02, - CORPSE_FLAG_UNK2 = 0x04, - CORPSE_FLAG_HIDE_HELM = 0x08, - CORPSE_FLAG_HIDE_CLOAK = 0x10, - CORPSE_FLAG_LOOTABLE = 0x20 -}; - -class Corpse : public WorldObject, public GridObject -{ - public: - explicit Corpse(CorpseType type = CORPSE_BONES); - ~Corpse(); - - void AddToWorld(); - void RemoveFromWorld(); - - bool Create(uint32 guidlow, Map *map); - bool Create(uint32 guidlow, Player *owner); - - void SaveToDB(); - //bool LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId); - bool LoadFromDB(uint32 guid, Field *fields); - - void DeleteBonesFromWorld(); - void DeleteFromDB(); - - uint64 const& GetOwnerGUID() const { return GetUInt64Value(CORPSE_FIELD_OWNER); } - - time_t const& GetGhostTime() const { return m_time; } - void ResetGhostTime() { m_time = time(NULL); } - CorpseType GetType() const { return m_type; } - - GridPair const& GetGrid() const { return m_grid; } - void SetGrid(GridPair const& grid) { m_grid = grid; } - - bool isVisibleForInState(Player const* u, bool inVisibleList) const; - - Loot loot; // remove insignia ONLY at BG - Player* lootRecipient; - bool lootForBody; - - void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } - void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } - void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); } - void Whisper(int32 textId,uint64 receiver) { MonsterWhisper(textId,receiver); } - void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); } - - private: - CorpseType m_type; - time_t m_time; - GridPair m_grid; // gride for corpse position for fast search -}; -#endif - diff --git a/src/server/game/Creature.cpp b/src/server/game/Creature.cpp deleted file mode 100644 index fbfae17a48b..00000000000 --- a/src/server/game/Creature.cpp +++ /dev/null @@ -1,2423 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "WorldPacket.h" -#include "World.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Creature.h" -#include "QuestDef.h" -#include "GossipDef.h" -#include "Player.h" -#include "PoolHandler.h" -#include "Opcodes.h" -#include "Log.h" -#include "LootMgr.h" -#include "MapManager.h" -#include "CreatureAI.h" -#include "CreatureAISelector.h" -#include "Formulas.h" -#include "WaypointMovementGenerator.h" -#include "InstanceData.h" -#include "BattleGroundMgr.h" -#include "Util.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "CellImpl.h" -#include "OutdoorPvPMgr.h" -#include "GameEventMgr.h" -#include "CreatureGroups.h" -#include "Vehicle.h" -#include "SpellAuraEffects.h" -// apply implementation of the singletons -#include "Policies/SingletonImp.h" - -TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const -{ - TrainerSpellMap::const_iterator itr = spellList.find(spell_id); - if (itr != spellList.end()) - return &itr->second; - - return NULL; -} - -bool VendorItemData::RemoveItem(uint32 item_id) -{ - bool found = false; - for (VendorItemList::iterator i = m_items.begin(); i != m_items.end();) - { - if ((*i)->item == item_id) - { - i = m_items.erase(i++); - found = true; - } - else - ++i; - } - return found; -} - -VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extendedCost) const -{ - 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; -} - -uint32 CreatureInfo::GetRandomValidModelId() const -{ - uint8 c = 0; - uint32 modelIDs[4]; - - if (Modelid1) modelIDs[c++] = Modelid1; - if (Modelid2) modelIDs[c++] = Modelid2; - if (Modelid3) modelIDs[c++] = Modelid3; - if (Modelid4) modelIDs[c++] = Modelid4; - - return ((c>0) ? modelIDs[urand(0,c-1)] : 0); -} - -uint32 CreatureInfo::GetFirstValidModelId() const -{ - if (Modelid1) return Modelid1; - if (Modelid2) return Modelid2; - if (Modelid3) return Modelid3; - if (Modelid4) return Modelid4; - return 0; -} - -bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - if (Unit* victim = Unit::GetUnit(m_owner, m_victim)) - { - while (!m_assistants.empty()) - { - Creature* assistant = Unit::GetCreature(m_owner, *m_assistants.begin()); - m_assistants.pop_front(); - - if (assistant && assistant->CanAssistTo(&m_owner, victim)) - { - assistant->SetNoCallAssistance(true); - assistant->CombatStart(victim); - if (assistant->IsAIEnabled) - assistant->AI()->AttackStart(victim); - } - } - } - return true; -} - -CreatureBaseStats const* CreatureBaseStats::GetBaseStats(uint8 level, uint8 unitClass) -{ - return objmgr.GetCreatureBaseStats(level, unitClass); -} - -bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - m_owner.ForcedDespawn(); - return true; -} - -Creature::Creature() : -Unit(), -lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupGUID(0), -m_lootMoney(0), m_lootRecipient(0), -m_deathTimer(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), -m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_AlreadyCallAssistance(false), -m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), -m_creatureInfo(NULL), m_reactState(REACT_AGGRESSIVE), m_formation(NULL) -, m_AlreadySearchedAssistance(false) -, m_creatureData(NULL), m_PlayerDamageReq(0) -{ - m_regenTimer = CREATURE_REGEN_INTERVAL; - m_valuesCount = UNIT_END; - - for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) - m_spells[i] = 0; - - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - m_GlobalCooldown = 0; - DisableReputationGain = false; - //m_unit_movement_flags = MONSTER_MOVE_WALK; - - m_SightDistance = sWorld.getConfig(CONFIG_SIGHT_MONSTER); - m_CombatDistance = 0;//MELEE_RANGE; - - ResetLootMode(); // restore default loot mode -} - -Creature::~Creature() -{ - m_vendorItemCounts.clear(); - - if (i_AI) - { - delete i_AI; - i_AI = NULL; - } - - //if (m_uint32Values) - // sLog.outError("Deconstruct Creature Entry = %u", GetEntry()); -} - -void Creature::AddToWorld() -{ - ///- Register the creature for guid lookup - if (!IsInWorld()) - { - if (m_zoneScript) - m_zoneScript->OnCreatureCreate(this, true); - ObjectAccessor::Instance().AddObject(this); - Unit::AddToWorld(); - SearchFormation(); - AIM_Initialize(); - if (IsVehicle()) - GetVehicleKit()->Install(); - } -} - -void Creature::RemoveFromWorld() -{ - if (IsInWorld()) - { - if (m_zoneScript) - m_zoneScript->OnCreatureCreate(this, false); - if (m_formation) - formation_mgr.RemoveCreatureFromGroup(m_formation, this); - Unit::RemoveFromWorld(); - ObjectAccessor::Instance().RemoveObject(this); - } -} - -void Creature::DisappearAndDie() -{ - DestroyForNearbyPlayers(); - //SetVisibility(VISIBILITY_OFF); - //ObjectAccessor::UpdateObjectVisibility(this); - if (isAlive()) - setDeathState(JUST_DIED); - RemoveCorpse(); -} - -void Creature::SearchFormation() -{ - if (isSummon()) - return; - - uint32 lowguid = GetDBTableGUIDLow(); - if (!lowguid) - return; - - CreatureGroupInfoType::iterator frmdata = CreatureGroupMap.find(lowguid); - if (frmdata != CreatureGroupMap.end()) - formation_mgr.AddCreatureToGroup(frmdata->second->leaderGUID, this); -} - -void Creature::RemoveCorpse() -{ - if ((getDeathState() != CORPSE && !m_isDeadByDefault) || (getDeathState() != ALIVE && m_isDeadByDefault)) - return; - - m_deathTimer = 0; - setDeathState(DEAD); - UpdateObjectVisibility(); - loot.clear(); - uint32 respawnDelay = m_respawnDelay; - if (IsAIEnabled) - AI()->CorpseRemoved(respawnDelay); - - m_respawnTime = time(NULL) + m_respawnDelay; - - float x,y,z,o; - GetRespawnCoord(x, y, z, &o); - SetHomePosition(x,y,z,o); - GetMap()->CreatureRelocation(this,x,y,z,o); -} - -/** - * change the entry of creature until respawn - */ -bool Creature::InitEntry(uint32 Entry, uint32 /*team*/, const CreatureData *data) -{ - CreatureInfo const *normalInfo = objmgr.GetCreatureTemplate(Entry); - if (!normalInfo) - { - sLog.outErrorDb("Creature::UpdateEntry creature entry %u does not exist.", Entry); - return false; - } - - // get difficulty 1 mode entry - uint32 actualEntry = Entry; - CreatureInfo const *cinfo = normalInfo; - // TODO correctly implement spawnmodes for non-bg maps - for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1; ++diff) - { - if (normalInfo->DifficultyEntry[diff]) - { - // we already have valid Map pointer for current creature! - if (GetMap()->GetSpawnMode() > diff) - { - cinfo = objmgr.GetCreatureTemplate(normalInfo->DifficultyEntry[diff]); - if (!cinfo) - { - // maybe check such things already at startup - sLog.outErrorDb("Creature::UpdateEntry creature difficulty %u entry %u does not exist.", diff + 1, actualEntry); - return false; - } - } - } - } - - SetEntry(Entry); // normal entry always - 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); - - // known valid are: CLASS_WARRIOR,CLASS_PALADIN,CLASS_ROGUE,CLASS_MAGE - SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class)); - - // Cancel load if no model defined - if (!(cinfo->GetFirstValidModelId())) - { - sLog.outErrorDb("Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ",Entry); - return false; - } - - uint32 display_id = objmgr.ChooseDisplayId(0, GetCreatureInfo(), data); - CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id); - if (!minfo) // Cancel load if no model defined - { - sLog.outErrorDb("Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ",Entry); - return false; - } - - display_id = minfo->modelid; // it can be different (for another gender) - - SetDisplayId(display_id); - SetNativeDisplayId(display_id); - SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender); - - // Load creature equipment - if (!data || data->equipmentId == 0) - { // use default from the template - LoadEquipment(cinfo->equipmentId); - } - else if (data && data->equipmentId != -1) - { // override, -1 means no equipment - LoadEquipment(data->equipmentId); - } - - SetName(normalInfo->Name); // at normal entry always - - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius); - SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach); - - 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 - - SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->scale); - - // checked at loading - m_defaultMovementType = MovementGeneratorType(cinfo->MovementType); - if (!m_respawnradius && m_defaultMovementType == RANDOM_MOTION_TYPE) - m_defaultMovementType = IDLE_MOTION_TYPE; - - for (uint8 i=0; i < CREATURE_MAX_SPELLS; ++i) - m_spells[i] = GetCreatureInfo()->spells[i]; - - return true; -} - -bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data) -{ - if (!InitEntry(Entry,team,data)) - return false; - - CreatureInfo const* cInfo = GetCreatureInfo(); - - m_regenHealth = cInfo->RegenHealth; - - // creatures always have melee weapon ready if any - SetSheath(SHEATH_STATE_MELEE); - - SelectLevel(GetCreatureInfo()); - if (team == HORDE) - setFaction(cInfo->faction_H); - else - setFaction(cInfo->faction_A); - - if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT) - SetUInt32Value(UNIT_NPC_FLAGS,cInfo->npcflag | gameeventmgr.GetNPCFlag(this)); - else - SetUInt32Value(UNIT_NPC_FLAGS,cInfo->npcflag); - - SetAttackTime(BASE_ATTACK, cInfo->baseattacktime); - SetAttackTime(OFF_ATTACK, cInfo->baseattacktime); - SetAttackTime(RANGED_ATTACK,cInfo->rangeattacktime); - - SetUInt32Value(UNIT_FIELD_FLAGS,cInfo->unit_flags); - SetUInt32Value(UNIT_DYNAMIC_FLAGS,cInfo->dynamicflags); - - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); - - SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool)); - CreatureBaseStats const* stats = objmgr.GetCreatureBaseStats(getLevel(), cInfo->unit_class); - float armor = stats->GenerateArmor(cInfo); // TODO: Why is this treated as uint32 when it's a float? - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, armor); - SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(cInfo->resistance1)); - SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(cInfo->resistance2)); - SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(cInfo->resistance3)); - SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(cInfo->resistance4)); - SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(cInfo->resistance5)); - SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance6)); - - SetCanModifyStats(true); - UpdateAllStats(); - - // checked and error show at loading templates - if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A)) - { - if (factionTemplate->factionFlags & FACTION_TEMPLATE_FLAG_PVP) - SetPvP(true); - else - SetPvP(false); - } - - // trigger creature is always not selectable and can not be attacked - if (isTrigger()) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - if (isTotem() || isTrigger() - || GetCreatureType() == CREATURE_TYPE_CRITTER) - SetReactState(REACT_PASSIVE); - /*else if (isCivilian()) - SetReactState(REACT_DEFENSIVE);*/ - else - SetReactState(REACT_AGGRESSIVE); - - if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT) - { - ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); - ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); - } - - // TODO: In fact monster move flags should be set - not movement flags. - if (cInfo->InhabitType & INHABIT_AIR) - AddUnitMovementFlag(MOVEMENTFLAG_FLY_MODE | MOVEMENTFLAG_FLYING); - - if (cInfo->InhabitType & INHABIT_WATER) - AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - - return true; -} - -void Creature::Update(uint32 diff) -{ - if (m_GlobalCooldown <= diff) - m_GlobalCooldown = 0; - else - m_GlobalCooldown -= diff; - - switch(m_deathState) - { - case JUST_ALIVED: - // Don't must be called, see Creature::setDeathState JUST_ALIVED -> ALIVE promoting. - sLog.outError("Creature (GUID: %u Entry: %u) in wrong state: JUST_ALIVED (4)",GetGUIDLow(),GetEntry()); - break; - case JUST_DIED: - // Don't must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting. - sLog.outError("Creature (GUID: %u Entry: %u) in wrong state: JUST_DEAD (1)",GetGUIDLow(),GetEntry()); - break; - case DEAD: - { - if (m_respawnTime <= time(NULL)) - { - if (!GetLinkedCreatureRespawnTime()) // Can respawn - Respawn(); - else // the master is dead - { - if (uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid)) - { - if (targetGuid == m_DBTableGuid) // if linking self, never respawn (check delayed to next day) - SetRespawnTime(DAY); - else - m_respawnTime = (time(NULL)>GetLinkedCreatureRespawnTime()? time(NULL):GetLinkedCreatureRespawnTime())+urand(5,MINUTE); // else copy time from master and add a little - SaveRespawnTime(); // also save to DB immediately - } - else - Respawn(); - } - } - break; - } - case CORPSE: - { - if (m_isDeadByDefault) - break; - - if (m_groupLootTimer && lootingGroupGUID) - { - // for delayed spells - m_Events.Update(diff); - - if (m_groupLootTimer <= diff) - { - Group* group = objmgr.GetGroupByGUID(lootingGroupGUID); - if (group) - group->EndRoll(&loot); - m_groupLootTimer = 0; - lootingGroupGUID = 0; - } - else m_groupLootTimer -= diff; - } - else if (m_deathTimer <= diff) - { - RemoveCorpse(); - DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY)); - } - else - { - // for delayed spells - m_Events.Update(diff); - m_deathTimer -= diff; - } - - break; - } - case ALIVE: - { - if (m_isDeadByDefault) - { - if (m_deathTimer <= diff) - { - RemoveCorpse(); - DEBUG_LOG("Removing alive corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY)); - } - else - { - m_deathTimer -= diff; - } - } - - Unit::Update(diff); - - // creature can be dead after Unit::Update call - // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly) - if (!isAlive()) - break; - - // if creature is charmed, switch to charmed AI - if (NeedChangeAI) - { - UpdateCharmAI(); - NeedChangeAI = false; - IsAIEnabled = true; - } - - if (!IsInEvadeMode() && IsAIEnabled) - { - // do not allow the AI to be changed during update - m_AI_locked = true; - i_AI->UpdateAI(diff); - m_AI_locked = false; - } - - // creature can be dead after UpdateAI call - // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly) - if (!isAlive()) - break; - - if (m_regenTimer > 0) - { - if (diff >= m_regenTimer) - m_regenTimer = 0; - else - m_regenTimer -= diff; - } - - if (m_regenTimer != 0) - break; - - bool bIsPolymorphed = IsPolymorphed(); - bool bInCombat = isInCombat() && (!getVictim() || // if isInCombat() is true and this has no victim - !getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player - !getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->isGameMaster()); // or the victim/owner/charmer is not a GameMaster - - /*if (m_regenTimer <= diff) - {*/ - if (!bInCombat || bIsPolymorphed) // regenerate health if not in combat or if polymorphed - RegenerateHealth(); - - if (getPowerType() == POWER_ENERGY) - { - if (!IsVehicle() || GetVehicleKit()->GetVehicleInfo()->m_powerType != POWER_PYRITE) - Regenerate(POWER_ENERGY); - } - else - RegenerateMana(); - - /*if (!bIsPolymorphed) // only increase the timer if not polymorphed - m_regenTimer += CREATURE_REGEN_INTERVAL - diff; - } - else - if (!bIsPolymorphed) // if polymorphed, skip the timer - m_regenTimer -= diff;*/ - m_regenTimer = CREATURE_REGEN_INTERVAL; - break; - } - case DEAD_FALLING: - GetMotionMaster()->UpdateMotion(diff); - break; - default: - break; - } -} - -void Creature::RegenerateMana() -{ - uint32 curValue = GetPower(POWER_MANA); - uint32 maxValue = GetMaxPower(POWER_MANA); - - if (curValue >= maxValue) - return; - - uint32 addvalue = 0; - - // Combat and any controlled creature - if (isInCombat() || GetCharmerOrOwnerGUID()) - { - if (!IsUnderLastManaUseEffect()) - { - float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA); - float Spirit = GetStat(STAT_SPIRIT); - - addvalue = uint32((Spirit/5.0f + 17.0f) * ManaIncreaseRate); - } - } - else - addvalue = maxValue/3; - - // Apply modifiers (if any). - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetMiscValue() == POWER_MANA) - addvalue *= ((*i)->GetAmount() + 100) / 100.0f; - - addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) * CREATURE_REGEN_INTERVAL / (5 * IN_MILISECONDS); - - ModifyPower(POWER_MANA, addvalue); -} - -void Creature::RegenerateHealth() -{ - if (!isRegeneratingHealth()) - return; - - uint32 curValue = GetHealth(); - uint32 maxValue = GetMaxHealth(); - - if (curValue >= maxValue) - return; - - uint32 addvalue = 0; - - // Not only pet, but any controlled creature - if (GetCharmerOrOwnerGUID()) - { - float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH); - float Spirit = GetStat(STAT_SPIRIT); - - if (GetPower(POWER_MANA) > 0) - addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate); - else - addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate); - } - else - addvalue = maxValue/3; - - // Apply modifiers (if any). - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - addvalue *= ((*i)->GetAmount() + 100) / 100.0f; - - addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * CREATURE_REGEN_INTERVAL / (5 * IN_MILISECONDS); - - ModifyHealth(addvalue); -} - -void Creature::DoFleeToGetAssistance() -{ - if (!getVictim()) - return; - - if (HasAuraType(SPELL_AURA_PREVENTS_FLEEING)) - return; - - float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS); - if (radius >0) - { - Creature* pCreature = NULL; - - CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - Trinity::NearestAssistCreatureInCreatureRangeCheck u_check(this, getVictim(), radius); - Trinity::CreatureLastSearcher searcher(this, pCreature, u_check); - - TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); - - cell.Visit(p, grid_creature_searcher, *GetMap(), *this, radius); - - SetNoSearchAssistance(true); - UpdateSpeed(MOVE_RUN, false); - - if (!pCreature) - //SetFeared(true, getVictim()->GetGUID(), 0 ,sWorld.getConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY)); - //TODO: use 31365 - SetControlled(true, UNIT_STAT_FLEEING); - else - GetMotionMaster()->MoveSeekAssistance(pCreature->GetPositionX(), pCreature->GetPositionY(), pCreature->GetPositionZ()); - } -} - -bool Creature::AIM_Initialize(CreatureAI* ai) -{ - // make sure nothing can change the AI during AI update - if (m_AI_locked) - { - sLog.outDebug("AIM_Initialize: failed to init, locked."); - return false; - } - - UnitAI *oldAI = i_AI; - - Motion_Initialize(); - - i_AI = ai ? ai : FactorySelector::selectAI(this); - if (oldAI) delete oldAI; - IsAIEnabled = true; - i_AI->InitializeAI(); - return true; -} - -void Creature::Motion_Initialize() -{ - if (!m_formation) - i_motionMaster.Initialize(); - else if (m_formation->getLeader() == this) - { - m_formation->FormationReset(false); - i_motionMaster.Initialize(); - } - else if (m_formation->isFormed()) - i_motionMaster.MoveIdle(MOTION_SLOT_IDLE); //wait the order of leader - else - i_motionMaster.Initialize(); -} - -bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 vehId, uint32 team, float x, float y, float z, float ang, const CreatureData *data) -{ - ASSERT(map); - SetMap(map); - SetPhaseMask(phaseMask,false); - - Relocate(x, y, z, ang); - - if (!IsPositionValid()) - { - sLog.outError("Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,Entry,x,y); - return false; - } - - //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0; - const bool bResult = CreateFromProto(guidlow, Entry, vehId, team, data); - - if (bResult) - { - switch (GetCreatureInfo()->rank) - { - case CREATURE_ELITE_RARE: - m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RARE); - break; - case CREATURE_ELITE_ELITE: - m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_ELITE); - break; - case CREATURE_ELITE_RAREELITE: - m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RAREELITE); - break; - case CREATURE_ELITE_WORLDBOSS: - m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_WORLDBOSS); - break; - default: - m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL); - break; - } - LoadCreaturesAddon(); - CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(GetNativeDisplayId()); - if (minfo && !isTotem()) // Cancel load if no model defined or if totem - { - uint32 display_id = minfo->modelid; // it can be different (for another gender) - - SetDisplayId(display_id); - SetNativeDisplayId(display_id); - SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender); - } - - if (GetCreatureInfo()->InhabitType & INHABIT_AIR) - { - if (GetDefaultMovementType() == IDLE_MOTION_TYPE) - AddUnitMovementFlag(MOVEMENTFLAG_FLY_MODE); - else - SetFlying(true); - } - - if (GetCreatureInfo()->InhabitType & INHABIT_WATER) - { - AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - } - LastUsedScriptID = GetCreatureInfo()->ScriptID; - } - - return bResult; -} - -bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const -{ - if (!isTrainer()) - return false; - - TrainerSpellData const* trainer_spells = GetTrainerSpells(); - - if ((!trainer_spells || trainer_spells->spellList.empty()) && GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS) - { - sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.", - GetGUIDLow(),GetEntry()); - return false; - } - - switch(GetCreatureInfo()->trainer_type) - { - case TRAINER_TYPE_CLASS: - if (pPlayer->getClass() != GetCreatureInfo()->trainer_class) - { - if (msg) - { - pPlayer->PlayerTalkClass->ClearMenus(); - switch(GetCreatureInfo()->trainer_class) - { - case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu(4913,GetGUID()); break; - case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break; - case CLASS_MAGE: pPlayer->PlayerTalkClass->SendGossipMenu(328,GetGUID()); break; - case CLASS_PALADIN:pPlayer->PlayerTalkClass->SendGossipMenu(1635,GetGUID()); break; - case CLASS_PRIEST: pPlayer->PlayerTalkClass->SendGossipMenu(4436,GetGUID()); break; - case CLASS_ROGUE: pPlayer->PlayerTalkClass->SendGossipMenu(4797,GetGUID()); break; - case CLASS_SHAMAN: pPlayer->PlayerTalkClass->SendGossipMenu(5003,GetGUID()); break; - case CLASS_WARLOCK:pPlayer->PlayerTalkClass->SendGossipMenu(5836,GetGUID()); break; - case CLASS_WARRIOR:pPlayer->PlayerTalkClass->SendGossipMenu(4985,GetGUID()); break; - } - } - return false; - } - break; - case TRAINER_TYPE_PETS: - if (pPlayer->getClass() != CLASS_HUNTER) - { - pPlayer->PlayerTalkClass->ClearMenus(); - pPlayer->PlayerTalkClass->SendGossipMenu(3620,GetGUID()); - return false; - } - break; - case TRAINER_TYPE_MOUNTS: - if (GetCreatureInfo()->trainer_race && pPlayer->getRace() != GetCreatureInfo()->trainer_race) - { - if (msg) - { - pPlayer->PlayerTalkClass->ClearMenus(); - switch(GetCreatureInfo()->trainer_class) - { - case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break; - case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break; - case RACE_HUMAN: pPlayer->PlayerTalkClass->SendGossipMenu(5861,GetGUID()); break; - case RACE_NIGHTELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break; - case RACE_ORC: pPlayer->PlayerTalkClass->SendGossipMenu(5863,GetGUID()); break; - case RACE_TAUREN: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break; - case RACE_TROLL: pPlayer->PlayerTalkClass->SendGossipMenu(5816,GetGUID()); break; - case RACE_UNDEAD_PLAYER:pPlayer->PlayerTalkClass->SendGossipMenu(624,GetGUID()); break; - case RACE_BLOODELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break; - case RACE_DRAENEI: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break; - } - } - return false; - } - break; - case TRAINER_TYPE_TRADESKILLS: - if (GetCreatureInfo()->trainer_spell && !pPlayer->HasSpell(GetCreatureInfo()->trainer_spell)) - { - if (msg) - { - pPlayer->PlayerTalkClass->ClearMenus(); - pPlayer->PlayerTalkClass->SendGossipMenu(11031,GetGUID()); - } - return false; - } - break; - default: - return false; // checked and error output at creature_template loading - } - return true; -} - -bool Creature::isCanInteractWithBattleMaster(Player* pPlayer, bool msg) const -{ - if (!isBattleMaster()) - return false; - - BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(GetEntry()); - if (!msg) - return pPlayer->GetBGAccessByLevel(bgTypeId); - - if (!pPlayer->GetBGAccessByLevel(bgTypeId)) - { - pPlayer->PlayerTalkClass->ClearMenus(); - switch(bgTypeId) - { - case BATTLEGROUND_AV: pPlayer->PlayerTalkClass->SendGossipMenu(7616,GetGUID()); break; - case BATTLEGROUND_WS: pPlayer->PlayerTalkClass->SendGossipMenu(7599,GetGUID()); break; - case BATTLEGROUND_AB: pPlayer->PlayerTalkClass->SendGossipMenu(7642,GetGUID()); break; - case BATTLEGROUND_EY: - case BATTLEGROUND_NA: - case BATTLEGROUND_BE: - case BATTLEGROUND_AA: - case BATTLEGROUND_RL: - case BATTLEGROUND_SA: - case BATTLEGROUND_DS: - case BATTLEGROUND_RV: pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break; - default: break; - } - return false; - } - return true; -} - -bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const -{ - return pPlayer->getLevel() >= 10 - && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS - && pPlayer->getClass() == GetCreatureInfo()->trainer_class; -} - -void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 /*MovementFlags*/, uint8 /*type*/) -{ - /* uint32 timeElap = getMSTime(); - if ((timeElap - m_startMove) < m_moveTime) - { - oX = (dX - oX) * ((timeElap - m_startMove) / m_moveTime); - oY = (dY - oY) * ((timeElap - m_startMove) / m_moveTime); - } - else - { - oX = dX; - oY = dY; - } - - dX = x; - dY = y; - m_orientation = atan2((oY - dY), (oX - dX)); - - m_startMove = getMSTime(); - m_moveTime = time;*/ - SendMonsterMove(x, y, z, time); -} - -Player *Creature::GetLootRecipient() const -{ - if (!m_lootRecipient) return NULL; - else return ObjectAccessor::FindPlayer(m_lootRecipient); -} - -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 - - if (!unit) - { - m_lootRecipient = 0; - RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE|UNIT_DYNFLAG_TAPPED); - return; - } - - Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself(); - if (!player) // normal creature, no player involved - return; - - m_lootRecipient = player->GetGUID(); - SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED); -} - -// return true if this creature is tapped by the player or by a member of his group. -bool Creature::isTappedBy(Player *player) const -{ - if (player->GetGUID() == m_lootRecipient) - return true; - - Player* recipient = GetLootRecipient(); - if (!recipient) - return false; // recipient exist but is offline. can't check any further. - - Group* recipientGroup = recipient->GetGroup(); - if (!recipientGroup) - return (player == recipient); - - Group* playerGroup = player->GetGroup(); - if (!playerGroup || playerGroup != recipientGroup) - return false; - - return true; -} - -void Creature::SaveToDB() -{ - // this should only be used when the creature has already been loaded - // preferably after adding to map, because mapid may not be valid otherwise - CreatureData const *data = objmgr.GetCreatureData(m_DBTableGuid); - if (!data) - { - sLog.outError("Creature::SaveToDB failed, cannot get creature data!"); - return; - } - - SaveToDB(GetMapId(), data->spawnMask,GetPhaseMask()); -} - -void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) -{ - // update in loaded data - if (!m_DBTableGuid) - m_DBTableGuid = GetGUIDLow(); - CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid); - - uint32 displayId = GetNativeDisplayId(); - - // check if it's a custom model and if not, use 0 for displayId - CreatureInfo const *cinfo = GetCreatureInfo(); - if (cinfo) - { - if (displayId == cinfo->Modelid1 || displayId == cinfo->Modelid2 || - displayId == cinfo->Modelid3 || displayId == cinfo->Modelid4) - displayId = 0; - } - - // data->guid = guid don't must be update at save - data.id = GetEntry(); - data.mapid = mapid; - data.phaseMask = phaseMask; - data.displayid = displayId; - data.equipmentId = GetEquipmentId(); - data.posX = GetPositionX(); - data.posY = GetPositionY(); - data.posZ = GetPositionZ(); - data.orientation = GetOrientation(); - data.spawntimesecs = m_respawnDelay; - // prevent add data integrity problems - data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0 : m_respawnradius; - data.currentwaypoint = 0; - data.curhealth = GetHealth(); - data.curmana = GetPower(POWER_MANA); - data.is_dead = m_isDeadByDefault; - // prevent add data integrity problems - data.movementType = !m_respawnradius && GetDefaultMovementType() == RANDOM_MOTION_TYPE - ? IDLE_MOTION_TYPE : GetDefaultMovementType(); - data.spawnMask = spawnMask; - - // updated in DB - WorldDatabase.BeginTransaction(); - - WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid); - - std::ostringstream ss; - ss << "INSERT INTO creature VALUES (" - << m_DBTableGuid << "," - << GetEntry() << "," - << mapid <<"," - << uint32(spawnMask) << "," // cast to prevent save as symbol - << uint16(GetPhaseMask()) << "," // prevent out of range error - << displayId <<"," - << GetEquipmentId() <<"," - << GetPositionX() << "," - << GetPositionY() << "," - << GetPositionZ() << "," - << GetOrientation() << "," - << m_respawnDelay << "," //respawn time - << (float) m_respawnradius << "," //spawn distance (float) - << (uint32) (0) << "," //currentwaypoint - << GetHealth() << "," //curhealth - << GetPower(POWER_MANA) << "," //curmana - << (m_isDeadByDefault ? 1 : 0) << "," //is_dead - << GetDefaultMovementType() << ")"; //default movement generator type - - WorldDatabase.PExecuteLog(ss.str().c_str()); - - WorldDatabase.CommitTransaction(); -} - -void Creature::SelectLevel(const CreatureInfo *cinfo) -{ - uint32 rank = isPet()? 0 : cinfo->rank; - - // level - uint8 minlevel = std::min(cinfo->maxlevel, cinfo->minlevel); - uint8 maxlevel = std::max(cinfo->maxlevel, cinfo->minlevel); - uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel); - SetLevel(level); - - CreatureBaseStats const* stats = objmgr.GetCreatureBaseStats(level, cinfo->unit_class); - - // health - float healthmod = _GetHealthMod(rank); - - uint32 basehp = stats->GenerateHealth(cinfo); - uint32 health = uint32(basehp * healthmod); - - SetCreateHealth(health); - SetMaxHealth(health); - SetHealth(health); - ResetPlayerDamageReq(); - - // mana - uint32 mana = stats->GenerateMana(cinfo); - - SetCreateMana(mana); - SetMaxPower(POWER_MANA, mana); //MAX Mana - SetPower(POWER_MANA, mana); - - // TODO: set UNIT_FIELD_POWER*, for some creature class case (energy, etc) - - SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health); - SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana); - - //damage - float damagemod = 1.0f;//_GetDamageMod(rank); - - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg * damagemod); - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg * damagemod); - - SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,cinfo->minrangedmg * damagemod); - SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,cinfo->maxrangedmg * damagemod); - - SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->attackpower * damagemod); - -} - -float Creature::_GetHealthMod(int32 Rank) -{ - switch (Rank) // define rates for each elite rank - { - case CREATURE_ELITE_NORMAL: - return sWorld.getRate(RATE_CREATURE_NORMAL_HP); - case CREATURE_ELITE_ELITE: - return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP); - case CREATURE_ELITE_RAREELITE: - return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_HP); - case CREATURE_ELITE_WORLDBOSS: - return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP); - case CREATURE_ELITE_RARE: - return sWorld.getRate(RATE_CREATURE_ELITE_RARE_HP); - default: - return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP); - } -} - -float Creature::_GetDamageMod(int32 Rank) -{ - switch (Rank) // define rates for each elite rank - { - case CREATURE_ELITE_NORMAL: - return sWorld.getRate(RATE_CREATURE_NORMAL_DAMAGE); - case CREATURE_ELITE_ELITE: - return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE); - case CREATURE_ELITE_RAREELITE: - return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE); - case CREATURE_ELITE_WORLDBOSS: - return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE); - case CREATURE_ELITE_RARE: - return sWorld.getRate(RATE_CREATURE_ELITE_RARE_DAMAGE); - default: - return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE); - } -} - -float Creature::GetSpellDamageMod(int32 Rank) -{ - switch (Rank) // define rates for each elite rank - { - case CREATURE_ELITE_NORMAL: - return sWorld.getRate(RATE_CREATURE_NORMAL_SPELLDAMAGE); - case CREATURE_ELITE_ELITE: - return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE); - case CREATURE_ELITE_RAREELITE: - return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE); - case CREATURE_ELITE_WORLDBOSS: - return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE); - case CREATURE_ELITE_RARE: - return sWorld.getRate(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE); - default: - return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE); - } -} - -bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData *data) -{ - SetZoneScript(); - if (m_zoneScript && data) - { - Entry = m_zoneScript->GetCreatureEntry(guidlow, data); - if (!Entry) - return false; - } - - CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry); - if (!cinfo) - { - sLog.outErrorDb("Creature entry %u does not exist.", Entry); - return false; - } - - SetOriginalEntry(Entry); - - if (!vehId) - vehId = cinfo->VehicleId; - - if (vehId && !CreateVehicleKit(vehId)) - vehId = 0; - - Object::_Create(guidlow, Entry, vehId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT); - - if (!UpdateEntry(Entry, team, data)) - return false; - - return true; -} - -bool Creature::LoadFromDB(uint32 guid, Map *map) -{ - CreatureData const* data = objmgr.GetCreatureData(guid); - - if (!data) - { - sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid); - return false; - } - - m_DBTableGuid = guid; - if (map->GetInstanceId() == 0) - { - if (map->GetCreature(MAKE_NEW_GUID(guid,data->id,HIGHGUID_UNIT))) - return false; - } - else - guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT); - - uint16 team = 0; - if (!Create(guid,map,data->phaseMask,data->id,0,team,data->posX,data->posY,data->posZ,data->orientation,data)) - return false; - - //We should set first home position, because then AI calls home movement - SetHomePosition(data->posX,data->posY,data->posZ,data->orientation); - - m_respawnradius = data->spawndist; - - m_respawnDelay = data->spawntimesecs; - m_isDeadByDefault = data->is_dead; - m_deathState = m_isDeadByDefault ? DEAD : ALIVE; - - m_respawnTime = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId()); - if (m_respawnTime) // respawn on Update - { - m_deathState = DEAD; - if (canFly()) - { - float tz = map->GetHeight(data->posX,data->posY,data->posZ,false); - if (data->posZ - tz > 0.1) - Relocate(data->posX,data->posY,tz); - } - } - - uint32 curhealth = data->curhealth; - if (curhealth) - { - curhealth = uint32(curhealth*_GetHealthMod(GetCreatureInfo()->rank)); - if (curhealth < 1) - curhealth = 1; - } - - SetHealth(m_deathState == ALIVE ? curhealth : 0); - SetPower(POWER_MANA,data->curmana); - - // checked at creature_template loading - m_defaultMovementType = MovementGeneratorType(data->movementType); - - m_creatureData = data; - - return true; -} - -void Creature::LoadEquipment(uint32 equip_entry, bool force) -{ - if (equip_entry == 0) - { - if (force) - { - for (uint8 i = 0; i < 3; ++i) - SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0); - m_equipmentId = 0; - } - return; - } - - EquipmentInfo const *einfo = objmgr.GetEquipmentInfo(equip_entry); - if (!einfo) - return; - - m_equipmentId = equip_entry; - for (uint8 i = 0; i < 3; ++i) - SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->equipentry[i]); -} - -bool Creature::hasQuest(uint32 quest_id) const -{ - QuestRelations const& qr = objmgr.mCreatureQuestRelations; - for (QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr) - { - if (itr->second == quest_id) - return true; - } - return false; -} - -bool Creature::hasInvolvedQuest(uint32 quest_id) const -{ - QuestRelations const& qr = objmgr.mCreatureQuestInvolvedRelations; - for (QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr) - { - if (itr->second == quest_id) - return true; - } - return false; -} - -void Creature::DeleteFromDB() -{ - if (!m_DBTableGuid) - { - sLog.outDebug("Trying to delete not saved creature!"); - return; - } - - objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0); - objmgr.DeleteCreatureData(m_DBTableGuid); - - WorldDatabase.BeginTransaction(); - WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.CommitTransaction(); -} - -bool Creature::canSeeOrDetect(Unit const* u, bool detect, bool /*inVisibleList*/, bool /*is3dDistance*/) const -{ - // not in world - if (!IsInWorld() || !u->IsInWorld()) - return false; - - // all dead creatures/players not visible for any creatures - if (!u->isAlive() || !isAlive()) - return false; - - // Always can see self - if (u == this) - return true; - - // phased visibility (both must phased in same way) - if (!InSamePhase(u)) - return false; - - // always seen by owner - if (GetGUID() == u->GetCharmerOrOwnerGUID()) - return true; - - if (u->GetVisibility() == VISIBILITY_OFF) //GM - return false; - - // invisible aura - if ((m_invisibilityMask || u->m_invisibilityMask) && !canDetectInvisibilityOf(u)) - return false; - - // unit got in stealth in this moment and must ignore old detected state - //if (m_Visibility == VISIBILITY_GROUP_NO_DETECT) - // return false; - - // GM invisibility checks early, invisibility if any detectable, so if not stealth then visible - if (u->GetVisibility() == VISIBILITY_GROUP_STEALTH) - { - //do not know what is the use of this detect - if (!detect || !canDetectStealthOf(u, GetDistance(u))) - return false; - } - - // Now check is target visible with LoS - //return u->IsWithinLOS(GetPositionX(),GetPositionY(),GetPositionZ()); - return true; -} - -bool Creature::canStartAttack(Unit const* who, bool force) const -{ - if (isCivilian()) - return false; - - if (!canFly() && (GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE + m_CombatDistance)) - //|| who->IsControlledByPlayer() && who->IsFlying())) - // we cannot check flying for other creatures, too much map/vmap calculation - // TODO: should switch to range attack - return false; - - if (!force) - { - if (!_IsTargetAcceptable(who)) - return false; - - if (who->isInCombat()) - if (Unit *victim = who->getAttackerForHelper()) - if (IsWithinDistInMap(victim, sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS))) - force = true; - - if (!force && (IsNeutralToAll() || !IsWithinDistInMap(who, GetAttackDistance(who) + m_CombatDistance))) - return false; - } - - if (!canCreatureAttack(who, force)) - return false; - - return IsWithinLOSInMap(who); -} - -float Creature::GetAttackDistance(Unit const* pl) const -{ - float aggroRate = sWorld.getRate(RATE_CREATURE_AGGRO); - if (aggroRate == 0) - return 0.0f; - - uint32 playerlevel = pl->getLevelForTarget(this); - uint32 creaturelevel = getLevelForTarget(pl); - - int32 leveldif = int32(playerlevel) - int32(creaturelevel); - - // "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob." - if (leveldif < - 25) - leveldif = -25; - - // "The aggro radius of a mob having the same level as the player is roughly 20 yards" - float RetDistance = 20; - - // "Aggro Radius varies with level difference at a rate of roughly 1 yard/level" - // radius grow if playlevel < creaturelevel - RetDistance -= (float)leveldif; - - if (creaturelevel+5 <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - { - // detect range auras - RetDistance += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE); - - // detected range auras - RetDistance += pl->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE); - } - - // "Minimum Aggro Radius for a mob seems to be combat range (5 yards)" - if (RetDistance < 5) - RetDistance = 5; - - return (RetDistance*aggroRate); -} - -void Creature::setDeathState(DeathState s) -{ - if ((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault)) - { - m_deathTimer = m_corpseDelay*IN_MILISECONDS; - - // always save boss respawn time at death to prevent crash cheating - if (sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss()) - SaveRespawnTime(); - } - Unit::setDeathState(s); - - if (s == JUST_DIED) - { - SetUInt64Value(UNIT_FIELD_TARGET,0); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState) - SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - - setActive(false); - - if (!isPet() && GetCreatureInfo()->SkinLootId) - if (LootTemplates_Skinning.HaveLootFor(GetCreatureInfo()->SkinLootId)) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - - if (HasSearchedAssistance()) - { - SetNoSearchAssistance(false); - UpdateSpeed(MOVE_RUN, false); - } - - //Dismiss group if is leader - if (m_formation && m_formation->getLeader() == this) - m_formation->FormationReset(true); - - if ((canFly() || IsFlying()) && FallGround()) - return; - - Unit::setDeathState(CORPSE); - } - else if (s == JUST_ALIVED) - { - //if (isPet()) - // setActive(true); - SetHealth(GetMaxHealth()); - SetLootRecipient(NULL); - ResetPlayerDamageReq(); - CreatureInfo const *cinfo = GetCreatureInfo(); - AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - if (GetCreatureInfo()->InhabitType & INHABIT_AIR) - AddUnitMovementFlag(MOVEMENTFLAG_FLY_MODE | MOVEMENTFLAG_FLYING); - if (GetCreatureInfo()->InhabitType & INHABIT_WATER) - AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag); - clearUnitState(UNIT_STAT_ALL_STATE); - SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); - LoadCreaturesAddon(true); - Motion_Initialize(); - if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask) - SetPhaseMask(GetCreatureData()->phaseMask, false); - if (m_vehicleKit) m_vehicleKit->Reset(); - Unit::setDeathState(ALIVE); - } -} - -bool Creature::FallGround() -{ - // Let's abort after we called this function one time - if (getDeathState() == DEAD_FALLING) - return false; - - float x, y, z; - GetPosition(x, y, z); - float ground_Z = GetMap()->GetHeight(x, y, z); - if (fabs(ground_Z - z) < 0.1f) - return false; - - GetMotionMaster()->MoveFall(ground_Z, EVENT_FALL_GROUND); - Unit::setDeathState(DEAD_FALLING); - return true; -} - -void Creature::Respawn(bool force) -{ - DestroyForNearbyPlayers(); - - if (force) - { - if (isAlive()) - setDeathState(JUST_DIED); - else if (getDeathState() != CORPSE) - setDeathState(CORPSE); - } - - RemoveCorpse(); - - if (getDeathState() == DEAD) - { - if (m_DBTableGuid) - objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0); - - DEBUG_LOG("Respawning..."); - m_respawnTime = 0; - lootForPickPocketed = false; - lootForBody = false; - - if (m_originalEntry != GetEntry()) - UpdateEntry(m_originalEntry); - - CreatureInfo const *cinfo = GetCreatureInfo(); - SelectLevel(cinfo); - - if (m_isDeadByDefault) - { - setDeathState(JUST_DIED); - i_motionMaster.Clear(); - clearUnitState(UNIT_STAT_ALL_STATE); - LoadCreaturesAddon(true); - } - else - setDeathState(JUST_ALIVED); - - CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(GetNativeDisplayId()); - if (minfo) // Cancel load if no model defined - { - uint32 display_id = minfo->modelid; // it can be different (for another gender) - - SetDisplayId(display_id); - SetNativeDisplayId(display_id); - SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender); - } - - //Call AI respawn virtual function - if (IsAIEnabled) - AI()->JustRespawned(); - - uint16 poolid = GetDBTableGUIDLow() ? poolhandler.IsPartOfAPool(GetDBTableGUIDLow()) : 0; - if (poolid) - poolhandler.UpdatePool(poolid, GetDBTableGUIDLow()); - } - - UpdateObjectVisibility(); -} - -void Creature::ForcedDespawn(uint32 timeMSToDespawn) -{ - if (timeMSToDespawn) - { - ForcedDespawnDelayEvent *pEvent = new ForcedDespawnDelayEvent(*this); - - m_Events.AddEvent(pEvent, m_Events.CalculateTime(timeMSToDespawn)); - return; - } - - if (isAlive()) - setDeathState(JUST_DIED); - - RemoveCorpse(); -} - -bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo) -{ - if (!spellInfo) - return false; - - if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1))) - return true; - - return Unit::IsImmunedToSpell(spellInfo); -} - -bool Creature::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const -{ - if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->EffectMechanic[index] - 1))) - return true; - - return Unit::IsImmunedToSpellEffect(spellInfo, index); -} - -SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim) -{ - if (!pVictim) - return NULL; - - for (uint32 i=0; i < CREATURE_MAX_SPELLS; ++i) - { - if (!m_spells[i]) - continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i]); - if (!spellInfo) - { - sLog.outError("WORLD: unknown spell id %i", m_spells[i]); - continue; - } - - bool bcontinue = true; - for (uint32 j=0; j<3; j++) - { - if ((spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE) || - (spellInfo->Effect[j] == SPELL_EFFECT_INSTAKILL) || - (spellInfo->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) || - (spellInfo->Effect[j] == SPELL_EFFECT_HEALTH_LEECH) -) - { - bcontinue = false; - break; - } - } - if (bcontinue) continue; - - if (spellInfo->manaCost > GetPower(POWER_MANA)) - continue; - SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); - float range = GetSpellMaxRangeForHostile(srange); - float minrange = GetSpellMinRangeForHostile(srange); - float dist = GetDistance(pVictim); - //if (!isInFront(pVictim, range) && spellInfo->AttributesEx) - // continue; - if (dist > range || dist < minrange) - continue; - if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) - continue; - if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) - continue; - return spellInfo; - } - return NULL; -} - -SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim) -{ - if (!pVictim) - return NULL; - - for (uint32 i=0; i < CREATURE_MAX_SPELLS; ++i) - { - if (!m_spells[i]) - continue; - SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i]); - if (!spellInfo) - { - sLog.outError("WORLD: unknown spell id %i", m_spells[i]); - continue; - } - - bool bcontinue = true; - for (uint32 j=0; j<3; j++) - { - if ((spellInfo->Effect[j] == SPELL_EFFECT_HEAL)) - { - bcontinue = false; - break; - } - } - if (bcontinue) continue; - - if (spellInfo->manaCost > GetPower(POWER_MANA)) - continue; - SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); - float range = GetSpellMaxRangeForFriend(srange); - float minrange = GetSpellMinRangeForFriend(srange); - float dist = GetDistance(pVictim); - //if (!isInFront(pVictim, range) && spellInfo->AttributesEx) - // continue; - if (dist > range || dist < minrange) - continue; - if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) - continue; - if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) - continue; - return spellInfo; - } - return NULL; -} - -bool Creature::IsVisibleInGridForPlayer(Player const* pl) const -{ - // gamemaster in GM mode see all, including ghosts - if (pl->isGameMaster()) - return true; - - // Trigger shouldn't be visible for players - //if (isTrigger()) - // return false; - // Rat: this makes no sense, triggers are always sent to players, but with invisible model and can not be attacked or targeted! - - // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0 - if (pl->isAlive() || pl->GetDeathTimer() > 0) - { - if (GetEntry() == VISUAL_WAYPOINT) - return false; - return (isAlive() || m_deathTimer > 0 || (m_isDeadByDefault && m_deathState == CORPSE)); - } - - // Dead player see live creatures near own corpse - if (isAlive()) - { - Corpse *corpse = pl->GetCorpse(); - if (corpse) - { - // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level - if (corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO))) - return true; - } - } - - // Dead player see Spirit Healer or Spirit Guide - if (isSpiritService()) - return true; - - // and not see any other - return false; -} - - -// select nearest hostile unit within the given distance (regardless of threat list). -Unit* Creature::SelectNearestTarget(float dist) const -{ - CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Unit *target = NULL; - - { - if (dist == 0.0f || dist > MAX_VISIBILITY_DISTANCE) - dist = MAX_VISIBILITY_DISTANCE; - - Trinity::NearestHostileUnitCheck u_check(this, dist); - Trinity::UnitLastSearcher searcher(this, target, u_check); - - TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); - TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); - - cell.Visit(p, world_unit_searcher, *GetMap(), *this, dist); - cell.Visit(p, grid_unit_searcher, *GetMap(), *this, dist); - } - - return target; -} - -// select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list. -Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const -{ - CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Unit *target = NULL; - - if (dist > ATTACK_DISTANCE) - sLog.outError("Creature (GUID: %u Entry: %u) SelectNearestTargetInAttackDistance called with dist > ATTACK_DISTANCE. Extra distance ignored.",GetGUIDLow(),GetEntry()); - - { - Trinity::NearestHostileUnitInAttackDistanceCheck u_check(this, dist); - Trinity::UnitLastSearcher searcher(this, target, u_check); - - TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); - TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); - - cell.Visit(p, world_unit_searcher, *GetMap(), *this, ATTACK_DISTANCE); - cell.Visit(p, grid_unit_searcher, *GetMap(), *this, ATTACK_DISTANCE); - } - - return target; -} - -void Creature::SendAIReaction(AiReaction reactionType) -{ - WorldPacket data(SMSG_AI_REACTION, 12); - - data << uint64(GetGUID()); - data << uint32(reactionType); - - ((WorldObject*)this)->SendMessageToSet(&data, true); - - sLog.outDebug("WORLD: Sent SMSG_AI_REACTION, type %u.", reactionType); -} - -void Creature::CallAssistance() -{ - if (!m_AlreadyCallAssistance && getVictim() && !isPet() && !isCharmed()) - { - SetNoCallAssistance(true); - - float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS); - - if (radius > 0) - { - std::list assistList; - - { - CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Trinity::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius); - Trinity::CreatureListSearcher searcher(this, assistList, u_check); - - TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); - - cell.Visit(p, grid_creature_searcher, *GetMap(), *this, radius); - } - - if (!assistList.empty()) - { - AssistDelayEvent *e = new AssistDelayEvent(getVictim()->GetGUID(), *this); - while (!assistList.empty()) - { - // Pushing guids because in delay can happen some creature gets despawned => invalid pointer - e->AddAssistant((*assistList.begin())->GetGUID()); - assistList.pop_front(); - } - m_Events.AddEvent(e, m_Events.CalculateTime(sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY))); - } - } - } -} - -void Creature::CallForHelp(float fRadius) -{ - if (fRadius <= 0.0f || !getVictim() || isPet() || isCharmed()) - return; - - CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Trinity::CallOfHelpCreatureInRangeDo u_do(this, getVictim(), fRadius); - Trinity::CreatureWorker worker(this, u_do); - - TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(worker); - - cell.Visit(p, grid_creature_searcher, *GetMap(), *this, fRadius); -} - -bool Creature::CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction /*= true*/) const -{ - // is it true? - if (!HasReactState(REACT_AGGRESSIVE)) - return false; - - // we don't need help from zombies :) - if (!isAlive()) - return false; - - // we don't need help from non-combatant ;) - if (isCivilian()) - return false; - - // skip fighting creature - if (isInCombat()) - return false; - - // only free creature - if (GetCharmerOrOwnerGUID()) - return false; - - // only from same creature faction - if (checkfaction) - { - if (getFaction() != u->getFaction()) - return false; - } - else - { - if (!IsFriendlyTo(u)) - return false; - } - - // skip non hostile to caster enemy creatures - if (!IsHostileTo(enemy)) - return false; - - return true; -} - -// use this function to avoid having hostile creatures attack -// friendlies and other mobs they shouldn't attack -bool Creature::_IsTargetAcceptable(const Unit *target) const -{ - assert(target); - - // if the target cannot be attacked, the target is not acceptable - if (IsFriendlyTo(target) - || !target->isAttackableByAOE() - || target->hasUnitState(UNIT_STAT_DIED) - || (m_vehicle && (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target)))) - return false; - - const Unit *myVictim = getAttackerForHelper(); - const Unit *targetVictim = target->getAttackerForHelper(); - - // if I'm already fighting target, or I'm hostile towards the target, the target is acceptable - if (myVictim == target || targetVictim == this || IsHostileTo(target)) - return true; - - // if the target's victim is friendly, and the target is neutral, the target is acceptable - if (targetVictim && IsFriendlyTo(targetVictim)) - return true; - - // if the target's victim is not friendly, or the target is friendly, the target is not acceptable - return false; -} - -void Creature::SaveRespawnTime() -{ - if (isSummon() || !m_DBTableGuid || m_creatureData && !m_creatureData->dbData) - return; - - if (m_respawnTime > time(NULL)) // dead (no corpse) - objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime); - else if (m_deathTimer > 0) // dead (corpse) - objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/IN_MILISECONDS); -} - -// this should not be called by petAI or -bool Creature::canCreatureAttack(Unit const *pVictim, bool force) const -{ - if (!pVictim->IsInMap(this)) - return false; - - if (!canAttack(pVictim, force)) - return false; - - if (!pVictim->isInAccessiblePlaceFor(this)) - return false; - - if (IsAIEnabled && !AI()->CanAIAttack(pVictim)) - return false; - - if (sMapStore.LookupEntry(GetMapId())->IsDungeon()) - return true; - - //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick. - float dist = std::max(GetAttackDistance(pVictim), (float)sWorld.getConfig(CONFIG_THREAT_RADIUS)) + m_CombatDistance; - - if (Unit *unit = GetCharmerOrOwner()) - return pVictim->IsWithinDist(unit, dist); - else - return pVictim->IsInDist(&m_homePosition, dist); -} - -CreatureDataAddon const* Creature::GetCreatureAddon() const -{ - if (m_DBTableGuid) - { - if (CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid)) - return addon; - } - - // dependent from difficulty mode entry - return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry); -} - -//creature_addon table -bool Creature::LoadCreaturesAddon(bool reload) -{ - CreatureDataAddon const *cainfo = GetCreatureAddon(); - if (!cainfo) - return false; - - if (cainfo->mount != 0) - Mount(cainfo->mount); - - if (cainfo->bytes1 != 0) - { - // 0 StandState - // 1 FreeTalentPoints Pet only, so always 0 for default creature - // 2 StandFlags - // 3 StandMiscFlags - - SetByteValue(UNIT_FIELD_BYTES_1, 0, uint8(cainfo->bytes1 & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_1, 1, uint8((cainfo->bytes1 >> 8) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_1, 1, 0); - SetByteValue(UNIT_FIELD_BYTES_1, 2, uint8((cainfo->bytes1 >> 16) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_1, 3, uint8((cainfo->bytes1 >> 24) & 0xFF)); - } - - if (cainfo->bytes2 != 0) - { - // 0 SheathState - // 1 Bytes2Flags - // 2 UnitRename Pet only, so always 0 for default creature - // 3 ShapeshiftForm Must be determined/set by shapeshift spell/aura - - SetByteValue(UNIT_FIELD_BYTES_2, 0, uint8(cainfo->bytes2 & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_2, 1, uint8((cainfo->bytes2 >> 8) & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_2, 2, uint8((cainfo->bytes2 >> 16) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_2, 2, 0); - //SetByteValue(UNIT_FIELD_BYTES_2, 3, uint8((cainfo->bytes2 >> 24) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); - } - - if (cainfo->emote != 0) - SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote); - - //Load Path - if (cainfo->path_id != 0) - m_path_id = cainfo->path_id; - - if (cainfo->auras) - { - for (CreatureDataAddonAura const* cAura = cainfo->auras; cAura->spell_id; ++cAura) - { - SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura->spell_id); - if (!AdditionalSpellInfo) - { - sLog.outErrorDb("Creature (GUID: %u Entry: %u) has wrong spell %u defined in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id); - continue; - } - - // skip already applied aura - if (HasAura(cAura->spell_id)) - { - if (!reload) - sLog.outErrorDb("Creature (GUID: %u Entry: %u) has duplicate aura (spell %u) in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id); - - continue; - } - - AddAura(AdditionalSpellInfo, cAura->effectMask, this); - sLog.outDebug("Spell: %u with AuraEffectMask %u added to creature (GUID: %u Entry: %u)", cAura->spell_id, cAura->effectMask,GetGUIDLow(),GetEntry()); - } - } - return true; -} - -/// Send a message to LocalDefense channel for players opposition team in the zone -void Creature::SendZoneUnderAttackMessage(Player* attacker) -{ - uint32 enemy_team = attacker->GetTeam(); - - WorldPacket data(SMSG_ZONE_UNDER_ATTACK,4); - data << (uint32)GetAreaId(); - sWorld.SendGlobalMessage(&data,NULL,(enemy_team == ALLIANCE ? HORDE : ALLIANCE)); -} - -void Creature::SetInCombatWithZone() -{ - if (!CanHaveThreatList()) - { - sLog.outError("Creature entry %u call SetInCombatWithZone but creature cannot have threat list.", GetEntry()); - return; - } - - Map* pMap = GetMap(); - - if (!pMap->IsDungeon()) - { - sLog.outError("Creature entry %u call SetInCombatWithZone for map (id: %u) that isn't an instance.", GetEntry(), pMap->GetId()); - return; - } - - Map::PlayerList const &PlList = pMap->GetPlayers(); - - if (PlList.isEmpty()) - return; - - for (Map::PlayerList::const_iterator i = PlList.begin(); i != PlList.end(); ++i) - { - if (Player* pPlayer = i->getSource()) - { - if (pPlayer->isGameMaster()) - continue; - - if (pPlayer->isAlive()) - { - pPlayer->SetInCombatWith(this); - AddThreat(pPlayer, 0.0f); - } - } - } -} - -void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time) -{ - m_CreatureSpellCooldowns[spell_id] = end_time; -} - -void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time) -{ - m_CreatureCategoryCooldowns[category] = apply_time; -} - -void Creature::AddCreatureSpellCooldown(uint32 spellid) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); - if (!spellInfo) - return; - - uint32 cooldown = GetSpellRecoveryTime(spellInfo); - if (Player *modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellid, SPELLMOD_COOLDOWN, cooldown); - - if (cooldown) - _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILISECONDS); - - if (spellInfo->Category) - _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL)); - - m_GlobalCooldown = spellInfo->StartRecoveryTime; -} - -bool Creature::HasCategoryCooldown(uint32 spell_id) const -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); - if (!spellInfo) - return false; - - // check global cooldown if spell affected by it - if (spellInfo->StartRecoveryCategory > 0 && m_GlobalCooldown > 0) - return true; - - CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category); - return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILISECONDS)) > time(NULL)); -} - -bool Creature::HasSpellCooldown(uint32 spell_id) const -{ - CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id); - return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id); -} - -bool Creature::HasSpell(uint32 spellID) const -{ - uint8 i; - for (i = 0; i < CREATURE_MAX_SPELLS; ++i) - if (spellID == m_spells[i]) - break; - return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells -} - -time_t Creature::GetRespawnTimeEx() const -{ - time_t now = time(NULL); - if (m_respawnTime > now) // dead (no corpse) - return m_respawnTime; - else if (m_deathTimer > 0) // dead (corpse) - return now+m_respawnDelay+m_deathTimer/IN_MILISECONDS; - else - return now; -} - -void Creature::GetRespawnCoord(float &x, float &y, float &z, float* ori, float* dist) const -{ - if (m_DBTableGuid) - { - if (CreatureData const* data = objmgr.GetCreatureData(GetDBTableGUIDLow())) - { - x = data->posX; - y = data->posY; - z = data->posZ; - if (ori) - *ori = data->orientation; - if (dist) - *dist = data->spawndist; - - return; - } - } - - x = GetPositionX(); - y = GetPositionY(); - z = GetPositionZ(); - if (ori) - *ori = GetOrientation(); - if (dist) - *dist = 0; -} - -void Creature::AllLootRemovedFromCorpse() -{ - if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) - { - uint32 nDeathTimer; - - CreatureInfo const *cinfo = GetCreatureInfo(); - - // corpse was not skinnable -> apply corpse looted timer - if (!cinfo || !cinfo->SkinLootId) - nDeathTimer = (uint32)((m_corpseDelay * IN_MILISECONDS) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED)); - // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update - else - nDeathTimer = 0; - - // update death timer only if looted timer is shorter - if (m_deathTimer > nDeathTimer) - m_deathTimer = nDeathTimer; - } -} - -uint8 Creature::getLevelForTarget(Unit const* target) const -{ - if (!isWorldBoss()) - return Unit::getLevelForTarget(target); - - uint16 level = target->getLevel()+sWorld.getConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF); - if (level < 1) - return 1; - if (level > 255) - return 255; - return level; -} - -std::string Creature::GetAIName() const -{ - return ObjectMgr::GetCreatureTemplate(GetEntry())->AIName; -} - -std::string Creature::GetScriptName() const -{ - return objmgr.GetScriptName(GetScriptId()); -} - -uint32 Creature::GetScriptId() const -{ - return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptID; -} - -VendorItemData const* Creature::GetVendorItems() const -{ - return objmgr.GetNpcVendorItemList(GetEntry()); -} - -uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem) -{ - if (!vItem->maxcount) - return vItem->maxcount; - - VendorItemCounts::iterator itr = m_vendorItemCounts.begin(); - for (; itr != m_vendorItemCounts.end(); ++itr) - if (itr->itemId == vItem->item) - break; - - if (itr == m_vendorItemCounts.end()) - return vItem->maxcount; - - VendorItemCount* vCount = &*itr; - - time_t ptime = time(NULL); - - if (vCount->lastIncrementTime + vItem->incrtime <= ptime) - { - ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item); - - uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime); - if ((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount) - { - m_vendorItemCounts.erase(itr); - return vItem->maxcount; - } - - vCount->count += diff * pProto->BuyCount; - vCount->lastIncrementTime = ptime; - } - - return vCount->count; -} - -uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count) -{ - if (!vItem->maxcount) - return 0; - - VendorItemCounts::iterator itr = m_vendorItemCounts.begin(); - for (; itr != m_vendorItemCounts.end(); ++itr) - if (itr->itemId == vItem->item) - break; - - if (itr == m_vendorItemCounts.end()) - { - int32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0; - m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count)); - return new_count; - } - - VendorItemCount* vCount = &*itr; - - time_t ptime = time(NULL); - - if (vCount->lastIncrementTime + vItem->incrtime <= ptime) - { - ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item); - - uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime); - if ((vCount->count + diff * pProto->BuyCount) < vItem->maxcount) - vCount->count += diff * pProto->BuyCount; - else - vCount->count = vItem->maxcount; - } - - vCount->count = vCount->count > used_count ? vCount->count-used_count : 0; - vCount->lastIncrementTime = ptime; - return vCount->count; -} - -TrainerSpellData const* Creature::GetTrainerSpells() const -{ - return objmgr.GetNpcTrainerSpells(GetEntry()); -} - -// overwrite WorldObject function for proper name localization -const char* Creature::GetNameForLocaleIdx(int32 loc_idx) const -{ - if (loc_idx >= 0) - { - CreatureLocale const *cl = objmgr.GetCreatureLocale(GetEntry()); - if (cl) - { - if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty()) - return cl->Name[loc_idx].c_str(); - } - } - - return GetName(); -} - -const CreatureData* Creature::GetLinkedRespawnCreatureData() const -{ - if (!m_DBTableGuid) // only hard-spawned creatures from DB can have a linked master - return NULL; - - if (uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid)) - return objmgr.GetCreatureData(targetGuid); - - return NULL; -} - -// returns master's remaining respawn time if any -time_t Creature::GetLinkedCreatureRespawnTime() const -{ - if (!m_DBTableGuid) // only hard-spawned creatures from DB can have a linked master - return 0; - - if (uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid)) - { - Map* targetMap = NULL; - if (const CreatureData* data = objmgr.GetCreatureData(targetGuid)) - { - if (data->mapid == GetMapId()) // look up on the same map - targetMap = GetMap(); - else // it shouldn't be instanceable map here - targetMap = MapManager::Instance().FindMap(data->mapid); - } - if (targetMap) - return objmgr.GetCreatureRespawnTime(targetGuid,targetMap->GetInstanceId()); - } - - return 0; -} diff --git a/src/server/game/Creature.h b/src/server/game/Creature.h deleted file mode 100644 index d2d93d787da..00000000000 --- a/src/server/game/Creature.h +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITYCORE_CREATURE_H -#define TRINITYCORE_CREATURE_H - -#include "Common.h" -#include "Unit.h" -#include "UpdateMask.h" -#include "ItemPrototype.h" -#include "LootMgr.h" -#include "Database/DatabaseEnv.h" -#include "Cell.h" - -#include - -struct SpellEntry; - -class CreatureAI; -class Quest; -class Player; -class WorldSession; -class CreatureGroup; - -enum CreatureFlagsExtra -{ - CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group - CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility) - CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry - CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry - CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block - CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks - CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP - CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature - CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me - CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) - //CREATURE_FLAG_EXTRA_CHARM_AI = 0x00008000, // use ai when charmed - CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes - CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills - CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre - CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // Creature is subject to all diminishing returns as player are -}; - -// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform -#if defined(__GNUC__) -#pragma pack(1) -#else -#pragma pack(push,1) -#endif - -#define MAX_KILL_CREDIT 2 -#define CREATURE_REGEN_INTERVAL 2 * IN_MILISECONDS - -// from `creature_template` table -struct CreatureInfo -{ - uint32 Entry; - uint32 DifficultyEntry[MAX_DIFFICULTY - 1]; - uint32 KillCredit[MAX_KILL_CREDIT]; - uint32 Modelid1; - uint32 Modelid2; - uint32 Modelid3; - uint32 Modelid4; - char* Name; - char* SubName; - char* IconName; - uint32 GossipMenuId; - uint8 minlevel; - uint8 maxlevel; - uint32 expansion; - uint32 faction_A; - uint32 faction_H; - uint32 npcflag; - float speed_walk; - float speed_run; - float scale; - uint32 rank; - float mindmg; - float maxdmg; - uint32 dmgschool; - uint32 attackpower; - float dmg_multiplier; - uint32 baseattacktime; - uint32 rangeattacktime; - uint32 unit_class; // enum Classes. Note only 4 classes are known for creatures. - uint32 unit_flags; // enum UnitFlags mask values - uint32 dynamicflags; - uint32 family; // enum CreatureFamily values (optional) - uint32 trainer_type; - uint32 trainer_spell; - uint32 trainer_class; - uint32 trainer_race; - float minrangedmg; - float maxrangedmg; - uint32 rangedattackpower; - uint32 type; // enum CreatureType values - uint32 type_flags; // enum CreatureTypeFlags mask values - uint32 lootid; - uint32 pickpocketLootId; - uint32 SkinLootId; - int32 resistance1; - int32 resistance2; - int32 resistance3; - int32 resistance4; - int32 resistance5; - int32 resistance6; - uint32 spells[CREATURE_MAX_SPELLS]; - uint32 PetSpellDataId; - uint32 VehicleId; - uint32 mingold; - uint32 maxgold; - char const* AIName; - uint32 MovementType; - uint32 InhabitType; - float ModHealth; - float ModMana; - float ModArmor; - bool RacialLeader; - uint32 questItems[6]; - uint32 movementId; - bool RegenHealth; - uint32 equipmentId; - uint32 MechanicImmuneMask; - uint32 flags_extra; - uint32 ScriptID; - uint32 GetRandomValidModelId() const; - uint32 GetFirstValidModelId() const; - - // helpers - SkillType GetRequiredLootSkill() const - { - if (type_flags & CREATURE_TYPEFLAGS_HERBLOOT) - return SKILL_HERBALISM; - else if (type_flags & CREATURE_TYPEFLAGS_MININGLOOT) - return SKILL_MINING; - else if (type_flags & CREATURE_TYPEFLAGS_ENGINEERLOOT) - return SKILL_ENGINERING; - else - return SKILL_SKINNING; // normal case - } - - bool isTameable(bool exotic) const - { - if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPEFLAGS_TAMEABLE) == 0) - return false; - - // if can tame exotic then can tame any temable - return exotic || (type_flags & CREATURE_TYPEFLAGS_EXOTIC) == 0; - } -}; - -// Represents max amount of expansions. -// TODO: Add MAX_EXPANSION constant. -#define MAX_CREATURE_BASE_HP 3 - -// Defines base stats for creatures (used to calculate HP/mana/armor). -struct CreatureBaseStats -{ - uint32 BaseHealth[MAX_CREATURE_BASE_HP]; - uint32 BaseMana; - uint32 BaseArmor; - - // Helpers - - uint32 GenerateHealth(CreatureInfo const* info) const - { - return uint32((BaseHealth[info->expansion] * info->ModHealth) + 0.5f); - } - - uint32 GenerateMana(CreatureInfo const* info) const - { - // Mana can be 0. - if (!BaseMana) - return 0; - - return uint32((BaseMana * info->ModMana) + 0.5f); - } - - uint32 GenerateArmor(CreatureInfo const* info) const - { - return uint32((BaseArmor * info->ModArmor) + 0.5f); - } - - static CreatureBaseStats const* GetBaseStats(uint8 level, uint8 unitClass); -}; - -typedef UNORDERED_MAP CreatureBaseStatsMap; - -struct CreatureLocale -{ - std::vector Name; - std::vector SubName; -}; - -struct GossipMenuItemsLocale -{ - std::vector OptionText; - std::vector BoxText; -}; - -struct PointOfInterestLocale -{ - std::vector IconName; -}; - -struct EquipmentInfo -{ - uint32 entry; - uint32 equipentry[3]; -}; - -// from `creature` table -struct CreatureData -{ - explicit CreatureData() : dbData(true) {} - uint32 id; // entry in creature_template - uint16 mapid; - uint16 phaseMask; - uint32 displayid; - int32 equipmentId; - float posX; - float posY; - float posZ; - float orientation; - uint32 spawntimesecs; - float spawndist; - uint32 currentwaypoint; - uint32 curhealth; - uint32 curmana; - bool is_dead; - uint8 movementType; - uint8 spawnMask; - bool dbData; -}; - -struct CreatureDataAddonAura -{ - uint32 spell_id; - uint8 effectMask; -}; - -// from `creature_addon` table -struct CreatureDataAddon -{ - uint32 guidOrEntry; - uint32 path_id; - uint32 mount; - uint32 bytes1; - uint32 bytes2; - uint32 emote; - CreatureDataAddonAura const* auras; // loaded as char* "spell1 eff1 spell2 eff2 ... " -}; - -struct CreatureModelInfo -{ - uint32 modelid; - float bounding_radius; - float combat_reach; - uint8 gender; - uint32 modelid_other_gender; -}; - -enum InhabitTypeValues -{ - INHABIT_GROUND = 1, - INHABIT_WATER = 2, - INHABIT_AIR = 4, - INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR -}; - -// Enums used by StringTextData::Type (CreatureEventAI) -enum ChatType -{ - CHAT_TYPE_SAY = 0, - CHAT_TYPE_YELL = 1, - CHAT_TYPE_TEXT_EMOTE = 2, - CHAT_TYPE_BOSS_EMOTE = 3, - CHAT_TYPE_WHISPER = 4, - CHAT_TYPE_BOSS_WHISPER = 5, - CHAT_TYPE_ZONE_YELL = 6 -}; - -// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform -#if defined(__GNUC__) -#pragma pack() -#else -#pragma pack(pop) -#endif - -// Vendors -struct VendorItem -{ - VendorItem(uint32 _item, int32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost) - : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {} - - uint32 item; - int32 maxcount; // 0 for infinity item amount - uint32 incrtime; // time for restore items amount if maxcount != 0 - uint32 ExtendedCost; -}; -typedef std::vector VendorItemList; - -struct VendorItemData -{ - VendorItemList m_items; - - VendorItem* GetItem(uint32 slot) const - { - if (slot >= m_items.size()) return NULL; - return m_items[slot]; - } - bool Empty() const { return m_items.empty(); } - uint8 GetItemCount() const { return m_items.size(); } - void AddItem(uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost) - { - m_items.push_back(new VendorItem(item, maxcount, ptime, ExtendedCost)); - } - bool RemoveItem(uint32 item_id); - VendorItem const* FindItemCostPair(uint32 item_id, uint32 extendedCost) const; - void Clear() - { - for (VendorItemList::const_iterator itr = m_items.begin(); itr != m_items.end(); ++itr) - delete (*itr); - m_items.clear(); - } -}; - -struct VendorItemCount -{ - explicit VendorItemCount(uint32 _item, uint32 _count) - : itemId(_item), count(_count), lastIncrementTime(time(NULL)) {} - - uint32 itemId; - uint32 count; - time_t lastIncrementTime; -}; - -typedef std::list VendorItemCounts; - -struct TrainerSpell -{ - TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0) - { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i) - learnedSpell[i] = 0; - } - - uint32 spell; - uint32 spellCost; - uint32 reqSkill; - uint32 reqSkillValue; - uint32 reqLevel; - uint32 learnedSpell[3]; - - // helpers - bool IsCastable() const { return learnedSpell[0] != spell; } -}; - -typedef UNORDERED_MAP TrainerSpellMap; - -struct TrainerSpellData -{ - TrainerSpellData() : trainerType(0) {} - - TrainerSpellMap spellList; - uint32 trainerType; // trainer type based at trainer spells, can be different from creature_template value. - // req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2. - TrainerSpell const* Find(uint32 spell_id) const; - void Clear() { spellList.clear(); } -}; - -typedef std::map CreatureSpellCooldowns; - -// max different by z coordinate for creature aggro reaction -#define CREATURE_Z_ATTACK_RANGE 3 - -#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY - -class Creature : public Unit, public GridObject -{ - public: - - explicit Creature(); - virtual ~Creature(); - - void AddToWorld(); - void RemoveFromWorld(); - - void DisappearAndDie(); - - bool Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 vehId, uint32 team, float x, float y, float z, float ang, const CreatureData *data = NULL); - bool LoadCreaturesAddon(bool reload = false); - void SelectLevel(const CreatureInfo *cinfo); - void LoadEquipment(uint32 equip_entry, bool force=false); - - uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; } - char const* GetSubName() const { return GetCreatureInfo()->SubName; } - - void Update(uint32 time); // overwrited Unit::Update - void GetRespawnCoord(float &x, float &y, float &z, float* ori = NULL, float* dist =NULL) const; - uint32 GetEquipmentId() const { return GetCreatureInfo()->equipmentId; } - - void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } - bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; } - bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; } - bool isTrigger() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER; } - bool canWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; } - bool canSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; } - //bool canFly() const { return GetCreatureInfo()->InhabitType & INHABIT_AIR; } - void SetReactState(ReactStates st) { m_reactState = st; } - ReactStates GetReactState() { return m_reactState; } - bool HasReactState(ReactStates state) const { return (m_reactState == state); } - ///// TODO RENAME THIS!!!!! - bool isCanTrainingOf(Player* player, bool msg) const; - bool isCanInteractWithBattleMaster(Player* player, bool msg) const; - bool isCanTrainingAndResetTalentsOf(Player* pPlayer) const; - bool canCreatureAttack(Unit const *pVictim, bool force = true) const; - bool IsImmunedToSpell(SpellEntry const* spellInfo); - // redefine Unit::IsImmunedToSpell - bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const; - // redefine Unit::IsImmunedToSpellEffect - bool isElite() const - { - if (isPet()) - return false; - - uint32 rank = GetCreatureInfo()->rank; - return rank != CREATURE_ELITE_NORMAL && rank != CREATURE_ELITE_RARE; - } - - bool isWorldBoss() const - { - if (isPet()) - return false; - - return GetCreatureInfo()->rank == CREATURE_ELITE_WORLDBOSS; - } - - uint8 getLevelForTarget(Unit const* target) const; // overwrite Unit::getLevelForTarget for boss level support - - bool IsInEvadeMode() const { return hasUnitState(UNIT_STAT_EVADE); } - - bool AIM_Initialize(CreatureAI* ai = NULL); - void Motion_Initialize(); - - void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type); - CreatureAI * AI() const { return (CreatureAI*)i_AI; } - - uint32 GetShieldBlockValue() const //dunno mob block value - { - return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20)); - } - - SpellSchoolMask GetMeleeDamageSchoolMask() const { return m_meleeDamageSchoolMask; } - void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); } - - void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time); - void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time); - void AddCreatureSpellCooldown(uint32 spellid); - bool HasSpellCooldown(uint32 spell_id) const; - bool HasCategoryCooldown(uint32 spell_id) const; - - bool HasSpell(uint32 spellID) const; - - bool UpdateEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL); - bool UpdateStats(Stats stat); - bool UpdateAllStats(); - void UpdateResistances(uint32 school); - void UpdateArmor(); - void UpdateMaxHealth(); - void UpdateMaxPower(Powers power); - void UpdateAttackPowerAndDamage(bool ranged = false); - void UpdateDamagePhysical(WeaponAttackType attType); - uint32 GetCurrentEquipmentId() { return m_equipmentId; } - float GetSpellDamageMod(int32 Rank); - - VendorItemData const* GetVendorItems() const; - uint32 GetVendorItemCurrentCount(VendorItem const* vItem); - uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count); - - TrainerSpellData const* GetTrainerSpells() const; - - CreatureInfo const *GetCreatureInfo() const { return m_creatureInfo; } - CreatureData const *GetCreatureData() const { return m_creatureData; } - CreatureDataAddon const* GetCreatureAddon() const; - - std::string GetAIName() const; - std::string GetScriptName() const; - uint32 GetScriptId() const; - - void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } - void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } - void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); } - void Whisper(int32 textId, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(textId,receiver,IsBossWhisper); } - void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); } - - // overwrite WorldObject function for proper name localization - const char* GetNameForLocaleIdx(int32 locale_idx) const; - - void setDeathState(DeathState s); // overwrite virtual Unit::setDeathState - bool FallGround(); - - bool LoadFromDB(uint32 guid, Map *map); - void SaveToDB(); - // overwrited in Pet - virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); - virtual void DeleteFromDB(); // overwrited in Pet - - Loot loot; - bool lootForPickPocketed; - bool lootForBody; - Player *GetLootRecipient() const; - bool hasLootRecipient() const { return m_lootRecipient != 0; } - bool isTappedBy(Player *player) const; // return true if the creature is tapped by the player or a member of his party. - - void SetLootRecipient (Unit* unit); - void AllLootRemovedFromCorpse(); - - uint16 GetLootMode() { return m_LootMode; } - bool HasLootMode(uint16 lootMode) { return m_LootMode & lootMode; } - void SetLootMode(uint16 lootMode) { m_LootMode = lootMode; } - void AddLootMode(uint16 lootMode) { m_LootMode |= lootMode; } - void RemoveLootMode(uint16 lootMode) { m_LootMode &= ~lootMode; } - void ResetLootMode() { m_LootMode = LOOT_MODE_DEFAULT; } - - SpellEntry const *reachWithSpellAttack(Unit *pVictim); - SpellEntry const *reachWithSpellCure(Unit *pVictim); - - uint32 m_spells[CREATURE_MAX_SPELLS]; - CreatureSpellCooldowns m_CreatureSpellCooldowns; - CreatureSpellCooldowns m_CreatureCategoryCooldowns; - uint32 m_GlobalCooldown; - - bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const; - bool canStartAttack(Unit const* u, bool force) const; - float GetAttackDistance(Unit const* pl) const; - - void SendAIReaction(AiReaction reactionType); - - Unit* SelectNearestTarget(float dist = 0) const; - Unit* SelectNearestTargetInAttackDistance(float dist = 0) const; - - void DoFleeToGetAssistance(); - void CallForHelp(float fRadius); - void CallAssistance(); - void SetNoCallAssistance(bool val) { m_AlreadyCallAssistance = val; } - void SetNoSearchAssistance(bool val) { m_AlreadySearchedAssistance = val; } - bool HasSearchedAssistance() { return m_AlreadySearchedAssistance; } - bool CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction = true) const; - bool _IsTargetAcceptable(const Unit* target) const; - - MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; } - void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; } - - // for use only in LoadHelper, Map::Add Map::CreatureCellRelocation - Cell const& GetCurrentCell() const { return m_currentCell; } - void SetCurrentCell(Cell const& cell) { m_currentCell = cell; } - - bool IsVisibleInGridForPlayer(Player const* pl) const; - - void RemoveCorpse(); - bool isDeadByDefault() const { return m_isDeadByDefault; }; - - void ForcedDespawn(uint32 timeMSToDespawn = 0); - - time_t const& GetRespawnTime() const { return m_respawnTime; } - time_t GetRespawnTimeEx() const; - void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(NULL) + respawn : 0; } - void Respawn(bool force = false); - void SaveRespawnTime(); - - uint32 GetRespawnDelay() const { return m_respawnDelay; } - void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; } - - float GetRespawnRadius() const { return m_respawnradius; } - void SetRespawnRadius(float dist) { m_respawnradius = dist; } - - // Linked Creature Respawning System - time_t GetLinkedCreatureRespawnTime() const; - const CreatureData* GetLinkedRespawnCreatureData() const; - - uint32 m_groupLootTimer; // (msecs)timer used for group loot - uint64 lootingGroupGUID; // used to find group which is looting corpse - - void SendZoneUnderAttackMessage(Player* attacker); - - void SetInCombatWithZone(); - - bool hasQuest(uint32 quest_id) const; - bool hasInvolvedQuest(uint32 quest_id) const; - - bool isRegeneratingHealth() { return m_regenHealth; } - virtual uint8 GetPetAutoSpellSize() const { return CREATURE_MAX_SPELLS; } - virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const - { - if (pos >= CREATURE_MAX_SPELLS || m_charmInfo->GetCharmSpell(pos)->GetType() != ACT_ENABLED) - return 0; - else - return m_charmInfo->GetCharmSpell(pos)->GetAction(); - } - - void SetHomePosition(float x, float y, float z, float o) { m_homePosition.Relocate(x, y, z, o); } - void SetHomePosition(const Position &pos) { m_homePosition.Relocate(pos); } - void GetHomePosition(float &x, float &y, float &z, float &ori) { m_homePosition.GetPosition(x, y, z, ori); } - Position GetHomePosition() { return m_homePosition; } - - uint32 GetGlobalCooldown() const { return m_GlobalCooldown; } - - uint32 GetWaypointPath(){return m_path_id;} - void LoadPath(uint32 pathid) { m_path_id = pathid; } - - uint32 GetCurrentWaypointID(){return m_waypointID;} - void UpdateWaypointID(uint32 wpID){m_waypointID = wpID;} - - void SearchFormation(); - CreatureGroup *GetFormation() {return m_formation;} - void SetFormation(CreatureGroup *formation) {m_formation = formation;} - - Unit *SelectVictim(); - void SetDeadByDefault (bool death_state) {m_isDeadByDefault = death_state;} - - void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; } - bool IsReputationGainDisabled() { return DisableReputationGain; } - bool IsDamageEnoughForLootingAndReward() const { return m_PlayerDamageReq == 0; } - void LowerPlayerDamageReq(uint32 unDamage) - { - if (m_PlayerDamageReq) - m_PlayerDamageReq > unDamage ? m_PlayerDamageReq -= unDamage : m_PlayerDamageReq = 0; - } - void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; } - uint32 m_PlayerDamageReq; - - void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; } - - static float _GetDamageMod(int32 Rank); - - float m_SightDistance, m_CombatDistance; - - protected: - bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData *data = NULL); - bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL); - - // vendor items - VendorItemCounts m_vendorItemCounts; - - void _RealtimeSetCreatureInfo(); - - static float _GetHealthMod(int32 Rank); - - uint32 m_lootMoney; - uint64 m_lootRecipient; - - /// Timers - uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance - time_t m_respawnTime; // (secs) time of next respawn - uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning - uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance - float m_respawnradius; - - ReactStates m_reactState; // for AI, not charmInfo - void RegenerateMana(); - void RegenerateHealth(); - void Regenerate(Powers power); - MovementGeneratorType m_defaultMovementType; - Cell m_currentCell; // store current cell where creature listed - uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid - uint32 m_equipmentId; - - bool m_AlreadyCallAssistance; - bool m_AlreadySearchedAssistance; - bool m_regenHealth; - bool m_AI_locked; - bool m_isDeadByDefault; - - SpellSchoolMask m_meleeDamageSchoolMask; - uint32 m_originalEntry; - - Position m_homePosition; - - bool DisableReputationGain; - - CreatureInfo const* m_creatureInfo; // in difficulty mode > 0 can different from ObjMgr::GetCreatureTemplate(GetEntry()) - CreatureData const* m_creatureData; - - uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable - - private: - //WaypointMovementGenerator vars - uint32 m_waypointID; - uint32 m_path_id; - - //Formation var - CreatureGroup *m_formation; -}; - -class AssistDelayEvent : public BasicEvent -{ - public: - AssistDelayEvent(const uint64& victim, Unit& owner) : BasicEvent(), m_victim(victim), m_owner(owner) { } - - bool Execute(uint64 e_time, uint32 p_time); - void AddAssistant(const uint64& guid) { m_assistants.push_back(guid); } - private: - AssistDelayEvent(); - - uint64 m_victim; - std::list m_assistants; - Unit& m_owner; -}; - -class ForcedDespawnDelayEvent : public BasicEvent -{ - public: - ForcedDespawnDelayEvent(Creature& owner) : BasicEvent(), m_owner(owner) { } - bool Execute(uint64 e_time, uint32 p_time); - - private: - Creature& m_owner; -}; - -#endif diff --git a/src/server/game/CreatureAI.cpp b/src/server/game/CreatureAI.cpp deleted file mode 100644 index fb8f37ae492..00000000000 --- a/src/server/game/CreatureAI.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "CreatureAI.h" -#include "CreatureAIImpl.h" -#include "Creature.h" -#include "World.h" -#include "SpellMgr.h" -#include "Vehicle.h" - -//Disable CreatureAI when charmed -void CreatureAI::OnCharmed(bool /*apply*/) -{ - //me->IsAIEnabled = !apply;*/ - me->NeedChangeAI = true; - me->IsAIEnabled = false; -} - -AISpellInfoType * UnitAI::AISpellInfo; - AISpellInfoType * GetAISpellInfo(uint32 i) { return &CreatureAI::AISpellInfo[i]; } - -void CreatureAI::DoZoneInCombat(Creature* creature) -{ - if (!creature) - creature = me; - - if (!creature->CanHaveThreatList()) - return; - - Map *map = creature->GetMap(); - if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated - { - sLog.outError("DoZoneInCombat call for map that isn't an instance (creature entry = %d)", creature->GetTypeId() == TYPEID_UNIT ? creature->ToCreature()->GetEntry() : 0); - return; - } - - if (!creature->HasReactState(REACT_PASSIVE) && !creature->getVictim()) - { - if (Unit *target = creature->SelectNearestTarget(50)) - creature->AI()->AttackStart(target); - else if (creature->isSummon()) - { - if (Unit *summoner = creature->ToTempSummon()->GetSummoner()) - { - Unit *target = summoner->getAttackerForHelper(); - if (!target && summoner->CanHaveThreatList() && !summoner->getThreatManager().isThreatListEmpty()) - target = summoner->getThreatManager().getHostilTarget(); - if (target && (creature->IsFriendlyTo(summoner) || creature->IsHostileTo(target))) - creature->AI()->AttackStart(target); - } - } - } - - if (!creature->HasReactState(REACT_PASSIVE) && !creature->getVictim()) - { - sLog.outError("DoZoneInCombat called for creature that has empty threat list (creature entry = %u)", creature->GetEntry()); - return; - } - - Map::PlayerList const &PlList = map->GetPlayers(); - - if (PlList.isEmpty()) - return; - - for (Map::PlayerList::const_iterator i = PlList.begin(); i != PlList.end(); ++i) - { - if (Player* pPlayer = i->getSource()) - { - if (pPlayer->isGameMaster()) - continue; - - if (pPlayer->isAlive()) - { - creature->SetInCombatWith(pPlayer); - pPlayer->SetInCombatWith(creature); - creature->AddThreat(pPlayer, 0.0f); - } - - /* Causes certain things to never leave the threat list (Priest Lightwell, etc): - for (Unit::ControlList::const_iterator itr = pPlayer->m_Controlled.begin(); itr != pPlayer->m_Controlled.end(); ++itr) - { - creature->SetInCombatWith(*itr); - (*itr)->SetInCombatWith(creature); - creature->AddThreat(*itr, 0.0f); - }*/ - } - } -} - -// scripts does not take care about MoveInLineOfSight loops -// MoveInLineOfSight can be called inside another MoveInLineOfSight and cause stack overflow -void CreatureAI::MoveInLineOfSight_Safe(Unit *who) -{ - if (m_MoveInLineOfSight_locked == true) - return; - m_MoveInLineOfSight_locked = true; - MoveInLineOfSight(who); - m_MoveInLineOfSight_locked = false; -} - -void CreatureAI::MoveInLineOfSight(Unit *who) -{ - if (me->getVictim()) - return; - - if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) // non-combat pets should just stand there and look good;) - return; - - if (me->canStartAttack(who, false)) - AttackStart(who); - //else if (who->getVictim() && me->IsFriendlyTo(who) - // && me->IsWithinDistInMap(who, sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)) - // && me->canStartAttack(who->getVictim(), true)) // TODO: if we use true, it will not attack it when it arrives - // me->GetMotionMaster()->MoveChase(who->getVictim()); -} - -void CreatureAI::EnterEvadeMode() -{ - if (!_EnterEvadeMode()) - return; - - sLog.outDebug("Creature %u enters evade mode.", me->GetEntry()); - - if (!me->GetVehicle()) // otherwise me will be in evade mode forever - { - if (Unit *owner = me->GetCharmerOrOwner()) - { - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); - } - else - me->GetMotionMaster()->MoveTargetedHome(); - } - - Reset(); - - if (me->IsVehicle()) // use the same sequence of addtoworld, aireset may remove all summons! - me->GetVehicleKit()->Reset(); -} - -/*void CreatureAI::AttackedBy(Unit* attacker) -{ - if (!me->getVictim()) - AttackStart(attacker); -}*/ diff --git a/src/server/game/CreatureAI.h b/src/server/game/CreatureAI.h deleted file mode 100644 index c03d3dd09d0..00000000000 --- a/src/server/game/CreatureAI.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CREATUREAI_H -#define TRINITY_CREATUREAI_H - -#include "UnitAI.h" -#include "Common.h" - -class WorldObject; -class Unit; -class Creature; -class Player; -struct SpellEntry; - -#define TIME_INTERVAL_LOOK 5000 -#define VISIBILITY_RANGE 10000 - -//Spell targets used by SelectSpell -enum SelectTargetType -{ - SELECT_TARGET_DONTCARE = 0, //All target types allowed - - SELECT_TARGET_SELF, //Only Self casting - - SELECT_TARGET_SINGLE_ENEMY, //Only Single Enemy - SELECT_TARGET_AOE_ENEMY, //Only AoE Enemy - SELECT_TARGET_ANY_ENEMY, //AoE or Single Enemy - - SELECT_TARGET_SINGLE_FRIEND, //Only Single Friend - SELECT_TARGET_AOE_FRIEND, //Only AoE Friend - SELECT_TARGET_ANY_FRIEND, //AoE or Single Friend -}; - -//Spell Effects used by SelectSpell -enum SelectEffect -{ - SELECT_EFFECT_DONTCARE = 0, //All spell effects allowed - SELECT_EFFECT_DAMAGE, //Spell does damage - SELECT_EFFECT_HEALING, //Spell does healing - SELECT_EFFECT_AURA, //Spell applies an aura -}; - -enum SCEquip -{ - EQUIP_NO_CHANGE = -1, - EQUIP_UNEQUIP = 0 -}; - -class CreatureAI : public UnitAI -{ - protected: - Creature * const me; - - bool UpdateVictim(); - bool UpdateVictimWithGaze(); - bool UpdateCombatState(); - - void SetGazeOn(Unit *target); - - Creature *DoSummon(uint32 uiEntry, const Position &pos, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); - Creature *DoSummon(uint32 uiEntry, WorldObject *obj, float fRadius = 5.0f, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); - Creature *DoSummonFlyer(uint32 uiEntry, WorldObject *obj, float fZ, float fRadius = 5.0f, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); - - public: - explicit CreatureAI(Creature *c) : UnitAI((Unit*)c), me(c), m_MoveInLineOfSight_locked(false) {} - - virtual ~CreatureAI() {} - - /// == Reactions At ================================= - - // Called if IsVisible(Unit *who) is true at each *who move, reaction at visibility zone enter - void MoveInLineOfSight_Safe(Unit *who); - - // Called for reaction at stopping attack at no attackers or targets - virtual void EnterEvadeMode(); - - // Called for reaction at enter to combat if not in combat yet (enemy can be NULL) - virtual void EnterCombat(Unit* /*enemy*/) {} - - // Called at any Damage from any attacker (before damage apply) - // Note: it for recalculation damage or special reaction at damage - // for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also - virtual void DamageTaken(Unit * /*done_by*/, uint32 & /*damage*/) {} - - // Called when the creature is killed - virtual void JustDied(Unit *) {} - - // Called when the creature kills a unit - virtual void KilledUnit(Unit *) {} - - // Called when the creature summon successfully other creature - virtual void JustSummoned(Creature*) {} - virtual void IsSummonedBy(Unit * /*summoner*/) {} - - virtual void SummonedCreatureDespawn(Creature* /*unit*/) {} - - // Called when hit by a spell - virtual void SpellHit(Unit*, const SpellEntry*) {} - - // Called when spell hits a target - virtual void SpellHitTarget(Unit* /*target*/, const SpellEntry*) {} - - // Called to get trigger target for aura effect - virtual Unit * GetAuraEffectTriggerTarget(uint32 /*spellId*/, uint8 /*effIndex*/) {return NULL;} - - // Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc) - //virtual void AttackedBy(Unit* attacker); - virtual bool IsEscorted() { return false; } - - // Called when creature is spawned or respawned (for reseting variables) - virtual void JustRespawned() { Reset(); } - - // Called at waypoint reached or point movement finished - virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {} - - void OnCharmed(bool apply); - - //virtual void SpellClick(Player *player) {} - - // Called at reaching home after evade - virtual void JustReachedHome() {} - - void DoZoneInCombat(Creature* pUnit = NULL); - - // Called at text emote receive from player - virtual void ReceiveEmote(Player* /*pPlayer*/, uint32 /*text_emote*/) {} - - /// == Triggered Actions Requested ================== - - // Called when creature attack expected (if creature can and no have current victim) - // Note: for reaction at hostile action must be called AttackedBy function. - //virtual void AttackStart(Unit *) {} - - // Called at World update tick - //virtual void UpdateAI(const uint32 /*diff*/) {} - - /// == State checks ================================= - - // Is unit visible for MoveInLineOfSight - //virtual bool IsVisible(Unit *) const { return false; } - - // called when the corpse of this creature gets removed - virtual void CorpseRemoved(uint32 & /*respawnDelay*/) {} - - // Called when victim entered water and creature can not enter water - //virtual bool canReachByRangeAttack(Unit*) { return false; } - - /// == Fields ======================================= - - // Pointer to controlled by AI creature - //Creature* const me; - - virtual void PassengerBoarded(Unit * /*who*/, int8 /*seatId*/, bool /*apply*/) {} - - protected: - virtual void MoveInLineOfSight(Unit *); - - bool _EnterEvadeMode(); - - private: - bool m_MoveInLineOfSight_locked; -}; - -enum Permitions -{ - PERMIT_BASE_NO = -1, - PERMIT_BASE_IDLE = 1, - PERMIT_BASE_REACTIVE = 100, - PERMIT_BASE_PROACTIVE = 200, - PERMIT_BASE_FACTION_SPECIFIC = 400, - PERMIT_BASE_SPECIAL = 800 -}; - -#endif diff --git a/src/server/game/CreatureAIFactory.h b/src/server/game/CreatureAIFactory.h deleted file mode 100644 index 6aa69eaaa29..00000000000 --- a/src/server/game/CreatureAIFactory.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef TRINITY_CREATUREAIFACTORY_H -#define TRINITY_CREATUREAIFACTORY_H - -//#include "Policies/Singleton.h" -#include "Dynamic/ObjectRegistry.h" -#include "Dynamic/FactoryHolder.h" - -struct SelectableAI : public FactoryHolder, public Permissible -{ - SelectableAI(const char *id) : FactoryHolder(id) {} -}; - -template -struct CreatureAIFactory : public SelectableAI -{ - CreatureAIFactory(const char *name) : SelectableAI(name) {} - - CreatureAI* Create(void *) const; - - int Permit(const Creature *c) const { return REAL_AI::Permissible(c); } -}; - -template -inline CreatureAI* -CreatureAIFactory::Create(void *data) const -{ - Creature* creature = reinterpret_cast(data); - return (new REAL_AI(creature)); -} - -typedef FactoryHolder CreatureAICreator; -typedef FactoryHolder::FactoryHolderRegistry CreatureAIRegistry; -typedef FactoryHolder::FactoryHolderRepository CreatureAIRepository; -#endif diff --git a/src/server/game/CreatureAIImpl.h b/src/server/game/CreatureAIImpl.h deleted file mode 100644 index d2f96ffcabc..00000000000 --- a/src/server/game/CreatureAIImpl.h +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef CREATUREAIIMPL_H -#define CREATUREAIIMPL_H - -#include "Common.h" -#include "Platform/Define.h" -#include "TemporarySummon.h" -#include "CreatureAI.h" -#include "SpellMgr.h" - -template -inline -const T& RAND(const T& v1, const T& v2) -{ - return (urand(0,1)) ? v1 : v2; -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3) -{ - switch (urand(0,2)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4) -{ - switch (urand(0,3)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) -{ - switch (urand(0,4)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6) -{ - switch (urand(0,5)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7) -{ - switch (urand(0,6)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8) -{ - switch (urand(0,7)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9) -{ - switch (urand(0,8)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10) -{ - switch (urand(0,9)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11) -{ - switch (urand(0,10)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12) -{ - switch (urand(0,11)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13) -{ - switch (urand(0,12)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14) -{ - switch (urand(0,13)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - case 13: return v14; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15) -{ - switch (urand(0,14)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - case 13: return v14; - case 14: return v15; - } -} - -template -inline -const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8, - const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15, const T& v16) -{ - switch (urand(0,15)) - { - default: - case 0: return v1; - case 1: return v2; - case 2: return v3; - case 3: return v4; - case 4: return v5; - case 5: return v6; - case 6: return v7; - case 7: return v8; - case 8: return v9; - case 9: return v10; - case 10: return v11; - case 11: return v12; - case 12: return v13; - case 13: return v14; - case 14: return v15; - case 15: return v16; - } -} - -class EventMap : private std::map -{ - private: - uint32 m_time, m_phase; - public: - explicit EventMap() : m_phase(0), m_time(0) {} - - uint32 GetTimer() const { return m_time; } - - void Reset() { clear(); m_time = 0; m_phase = 0; } - - void Update(uint32 time) { m_time += time; } - - void SetPhase(uint32 phase) - { - if (phase && phase < 9) - m_phase = (1 << (phase + 24)); - } - - void ScheduleEvent(uint32 eventId, uint32 time, uint32 gcd = 0, uint32 phase = 0) - { - time += m_time; - if (gcd && gcd < 9) - eventId |= (1 << (gcd + 16)); - if (phase && phase < 9) - eventId |= (1 << (phase + 24)); - iterator itr = find(time); - while (itr != end()) - { - ++time; - itr = find(time); - } - insert(std::make_pair(time, eventId)); - } - - void RescheduleEvent(uint32 eventId, uint32 time, uint32 gcd = 0, uint32 phase = 0) - { - CancelEvent(eventId); - ScheduleEvent(eventId, time, gcd, phase); - } - - void RepeatEvent(uint32 time) - { - if (empty()) - return; - uint32 eventId = begin()->second; - erase(begin()); - time += m_time; - iterator itr = find(time); - while (itr != end()) - { - ++time; - itr = find(time); - } - insert(std::make_pair(time, eventId)); - } - - void PopEvent() - { - erase(begin()); - } - - uint32 ExecuteEvent() - { - while (!empty()) - { - if (begin()->first > m_time) - return 0; - else if (m_phase && (begin()->second & 0xFF000000) && !(begin()->second & m_phase)) - erase(begin()); - else - { - uint32 eventId = (begin()->second & 0x0000FFFF); - erase(begin()); - return eventId; - } - } - return 0; - } - - uint32 GetEvent() - { - while (!empty()) - { - if (begin()->first > m_time) - return 0; - else if (m_phase && (begin()->second & 0xFF000000) && !(begin()->second & m_phase)) - erase(begin()); - else - return (begin()->second & 0x0000FFFF); - } - return 0; - } - - // Delay all events - void DelayEvents(uint32 delay) - { - if (delay < m_time) - m_time -= delay; - else - m_time = 0; - } - - // Delay all events having the specified Global Cooldown. - void DelayEvents(uint32 delay, uint32 gcd) - { - uint32 nextTime = m_time + delay; - gcd = (1 << (gcd + 16)); - for (iterator itr = begin(); itr != end() && itr->first < nextTime;) - { - if (itr->second & gcd) - { - ScheduleEvent(itr->second, itr->first-m_time+delay); - erase(itr); - itr = begin(); - } - else - ++itr; - } - } - - void CancelEvent(uint32 eventId) - { - for (iterator itr = begin(); itr != end();) - { - if (eventId == (itr->second & 0x0000FFFF)) - { - erase(itr); - itr = begin(); - } - else - ++itr; - } - } - - void CancelEventsByGCD(uint32 gcd) - { - gcd = (1 << (gcd + 16)); - - for (iterator itr = begin(); itr != end();) - { - if (itr->second & gcd) - { - erase(itr); - itr = begin(); - } - else - ++itr; - } - } -}; - -enum AITarget -{ - AITARGET_SELF, - AITARGET_VICTIM, - AITARGET_ENEMY, - AITARGET_ALLY, - AITARGET_BUFF, - AITARGET_DEBUFF, -}; - -enum AICondition -{ - AICOND_AGGRO, - AICOND_COMBAT, - AICOND_DIE, -}; - -#define AI_DEFAULT_COOLDOWN 5000 - -struct AISpellInfoType -{ - AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT) - , cooldown(AI_DEFAULT_COOLDOWN), realCooldown(0), maxRange(0.0f){} - AITarget target; - AICondition condition; - uint32 cooldown; - uint32 realCooldown; - float maxRange; -}; - - AISpellInfoType * GetAISpellInfo(uint32 i); - -inline void CreatureAI::SetGazeOn(Unit *target) -{ - if (me->canAttack(target)) - { - AttackStart(target); - me->SetReactState(REACT_PASSIVE); - } -} - -inline bool CreatureAI::UpdateVictimWithGaze() -{ - if (!me->isInCombat()) - return false; - - if (me->HasReactState(REACT_PASSIVE)) - { - if (me->getVictim()) - return true; - else - me->SetReactState(REACT_AGGRESSIVE); - } - - if (Unit *victim = me->SelectVictim()) - AttackStart(victim); - return me->getVictim(); -} - -inline bool CreatureAI::UpdateCombatState() -{ - if (!me->isInCombat()) - return false; - - if (!me->HasReactState(REACT_PASSIVE)) - { - if (Unit *victim = me->SelectVictim()) - AttackStart(victim); - return me->getVictim(); - } - else if (me->getThreatManager().isThreatListEmpty()) - { - EnterEvadeMode(); - return false; - } - - return true; -} - -inline bool CreatureAI::UpdateVictim() -{ - if (!me->isInCombat()) - return false; - - if (!me->HasReactState(REACT_PASSIVE)) - { - if (Unit *victim = me->SelectVictim()) - AttackStart(victim); - return me->getVictim(); - } - else if (me->getThreatManager().isThreatListEmpty()) - { - EnterEvadeMode(); - return false; - } - - return true; -} - -/* -inline bool CreatureAI::UpdateVictim() -{ - if (!me->isInCombat()) - return false; - if (Unit *victim = me->SelectVictim()) - AttackStart(victim); - return me->getVictim(); -} -*/ - -inline bool CreatureAI::_EnterEvadeMode() -{ - if (!me->isAlive()) - return false; - - // sometimes bosses stuck in combat? - me->RemoveAllAuras(); - me->DeleteThreatList(); - me->CombatStop(true); - me->LoadCreaturesAddon(); - me->SetLootRecipient(NULL); - me->ResetPlayerDamageReq(); - - if (me->IsInEvadeMode()) - return false; - - return true; -} - -inline void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered) -{ - if (!victim || (me->hasUnitState(UNIT_STAT_CASTING) && !triggered)) - return; - - me->CastSpell(victim, spellId, triggered); -} - -inline void UnitAI::DoCastVictim(uint32 spellId, bool triggered) -{ - me->CastSpell(me->getVictim(), spellId, triggered); -} - -inline void UnitAI::DoCastAOE(uint32 spellId, bool triggered) -{ - if (!triggered && me->hasUnitState(UNIT_STAT_CASTING)) - return; - - me->CastSpell((Unit*)NULL, spellId, triggered); -} - -inline Creature *CreatureAI::DoSummon(uint32 uiEntry, const Position &pos, uint32 uiDespawntime, TempSummonType uiType) -{ - return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime); -} - -inline Creature *CreatureAI::DoSummon(uint32 uiEntry, WorldObject* obj, float fRadius, uint32 uiDespawntime, TempSummonType uiType) -{ - Position pos; - obj->GetRandomNearPosition(pos, fRadius); - return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime); -} - -inline Creature *CreatureAI::DoSummonFlyer(uint32 uiEntry, WorldObject *obj, float _fZ, float fRadius, uint32 uiDespawntime, TempSummonType uiType) -{ - Position pos; - obj->GetRandomNearPosition(pos, fRadius); - pos.m_positionZ += _fZ; - return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime); -} - -#endif - diff --git a/src/server/game/CreatureAIRegistry.cpp b/src/server/game/CreatureAIRegistry.cpp deleted file mode 100644 index 9db30a0a5c4..00000000000 --- a/src/server/game/CreatureAIRegistry.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "PassiveAI.h" -#include "ReactorAI.h" -#include "CombatAI.h" -#include "GuardAI.h" -#include "PetAI.h" -#include "TotemAI.h" -#include "CreatureEventAI.h" -#include "RandomMovementGenerator.h" -#include "MovementGeneratorImpl.h" -#include "CreatureAIRegistry.h" -#include "WaypointMovementGenerator.h" -#include "CreatureAIFactory.h" - -//#include "CreatureAIImpl.h" -namespace AIRegistry -{ - void Initialize() - { - (new CreatureAIFactory("NullCreatureAI"))->RegisterSelf(); - (new CreatureAIFactory("TriggerAI"))->RegisterSelf(); - (new CreatureAIFactory("AggressorAI"))->RegisterSelf(); - (new CreatureAIFactory("ReactorAI"))->RegisterSelf(); - (new CreatureAIFactory("PassiveAI"))->RegisterSelf(); - (new CreatureAIFactory("CritterAI"))->RegisterSelf(); - (new CreatureAIFactory("GuardAI"))->RegisterSelf(); - (new CreatureAIFactory("PetAI"))->RegisterSelf(); - (new CreatureAIFactory("TotemAI"))->RegisterSelf(); - (new CreatureAIFactory("CombatAI"))->RegisterSelf(); - (new CreatureAIFactory("ArchorAI"))->RegisterSelf(); - (new CreatureAIFactory("TurretAI"))->RegisterSelf(); - (new CreatureAIFactory("EventAI"))->RegisterSelf(); - (new CreatureAIFactory("AOEAI"))->RegisterSelf(); - (new CreatureAIFactory("VehicleAI"))->RegisterSelf(); - - (new MovementGeneratorFactory >(RANDOM_MOTION_TYPE))->RegisterSelf(); - (new MovementGeneratorFactory >(WAYPOINT_MOTION_TYPE))->RegisterSelf(); - } -} - diff --git a/src/server/game/CreatureAIRegistry.h b/src/server/game/CreatureAIRegistry.h deleted file mode 100644 index 0d83d29cf45..00000000000 --- a/src/server/game/CreatureAIRegistry.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CREATUREAIREGISTRY_H -#define TRINITY_CREATUREAIREGISTRY_H - -namespace AIRegistry -{ - void Initialize(void); -} -#endif - diff --git a/src/server/game/CreatureAISelector.cpp b/src/server/game/CreatureAISelector.cpp deleted file mode 100644 index d3fd5a8aed9..00000000000 --- a/src/server/game/CreatureAISelector.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Creature.h" -#include "CreatureAISelector.h" -#include "PassiveAI.h" -#include "Policies/SingletonImp.h" -#include "MovementGenerator.h" -#include "Pet.h" -#include "TemporarySummon.h" -#include "CreatureAIFactory.h" -#include "ScriptMgr.h" - -INSTANTIATE_SINGLETON_1(CreatureAIRegistry); -INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry); - -namespace FactorySelector -{ - CreatureAI* selectAI(Creature *creature) - { - const CreatureAICreator *ai_factory = NULL; - CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance()); - - if (creature->isPet()) - ai_factory = ai_registry.GetRegistryItem("PetAI"); - - //scriptname in db - if (!ai_factory) - if (CreatureAI* scriptedAI = sScriptMgr.GetAI(creature)) - return scriptedAI; - - // AIname in db - std::string ainame=creature->GetAIName(); - if (!ai_factory && !ainame.empty()) - ai_factory = ai_registry.GetRegistryItem(ainame.c_str()); - - // select by NPC flags - if (!ai_factory) - { - if (creature->IsVehicle()) - ai_factory = ai_registry.GetRegistryItem("VehicleAI"); - else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) - ai_factory = ai_registry.GetRegistryItem("PetAI"); - else if (creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) - ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); - else if (creature->isGuard()) - ai_factory = ai_registry.GetRegistryItem("GuardAI"); - else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - ai_factory = ai_registry.GetRegistryItem("PetAI"); - else if (creature->isTotem()) - ai_factory = ai_registry.GetRegistryItem("TotemAI"); - else if (creature->isTrigger()) - { - if (creature->m_spells[0]) - ai_factory = ai_registry.GetRegistryItem("TriggerAI"); - else - ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); - } - else if (creature->GetCreatureType() == CREATURE_TYPE_CRITTER && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) - ai_factory = ai_registry.GetRegistryItem("CritterAI"); - } - - // select by permit check - if (!ai_factory) - { - int best_val = -1; - typedef CreatureAIRegistry::RegistryMapType RMT; - RMT const &l = ai_registry.GetRegisteredItems(); - for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter) - { - const CreatureAICreator *factory = iter->second; - const SelectableAI *p = dynamic_cast(factory); - assert(p != NULL); - int val = p->Permit(creature); - if (val > best_val) - { - best_val = val; - ai_factory = p; - } - } - } - - // select NullCreatureAI if not another cases - ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key(); - - DEBUG_LOG("Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str()); - return (ai_factory == NULL ? new NullCreatureAI(creature) : ai_factory->Create(creature)); - } - - MovementGenerator* selectMovementGenerator(Creature *creature) - { - MovementGeneratorRegistry &mv_registry(MovementGeneratorRepository::Instance()); - assert(creature->GetCreatureInfo() != NULL); - const MovementGeneratorCreator *mv_factory = mv_registry.GetRegistryItem(creature->GetDefaultMovementType()); - - /* if (mv_factory == NULL) - { - int best_val = -1; - std::vector l; - mv_registry.GetRegisteredItems(l); - for (std::vector::iterator iter = l.begin(); iter != l.end(); ++iter) - { - const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str()); - const SelectableMovement *p = dynamic_cast(factory); - assert(p != NULL); - int val = p->Permit(creature); - if (val > best_val) - { - best_val = val; - mv_factory = p; - } - } - }*/ - - return (mv_factory == NULL ? NULL : mv_factory->Create(creature)); - - } -} - diff --git a/src/server/game/CreatureAISelector.h b/src/server/game/CreatureAISelector.h deleted file mode 100644 index 98eeee809d2..00000000000 --- a/src/server/game/CreatureAISelector.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CREATUREAISELECTOR_H -#define TRINITY_CREATUREAISELECTOR_H - -class CreatureAI; -class Creature; -class MovementGenerator; - -namespace FactorySelector -{ - CreatureAI* selectAI(Creature *); - MovementGenerator* selectMovementGenerator(Creature *); -} -#endif - diff --git a/src/server/game/CreatureEventAI.cpp b/src/server/game/CreatureEventAI.cpp deleted file mode 100644 index 47c8e9e6ad8..00000000000 --- a/src/server/game/CreatureEventAI.cpp +++ /dev/null @@ -1,1384 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "CreatureEventAI.h" -#include "CreatureEventAIMgr.h" -#include "ObjectMgr.h" -#include "Spell.h" -#include "World.h" -#include "Cell.h" -#include "CellImpl.h" -#include "GameEventMgr.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "InstanceData.h" -#include "SpellMgr.h" -#include "CreatureAIImpl.h" -#include "ConditionMgr.h" - -bool CreatureEventAIHolder::UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax) -{ - if (repeatMin == repeatMax) - Time = repeatMin; - else if (repeatMax > repeatMin) - Time = urand(repeatMin, repeatMax); - else - { - sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", creature->GetEntry(), Event.event_id, Event.event_type); - Enabled = false; - return false; - } - - return true; -} - -int CreatureEventAI::Permissible(const Creature *creature) -{ - if (creature->GetAIName() == "EventAI") - return PERMIT_BASE_SPECIAL; - return PERMIT_BASE_NO; -} - -CreatureEventAI::CreatureEventAI(Creature *c) : CreatureAI(c) -{ - // Need make copy for filter unneeded steps and safe in case table reload - CreatureEventAI_Event_Map::const_iterator CreatureEvents = CreatureEAI_Mgr.GetCreatureEventAIMap().find(me->GetEntry()); - if (CreatureEvents != CreatureEAI_Mgr.GetCreatureEventAIMap().end()) - { - std::vector::const_iterator i; - for (i = (*CreatureEvents).second.begin(); i != (*CreatureEvents).second.end(); ++i) - { - - //Debug check - #ifndef TRINITY_DEBUG - if ((*i).event_flags & EFLAG_DEBUG_ONLY) - continue; - #endif - if (me->GetMap()->IsDungeon()) - { - if ((1 << (me->GetMap()->GetSpawnMode()+1)) & (*i).event_flags) - { - //event flagged for instance mode - CreatureEventAIList.push_back(CreatureEventAIHolder(*i)); - } - continue; - } - CreatureEventAIList.push_back(CreatureEventAIHolder(*i)); - } - //EventMap had events but they were not added because they must be for instance - if (CreatureEventAIList.empty()) - sLog.outError("CreatureEventAI: Creature %u has events but no events added to list because of instance flags.", me->GetEntry()); - } - else - sLog.outError("CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI.", me->GetEntry()); - - bEmptyList = CreatureEventAIList.empty(); - Phase = 0; - CombatMovementEnabled = true; - MeleeEnabled = true; - AttackDistance = 0.0f; - AttackAngle = 0.0f; - - InvinceabilityHpLevel = 0; - - //Handle Spawned Events - if (!bEmptyList) - { - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - if (SpawnedEventConditionsCheck((*i).Event)) - ProcessEvent(*i); - } -} - -bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker /*=NULL*/) -{ - if (!pHolder.Enabled || pHolder.Time) - return false; - - //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask) - if (pHolder.Event.event_inverse_phase_mask & (1 << Phase)) - return false; - - CreatureEventAI_Event const& event = pHolder.Event; - - //Check event conditions based on the event type, also reset events - switch (event.event_type) - { - case EVENT_T_TIMER: - if (!me->isInCombat()) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.timer.repeatMin,event.timer.repeatMax); - break; - case EVENT_T_TIMER_OOC: - if (me->isInCombat()) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.timer.repeatMin,event.timer.repeatMax); - break; - case EVENT_T_HP: - { - if (!me->isInCombat() || !me->GetMaxHealth()) - return false; - - uint32 perc = (me->GetHealth()*100) / me->GetMaxHealth(); - - if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); - break; - } - case EVENT_T_MANA: - { - if (!me->isInCombat() || !me->GetMaxPower(POWER_MANA)) - return false; - - uint32 perc = (me->GetPower(POWER_MANA)*100) / me->GetMaxPower(POWER_MANA); - - if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); - break; - } - case EVENT_T_AGGRO: - break; - case EVENT_T_KILL: - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.kill.repeatMin,event.kill.repeatMax); - break; - case EVENT_T_DEATH: - case EVENT_T_EVADE: - break; - case EVENT_T_SPELLHIT: - //Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.spell_hit.repeatMin,event.spell_hit.repeatMax); - break; - case EVENT_T_RANGE: - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.range.repeatMin,event.range.repeatMax); - break; - case EVENT_T_OOC_LOS: - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.ooc_los.repeatMin,event.ooc_los.repeatMax); - break; - case EVENT_T_RESET: - case EVENT_T_SPAWNED: - break; - case EVENT_T_TARGET_HP: - { - if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxHealth()) - return false; - - uint32 perc = (me->getVictim()->GetHealth()*100) / me->getVictim()->GetMaxHealth(); - - if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); - break; - } - case EVENT_T_TARGET_CASTING: - if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->IsNonMeleeSpellCasted(false, false, true)) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.target_casting.repeatMin,event.target_casting.repeatMax); - break; - case EVENT_T_FRIENDLY_HP: - { - if (!me->isInCombat()) - return false; - - Unit* pUnit = DoSelectLowestHpFriendly(event.friendly_hp.radius, event.friendly_hp.hpDeficit); - if (!pUnit) - return false; - - pActionInvoker = pUnit; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.friendly_hp.repeatMin,event.friendly_hp.repeatMax); - break; - } - case EVENT_T_FRIENDLY_IS_CC: - { - if (!me->isInCombat()) - return false; - - std::list pList; - DoFindFriendlyCC(pList, event.friendly_is_cc.radius); - - //List is empty - if (pList.empty()) - return false; - - //We don't really care about the whole list, just return first available - pActionInvoker = *(pList.begin()); - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.friendly_is_cc.repeatMin,event.friendly_is_cc.repeatMax); - break; - } - case EVENT_T_FRIENDLY_MISSING_BUFF: - { - std::list pList; - DoFindFriendlyMissingBuff(pList, event.friendly_buff.radius, event.friendly_buff.spellId); - - //List is empty - if (pList.empty()) - return false; - - //We don't really care about the whole list, just return first available - pActionInvoker = *(pList.begin()); - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.friendly_buff.repeatMin,event.friendly_buff.repeatMax); - break; - } - case EVENT_T_SUMMONED_UNIT: - { - //Prevent event from occuring on no unit or non creatures - if (!pActionInvoker || pActionInvoker->GetTypeId() != TYPEID_UNIT) - return false; - - //Creature id doesn't match up - if (pActionInvoker->ToCreature()->GetEntry() != event.summon_unit.creatureId) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.summon_unit.repeatMin,event.summon_unit.repeatMax); - break; - } - case EVENT_T_TARGET_MANA: - { - if (!me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxPower(POWER_MANA)) - return false; - - uint32 perc = (me->getVictim()->GetPower(POWER_MANA)*100) / me->getVictim()->GetMaxPower(POWER_MANA); - - if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.percent_range.repeatMin,event.percent_range.repeatMax); - break; - } - case EVENT_T_REACHED_HOME: - case EVENT_T_RECEIVE_EMOTE: - break; - case EVENT_T_BUFFED: - { - //Note: checked only aura for effect 0, if need check aura for effect 1/2 then - // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) - Aura const * aura = me->GetAura(event.buffed.spellId); - if (!aura || aura->GetStackAmount() < event.buffed.amount) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.buffed.repeatMin,event.buffed.repeatMax); - break; - } - case EVENT_T_TARGET_BUFFED: - { - //Prevent event from occuring on no unit - if (!pActionInvoker) - return false; - - //Note: checked only aura for effect 0, if need check aura for effect 1/2 then - // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) - Aura const * aura = pActionInvoker->GetAura(event.buffed.spellId); - if (!aura || aura->GetStackAmount() < event.buffed.amount) - return false; - - //Repeat Timers - pHolder.UpdateRepeatTimer(me,event.buffed.repeatMin,event.buffed.repeatMax); - break; - } - default: - sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", me->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); - break; - } - - //Disable non-repeatable events - if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) - pHolder.Enabled = false; - - //Store random here so that all random actions match up - uint32 rnd = rand(); - - //Return if chance for event is not met - if (pHolder.Event.event_chance <= rnd % 100) - return false; - - //Process actions - for (uint8 j = 0; j < MAX_ACTIONS; ++j) - ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker); - - return true; -} - -void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker) -{ - switch (action.type) - { - case ACTION_T_TEXT: - { - if (!action.text.TextId1) - return; - - int32 temp = 0; - - if (action.text.TextId2 && action.text.TextId3) - temp = RAND(action.text.TextId1,action.text.TextId2,action.text.TextId3); - else if (action.text.TextId2 && urand(0,1)) - temp = action.text.TextId2; - else - temp = action.text.TextId1; - - if (temp) - { - Unit* target = NULL; - - if (pActionInvoker) - { - if (pActionInvoker->GetTypeId() == TYPEID_PLAYER) - target = pActionInvoker; - else if (Unit* owner = pActionInvoker->GetOwner()) - { - if (owner->GetTypeId() == TYPEID_PLAYER) - target = owner; - } - } - else - { - target = me->getVictim(); - if (target && target->GetTypeId() != TYPEID_PLAYER) - if (Unit* owner = target->GetOwner()) - if (owner->GetTypeId() == TYPEID_PLAYER) - target = owner; - } - - DoScriptText(temp, me, target); - } - break; - } - case ACTION_T_SET_FACTION: - { - if (action.set_faction.factionId) - me->setFaction(action.set_faction.factionId); - else - { - if (CreatureInfo const* ci = GetCreatureTemplateStore(me->GetEntry())) - { - //if no id provided, assume reset and then use default - if (me->getFaction() != ci->faction_A) - me->setFaction(ci->faction_A); - } - } - break; - } - case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: - { - if (action.morph.creatureId || action.morph.modelId) - { - //set model based on entry from creature_template - if (action.morph.creatureId) - { - if (CreatureInfo const* ci = GetCreatureTemplateStore(action.morph.creatureId)) - { - uint32 display_id = objmgr.ChooseDisplayId(0,ci); - me->SetDisplayId(display_id); - } - } - //if no param1, then use value from param2 (modelId) - else - me->SetDisplayId(action.morph.modelId); - } - else - me->DeMorph(); - break; - } - case ACTION_T_SOUND: - me->PlayDirectSound(action.sound.soundId); - break; - case ACTION_T_EMOTE: - me->HandleEmoteCommand(action.emote.emoteId); - break; - case ACTION_T_RANDOM_SOUND: - { - int32 temp = GetRandActionParam(rnd, action.random_sound.soundId1, action.random_sound.soundId2, action.random_sound.soundId3); - if (temp >= 0) - me->PlayDirectSound(temp); - break; - } - case ACTION_T_RANDOM_EMOTE: - { - int32 temp = GetRandActionParam(rnd, action.random_emote.emoteId1, action.random_emote.emoteId2, action.random_emote.emoteId3); - if (temp >= 0) - me->HandleEmoteCommand(temp); - break; - } - case ACTION_T_CAST: - { - Unit* target = GetTargetByType(action.cast.target, pActionInvoker); - Unit* caster = me; - - if (!target) - return; - - if (action.cast.castFlags & CAST_FORCE_TARGET_SELF) - caster = target; - - //Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered - bool canCast = !caster->IsNonMeleeSpellCasted(false) || (action.cast.castFlags & (CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS)); - - // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them - if (action.cast.castFlags & CAST_AURA_NOT_PRESENT) - { - if (target->HasAura(action.cast.spellId)) - return; - } - - if (canCast) - { - const SpellEntry* tSpell = GetSpellStore()->LookupEntry(action.cast.spellId); - - //Verify that spell exists - if (tSpell) - { - //Check if cannot cast spell - if (!(action.cast.castFlags & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) && - !CanCast(target, tSpell, (action.cast.castFlags & CAST_TRIGGERED))) - { - //Melee current victim if flag not set - if (!(action.cast.castFlags & CAST_NO_MELEE_IF_OOM)) - { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) - { - AttackDistance = 0.0f; - AttackAngle = 0.0f; - - me->GetMotionMaster()->MoveChase(me->getVictim(), AttackDistance, AttackAngle); - } - } - - } - else - { - //Interrupt any previous spell - if (caster->IsNonMeleeSpellCasted(false) && action.cast.castFlags & CAST_INTURRUPT_PREVIOUS) - caster->InterruptNonMeleeSpells(false); - - caster->CastSpell(target, action.cast.spellId, (action.cast.castFlags & CAST_TRIGGERED)); - } - - } - else - sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, me->GetEntry(), action.cast.spellId); - } - break; - } - case ACTION_T_SUMMON: - { - Unit* target = GetTargetByType(action.summon.target, pActionInvoker); - - Creature* pCreature = NULL; - - if (action.summon.duration) - pCreature = me->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, action.summon.duration); - else - pCreature = me->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); - - if (!pCreature) - sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", action.summon.creatureId, EventId, me->GetEntry()); - else if (action.summon.target != TARGET_T_SELF && target) - pCreature->AI()->AttackStart(target); - break; - } - case ACTION_T_THREAT_SINGLE_PCT: - if (Unit* target = GetTargetByType(action.threat_single_pct.target, pActionInvoker)) - me->getThreatManager().modifyThreatPercent(target, action.threat_single_pct.percent); - break; - case ACTION_T_THREAT_ALL_PCT: - { - std::list& threatList = me->getThreatManager().getThreatList(); - for (std::list::iterator i = threatList.begin(); i != threatList.end(); ++i) - if (Unit* Temp = Unit::GetUnit(*me,(*i)->getUnitGuid())) - me->getThreatManager().modifyThreatPercent(Temp, action.threat_all_pct.percent); - break; - } - case ACTION_T_QUEST_EVENT: - if (Unit* target = GetTargetByType(action.quest_event.target, pActionInvoker)) - if (target->GetTypeId() == TYPEID_PLAYER) - target->ToPlayer()->AreaExploredOrEventHappens(action.quest_event.questId); - break; - case ACTION_T_CAST_EVENT: - if (Unit* target = GetTargetByType(action.cast_event.target, pActionInvoker)) - if (target->GetTypeId() == TYPEID_PLAYER) - target->ToPlayer()->CastedCreatureOrGO(action.cast_event.creatureId, me->GetGUID(), action.cast_event.spellId); - break; - case ACTION_T_SET_UNIT_FIELD: - { - Unit* target = GetTargetByType(action.set_unit_field.target, pActionInvoker); - - // not allow modify important for integrity object fields - if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END) - return; - - if (target) - target->SetUInt32Value(action.set_unit_field.field, action.set_unit_field.value); - - break; - } - case ACTION_T_SET_UNIT_FLAG: - if (Unit* target = GetTargetByType(action.unit_flag.target, pActionInvoker)) - target->SetFlag(UNIT_FIELD_FLAGS, action.unit_flag.value); - break; - case ACTION_T_REMOVE_UNIT_FLAG: - if (Unit* target = GetTargetByType(action.unit_flag.target, pActionInvoker)) - target->RemoveFlag(UNIT_FIELD_FLAGS, action.unit_flag.value); - break; - case ACTION_T_AUTO_ATTACK: - MeleeEnabled = action.auto_attack.state != 0; - break; - case ACTION_T_COMBAT_MOVEMENT: - // ignore no affect case - if (CombatMovementEnabled == (action.combat_movement.state != 0)) - return; - - CombatMovementEnabled = action.combat_movement.state != 0; - - //Allow movement (create new targeted movement gen only if idle) - if (CombatMovementEnabled) - { - Unit* victim = me->getVictim(); - if (me->isInCombat() && victim) - { - if (action.combat_movement.melee) - { - me->addUnitState(UNIT_STAT_MELEE_ATTACKING); - me->SendMeleeAttackStart(victim); - } - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) - me->GetMotionMaster()->MoveChase(victim, AttackDistance, AttackAngle); // Targeted movement generator will start melee automatically, no need to send it explicitly - } - } - else - { - if (me->isInCombat()) - { - Unit* victim = me->getVictim(); - if (action.combat_movement.melee && victim) - { - me->clearUnitState(UNIT_STAT_MELEE_ATTACKING); - me->SendMeleeAttackStop(victim); - } - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) - me->GetMotionMaster()->MoveIdle(); - } - } - break; - case ACTION_T_SET_PHASE: - Phase = action.set_phase.phase; - break; - case ACTION_T_INC_PHASE: - { - int32 new_phase = int32(Phase)+action.set_inc_phase.step; - if (new_phase < 0) - { - sLog.outErrorDb("CreatureEventAI: Event %d decrease Phase under 0. CreatureEntry = %d", EventId, me->GetEntry()); - Phase = 0; - } - else if (new_phase >= MAX_PHASE) - { - sLog.outErrorDb("CreatureEventAI: Event %d incremented Phase above %u. Phase mask cannot be used with phases past %u. CreatureEntry = %d", EventId, MAX_PHASE-1, MAX_PHASE-1, me->GetEntry()); - Phase = MAX_PHASE-1; - } - else - Phase = new_phase; - - break; - } - case ACTION_T_EVADE: - EnterEvadeMode(); - break; - case ACTION_T_FLEE_FOR_ASSIST: - me->DoFleeToGetAssistance(); - break; - case ACTION_T_QUEST_EVENT_ALL: - if (pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER) - { - if (Unit* Temp = Unit::GetUnit(*me,pActionInvoker->GetGUID())) - if (Temp->GetTypeId() == TYPEID_PLAYER) - Temp->ToPlayer()->GroupEventHappens(action.quest_event_all.questId,me); - } - break; - case ACTION_T_CAST_EVENT_ALL: - { - std::list& threatList = me->getThreatManager().getThreatList(); - for (std::list::iterator i = threatList.begin(); i != threatList.end(); ++i) - if (Unit* Temp = Unit::GetUnit(*me,(*i)->getUnitGuid())) - if (Temp->GetTypeId() == TYPEID_PLAYER) - Temp->ToPlayer()->CastedCreatureOrGO(action.cast_event_all.creatureId, me->GetGUID(), action.cast_event_all.spellId); - break; - } - case ACTION_T_REMOVEAURASFROMSPELL: - if (Unit* target = GetTargetByType(action.remove_aura.target, pActionInvoker)) - target->RemoveAurasDueToSpell(action.remove_aura.spellId); - break; - case ACTION_T_RANGED_MOVEMENT: - AttackDistance = (float)action.ranged_movement.distance; - AttackAngle = action.ranged_movement.angle/180.0f*M_PI; - - if (CombatMovementEnabled) - { - me->GetMotionMaster()->MoveChase(me->getVictim(), AttackDistance, AttackAngle); - } - break; - case ACTION_T_RANDOM_PHASE: - Phase = GetRandActionParam(rnd, action.random_phase.phase1, action.random_phase.phase2, action.random_phase.phase3); - break; - case ACTION_T_RANDOM_PHASE_RANGE: - if (action.random_phase_range.phaseMin <= action.random_phase_range.phaseMax) - Phase = urand(action.random_phase_range.phaseMin, action.random_phase_range.phaseMax); - else - sLog.outErrorDb("CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 < Param1. Event = %d. CreatureEntry = %d", EventId, me->GetEntry()); - break; - case ACTION_T_SUMMON_ID: - { - Unit* target = GetTargetByType(action.summon_id.target, pActionInvoker); - - CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(action.summon_id.spawnId); - if (i == CreatureEAI_Mgr.GetCreatureEventAISummonMap().end()) - { - sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", action.summon_id.creatureId, action.summon_id.spawnId, EventId, me->GetEntry()); - return; - } - - Creature* pCreature = NULL; - if ((*i).second.SpawnTimeSecs) - pCreature = me->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs); - else - pCreature = me->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); - - if (!pCreature) - sLog.outErrorDb("CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, me->GetEntry()); - else if (action.summon_id.target != TARGET_T_SELF && target) - pCreature->AI()->AttackStart(target); - - break; - } - case ACTION_T_KILLED_MONSTER: - //first attempt player who tapped creature - if (Player* pPlayer = me->GetLootRecipient()) - pPlayer->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, me); - else - { - //if not available, use pActionInvoker - if (Unit* pTarget = GetTargetByType(action.killed_monster.target, pActionInvoker)) - if (Player* pPlayer2 = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself()) - pPlayer2->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, me); - } - break; - case ACTION_T_SET_INST_DATA: - { - InstanceData* pInst = (InstanceData*)me->GetInstanceData(); - if (!pInst) - { - sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d", EventId, me->GetEntry()); - return; - } - - pInst->SetData(action.set_inst_data.field, action.set_inst_data.value); - break; - } - case ACTION_T_SET_INST_DATA64: - { - Unit* target = GetTargetByType(action.set_inst_data64.target, pActionInvoker); - if (!target) - { - sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, me->GetEntry()); - return; - } - - InstanceData* pInst = (InstanceData*)me->GetInstanceData(); - if (!pInst) - { - sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, me->GetEntry()); - return; - } - - pInst->SetData64(action.set_inst_data64.field, target->GetGUID()); - break; - } - case ACTION_T_UPDATE_TEMPLATE: - if (me->GetEntry() == action.update_template.creatureId) - { - - sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, me->GetEntry()); - return; - } - - me->UpdateEntry(action.update_template.creatureId, action.update_template.team ? HORDE : ALLIANCE); - break; - case ACTION_T_DIE: - if (me->isDead()) - { - - sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, me->GetEntry()); - return; - } - me->Kill(me); - break; - case ACTION_T_ZONE_COMBAT_PULSE: - { - me->SetInCombatWithZone(); - break; - } - case ACTION_T_CALL_FOR_HELP: - { - me->CallForHelp(action.call_for_help.radius); - break; - } - break; - - // TRINITY ONLY - case ACTION_T_MOVE_RANDOM_POINT: //dosen't work in combat - { - float x,y,z; - me->GetClosePoint(x, y, z, me->GetObjectSize() / 3, action.raw.param1); - me->GetMotionMaster()->MovePoint(0,x,y,z); - break; - } - case ACTION_T_SET_STAND_STATE: - me->SetStandState(UnitStandStateType(action.raw.param1)); - break; - case ACTION_T_SET_PHASE_MASK: - me->SetPhaseMask(action.raw.param1, true); - break; - case ACTION_T_SET_VISIBILITY: - me->SetVisibility(UnitVisibility(action.raw.param1)); - break; - case ACTION_T_SET_ACTIVE: - me->setActive(action.raw.param1 ? true : false); - break; - case ACTION_T_SET_AGGRESSIVE: - me->SetReactState(ReactStates(action.raw.param1)); - break; - case ACTION_T_ATTACK_START_PULSE: - AttackStart(me->SelectNearestTarget((float)action.raw.param1)); - break; - case ACTION_T_SUMMON_GO: - { - GameObject* pObject = NULL; - - float x,y,z; - me->GetPosition(x,y,z); - pObject = me->SummonGameObject(action.raw.param1, x, y, z, 0, 0, 0, 0, 0, action.raw.param2); - if (!pObject) - { - sLog.outErrorDb("TSCR: EventAI failed to spawn object %u. Spawn event %d is on creature %d", action.raw.param1, EventId, me->GetEntry()); - } - break; - } - - case ACTION_T_SET_SHEATH: - { - me->SetSheath(SheathState(action.set_sheath.sheath)); - break; - } - case ACTION_T_FORCE_DESPAWN: - { - me->ForcedDespawn(action.forced_despawn.msDelay); - break; - } - case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: - { - if (action.invincibility_hp_level.is_percent) - InvinceabilityHpLevel = me->GetMaxHealth()*action.invincibility_hp_level.hp_level/100; - else - InvinceabilityHpLevel = action.invincibility_hp_level.hp_level; - break; - } - } -} - -void CreatureEventAI::JustRespawned() -{ - Reset(); - - if (bEmptyList) - return; - - //Handle Spawned Events - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - if (SpawnedEventConditionsCheck((*i).Event)) - ProcessEvent(*i); -} - -void CreatureEventAI::Reset() -{ - EventUpdateTime = EVENT_UPDATE_TIME; - EventDiff = 0; - - if (bEmptyList) - return; - - - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - if ((*i).Event.event_type == EVENT_T_RESET) - ProcessEvent(*i); - } - - - //Reset all events to enabled - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - CreatureEventAI_Event const& event = (*i).Event; - switch (event.event_type) - { - //Reset all out of combat timers - case EVENT_T_TIMER_OOC: - { - if ((*i).UpdateRepeatTimer(me,event.timer.initialMin,event.timer.initialMax)) - (*i).Enabled = true; - break; - } - //default: - //TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void EnterCombat() - //(*i).Enabled = true; - //(*i).Time = 0; - //break; - } - } -} - -void CreatureEventAI::JustReachedHome() -{ - me->LoadCreaturesAddon(); - - if (!bEmptyList) - { - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - if ((*i).Event.event_type == EVENT_T_REACHED_HOME) - ProcessEvent(*i); - } - } - - Reset(); -} - -void CreatureEventAI::EnterEvadeMode() -{ - CreatureAI::EnterEvadeMode(); - - if (bEmptyList) - return; - - //Handle Evade events - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - if ((*i).Event.event_type == EVENT_T_EVADE) - ProcessEvent(*i); - } -} - -void CreatureEventAI::JustDied(Unit* killer) -{ - Reset(); - - if (bEmptyList) - return; - - //Handle Evade events - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - if ((*i).Event.event_type == EVENT_T_DEATH) - ProcessEvent(*i, killer); - } - - // reset phase after any death state events - Phase = 0; -} - -void CreatureEventAI::KilledUnit(Unit* victim) -{ - if (bEmptyList || victim->GetTypeId() != TYPEID_PLAYER) - return; - - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - if ((*i).Event.event_type == EVENT_T_KILL) - ProcessEvent(*i, victim); - } -} - -void CreatureEventAI::JustSummoned(Creature* pUnit) -{ - if (bEmptyList || !pUnit) - return; - - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT) - ProcessEvent(*i, pUnit); - } -} - -void CreatureEventAI::EnterCombat(Unit *enemy) -{ - //Check for on combat start events - if (!bEmptyList) - { - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - CreatureEventAI_Event const& event = (*i).Event; - switch (event.event_type) - { - case EVENT_T_AGGRO: - (*i).Enabled = true; - ProcessEvent(*i, enemy); - break; - //Reset all in combat timers - case EVENT_T_TIMER: - if ((*i).UpdateRepeatTimer(me,event.timer.initialMin,event.timer.initialMax)) - (*i).Enabled = true; - break; - //All normal events need to be re-enabled and their time set to 0 - default: - (*i).Enabled = true; - (*i).Time = 0; - break; - } - } - } - - EventUpdateTime = EVENT_UPDATE_TIME; - EventDiff = 0; -} - -void CreatureEventAI::AttackStart(Unit *who) -{ - if (!who) - return; - - if (me->Attack(who, MeleeEnabled)) - { - if (CombatMovementEnabled) - { - me->GetMotionMaster()->MoveChase(who, AttackDistance, AttackAngle); - } - else - { - me->GetMotionMaster()->MoveIdle(); - } - } -} - -void CreatureEventAI::MoveInLineOfSight(Unit *who) -{ - if (me->getVictim()) - return; - - //Check for OOC LOS Event - if (!bEmptyList) - { - for (std::list::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr) - { - if ((*itr).Event.event_type == EVENT_T_OOC_LOS) - { - //can trigger if closer than fMaxAllowedRange - float fMaxAllowedRange = (*itr).Event.ooc_los.maxRange; - - //if range is ok and we are actually in LOS - if (me->IsWithinDistInMap(who, fMaxAllowedRange) && me->IsWithinLOSInMap(who)) - { - //if friendly event&&who is not hostile OR hostile event&&who is hostile - if (((*itr).Event.ooc_los.noHostile && !me->IsHostileTo(who)) || - ((!(*itr).Event.ooc_los.noHostile) && me->IsHostileTo(who))) - ProcessEvent(*itr, who); - } - } - } - } - - CreatureAI::MoveInLineOfSight(who); -} - -void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell) -{ - - if (bEmptyList) - return; - - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - if ((*i).Event.event_type == EVENT_T_SPELLHIT) - //If spell id matches (or no spell id) & if spell school matches (or no spell school) - if (!(*i).Event.spell_hit.spellId || pSpell->Id == (*i).Event.spell_hit.spellId) - if (pSpell->SchoolMask & (*i).Event.spell_hit.schoolMask) - ProcessEvent(*i, pUnit); -} - -void CreatureEventAI::UpdateAI(const uint32 diff) -{ - //Check if we are in combat (also updates calls threat update code) - bool Combat = UpdateVictim(); - - if (!bEmptyList) - { - //Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events - if (EventUpdateTime <= diff) - { - EventDiff += diff; - - //Check for time based events - for (std::list::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i) - { - //Decrement Timers - if ((*i).Time) - { - if (EventDiff <= (*i).Time) - { - //Do not decrement timers if event cannot trigger in this phase - if (!((*i).Event.event_inverse_phase_mask & (1 << Phase))) - (*i).Time -= EventDiff; - - //Skip processing of events that have time remaining - continue; - } - else (*i).Time = 0; - } - - //Events that are updated every EVENT_UPDATE_TIME - switch ((*i).Event.event_type) - { - case EVENT_T_TIMER_OOC: - ProcessEvent(*i); - break; - case EVENT_T_TIMER: - case EVENT_T_MANA: - case EVENT_T_HP: - case EVENT_T_TARGET_HP: - case EVENT_T_TARGET_CASTING: - case EVENT_T_FRIENDLY_HP: - if (me->getVictim()) - ProcessEvent(*i); - break; - case EVENT_T_RANGE: - if (me->getVictim()) - if (me->IsInMap(me->getVictim())) - if (me->IsInRange(me->getVictim(),(float)(*i).Event.range.minDist,(float)(*i).Event.range.maxDist)) - ProcessEvent(*i); - break; - } - } - - EventDiff = 0; - EventUpdateTime = EVENT_UPDATE_TIME; - } - else - { - EventDiff += diff; - EventUpdateTime -= diff; - } - } - - //Melee Auto-Attack - if (Combat && MeleeEnabled) - DoMeleeAttackIfReady(); -} - -inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3) -{ - switch (rnd % 3) - { - case 0: return param1; - case 1: return param2; - case 2: return param3; - } - return 0; -} - -inline int32 CreatureEventAI::GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3) -{ - switch (rnd % 3) - { - case 0: return param1; - case 1: return param2; - case 2: return param3; - } - return 0; -} - -inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker) -{ - switch (Target) - { - case TARGET_T_SELF: - return me; - case TARGET_T_HOSTILE: - return me->getVictim(); - case TARGET_T_HOSTILE_SECOND_AGGRO: - return SelectTarget(SELECT_TARGET_TOPAGGRO,1); - case TARGET_T_HOSTILE_LAST_AGGRO: - return SelectTarget(SELECT_TARGET_BOTTOMAGGRO,0); - case TARGET_T_HOSTILE_RANDOM: - return SelectTarget(SELECT_TARGET_RANDOM,0); - case TARGET_T_HOSTILE_RANDOM_NOT_TOP: - return SelectTarget(SELECT_TARGET_RANDOM,1); - case TARGET_T_ACTION_INVOKER: - return pActionInvoker; - default: - return NULL; - }; -} - -Unit* CreatureEventAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff) -{ - CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Unit* pUnit = NULL; - - Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff); - Trinity::UnitLastSearcher searcher(me, pUnit, u_check); - - /* - typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes; - This means that if we only search grid then we cannot possibly return pets or players so this is safe - */ - TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); - - cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, range); - return pUnit; -} - -void CreatureEventAI::DoFindFriendlyCC(std::list& _list, float range) -{ - CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Trinity::FriendlyCCedInRange u_check(me, range); - Trinity::CreatureListSearcher searcher(me, _list, u_check); - - TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); - - cell.Visit(p, grid_creature_searcher, *me->GetMap()); -} - -void CreatureEventAI::DoFindFriendlyMissingBuff(std::list& _list, float range, uint32 spellid) -{ - CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - cell.SetNoCreate(); - - Trinity::FriendlyMissingBuffInRange u_check(me, range, spellid); - Trinity::CreatureListSearcher searcher(me, _list, u_check); - - TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); - - cell.Visit(p, grid_creature_searcher, *me->GetMap()); -} - -//********************************* -//*** Functions used globally *** - -void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target) -{ - if (!pSource) - { - sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i, invalid Source pointer.",textEntry); - return; - } - - if (textEntry >= 0) - { - sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry); - return; - } - - CreatureEventAI_TextMap::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAITextMap().find(textEntry); - - if (i == CreatureEAI_Mgr.GetCreatureEventAITextMap().end()) - { - sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry); - return; - } - - sLog.outDebug("CreatureEventAI: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u",textEntry,(*i).second.SoundId,(*i).second.Type,(*i).second.Language,(*i).second.Emote); - - if ((*i).second.SoundId) - { - if (GetSoundEntriesStore()->LookupEntry((*i).second.SoundId)) - pSource->PlayDirectSound((*i).second.SoundId); - else - sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u.",textEntry,(*i).second.SoundId); - } - - if ((*i).second.Emote) - { - if (pSource->GetTypeId() == TYPEID_UNIT || pSource->GetTypeId() == TYPEID_PLAYER) - { - ((Unit*)pSource)->HandleEmoteCommand((*i).second.Emote); - } - else - sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u).",textEntry,pSource->GetTypeId()); - } - - switch((*i).second.Type) - { - case CHAT_TYPE_SAY: - pSource->MonsterSay(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); - break; - case CHAT_TYPE_YELL: - pSource->MonsterYell(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); - break; - case CHAT_TYPE_TEXT_EMOTE: - pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0); - break; - case CHAT_TYPE_BOSS_EMOTE: - pSource->MonsterTextEmote(textEntry, target ? target->GetGUID() : 0, true); - break; - case CHAT_TYPE_WHISPER: - { - if (target && target->GetTypeId() == TYPEID_PLAYER) - pSource->MonsterWhisper(textEntry, target->GetGUID()); - else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry); - }break; - case CHAT_TYPE_BOSS_WHISPER: - { - if (target && target->GetTypeId() == TYPEID_PLAYER) - pSource->MonsterWhisper(textEntry, target->GetGUID(), true); - else sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i cannot whisper without target unit (TYPEID_PLAYER).", textEntry); - }break; - case CHAT_TYPE_ZONE_YELL: - pSource->MonsterYellToZone(textEntry, (*i).second.Language, target ? target->GetGUID() : 0); - break; - } -} - -bool CreatureEventAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered) -{ - //No target so we can't cast - if (!Target || !Spell) - return false; - - //Silenced so we can't cast - if (!Triggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) - return false; - - //Check for power - if (!Triggered && me->GetPower((Powers)Spell->powerType) < CalculatePowerCost(Spell, me, GetSpellSchoolMask(Spell))) - return false; - - SpellRangeEntry const *TempRange = NULL; - - TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex); - - //Spell has invalid range store so we can't use it - if (!TempRange) - return false; - - //Unit is out of range of this spell - if (!me->IsInRange(Target,TempRange->minRangeHostile,TempRange->maxRangeHostile)) - return false; - - return true; -} - -void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote) -{ - if (bEmptyList) - return; - - for (std::list::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr) - { - if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE) - { - if ((*itr).Event.receive_emote.emoteId != text_emote) - return; - - Condition* cond = new Condition(); - cond->mConditionType = ConditionType((*itr).Event.receive_emote.condition); - cond->mConditionValue1 = (*itr).Event.receive_emote.conditionValue1; - cond->mConditionValue2 = (*itr).Event.receive_emote.conditionValue2; - - if (cond->Meets(pPlayer)) - { - sLog.outDebug("CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing"); - ProcessEvent(*itr, pPlayer); - } - } - } -} - -void CreatureEventAI::DamageTaken(Unit* /*done_by*/, uint32& damage) -{ - if (InvinceabilityHpLevel > 0 && me->GetHealth() < InvinceabilityHpLevel+damage) - { - if (me->GetHealth() <= InvinceabilityHpLevel) - damage = 0; - else - damage = me->GetHealth() - InvinceabilityHpLevel; - } -} - -bool CreatureEventAI::SpawnedEventConditionsCheck(CreatureEventAI_Event const& event) -{ - if (event.event_type != EVENT_T_SPAWNED) - return false; - - switch (event.spawned.condition) - { - case SPAWNED_EVENT_ALWAY: - // always - return true; - case SPAWNED_EVENT_MAP: - // map ID check - return me->GetMapId() == event.spawned.conditionValue1; - case SPAWNED_EVENT_ZONE: - { - // zone ID check - uint32 zone, area; - me->GetZoneAndAreaId(zone,area); - return zone == event.spawned.conditionValue1 || area == event.spawned.conditionValue1; - } - default: - break; - } - - return false; -} diff --git a/src/server/game/CreatureEventAI.h b/src/server/game/CreatureEventAI.h deleted file mode 100644 index 2fc5de46089..00000000000 --- a/src/server/game/CreatureEventAI.h +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CREATURE_EAI_H -#define TRINITY_CREATURE_EAI_H - -#include "Common.h" -#include "Creature.h" -#include "CreatureAI.h" -#include "Unit.h" - -class Player; -class WorldObject; - -#define EVENT_UPDATE_TIME 500 -#define MAX_ACTIONS 3 -#define MAX_PHASE 32 - -enum EventAI_Type -{ - EVENT_T_TIMER = 0, // InitialMin, InitialMax, RepeatMin, RepeatMax - EVENT_T_TIMER_OOC = 1, // InitialMin, InitialMax, RepeatMin, RepeatMax - EVENT_T_HP = 2, // HPMax%, HPMin%, RepeatMin, RepeatMax - EVENT_T_MANA = 3, // ManaMax%,ManaMin% RepeatMin, RepeatMax - EVENT_T_AGGRO = 4, // NONE - EVENT_T_KILL = 5, // RepeatMin, RepeatMax - EVENT_T_DEATH = 6, // NONE - EVENT_T_EVADE = 7, // NONE - EVENT_T_SPELLHIT = 8, // SpellID, School, RepeatMin, RepeatMax - EVENT_T_RANGE = 9, // MinDist, MaxDist, RepeatMin, RepeatMax - EVENT_T_OOC_LOS = 10, // NoHostile, MaxRnage, RepeatMin, RepeatMax - EVENT_T_SPAWNED = 11, // Condition, CondValue1 - EVENT_T_TARGET_HP = 12, // HPMax%, HPMin%, RepeatMin, RepeatMax - EVENT_T_TARGET_CASTING = 13, // RepeatMin, RepeatMax - EVENT_T_FRIENDLY_HP = 14, // HPDeficit, Radius, RepeatMin, RepeatMax - EVENT_T_FRIENDLY_IS_CC = 15, // DispelType, Radius, RepeatMin, RepeatMax - EVENT_T_FRIENDLY_MISSING_BUFF = 16, // SpellId, Radius, RepeatMin, RepeatMax - EVENT_T_SUMMONED_UNIT = 17, // CreatureId, RepeatMin, RepeatMax - EVENT_T_TARGET_MANA = 18, // ManaMax%, ManaMin%, RepeatMin, RepeatMax - EVENT_T_QUEST_ACCEPT = 19, // QuestID - EVENT_T_QUEST_COMPLETE = 20, // - EVENT_T_REACHED_HOME = 21, // NONE - EVENT_T_RECEIVE_EMOTE = 22, // EmoteId, Condition, CondValue1, CondValue2 - EVENT_T_BUFFED = 23, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max - EVENT_T_TARGET_BUFFED = 24, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max - EVENT_T_RESET = 35, // Is it called after combat, when the creature respawn and spawn. -- TRINITY ONLY - - EVENT_T_END, -}; - -enum EventAI_ActionType -{ - ACTION_T_NONE = 0, // No action - ACTION_T_TEXT = 1, // TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values. - ACTION_T_SET_FACTION = 2, // FactionId (or 0 for default) - ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph) - ACTION_T_SOUND = 4, // SoundId - ACTION_T_EMOTE = 5, // EmoteId - ACTION_T_RANDOM_SAY = 6, // UNUSED - ACTION_T_RANDOM_YELL = 7, // UNUSED - ACTION_T_RANDOM_TEXTEMOTE = 8, // UNUSED - ACTION_T_RANDOM_SOUND = 9, // SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field) - ACTION_T_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field) - ACTION_T_CAST = 11, // SpellId, Target, CastFlags - ACTION_T_SUMMON = 12, // CreatureID, Target, Duration in ms - ACTION_T_THREAT_SINGLE_PCT = 13, // Threat%, Target - ACTION_T_THREAT_ALL_PCT = 14, // Threat% - ACTION_T_QUEST_EVENT = 15, // QuestID, Target - ACTION_T_CAST_EVENT = 16, // QuestID, SpellId, Target - must be removed as hack? - ACTION_T_SET_UNIT_FIELD = 17, // Field_Number, Value, Target - ACTION_T_SET_UNIT_FLAG = 18, // Flags (may be more than one field OR'd together), Target - ACTION_T_REMOVE_UNIT_FLAG = 19, // Flags (may be more than one field OR'd together), Target - ACTION_T_AUTO_ATTACK = 20, // AllowAttackState (0 = stop attack, anything else means continue attacking) - ACTION_T_COMBAT_MOVEMENT = 21, // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) - ACTION_T_SET_PHASE = 22, // Phase - ACTION_T_INC_PHASE = 23, // Value (may be negative to decrement phase, should not be 0) - ACTION_T_EVADE = 24, // No Params - ACTION_T_FLEE_FOR_ASSIST = 25, // No Params - ACTION_T_QUEST_EVENT_ALL = 26, // QuestID - ACTION_T_CAST_EVENT_ALL = 27, // CreatureId, SpellId - ACTION_T_REMOVEAURASFROMSPELL = 28, // Target, Spellid - ACTION_T_RANGED_MOVEMENT = 29, // Distance, Angle - ACTION_T_RANDOM_PHASE = 30, // PhaseId1, PhaseId2, PhaseId3 - ACTION_T_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax - ACTION_T_SUMMON_ID = 32, // CreatureId, Target, SpawnId - ACTION_T_KILLED_MONSTER = 33, // CreatureId, Target - ACTION_T_SET_INST_DATA = 34, // Field, Data - ACTION_T_SET_INST_DATA64 = 35, // Field, Target - ACTION_T_UPDATE_TEMPLATE = 36, // Entry, Team - ACTION_T_DIE = 37, // No Params - ACTION_T_ZONE_COMBAT_PULSE = 38, // No Params - ACTION_T_CALL_FOR_HELP = 39, // Radius - ACTION_T_SET_SHEATH = 40, // Sheath (0-passive,1-melee,2-ranged) - ACTION_T_FORCE_DESPAWN = 41, // No Params - ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue, format(0-flat,1-percent from max health) - - ACTION_T_SET_PHASE_MASK = 97, - ACTION_T_SET_STAND_STATE = 98, - ACTION_T_MOVE_RANDOM_POINT = 99, - ACTION_T_SET_VISIBILITY = 100, - ACTION_T_SET_ACTIVE = 101, //Apply - ACTION_T_SET_AGGRESSIVE = 102, //Apply - ACTION_T_ATTACK_START_PULSE = 103, //Distance - ACTION_T_SUMMON_GO = 104, //GameObjectID, DespawnTime in ms - - ACTION_T_END = 105, -}; - -enum Target -{ - //Self (me) - TARGET_T_SELF = 0, //Self cast - - //Hostile targets (if pet then returns pet owner) - TARGET_T_HOSTILE, //Our current target (ie: highest aggro) - TARGET_T_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) - TARGET_T_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) - TARGET_T_HOSTILE_RANDOM, //Just any random target on our threat list - TARGET_T_HOSTILE_RANDOM_NOT_TOP, //Any random target except top threat - - //Invoker targets (if pet then returns pet owner) - TARGET_T_ACTION_INVOKER, //Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF) - - //Hostile targets (including pets) - TARGET_T_HOSTILE_WPET, //Current target (can be a pet) - TARGET_T_HOSTILE_WPET_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) - TARGET_T_HOSTILE_WPET_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) - TARGET_T_HOSTILE_WPET_RANDOM, //Just any random target on our threat list - TARGET_T_HOSTILE_WPET_RANDOM_NOT_TOP, //Any random target except top threat - - TARGET_T_ACTION_INVOKER_WPET, - - TARGET_T_END -}; - -enum CastFlags -{ - CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting - CAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time) - CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range - CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range - CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself - CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell -}; - -enum EventFlags -{ - EFLAG_REPEATABLE = 0x01, //Event repeats - EFLAG_DIFFICULTY_0 = 0x02, //Event only occurs in instance difficulty 0 - EFLAG_DIFFICULTY_1 = 0x04, //Event only occurs in instance difficulty 1 - EFLAG_DIFFICULTY_2 = 0x08, //Event only occurs in instance difficulty 2 - EFLAG_DIFFICULTY_3 = 0x10, //Event only occurs in instance difficulty 3 - EFLAG_RESERVED_5 = 0x20, - EFLAG_RESERVED_6 = 0x40, - EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build - - EFLAG_DIFFICULTY_ALL = (EFLAG_DIFFICULTY_0|EFLAG_DIFFICULTY_1|EFLAG_DIFFICULTY_2|EFLAG_DIFFICULTY_3) -}; - -enum SpawnedEventMode -{ - SPAWNED_EVENT_ALWAY = 0, - SPAWNED_EVENT_MAP = 1, - SPAWNED_EVENT_ZONE = 2 -}; - -// String text additional data, used in (CreatureEventAI) -struct StringTextData -{ - uint32 SoundId; - uint8 Type; - uint32 Language; - uint32 Emote; -}; -// Text Maps -typedef UNORDERED_MAP CreatureEventAI_TextMap; - -struct CreatureEventAI_Action -{ - EventAI_ActionType type: 16; - union - { - // ACTION_T_TEXT = 1 - struct - { - int32 TextId1; - int32 TextId2; - int32 TextId3; - } text; - // ACTION_T_SET_FACTION = 2 - struct - { - uint32 factionId; // faction or 0 for default) - } set_faction; - // ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3 - struct - { - uint32 creatureId; // set one from fields (or 0 for both to demorph) - uint32 modelId; - } morph; - // ACTION_T_SOUND = 4 - struct - { - uint32 soundId; - } sound; - // ACTION_T_EMOTE = 5 - struct - { - uint32 emoteId; - } emote; - // ACTION_T_RANDOM_SOUND = 9 - struct - { - int32 soundId1; // (-1 in any field means no output if randomed that field) - int32 soundId2; - int32 soundId3; - } random_sound; - // ACTION_T_RANDOM_EMOTE = 10 - struct - { - int32 emoteId1; // (-1 in any field means no output if randomed that field) - int32 emoteId2; - int32 emoteId3; - } random_emote; - // ACTION_T_CAST = 11 - struct - { - uint32 spellId; - uint32 target; - uint32 castFlags; - } cast; - // ACTION_T_SUMMON = 12 - struct - { - uint32 creatureId; - uint32 target; - uint32 duration; - } summon; - // ACTION_T_THREAT_SINGLE_PCT = 13 - struct - { - int32 percent; - uint32 target; - } threat_single_pct; - // ACTION_T_THREAT_ALL_PCT = 14 - struct - { - int32 percent; - } threat_all_pct; - // ACTION_T_QUEST_EVENT = 15 - struct - { - uint32 questId; - uint32 target; - } quest_event; - // ACTION_T_CAST_EVENT = 16 - struct - { - uint32 creatureId; - uint32 spellId; - uint32 target; - } cast_event; - // ACTION_T_SET_UNIT_FIELD = 17 - struct - { - uint32 field; - uint32 value; - uint32 target; - } set_unit_field; - // ACTION_T_SET_UNIT_FLAG = 18, // value provided mask bits that will be set - // ACTION_T_REMOVE_UNIT_FLAG = 19, // value provided mask bits that will be clear - struct - { - uint32 value; - uint32 target; - } unit_flag; - // ACTION_T_AUTO_ATTACK = 20 - struct - { - uint32 state; // 0 = stop attack, anything else means continue attacking - } auto_attack; - // ACTION_T_COMBAT_MOVEMENT = 21 - struct - { - uint32 state; // 0 = stop combat based movement, anything else continue attacking - uint32 melee; // if set: at stop send melee combat stop if in combat, use for terminate melee fighting state for switch to ranged - } combat_movement; - // ACTION_T_SET_PHASE = 22 - struct - { - uint32 phase; - } set_phase; - // ACTION_T_INC_PHASE = 23 - struct - { - int32 step; - } set_inc_phase; - // ACTION_T_QUEST_EVENT_ALL = 26 - struct - { - uint32 questId; - } quest_event_all; - // ACTION_T_CAST_EVENT_ALL = 27 - struct - { - uint32 creatureId; - uint32 spellId; - } cast_event_all; - // ACTION_T_REMOVEAURASFROMSPELL = 28 - struct - { - uint32 target; - uint32 spellId; - } remove_aura; - // ACTION_T_RANGED_MOVEMENT = 29 - struct - { - uint32 distance; - int32 angle; - } ranged_movement; - // ACTION_T_RANDOM_PHASE = 30 - struct - { - uint32 phase1; - uint32 phase2; - uint32 phase3; - } random_phase; - // ACTION_T_RANDOM_PHASE_RANGE = 31 - struct - { - uint32 phaseMin; - uint32 phaseMax; - } random_phase_range; - // ACTION_T_SUMMON_ID = 32 - struct - { - uint32 creatureId; - uint32 target; - uint32 spawnId; - } summon_id; - // ACTION_T_KILLED_MONSTER = 33 - struct - { - uint32 creatureId; - uint32 target; - } killed_monster; - // ACTION_T_SET_INST_DATA = 34 - struct - { - uint32 field; - uint32 value; - } set_inst_data; - // ACTION_T_SET_INST_DATA64 = 35 - struct - { - uint32 field; - uint32 target; - } set_inst_data64; - // ACTION_T_UPDATE_TEMPLATE = 36 - struct - { - uint32 creatureId; - uint32 team; - } update_template; - // ACTION_T_CALL_FOR_HELP = 39 - struct - { - uint32 radius; - } call_for_help; - // ACTION_T_SET_SHEATH = 40 - struct - { - uint32 sheath; - } set_sheath; - // ACTION_T_FORCE_DESPAWN = 41 - struct - { - uint32 msDelay; - } forced_despawn; - // ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42 - struct - { - uint32 hp_level; - uint32 is_percent; - } invincibility_hp_level; - // RAW - struct - { - uint32 param1; - uint32 param2; - uint32 param3; - } raw; - }; -}; - -struct CreatureEventAI_Event -{ - uint32 event_id; - - uint32 creature_id; - - uint32 event_inverse_phase_mask; - - EventAI_Type event_type : 16; - uint8 event_chance : 8; - uint8 event_flags : 8; - - union - { - // EVENT_T_TIMER = 0 - // EVENT_T_TIMER_OOC = 1 - struct - { - uint32 initialMin; - uint32 initialMax; - uint32 repeatMin; - uint32 repeatMax; - } timer; - // EVENT_T_HP = 2 - // EVENT_T_MANA = 3 - // EVENT_T_TARGET_HP = 12 - // EVENT_T_TARGET_MANA = 18 - struct - { - uint32 percentMax; - uint32 percentMin; - uint32 repeatMin; - uint32 repeatMax; - } percent_range; - // EVENT_T_KILL = 5 - struct - { - uint32 repeatMin; - uint32 repeatMax; - } kill; - // EVENT_T_SPELLHIT = 8 - struct - { - uint32 spellId; - uint32 schoolMask; // -1 ( == 0xffffffff) is ok value for full mask, or must be more limited mask like (0 < 1) = 1 for normal/physical school - uint32 repeatMin; - uint32 repeatMax; - } spell_hit; - // EVENT_T_RANGE = 9 - struct - { - uint32 minDist; - uint32 maxDist; - uint32 repeatMin; - uint32 repeatMax; - } range; - // EVENT_T_OOC_LOS = 10 - struct - { - uint32 noHostile; - uint32 maxRange; - uint32 repeatMin; - uint32 repeatMax; - } ooc_los; - // EVENT_T_SPAWNED = 11 - struct - { - uint32 condition; - uint32 conditionValue1; - } spawned; - // EVENT_T_TARGET_CASTING = 13 - struct - { - uint32 repeatMin; - uint32 repeatMax; - } target_casting; - // EVENT_T_FRIENDLY_HP = 14 - struct - { - uint32 hpDeficit; - uint32 radius; - uint32 repeatMin; - uint32 repeatMax; - } friendly_hp; - // EVENT_T_FRIENDLY_IS_CC = 15 - struct - { - uint32 dispelType; // unused ? - uint32 radius; - uint32 repeatMin; - uint32 repeatMax; - } friendly_is_cc; - // EVENT_T_FRIENDLY_MISSING_BUFF = 16 - struct - { - uint32 spellId; - uint32 radius; - uint32 repeatMin; - uint32 repeatMax; - } friendly_buff; - // EVENT_T_SUMMONED_UNIT = 17 - struct - { - uint32 creatureId; - uint32 repeatMin; - uint32 repeatMax; - } summon_unit; - // EVENT_T_QUEST_ACCEPT = 19 - // EVENT_T_QUEST_COMPLETE = 20 - struct - { - uint32 questId; - } quest; - // EVENT_T_RECEIVE_EMOTE = 22 - struct - { - uint32 emoteId; - uint32 condition; - uint32 conditionValue1; - uint32 conditionValue2; - } receive_emote; - // EVENT_T_BUFFED = 23 - // EVENT_T_TARGET_BUFFED = 24 - struct - { - uint32 spellId; - uint32 amount; - uint32 repeatMin; - uint32 repeatMax; - } buffed; - - // RAW - struct - { - uint32 param1; - uint32 param2; - uint32 param3; - uint32 param4; - } raw; - }; - - CreatureEventAI_Action action[MAX_ACTIONS]; -}; -//Event_Map -typedef UNORDERED_MAP > CreatureEventAI_Event_Map; - -struct CreatureEventAI_Summon -{ - uint32 id; - - float position_x; - float position_y; - float position_z; - float orientation; - uint32 SpawnTimeSecs; -}; - -//EventSummon_Map -typedef UNORDERED_MAP CreatureEventAI_Summon_Map; - -struct CreatureEventAIHolder -{ - CreatureEventAIHolder(CreatureEventAI_Event p) : Event(p), Time(0), Enabled(true){} - - CreatureEventAI_Event Event; - uint32 Time; - bool Enabled; - - // helper - bool UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax); -}; - -class CreatureEventAI : public CreatureAI -{ - - public: - explicit CreatureEventAI(Creature *c); - ~CreatureEventAI() - { - CreatureEventAIList.clear(); - } - void JustRespawned(); - void Reset(); - void JustReachedHome(); - void EnterCombat(Unit *enemy); - void EnterEvadeMode(); - void JustDied(Unit* /*killer*/); - void KilledUnit(Unit* victim); - void JustSummoned(Creature* pUnit); - void AttackStart(Unit *who); - void MoveInLineOfSight(Unit *who); - void SpellHit(Unit* pUnit, const SpellEntry* pSpell); - void DamageTaken(Unit* done_by, uint32& damage); - void UpdateAI(const uint32 diff); - void ReceiveEmote(Player* pPlayer, uint32 text_emote); - static int Permissible(const Creature *); - - bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL); - void ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker); - inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3); - inline int32 GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3); - inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker); - - void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target); - bool CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered); - - bool SpawnedEventConditionsCheck(CreatureEventAI_Event const& event); - - Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff); - void DoFindFriendlyMissingBuff(std::list& _list, float range, uint32 spellid); - void DoFindFriendlyCC(std::list& _list, float range); - - //Holder for events (stores enabled, time, and eventid) - std::list CreatureEventAIList; - uint32 EventUpdateTime; //Time between event updates - uint32 EventDiff; //Time between the last event call - bool bEmptyList; - - //Variables used by Events themselves - uint8 Phase; // Current phase, max 32 phases - bool CombatMovementEnabled; // If we allow targeted movment gen (movement twoards top threat) - bool MeleeEnabled; // If we allow melee auto attack - float AttackDistance; // Distance to attack from - float AttackAngle; // Angle of attack - uint32 InvinceabilityHpLevel; // Minimal health level allowed at damage apply -}; -#endif diff --git a/src/server/game/CreatureEventAIMgr.cpp b/src/server/game/CreatureEventAIMgr.cpp deleted file mode 100644 index 83d62ca74dc..00000000000 --- a/src/server/game/CreatureEventAIMgr.cpp +++ /dev/null @@ -1,745 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "Database/SQLStorage.h" -#include "CreatureEventAI.h" -#include "CreatureEventAIMgr.h" -#include "ObjectMgr.h" -#include "ProgressBar.h" -#include "Policies/SingletonImp.h" -#include "ObjectDefines.h" -#include "GridDefines.h" -#include "ConditionMgr.h" - -INSTANTIATE_SINGLETON_1(CreatureEventAIMgr); - -// ------------------- -void CreatureEventAIMgr::LoadCreatureEventAI_Texts() -{ - // Drop Existing Text Map, only done once and we are ready to add data from multiple sources. - m_CreatureEventAI_TextMap.clear(); - - // Load EventAI Text - objmgr.LoadTrinityStrings(WorldDatabase,"creature_ai_texts",MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID); - - // Gather Additional data from EventAI Texts - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, sound, type, language, emote FROM creature_ai_texts"); - - sLog.outString("Loading EventAI Texts additional data..."); - if (result) - { - barGoLink bar(result->GetRowCount()); - uint32 count = 0; - - do - { - bar.step(); - Field* fields = result->Fetch(); - StringTextData temp; - - int32 i = fields[0].GetInt32(); - temp.SoundId = fields[1].GetInt32(); - temp.Type = fields[2].GetInt32(); - temp.Language = fields[3].GetInt32(); - temp.Emote = fields[4].GetInt32(); - - // range negative - if (i > MIN_CREATURE_AI_TEXT_STRING_ID || i <= MAX_CREATURE_AI_TEXT_STRING_ID) - { - sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not in valid range(%d-%d)",i,MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID); - continue; - } - - // range negative (don't must be happen, loaded from same table) - if (!objmgr.GetTrinityStringLocale(i)) - { - sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` not found",i); - continue; - } - - if (temp.SoundId) - { - if (!sSoundEntriesStore.LookupEntry(temp.SoundId)) - sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Sound %u but sound does not exist.",i,temp.SoundId); - } - - if (!GetLanguageDescByID(temp.Language)) - sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.",i,temp.Language); - - if (temp.Type > CHAT_TYPE_ZONE_YELL) - sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.",i,temp.Type); - - if (temp.Emote) - { - if (!sEmotesStore.LookupEntry(temp.Emote)) - sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Emote %u but emote does not exist.",i,temp.Emote); - } - - m_CreatureEventAI_TextMap[i] = temp; - ++count; - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u additional CreatureEventAI Texts data.", count); - } - else - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty."); - } - -} - -// ------------------- -void CreatureEventAIMgr::LoadCreatureEventAI_Summons() -{ - - //Drop Existing EventSummon Map - m_CreatureEventAI_Summon_Map.clear(); - - // Gather additional data for EventAI - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons"); - if (result) - { - barGoLink bar(result->GetRowCount()); - uint32 Count = 0; - - do - { - bar.step(); - Field *fields = result->Fetch(); - - CreatureEventAI_Summon temp; - - uint32 i = fields[0].GetUInt32(); - temp.position_x = fields[1].GetFloat(); - temp.position_y = fields[2].GetFloat(); - temp.position_z = fields[3].GetFloat(); - temp.orientation = fields[4].GetFloat(); - temp.SpawnTimeSecs = fields[5].GetUInt32(); - - if (!Trinity::IsValidMapCoord(temp.position_x,temp.position_y,temp.position_z,temp.orientation)) - { - sLog.outErrorDb("CreatureEventAI: Summon id %u have wrong coordinates (%f,%f,%f,%f), skipping.", i,temp.position_x,temp.position_y,temp.position_z,temp.orientation); - continue; - } - - //Add to map - m_CreatureEventAI_Summon_Map[i] = temp; - ++Count; - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count); - } - else - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty."); - } - -} - -// ------------------- -void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() -{ - //Drop Existing EventAI List - m_CreatureEventAI_Event_Map.clear(); - - // Gather event data - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " - "event_param1, event_param2, event_param3, event_param4, " - "action1_type, action1_param1, action1_param2, action1_param3, " - "action2_type, action2_param1, action2_param2, action2_param3, " - "action3_type, action3_param1, action3_param2, action3_param3 " - "FROM creature_ai_scripts"); - if (result) - { - barGoLink bar(result->GetRowCount()); - uint32 Count = 0; - - do - { - bar.step(); - Field *fields = result->Fetch(); - - CreatureEventAI_Event temp; - temp.event_id = EventAI_Type(fields[0].GetUInt32()); - uint32 i = temp.event_id; - - temp.creature_id = fields[1].GetUInt32(); - uint32 creature_id = temp.creature_id; - - uint32 e_type = fields[2].GetUInt32(); - //Report any errors in event - if (e_type >= EVENT_T_END) - { - sLog.outErrorDb("CreatureEventAI: Event %u have wrong type (%u), skipping.", i,e_type); - continue; - } - temp.event_type = EventAI_Type(e_type); - - temp.event_inverse_phase_mask = fields[3].GetUInt32(); - temp.event_chance = fields[4].GetUInt8(); - temp.event_flags = fields[5].GetUInt8(); - temp.raw.param1 = fields[6].GetUInt32(); - temp.raw.param2 = fields[7].GetUInt32(); - temp.raw.param3 = fields[8].GetUInt32(); - temp.raw.param4 = fields[9].GetUInt32(); - - //Creature does not exist in database - if (!sCreatureStorage.LookupEntry(temp.creature_id)) - { - sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id); - continue; - } - - //No chance of this event occuring - if (temp.event_chance == 0) - sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i); - //Chance above 100, force it to be 100 - else if (temp.event_chance > 100) - { - sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i); - temp.event_chance = 100; - } - - //Individual event checks - switch (temp.event_type) - { - case EVENT_T_TIMER: - case EVENT_T_TIMER_OOC: - if (temp.timer.initialMax < temp.timer.initialMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i); - if (temp.timer.repeatMax < temp.timer.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_HP: - case EVENT_T_MANA: - case EVENT_T_TARGET_HP: - case EVENT_T_TARGET_MANA: - if (temp.percent_range.percentMax > 100) - sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); - - if (temp.percent_range.percentMax <= temp.percent_range.percentMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i); - - if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax) - { - sLog.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i); - temp.event_flags &= ~EFLAG_REPEATABLE; - } - break; - case EVENT_T_SPELLHIT: - if (temp.spell_hit.spellId) - { - SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); - if (!pSpell) - { - sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); - continue; - } - - if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask) - sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i); - } - - if (!temp.spell_hit.schoolMask) - sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i); - - if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_RANGE: - if (temp.range.maxDist < temp.range.minDist) - sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i); - if (temp.range.repeatMax < temp.range.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_OOC_LOS: - if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_SPAWNED: - switch(temp.spawned.condition) - { - case SPAWNED_EVENT_ALWAY: - break; - case SPAWNED_EVENT_MAP: - if (!sMapStore.LookupEntry(temp.spawned.conditionValue1)) - sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'map specific' but with not existed map (%u) in param2. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); - break; - case SPAWNED_EVENT_ZONE: - if (!GetAreaEntryByAreaID(temp.spawned.conditionValue1)) - sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'area specific' but with not existed area (%u) in param2. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); - default: - sLog.outErrorDb("CreatureEventAI: Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition); - break; - } - break; - case EVENT_T_FRIENDLY_HP: - if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_FRIENDLY_IS_CC: - if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_FRIENDLY_MISSING_BUFF: - { - SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); - if (!pSpell) - { - sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); - continue; - } - if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - } - case EVENT_T_KILL: - if (temp.kill.repeatMax < temp.kill.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_TARGET_CASTING: - if (temp.target_casting.repeatMax < temp.target_casting.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_SUMMONED_UNIT: - if (!sCreatureStorage.LookupEntry(temp.summon_unit.creatureId)) - sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summon_unit.creatureId); - if (temp.summon_unit.repeatMax < temp.summon_unit.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - case EVENT_T_QUEST_ACCEPT: - case EVENT_T_QUEST_COMPLETE: - if (!objmgr.GetQuestTemplate(temp.quest.questId)) - sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed qyest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId); - sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i); - continue; - - case EVENT_T_AGGRO: - case EVENT_T_DEATH: - case EVENT_T_EVADE: - case EVENT_T_REACHED_HOME: - { - if (temp.event_flags & EFLAG_REPEATABLE) - { - sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i); - temp.event_flags &= ~EFLAG_REPEATABLE; - } - - break; - } - - case EVENT_T_RECEIVE_EMOTE: - { - if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId)) - { - sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId); - continue; - } - if (temp.receive_emote.condition) - { - Condition* cond = new Condition(); - cond->mConditionType = ConditionType(temp.receive_emote.condition); - cond->mConditionValue1 = temp.receive_emote.conditionValue1; - cond->mConditionValue2 = temp.receive_emote.conditionValue2; - if (!sConditionMgr.isConditionTypeValid(cond)) - { - sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition); - continue; - } - } - - if (!(temp.event_flags & EFLAG_REPEATABLE)) - { - sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i); - temp.event_flags |= EFLAG_REPEATABLE; - } - - break; - } - - case EVENT_T_BUFFED: - case EVENT_T_TARGET_BUFFED: - { - SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId); - if (!pSpell) - { - sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); - continue; - } - if (temp.buffed.repeatMax < temp.buffed.repeatMin) - sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); - break; - } - - default: - sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i); - break; - } - - for (uint32 j = 0; j < MAX_ACTIONS; j++) - { - uint16 action_type = fields[10+(j*4)].GetUInt16(); - if (action_type >= ACTION_T_END) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j+1, action_type); - temp.action[j].type = ACTION_T_NONE; - continue; - } - - CreatureEventAI_Action& action = temp.action[j]; - - action.type = EventAI_ActionType(action_type); - action.raw.param1 = fields[11+(j*4)].GetUInt32(); - action.raw.param2 = fields[12+(j*4)].GetUInt32(); - action.raw.param3 = fields[13+(j*4)].GetUInt32(); - - //Report any errors in actions - switch (action.type) - { - case ACTION_T_NONE: - break; - case ACTION_T_TEXT: - { - if (action.text.TextId1 < 0) - { - if (m_CreatureEventAI_TextMap.find(action.text.TextId1) == m_CreatureEventAI_TextMap.end()) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1); - } - if (action.text.TextId2 < 0) - { - if (m_CreatureEventAI_TextMap.find(action.text.TextId2) == m_CreatureEventAI_TextMap.end()) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1); - - if (!action.text.TextId1) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1); - } - if (action.text.TextId3 < 0) - { - if (m_CreatureEventAI_TextMap.find(action.text.TextId3) == m_CreatureEventAI_TextMap.end()) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1); - - if (!action.text.TextId1 || !action.text.TextId2) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1); - } - break; - } - case ACTION_T_SET_FACTION: - if (action.set_faction.factionId !=0 && !sFactionStore.LookupEntry(action.set_faction.factionId)) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i, j+1, action.set_faction.factionId); - action.set_faction.factionId = 0; - } - break; - case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: - if (action.morph.creatureId !=0 || action.morph.modelId !=0) - { - if (action.morph.creatureId && !sCreatureStorage.LookupEntry(action.morph.creatureId)) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, action.morph.creatureId); - action.morph.creatureId = 0; - } - - if (action.morph.modelId) - { - if (action.morph.creatureId) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j+1, action.morph.modelId,action.morph.creatureId); - action.morph.modelId = 0; - } - else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId)) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, action.morph.modelId); - action.morph.modelId = 0; - } - } - } - break; - case ACTION_T_SOUND: - if (!sSoundEntriesStore.LookupEntry(action.sound.soundId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId); - break; - case ACTION_T_EMOTE: - if (!sEmotesStore.LookupEntry(action.emote.emoteId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.emote.emoteId); - break; - case ACTION_T_RANDOM_SOUND: - if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId1); - if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId2); - if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId3); - break; - case ACTION_T_RANDOM_EMOTE: - if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId1); - if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId2); - if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId3); - break; - case ACTION_T_CAST: - { - const SpellEntry *spell = sSpellStore.LookupEntry(action.cast.spellId); - if (!spell) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast.spellId); - /* FIXME: temp.raw.param3 not have event tipes with recovery time in it.... - else - { - if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE) - { - //output as debug for now, also because there's no general rule all spells have RecoveryTime - if (temp.event_param3 < spell->RecoveryTime) - sLog.outDebug("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3); - } - } - */ - - //Cast is always triggered if target is forced to cast on self - if (action.cast.castFlags & CAST_FORCE_TARGET_SELF) - action.cast.castFlags |= CAST_TRIGGERED; - - if (action.cast.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - } - case ACTION_T_SUMMON: - if (!sCreatureStorage.LookupEntry(action.summon.creatureId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.summon.creatureId); - - if (action.summon.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_THREAT_SINGLE_PCT: - if (std::abs(action.threat_single_pct.percent) > 100) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_single_pct.percent); - if (action.threat_single_pct.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_THREAT_ALL_PCT: - if (std::abs(action.threat_all_pct.percent) > 100) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_all_pct.percent); - break; - case ACTION_T_QUEST_EVENT: - if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event.questId)) - { - if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event.questId); - } - else - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event.questId); - - if (action.quest_event.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - - break; - case ACTION_T_CAST_EVENT: - if (!sCreatureStorage.LookupEntry(action.cast_event.creatureId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event.creatureId); - if (!sSpellStore.LookupEntry(action.cast_event.spellId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event.spellId); - if (action.cast_event.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_SET_UNIT_FIELD: - if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1); - if (action.set_unit_field.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_SET_UNIT_FLAG: - case ACTION_T_REMOVE_UNIT_FLAG: - if (action.unit_flag.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_SET_PHASE: - if (action.set_phase.phase >= MAX_PHASE) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); - break; - case ACTION_T_INC_PHASE: - if (action.set_inc_phase.step == 0) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1); - else if (std::abs(action.set_inc_phase.step) > MAX_PHASE-1) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u is change phase by too large for any use %i.", i, j+1, action.set_inc_phase.step); - break; - case ACTION_T_QUEST_EVENT_ALL: - if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event_all.questId)) - { - if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event_all.questId); - } - else - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event_all.questId); - break; - case ACTION_T_CAST_EVENT_ALL: - if (!sCreatureStorage.LookupEntry(action.cast_event_all.creatureId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event_all.creatureId); - if (!sSpellStore.LookupEntry(action.cast_event_all.spellId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event_all.spellId); - break; - case ACTION_T_REMOVEAURASFROMSPELL: - if (!sSpellStore.LookupEntry(action.remove_aura.spellId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.remove_aura.spellId); - if (action.remove_aura.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_RANDOM_PHASE: //PhaseId1, PhaseId2, PhaseId3 - if (action.random_phase.phase1 >= MAX_PHASE) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); - if (action.random_phase.phase2 >= MAX_PHASE) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); - if (action.random_phase.phase3 >= MAX_PHASE) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); - break; - case ACTION_T_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax - if (action.random_phase_range.phaseMin >= MAX_PHASE) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); - if (action.random_phase_range.phaseMin >= MAX_PHASE) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); - if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j+1); - std::swap(action.random_phase_range.phaseMin,action.random_phase_range.phaseMax); - // equal case processed at call - } - break; - case ACTION_T_SUMMON_ID: - if (!sCreatureStorage.LookupEntry(action.summon_id.creatureId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.summon_id.creatureId); - if (action.summon_id.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end()) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, action.summon_id.spawnId); - break; - case ACTION_T_KILLED_MONSTER: - if (!sCreatureStorage.LookupEntry(action.killed_monster.creatureId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.killed_monster.creatureId); - if (action.killed_monster.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_SET_INST_DATA: - if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1); - if (action.set_inst_data.value > 4/*SPECIAL*/) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1); - break; - case ACTION_T_SET_INST_DATA64: - if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1); - if (action.set_inst_data64.target >= TARGET_T_END) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); - break; - case ACTION_T_UPDATE_TEMPLATE: - if (!sCreatureStorage.LookupEntry(action.update_template.creatureId)) - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.update_template.creatureId); - break; - case ACTION_T_SET_SHEATH: - if (action.set_sheath.sheath >= MAX_SHEATH_STATE) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i, j+1, action.set_sheath.sheath); - action.set_sheath.sheath = SHEATH_STATE_UNARMED; - } - break; - case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: - if (action.invincibility_hp_level.is_percent) - { - if (action.invincibility_hp_level.hp_level > 100) - { - sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i, j+1, action.invincibility_hp_level.hp_level); - action.invincibility_hp_level.hp_level = 100; - } - } - break; - case ACTION_T_EVADE: //No Params - case ACTION_T_FLEE_FOR_ASSIST: //No Params - case ACTION_T_DIE: //No Params - case ACTION_T_ZONE_COMBAT_PULSE: //No Params - case ACTION_T_FORCE_DESPAWN: //No Params - case ACTION_T_AUTO_ATTACK: //AllowAttackState (0 = stop attack, anything else means continue attacking) - case ACTION_T_COMBAT_MOVEMENT: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) - case ACTION_T_RANGED_MOVEMENT: //Distance, Angle - case ACTION_T_CALL_FOR_HELP: //Distance - break; - - case ACTION_T_RANDOM_SAY: - case ACTION_T_RANDOM_YELL: - case ACTION_T_RANDOM_TEXTEMOTE: - sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1); - break; - - case ACTION_T_MOVE_RANDOM_POINT: - case ACTION_T_SET_STAND_STATE: - case ACTION_T_SET_PHASE_MASK: - case ACTION_T_SET_VISIBILITY: - case ACTION_T_SET_ACTIVE: - case ACTION_T_SET_AGGRESSIVE: - case ACTION_T_ATTACK_START_PULSE: - case ACTION_T_SUMMON_GO: - break; - - default: - sLog.outErrorDb("CreatureEventAI: Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j+1, temp.action[j].type); - break; - } - } - - //Add to list - m_CreatureEventAI_Event_Map[creature_id].push_back(temp); - ++Count; - - if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(temp.creature_id)) - { - if (!cInfo->AIName || !cInfo->AIName[0]) - { - //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but its AIName is empty. Set to EventAI as default.", cInfo->Entry); - size_t len = strlen("EventAI")+1; - const_cast(cInfo)->AIName = new char[len]; - strncpy(const_cast(cInfo->AIName), "EventAI", len); - } - if (strcmp(cInfo->AIName, "EventAI")) - { - //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it has AIName %s. EventAI script will be overriden.", cInfo->Entry, cInfo->AIName); - } - if (cInfo->ScriptID) - { - //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it also has C++ script. EventAI script will be overriden.", cInfo->Entry); - } - } - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u CreatureEventAI scripts", Count); - } - else - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty."); - } -} diff --git a/src/server/game/CreatureEventAIMgr.h b/src/server/game/CreatureEventAIMgr.h deleted file mode 100644 index ef191b22463..00000000000 --- a/src/server/game/CreatureEventAIMgr.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_CREATURE_EAI_MGR_H -#define TRINITY_CREATURE_EAI_MGR_H - -#include "Common.h" -#include "CreatureEventAI.h" - -class CreatureEventAIMgr -{ - public: - CreatureEventAIMgr(){}; - ~CreatureEventAIMgr(){}; - - void LoadCreatureEventAI_Texts(); - void LoadCreatureEventAI_Summons(); - void LoadCreatureEventAI_Scripts(); - - CreatureEventAI_Event_Map const& GetCreatureEventAIMap() const { return m_CreatureEventAI_Event_Map; } - CreatureEventAI_Summon_Map const& GetCreatureEventAISummonMap() const { return m_CreatureEventAI_Summon_Map; } - CreatureEventAI_TextMap const& GetCreatureEventAITextMap() const { return m_CreatureEventAI_TextMap; } - - private: - CreatureEventAI_Event_Map m_CreatureEventAI_Event_Map; - CreatureEventAI_Summon_Map m_CreatureEventAI_Summon_Map; - CreatureEventAI_TextMap m_CreatureEventAI_TextMap; -}; - -#define CreatureEAI_Mgr Trinity::Singleton::Instance() -#endif diff --git a/src/server/game/CreatureGroups.cpp b/src/server/game/CreatureGroups.cpp deleted file mode 100644 index c2af59458f7..00000000000 --- a/src/server/game/CreatureGroups.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Creature.h" -#include "CreatureGroups.h" -#include "ObjectMgr.h" -#include "ProgressBar.h" -#include "Policies/SingletonImp.h" -#include "CreatureAI.h" - -#define MAX_DESYNC 5.0f - -INSTANTIATE_SINGLETON_1(CreatureGroupManager); - -CreatureGroupInfoType CreatureGroupMap; - -void CreatureGroupManager::AddCreatureToGroup(uint32 groupId, Creature *member) -{ - Map *map = member->FindMap(); - if (!map) - return; - - CreatureGroupHolderType::iterator itr = map->CreatureGroupHolder.find(groupId); - - //Add member to an existing group - if (itr != map->CreatureGroupHolder.end()) - { - sLog.outDebug("Group found: %u, inserting creature GUID: %u, Group InstanceID %u", groupId, member->GetGUIDLow(), member->GetInstanceId()); - itr->second->AddMember(member); - } - //Create new group - else - { - sLog.outDebug("Group not found: %u. Creating new group.", groupId); - CreatureGroup* group = new CreatureGroup(groupId); - map->CreatureGroupHolder[groupId] = group; - group->AddMember(member); - } -} - -void CreatureGroupManager::RemoveCreatureFromGroup(CreatureGroup *group, Creature *member) -{ - sLog.outDebug("Deleting member pointer to GUID: %u from group %u", group->GetId(), member->GetDBTableGUIDLow()); - group->RemoveMember(member); - - if (group->isEmpty()) - { - Map *map = member->FindMap(); - if (!map) - return; - - sLog.outDebug("Deleting group with InstanceID %u", member->GetInstanceId()); - map->CreatureGroupHolder.erase(group->GetId()); - delete group; - } -} - -void CreatureGroupManager::LoadCreatureFormations() -{ - //Clear existing map - CreatureGroupMap.clear(); - - //Check Integrity of the table - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT MAX(leaderGUID) FROM creature_formations"); - - if (!result) - { - sLog.outErrorDb(" ...an error occured while loading the table creature_formations (maybe it doesn't exist ?)\n"); - return; - } - - //Get group data - result = WorldDatabase.Query("SELECT leaderGUID, memberGUID, dist, angle, groupAI FROM creature_formations ORDER BY leaderGUID"); - - if (!result) - { - sLog.outErrorDb("The table creature_formations is empty or corrupted"); - return; - } - - uint32 total_records = result->GetRowCount(); - barGoLink bar(total_records); - Field *fields; - - FormationInfo *group_member; - //Loading data... - do - { - fields = result->Fetch(); - - bar.step(); - //Load group member data - group_member = new FormationInfo; - group_member->leaderGUID = fields[0].GetUInt32(); - uint32 memberGUID = fields[1].GetUInt32(); - group_member->groupAI = fields[4].GetUInt8(); - //If creature is group leader we may skip loading of dist/angle - if (group_member->leaderGUID != memberGUID) - { - group_member->follow_dist = fields[2].GetFloat(); - group_member->follow_angle = fields[3].GetFloat() * M_PI / 180; - } - else - { - group_member->follow_dist = 0; - group_member->follow_angle = 0; - } - - // check data correctness - { - QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE guid = %u", group_member->leaderGUID); - if (!result) - { - sLog.outErrorDb("creature_formations table leader guid %u incorrect (not exist)", group_member->leaderGUID); - delete group_member; - continue; - } - - result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE guid = %u", memberGUID); - if (!result) - { - sLog.outErrorDb("creature_formations table member guid %u incorrect (not exist)", memberGUID); - delete group_member; - continue; - } - } - - CreatureGroupMap[memberGUID] = group_member; - } - while (result->NextRow()) ; - - sLog.outString(); - sLog.outString(">> Loaded %u creatures in formations", total_records); - sLog.outString(); -} - -void CreatureGroup::AddMember(Creature *member) -{ - sLog.outDebug("CreatureGroup::AddMember: Adding unit GUID: %u.", member->GetGUIDLow()); - - //Check if it is a leader - if (member->GetDBTableGUIDLow() == m_groupID) - { - sLog.outDebug("Unit GUID: %u is formation leader. Adding group.", member->GetGUIDLow()); - m_leader = member; - } - - m_members[member] = CreatureGroupMap.find(member->GetDBTableGUIDLow())->second; - member->SetFormation(this); -} - -void CreatureGroup::RemoveMember(Creature *member) -{ - if (m_leader == member) - m_leader = NULL; - - m_members.erase(member); - member->SetFormation(NULL); -} - -void CreatureGroup::MemberAttackStart(Creature *member, Unit *target) -{ - uint8 groupAI = CreatureGroupMap[member->GetDBTableGUIDLow()]->groupAI; - if (!groupAI) - return; - - if (groupAI == 1 && member != m_leader) - return; - - for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - if (m_leader) // avoid crash if leader was killed and reset. - sLog.outDebug("GROUP ATTACK: group instance id %u calls member instid %u", m_leader->GetInstanceId(), member->GetInstanceId()); - - //Skip one check - if (itr->first == member) - continue; - - if (!itr->first->isAlive()) - continue; - - if (itr->first->getVictim()) - continue; - - if (itr->first->canAttack(target) && itr->first->AI()) - itr->first->AI()->AttackStart(target); - } -} - -void CreatureGroup::FormationReset(bool dismiss) -{ - for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - if (itr->first != m_leader && itr->first->isAlive()) - { - if (dismiss) - itr->first->GetMotionMaster()->Initialize(); - else - itr->first->GetMotionMaster()->MoveIdle(MOTION_SLOT_IDLE); - sLog.outDebug("Set %s movement for member GUID: %u", dismiss ? "default" : "idle", itr->first->GetGUIDLow()); - } - } - m_Formed = !dismiss; -} - -void CreatureGroup::LeaderMoveTo(float x, float y, float z) -{ - if (!m_leader) - return; - - float pathangle = atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x); - - for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr) - { - Creature *member = itr->first; - if (member == m_leader || !member->isAlive() || member->getVictim()) - continue; - - float angle = itr->second->follow_angle; - float dist = itr->second->follow_dist; - - float dx = x + cos(angle + pathangle) * dist; - float dy = y + sin(angle + pathangle) * dist; - float dz = z; - - Trinity::NormalizeMapCoord(dx); - Trinity::NormalizeMapCoord(dy); - - member->UpdateGroundPositionZ(dx, dy, dz); - - if (member->IsWithinDist(m_leader, dist + MAX_DESYNC)) - member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags()); - else - member->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - - member->GetMotionMaster()->MovePoint(0, dx, dy, dz); - member->SetHomePosition(dx, dy, dz, pathangle); - } -} diff --git a/src/server/game/CreatureGroups.h b/src/server/game/CreatureGroups.h deleted file mode 100644 index 521586b7457..00000000000 --- a/src/server/game/CreatureGroups.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _FORMATIONS_H -#define _FORMATIONS_H - -#include "Common.h" - -class CreatureGroup; - -struct FormationInfo -{ - uint32 leaderGUID; - float follow_dist; - float follow_angle; - uint8 groupAI; -}; - -class CreatureGroupManager -{ - public: - void AddCreatureToGroup(uint32 group_id, Creature *creature); - void RemoveCreatureFromGroup(CreatureGroup *group, Creature *creature); - void LoadCreatureFormations(); -}; - -typedef UNORDERED_MAP CreatureGroupInfoType; - -extern CreatureGroupInfoType CreatureGroupMap; - -class CreatureGroup -{ - private: - Creature *m_leader; //Important do not forget sometimes to work with pointers instead synonims :D:D - typedef std::map CreatureGroupMemberType; - CreatureGroupMemberType m_members; - - uint32 m_groupID; - bool m_Formed; - - public: - //Group cannot be created empty - explicit CreatureGroup(uint32 id) : m_groupID(id), m_leader(NULL), m_Formed(false) {} - ~CreatureGroup() { sLog.outDebug("Destroying group"); } - - Creature* getLeader() const { return m_leader; } - uint32 GetId() const { return m_groupID; } - bool isEmpty() const { return m_members.empty(); } - bool isFormed() const { return m_Formed; } - - void AddMember(Creature *member); - void RemoveMember(Creature *member); - void FormationReset(bool dismiss); - - void LeaderMoveTo(float x, float y, float z); - void MemberAttackStart(Creature* member, Unit *target); -}; - -#define formation_mgr Trinity::Singleton::Instance() - -#endif diff --git a/src/server/game/DBCEnums.h b/src/server/game/DBCEnums.h deleted file mode 100644 index bd72026d753..00000000000 --- a/src/server/game/DBCEnums.h +++ /dev/null @@ -1,397 +0,0 @@ -/* -* Copyright (C) 2005-2009 MaNGOS -* -* 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, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef DBCENUMS_H -#define DBCENUMS_H - -// Client expected level limitation, like as used in DBC item max levels for "until max player level" -// use as default max player level, must be fit max level for used client -// also see MAX_LEVEL and STRONG_MAX_LEVEL define -#define DEFAULT_MAX_LEVEL 80 - -// client supported max level for player/pets/etc. Avoid overflow or client stability affected. -// also see GT_MAX_LEVEL define -#define MAX_LEVEL 100 - -// Server side limitation. Base at used code requirements. -// also see MAX_LEVEL and GT_MAX_LEVEL define -#define STRONG_MAX_LEVEL 255 - -enum BattleGroundBracketId // bracketId for level ranges -{ - BG_BRACKET_ID_FIRST = 0, - BG_BRACKET_ID_LAST = 15 -}; - -// must be max value in PvPDificulty slot+1 -#define MAX_BATTLEGROUND_BRACKETS 16 - -enum AreaTeams -{ - AREATEAM_NONE = 0, - AREATEAM_ALLY = 2, - AREATEAM_HORDE = 4 -}; - -enum AchievementFaction -{ - ACHIEVEMENT_FACTION_HORDE = 0, - ACHIEVEMENT_FACTION_ALLIANCE = 1, - ACHIEVEMENT_FACTION_ANY = -1, -}; - -enum AchievementFlags -{ - ACHIEVEMENT_FLAG_COUNTER = 0x00000001, // Just count statistic (never stop and complete) - ACHIEVEMENT_FLAG_TRACKING = 0x00000002, // Not sent to client - internal use only - ACHIEVEMENT_FLAG_STORE_MAX_VALUE = 0x00000004, // Store only max value? used only in "Reach level xx" - ACHIEVEMENT_FLAG_SUMM = 0x00000008, // Use summ criteria value from all reqirements (and calculate max value) - ACHIEVEMENT_FLAG_MAX_USED = 0x00000010, // Show max criteria (and calculate max value ??) - ACHIEVEMENT_FLAG_REQ_COUNT = 0x00000020, // Use not zero req count (and calculate max value) - ACHIEVEMENT_FLAG_AVERAGE = 0x00000040, // Show as average value (value / time_in_days) depend from other flag (by def use last criteria value) - ACHIEVEMENT_FLAG_BAR = 0x00000080, // Show as progress bar (value / max vale) depend from other flag (by def use last criteria value) - ACHIEVEMENT_FLAG_REALM_FIRST_REACH = 0x00000100, // - ACHIEVEMENT_FLAG_REALM_FIRST_KILL = 0x00000200, // -}; - -enum AchievementCriteriaCondition -{ - ACHIEVEMENT_CRITERIA_CONDITION_NONE = 0, - ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH = 1, - ACHIEVEMENT_CRITERIA_CONDITION_UNK1 = 2, // only used in "Complete a daily quest every day for five consecutive days" - ACHIEVEMENT_CRITERIA_CONDITION_MAP = 3, // requires you to be on specific map - ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE = 4, // only used in "Win 10 arenas without losing" - ACHIEVEMENT_CRITERIA_CONDITION_UNK2 = 9, // unk - ACHIEVEMENT_CRITERIA_CONDITION_UNK3 = 13, // unk -}; - -enum AchievementCriteriaCompletionFlags -{ - ACHIEVEMENT_CRITERIA_FLAG_SHOW_PROGRESS_BAR = 0x00000001, // Show progress as bar - ACHIEVEMENT_CRITERIA_FLAG_HIDE_CRITERIA = 0x00000002, // Not show criteria in client - ACHIEVEMENT_CRITERIA_FLAG_UNK3 = 0x00000004, // BG related?? - ACHIEVEMENT_CRITERIA_FLAG_UNK4 = 0x00000008, // - ACHIEVEMENT_CRITERIA_FLAG_UNK5 = 0x00000010, // not used - ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER = 0x00000020, // Displays counter as money -}; - -enum AchievementCriteriaTimedTypes -{ - ACHIEVEMENT_TIMED_TYPE_EVENT = 1, // Timer is started by internal event with id in timerStartEvent - ACHIEVEMENT_TIMED_TYPE_QUEST = 2, // Timer is started by acceting quest with entry in timerStartEvent - ACHIEVEMENT_TIMED_TYPE_SPELL_CASTER = 5, // Timer is started by casting a spell with entry in timerStartEvent - ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET = 6, // Timer is started by being target of spell with entry in timerStartEvent - ACHIEVEMENT_TIMED_TYPE_CREATURE = 7, // Timer is started by killing creature with entry in timerStartEvent - ACHIEVEMENT_TIMED_TYPE_ITEM = 9, // Timer is started by using item with entry in timerStartEvent - - ACHIEVEMENT_TIMED_TYPE_MAX, -}; - -enum AchievementCriteriaTypes -{ - ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0, - ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1, - ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5, - ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9, - // you have to complete a daily quest x times in a row - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY = 10, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11, - ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND= 15, - ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP= 16, - ACHIEVEMENT_CRITERIA_TYPE_DEATH= 17, - ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19, - ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20, - ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER = 23, - ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24, - ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26, - ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27, - ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28, - ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL= 29, - ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE = 30, - ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31, - ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32, - ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33, - ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34, - // TODO: this criteria has additional conditions which can not be found in the dbcs - ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL = 35, - ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36, - // TODO: the archievements 1162 and 1163 requires a special rating which can't be found in the dbc - ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38, - ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39, - ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40, - ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41, - ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM= 42, - ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43, - ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK= 44, - ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT= 45, - ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION= 46, - ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION= 47, - // noted: rewarded as soon as the player payed, not at taking place at the seat - ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP= 48, - ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49, - // TODO: itemlevel is mentioned in text but not present in dbc - ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50, - ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT= 51, - ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52, - ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53, - ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54, - ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55, - // TODO: in some cases map not present, and in some cases need do without die - ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56, - ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57, - ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS = 59, - ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS = 60, - ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS = 61, - ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD = 62, - ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING = 63, - ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER = 65, - ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL = 66, - ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67, - ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68, - ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2= 69, - ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL= 70, - ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72, - // TODO: title id is not mentioned in dbc - ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE = 74, - ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS= 75, - ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76, - ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL = 77, - // TODO: creature type (demon, undead etc.) is not stored in dbc - ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE = 78, - ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS= 80, - ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION= 82, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID= 83, - ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS= 84, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD = 85, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED = 86, - ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION = 87, - ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION = 88, - ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS = 89, - ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM = 90, - ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM = 91, - ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED = 93, - ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED = 94, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH = 95, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR = 99, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT = 101, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED = 102, - ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED = 103, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED = 104, - ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED = 105, - ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED = 106, - ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED = 107, - ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN = 108, - ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109, - // TODO: target entry is missing - ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110, - ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE= 112, - ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113, - ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS = 114, - // 0..115 => 116 criteria types total - ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS = 115, - ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS = 119, - // 120 - // 121 - // 122 - // 123 - // 0..123 => 124 criteria types total - ACHIEVEMENT_CRITERIA_TYPE_TOTAL = 124, -}; - -enum AreaFlags -{ - AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring) - AREA_FLAG_UNK1 = 0x00000002, // may be necropolis? - AREA_FLAG_UNK2 = 0x00000004, // Only used for areas on map 571 (development before) - AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subsones - AREA_FLAG_UNK3 = 0x00000010, // can't find common meaning - AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? - AREA_FLAG_UNK4 = 0x00000040, // many zones have this flag - AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas - AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag - AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?) - AREA_FLAG_OUTLAND = 0x00000400, // expansion zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag) - AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled) - AREA_FLAG_NEED_FLY = 0x00001000, // only Netherwing Ledge, Socrethar's Seat, Tempest Keep, The Arcatraz, The Botanica, The Mechanar, Sorrow Wing Point, Dragonspine Ridge, Netherwing Mines, Dragonmaw Base Camp, Dragonmaw Skyway - AREA_FLAG_UNUSED1 = 0x00002000, // not used now (no area/zones with this flag set in 3.0.3) - AREA_FLAG_OUTLAND2 = 0x00004000, // expansion zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag) - AREA_FLAG_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area) - AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only - AREA_FLAG_UNUSED2 = 0x00020000, // not used now (no area/zones with this flag set in 3.0.3) - AREA_FLAG_UNK5 = 0x00040000, // only used for Amani Pass, Hatchet Hills - AREA_FLAG_UNK6 = 0x00080000, // Valgarde and Acherus: The Ebon Hold - AREA_FLAG_LOWLEVEL = 0x00100000, // used for some starting areas with area_level <= 15 - AREA_FLAG_TOWN = 0x00200000, // small towns with Inn - AREA_FLAG_UNK7 = 0x00400000, // Warsong Hold, Acherus: The Ebon Hold, New Agamand Inn, Vengeance Landing Inn - AREA_FLAG_UNK8 = 0x00800000, // Westguard Inn, Acherus: The Ebon Hold, Valgarde - AREA_FLAG_OUTDOOR_PVP = 0x01000000, // Wintergrasp and it's subzones - AREA_FLAG_INSIDE = 0x02000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors - AREA_FLAG_OUTSIDE = 0x04000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors - AREA_FLAG_OUTDOOR_PVP2 = 0x08000000, // Wintergrasp and it's subzones - AREA_FLAG_NO_FLY_ZONE = 0x20000000 // Marks zones where you cannot fly -}; - -enum Difficulty -{ - REGULAR_DIFFICULTY = 0, - - DUNGEON_DIFFICULTY_NORMAL = 0, - DUNGEON_DIFFICULTY_HEROIC = 1, - DUNGEON_DIFFICULTY_EPIC = 2, - - RAID_DIFFICULTY_10MAN_NORMAL = 0, - RAID_DIFFICULTY_25MAN_NORMAL = 1, - RAID_DIFFICULTY_10MAN_HEROIC = 2, - RAID_DIFFICULTY_25MAN_HEROIC = 3, -}; - -#define MAX_DUNGEON_DIFFICULTY 3 -#define MAX_RAID_DIFFICULTY 4 -#define MAX_DIFFICULTY 4 - -enum SpawnMask -{ - SPAWNMASK_CONTINENT = (1 << REGULAR_DIFFICULTY),// any any maps without spawn modes - - SPAWNMASK_DUNGEON_NORMAL = (1 << DUNGEON_DIFFICULTY_NORMAL), - SPAWNMASK_DUNGEON_HEROIC = (1 << DUNGEON_DIFFICULTY_HEROIC), - SPAWNMASK_DUNGEON_ALL = (SPAWNMASK_DUNGEON_NORMAL | SPAWNMASK_DUNGEON_HEROIC), - - SPAWNMASK_RAID_10MAN_NORMAL = (1 << RAID_DIFFICULTY_10MAN_NORMAL), - SPAWNMASK_RAID_25MAN_NORMAL = (1 << RAID_DIFFICULTY_25MAN_NORMAL), - SPAWNMASK_RAID_NORMAL_ALL = (SPAWNMASK_RAID_10MAN_NORMAL | SPAWNMASK_RAID_25MAN_NORMAL), - - SPAWNMASK_RAID_10MAN_HEROIC = (1 << RAID_DIFFICULTY_10MAN_HEROIC), - SPAWNMASK_RAID_25MAN_HEROIC = (1 << RAID_DIFFICULTY_25MAN_HEROIC), - SPAWNMASK_RAID_HEROIC_ALL = (SPAWNMASK_RAID_10MAN_HEROIC | SPAWNMASK_RAID_25MAN_HEROIC), - - SPAWNMASK_RAID_ALL = (SPAWNMASK_RAID_NORMAL_ALL | SPAWNMASK_RAID_HEROIC_ALL), -}; - -enum FactionTemplateFlags -{ - FACTION_TEMPLATE_FLAG_PVP = 0x00000800, // flagged for PvP - FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats -}; - -enum FactionMasks -{ - FACTION_MASK_PLAYER = 1, // any player - FACTION_MASK_ALLIANCE = 2, // player or creature from alliance team - FACTION_MASK_HORDE = 4, // player or creature from horde team - FACTION_MASK_MONSTER = 8 // aggressive creature from monster team - // if none flags set then non-aggressive creature -}; - -enum MapTypes // Lua_IsInInstance -{ - MAP_COMMON = 0, // none - MAP_INSTANCE = 1, // party - MAP_RAID = 2, // raid - MAP_BATTLEGROUND = 3, // pvp - MAP_ARENA = 4 // arena -}; - -enum AbilytyLearnType -{ - ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1, - ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2 -}; - -enum ItemEnchantmentType -{ - ITEM_ENCHANTMENT_TYPE_NONE = 0, - ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL = 1, - ITEM_ENCHANTMENT_TYPE_DAMAGE = 2, - ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL = 3, - ITEM_ENCHANTMENT_TYPE_RESISTANCE = 4, - ITEM_ENCHANTMENT_TYPE_STAT = 5, - ITEM_ENCHANTMENT_TYPE_TOTEM = 6, - ITEM_ENCHANTMENT_TYPE_USE_SPELL = 7, - ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET = 8 -}; - -enum TotemCategoryType -{ - TOTEM_CATEGORY_TYPE_KNIFE = 1, - TOTEM_CATEGORY_TYPE_TOTEM = 2, - TOTEM_CATEGORY_TYPE_ROD = 3, - TOTEM_CATEGORY_TYPE_PICK = 21, - TOTEM_CATEGORY_TYPE_STONE = 22, - TOTEM_CATEGORY_TYPE_HAMMER = 23, - TOTEM_CATEGORY_TYPE_SPANNER = 24 -}; - -// SummonProperties.dbc, col 1 -enum SummonPropGroup -{ - SUMMON_PROP_GROUP_UNKNOWN1 = 0, // 1160 spells in 3.0.3 - SUMMON_PROP_GROUP_UNKNOWN2 = 1, // 861 spells in 3.0.3 - SUMMON_PROP_GROUP_PETS = 2, // 52 spells in 3.0.3, pets mostly - SUMMON_PROP_GROUP_CONTROLLABLE = 3, // 13 spells in 3.0.3, mostly controllable - SUMMON_PROP_GROUP_UNKNOWN3 = 4 // 86 spells in 3.0.3, taxi/mounts -}; - -// SummonProperties.dbc, col 3 -enum SummonPropType -{ - SUMMON_PROP_TYPE_UNKNOWN = 0, // different summons, 1330 spells in 3.0.3 - SUMMON_PROP_TYPE_SUMMON = 1, // generic summons, 49 spells in 3.0.3 - SUMMON_PROP_TYPE_GUARDIAN = 2, // summon guardian, 393 spells in 3.0.3 - SUMMON_PROP_TYPE_ARMY = 3, // summon army, 5 spells in 3.0.3 - SUMMON_PROP_TYPE_TOTEM = 4, // summon totem, 169 spells in 3.0.3 - SUMMON_PROP_TYPE_CRITTER = 5, // critter/minipet, 195 spells in 3.0.3 - SUMMON_PROP_TYPE_DK = 6, // summon DRW/Ghoul, 2 spells in 3.0.3 - SUMMON_PROP_TYPE_BOMB = 7, // summon bot/bomb, 4 spells in 3.0.3 - SUMMON_PROP_TYPE_PHASING = 8, // something todo with DK prequest line, 2 spells in 3.0.3 - SUMMON_PROP_TYPE_SIEGE_VEH = 9, // summon different vehicles, 14 spells in 3.0.3 - SUMMON_PROP_TYPE_DRAKE_VEH = 10, // summon drake (vehicle), 3 spells - SUMMON_PROP_TYPE_LIGHTWELL = 11 // summon lightwell, 6 spells in 3.0.3 -}; - -// SummonProperties.dbc, col 5 -enum SummonPropFlags -{ - SUMMON_PROP_FLAG_NONE = 0x0000, // 1342 spells in 3.0.3 - SUMMON_PROP_FLAG_UNK1 = 0x0001, // 75 spells in 3.0.3, something unfriendly - SUMMON_PROP_FLAG_UNK2 = 0x0002, // 616 spells in 3.0.3, something friendly - SUMMON_PROP_FLAG_UNK3 = 0x0004, // 22 spells in 3.0.3, no idea... - SUMMON_PROP_FLAG_UNK4 = 0x0008, // 49 spells in 3.0.3, some mounts - SUMMON_PROP_FLAG_UNK5 = 0x0010, // 25 spells in 3.0.3, quest related? - SUMMON_PROP_FLAG_UNK6 = 0x0020, // 0 spells in 3.0.3, unused - SUMMON_PROP_FLAG_UNK7 = 0x0040, // 12 spells in 3.0.3, no idea - SUMMON_PROP_FLAG_UNK8 = 0x0080, // 4 spells in 3.0.3, no idea - SUMMON_PROP_FLAG_UNK9 = 0x0100, // 51 spells in 3.0.3, no idea, many quest related - SUMMON_PROP_FLAG_UNK10 = 0x0200, // 51 spells in 3.0.3, something defensive - SUMMON_PROP_FLAG_UNK11 = 0x0400, // 3 spells, requires something near? - SUMMON_PROP_FLAG_UNK12 = 0x0800, // 30 spells in 3.0.3, no idea - SUMMON_PROP_FLAG_UNK13 = 0x1000, // 8 spells in 3.0.3, siege vehicle - SUMMON_PROP_FLAG_UNK14 = 0x2000, // 2 spells in 3.0.3, escort? -}; - -#endif diff --git a/src/server/game/DBCStores.cpp b/src/server/game/DBCStores.cpp deleted file mode 100644 index ba0e3af9a64..00000000000 --- a/src/server/game/DBCStores.cpp +++ /dev/null @@ -1,836 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "DBCStores.h" -#include "Policies/SingletonImp.h" -#include "Log.h" -#include "ProgressBar.h" -#include "SharedDefines.h" -#include "SpellMgr.h" - -#include "DBCfmt.h" - -#include - -typedef std::map AreaFlagByAreaID; -typedef std::map AreaFlagByMapID; - -struct WMOAreaTableTripple -{ - WMOAreaTableTripple(int32 r, int32 a, int32 g) : rootId(r), adtId(a), groupId(g) - { - } - - bool operator <(const WMOAreaTableTripple& b) const - { - return memcmp(this, &b, sizeof(WMOAreaTableTripple))<0; - } - - // ordered by entropy; that way memcmp will have a minimal medium runtime - int32 groupId; - int32 rootId; - int32 adtId; -}; - -typedef std::map WMOAreaInfoByTripple; - -DBCStorage sAreaStore(AreaTableEntryfmt); -DBCStorage sAreaGroupStore(AreaGroupEntryfmt); -DBCStorage sAreaPOIStore(AreaPOIEntryfmt); -static AreaFlagByAreaID sAreaFlagByAreaID; -static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files - -static WMOAreaInfoByTripple sWMOAreaInfoByTripple; - -DBCStorage sAchievementStore(Achievementfmt); -DBCStorage sAchievementCriteriaStore(AchievementCriteriafmt); -DBCStorage sAreaTriggerStore(AreaTriggerEntryfmt); -DBCStorage sAuctionHouseStore(AuctionHouseEntryfmt); -DBCStorage sBankBagSlotPricesStore(BankBagSlotPricesEntryfmt); -DBCStorage sBattlemasterListStore(BattlemasterListEntryfmt); -DBCStorage sBarberShopStyleStore(BarberShopStyleEntryfmt); -DBCStorage sCharStartOutfitStore(CharStartOutfitEntryfmt); -DBCStorage sCharTitlesStore(CharTitlesEntryfmt); -DBCStorage sChatChannelsStore(ChatChannelsEntryfmt); -DBCStorage sChrClassesStore(ChrClassesEntryfmt); -DBCStorage sChrRacesStore(ChrRacesEntryfmt); -DBCStorage sCinematicSequencesStore(CinematicSequencesEntryfmt); -DBCStorage sCreatureDisplayInfoStore(CreatureDisplayInfofmt); -DBCStorage sCreatureFamilyStore(CreatureFamilyfmt); -DBCStorage sCreatureSpellDataStore(CreatureSpellDatafmt); -DBCStorage sCreatureTypeStore(CreatureTypefmt); -DBCStorage sCurrencyTypesStore(CurrencyTypesfmt); - -DBCStorage sDurabilityQualityStore(DurabilityQualityfmt); -DBCStorage sDurabilityCostsStore(DurabilityCostsfmt); - -DBCStorage sEmotesStore(EmotesEntryfmt); -DBCStorage sEmotesTextStore(EmotesTextEntryfmt); - -typedef std::map FactionTeamMap; -static FactionTeamMap sFactionTeamMap; -DBCStorage sFactionStore(FactionEntryfmt); -DBCStorage sFactionTemplateStore(FactionTemplateEntryfmt); - -DBCStorage sGameObjectDisplayInfoStore(GameObjectDisplayInfofmt); -DBCStorage sGemPropertiesStore(GemPropertiesEntryfmt); -DBCStorage sGlyphPropertiesStore(GlyphPropertiesfmt); -DBCStorage sGlyphSlotStore(GlyphSlotfmt); - -DBCStorage sGtBarberShopCostBaseStore(GtBarberShopCostBasefmt); -DBCStorage sGtCombatRatingsStore(GtCombatRatingsfmt); -DBCStorage sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt); -DBCStorage sGtChanceToMeleeCritStore(GtChanceToMeleeCritfmt); -DBCStorage sGtChanceToSpellCritBaseStore(GtChanceToSpellCritBasefmt); -DBCStorage sGtChanceToSpellCritStore(GtChanceToSpellCritfmt); -DBCStorage sGtOCTRegenHPStore(GtOCTRegenHPfmt); -//DBCStorage sGtOCTRegenMPStore(GtOCTRegenMPfmt); -- not used currently -DBCStorage sGtRegenHPPerSptStore(GtRegenHPPerSptfmt); -DBCStorage sGtRegenMPPerSptStore(GtRegenMPPerSptfmt); - -DBCStorage sHolidaysStore(Holidaysfmt); - -DBCStorage sItemStore(Itemfmt); -DBCStorage sItemBagFamilyStore(ItemBagFamilyfmt); -//DBCStorage sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); -//DBCStorage sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently -DBCStorage sItemExtendedCostStore(ItemExtendedCostEntryfmt); -DBCStorage sItemLimitCategoryStore(ItemLimitCategoryEntryfmt); -DBCStorage sItemRandomPropertiesStore(ItemRandomPropertiesfmt); -DBCStorage sItemRandomSuffixStore(ItemRandomSuffixfmt); -DBCStorage sItemSetStore(ItemSetEntryfmt); - -DBCStorage sLFGDungeonStore(LFGDungeonEntryfmt); - -DBCStorage sLockStore(LockEntryfmt); - -DBCStorage sMailTemplateStore(MailTemplateEntryfmt); -DBCStorage sMapStore(MapEntryfmt); - -// DBC used only for initialization sMapDifficultyMap at startup. -DBCStorage sMapDifficultyStore(MapDifficultyEntryfmt); // only for loading -MapDifficultyMap sMapDifficultyMap; - -DBCStorage sMovieStore(MovieEntryfmt); - -DBCStorage sPvPDifficultyStore(PvPDifficultyfmt); - -DBCStorage sQuestSortStore(QuestSortEntryfmt); -DBCStorage sQuestXPStore(QuestXPfmt); -DBCStorage sQuestFactionRewardStore(QuestFactionRewardfmt); -DBCStorage sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); -DBCStorage sScalingStatDistributionStore(ScalingStatDistributionfmt); -DBCStorage sScalingStatValuesStore(ScalingStatValuesfmt); - -DBCStorage sSkillLineStore(SkillLinefmt); -DBCStorage sSkillLineAbilityStore(SkillLineAbilityfmt); - -DBCStorage sSoundEntriesStore(SoundEntriesfmt); - -DBCStorage sSpellItemEnchantmentStore(SpellItemEnchantmentfmt); -DBCStorage sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt); -DBCStorage sSpellStore(SpellEntryfmt); -SpellCategoryStore sSpellCategoryStore; -PetFamilySpellsStore sPetFamilySpellsStore; - -DBCStorage sSpellCastTimesStore(SpellCastTimefmt); -DBCStorage sSpellDifficultyStore(SpellDifficultyfmt); -DBCStorage sSpellDurationStore(SpellDurationfmt); -DBCStorage sSpellFocusObjectStore(SpellFocusObjectfmt); -DBCStorage sSpellRadiusStore(SpellRadiusfmt); -DBCStorage sSpellRangeStore(SpellRangefmt); -DBCStorage sSpellRuneCostStore(SpellRuneCostfmt); -DBCStorage sSpellShapeshiftStore(SpellShapeshiftfmt); -DBCStorage sStableSlotPricesStore(StableSlotPricesfmt); -DBCStorage sSummonPropertiesStore(SummonPropertiesfmt); -DBCStorage sTalentStore(TalentEntryfmt); -TalentSpellPosMap sTalentSpellPosMap; -DBCStorage sTalentTabStore(TalentTabEntryfmt); - -// store absolute bit position for first rank for talent inspect -static uint32 sTalentTabPages[MAX_CLASSES][3]; - -DBCStorage sTaxiNodesStore(TaxiNodesEntryfmt); -TaxiMask sTaxiNodesMask; -TaxiMask sOldContinentsNodesMask; - -// DBC used only for initialization sTaxiPathSetBySource at startup. -TaxiPathSetBySource sTaxiPathSetBySource; -DBCStorage sTaxiPathStore(TaxiPathEntryfmt); - -// DBC used only for initialization sTaxiPathNodeStore at startup. -TaxiPathNodesByPath sTaxiPathNodesByPath; -static DBCStorage sTaxiPathNodeStore(TaxiPathNodeEntryfmt); - -DBCStorage sTotemCategoryStore(TotemCategoryEntryfmt); -DBCStorage sVehicleStore(VehicleEntryfmt); -DBCStorage sVehicleSeatStore(VehicleSeatEntryfmt); -DBCStorage sWMOAreaTableStore(WMOAreaTableEntryfmt); -DBCStorage sWorldMapAreaStore(WorldMapAreaEntryfmt); -DBCStorage sWorldMapOverlayStore(WorldMapOverlayEntryfmt); -DBCStorage sWorldSafeLocsStore(WorldSafeLocsEntryfmt); - -typedef std::list StoreProblemList; - -static bool LoadDBC_assert_print(uint32 fsize,uint32 rsize, const std::string& filename) -{ - sLog.outError("ERROR: Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).",filename.c_str(),fsize,rsize); - - // assert must fail after function call - return false; -} - -template -inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList& errlist, DBCStorage& storage, const std::string& dbc_path, const std::string& filename, const std::string * custom_entries = NULL, const std::string * idname = NULL) -{ - // compatibility format and C++ structure sizes - assert(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()),sizeof(T),filename)); - - std::string dbc_filename = dbc_path + filename; - SqlDbc * sql = NULL; - if (custom_entries) - sql = new SqlDbc(&filename,custom_entries, idname,storage.GetFormat()); - - if (storage.Load(dbc_filename.c_str(), sql)) - { - bar.step(); - for (uint8 i = 0; i < MAX_LOCALE; ++i) - { - if (!(availableDbcLocales & (1 << i))) - continue; - - std::string dbc_filename_loc = dbc_path + localeNames[i] + "/" + filename; - if (!storage.LoadStringsFrom(dbc_filename_loc.c_str())) - availableDbcLocales &= ~(1<DBC records - sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID),area->exploreFlag)); - - // fill MapId->DBC records (skip sub zones and continents) - if (area->zone == 0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 && area->mapid != 571) - sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid,area->exploreFlag)); - } - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAchievementStore, dbcPath,"Achievement.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAchievementCriteriaStore, dbcPath,"Achievement_Criteria.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaTriggerStore, dbcPath,"AreaTrigger.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaGroupStore, dbcPath,"AreaGroup.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaPOIStore, dbcPath,"AreaPOI.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAuctionHouseStore, dbcPath,"AuctionHouse.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBankBagSlotPricesStore, dbcPath,"BankBagSlotPrices.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBattlemasterListStore, dbcPath,"BattlemasterList.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBarberShopStyleStore, dbcPath,"BarberShopStyle.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharStartOutfitStore, dbcPath,"CharStartOutfit.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharTitlesStore, dbcPath,"CharTitles.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChatChannelsStore, dbcPath,"ChatChannels.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrClassesStore, dbcPath,"ChrClasses.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrRacesStore, dbcPath,"ChrRaces.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCinematicSequencesStore, dbcPath,"CinematicSequences.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoStore, dbcPath,"CreatureDisplayInfo.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureFamilyStore, dbcPath,"CreatureFamily.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureSpellDataStore, dbcPath,"CreatureSpellData.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureTypeStore, dbcPath,"CreatureType.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCurrencyTypesStore, dbcPath,"CurrencyTypes.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityCostsStore, dbcPath,"DurabilityCosts.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityQualityStore, dbcPath,"DurabilityQuality.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesStore, dbcPath,"Emotes.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesTextStore, dbcPath,"EmotesText.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionStore, dbcPath,"Faction.dbc"); - for (uint32 i=0; iteam) - { - SimpleFactionsList &flist = sFactionTeamMap[faction->team]; - flist.push_back(i); - } - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionTemplateStore, dbcPath,"FactionTemplate.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGameObjectDisplayInfoStore, dbcPath,"GameObjectDisplayInfo.dbc"); - for (uint32 i = 0; i < sGameObjectDisplayInfoStore.GetNumRows(); ++i) - { - if (GameObjectDisplayInfoEntry const * info = sGameObjectDisplayInfoStore.LookupEntry(i)) - { - if (info->maxX < info->minX) - std::swap(*(float*)(&info->maxX), *(float*)(&info->minX)); - if (info->maxY < info->minY) - std::swap(*(float*)(&info->maxY), *(float*)(&info->minY)); - if (info->maxZ < info->minZ) - std::swap(*(float*)(&info->maxZ), *(float*)(&info->minZ)); - } - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGemPropertiesStore, dbcPath,"GemProperties.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGlyphPropertiesStore, dbcPath,"GlyphProperties.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGlyphSlotStore, dbcPath,"GlyphSlot.dbc"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtBarberShopCostBaseStore,dbcPath,"gtBarberShopCostBase.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtCombatRatingsStore, dbcPath,"gtCombatRatings.dbc"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritBaseStore, dbcPath,"gtChanceToMeleeCritBase.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritStore, dbcPath,"gtChanceToMeleeCrit.dbc"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritBaseStore, dbcPath,"gtChanceToSpellCritBase.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritStore, dbcPath,"gtChanceToSpellCrit.dbc"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenHPStore, dbcPath,"gtOCTRegenHP.dbc"); - //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenMPStore, dbcPath,"gtOCTRegenMP.dbc"); -- not used currently - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenHPPerSptStore, dbcPath,"gtRegenHPPerSpt.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenMPPerSptStore, dbcPath,"gtRegenMPPerSpt.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sHolidaysStore, dbcPath,"Holidays.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemStore, dbcPath,"Item.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemBagFamilyStore, dbcPath,"ItemBagFamily.dbc"); - //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemDisplayInfoStore, dbcPath,"ItemDisplayInfo.dbc"); -- not used currently - //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemCondExtCostsStore, dbcPath,"ItemCondExtCosts.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemExtendedCostStore, dbcPath,"ItemExtendedCost.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemLimitCategoryStore, dbcPath,"ItemLimitCategory.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomPropertiesStore,dbcPath,"ItemRandomProperties.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomSuffixStore, dbcPath,"ItemRandomSuffix.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemSetStore, dbcPath,"ItemSet.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sLFGDungeonStore, dbcPath,"LFGDungeons.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sLockStore, dbcPath,"Lock.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMailTemplateStore, dbcPath,"MailTemplate.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMapStore, dbcPath,"Map.dbc"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMapDifficultyStore, dbcPath,"MapDifficulty.dbc"); - // fill data - for (uint32 i = 1; i < sMapDifficultyStore.GetNumRows(); ++i) - if (MapDifficultyEntry const* entry = sMapDifficultyStore.LookupEntry(i)) - sMapDifficultyMap[MAKE_PAIR32(entry->MapId,entry->Difficulty)] = MapDifficulty(entry->resetTime,entry->maxPlayers); - sMapDifficultyStore.Clear(); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMovieStore, dbcPath,"Movie.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestSortStore, dbcPath,"QuestSort.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sPvPDifficultyStore, dbcPath,"PvpDifficulty.dbc"); - for (uint32 i = 0; i < sPvPDifficultyStore.GetNumRows(); ++i) - if (PvPDifficultyEntry const* entry = sPvPDifficultyStore.LookupEntry(i)) - if (entry->bracketId > MAX_BATTLEGROUND_BRACKETS) - assert(false && "Need update MAX_BATTLEGROUND_BRACKETS by DBC data"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestXPStore, dbcPath,"QuestXP.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestFactionRewardStore, dbcPath,"QuestFactionReward.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sRandomPropertiesPointsStore, dbcPath,"RandPropPoints.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sScalingStatDistributionStore, dbcPath,"ScalingStatDistribution.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sScalingStatValuesStore, dbcPath,"ScalingStatValues.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineStore, dbcPath,"SkillLine.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineAbilityStore, dbcPath,"SkillLineAbility.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSoundEntriesStore, dbcPath,"SoundEntries.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellStore, dbcPath,"Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex); - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) - { - SpellEntry const * spell = sSpellStore.LookupEntry(i); - if (spell && spell->Category) - sSpellCategoryStore[spell->Category].insert(i); - } - - for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) - { - SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j); - - if (!skillLine) - continue; - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); - - if (spellInfo && IsPassiveSpell(spellInfo->Id)) - { - for (uint32 i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i) - { - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i); - if (!cFamily) - continue; - - if (skillLine->skillId != cFamily->skillLine[0] && skillLine->skillId != cFamily->skillLine[1]) - continue; - if (spellInfo->spellLevel) - continue; - - if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) - continue; - - sPetFamilySpellsStore[i].insert(spellInfo->Id); - } - } - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellCastTimesStore, dbcPath,"SpellCastTimes.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellDifficultyStore, dbcPath,"SpellDifficulty.dbc", &CustomSpellDifficultyfmt, &CustomSpellDifficultyIndex); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellDurationStore, dbcPath,"SpellDuration.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellFocusObjectStore, dbcPath,"SpellFocusObject.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentStore,dbcPath,"SpellItemEnchantment.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentConditionStore,dbcPath,"SpellItemEnchantmentCondition.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRadiusStore, dbcPath,"SpellRadius.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRangeStore, dbcPath,"SpellRange.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRuneCostStore, dbcPath,"SpellRuneCost.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellShapeshiftStore, dbcPath,"SpellShapeshiftForm.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSummonPropertiesStore, dbcPath,"SummonProperties.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc"); - - // Create Spelldifficulty searcher - for (uint32 i = 0; i < sSpellDifficultyStore.GetNumRows(); ++i) - { - SpellDifficultyEntry const *spellDiff = sSpellDifficultyStore.LookupEntry(i); - if (!spellDiff) - continue; - - SpellDifficultyEntry newEntry; - for (int x = 0; x < MAX_DIFFICULTY; ++x) - { - if (spellDiff->SpellID[x] <= 0 || !sSpellStore.LookupEntry(spellDiff->SpellID[x])) - { - if (spellDiff->SpellID[x] > 0)//don't show error if spell is <= 0, not all modes have spells and there are unknown negative values - sLog.outErrorDb("spelldifficulty_dbc: spell %i at field id:%u at spellid%i does not exist in SpellStore (spell.dbc), loaded as 0", spellDiff->SpellID[x], spellDiff->ID, x); - newEntry.SpellID[x] = 0;//spell was <= 0 or invalid, set to 0 - } else newEntry.SpellID[x] = spellDiff->SpellID[x]; - } - if (newEntry.SpellID[0] <= 0 || newEntry.SpellID[1] <= 0)//id0-1 must be always set! - continue; - - for (int x = 0; x < MAX_DIFFICULTY; ++x) - spellmgr.SetSpellDifficultyId(uint32(newEntry.SpellID[x]), spellDiff->ID); - } - - // create talent spells set - for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i) - { - TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); - if (!talentInfo) continue; - for (int j = 0; j < MAX_TALENT_RANK; j++) - if (talentInfo->RankID[j]) - sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i,j); - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentTabStore, dbcPath,"TalentTab.dbc"); - - // prepare fast data access to bit pos of talent ranks for use at inspecting - { - // now have all max ranks (and then bit amount used for store talent ranks in inspect) - for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) - { - TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentTabId); - if (!talentTabInfo) - continue; - - // prevent memory corruption; otherwise cls will become 12 below - if ((talentTabInfo->ClassMask & CLASSMASK_ALL_PLAYABLE) == 0) - continue; - - // store class talent tab pages - uint32 cls = 1; - for (uint32 m=1; !(m & talentTabInfo->ClassMask) && cls < MAX_CLASSES; m <<= 1, ++cls) {} - - sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId; - } - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiNodesStore, dbcPath,"TaxiNodes.dbc"); - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathStore, dbcPath,"TaxiPath.dbc"); - for (uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) - if (TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i)) - sTaxiPathSetBySource[entry->from][entry->to] = TaxiPathBySourceAndDestination(entry->ID,entry->price); - uint32 pathCount = sTaxiPathStore.GetNumRows(); - - //## TaxiPathNode.dbc ## Loaded only for initialization different structures - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathNodeStore, dbcPath,"TaxiPathNode.dbc"); - // Calculate path nodes count - std::vector pathLength; - pathLength.resize(pathCount); // 0 and some other indexes not used - for (uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) - if (TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) - { - if (pathLength[entry->path] < entry->index + 1) - pathLength[entry->path] = entry->index + 1; - } - // Set path length - sTaxiPathNodesByPath.resize(pathCount); // 0 and some other indexes not used - for (uint32 i = 1; i < sTaxiPathNodesByPath.size(); ++i) - sTaxiPathNodesByPath[i].resize(pathLength[i]); - // fill data - for (uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) - if (TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) - sTaxiPathNodesByPath[entry->path][entry->index] = TaxiPathNode(entry->mapid,entry->x,entry->y,entry->z,entry->actionFlag,entry->delay); - sTaxiPathNodeStore.Clear(); - - // Initialize global taxinodes mask - // include existed nodes that have at least single not spell base (scripted) path - { - std::set spellPaths; - for (uint32 i = 1; i < sSpellStore.GetNumRows (); ++i) - if (SpellEntry const* sInfo = sSpellStore.LookupEntry (i)) - for (int j=0; j < 3; ++j) - if (sInfo->Effect[j] == 123 /*SPELL_EFFECT_SEND_TAXI*/) - spellPaths.insert(sInfo->EffectMiscValue[j]); - - memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask)); - memset(sOldContinentsNodesMask,0,sizeof(sTaxiNodesMask)); - for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) - { - TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i); - if (!node) - continue; - - TaxiPathSetBySource::const_iterator src_i = sTaxiPathSetBySource.find(i); - if (src_i != sTaxiPathSetBySource.end() && !src_i->second.empty()) - { - bool ok = false; - for (TaxiPathSetForSource::const_iterator dest_i = src_i->second.begin(); dest_i != src_i->second.end(); ++dest_i) - { - // not spell path - if (spellPaths.find(dest_i->second.ID) == spellPaths.end()) - { - ok = true; - break; - } - } - - if (!ok) - continue; - } - - // valid taxi network node - uint8 field = (uint8)((i - 1) / 32); - uint32 submask = 1<<((i-1)%32); - sTaxiNodesMask[field] |= submask; - - // old continent node (+ nodes virtually at old continents, check explicitly to avoid loading map files for zone info) - if (node->map_id < 2 || i == 82 || i == 83 || i == 93 || i == 94) - sOldContinentsNodesMask[field] |= submask; - - // fix DK node at Ebon Hold - if (i == 315) { - ((TaxiNodesEntry*)node)->MountCreatureID[1] = 32981; - } - } - } - - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleStore, dbcPath,"Vehicle.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleSeatStore, dbcPath,"VehicleSeat.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWMOAreaTableStore, dbcPath,"WMOAreaTable.dbc"); - for(uint32 i = 0; i < sWMOAreaTableStore.GetNumRows(); ++i) - { - if(WMOAreaTableEntry const* entry = sWMOAreaTableStore.LookupEntry(i)) - { - sWMOAreaInfoByTripple.insert(WMOAreaInfoByTripple::value_type(WMOAreaTableTripple(entry->rootId, entry->adtId, entry->groupId), entry)); - } - } - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapOverlayStore, dbcPath,"WorldMapOverlay.dbc"); - LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc"); - - // error checks - if (bad_dbc_files.size() >= DBCFilesCount) - { - sLog.outError("\nIncorrect DataDir value in Trinityd.conf or ALL required *.dbc files (%d) not found by path: %sdbc",DBCFilesCount,dataPath.c_str()); - exit(1); - } - else if (!bad_dbc_files.empty()) - { - std::string str; - for (std::list::iterator i = bad_dbc_files.begin(); i != bad_dbc_files.end(); ++i) - str += *i + "\n"; - - sLog.outError("\nSome required *.dbc files (%u from %d) not found or not compatible:\n%s",(uint32)bad_dbc_files.size(),DBCFilesCount,str.c_str()); - exit(1); - } - - // Check loaded DBC files proper version - if (!sAreaStore.LookupEntry(3617) || // last area (areaflag) added in 3.3.3a - !sCharTitlesStore.LookupEntry(177) || // last char title added in 3.3.3a - !sGemPropertiesStore.LookupEntry(1629) || // last added spell in 3.3.3a - !sItemStore.LookupEntry(54860) || // last gem property added in 3.3.3a - !sItemExtendedCostStore.LookupEntry(2997) || // last item extended cost added in 3.3.3a - !sMapStore.LookupEntry(724) || // last map added in 3.3.3a - !sSpellStore.LookupEntry(76567) ) // last client known item added in 3.3.3a - { - sLog.outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client."); - exit(1); - } - sLog.outString(); - sLog.outString(">> Initialized %d data stores", DBCFilesCount); -} - -SimpleFactionsList const* GetFactionTeamList(uint32 faction, bool &isTeamMember) -{ - for (FactionTeamMap::const_iterator itr = sFactionTeamMap.begin(); itr != sFactionTeamMap.end(); ++itr) - { - if (itr->first == faction) - { - isTeamMember = false; - return &itr->second; - } - for (SimpleFactionsList::const_iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2) - { - if ((*itr2) == faction) - { - isTeamMember = true; - return &itr->second; - } - } - } - return NULL; -} - -char* GetPetName(uint32 petfamily, uint32 dbclang) -{ - if (!petfamily) - return NULL; - CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(petfamily); - if (!pet_family) - return NULL; - return pet_family->Name[dbclang]?pet_family->Name[dbclang]:NULL; -} - -TalentSpellPos const* GetTalentSpellPos(uint32 spellId) -{ - TalentSpellPosMap::const_iterator itr = sTalentSpellPosMap.find(spellId); - if (itr == sTalentSpellPosMap.end()) - return NULL; - - return &itr->second; -} - -uint32 GetTalentSpellCost(uint32 spellId) -{ - if (TalentSpellPos const* pos = GetTalentSpellPos(spellId)) - return pos->rank+1; - - return 0; -} - -int32 GetAreaFlagByAreaID(uint32 area_id) -{ - AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); - if (i == sAreaFlagByAreaID.end()) - return -1; - - return i->second; -} - -WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid) -{ - WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid)); - if(i == sWMOAreaInfoByTripple.end()) - return NULL; - return i->second; -} - -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) -{ - int32 areaflag = GetAreaFlagByAreaID(area_id); - if (areaflag < 0) - return NULL; - - return sAreaStore.LookupEntry(areaflag); -} - -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id) -{ - if (area_flag) - return sAreaStore.LookupEntry(area_flag); - - if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) - return GetAreaEntryByAreaID(mapEntry->linked_zone); - - return NULL; -} - -uint32 GetAreaFlagByMapId(uint32 mapid) -{ - AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); - if (i == sAreaFlagByMapID.end()) - return 0; - else - return i->second; -} - -uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) -{ - if (mapid != 530 && mapid != 571) // speed for most cases - return mapid; - - if (WorldMapAreaEntry const* wma = sWorldMapAreaStore.LookupEntry(zoneId)) - return wma->virtual_map_id >= 0 ? wma->virtual_map_id : wma->map_id; - - return mapid; -} - -ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId) -{ - mapid = GetVirtualMapForMapAndZone(mapid,zoneId); - if (mapid < 2) - return CONTENT_1_60; - - MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); - if (!mapEntry) - return CONTENT_1_60; - - switch(mapEntry->Expansion()) - { - default: return CONTENT_1_60; - case 1: return CONTENT_61_70; - case 2: return CONTENT_71_80; - } -} - -ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) -{ - // not sorted, numbering index from 0 - for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) - { - ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); - if (ch && ch->ChannelID == channel_id) - return ch; - } - return NULL; -} - -bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) -{ - if (requiredTotemCategoryId == 0) - return true; - if (itemTotemCategoryId == 0) - return false; - - TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); - if (!itemEntry) - return false; - TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); - if (!reqEntry) - return false; - - if (itemEntry->categoryType != reqEntry->categoryType) - return false; - - return (itemEntry->categoryMask & reqEntry->categoryMask) == reqEntry->categoryMask; -} - -void Zone2MapCoordinates(float& x,float& y,uint32 zone) -{ - WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); - - // if not listed then map coordinates (instance) - if (!maEntry) - return; - - std::swap(x,y); // at client map coords swapped - x = x*((maEntry->x2-maEntry->x1)/100)+maEntry->x1; - y = y*((maEntry->y2-maEntry->y1)/100)+maEntry->y1; // client y coord from top to down -} - -void Map2ZoneCoordinates(float& x,float& y,uint32 zone) -{ - WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); - - // if not listed then map coordinates (instance) - if (!maEntry) - return; - - x = (x-maEntry->x1)/((maEntry->x2-maEntry->x1)/100); - y = (y-maEntry->y1)/((maEntry->y2-maEntry->y1)/100); // client y coord from top to down - std::swap(x,y); // client have map coords swapped -} - -MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty) -{ - MapDifficultyMap::const_iterator itr = sMapDifficultyMap.find(MAKE_PAIR32(mapId,difficulty)); - return itr != sMapDifficultyMap.end() ? &itr->second : NULL; -} - -PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level) -{ - // prevent out-of-range levels for dbc data - if (level > DEFAULT_MAX_LEVEL) - level = DEFAULT_MAX_LEVEL; - - for (uint32 i = 0; i < sPvPDifficultyStore.GetNumRows(); ++i) - if (PvPDifficultyEntry const* entry = sPvPDifficultyStore.LookupEntry(i)) - if (entry->mapId == mapid && entry->minLevel <= level && entry->maxLevel >= level) - return entry; - - return NULL; -} - -PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattleGroundBracketId id) -{ - for (uint32 i = 0; i < sPvPDifficultyStore.GetNumRows(); ++i) - if (PvPDifficultyEntry const* entry = sPvPDifficultyStore.LookupEntry(i)) - if (entry->mapId == mapid && entry->GetBracketId() == id) - return entry; - - return NULL; -} - -uint32 const* GetTalentTabPages(uint8 cls) -{ - return sTalentTabPages[cls]; -} - -// script support functions - DBCStorage const* GetSoundEntriesStore() { return &sSoundEntriesStore; } - DBCStorage const* GetSpellStore() { return &sSpellStore; } - DBCStorage const* GetSpellRangeStore() { return &sSpellRangeStore; } - DBCStorage const* GetFactionStore() { return &sFactionStore; } - DBCStorage const* GetItemDisplayStore() { return &sItemStore; } - DBCStorage const* GetCreatureDisplayStore() { return &sCreatureDisplayInfoStore; } - DBCStorage const* GetEmotesStore() { return &sEmotesStore; } - DBCStorage const* GetEmotesTextStore() { return &sEmotesTextStore; } - DBCStorage const* GetAchievementStore() { return &sAchievementStore; } diff --git a/src/server/game/DBCStores.h b/src/server/game/DBCStores.h deleted file mode 100644 index b14814e07a1..00000000000 --- a/src/server/game/DBCStores.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_DBCSTORES_H -#define TRINITY_DBCSTORES_H - -#include "Common.h" -#include "Database/DBCStore.h" -#include "DBCStructure.h" - -#include - -typedef std::list SimpleFactionsList; - -SimpleFactionsList const* GetFactionTeamList(uint32 faction, bool &isTeamMember); -char* GetPetName(uint32 petfamily, uint32 dbclang); -uint32 GetTalentSpellCost(uint32 spellId); -TalentSpellPos const* GetTalentSpellPos(uint32 spellId); - -int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); -uint32 GetAreaFlagByMapId(uint32 mapid); - -WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid); - -uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); - -enum ContentLevels -{ - CONTENT_1_60 = 0, - CONTENT_61_70, - CONTENT_71_80 -}; -ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId); - -ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id); - -bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); - -void Zone2MapCoordinates(float &x, float &y, uint32 zone); -void Map2ZoneCoordinates(float &x, float &y, uint32 zone); - -typedef std::map MapDifficultyMap; -MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty); - -uint32 const* /*[3]*/ GetTalentTabPages(uint8 cls); - -PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level); -PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattleGroundBracketId id); - -extern DBCStorage sAchievementStore; -extern DBCStorage sAchievementCriteriaStore; -extern DBCStorage sAreaStore;// recommend access using functions -extern DBCStorage sAreaGroupStore; -extern DBCStorage sAreaPOIStore; -extern DBCStorage sAreaTriggerStore; -extern DBCStorage sAuctionHouseStore; -extern DBCStorage sBankBagSlotPricesStore; -extern DBCStorage sBarberShopStyleStore; -extern DBCStorage sBattlemasterListStore; -//extern DBCStorage sChatChannelsStore; -- accessed using function, no usable index -extern DBCStorage sCharStartOutfitStore; -extern DBCStorage sCharTitlesStore; -extern DBCStorage sChrClassesStore; -extern DBCStorage sChrRacesStore; -extern DBCStorage sCinematicSequencesStore; -extern DBCStorage sCreatureDisplayInfoStore; -extern DBCStorage sCreatureFamilyStore; -extern DBCStorage sCreatureSpellDataStore; -extern DBCStorage sCreatureTypeStore; -extern DBCStorage sCurrencyTypesStore; -extern DBCStorage sDurabilityCostsStore; -extern DBCStorage sDurabilityQualityStore; -extern DBCStorage sEmotesStore; -extern DBCStorage sEmotesTextStore; -extern DBCStorage sFactionStore; -extern DBCStorage sFactionTemplateStore; -extern DBCStorage sGameObjectDisplayInfoStore; -extern DBCStorage sGemPropertiesStore; -extern DBCStorage sGlyphPropertiesStore; -extern DBCStorage sGlyphSlotStore; - -extern DBCStorage sGtBarberShopCostBaseStore; -extern DBCStorage sGtCombatRatingsStore; -extern DBCStorage sGtChanceToMeleeCritBaseStore; -extern DBCStorage sGtChanceToMeleeCritStore; -extern DBCStorage sGtChanceToSpellCritBaseStore; -extern DBCStorage sGtChanceToSpellCritStore; -extern DBCStorage sGtOCTRegenHPStore; -//extern DBCStorage sGtOCTRegenMPStore; -- not used currently -extern DBCStorage sGtRegenHPPerSptStore; -extern DBCStorage sGtRegenMPPerSptStore; -extern DBCStorage sHolidaysStore; -extern DBCStorage sItemStore; -extern DBCStorage sItemBagFamilyStore; -//extern DBCStorage sItemDisplayInfoStore; -- not used currently -extern DBCStorage sItemExtendedCostStore; -extern DBCStorage sItemLimitCategoryStore; -extern DBCStorage sItemRandomPropertiesStore; -extern DBCStorage sItemRandomSuffixStore; -extern DBCStorage sItemSetStore; -extern DBCStorage sLFGDungeonStore; -extern DBCStorage sLockStore; -extern DBCStorage sMailTemplateStore; -extern DBCStorage sMapStore; -//extern DBCStorage sMapDifficultyStore; -- use GetMapDifficultyData insteed -extern MapDifficultyMap sMapDifficultyMap; -extern DBCStorage sMovieStore; -extern DBCStorage sQuestSortStore; -extern DBCStorage sQuestXPStore; -extern DBCStorage sQuestFactionRewardStore; -extern DBCStorage sRandomPropertiesPointsStore; -extern DBCStorage sScalingStatDistributionStore; -extern DBCStorage sScalingStatValuesStore; -extern DBCStorage sSkillLineStore; -extern DBCStorage sSkillLineAbilityStore; -extern DBCStorage sSoundEntriesStore; -extern DBCStorage sSpellCastTimesStore; -extern DBCStorage sSpellDifficultyStore; -extern DBCStorage sSpellDurationStore; -extern DBCStorage sSpellFocusObjectStore; -extern DBCStorage sSpellItemEnchantmentStore; -extern DBCStorage sSpellItemEnchantmentConditionStore; -extern SpellCategoryStore sSpellCategoryStore; -extern PetFamilySpellsStore sPetFamilySpellsStore; -extern DBCStorage sSpellRadiusStore; -extern DBCStorage sSpellRangeStore; -extern DBCStorage sSpellRuneCostStore; -extern DBCStorage sSpellShapeshiftStore; -extern DBCStorage sSpellStore; -extern DBCStorage sStableSlotPricesStore; -extern DBCStorage sSummonPropertiesStore; -extern DBCStorage sTalentStore; -extern DBCStorage sTalentTabStore; -extern DBCStorage sTaxiNodesStore; -extern DBCStorage sTaxiPathStore; -extern TaxiMask sTaxiNodesMask; -extern TaxiMask sOldContinentsNodesMask; -extern TaxiPathSetBySource sTaxiPathSetBySource; -extern TaxiPathNodesByPath sTaxiPathNodesByPath; -extern DBCStorage sTotemCategoryStore; -extern DBCStorage sVehicleStore; -extern DBCStorage sVehicleSeatStore; -extern DBCStorage sWMOAreaTableStore; -//extern DBCStorage sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates -extern DBCStorage sWorldMapOverlayStore; -extern DBCStorage sWorldSafeLocsStore; - -void LoadDBCStores(const std::string& dataPath); - -// script support functions - DBCStorage const* GetSoundEntriesStore(); - DBCStorage const* GetSpellStore(); - DBCStorage const* GetSpellRangeStore(); - DBCStorage const* GetFactionStore(); - DBCStorage const* GetItemDisplayStore(); - DBCStorage const* GetCreatureDisplayStore(); - DBCStorage const* GetEmotesStore(); - DBCStorage const* GetEmotesTextStore(); - DBCStorage const* GetAchievementStore(); -#endif diff --git a/src/server/game/DBCStructure.h b/src/server/game/DBCStructure.h deleted file mode 100644 index 0fc0d1251f5..00000000000 --- a/src/server/game/DBCStructure.h +++ /dev/null @@ -1,1930 +0,0 @@ -/* - * Copyright (C) 2005-2009 MaNGOS - * - * Copyright (C) 2008-2010 Trinity - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef TRINITY_DBCSTRUCTURE_H -#define TRINITY_DBCSTRUCTURE_H - -#include "DBCEnums.h" -#include "Platform/Define.h" -#include "Util.h" - -#include -#include -#include - -// Structures using to access raw DBC data and required packing to portability - -// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform -#if defined(__GNUC__) -#pragma pack(1) -#else -#pragma pack(push,1) -#endif - -struct AchievementEntry -{ - uint32 ID; // 0 - uint32 factionFlag; // 1 -1=all, 0=horde, 1=alliance - uint32 mapID; // 2 -1=none - //uint32 parentAchievement; // 3 its Achievement parent (can`t start while parent uncomplete, use its Criteria if don`t have own, use its progress on begin) - char *name[16]; // 4-19 - //uint32 name_flags; // 20 - //char *description[16]; // 21-36 - //uint32 desc_flags; // 37 - uint32 categoryId; // 38 - uint32 points; // 39 reward points - //uint32 OrderInCategory; // 40 - uint32 flags; // 41 - //uint32 icon; // 42 icon (from SpellIcon.dbc) - //char *titleReward[16]; // 43-58 - //uint32 titleReward_flags; // 59 - uint32 count; // 60 - need this count of completed criterias (own or referenced achievement criterias) - uint32 refAchievement; // 61 - referenced achievement (counting of all completed criterias) -}; - -struct AchievementCategoryEntry -{ - uint32 ID; // 0 - uint32 parentCategory; // 1 -1 for main category - //char *name[16]; // 2-17 - //uint32 name_flags; // 18 - //uint32 sortOrder; // 19 -}; - -struct AchievementCriteriaEntry -{ - uint32 ID; // 0 - uint32 referredAchievement; // 1 - uint32 requiredType; // 2 - union - { - // ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0 - // TODO: also used for player deaths.. - struct - { - uint32 creatureID; // 3 - uint32 creatureCount; // 4 - } kill_creature; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 - struct - { - uint32 bgMapID; // 3 - uint32 winCount; // 4 - uint32 additionalRequirement1_type; // 5 additional requirement 1 type - uint32 additionalRequirement1_value; // 6 additional requirement 1 value - uint32 additionalRequirement2_type; // 7 additional requirement 2 type - uint32 additionalRequirement2_value; // 8 additional requirement 1 value - } win_bg; - - // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 - struct - { - uint32 unused; // 3 - uint32 level; // 4 - } reach_level; - - // ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7 - struct - { - uint32 skillID; // 3 - uint32 skillLevel; // 4 - } reach_skill_level; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8 - struct - { - uint32 linkedAchievement; // 3 - } complete_achievement; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9 - struct - { - uint32 unused; // 3 - uint32 totalQuestCount; // 4 - } complete_quest_count; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY = 10 - struct - { - uint32 unused; // 3 - uint32 numberOfDays; // 4 - } complete_daily_quest_daily; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11 - struct - { - uint32 zoneID; // 3 - uint32 questCount; // 4 - } complete_quests_in_zone; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14 - struct - { - uint32 unused; // 3 - uint32 questCount; // 4 - } complete_daily_quest; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND = 15 - struct - { - uint32 mapID; // 3 - } complete_battleground; - - // ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP = 16 - struct - { - uint32 mapID; // 3 - } death_at_map; - - // ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18 - struct - { - uint32 manLimit; // 3 - } death_in_dungeon; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19 - struct - { - uint32 groupSize; // 3 can be 5, 10 or 25 - } complete_raid; - - // ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20 - struct - { - uint32 creatureEntry; // 3 - } killed_by_creature; - - // ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24 - struct - { - uint32 unused; // 3 - uint32 fallHeight; // 4 - } fall_without_dying; - - // ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26 - struct - { - uint32 type; // 3, see enum EnviromentalDamage - } death_from; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27 - struct - { - uint32 questID; // 3 - uint32 questCount; // 4 - } complete_quest; - - // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28 - // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2 = 69 - struct - { - uint32 spellID; // 3 - uint32 spellCount; // 4 - } be_spell_target; - - // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL = 29 - // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110 - struct - { - uint32 spellID; // 3 - uint32 castCount; // 4 - } cast_spell; - - // ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31 - struct - { - uint32 areaID; // 3 Reference to AreaTable.dbc - uint32 killCount; // 4 - } honorable_kill_at_area; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32 - struct - { - uint32 mapID; // 3 Reference to Map.dbc - } win_arena; - - // ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33 - struct - { - uint32 mapID; // 3 Reference to Map.dbc - } play_arena; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34 - struct - { - uint32 spellID; // 3 Reference to Map.dbc - } learn_spell; - - // ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36 - struct - { - uint32 itemID; // 3 - uint32 itemCount; // 4 - } own_item; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37 - struct - { - uint32 unused; // 3 - uint32 count; // 4 - uint32 flag; // 5 4=in a row - } win_rated_arena; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38 - struct - { - uint32 teamtype; // 3 {2,3,5} - } highest_team_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39 - struct - { - uint32 teamtype; // 3 {2,3,5} - uint32 teamrating; // 4 - } reach_team_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40 - struct - { - uint32 skillID; // 3 - uint32 skillLevel; // 4 apprentice=1, journeyman=2, expert=3, artisan=4, master=5, grand master=6 - } learn_skill_level; - - // ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41 - struct - { - uint32 itemID; // 3 - uint32 itemCount; // 4 - } use_item; - - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42 - struct - { - uint32 itemID; // 3 - uint32 itemCount; // 4 - } loot_item; - - // ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43 - struct - { - // TODO: This rank is _NOT_ the index from AreaTable.dbc - uint32 areaReference; // 3 - } explore_area; - - // ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK = 44 - struct - { - // TODO: This rank is _NOT_ the index from CharTitles.dbc - uint32 rank; // 3 - } own_rank; - - // ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT = 45 - struct - { - uint32 unused; // 3 - uint32 numberOfSlots; // 4 - } buy_bank_slot; - - // ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION = 46 - struct - { - uint32 factionID; // 3 - uint32 reputationAmount; // 4 Total reputation amount, so 42000 = exalted - } gain_reputation; - - // ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION= 47 - struct - { - uint32 unused; // 3 - uint32 numberOfExaltedFactions; // 4 - } gain_exalted_reputation; - - // ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP = 48 - struct - { - uint32 unused; // 3 - uint32 numberOfVisits; // 4 - } visit_barber; - - // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49 - // TODO: where is the required itemlevel stored? - struct - { - uint32 itemSlot; // 3 - uint32 count; // 4 - } equip_epic_item; - - // ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50 - struct - { - uint32 rollValue; // 3 - uint32 count; // 4 - } roll_need_on_loot; - // ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT = 51 - struct - { - uint32 rollValue; // 3 - uint32 count; // 4 - } roll_greed_on_loot; - - // ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52 - struct - { - uint32 classID; // 3 - uint32 count; // 4 - } hk_class; - - // ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53 - struct - { - uint32 raceID; // 3 - uint32 count; // 4 - } hk_race; - - // ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54 - // TODO: where is the information about the target stored? - struct - { - uint32 emoteID; // 3 enum TextEmotes - uint32 count; // 4 count of emotes, always required special target or requirements - } do_emote; - // ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13 - // ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55 - // ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56 - struct - { - uint32 unused; // 3 - uint32 count; // 4 - uint32 flag; // 5 =3 for battleground healing - uint32 mapid; // 6 - } healing_done; - - // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57 - struct - { - uint32 itemID; // 3 - uint32 count; // 4 - } equip_item; - - // ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD= 62 - struct - { - uint32 unused; // 3 - uint32 goldInCopper; // 4 - } quest_reward_money; - - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67 - struct - { - uint32 unused; // 3 - uint32 goldInCopper; // 4 - } loot_money; - - // ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68 - struct - { - uint32 goEntry; // 3 - uint32 useCount; // 4 - } use_gameobject; - - // ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL = 70 - // TODO: are those special criteria stored in the dbc or do we have to add another sql table? - struct - { - uint32 unused; // 3 - uint32 killCount; // 4 - } special_pvp_kill; - - // ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72 - struct - { - uint32 goEntry; // 3 - uint32 lootCount; // 4 - } fish_in_gameobject; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75 - struct - { - uint32 skillLine; // 3 - uint32 spellCount; // 4 - } learn_skillline_spell; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76 - struct - { - uint32 unused; // 3 - uint32 duelCount; // 4 - } win_duel; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96 - struct - { - uint32 powerType; // 3 mana=0, 1=rage, 3=energy, 6=runic power - } highest_power; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97 - struct - { - uint32 statType; // 3 4=spirit, 3=int, 2=stamina, 1=agi, 0=strength - } highest_stat; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98 - struct - { - uint32 spellSchool; // 3 - } highest_spellpower; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100 - struct - { - uint32 ratingType; // 3 - } highest_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109 - struct - { - uint32 lootType; // 3 3=fishing, 2=pickpocket, 4=disentchant - uint32 lootTypeCount; // 4 - } loot_type; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE = 112 - struct - { - uint32 skillLine; // 3 - uint32 spellCount; // 4 - } learn_skill_line; - - // ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113 - struct - { - uint32 unused; // 3 - uint32 killCount; // 4 - } honorable_kill; - - struct - { - uint32 field3; // 3 main requirement - uint32 count; // 4 main requirement count - uint32 additionalRequirement1_type; // 5 additional requirement 1 type - uint32 additionalRequirement1_value; // 6 additional requirement 1 value - uint32 additionalRequirement2_type; // 7 additional requirement 2 type - uint32 additionalRequirement2_value; // 8 additional requirement 1 value - } raw; - }; - //char* name[16]; // 9-24 - //uint32 name_flags; // 25 - uint32 completionFlag; // 26 - uint32 timedType; // 27 - uint32 timerStartEvent; // 28 Alway appears with timed events - // for timed spells it is spell id for - // timed kills it is creature id - uint32 timeLimit; // 29 time limit in seconds - //uint32 showOrder; // 30 show order -}; - -struct AreaTableEntry -{ - uint32 ID; // 0 - uint32 mapid; // 1 - uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area - uint32 exploreFlag; // 3, main index - uint32 flags; // 4, unknown value but 312 for all cities - // 5-9 unused - int32 area_level; // 10 - char* area_name[16]; // 11-26 - // 27, string flags, unused - uint32 team; // 28 - - // helpers - bool IsSanctuary() const - { - if (mapid == 609) - return true; - return (flags & AREA_FLAG_SANCTUARY); - } -}; - -struct AreaGroupEntry -{ - uint32 AreaGroupId; // 0 - uint32 AreaId[6]; // 1-6 - uint32 nextGroup; // 7 index of next group -}; - -struct AreaPOIEntry -{ - uint32 id; //0 - uint32 icon[11]; //1-11 - float x; //12 - float y; //13 - float z; //14 - uint32 mapId; //15 - //uint32 val1; //16 - uint32 zoneId; //17 - //char* name[16]; //18-33 - //uint32 name_flag; //34 - //char* name2[16]; //35-50 - //uint32 name_flag2; //51 - uint32 worldState; //52 - //uint32 val2; //53 -}; - -struct AreaTriggerEntry -{ - uint32 id; // 0 m_ID - uint32 mapid; // 1 m_ContinentID - float x; // 2 m_x - float y; // 3 m_y - float z; // 4 m_z - float radius; // 5 m_radius - float box_x; // 6 m_box_length - float box_y; // 7 m_box_width - float box_z; // 8 m_box_heigh - float box_orientation; // 9 m_box_yaw -}; - -struct AuctionHouseEntry -{ - uint32 houseId; // 0 index - uint32 faction; // 1 id of faction.dbc for player factions associated with city - uint32 depositPercent; // 2 1/3 from real - uint32 cutPercent; // 3 - //char* name[16]; // 4-19 - // 20 string flag, unused -}; - -struct BankBagSlotPricesEntry -{ - uint32 ID; - uint32 price; -}; - -struct BarberShopStyleEntry -{ - uint32 Id; // 0 - uint32 type; // 1 value 0 -> hair, value 2 -> facialhair - //char* name[16]; // 2-17 name of hair style - //uint32 name_flags; // 18 - //uint32 unk_name[16]; // 19-34, all empty - //uint32 unk_flags; // 35 - //float CostMultiplier; // 36 values 1 and 0.75 - uint32 race; // 37 race - uint32 gender; // 38 0 -> male, 1 -> female - uint32 hair_id; // 39 real ID to hair/facial hair -}; - -struct BattlemasterListEntry -{ - uint32 id; // 0 - int32 mapid[8]; // 1-8 mapid - uint32 type; // 9 (3 - BG, 4 - arena) - //uint32 canJoinAsGroup; // 10 (0 or 1) - char* name[16]; // 11-26 - //uint32 nameFlags // 27 string flag, unused - uint32 maxGroupSize; // 28 maxGroupSize, used for checking if queue as group - uint32 HolidayWorldStateId; // 29 new 3.1 - //uint32 MinLevel; // 30 - //uint32 SomeLevel; // 31, may be max level -}; - -#define MAX_OUTFIT_ITEMS 24 - -struct CharStartOutfitEntry -{ - //uint32 Id; // 0 - uint32 RaceClassGender; // 1 (UNIT_FIELD_BYTES_0 & 0x00FFFFFF) comparable (0 byte = race, 1 byte = class, 2 byte = gender) - int32 ItemId[MAX_OUTFIT_ITEMS]; // 2-13 - //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 14-25 not required at server side - //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 26-37 not required at server side - //uint32 Unknown1; // 38, unique values (index-like with gaps ordered in other way as ids) - //uint32 Unknown2; // 39 - //uint32 Unknown3; // 40 -}; - -struct CharTitlesEntry -{ - uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId() - //uint32 unk1; // 1 flags? - char* name[16]; // 2-17 - // 18 string flag, unused - //char* name2[16]; // 19-34, unused - // 35 string flag, unused - uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<