From 455bfb01645510c677b88c693e0092244e1901e4 Mon Sep 17 00:00:00 2001 From: click Date: Sat, 5 Jun 2010 21:22:47 +0200 Subject: Move core/realm files to new subdirectory --HG-- branch : trunk rename : src/framework/CMakeLists.txt => src/server/framework/CMakeLists.txt rename : src/framework/Dynamic/FactoryHolder.h => src/server/framework/Dynamic/FactoryHolder.h rename : src/framework/Dynamic/ObjectRegistry.h => src/server/framework/Dynamic/ObjectRegistry.h rename : src/framework/GameSystem/Grid.h => src/server/framework/GameSystem/Grid.h rename : src/framework/GameSystem/GridLoader.h => src/server/framework/GameSystem/GridLoader.h rename : src/framework/GameSystem/GridRefManager.h => src/server/framework/GameSystem/GridRefManager.h rename : src/framework/GameSystem/GridReference.h => src/server/framework/GameSystem/GridReference.h rename : src/framework/GameSystem/NGrid.h => src/server/framework/GameSystem/NGrid.h rename : src/framework/GameSystem/TypeContainer.h => src/server/framework/GameSystem/TypeContainer.h rename : src/framework/GameSystem/TypeContainerFunctions.h => src/server/framework/GameSystem/TypeContainerFunctions.h rename : src/framework/GameSystem/TypeContainerFunctionsPtr.h => src/server/framework/GameSystem/TypeContainerFunctionsPtr.h rename : src/framework/GameSystem/TypeContainerVisitor.h => src/server/framework/GameSystem/TypeContainerVisitor.h rename : src/framework/Network/SocketDefines.h => src/server/framework/Network/SocketDefines.h rename : src/framework/Platform/CompilerDefs.h => src/server/framework/Platform/CompilerDefs.h rename : src/framework/Platform/Define.h => src/server/framework/Platform/Define.h rename : src/framework/Policies/CreationPolicy.h => src/server/framework/Policies/CreationPolicy.h rename : src/framework/Policies/ObjectLifeTime.cpp => src/server/framework/Policies/ObjectLifeTime.cpp rename : src/framework/Policies/ObjectLifeTime.h => src/server/framework/Policies/ObjectLifeTime.h rename : src/framework/Policies/Singleton.h => src/server/framework/Policies/Singleton.h rename : src/framework/Policies/SingletonImp.h => src/server/framework/Policies/SingletonImp.h rename : src/framework/Policies/ThreadingModel.h => src/server/framework/Policies/ThreadingModel.h rename : src/framework/Utilities/ByteConverter.h => src/server/framework/Utilities/ByteConverter.h rename : src/framework/Utilities/Callback.h => src/server/framework/Utilities/Callback.h rename : src/framework/Utilities/CountedReference/Reference.h => src/server/framework/Utilities/CountedReference/Reference.h rename : src/framework/Utilities/CountedReference/ReferenceHolder.h => src/server/framework/Utilities/CountedReference/ReferenceHolder.h rename : src/framework/Utilities/CountedReference/ReferenceImpl.h => src/server/framework/Utilities/CountedReference/ReferenceImpl.h rename : src/framework/Utilities/EventProcessor.cpp => src/server/framework/Utilities/EventProcessor.cpp rename : src/framework/Utilities/EventProcessor.h => src/server/framework/Utilities/EventProcessor.h rename : src/framework/Utilities/LinkedList.h => src/server/framework/Utilities/LinkedList.h rename : src/framework/Utilities/LinkedReference/RefManager.h => src/server/framework/Utilities/LinkedReference/RefManager.h rename : src/framework/Utilities/LinkedReference/Reference.h => src/server/framework/Utilities/LinkedReference/Reference.h rename : src/framework/Utilities/TypeList.h => src/server/framework/Utilities/TypeList.h rename : src/framework/Utilities/UnorderedMap.h => src/server/framework/Utilities/UnorderedMap.h rename : src/game/AccountMgr.cpp => src/server/game/AccountMgr.cpp rename : src/game/AccountMgr.h => src/server/game/AccountMgr.h rename : src/game/AchievementMgr.cpp => src/server/game/AchievementMgr.cpp rename : src/game/AchievementMgr.h => src/server/game/AchievementMgr.h rename : src/game/AddonHandler.cpp => src/server/game/AddonHandler.cpp rename : src/game/AddonHandler.h => src/server/game/AddonHandler.h rename : src/game/AddonMgr.cpp => src/server/game/AddonMgr.cpp rename : src/game/AddonMgr.h => src/server/game/AddonMgr.h rename : src/game/ArenaTeam.cpp => src/server/game/ArenaTeam.cpp rename : src/game/ArenaTeam.h => src/server/game/ArenaTeam.h rename : src/game/ArenaTeamHandler.cpp => src/server/game/ArenaTeamHandler.cpp rename : src/game/AuctionHouseBot.cpp => src/server/game/AuctionHouseBot.cpp rename : src/game/AuctionHouseBot.h => src/server/game/AuctionHouseBot.h rename : src/game/AuctionHouseHandler.cpp => src/server/game/AuctionHouseHandler.cpp rename : src/game/AuctionHouseMgr.cpp => src/server/game/AuctionHouseMgr.cpp rename : src/game/AuctionHouseMgr.h => src/server/game/AuctionHouseMgr.h rename : src/game/Bag.cpp => src/server/game/Bag.cpp rename : src/game/Bag.h => src/server/game/Bag.h rename : src/game/BattleGround.cpp => src/server/game/BattleGround.cpp rename : src/game/BattleGround.h => src/server/game/BattleGround.h rename : src/game/BattleGroundAA.cpp => src/server/game/BattleGroundAA.cpp rename : src/game/BattleGroundAA.h => src/server/game/BattleGroundAA.h rename : src/game/BattleGroundAB.cpp => src/server/game/BattleGroundAB.cpp rename : src/game/BattleGroundAB.h => src/server/game/BattleGroundAB.h rename : src/game/BattleGroundAV.cpp => src/server/game/BattleGroundAV.cpp rename : src/game/BattleGroundAV.h => src/server/game/BattleGroundAV.h rename : src/game/BattleGroundBE.cpp => src/server/game/BattleGroundBE.cpp rename : src/game/BattleGroundBE.h => src/server/game/BattleGroundBE.h rename : src/game/BattleGroundDS.cpp => src/server/game/BattleGroundDS.cpp rename : src/game/BattleGroundDS.h => src/server/game/BattleGroundDS.h rename : src/game/BattleGroundEY.cpp => src/server/game/BattleGroundEY.cpp rename : src/game/BattleGroundEY.h => src/server/game/BattleGroundEY.h rename : src/game/BattleGroundHandler.cpp => src/server/game/BattleGroundHandler.cpp rename : src/game/BattleGroundIC.cpp => src/server/game/BattleGroundIC.cpp rename : src/game/BattleGroundIC.h => src/server/game/BattleGroundIC.h rename : src/game/BattleGroundMgr.cpp => src/server/game/BattleGroundMgr.cpp rename : src/game/BattleGroundMgr.h => src/server/game/BattleGroundMgr.h rename : src/game/BattleGroundNA.cpp => src/server/game/BattleGroundNA.cpp rename : src/game/BattleGroundNA.h => src/server/game/BattleGroundNA.h rename : src/game/BattleGroundRB.cpp => src/server/game/BattleGroundRB.cpp rename : src/game/BattleGroundRB.h => src/server/game/BattleGroundRB.h rename : src/game/BattleGroundRL.cpp => src/server/game/BattleGroundRL.cpp rename : src/game/BattleGroundRL.h => src/server/game/BattleGroundRL.h rename : src/game/BattleGroundRV.cpp => src/server/game/BattleGroundRV.cpp rename : src/game/BattleGroundRV.h => src/server/game/BattleGroundRV.h rename : src/game/BattleGroundSA.cpp => src/server/game/BattleGroundSA.cpp rename : src/game/BattleGroundSA.h => src/server/game/BattleGroundSA.h rename : src/game/BattleGroundWS.cpp => src/server/game/BattleGroundWS.cpp rename : src/game/BattleGroundWS.h => src/server/game/BattleGroundWS.h rename : src/game/CMakeLists.txt => src/server/game/CMakeLists.txt rename : src/game/Calendar.cpp => src/server/game/Calendar.cpp rename : src/game/Calendar.h => src/server/game/Calendar.h rename : src/game/CalendarHandler.cpp => src/server/game/CalendarHandler.cpp rename : src/game/Cell.h => src/server/game/Cell.h rename : src/game/CellImpl.h => src/server/game/CellImpl.h rename : src/game/Channel.cpp => src/server/game/Channel.cpp rename : src/game/Channel.h => src/server/game/Channel.h rename : src/game/ChannelHandler.cpp => src/server/game/ChannelHandler.cpp rename : src/game/ChannelMgr.cpp => src/server/game/ChannelMgr.cpp rename : src/game/ChannelMgr.h => src/server/game/ChannelMgr.h rename : src/game/CharacterHandler.cpp => src/server/game/CharacterHandler.cpp rename : src/game/Chat.cpp => src/server/game/Chat.cpp rename : src/game/Chat.h => src/server/game/Chat.h rename : src/game/ChatHandler.cpp => src/server/game/ChatHandler.cpp rename : src/game/CombatAI.cpp => src/server/game/CombatAI.cpp rename : src/game/CombatAI.h => src/server/game/CombatAI.h rename : src/game/CombatHandler.cpp => src/server/game/CombatHandler.cpp rename : src/game/ConditionMgr.cpp => src/server/game/ConditionMgr.cpp rename : src/game/ConditionMgr.h => src/server/game/ConditionMgr.h rename : src/game/ConfusedMovementGenerator.cpp => src/server/game/ConfusedMovementGenerator.cpp rename : src/game/ConfusedMovementGenerator.h => src/server/game/ConfusedMovementGenerator.h rename : src/game/Corpse.cpp => src/server/game/Corpse.cpp rename : src/game/Corpse.h => src/server/game/Corpse.h rename : src/game/Creature.cpp => src/server/game/Creature.cpp rename : src/game/Creature.h => src/server/game/Creature.h rename : src/game/CreatureAI.cpp => src/server/game/CreatureAI.cpp rename : src/game/CreatureAI.h => src/server/game/CreatureAI.h rename : src/game/CreatureAIFactory.h => src/server/game/CreatureAIFactory.h rename : src/game/CreatureAIImpl.h => src/server/game/CreatureAIImpl.h rename : src/game/CreatureAIRegistry.cpp => src/server/game/CreatureAIRegistry.cpp rename : src/game/CreatureAIRegistry.h => src/server/game/CreatureAIRegistry.h rename : src/game/CreatureAISelector.cpp => src/server/game/CreatureAISelector.cpp rename : src/game/CreatureAISelector.h => src/server/game/CreatureAISelector.h rename : src/game/CreatureEventAI.cpp => src/server/game/CreatureEventAI.cpp rename : src/game/CreatureEventAI.h => src/server/game/CreatureEventAI.h rename : src/game/CreatureEventAIMgr.cpp => src/server/game/CreatureEventAIMgr.cpp rename : src/game/CreatureEventAIMgr.h => src/server/game/CreatureEventAIMgr.h rename : src/game/CreatureGroups.cpp => src/server/game/CreatureGroups.cpp rename : src/game/CreatureGroups.h => src/server/game/CreatureGroups.h rename : src/game/DBCEnums.h => src/server/game/DBCEnums.h rename : src/game/DBCStores.cpp => src/server/game/DBCStores.cpp rename : src/game/DBCStores.h => src/server/game/DBCStores.h rename : src/game/DBCStructure.h => src/server/game/DBCStructure.h rename : src/game/DBCfmt.h => src/server/game/DBCfmt.h rename : src/game/Debugcmds.cpp => src/server/game/Debugcmds.cpp rename : src/game/DestinationHolder.cpp => src/server/game/DestinationHolder.cpp rename : src/game/DestinationHolder.h => src/server/game/DestinationHolder.h rename : src/game/DestinationHolderImp.h => src/server/game/DestinationHolderImp.h rename : src/game/DuelHandler.cpp => src/server/game/DuelHandler.cpp rename : src/game/DynamicObject.cpp => src/server/game/DynamicObject.cpp rename : src/game/DynamicObject.h => src/server/game/DynamicObject.h rename : src/game/FleeingMovementGenerator.cpp => src/server/game/FleeingMovementGenerator.cpp rename : src/game/FleeingMovementGenerator.h => src/server/game/FleeingMovementGenerator.h rename : src/game/FollowerRefManager.h => src/server/game/FollowerRefManager.h rename : src/game/FollowerReference.cpp => src/server/game/FollowerReference.cpp rename : src/game/FollowerReference.h => src/server/game/FollowerReference.h rename : src/game/Formulas.h => src/server/game/Formulas.h rename : src/game/GameEventMgr.cpp => src/server/game/GameEventMgr.cpp rename : src/game/GameEventMgr.h => src/server/game/GameEventMgr.h rename : src/game/GameObject.cpp => src/server/game/GameObject.cpp rename : src/game/GameObject.h => src/server/game/GameObject.h rename : src/game/GlobalEvents.cpp => src/server/game/GlobalEvents.cpp rename : src/game/GlobalEvents.h => src/server/game/GlobalEvents.h rename : src/game/GossipDef.cpp => src/server/game/GossipDef.cpp rename : src/game/GossipDef.h => src/server/game/GossipDef.h rename : src/game/GridDefines.h => src/server/game/GridDefines.h rename : src/game/GridNotifiers.cpp => src/server/game/GridNotifiers.cpp rename : src/game/GridNotifiers.h => src/server/game/GridNotifiers.h rename : src/game/GridNotifiersImpl.h => src/server/game/GridNotifiersImpl.h rename : src/game/GridStates.cpp => src/server/game/GridStates.cpp rename : src/game/GridStates.h => src/server/game/GridStates.h rename : src/game/Group.cpp => src/server/game/Group.cpp rename : src/game/Group.h => src/server/game/Group.h rename : src/game/GroupHandler.cpp => src/server/game/GroupHandler.cpp rename : src/game/GroupRefManager.h => src/server/game/GroupRefManager.h rename : src/game/GroupReference.cpp => src/server/game/GroupReference.cpp rename : src/game/GroupReference.h => src/server/game/GroupReference.h rename : src/game/GuardAI.cpp => src/server/game/GuardAI.cpp rename : src/game/GuardAI.h => src/server/game/GuardAI.h rename : src/game/Guild.cpp => src/server/game/Guild.cpp rename : src/game/Guild.h => src/server/game/Guild.h rename : src/game/GuildHandler.cpp => src/server/game/GuildHandler.cpp rename : src/game/HomeMovementGenerator.cpp => src/server/game/HomeMovementGenerator.cpp rename : src/game/HomeMovementGenerator.h => src/server/game/HomeMovementGenerator.h rename : src/game/HostileRefManager.cpp => src/server/game/HostileRefManager.cpp rename : src/game/HostileRefManager.h => src/server/game/HostileRefManager.h rename : src/game/IdleMovementGenerator.cpp => src/server/game/IdleMovementGenerator.cpp rename : src/game/IdleMovementGenerator.h => src/server/game/IdleMovementGenerator.h rename : src/game/InstanceData.cpp => src/server/game/InstanceData.cpp rename : src/game/InstanceData.h => src/server/game/InstanceData.h rename : src/game/InstanceSaveMgr.cpp => src/server/game/InstanceSaveMgr.cpp rename : src/game/InstanceSaveMgr.h => src/server/game/InstanceSaveMgr.h rename : src/game/Item.cpp => src/server/game/Item.cpp rename : src/game/Item.h => src/server/game/Item.h rename : src/game/ItemEnchantmentMgr.cpp => src/server/game/ItemEnchantmentMgr.cpp rename : src/game/ItemEnchantmentMgr.h => src/server/game/ItemEnchantmentMgr.h rename : src/game/ItemHandler.cpp => src/server/game/ItemHandler.cpp rename : src/game/ItemPrototype.h => src/server/game/ItemPrototype.h rename : src/game/LFG.h => src/server/game/LFG.h rename : src/game/LFGHandler.cpp => src/server/game/LFGHandler.cpp rename : src/game/LFGMgr.cpp => src/server/game/LFGMgr.cpp rename : src/game/LFGMgr.h => src/server/game/LFGMgr.h rename : src/game/Language.h => src/server/game/Language.h rename : src/game/Level0.cpp => src/server/game/Level0.cpp rename : src/game/Level1.cpp => src/server/game/Level1.cpp rename : src/game/Level2.cpp => src/server/game/Level2.cpp rename : src/game/Level3.cpp => src/server/game/Level3.cpp rename : src/game/LootHandler.cpp => src/server/game/LootHandler.cpp rename : src/game/LootMgr.cpp => src/server/game/LootMgr.cpp rename : src/game/LootMgr.h => src/server/game/LootMgr.h rename : src/game/Mail.cpp => src/server/game/Mail.cpp rename : src/game/Mail.h => src/server/game/Mail.h rename : src/game/Map.cpp => src/server/game/Map.cpp rename : src/game/Map.h => src/server/game/Map.h rename : src/game/MapInstanced.cpp => src/server/game/MapInstanced.cpp rename : src/game/MapInstanced.h => src/server/game/MapInstanced.h rename : src/game/MapManager.cpp => src/server/game/MapManager.cpp rename : src/game/MapManager.h => src/server/game/MapManager.h rename : src/game/MapRefManager.h => src/server/game/MapRefManager.h rename : src/game/MapReference.h => src/server/game/MapReference.h rename : src/game/MapUpdater.cpp => src/server/game/MapUpdater.cpp rename : src/game/MapUpdater.h => src/server/game/MapUpdater.h rename : src/game/MiscHandler.cpp => src/server/game/MiscHandler.cpp rename : src/game/MotionMaster.cpp => src/server/game/MotionMaster.cpp rename : src/game/MotionMaster.h => src/server/game/MotionMaster.h rename : src/game/MovementGenerator.cpp => src/server/game/MovementGenerator.cpp rename : src/game/MovementGenerator.h => src/server/game/MovementGenerator.h rename : src/game/MovementGeneratorImpl.h => src/server/game/MovementGeneratorImpl.h rename : src/game/MovementHandler.cpp => src/server/game/MovementHandler.cpp rename : src/game/NPCHandler.cpp => src/server/game/NPCHandler.cpp rename : src/game/NPCHandler.h => src/server/game/NPCHandler.h rename : src/game/Object.cpp => src/server/game/Object.cpp rename : src/game/Object.h => src/server/game/Object.h rename : src/game/ObjectAccessor.cpp => src/server/game/ObjectAccessor.cpp rename : src/game/ObjectAccessor.h => src/server/game/ObjectAccessor.h rename : src/game/ObjectDefines.h => src/server/game/ObjectDefines.h rename : src/game/ObjectGridLoader.cpp => src/server/game/ObjectGridLoader.cpp rename : src/game/ObjectGridLoader.h => src/server/game/ObjectGridLoader.h rename : src/game/ObjectMgr.cpp => src/server/game/ObjectMgr.cpp rename : src/game/ObjectMgr.h => src/server/game/ObjectMgr.h rename : src/game/ObjectPosSelector.cpp => src/server/game/ObjectPosSelector.cpp rename : src/game/ObjectPosSelector.h => src/server/game/ObjectPosSelector.h rename : src/game/Opcodes.cpp => src/server/game/Opcodes.cpp rename : src/game/Opcodes.h => src/server/game/Opcodes.h rename : src/game/OutdoorPvP.cpp => src/server/game/OutdoorPvP.cpp rename : src/game/OutdoorPvP.h => src/server/game/OutdoorPvP.h rename : src/game/OutdoorPvPEP.cpp => src/server/game/OutdoorPvPEP.cpp rename : src/game/OutdoorPvPEP.h => src/server/game/OutdoorPvPEP.h rename : src/game/OutdoorPvPHP.cpp => src/server/game/OutdoorPvPHP.cpp rename : src/game/OutdoorPvPHP.h => src/server/game/OutdoorPvPHP.h rename : src/game/OutdoorPvPImpl.h => src/server/game/OutdoorPvPImpl.h rename : src/game/OutdoorPvPMgr.cpp => src/server/game/OutdoorPvPMgr.cpp rename : src/game/OutdoorPvPMgr.h => src/server/game/OutdoorPvPMgr.h rename : src/game/OutdoorPvPNA.cpp => src/server/game/OutdoorPvPNA.cpp rename : src/game/OutdoorPvPNA.h => src/server/game/OutdoorPvPNA.h rename : src/game/OutdoorPvPSI.cpp => src/server/game/OutdoorPvPSI.cpp rename : src/game/OutdoorPvPSI.h => src/server/game/OutdoorPvPSI.h rename : src/game/OutdoorPvPTF.cpp => src/server/game/OutdoorPvPTF.cpp rename : src/game/OutdoorPvPTF.h => src/server/game/OutdoorPvPTF.h rename : src/game/OutdoorPvPZM.cpp => src/server/game/OutdoorPvPZM.cpp rename : src/game/OutdoorPvPZM.h => src/server/game/OutdoorPvPZM.h rename : src/game/PassiveAI.cpp => src/server/game/PassiveAI.cpp rename : src/game/PassiveAI.h => src/server/game/PassiveAI.h rename : src/game/Path.h => src/server/game/Path.h rename : src/game/Pet.cpp => src/server/game/Pet.cpp rename : src/game/Pet.h => src/server/game/Pet.h rename : src/game/PetAI.cpp => src/server/game/PetAI.cpp rename : src/game/PetAI.h => src/server/game/PetAI.h rename : src/game/PetHandler.cpp => src/server/game/PetHandler.cpp rename : src/game/PetitionsHandler.cpp => src/server/game/PetitionsHandler.cpp rename : src/game/Player.cpp => src/server/game/Player.cpp rename : src/game/Player.h => src/server/game/Player.h rename : src/game/PlayerDump.cpp => src/server/game/PlayerDump.cpp rename : src/game/PlayerDump.h => src/server/game/PlayerDump.h rename : src/game/PointMovementGenerator.cpp => src/server/game/PointMovementGenerator.cpp rename : src/game/PointMovementGenerator.h => src/server/game/PointMovementGenerator.h rename : src/game/PoolHandler.cpp => src/server/game/PoolHandler.cpp rename : src/game/PoolHandler.h => src/server/game/PoolHandler.h rename : src/game/QueryHandler.cpp => src/server/game/QueryHandler.cpp rename : src/game/QuestDef.cpp => src/server/game/QuestDef.cpp rename : src/game/QuestDef.h => src/server/game/QuestDef.h rename : src/game/QuestHandler.cpp => src/server/game/QuestHandler.cpp rename : src/game/RandomMovementGenerator.cpp => src/server/game/RandomMovementGenerator.cpp rename : src/game/RandomMovementGenerator.h => src/server/game/RandomMovementGenerator.h rename : src/game/ReactorAI.cpp => src/server/game/ReactorAI.cpp rename : src/game/ReactorAI.h => src/server/game/ReactorAI.h rename : src/game/ReputationMgr.cpp => src/server/game/ReputationMgr.cpp rename : src/game/ReputationMgr.h => src/server/game/ReputationMgr.h rename : src/game/ScriptLoader.cpp => src/server/game/ScriptLoader.cpp rename : src/game/ScriptLoader.h => src/server/game/ScriptLoader.h rename : src/game/ScriptMgr.cpp => src/server/game/ScriptMgr.cpp rename : src/game/ScriptMgr.h => src/server/game/ScriptMgr.h rename : src/game/ScriptSystem.cpp => src/server/game/ScriptSystem.cpp rename : src/game/ScriptSystem.h => src/server/game/ScriptSystem.h rename : src/game/ScriptedCreature.cpp => src/server/game/ScriptedCreature.cpp rename : src/game/ScriptedCreature.h => src/server/game/ScriptedCreature.h rename : src/game/ScriptedEscortAI.cpp => src/server/game/ScriptedEscortAI.cpp rename : src/game/ScriptedEscortAI.h => src/server/game/ScriptedEscortAI.h rename : src/game/ScriptedFollowerAI.cpp => src/server/game/ScriptedFollowerAI.cpp rename : src/game/ScriptedFollowerAI.h => src/server/game/ScriptedFollowerAI.h rename : src/game/ScriptedGossip.h => src/server/game/ScriptedGossip.h rename : src/game/ScriptedGuardAI.cpp => src/server/game/ScriptedGuardAI.cpp rename : src/game/ScriptedGuardAI.h => src/server/game/ScriptedGuardAI.h rename : src/game/ScriptedInstance.h => src/server/game/ScriptedInstance.h rename : src/game/ScriptedPch.cpp => src/server/game/ScriptedPch.cpp rename : src/game/ScriptedPch.h => src/server/game/ScriptedPch.h rename : src/game/ScriptedSimpleAI.cpp => src/server/game/ScriptedSimpleAI.cpp rename : src/game/ScriptedSimpleAI.h => src/server/game/ScriptedSimpleAI.h rename : src/game/ScriptedSmartAI.cpp => src/server/game/ScriptedSmartAI.cpp rename : src/game/ScriptedSmartAI.h => src/server/game/ScriptedSmartAI.h rename : src/game/SharedDefines.h => src/server/game/SharedDefines.h rename : src/game/SkillDiscovery.cpp => src/server/game/SkillDiscovery.cpp rename : src/game/SkillDiscovery.h => src/server/game/SkillDiscovery.h rename : src/game/SkillExtraItems.cpp => src/server/game/SkillExtraItems.cpp rename : src/game/SkillExtraItems.h => src/server/game/SkillExtraItems.h rename : src/game/SkillHandler.cpp => src/server/game/SkillHandler.cpp rename : src/game/SocialMgr.cpp => src/server/game/SocialMgr.cpp rename : src/game/SocialMgr.h => src/server/game/SocialMgr.h rename : src/game/Spell.cpp => src/server/game/Spell.cpp rename : src/game/Spell.h => src/server/game/Spell.h rename : src/game/SpellAuraDefines.h => src/server/game/SpellAuraDefines.h rename : src/game/SpellAuraEffects.cpp => src/server/game/SpellAuraEffects.cpp rename : src/game/SpellAuraEffects.h => src/server/game/SpellAuraEffects.h rename : src/game/SpellAuras.cpp => src/server/game/SpellAuras.cpp rename : src/game/SpellAuras.h => src/server/game/SpellAuras.h rename : src/game/SpellEffects.cpp => src/server/game/SpellEffects.cpp rename : src/game/SpellHandler.cpp => src/server/game/SpellHandler.cpp rename : src/game/SpellMgr.cpp => src/server/game/SpellMgr.cpp rename : src/game/SpellMgr.h => src/server/game/SpellMgr.h rename : src/game/StatSystem.cpp => src/server/game/StatSystem.cpp rename : src/game/TargetedMovementGenerator.cpp => src/server/game/TargetedMovementGenerator.cpp rename : src/game/TargetedMovementGenerator.h => src/server/game/TargetedMovementGenerator.h rename : src/game/TaxiHandler.cpp => src/server/game/TaxiHandler.cpp rename : src/game/TemporarySummon.cpp => src/server/game/TemporarySummon.cpp rename : src/game/TemporarySummon.h => src/server/game/TemporarySummon.h rename : src/game/ThreatManager.cpp => src/server/game/ThreatManager.cpp rename : src/game/ThreatManager.h => src/server/game/ThreatManager.h rename : src/game/TicketHandler.cpp => src/server/game/TicketHandler.cpp rename : src/game/TimeMgr.cpp => src/server/game/TimeMgr.cpp rename : src/game/TimeMgr.h => src/server/game/TimeMgr.h rename : src/game/Tools.cpp => src/server/game/Tools.cpp rename : src/game/Tools.h => src/server/game/Tools.h rename : src/game/Totem.cpp => src/server/game/Totem.cpp rename : src/game/Totem.h => src/server/game/Totem.h rename : src/game/TotemAI.cpp => src/server/game/TotemAI.cpp rename : src/game/TotemAI.h => src/server/game/TotemAI.h rename : src/game/TradeHandler.cpp => src/server/game/TradeHandler.cpp rename : src/game/Transports.cpp => src/server/game/Transports.cpp rename : src/game/Transports.h => src/server/game/Transports.h rename : src/game/Traveller.h => src/server/game/Traveller.h rename : src/game/Unit.cpp => src/server/game/Unit.cpp rename : src/game/Unit.h => src/server/game/Unit.h rename : src/game/UnitAI.cpp => src/server/game/UnitAI.cpp rename : src/game/UnitAI.h => src/server/game/UnitAI.h rename : src/game/UnitEvents.h => src/server/game/UnitEvents.h rename : src/game/UpdateData.cpp => src/server/game/UpdateData.cpp rename : src/game/UpdateData.h => src/server/game/UpdateData.h rename : src/game/UpdateFields.h => src/server/game/UpdateFields.h rename : src/game/UpdateMask.h => src/server/game/UpdateMask.h rename : src/game/Vehicle.cpp => src/server/game/Vehicle.cpp rename : src/game/Vehicle.h => src/server/game/Vehicle.h rename : src/game/VoiceChatHandler.cpp => src/server/game/VoiceChatHandler.cpp rename : src/game/WaypointManager.cpp => src/server/game/WaypointManager.cpp rename : src/game/WaypointManager.h => src/server/game/WaypointManager.h rename : src/game/WaypointMovementGenerator.cpp => src/server/game/WaypointMovementGenerator.cpp rename : src/game/WaypointMovementGenerator.h => src/server/game/WaypointMovementGenerator.h rename : src/game/Weather.cpp => src/server/game/Weather.cpp rename : src/game/Weather.h => src/server/game/Weather.h rename : src/game/World.cpp => src/server/game/World.cpp rename : src/game/World.h => src/server/game/World.h rename : src/game/WorldLog.cpp => src/server/game/WorldLog.cpp rename : src/game/WorldLog.h => src/server/game/WorldLog.h rename : src/game/WorldSession.cpp => src/server/game/WorldSession.cpp rename : src/game/WorldSession.h => src/server/game/WorldSession.h rename : src/game/WorldSocket.cpp => src/server/game/WorldSocket.cpp rename : src/game/WorldSocket.h => src/server/game/WorldSocket.h rename : src/game/WorldSocketMgr.cpp => src/server/game/WorldSocketMgr.cpp rename : src/game/WorldSocketMgr.h => src/server/game/WorldSocketMgr.h rename : src/game/ZoneScript.h => src/server/game/ZoneScript.h rename : src/game/pchdef.cpp => src/server/game/pchdef.cpp rename : src/game/pchdef.h => src/server/game/pchdef.h rename : src/game/pchlinux.cpp => src/server/game/pchlinux.cpp rename : src/game/pchlinux.h => src/server/game/pchlinux.h rename : src/scripts/CMakeLists.txt => src/server/scripts/CMakeLists.txt rename : src/scripts/custom/custom_example.cpp => src/server/scripts/custom/custom_example.cpp rename : src/scripts/custom/custom_gossip_codebox.cpp => src/server/scripts/custom/custom_gossip_codebox.cpp rename : src/scripts/custom/npc_acherus_taxi.cpp => src/server/scripts/custom/npc_acherus_taxi.cpp rename : src/scripts/custom/npc_wyrmresttempel_taxi.cpp => src/server/scripts/custom/npc_wyrmresttempel_taxi.cpp rename : src/scripts/custom/on_events.cpp => src/server/scripts/custom/on_events.cpp rename : src/scripts/custom/test.cpp => src/server/scripts/custom/test.cpp rename : src/scripts/eastern_kingdoms/alterac_mountains.cpp => src/server/scripts/eastern_kingdoms/alterac_mountains.cpp rename : src/scripts/eastern_kingdoms/alterac_valley/alterac_valley.cpp => src/server/scripts/eastern_kingdoms/alterac_valley/alterac_valley.cpp rename : src/scripts/eastern_kingdoms/alterac_valley/boss_balinda.cpp => src/server/scripts/eastern_kingdoms/alterac_valley/boss_balinda.cpp rename : src/scripts/eastern_kingdoms/alterac_valley/boss_drekthar.cpp => src/server/scripts/eastern_kingdoms/alterac_valley/boss_drekthar.cpp rename : src/scripts/eastern_kingdoms/alterac_valley/boss_galvangar.cpp => src/server/scripts/eastern_kingdoms/alterac_valley/boss_galvangar.cpp rename : src/scripts/eastern_kingdoms/alterac_valley/boss_vanndar.cpp => src/server/scripts/eastern_kingdoms/alterac_valley/boss_vanndar.cpp rename : src/scripts/eastern_kingdoms/arathi_highlands.cpp => src/server/scripts/eastern_kingdoms/arathi_highlands.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.h => src/server/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.h rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp rename : src/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp => src/server/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/blackrock_spire.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/blackrock_spire.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/blackrock_spire.h => src/server/scripts/eastern_kingdoms/blackrock_spire/blackrock_spire.h rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp rename : src/scripts/eastern_kingdoms/blackrock_spire/instance_blackrock_spire.cpp => src/server/scripts/eastern_kingdoms/blackrock_spire/instance_blackrock_spire.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp rename : src/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp => src/server/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp rename : src/scripts/eastern_kingdoms/blasted_lands.cpp => src/server/scripts/eastern_kingdoms/blasted_lands.cpp rename : src/scripts/eastern_kingdoms/boss_kruul.cpp => src/server/scripts/eastern_kingdoms/boss_kruul.cpp rename : src/scripts/eastern_kingdoms/burning_steppes.cpp => src/server/scripts/eastern_kingdoms/burning_steppes.cpp rename : src/scripts/eastern_kingdoms/deadmines/boss_mr_smite.cpp => src/server/scripts/eastern_kingdoms/deadmines/boss_mr_smite.cpp rename : src/scripts/eastern_kingdoms/deadmines/deadmines.cpp => src/server/scripts/eastern_kingdoms/deadmines/deadmines.cpp rename : src/scripts/eastern_kingdoms/deadmines/deadmines.h => src/server/scripts/eastern_kingdoms/deadmines/deadmines.h rename : src/scripts/eastern_kingdoms/deadmines/instance_deadmines.cpp => src/server/scripts/eastern_kingdoms/deadmines/instance_deadmines.cpp rename : src/scripts/eastern_kingdoms/dun_morogh.cpp => src/server/scripts/eastern_kingdoms/dun_morogh.cpp rename : src/scripts/eastern_kingdoms/duskwood.cpp => src/server/scripts/eastern_kingdoms/duskwood.cpp rename : src/scripts/eastern_kingdoms/eastern_plaguelands.cpp => src/server/scripts/eastern_kingdoms/eastern_plaguelands.cpp rename : src/scripts/eastern_kingdoms/elwynn_forest.cpp => src/server/scripts/eastern_kingdoms/elwynn_forest.cpp rename : src/scripts/eastern_kingdoms/eversong_woods.cpp => src/server/scripts/eastern_kingdoms/eversong_woods.cpp rename : src/scripts/eastern_kingdoms/ghostlands.cpp => src/server/scripts/eastern_kingdoms/ghostlands.cpp rename : src/scripts/eastern_kingdoms/gnomeregan/gnomeregan.cpp => src/server/scripts/eastern_kingdoms/gnomeregan/gnomeregan.cpp rename : src/scripts/eastern_kingdoms/gnomeregan/gnomeregan.h => src/server/scripts/eastern_kingdoms/gnomeregan/gnomeregan.h rename : src/scripts/eastern_kingdoms/gnomeregan/instance_gnomeregan.cpp => src/server/scripts/eastern_kingdoms/gnomeregan/instance_gnomeregan.cpp rename : src/scripts/eastern_kingdoms/hinterlands.cpp => src/server/scripts/eastern_kingdoms/hinterlands.cpp rename : src/scripts/eastern_kingdoms/ironforge.cpp => src/server/scripts/eastern_kingdoms/ironforge.cpp rename : src/scripts/eastern_kingdoms/isle_of_queldanas.cpp => src/server/scripts/eastern_kingdoms/isle_of_queldanas.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_curator.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_curator.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp rename : src/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp => src/server/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp rename : src/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp => src/server/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp rename : src/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp => src/server/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp rename : src/scripts/eastern_kingdoms/karazhan/karazhan.cpp => src/server/scripts/eastern_kingdoms/karazhan/karazhan.cpp rename : src/scripts/eastern_kingdoms/karazhan/karazhan.h => src/server/scripts/eastern_kingdoms/karazhan/karazhan.h rename : src/scripts/eastern_kingdoms/loch_modan.cpp => src/server/scripts/eastern_kingdoms/loch_modan.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp => src/server/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp => src/server/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp => src/server/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp => src/server/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp => src/server/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp => src/server/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp rename : src/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.h => src/server/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.h rename : src/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_garr.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_garr.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp rename : src/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp => src/server/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp rename : src/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp => src/server/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp rename : src/scripts/eastern_kingdoms/molten_core/molten_core.cpp => src/server/scripts/eastern_kingdoms/molten_core/molten_core.cpp rename : src/scripts/eastern_kingdoms/molten_core/molten_core.h => src/server/scripts/eastern_kingdoms/molten_core/molten_core.h rename : src/scripts/eastern_kingdoms/redridge_mountains.cpp => src/server/scripts/eastern_kingdoms/redridge_mountains.cpp rename : src/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp => src/server/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp rename : src/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp => src/server/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp rename : src/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp => src/server/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp rename : src/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp => src/server/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp => src/server/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp rename : src/scripts/eastern_kingdoms/scarlet_monastery/scarlet_monastery.h => src/server/scripts/eastern_kingdoms/scarlet_monastery/scarlet_monastery.h rename : src/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp rename : src/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp => src/server/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp rename : src/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp => src/server/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp rename : src/scripts/eastern_kingdoms/scholomance/scholomance.h => src/server/scripts/eastern_kingdoms/scholomance/scholomance.h rename : src/scripts/eastern_kingdoms/searing_gorge.cpp => src/server/scripts/eastern_kingdoms/searing_gorge.cpp rename : src/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp => src/server/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp rename : src/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp => src/server/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp rename : src/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.h => src/server/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.h rename : src/scripts/eastern_kingdoms/silvermoon_city.cpp => src/server/scripts/eastern_kingdoms/silvermoon_city.cpp rename : src/scripts/eastern_kingdoms/silverpine_forest.cpp => src/server/scripts/eastern_kingdoms/silverpine_forest.cpp rename : src/scripts/eastern_kingdoms/stormwind_city.cpp => src/server/scripts/eastern_kingdoms/stormwind_city.cpp rename : src/scripts/eastern_kingdoms/stranglethorn_vale.cpp => src/server/scripts/eastern_kingdoms/stranglethorn_vale.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp rename : src/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp => src/server/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp rename : src/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp => src/server/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp rename : src/scripts/eastern_kingdoms/stratholme/stratholme.cpp => src/server/scripts/eastern_kingdoms/stratholme/stratholme.cpp rename : src/scripts/eastern_kingdoms/stratholme/stratholme.h => src/server/scripts/eastern_kingdoms/stratholme/stratholme.h rename : src/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp => src/server/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp rename : src/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp => src/server/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp rename : src/scripts/eastern_kingdoms/sunken_temple/sunken_temple.h => src/server/scripts/eastern_kingdoms/sunken_temple/sunken_temple.h rename : src/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp => src/server/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp rename : src/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.h => src/server/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.h rename : src/scripts/eastern_kingdoms/tirisfal_glades.cpp => src/server/scripts/eastern_kingdoms/tirisfal_glades.cpp rename : src/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp => src/server/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp rename : src/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp => src/server/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp rename : src/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp => src/server/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp rename : src/scripts/eastern_kingdoms/uldaman/uldaman.cpp => src/server/scripts/eastern_kingdoms/uldaman/uldaman.cpp rename : src/scripts/eastern_kingdoms/undercity.cpp => src/server/scripts/eastern_kingdoms/undercity.cpp rename : src/scripts/eastern_kingdoms/western_plaguelands.cpp => src/server/scripts/eastern_kingdoms/western_plaguelands.cpp rename : src/scripts/eastern_kingdoms/westfall.cpp => src/server/scripts/eastern_kingdoms/westfall.cpp rename : src/scripts/eastern_kingdoms/wetlands.cpp => src/server/scripts/eastern_kingdoms/wetlands.cpp rename : src/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp => src/server/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp rename : src/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp => src/server/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp rename : src/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp => src/server/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp rename : src/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp => src/server/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp rename : src/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp => src/server/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp rename : src/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp => src/server/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp rename : src/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp => src/server/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp rename : src/scripts/eastern_kingdoms/zulaman/zulaman.cpp => src/server/scripts/eastern_kingdoms/zulaman/zulaman.cpp rename : src/scripts/eastern_kingdoms/zulaman/zulaman.h => src/server/scripts/eastern_kingdoms/zulaman/zulaman.h rename : src/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp rename : src/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp => src/server/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp rename : src/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp => src/server/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp rename : src/scripts/eastern_kingdoms/zulgurub/zulgurub.h => src/server/scripts/eastern_kingdoms/zulgurub/zulgurub.h rename : src/scripts/examples/example_creature.cpp => src/server/scripts/examples/example_creature.cpp rename : src/scripts/examples/example_escort.cpp => src/server/scripts/examples/example_escort.cpp rename : src/scripts/examples/example_gossip_codebox.cpp => src/server/scripts/examples/example_gossip_codebox.cpp rename : src/scripts/examples/example_misc.cpp => src/server/scripts/examples/example_misc.cpp rename : src/scripts/kalimdor/ashenvale.cpp => src/server/scripts/kalimdor/ashenvale.cpp rename : src/scripts/kalimdor/azshara.cpp => src/server/scripts/kalimdor/azshara.cpp rename : src/scripts/kalimdor/azuremyst_isle.cpp => src/server/scripts/kalimdor/azuremyst_isle.cpp rename : src/scripts/kalimdor/blackfathom_depths/blackfathom_deeps.cpp => src/server/scripts/kalimdor/blackfathom_depths/blackfathom_deeps.cpp rename : src/scripts/kalimdor/blackfathom_depths/blackfathom_deeps.h => src/server/scripts/kalimdor/blackfathom_depths/blackfathom_deeps.h rename : src/scripts/kalimdor/blackfathom_depths/boss_aku_mai.cpp => src/server/scripts/kalimdor/blackfathom_depths/boss_aku_mai.cpp rename : src/scripts/kalimdor/blackfathom_depths/boss_gelihast.cpp => src/server/scripts/kalimdor/blackfathom_depths/boss_gelihast.cpp rename : src/scripts/kalimdor/blackfathom_depths/boss_kelris.cpp => src/server/scripts/kalimdor/blackfathom_depths/boss_kelris.cpp rename : src/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp => src/server/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp rename : src/scripts/kalimdor/bloodmyst_isle.cpp => src/server/scripts/kalimdor/bloodmyst_isle.cpp rename : src/scripts/kalimdor/boss_azuregos.cpp => src/server/scripts/kalimdor/boss_azuregos.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_infinite.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_infinite.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.cpp rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.h => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.h rename : src/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp => src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp rename : src/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp => src/server/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp rename : src/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp => src/server/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp rename : src/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp => src/server/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp rename : src/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp => src/server/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp rename : src/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.h => src/server/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.h rename : src/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp => src/server/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/hyjal.h => src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal.h rename : src/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h => src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h rename : src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp rename : src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h => src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h rename : src/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp => src/server/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp rename : src/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp => src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp rename : src/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp => src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp rename : src/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp => src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp rename : src/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp => src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp rename : src/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp => src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp rename : src/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.h => src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.h rename : src/scripts/kalimdor/darkshore.cpp => src/server/scripts/kalimdor/darkshore.cpp rename : src/scripts/kalimdor/desolace.cpp => src/server/scripts/kalimdor/desolace.cpp rename : src/scripts/kalimdor/durotar.cpp => src/server/scripts/kalimdor/durotar.cpp rename : src/scripts/kalimdor/dustwallow_marsh.cpp => src/server/scripts/kalimdor/dustwallow_marsh.cpp rename : src/scripts/kalimdor/felwood.cpp => src/server/scripts/kalimdor/felwood.cpp rename : src/scripts/kalimdor/feralas.cpp => src/server/scripts/kalimdor/feralas.cpp rename : src/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp => src/server/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp rename : src/scripts/kalimdor/maraudon/boss_landslide.cpp => src/server/scripts/kalimdor/maraudon/boss_landslide.cpp rename : src/scripts/kalimdor/maraudon/boss_noxxion.cpp => src/server/scripts/kalimdor/maraudon/boss_noxxion.cpp rename : src/scripts/kalimdor/maraudon/boss_princess_theradras.cpp => src/server/scripts/kalimdor/maraudon/boss_princess_theradras.cpp rename : src/scripts/kalimdor/moonglade.cpp => src/server/scripts/kalimdor/moonglade.cpp rename : src/scripts/kalimdor/mulgore.cpp => src/server/scripts/kalimdor/mulgore.cpp rename : src/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp => src/server/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp rename : src/scripts/kalimdor/onyxias_lair/instance_onyxias_lair.cpp => src/server/scripts/kalimdor/onyxias_lair/instance_onyxias_lair.cpp rename : src/scripts/kalimdor/onyxias_lair/onyxias_lair.h => src/server/scripts/kalimdor/onyxias_lair/onyxias_lair.h rename : src/scripts/kalimdor/orgrimmar.cpp => src/server/scripts/kalimdor/orgrimmar.cpp rename : src/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp => src/server/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp rename : src/scripts/kalimdor/razorfen_downs/instance_razorfen_downs.cpp => src/server/scripts/kalimdor/razorfen_downs/instance_razorfen_downs.cpp rename : src/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp => src/server/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp rename : src/scripts/kalimdor/razorfen_downs/razorfen_downs.h => src/server/scripts/kalimdor/razorfen_downs/razorfen_downs.h rename : src/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp => src/server/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp rename : src/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp => src/server/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp rename : src/scripts/kalimdor/razorfen_kraul/razorfen_kraul.h => src/server/scripts/kalimdor/razorfen_kraul/razorfen_kraul.h rename : src/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp => src/server/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp rename : src/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h => src/server/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h rename : src/scripts/kalimdor/silithus.cpp => src/server/scripts/kalimdor/silithus.cpp rename : src/scripts/kalimdor/stonetalon_mountains.cpp => src/server/scripts/kalimdor/stonetalon_mountains.cpp rename : src/scripts/kalimdor/tanaris.cpp => src/server/scripts/kalimdor/tanaris.cpp rename : src/scripts/kalimdor/teldrassil.cpp => src/server/scripts/kalimdor/teldrassil.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp => src/server/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp rename : src/scripts/kalimdor/temple_of_ahnqiraj/temple_of_ahnqiraj.h => src/server/scripts/kalimdor/temple_of_ahnqiraj/temple_of_ahnqiraj.h rename : src/scripts/kalimdor/the_barrens.cpp => src/server/scripts/kalimdor/the_barrens.cpp rename : src/scripts/kalimdor/thousand_needles.cpp => src/server/scripts/kalimdor/thousand_needles.cpp rename : src/scripts/kalimdor/thunder_bluff.cpp => src/server/scripts/kalimdor/thunder_bluff.cpp rename : src/scripts/kalimdor/ungoro_crater.cpp => src/server/scripts/kalimdor/ungoro_crater.cpp rename : src/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp => src/server/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp rename : src/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp => src/server/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp rename : src/scripts/kalimdor/wailing_caverns/wailing_caverns.h => src/server/scripts/kalimdor/wailing_caverns/wailing_caverns.h rename : src/scripts/kalimdor/winterspring.cpp => src/server/scripts/kalimdor/winterspring.cpp rename : src/scripts/kalimdor/zulfarrak/instance_zulfarrak.cpp => src/server/scripts/kalimdor/zulfarrak/instance_zulfarrak.cpp rename : src/scripts/kalimdor/zulfarrak/zulfarrak.cpp => src/server/scripts/kalimdor/zulfarrak/zulfarrak.cpp rename : src/scripts/northrend/azjol_nerub/ahnkahet/ahnkahet.h => src/server/scripts/northrend/azjol_nerub/ahnkahet/ahnkahet.h rename : src/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp => src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp rename : src/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp => src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp rename : src/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp => src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp rename : src/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp => src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp rename : src/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp => src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp rename : src/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp => src/server/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp rename : src/scripts/northrend/azjol_nerub/azjol_nerub/azjol_nerub.h => src/server/scripts/northrend/azjol_nerub/azjol_nerub/azjol_nerub.h rename : src/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp => src/server/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp rename : src/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp => src/server/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp rename : src/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp => src/server/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp rename : src/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp => src/server/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp rename : src/scripts/northrend/borean_tundra.cpp => src/server/scripts/northrend/borean_tundra.cpp rename : src/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_argent_challenge.cpp => src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_argent_challenge.cpp rename : src/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_black_knight.cpp => src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_black_knight.cpp rename : src/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_grand_champions.cpp => src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_grand_champions.cpp rename : src/scripts/northrend/crusaders_coliseum/trial_of_the_champion/instance_trial_of_the_champion.cpp => src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/instance_trial_of_the_champion.cpp rename : src/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.cpp => src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.cpp rename : src/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.h => src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.h rename : src/scripts/northrend/crystalsong_forest.cpp => src/server/scripts/northrend/crystalsong_forest.cpp rename : src/scripts/northrend/dalaran.cpp => src/server/scripts/northrend/dalaran.cpp rename : src/scripts/northrend/dragonblight.cpp => src/server/scripts/northrend/dragonblight.cpp rename : src/scripts/northrend/draktharon_keep/boss_dred.cpp => src/server/scripts/northrend/draktharon_keep/boss_dred.cpp rename : src/scripts/northrend/draktharon_keep/boss_novos.cpp => src/server/scripts/northrend/draktharon_keep/boss_novos.cpp rename : src/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp => src/server/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp rename : src/scripts/northrend/draktharon_keep/boss_trollgore.cpp => src/server/scripts/northrend/draktharon_keep/boss_trollgore.cpp rename : src/scripts/northrend/draktharon_keep/drak_tharon_keep.h => src/server/scripts/northrend/draktharon_keep/drak_tharon_keep.h rename : src/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp => src/server/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp rename : src/scripts/northrend/frozen_halls/forge_of_souls/boss_bronjahm.cpp => src/server/scripts/northrend/frozen_halls/forge_of_souls/boss_bronjahm.cpp rename : src/scripts/northrend/frozen_halls/forge_of_souls/boss_devourer_of_souls.cpp => src/server/scripts/northrend/frozen_halls/forge_of_souls/boss_devourer_of_souls.cpp rename : src/scripts/northrend/frozen_halls/forge_of_souls/forge_of_souls.cpp => src/server/scripts/northrend/frozen_halls/forge_of_souls/forge_of_souls.cpp rename : src/scripts/northrend/frozen_halls/forge_of_souls/forge_of_souls.h => src/server/scripts/northrend/frozen_halls/forge_of_souls/forge_of_souls.h rename : src/scripts/northrend/frozen_halls/forge_of_souls/instance_forge_of_souls.cpp => src/server/scripts/northrend/frozen_halls/forge_of_souls/instance_forge_of_souls.cpp rename : src/scripts/northrend/frozen_halls/halls_of_reflection/boss_falric.cpp => src/server/scripts/northrend/frozen_halls/halls_of_reflection/boss_falric.cpp rename : src/scripts/northrend/frozen_halls/halls_of_reflection/boss_marwyn.cpp => src/server/scripts/northrend/frozen_halls/halls_of_reflection/boss_marwyn.cpp rename : src/scripts/northrend/frozen_halls/halls_of_reflection/halls_of_reflection.cpp => src/server/scripts/northrend/frozen_halls/halls_of_reflection/halls_of_reflection.cpp rename : src/scripts/northrend/frozen_halls/halls_of_reflection/halls_of_reflection.h => src/server/scripts/northrend/frozen_halls/halls_of_reflection/halls_of_reflection.h rename : src/scripts/northrend/frozen_halls/halls_of_reflection/instance_halls_of_reflection.cpp => src/server/scripts/northrend/frozen_halls/halls_of_reflection/instance_halls_of_reflection.cpp rename : src/scripts/northrend/frozen_halls/pit_of_saron/boss_forgemaster_garfrost.cpp => src/server/scripts/northrend/frozen_halls/pit_of_saron/boss_forgemaster_garfrost.cpp rename : src/scripts/northrend/frozen_halls/pit_of_saron/boss_krickandick.cpp => src/server/scripts/northrend/frozen_halls/pit_of_saron/boss_krickandick.cpp rename : src/scripts/northrend/frozen_halls/pit_of_saron/boss_scourgelord_tyrannus.cpp => src/server/scripts/northrend/frozen_halls/pit_of_saron/boss_scourgelord_tyrannus.cpp rename : src/scripts/northrend/frozen_halls/pit_of_saron/instance_pit_of_saron.cpp => src/server/scripts/northrend/frozen_halls/pit_of_saron/instance_pit_of_saron.cpp rename : src/scripts/northrend/frozen_halls/pit_of_saron/pit_of_saron.cpp => src/server/scripts/northrend/frozen_halls/pit_of_saron/pit_of_saron.cpp rename : src/scripts/northrend/frozen_halls/pit_of_saron/pit_of_saron.h => src/server/scripts/northrend/frozen_halls/pit_of_saron/pit_of_saron.h rename : src/scripts/northrend/grizzly_hills.cpp => src/server/scripts/northrend/grizzly_hills.cpp rename : src/scripts/northrend/gundrak/boss_drakkari_colossus.cpp => src/server/scripts/northrend/gundrak/boss_drakkari_colossus.cpp rename : src/scripts/northrend/gundrak/boss_eck.cpp => src/server/scripts/northrend/gundrak/boss_eck.cpp rename : src/scripts/northrend/gundrak/boss_gal_darah.cpp => src/server/scripts/northrend/gundrak/boss_gal_darah.cpp rename : src/scripts/northrend/gundrak/boss_moorabi.cpp => src/server/scripts/northrend/gundrak/boss_moorabi.cpp rename : src/scripts/northrend/gundrak/boss_slad_ran.cpp => src/server/scripts/northrend/gundrak/boss_slad_ran.cpp rename : src/scripts/northrend/gundrak/gundrak.h => src/server/scripts/northrend/gundrak/gundrak.h rename : src/scripts/northrend/gundrak/instance_gundrak.cpp => src/server/scripts/northrend/gundrak/instance_gundrak.cpp rename : src/scripts/northrend/howling_fjord.cpp => src/server/scripts/northrend/howling_fjord.cpp rename : src/scripts/northrend/icecrown.cpp => src/server/scripts/northrend/icecrown.cpp rename : src/scripts/northrend/naxxramas/boss_anubrekhan.cpp => src/server/scripts/northrend/naxxramas/boss_anubrekhan.cpp rename : src/scripts/northrend/naxxramas/boss_faerlina.cpp => src/server/scripts/northrend/naxxramas/boss_faerlina.cpp rename : src/scripts/northrend/naxxramas/boss_four_horsemen.cpp => src/server/scripts/northrend/naxxramas/boss_four_horsemen.cpp rename : src/scripts/northrend/naxxramas/boss_gluth.cpp => src/server/scripts/northrend/naxxramas/boss_gluth.cpp rename : src/scripts/northrend/naxxramas/boss_gothik.cpp => src/server/scripts/northrend/naxxramas/boss_gothik.cpp rename : src/scripts/northrend/naxxramas/boss_grobbulus.cpp => src/server/scripts/northrend/naxxramas/boss_grobbulus.cpp rename : src/scripts/northrend/naxxramas/boss_heigan.cpp => src/server/scripts/northrend/naxxramas/boss_heigan.cpp rename : src/scripts/northrend/naxxramas/boss_highlord_mograine.cpp => src/server/scripts/northrend/naxxramas/boss_highlord_mograine.cpp rename : src/scripts/northrend/naxxramas/boss_kelthuzad.cpp => src/server/scripts/northrend/naxxramas/boss_kelthuzad.cpp rename : src/scripts/northrend/naxxramas/boss_loatheb.cpp => src/server/scripts/northrend/naxxramas/boss_loatheb.cpp rename : src/scripts/northrend/naxxramas/boss_maexxna.cpp => src/server/scripts/northrend/naxxramas/boss_maexxna.cpp rename : src/scripts/northrend/naxxramas/boss_noth.cpp => src/server/scripts/northrend/naxxramas/boss_noth.cpp rename : src/scripts/northrend/naxxramas/boss_patchwerk.cpp => src/server/scripts/northrend/naxxramas/boss_patchwerk.cpp rename : src/scripts/northrend/naxxramas/boss_razuvious.cpp => src/server/scripts/northrend/naxxramas/boss_razuvious.cpp rename : src/scripts/northrend/naxxramas/boss_sapphiron.cpp => src/server/scripts/northrend/naxxramas/boss_sapphiron.cpp rename : src/scripts/northrend/naxxramas/boss_thaddius.cpp => src/server/scripts/northrend/naxxramas/boss_thaddius.cpp rename : src/scripts/northrend/naxxramas/instance_naxxramas.cpp => src/server/scripts/northrend/naxxramas/instance_naxxramas.cpp rename : src/scripts/northrend/naxxramas/naxxramas.h => src/server/scripts/northrend/naxxramas/naxxramas.h rename : src/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp => src/server/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp rename : src/scripts/northrend/nexus/eye_of_eternity/eye_of_eternity.h => src/server/scripts/northrend/nexus/eye_of_eternity/eye_of_eternity.h rename : src/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp => src/server/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp rename : src/scripts/northrend/nexus/nexus/boss_anomalus.cpp => src/server/scripts/northrend/nexus/nexus/boss_anomalus.cpp rename : src/scripts/northrend/nexus/nexus/boss_keristrasza.cpp => src/server/scripts/northrend/nexus/nexus/boss_keristrasza.cpp rename : src/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp => src/server/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp rename : src/scripts/northrend/nexus/nexus/boss_ormorok.cpp => src/server/scripts/northrend/nexus/nexus/boss_ormorok.cpp rename : src/scripts/northrend/nexus/nexus/commander_kolurg.cpp => src/server/scripts/northrend/nexus/nexus/commander_kolurg.cpp rename : src/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp => src/server/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp rename : src/scripts/northrend/nexus/nexus/instance_nexus.cpp => src/server/scripts/northrend/nexus/nexus/instance_nexus.cpp rename : src/scripts/northrend/nexus/nexus/nexus.h => src/server/scripts/northrend/nexus/nexus/nexus.h rename : src/scripts/northrend/nexus/oculus/boss_drakos.cpp => src/server/scripts/northrend/nexus/oculus/boss_drakos.cpp rename : src/scripts/northrend/nexus/oculus/boss_eregos.cpp => src/server/scripts/northrend/nexus/oculus/boss_eregos.cpp rename : src/scripts/northrend/nexus/oculus/boss_urom.cpp => src/server/scripts/northrend/nexus/oculus/boss_urom.cpp rename : src/scripts/northrend/nexus/oculus/boss_varos.cpp => src/server/scripts/northrend/nexus/oculus/boss_varos.cpp rename : src/scripts/northrend/nexus/oculus/instance_oculus.cpp => src/server/scripts/northrend/nexus/oculus/instance_oculus.cpp rename : src/scripts/northrend/nexus/oculus/oculus.cpp => src/server/scripts/northrend/nexus/oculus/oculus.cpp rename : src/scripts/northrend/nexus/oculus/oculus.h => src/server/scripts/northrend/nexus/oculus/oculus.h rename : src/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp => src/server/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp rename : src/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp => src/server/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp rename : src/scripts/northrend/obsidian_sanctum/obsidian_sanctum.h => src/server/scripts/northrend/obsidian_sanctum/obsidian_sanctum.h rename : src/scripts/northrend/sholazar_basin.cpp => src/server/scripts/northrend/sholazar_basin.cpp rename : src/scripts/northrend/storm_peaks.cpp => src/server/scripts/northrend/storm_peaks.cpp rename : src/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp => src/server/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp rename : src/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp => src/server/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp rename : src/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp => src/server/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp rename : src/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp => src/server/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp rename : src/scripts/northrend/ulduar/halls_of_lightning/halls_of_lightning.h => src/server/scripts/northrend/ulduar/halls_of_lightning/halls_of_lightning.h rename : src/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp => src/server/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp rename : src/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp => src/server/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp rename : src/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp => src/server/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp rename : src/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp => src/server/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp rename : src/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp => src/server/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp rename : src/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.h => src/server/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.h rename : src/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp => src/server/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_algalon.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_algalon.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_freya.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_freya.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_hodir.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_hodir.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_ignis.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_ignis.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_thorim.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_thorim.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_xt002.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_xt002.cpp rename : src/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp => src/server/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp rename : src/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp => src/server/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp rename : src/scripts/northrend/ulduar/ulduar/ulduar.h => src/server/scripts/northrend/ulduar/ulduar/ulduar.h rename : src/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp => src/server/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.h => src/server/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.h rename : src/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp => src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp rename : src/scripts/northrend/utgarde_keep/utgarde_pinnacle/utgarde_pinnacle.h => src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/utgarde_pinnacle.h rename : src/scripts/northrend/vault_of_archavon/boss_archavon.cpp => src/server/scripts/northrend/vault_of_archavon/boss_archavon.cpp rename : src/scripts/northrend/vault_of_archavon/boss_emalon.cpp => src/server/scripts/northrend/vault_of_archavon/boss_emalon.cpp rename : src/scripts/northrend/vault_of_archavon/boss_koralon.cpp => src/server/scripts/northrend/vault_of_archavon/boss_koralon.cpp rename : src/scripts/northrend/vault_of_archavon/boss_toravon.cpp => src/server/scripts/northrend/vault_of_archavon/boss_toravon.cpp rename : src/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp => src/server/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp rename : src/scripts/northrend/vault_of_archavon/vault_of_archavon.h => src/server/scripts/northrend/vault_of_archavon/vault_of_archavon.h rename : src/scripts/northrend/violet_hold/boss_cyanigosa.cpp => src/server/scripts/northrend/violet_hold/boss_cyanigosa.cpp rename : src/scripts/northrend/violet_hold/boss_erekem.cpp => src/server/scripts/northrend/violet_hold/boss_erekem.cpp rename : src/scripts/northrend/violet_hold/boss_ichoron.cpp => src/server/scripts/northrend/violet_hold/boss_ichoron.cpp rename : src/scripts/northrend/violet_hold/boss_lavanthor.cpp => src/server/scripts/northrend/violet_hold/boss_lavanthor.cpp rename : src/scripts/northrend/violet_hold/boss_moragg.cpp => src/server/scripts/northrend/violet_hold/boss_moragg.cpp rename : src/scripts/northrend/violet_hold/boss_xevozz.cpp => src/server/scripts/northrend/violet_hold/boss_xevozz.cpp rename : src/scripts/northrend/violet_hold/boss_zuramat.cpp => src/server/scripts/northrend/violet_hold/boss_zuramat.cpp rename : src/scripts/northrend/violet_hold/instance_violet_hold.cpp => src/server/scripts/northrend/violet_hold/instance_violet_hold.cpp rename : src/scripts/northrend/violet_hold/violet_hold.cpp => src/server/scripts/northrend/violet_hold/violet_hold.cpp rename : src/scripts/northrend/violet_hold/violet_hold.h => src/server/scripts/northrend/violet_hold/violet_hold.h rename : src/scripts/northrend/zuldrak.cpp => src/server/scripts/northrend/zuldrak.cpp rename : src/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp => src/server/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp rename : src/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp => src/server/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp rename : src/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp => src/server/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp rename : src/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp => src/server/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp rename : src/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp => src/server/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp rename : src/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp => src/server/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp rename : src/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp => src/server/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp rename : src/scripts/outland/auchindoun/sethekk_halls/sethekk_halls.h => src/server/scripts/outland/auchindoun/sethekk_halls/sethekk_halls.h rename : src/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp => src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp rename : src/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp => src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp rename : src/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp => src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp rename : src/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp => src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp rename : src/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp => src/server/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp rename : src/scripts/outland/auchindoun/shadow_labyrinth/shadow_labyrinth.h => src/server/scripts/outland/auchindoun/shadow_labyrinth/shadow_labyrinth.h rename : src/scripts/outland/black_temple/black_temple.cpp => src/server/scripts/outland/black_temple/black_temple.cpp rename : src/scripts/outland/black_temple/black_temple.h => src/server/scripts/outland/black_temple/black_temple.h rename : src/scripts/outland/black_temple/boss_bloodboil.cpp => src/server/scripts/outland/black_temple/boss_bloodboil.cpp rename : src/scripts/outland/black_temple/boss_illidan.cpp => src/server/scripts/outland/black_temple/boss_illidan.cpp rename : src/scripts/outland/black_temple/boss_mother_shahraz.cpp => src/server/scripts/outland/black_temple/boss_mother_shahraz.cpp rename : src/scripts/outland/black_temple/boss_reliquary_of_souls.cpp => src/server/scripts/outland/black_temple/boss_reliquary_of_souls.cpp rename : src/scripts/outland/black_temple/boss_shade_of_akama.cpp => src/server/scripts/outland/black_temple/boss_shade_of_akama.cpp rename : src/scripts/outland/black_temple/boss_supremus.cpp => src/server/scripts/outland/black_temple/boss_supremus.cpp rename : src/scripts/outland/black_temple/boss_teron_gorefiend.cpp => src/server/scripts/outland/black_temple/boss_teron_gorefiend.cpp rename : src/scripts/outland/black_temple/boss_warlord_najentus.cpp => src/server/scripts/outland/black_temple/boss_warlord_najentus.cpp rename : src/scripts/outland/black_temple/illidari_council.cpp => src/server/scripts/outland/black_temple/illidari_council.cpp rename : src/scripts/outland/black_temple/instance_black_temple.cpp => src/server/scripts/outland/black_temple/instance_black_temple.cpp rename : src/scripts/outland/blades_edge_mountains.cpp => src/server/scripts/outland/blades_edge_mountains.cpp rename : src/scripts/outland/boss_doomlord_kazzak.cpp => src/server/scripts/outland/boss_doomlord_kazzak.cpp rename : src/scripts/outland/boss_doomwalker.cpp => src/server/scripts/outland/boss_doomwalker.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp rename : src/scripts/outland/coilfang_resevoir/serpent_shrine/serpent_shrine.h => src/server/scripts/outland/coilfang_resevoir/serpent_shrine/serpent_shrine.h rename : src/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp => src/server/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp rename : src/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp => src/server/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp rename : src/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp => src/server/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp rename : src/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp => src/server/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp rename : src/scripts/outland/coilfang_resevoir/steam_vault/steam_vault.h => src/server/scripts/outland/coilfang_resevoir/steam_vault/steam_vault.h rename : src/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp => src/server/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp rename : src/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp => src/server/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp rename : src/scripts/outland/gruuls_lair/boss_gruul.cpp => src/server/scripts/outland/gruuls_lair/boss_gruul.cpp rename : src/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp => src/server/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp rename : src/scripts/outland/gruuls_lair/gruuls_lair.h => src/server/scripts/outland/gruuls_lair/gruuls_lair.h rename : src/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp => src/server/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp rename : src/scripts/outland/hellfire_citadel/blood_furnace/blood_furnace.h => src/server/scripts/outland/hellfire_citadel/blood_furnace/blood_furnace.h rename : src/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp => src/server/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp rename : src/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp => src/server/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp rename : src/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp => src/server/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp rename : src/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp => src/server/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp rename : src/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp => src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp rename : src/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp => src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp rename : src/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp => src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp rename : src/scripts/outland/hellfire_citadel/hellfire_ramparts/hellfire_ramparts.h => src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/hellfire_ramparts.h rename : src/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp => src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp rename : src/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp => src/server/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp rename : src/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp => src/server/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp rename : src/scripts/outland/hellfire_citadel/magtheridons_lair/magtheridons_lair.h => src/server/scripts/outland/hellfire_citadel/magtheridons_lair/magtheridons_lair.h rename : src/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp => src/server/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp rename : src/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp => src/server/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp rename : src/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp => src/server/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp rename : src/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp => src/server/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp rename : src/scripts/outland/hellfire_citadel/shattered_halls/shattered_halls.h => src/server/scripts/outland/hellfire_citadel/shattered_halls/shattered_halls.h rename : src/scripts/outland/hellfire_peninsula.cpp => src/server/scripts/outland/hellfire_peninsula.cpp rename : src/scripts/outland/nagrand.cpp => src/server/scripts/outland/nagrand.cpp rename : src/scripts/outland/netherstorm.cpp => src/server/scripts/outland/netherstorm.cpp rename : src/scripts/outland/shadowmoon_valley.cpp => src/server/scripts/outland/shadowmoon_valley.cpp rename : src/scripts/outland/shattrath_city.cpp => src/server/scripts/outland/shattrath_city.cpp rename : src/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp => src/server/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp rename : src/scripts/outland/tempest_keep/arcatraz/arcatraz.h => src/server/scripts/outland/tempest_keep/arcatraz/arcatraz.h rename : src/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp => src/server/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp rename : src/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp => src/server/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp rename : src/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp => src/server/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp rename : src/scripts/outland/tempest_keep/botanica/boss_laj.cpp => src/server/scripts/outland/tempest_keep/botanica/boss_laj.cpp rename : src/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp => src/server/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp rename : src/scripts/outland/tempest_keep/the_eye/boss_alar.cpp => src/server/scripts/outland/tempest_keep/the_eye/boss_alar.cpp rename : src/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp => src/server/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp rename : src/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp => src/server/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp rename : src/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp => src/server/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp rename : src/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp => src/server/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp rename : src/scripts/outland/tempest_keep/the_eye/the_eye.cpp => src/server/scripts/outland/tempest_keep/the_eye/the_eye.cpp rename : src/scripts/outland/tempest_keep/the_eye/the_eye.h => src/server/scripts/outland/tempest_keep/the_eye/the_eye.h rename : src/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp => src/server/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp rename : src/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp => src/server/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp rename : src/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp => src/server/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp rename : src/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp => src/server/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp rename : src/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp => src/server/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp rename : src/scripts/outland/tempest_keep/the_mechanar/mechanar.h => src/server/scripts/outland/tempest_keep/the_mechanar/mechanar.h rename : src/scripts/outland/terokkar_forest.cpp => src/server/scripts/outland/terokkar_forest.cpp rename : src/scripts/outland/zangarmarsh.cpp => src/server/scripts/outland/zangarmarsh.cpp rename : src/scripts/world/areatrigger_scripts.cpp => src/server/scripts/world/areatrigger_scripts.cpp rename : src/scripts/world/boss_emeriss.cpp => src/server/scripts/world/boss_emeriss.cpp rename : src/scripts/world/boss_lethon.cpp => src/server/scripts/world/boss_lethon.cpp rename : src/scripts/world/boss_taerar.cpp => src/server/scripts/world/boss_taerar.cpp rename : src/scripts/world/boss_ysondre.cpp => src/server/scripts/world/boss_ysondre.cpp rename : src/scripts/world/go_scripts.cpp => src/server/scripts/world/go_scripts.cpp rename : src/scripts/world/guards.cpp => src/server/scripts/world/guards.cpp rename : src/scripts/world/item_scripts.cpp => src/server/scripts/world/item_scripts.cpp rename : src/scripts/world/mob_generic_creature.cpp => src/server/scripts/world/mob_generic_creature.cpp rename : src/scripts/world/npc_innkeeper.cpp => src/server/scripts/world/npc_innkeeper.cpp rename : src/scripts/world/npc_professions.cpp => src/server/scripts/world/npc_professions.cpp rename : src/scripts/world/npc_taxi.cpp => src/server/scripts/world/npc_taxi.cpp rename : src/scripts/world/npcs_special.cpp => src/server/scripts/world/npcs_special.cpp rename : src/shared/Auth/AuthCrypt.cpp => src/server/shared/Auth/AuthCrypt.cpp rename : src/shared/Auth/AuthCrypt.h => src/server/shared/Auth/AuthCrypt.h rename : src/shared/Auth/BigNumber.cpp => src/server/shared/Auth/BigNumber.cpp rename : src/shared/Auth/BigNumber.h => src/server/shared/Auth/BigNumber.h rename : src/shared/Auth/CMakeLists.txt => src/server/shared/Auth/CMakeLists.txt rename : src/shared/Auth/Hmac.cpp => src/server/shared/Auth/Hmac.cpp rename : src/shared/Auth/Hmac.h => src/server/shared/Auth/Hmac.h rename : src/shared/Auth/SARC4.cpp => src/server/shared/Auth/SARC4.cpp rename : src/shared/Auth/SARC4.h => src/server/shared/Auth/SARC4.h rename : src/shared/Auth/Sha1.cpp => src/server/shared/Auth/Sha1.cpp rename : src/shared/Auth/Sha1.h => src/server/shared/Auth/Sha1.h rename : src/shared/Auth/md5.c => src/server/shared/Auth/md5.c rename : src/shared/Auth/md5.h => src/server/shared/Auth/md5.h rename : src/shared/ByteBuffer.h => src/server/shared/ByteBuffer.h rename : src/shared/CMakeLists.txt => src/server/shared/CMakeLists.txt rename : src/shared/Common.cpp => src/server/shared/Common.cpp rename : src/shared/Common.h => src/server/shared/Common.h rename : src/shared/Config/CMakeLists.txt => src/server/shared/Config/CMakeLists.txt rename : src/shared/Config/Config.cpp => src/server/shared/Config/Config.cpp rename : src/shared/Config/Config.h => src/server/shared/Config/Config.h rename : src/shared/Config/ConfigEnv.h => src/server/shared/Config/ConfigEnv.h rename : src/shared/Config/ConfigLibrary.vcproj => src/server/shared/Config/ConfigLibrary.vcproj rename : src/shared/Config/dotconfpp/dotconfpp.cpp => src/server/shared/Config/dotconfpp/dotconfpp.cpp rename : src/shared/Config/dotconfpp/dotconfpp.h => src/server/shared/Config/dotconfpp/dotconfpp.h rename : src/shared/Config/dotconfpp/mempool.cpp => src/server/shared/Config/dotconfpp/mempool.cpp rename : src/shared/Config/dotconfpp/mempool.h => src/server/shared/Config/dotconfpp/mempool.h rename : src/shared/Database/CMakeLists.txt => src/server/shared/Database/CMakeLists.txt rename : src/shared/Database/DBCFileLoader.cpp => src/server/shared/Database/DBCFileLoader.cpp rename : src/shared/Database/DBCFileLoader.h => src/server/shared/Database/DBCFileLoader.h rename : src/shared/Database/DBCStore.h => src/server/shared/Database/DBCStore.h rename : src/shared/Database/Database.cpp => src/server/shared/Database/Database.cpp rename : src/shared/Database/Database.h => src/server/shared/Database/Database.h rename : src/shared/Database/DatabaseEnv.h => src/server/shared/Database/DatabaseEnv.h rename : src/shared/Database/DatabaseImpl.h => src/server/shared/Database/DatabaseImpl.h rename : src/shared/Database/Field.cpp => src/server/shared/Database/Field.cpp rename : src/shared/Database/Field.h => src/server/shared/Database/Field.h rename : src/shared/Database/QueryResult.cpp => src/server/shared/Database/QueryResult.cpp rename : src/shared/Database/QueryResult.h => src/server/shared/Database/QueryResult.h rename : src/shared/Database/SQLStorage.cpp => src/server/shared/Database/SQLStorage.cpp rename : src/shared/Database/SQLStorage.h => src/server/shared/Database/SQLStorage.h rename : src/shared/Database/SQLStorageImpl.h => src/server/shared/Database/SQLStorageImpl.h rename : src/shared/Database/SqlDelayThread.cpp => src/server/shared/Database/SqlDelayThread.cpp rename : src/shared/Database/SqlDelayThread.h => src/server/shared/Database/SqlDelayThread.h rename : src/shared/Database/SqlOperations.cpp => src/server/shared/Database/SqlOperations.cpp rename : src/shared/Database/SqlOperations.h => src/server/shared/Database/SqlOperations.h rename : src/shared/DelayExecutor.cpp => src/server/shared/DelayExecutor.cpp rename : src/shared/DelayExecutor.h => src/server/shared/DelayExecutor.h rename : src/shared/Errors.h => src/server/shared/Errors.h rename : src/shared/LockedQueue.h => src/server/shared/LockedQueue.h rename : src/shared/Log.cpp => src/server/shared/Log.cpp rename : src/shared/Log.h => src/server/shared/Log.h rename : src/shared/MemoryLeaks.cpp => src/server/shared/MemoryLeaks.cpp rename : src/shared/MemoryLeaks.h => src/server/shared/MemoryLeaks.h rename : src/shared/PacketLog.cpp => src/server/shared/PacketLog.cpp rename : src/shared/PacketLog.h => src/server/shared/PacketLog.h rename : src/shared/ProgressBar.cpp => src/server/shared/ProgressBar.cpp rename : src/shared/ProgressBar.h => src/server/shared/ProgressBar.h rename : src/shared/ServiceWin32.cpp => src/server/shared/ServiceWin32.cpp rename : src/shared/ServiceWin32.h => src/server/shared/ServiceWin32.h rename : src/shared/SignalHandler.h => src/server/shared/SignalHandler.h rename : src/shared/SystemConfig.h => src/server/shared/SystemConfig.h rename : src/shared/Threading.cpp => src/server/shared/Threading.cpp rename : src/shared/Threading.h => src/server/shared/Threading.h rename : src/shared/Timer.h => src/server/shared/Timer.h rename : src/shared/Util.cpp => src/server/shared/Util.cpp rename : src/shared/Util.h => src/server/shared/Util.h rename : src/shared/WheatyExceptionReport.cpp => src/server/shared/WheatyExceptionReport.cpp rename : src/shared/WheatyExceptionReport.h => src/server/shared/WheatyExceptionReport.h rename : src/shared/WorldPacket.h => src/server/shared/WorldPacket.h rename : src/shared/vmap/BIH.cpp => src/server/shared/vmap/BIH.cpp rename : src/shared/vmap/BIH.h => src/server/shared/vmap/BIH.h rename : src/shared/vmap/CMakeLists.txt => src/server/shared/vmap/CMakeLists.txt rename : src/shared/vmap/IVMapManager.h => src/server/shared/vmap/IVMapManager.h rename : src/shared/vmap/MapTree.cpp => src/server/shared/vmap/MapTree.cpp rename : src/shared/vmap/MapTree.h => src/server/shared/vmap/MapTree.h rename : src/shared/vmap/ModelInstance.cpp => src/server/shared/vmap/ModelInstance.cpp rename : src/shared/vmap/ModelInstance.h => src/server/shared/vmap/ModelInstance.h rename : src/shared/vmap/TileAssembler.cpp => src/server/shared/vmap/TileAssembler.cpp rename : src/shared/vmap/TileAssembler.h => src/server/shared/vmap/TileAssembler.h rename : src/shared/vmap/VMapDefinitions.h => src/server/shared/vmap/VMapDefinitions.h rename : src/shared/vmap/VMapFactory.cpp => src/server/shared/vmap/VMapFactory.cpp rename : src/shared/vmap/VMapFactory.h => src/server/shared/vmap/VMapFactory.h rename : src/shared/vmap/VMapManager2.cpp => src/server/shared/vmap/VMapManager2.cpp rename : src/shared/vmap/VMapManager2.h => src/server/shared/vmap/VMapManager2.h rename : src/shared/vmap/VMapTools.h => src/server/shared/vmap/VMapTools.h rename : src/shared/vmap/WorldModel.cpp => src/server/shared/vmap/WorldModel.cpp rename : src/shared/vmap/WorldModel.h => src/server/shared/vmap/WorldModel.h rename : src/trinitycore/CMakeLists.txt => src/server/trinitycore/CMakeLists.txt rename : src/trinitycore/CliRunnable.cpp => src/server/trinitycore/CliRunnable.cpp rename : src/trinitycore/CliRunnable.h => src/server/trinitycore/CliRunnable.h rename : src/trinitycore/Main.cpp => src/server/trinitycore/Main.cpp rename : src/trinitycore/Master.cpp => src/server/trinitycore/Master.cpp rename : src/trinitycore/Master.h => src/server/trinitycore/Master.h rename : src/trinitycore/RASocket.cpp => src/server/trinitycore/RASocket.cpp rename : src/trinitycore/RASocket.h => src/server/trinitycore/RASocket.h rename : src/trinitycore/TrinityCore.ico => src/server/trinitycore/TrinityCore.ico rename : src/trinitycore/TrinityCore.rc => src/server/trinitycore/TrinityCore.rc rename : src/trinitycore/WorldRunnable.cpp => src/server/trinitycore/WorldRunnable.cpp rename : src/trinitycore/WorldRunnable.h => src/server/trinitycore/WorldRunnable.h rename : src/trinitycore/resource.h => src/server/trinitycore/resource.h rename : src/trinitycore/trinitycore.conf.dist => src/server/trinitycore/trinitycore.conf.dist rename : src/trinityrealm/AuthCodes.cpp => src/server/trinityrealm/AuthCodes.cpp rename : src/trinityrealm/AuthCodes.h => src/server/trinityrealm/AuthCodes.h rename : src/trinityrealm/AuthSocket.cpp => src/server/trinityrealm/AuthSocket.cpp rename : src/trinityrealm/AuthSocket.h => src/server/trinityrealm/AuthSocket.h rename : src/trinityrealm/CMakeLists.txt => src/server/trinityrealm/CMakeLists.txt rename : src/trinityrealm/Main.cpp => src/server/trinityrealm/Main.cpp rename : src/trinityrealm/RealmAcceptor.h => src/server/trinityrealm/RealmAcceptor.h rename : src/trinityrealm/RealmList.cpp => src/server/trinityrealm/RealmList.cpp rename : src/trinityrealm/RealmList.h => src/server/trinityrealm/RealmList.h rename : src/trinityrealm/RealmSocket.cpp => src/server/trinityrealm/RealmSocket.cpp rename : src/trinityrealm/RealmSocket.h => src/server/trinityrealm/RealmSocket.h rename : src/trinityrealm/TrinityRealm.ico => src/server/trinityrealm/TrinityRealm.ico rename : src/trinityrealm/TrinityRealm.rc => src/server/trinityrealm/TrinityRealm.rc rename : src/trinityrealm/resource.h => src/server/trinityrealm/resource.h rename : src/trinityrealm/trinityrealm.conf.dist => src/server/trinityrealm/trinityrealm.conf.dist --- src/server/framework/CMakeLists.txt | 11 + src/server/framework/Dynamic/FactoryHolder.h | 63 + src/server/framework/Dynamic/ObjectRegistry.h | 111 + src/server/framework/GameSystem/Grid.h | 143 + src/server/framework/GameSystem/GridLoader.h | 79 + src/server/framework/GameSystem/GridRefManager.h | 44 + src/server/framework/GameSystem/GridReference.h | 55 + src/server/framework/GameSystem/NGrid.h | 189 + src/server/framework/GameSystem/TypeContainer.h | 126 + .../framework/GameSystem/TypeContainerFunctions.h | 116 + .../GameSystem/TypeContainerFunctionsPtr.h | 170 + .../framework/GameSystem/TypeContainerVisitor.h | 119 + src/server/framework/Network/SocketDefines.h | 49 + src/server/framework/Platform/CompilerDefs.h | 64 + src/server/framework/Platform/Define.h | 85 + src/server/framework/Policies/CreationPolicy.h | 110 + src/server/framework/Policies/ObjectLifeTime.cpp | 36 + src/server/framework/Policies/ObjectLifeTime.h | 53 + src/server/framework/Policies/Singleton.h | 65 + src/server/framework/Policies/SingletonImp.h | 93 + src/server/framework/Policies/ThreadingModel.h | 130 + src/server/framework/Utilities/ByteConverter.h | 66 + src/server/framework/Utilities/Callback.h | 386 + .../Utilities/CountedReference/Reference.h | 100 + .../Utilities/CountedReference/ReferenceHolder.h | 42 + .../Utilities/CountedReference/ReferenceImpl.h | 133 + src/server/framework/Utilities/EventProcessor.cpp | 101 + src/server/framework/Utilities/EventProcessor.h | 73 + src/server/framework/Utilities/LinkedList.h | 247 + .../Utilities/LinkedReference/RefManager.h | 58 + .../Utilities/LinkedReference/Reference.h | 94 + src/server/framework/Utilities/TypeList.h | 46 + src/server/framework/Utilities/UnorderedMap.h | 73 + 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/AddonHandler.cpp | 234 + src/server/game/AddonHandler.h | 41 + src/server/game/AddonMgr.cpp | 93 + src/server/game/AddonMgr.h | 89 + src/server/game/ArenaTeam.cpp | 764 + src/server/game/ArenaTeam.h | 224 + src/server/game/ArenaTeamHandler.cpp | 391 + 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/CMakeLists.txt | 358 + src/server/game/Calendar.cpp | 17 + src/server/game/Calendar.h | 26 + src/server/game/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/ChatHandler.cpp | 756 + 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/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/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/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/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/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/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/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/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/Map.cpp | 3936 +++ src/server/game/Map.h | 689 + 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/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/OutdoorPvP.cpp | 598 + src/server/game/OutdoorPvP.h | 260 + 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/QueryHandler.cpp | 539 + src/server/game/QuestDef.cpp | 206 + src/server/game/QuestDef.h | 372 + src/server/game/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/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/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/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/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/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/World.cpp | 2733 +++ src/server/game/World.h | 784 + 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 + src/server/scripts/CMakeLists.txt | 598 + src/server/scripts/custom/custom_example.cpp | 278 + .../scripts/custom/custom_gossip_codebox.cpp | 89 + src/server/scripts/custom/npc_acherus_taxi.cpp | 35 + .../scripts/custom/npc_wyrmresttempel_taxi.cpp | 112 + src/server/scripts/custom/on_events.cpp | 108 + src/server/scripts/custom/test.cpp | 218 + .../scripts/eastern_kingdoms/alterac_mountains.cpp | 32 + .../alterac_valley/alterac_valley.cpp | 180 + .../alterac_valley/boss_balinda.cpp | 203 + .../alterac_valley/boss_drekthar.cpp | 138 + .../alterac_valley/boss_galvangar.cpp | 129 + .../alterac_valley/boss_vanndar.cpp | 129 + .../scripts/eastern_kingdoms/arathi_highlands.cpp | 132 + .../blackrock_depths/blackrock_depths.cpp | 1338 ++ .../blackrock_depths/blackrock_depths.h | 45 + .../blackrock_depths/boss_ambassador_flamelash.cpp | 91 + .../blackrock_depths/boss_anubshiah.cpp | 116 + .../boss_emperor_dagran_thaurissan.cpp | 111 + .../blackrock_depths/boss_general_angerforge.cpp | 132 + .../blackrock_depths/boss_gorosh_the_dervish.cpp | 84 + .../blackrock_depths/boss_grizzle.cpp | 94 + .../boss_high_interrogator_gerstahn.cpp | 106 + .../blackrock_depths/boss_magmus.cpp | 98 + .../blackrock_depths/boss_moira_bronzebeard.cpp | 98 + .../blackrock_depths/boss_tomb_of_seven.cpp | 254 + .../blackrock_depths/instance_blackrock_depths.cpp | 460 + .../blackrock_spire/blackrock_spire.cpp | 77 + .../blackrock_spire/blackrock_spire.h | 26 + .../blackrock_spire/boss_drakkisath.cpp | 102 + .../eastern_kingdoms/blackrock_spire/boss_gyth.cpp | 205 + .../blackrock_spire/boss_halycon.cpp | 96 + .../blackrock_spire/boss_highlord_omokk.cpp | 132 + .../blackrock_spire/boss_mother_smolderweb.cpp | 87 + .../blackrock_spire/boss_overlord_wyrmthalak.cpp | 126 + .../blackrock_spire/boss_pyroguard_emberseer.cpp | 93 + .../blackrock_spire/boss_quartermaster_zigris.cpp | 86 + .../blackrock_spire/boss_rend_blackhand.cpp | 92 + .../boss_shadow_hunter_voshgajin.cpp | 93 + .../blackrock_spire/boss_the_beast.cpp | 93 + .../blackrock_spire/boss_warmaster_voone.cpp | 122 + .../blackrock_spire/instance_blackrock_spire.cpp | 104 + .../blackwing_lair/boss_broodlord_lashlayer.cpp | 112 + .../blackwing_lair/boss_chromaggus.cpp | 301 + .../blackwing_lair/boss_ebonroc.cpp | 104 + .../blackwing_lair/boss_firemaw.cpp | 95 + .../blackwing_lair/boss_flamegor.cpp | 98 + .../blackwing_lair/boss_nefarian.cpp | 231 + .../blackwing_lair/boss_razorgore.cpp | 125 + .../blackwing_lair/boss_vaelastrasz.cpp | 262 + .../blackwing_lair/boss_victor_nefarius.cpp | 385 + .../blackwing_lair/instance_blackwing_lair.cpp | 25 + .../scripts/eastern_kingdoms/blasted_lands.cpp | 161 + src/server/scripts/eastern_kingdoms/boss_kruul.cpp | 154 + .../scripts/eastern_kingdoms/burning_steppes.cpp | 153 + .../eastern_kingdoms/deadmines/boss_mr_smite.cpp | 182 + .../eastern_kingdoms/deadmines/deadmines.cpp | 60 + .../scripts/eastern_kingdoms/deadmines/deadmines.h | 35 + .../deadmines/instance_deadmines.cpp | 256 + src/server/scripts/eastern_kingdoms/dun_morogh.cpp | 102 + src/server/scripts/eastern_kingdoms/duskwood.cpp | 126 + .../eastern_kingdoms/eastern_plaguelands.cpp | 185 + .../scripts/eastern_kingdoms/elwynn_forest.cpp | 101 + .../scripts/eastern_kingdoms/eversong_woods.cpp | 711 + src/server/scripts/eastern_kingdoms/ghostlands.cpp | 250 + .../eastern_kingdoms/gnomeregan/gnomeregan.cpp | 593 + .../eastern_kingdoms/gnomeregan/gnomeregan.h | 48 + .../gnomeregan/instance_gnomeregan.cpp | 141 + .../scripts/eastern_kingdoms/hinterlands.cpp | 350 + src/server/scripts/eastern_kingdoms/ironforge.cpp | 93 + .../scripts/eastern_kingdoms/isle_of_queldanas.cpp | 151 + .../eastern_kingdoms/karazhan/boss_curator.cpp | 196 + .../karazhan/boss_maiden_of_virtue.cpp | 138 + .../eastern_kingdoms/karazhan/boss_midnight.cpp | 310 + .../eastern_kingdoms/karazhan/boss_moroes.cpp | 785 + .../eastern_kingdoms/karazhan/boss_netherspite.cpp | 343 + .../eastern_kingdoms/karazhan/boss_nightbane.cpp | 418 + .../karazhan/boss_prince_malchezaar.cpp | 604 + .../karazhan/boss_shade_of_aran.cpp | 569 + .../karazhan/boss_terestian_illhoof.cpp | 417 + .../eastern_kingdoms/karazhan/bosses_opera.cpp | 1494 ++ .../karazhan/instance_karazhan.cpp | 308 + .../scripts/eastern_kingdoms/karazhan/karazhan.cpp | 646 + .../scripts/eastern_kingdoms/karazhan/karazhan.h | 53 + src/server/scripts/eastern_kingdoms/loch_modan.cpp | 100 + .../magisters_terrace/boss_felblood_kaelthas.cpp | 680 + .../magisters_terrace/boss_priestess_delrissa.cpp | 1321 + .../magisters_terrace/boss_selin_fireheart.cpp | 366 + .../magisters_terrace/boss_vexallus.cpp | 226 + .../instance_magisters_terrace.cpp | 205 + .../magisters_terrace/magisters_terrace.cpp | 174 + .../magisters_terrace/magisters_terrace.h | 31 + .../molten_core/boss_baron_geddon.cpp | 107 + .../eastern_kingdoms/molten_core/boss_garr.cpp | 143 + .../eastern_kingdoms/molten_core/boss_gehennas.cpp | 93 + .../eastern_kingdoms/molten_core/boss_golemagg.cpp | 198 + .../eastern_kingdoms/molten_core/boss_lucifron.cpp | 91 + .../eastern_kingdoms/molten_core/boss_magmadar.cpp | 100 + .../molten_core/boss_majordomo_executus.cpp | 134 + .../eastern_kingdoms/molten_core/boss_ragnaros.cpp | 297 + .../eastern_kingdoms/molten_core/boss_shazzrah.cpp | 121 + .../molten_core/boss_sulfuron_harbinger.cpp | 205 + .../molten_core/instance_molten_core.cpp | 266 + .../eastern_kingdoms/molten_core/molten_core.cpp | 81 + .../eastern_kingdoms/molten_core/molten_core.h | 22 + .../eastern_kingdoms/redridge_mountains.cpp | 172 + .../eastern_kingdoms/scarlet_enclave/chapter1.cpp | 1079 + .../eastern_kingdoms/scarlet_enclave/chapter2.cpp | 995 + .../eastern_kingdoms/scarlet_enclave/chapter5.cpp | 1684 ++ .../scarlet_enclave/the_scarlet_enclave.cpp | 119 + .../scarlet_monastery/boss_arcanist_doan.cpp | 128 + .../boss_azshir_the_sleepless.cpp | 98 + .../scarlet_monastery/boss_bloodmage_thalnos.cpp | 124 + .../scarlet_monastery/boss_headless_horseman.cpp | 892 + .../scarlet_monastery/boss_herod.cpp | 158 + .../boss_high_inquisitor_fairbanks.cpp | 133 + .../scarlet_monastery/boss_houndmaster_loksey.cpp | 77 + .../scarlet_monastery/boss_interrogator_vishas.cpp | 118 + .../boss_mograine_and_whitemane.cpp | 359 + .../scarlet_monastery/boss_scorn.cpp | 101 + .../instance_scarlet_monastery.cpp | 157 + .../scarlet_monastery/scarlet_monastery.h | 18 + .../scholomance/boss_darkmaster_gandling.cpp | 225 + .../scholomance/boss_death_knight_darkreaver.cpp | 58 + .../scholomance/boss_doctor_theolen_krastinov.cpp | 117 + .../scholomance/boss_illucia_barov.cpp | 117 + .../scholomance/boss_instructor_malicia.cpp | 153 + .../scholomance/boss_jandice_barov.cpp | 201 + .../eastern_kingdoms/scholomance/boss_kormok.cpp | 120 + .../scholomance/boss_lord_alexei_barov.cpp | 99 + .../scholomance/boss_lorekeeper_polkelt.cpp | 114 + .../scholomance/boss_ras_frostwhisper.cpp | 123 + .../scholomance/boss_the_ravenian.cpp | 117 + .../eastern_kingdoms/scholomance/boss_vectus.cpp | 105 + .../scholomance/instance_scholomance.cpp | 146 + .../eastern_kingdoms/scholomance/scholomance.h | 17 + .../scripts/eastern_kingdoms/searing_gorge.cpp | 169 + .../shadowfang_keep/instance_shadowfang_keep.cpp | 277 + .../shadowfang_keep/shadowfang_keep.cpp | 198 + .../shadowfang_keep/shadowfang_keep.h | 17 + .../scripts/eastern_kingdoms/silvermoon_city.cpp | 102 + .../scripts/eastern_kingdoms/silverpine_forest.cpp | 385 + .../scripts/eastern_kingdoms/stormwind_city.cpp | 696 + .../eastern_kingdoms/stranglethorn_vale.cpp | 122 + .../stratholme/boss_baron_rivendare.cpp | 190 + .../stratholme/boss_baroness_anastari.cpp | 127 + .../stratholme/boss_cannon_master_willey.cpp | 220 + .../stratholme/boss_dathrohan_balnazzar.cpp | 217 + .../stratholme/boss_magistrate_barthilas.cpp | 128 + .../stratholme/boss_maleki_the_pallid.cpp | 108 + .../stratholme/boss_nerubenkan.cpp | 121 + .../stratholme/boss_order_of_silver_hand.cpp | 157 + .../stratholme/boss_postmaster_malown.cpp | 144 + .../stratholme/boss_ramstein_the_gorger.cpp | 102 + .../stratholme/boss_timmy_the_cruel.cpp | 83 + .../stratholme/instance_stratholme.cpp | 458 + .../eastern_kingdoms/stratholme/stratholme.cpp | 282 + .../eastern_kingdoms/stratholme/stratholme.h | 28 + .../sunken_temple/instance_sunken_temple.cpp | 204 + .../sunken_temple/sunken_temple.cpp | 71 + .../eastern_kingdoms/sunken_temple/sunken_temple.h | 22 + .../sunwell_plateau/boss_brutallus.cpp | 349 + .../sunwell_plateau/boss_eredar_twins.cpp | 762 + .../sunwell_plateau/boss_felmyst.cpp | 579 + .../sunwell_plateau/boss_kalecgos.cpp | 794 + .../sunwell_plateau/boss_kiljaeden.cpp | 1408 ++ .../eastern_kingdoms/sunwell_plateau/boss_muru.cpp | 640 + .../sunwell_plateau/instance_sunwell_plateau.cpp | 297 + .../sunwell_plateau/sunwell_plateau.cpp | 65 + .../sunwell_plateau/sunwell_plateau.h | 95 + .../scripts/eastern_kingdoms/tirisfal_glades.cpp | 208 + .../eastern_kingdoms/uldaman/boss_archaedas.cpp | 497 + .../eastern_kingdoms/uldaman/boss_ironaya.cpp | 106 + .../eastern_kingdoms/uldaman/instance_uldaman.cpp | 300 + .../scripts/eastern_kingdoms/uldaman/uldaman.cpp | 205 + src/server/scripts/eastern_kingdoms/undercity.cpp | 252 + .../eastern_kingdoms/western_plaguelands.cpp | 396 + src/server/scripts/eastern_kingdoms/westfall.cpp | 263 + src/server/scripts/eastern_kingdoms/wetlands.cpp | 168 + .../eastern_kingdoms/zulaman/boss_akilzon.cpp | 467 + .../eastern_kingdoms/zulaman/boss_halazzi.cpp | 401 + .../eastern_kingdoms/zulaman/boss_hexlord.cpp | 907 + .../eastern_kingdoms/zulaman/boss_janalai.cpp | 687 + .../eastern_kingdoms/zulaman/boss_nalorakk.cpp | 451 + .../eastern_kingdoms/zulaman/boss_zuljin.cpp | 623 + .../eastern_kingdoms/zulaman/instance_zulaman.cpp | 320 + .../scripts/eastern_kingdoms/zulaman/zulaman.cpp | 192 + .../scripts/eastern_kingdoms/zulaman/zulaman.h | 19 + .../eastern_kingdoms/zulgurub/boss_arlokk.cpp | 278 + .../eastern_kingdoms/zulgurub/boss_gahzranka.cpp | 93 + .../eastern_kingdoms/zulgurub/boss_grilek.cpp | 93 + .../eastern_kingdoms/zulgurub/boss_hakkar.cpp | 250 + .../eastern_kingdoms/zulgurub/boss_hazzarah.cpp | 104 + .../eastern_kingdoms/zulgurub/boss_jeklik.cpp | 291 + .../eastern_kingdoms/zulgurub/boss_jindo.cpp | 263 + .../eastern_kingdoms/zulgurub/boss_mandokir.cpp | 333 + .../eastern_kingdoms/zulgurub/boss_marli.cpp | 261 + .../eastern_kingdoms/zulgurub/boss_renataki.cpp | 152 + .../eastern_kingdoms/zulgurub/boss_thekal.cpp | 556 + .../eastern_kingdoms/zulgurub/boss_venoxis.cpp | 199 + .../eastern_kingdoms/zulgurub/boss_wushoolay.cpp | 85 + .../zulgurub/instance_zulgurub.cpp | 159 + .../scripts/eastern_kingdoms/zulgurub/zulgurub.h | 28 + src/server/scripts/examples/example_creature.cpp | 292 + src/server/scripts/examples/example_escort.cpp | 229 + .../scripts/examples/example_gossip_codebox.cpp | 99 + src/server/scripts/examples/example_misc.cpp | 69 + src/server/scripts/kalimdor/ashenvale.cpp | 465 + src/server/scripts/kalimdor/azshara.cpp | 514 + src/server/scripts/kalimdor/azuremyst_isle.cpp | 718 + .../blackfathom_depths/blackfathom_deeps.cpp | 252 + .../blackfathom_depths/blackfathom_deeps.h | 53 + .../kalimdor/blackfathom_depths/boss_aku_mai.cpp | 94 + .../kalimdor/blackfathom_depths/boss_gelihast.cpp | 85 + .../kalimdor/blackfathom_depths/boss_kelris.cpp | 108 + .../instance_blackfathom_deeps.cpp | 259 + src/server/scripts/kalimdor/bloodmyst_isle.cpp | 149 + src/server/scripts/kalimdor/boss_azuregos.cpp | 155 + .../culling_of_stratholme/boss_epoch.cpp | 156 + .../culling_of_stratholme/boss_infinite.cpp | 88 + .../culling_of_stratholme/boss_mal_ganis.cpp | 262 + .../culling_of_stratholme/boss_meathook.cpp | 140 + .../culling_of_stratholme/boss_salramm.cpp | 170 + .../culling_of_stratholme.cpp | 1210 + .../culling_of_stratholme/culling_of_stratholme.h | 68 + .../instance_culling_of_stratholme.cpp | 244 + .../caverns_of_time/dark_portal/boss_aeonus.cpp | 145 + .../dark_portal/boss_chrono_lord_deja.cpp | 154 + .../caverns_of_time/dark_portal/boss_temporus.cpp | 152 + .../caverns_of_time/dark_portal/dark_portal.cpp | 404 + .../caverns_of_time/dark_portal/dark_portal.h | 35 + .../dark_portal/instance_dark_portal.cpp | 346 + .../caverns_of_time/hyjal/boss_anetheron.cpp | 309 + .../caverns_of_time/hyjal/boss_archimonde.cpp | 635 + .../caverns_of_time/hyjal/boss_azgalor.cpp | 285 + .../caverns_of_time/hyjal/boss_kazrogal.cpp | 197 + .../hyjal/boss_rage_winterchill.cpp | 192 + .../kalimdor/caverns_of_time/hyjal/hyjal.cpp | 247 + .../scripts/kalimdor/caverns_of_time/hyjal/hyjal.h | 38 + .../kalimdor/caverns_of_time/hyjal/hyjalAI.cpp | 1157 + .../kalimdor/caverns_of_time/hyjal/hyjalAI.h | 248 + .../kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp | 1440 ++ .../kalimdor/caverns_of_time/hyjal/hyjal_trash.h | 35 + .../caverns_of_time/hyjal/instance_hyjal.cpp | 324 + .../old_hillsbrad/boss_captain_skarloc.cpp | 152 + .../old_hillsbrad/boss_epoch_hunter.cpp | 138 + .../old_hillsbrad/boss_leutenant_drake.cpp | 190 + .../old_hillsbrad/instance_old_hillsbrad.cpp | 238 + .../old_hillsbrad/old_hillsbrad.cpp | 658 + .../caverns_of_time/old_hillsbrad/old_hillsbrad.h | 19 + src/server/scripts/kalimdor/darkshore.cpp | 398 + src/server/scripts/kalimdor/desolace.cpp | 259 + src/server/scripts/kalimdor/durotar.cpp | 103 + src/server/scripts/kalimdor/dustwallow_marsh.cpp | 425 + src/server/scripts/kalimdor/felwood.cpp | 90 + src/server/scripts/kalimdor/feralas.cpp | 206 + .../kalimdor/maraudon/boss_celebras_the_cursed.cpp | 98 + .../scripts/kalimdor/maraudon/boss_landslide.cpp | 95 + .../scripts/kalimdor/maraudon/boss_noxxion.cpp | 131 + .../kalimdor/maraudon/boss_princess_theradras.cpp | 109 + src/server/scripts/kalimdor/moonglade.cpp | 571 + src/server/scripts/kalimdor/mulgore.cpp | 316 + .../scripts/kalimdor/onyxias_lair/boss_onyxia.cpp | 500 + .../onyxias_lair/instance_onyxias_lair.cpp | 249 + .../scripts/kalimdor/onyxias_lair/onyxias_lair.h | 65 + src/server/scripts/kalimdor/orgrimmar.cpp | 286 + .../boss_amnennar_the_coldbringer.cpp | 130 + .../razorfen_downs/instance_razorfen_downs.cpp | 215 + .../kalimdor/razorfen_downs/razorfen_downs.cpp | 165 + .../kalimdor/razorfen_downs/razorfen_downs.h | 45 + .../razorfen_kraul/instance_razorfen_kraul.cpp | 104 + .../kalimdor/razorfen_kraul/razorfen_kraul.cpp | 194 + .../kalimdor/razorfen_kraul/razorfen_kraul.h | 21 + .../kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp | 125 + .../kalimdor/ruins_of_ahnqiraj/boss_buru.cpp | 72 + .../kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp | 143 + .../kalimdor/ruins_of_ahnqiraj/boss_moam.cpp | 162 + .../kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp | 79 + .../kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp | 85 + .../instance_ruins_of_ahnqiraj.cpp | 213 + .../kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h | 34 + src/server/scripts/kalimdor/silithus.cpp | 1140 + .../scripts/kalimdor/stonetalon_mountains.cpp | 172 + src/server/scripts/kalimdor/tanaris.cpp | 635 + src/server/scripts/kalimdor/teldrassil.cpp | 114 + .../kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp | 346 + .../kalimdor/temple_of_ahnqiraj/boss_cthun.cpp | 1307 + .../kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp | 206 + .../kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp | 148 + .../kalimdor/temple_of_ahnqiraj/boss_ouro.cpp | 140 + .../kalimdor/temple_of_ahnqiraj/boss_sartura.cpp | 297 + .../kalimdor/temple_of_ahnqiraj/boss_skeram.cpp | 298 + .../temple_of_ahnqiraj/boss_twinemperors.cpp | 596 + .../kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp | 30 + .../instance_temple_of_ahnqiraj.cpp | 166 + .../temple_of_ahnqiraj/mob_anubisath_sentinel.cpp | 296 + .../temple_of_ahnqiraj/temple_of_ahnqiraj.h | 23 + src/server/scripts/kalimdor/the_barrens.cpp | 701 + src/server/scripts/kalimdor/thousand_needles.cpp | 454 + src/server/scripts/kalimdor/thunder_bluff.cpp | 138 + src/server/scripts/kalimdor/ungoro_crater.cpp | 356 + .../wailing_caverns/instance_wailing_caverns.cpp | 142 + .../kalimdor/wailing_caverns/wailing_caverns.cpp | 396 + .../kalimdor/wailing_caverns/wailing_caverns.h | 24 + src/server/scripts/kalimdor/winterspring.cpp | 168 + .../kalimdor/zulfarrak/instance_zulfarrak.cpp | 58 + .../scripts/kalimdor/zulfarrak/zulfarrak.cpp | 280 + .../northrend/azjol_nerub/ahnkahet/ahnkahet.h | 52 + .../azjol_nerub/ahnkahet/boss_amanitar.cpp | 226 + .../azjol_nerub/ahnkahet/boss_elder_nadox.cpp | 283 + .../azjol_nerub/ahnkahet/boss_herald_volazj.cpp | 328 + .../ahnkahet/boss_jedoga_shadowseeker.cpp | 588 + .../azjol_nerub/ahnkahet/boss_prince_taldaram.cpp | 412 + .../azjol_nerub/ahnkahet/instance_ahnkahet.cpp | 313 + .../azjol_nerub/azjol_nerub/azjol_nerub.h | 38 + .../azjol_nerub/azjol_nerub/boss_anubarak.cpp | 360 + .../azjol_nerub/azjol_nerub/boss_hadronox.cpp | 206 + .../azjol_nerub/boss_krikthir_the_gatewatcher.cpp | 553 + .../azjol_nerub/instance_azjol_nerub.cpp | 215 + src/server/scripts/northrend/borean_tundra.cpp | 2323 ++ .../boss_argent_challenge.cpp | 512 + .../trial_of_the_champion/boss_black_knight.cpp | 373 + .../trial_of_the_champion/boss_grand_champions.cpp | 993 + .../instance_trial_of_the_champion.cpp | 340 + .../trial_of_the_champion.cpp | 512 + .../trial_of_the_champion/trial_of_the_champion.h | 115 + .../scripts/northrend/crystalsong_forest.cpp | 110 + src/server/scripts/northrend/dalaran.cpp | 159 + src/server/scripts/northrend/dragonblight.cpp | 272 + .../northrend/draktharon_keep/boss_dred.cpp | 270 + .../northrend/draktharon_keep/boss_novos.cpp | 328 + .../northrend/draktharon_keep/boss_tharon_ja.cpp | 260 + .../northrend/draktharon_keep/boss_trollgore.cpp | 197 + .../northrend/draktharon_keep/drak_tharon_keep.h | 40 + .../draktharon_keep/instance_drak_tharon_keep.cpp | 243 + .../frozen_halls/forge_of_souls/boss_bronjahm.cpp | 263 + .../forge_of_souls/boss_devourer_of_souls.cpp | 349 + .../frozen_halls/forge_of_souls/forge_of_souls.cpp | 899 + .../frozen_halls/forge_of_souls/forge_of_souls.h | 52 + .../forge_of_souls/instance_forge_of_souls.cpp | 168 + .../halls_of_reflection/boss_falric.cpp | 142 + .../halls_of_reflection/boss_marwyn.cpp | 133 + .../halls_of_reflection/halls_of_reflection.cpp | 1022 + .../halls_of_reflection/halls_of_reflection.h | 156 + .../instance_halls_of_reflection.cpp | 431 + .../pit_of_saron/boss_forgemaster_garfrost.cpp | 209 + .../frozen_halls/pit_of_saron/boss_krickandick.cpp | 482 + .../pit_of_saron/boss_scourgelord_tyrannus.cpp | 273 + .../pit_of_saron/instance_pit_of_saron.cpp | 236 + .../frozen_halls/pit_of_saron/pit_of_saron.cpp | 1101 + .../frozen_halls/pit_of_saron/pit_of_saron.h | 63 + src/server/scripts/northrend/grizzly_hills.cpp | 625 + .../northrend/gundrak/boss_drakkari_colossus.cpp | 317 + src/server/scripts/northrend/gundrak/boss_eck.cpp | 168 + .../scripts/northrend/gundrak/boss_gal_darah.cpp | 288 + .../scripts/northrend/gundrak/boss_moorabi.cpp | 175 + .../scripts/northrend/gundrak/boss_slad_ran.cpp | 266 + src/server/scripts/northrend/gundrak/gundrak.h | 54 + .../scripts/northrend/gundrak/instance_gundrak.cpp | 536 + src/server/scripts/northrend/howling_fjord.cpp | 338 + src/server/scripts/northrend/icecrown.cpp | 422 + .../northrend/naxxramas/boss_anubrekhan.cpp | 185 + .../scripts/northrend/naxxramas/boss_faerlina.cpp | 218 + .../northrend/naxxramas/boss_four_horsemen.cpp | 397 + .../scripts/northrend/naxxramas/boss_gluth.cpp | 147 + .../scripts/northrend/naxxramas/boss_gothik.cpp | 582 + .../scripts/northrend/naxxramas/boss_grobbulus.cpp | 142 + .../scripts/northrend/naxxramas/boss_heigan.cpp | 149 + .../northrend/naxxramas/boss_highlord_mograine.cpp | 179 + .../scripts/northrend/naxxramas/boss_kelthuzad.cpp | 706 + .../scripts/northrend/naxxramas/boss_loatheb.cpp | 125 + .../scripts/northrend/naxxramas/boss_maexxna.cpp | 186 + .../scripts/northrend/naxxramas/boss_noth.cpp | 212 + .../scripts/northrend/naxxramas/boss_patchwerk.cpp | 159 + .../scripts/northrend/naxxramas/boss_razuvious.cpp | 139 + .../scripts/northrend/naxxramas/boss_sapphiron.cpp | 403 + .../scripts/northrend/naxxramas/boss_thaddius.cpp | 400 + .../northrend/naxxramas/instance_naxxramas.cpp | 324 + src/server/scripts/northrend/naxxramas/naxxramas.h | 75 + .../nexus/eye_of_eternity/boss_malygos.cpp | 175 + .../nexus/eye_of_eternity/eye_of_eternity.h | 4 + .../eye_of_eternity/instance_eye_of_eternity.cpp | 21 + .../northrend/nexus/nexus/boss_anomalus.cpp | 253 + .../northrend/nexus/nexus/boss_keristrasza.cpp | 247 + .../northrend/nexus/nexus/boss_magus_telestra.cpp | 327 + .../scripts/northrend/nexus/nexus/boss_ormorok.cpp | 298 + .../northrend/nexus/nexus/commander_kolurg.cpp | 58 + .../northrend/nexus/nexus/commander_stoutbeard.cpp | 64 + .../northrend/nexus/nexus/instance_nexus.cpp | 259 + src/server/scripts/northrend/nexus/nexus/nexus.h | 35 + .../scripts/northrend/nexus/oculus/boss_drakos.cpp | 218 + .../scripts/northrend/nexus/oculus/boss_eregos.cpp | 129 + .../scripts/northrend/nexus/oculus/boss_urom.cpp | 356 + .../scripts/northrend/nexus/oculus/boss_varos.cpp | 105 + .../northrend/nexus/oculus/instance_oculus.cpp | 205 + .../scripts/northrend/nexus/oculus/oculus.cpp | 174 + src/server/scripts/northrend/nexus/oculus/oculus.h | 34 + .../northrend/obsidian_sanctum/boss_sartharion.cpp | 1421 ++ .../obsidian_sanctum/instance_obsidian_sanctum.cpp | 97 + .../northrend/obsidian_sanctum/obsidian_sanctum.h | 20 + src/server/scripts/northrend/sholazar_basin.cpp | 449 + src/server/scripts/northrend/storm_peaks.cpp | 491 + .../ulduar/halls_of_lightning/boss_bjarngrim.cpp | 432 + .../ulduar/halls_of_lightning/boss_ionar.cpp | 388 + .../ulduar/halls_of_lightning/boss_loken.cpp | 222 + .../ulduar/halls_of_lightning/boss_volkhan.cpp | 478 + .../ulduar/halls_of_lightning/halls_of_lightning.h | 34 + .../instance_halls_of_lightning.cpp | 248 + .../ulduar/halls_of_stone/boss_krystallus.cpp | 148 + .../ulduar/halls_of_stone/boss_maiden_of_grief.cpp | 166 + .../ulduar/halls_of_stone/boss_sjonnir.cpp | 300 + .../ulduar/halls_of_stone/halls_of_stone.cpp | 724 + .../ulduar/halls_of_stone/halls_of_stone.h | 48 + .../halls_of_stone/instance_halls_of_stone.cpp | 254 + .../northrend/ulduar/ulduar/boss_algalon.cpp | 384 + .../ulduar/ulduar/boss_assembly_of_iron.cpp | 589 + .../northrend/ulduar/ulduar/boss_auriaya.cpp | 106 + .../ulduar/ulduar/boss_flame_leviathan.cpp | 574 + .../scripts/northrend/ulduar/ulduar/boss_freya.cpp | 125 + .../northrend/ulduar/ulduar/boss_general_vezax.cpp | 31 + .../scripts/northrend/ulduar/ulduar/boss_hodir.cpp | 89 + .../scripts/northrend/ulduar/ulduar/boss_ignis.cpp | 137 + .../northrend/ulduar/ulduar/boss_kologarn.cpp | 166 + .../northrend/ulduar/ulduar/boss_mimiron.cpp | 44 + .../northrend/ulduar/ulduar/boss_razorscale.cpp | 331 + .../northrend/ulduar/ulduar/boss_thorim.cpp | 103 + .../scripts/northrend/ulduar/ulduar/boss_xt002.cpp | 850 + .../northrend/ulduar/ulduar/boss_yoggsaron.cpp | 55 + .../northrend/ulduar/ulduar/instance_ulduar.cpp | 408 + .../scripts/northrend/ulduar/ulduar/ulduar.h | 71 + .../northrend/ulduar/ulduar/ulduar_teleporter.cpp | 92 + .../utgarde_keep/boss_ingvar_the_plunderer.cpp | 442 + .../utgarde_keep/utgarde_keep/boss_keleseth.cpp | 361 + .../utgarde_keep/boss_skarvald_dalronn.cpp | 389 + .../utgarde_keep/instance_utgarde_keep.cpp | 312 + .../utgarde_keep/utgarde_keep/utgarde_keep.cpp | 160 + .../utgarde_keep/utgarde_keep/utgarde_keep.h | 35 + .../utgarde_pinnacle/boss_palehoof.cpp | 830 + .../utgarde_keep/utgarde_pinnacle/boss_skadi.cpp | 477 + .../utgarde_keep/utgarde_pinnacle/boss_svala.cpp | 408 + .../utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp | 367 + .../utgarde_pinnacle/instance_pinnacle.cpp | 241 + .../utgarde_pinnacle/utgarde_pinnacle.h | 42 + .../northrend/vault_of_archavon/boss_archavon.cpp | 218 + .../northrend/vault_of_archavon/boss_emalon.cpp | 270 + .../northrend/vault_of_archavon/boss_koralon.cpp | 222 + .../northrend/vault_of_archavon/boss_toravon.cpp | 301 + .../instance_vault_of_archavon.cpp | 143 + .../vault_of_archavon/vault_of_archavon.h | 28 + .../northrend/violet_hold/boss_cyanigosa.cpp | 156 + .../scripts/northrend/violet_hold/boss_erekem.cpp | 329 + .../scripts/northrend/violet_hold/boss_ichoron.cpp | 394 + .../northrend/violet_hold/boss_lavanthor.cpp | 155 + .../scripts/northrend/violet_hold/boss_moragg.cpp | 133 + .../scripts/northrend/violet_hold/boss_xevozz.cpp | 307 + .../scripts/northrend/violet_hold/boss_zuramat.cpp | 178 + .../northrend/violet_hold/instance_violet_hold.cpp | 528 + .../scripts/northrend/violet_hold/violet_hold.cpp | 274 + .../scripts/northrend/violet_hold/violet_hold.h | 71 + src/server/scripts/northrend/zuldrak.cpp | 1403 ++ .../auchenai_crypts/boss_exarch_maladaar.cpp | 348 + .../boss_shirrak_the_dead_watcher.cpp | 211 + .../mana_tombs/boss_nexusprince_shaffar.cpp | 358 + .../auchindoun/mana_tombs/boss_pandemonius.cpp | 126 + .../sethekk_halls/boss_darkweaver_syth.cpp | 414 + .../sethekk_halls/boss_tailonking_ikiss.cpp | 213 + .../sethekk_halls/instance_sethekk_halls.cpp | 91 + .../auchindoun/sethekk_halls/sethekk_halls.h | 14 + .../shadow_labyrinth/boss_ambassador_hellmaw.cpp | 206 + .../boss_blackheart_the_inciter.cpp | 169 + .../shadow_labyrinth/boss_grandmaster_vorpil.cpp | 313 + .../auchindoun/shadow_labyrinth/boss_murmur.cpp | 205 + .../shadow_labyrinth/instance_shadow_labyrinth.cpp | 227 + .../auchindoun/shadow_labyrinth/shadow_labyrinth.h | 15 + .../scripts/outland/black_temple/black_temple.cpp | 69 + .../scripts/outland/black_temple/black_temple.h | 39 + .../outland/black_temple/boss_bloodboil.cpp | 332 + .../scripts/outland/black_temple/boss_illidan.cpp | 2248 ++ .../outland/black_temple/boss_mother_shahraz.cpp | 302 + .../black_temple/boss_reliquary_of_souls.cpp | 690 + .../outland/black_temple/boss_shade_of_akama.cpp | 871 + .../scripts/outland/black_temple/boss_supremus.cpp | 296 + .../outland/black_temple/boss_teron_gorefiend.cpp | 508 + .../outland/black_temple/boss_warlord_najentus.cpp | 226 + .../outland/black_temple/illidari_council.cpp | 874 + .../outland/black_temple/instance_black_temple.cpp | 346 + .../scripts/outland/blades_edge_mountains.cpp | 525 + .../scripts/outland/boss_doomlord_kazzak.cpp | 174 + src/server/scripts/outland/boss_doomwalker.cpp | 178 + .../serpent_shrine/boss_fathomlord_karathress.cpp | 746 + .../serpent_shrine/boss_hydross_the_unstable.cpp | 379 + .../serpent_shrine/boss_lady_vashj.cpp | 1039 + .../serpent_shrine/boss_leotheras_the_blind.cpp | 787 + .../serpent_shrine/boss_lurker_below.cpp | 458 + .../serpent_shrine/boss_morogrim_tidewalker.cpp | 369 + .../serpent_shrine/instance_serpent_shrine.cpp | 398 + .../serpent_shrine/serpent_shrine.h | 45 + .../steam_vault/boss_hydromancer_thespia.cpp | 187 + .../steam_vault/boss_mekgineer_steamrigger.cpp | 264 + .../steam_vault/boss_warlord_kalithresh.cpp | 204 + .../steam_vault/instance_steam_vault.cpp | 231 + .../coilfang_resevoir/steam_vault/steam_vault.h | 17 + .../coilfang_resevoir/underbog/boss_hungarfen.cpp | 154 + .../underbog/boss_the_black_stalker.cpp | 185 + .../scripts/outland/gruuls_lair/boss_gruul.cpp | 255 + .../outland/gruuls_lair/boss_high_king_maulgar.cpp | 783 + .../scripts/outland/gruuls_lair/gruuls_lair.h | 21 + .../outland/gruuls_lair/instance_gruuls_lair.cpp | 193 + .../hellfire_citadel/blood_furnace/blood_furnace.h | 29 + .../blood_furnace/boss_broggok.cpp | 131 + .../blood_furnace/boss_kelidan_the_breaker.cpp | 356 + .../blood_furnace/boss_the_maker.cpp | 152 + .../blood_furnace/instance_blood_furnace.cpp | 232 + .../hellfire_ramparts/boss_omor_the_unscarred.cpp | 206 + .../hellfire_ramparts/boss_vazruden_the_herald.cpp | 467 + .../boss_watchkeeper_gargolmar.cpp | 156 + .../hellfire_ramparts/hellfire_ramparts.h | 16 + .../instance_hellfire_ramparts.cpp | 84 + .../magtheridons_lair/boss_magtheridon.cpp | 570 + .../instance_magtheridons_lair.cpp | 254 + .../magtheridons_lair/magtheridons_lair.h | 14 + .../shattered_halls/boss_nethekurse.cpp | 396 + .../shattered_halls/boss_warbringer_omrogg.cpp | 404 + .../boss_warchief_kargath_bladefist.cpp | 288 + .../shattered_halls/instance_shattered_halls.cpp | 114 + .../shattered_halls/shattered_halls.h | 14 + src/server/scripts/outland/hellfire_peninsula.cpp | 522 + src/server/scripts/outland/nagrand.cpp | 886 + src/server/scripts/outland/netherstorm.cpp | 925 + src/server/scripts/outland/shadowmoon_valley.cpp | 1828 ++ src/server/scripts/outland/shattrath_city.cpp | 673 + .../outland/tempest_keep/arcatraz/arcatraz.cpp | 520 + .../outland/tempest_keep/arcatraz/arcatraz.h | 21 + .../arcatraz/boss_harbinger_skyriss.cpp | 293 + .../tempest_keep/arcatraz/instance_arcatraz.cpp | 240 + .../botanica/boss_high_botanist_freywinn.cpp | 191 + .../outland/tempest_keep/botanica/boss_laj.cpp | 205 + .../tempest_keep/botanica/boss_warp_splinter.cpp | 216 + .../outland/tempest_keep/the_eye/boss_alar.cpp | 523 + .../tempest_keep/the_eye/boss_astromancer.cpp | 466 + .../outland/tempest_keep/the_eye/boss_kaelthas.cpp | 1498 ++ .../tempest_keep/the_eye/boss_void_reaver.cpp | 179 + .../tempest_keep/the_eye/instance_the_eye.cpp | 176 + .../outland/tempest_keep/the_eye/the_eye.cpp | 98 + .../scripts/outland/tempest_keep/the_eye/the_eye.h | 20 + .../the_mechanar/boss_gatewatcher_gyrokill.cpp | 39 + .../the_mechanar/boss_gatewatcher_ironhand.cpp | 130 + .../the_mechanar/boss_nethermancer_sepethrea.cpp | 245 + .../the_mechanar/boss_pathaleon_the_calculator.cpp | 246 + .../the_mechanar/instance_mechanar.cpp | 86 + .../outland/tempest_keep/the_mechanar/mechanar.h | 6 + src/server/scripts/outland/terokkar_forest.cpp | 680 + src/server/scripts/outland/zangarmarsh.cpp | 424 + src/server/scripts/world/areatrigger_scripts.cpp | 285 + src/server/scripts/world/boss_emeriss.cpp | 144 + src/server/scripts/world/boss_lethon.cpp | 25 + src/server/scripts/world/boss_taerar.cpp | 263 + src/server/scripts/world/boss_ysondre.cpp | 203 + src/server/scripts/world/go_scripts.cpp | 1182 + src/server/scripts/world/guards.cpp | 227 + src/server/scripts/world/item_scripts.cpp | 479 + src/server/scripts/world/mob_generic_creature.cpp | 228 + src/server/scripts/world/npc_innkeeper.cpp | 131 + src/server/scripts/world/npc_professions.cpp | 1342 ++ src/server/scripts/world/npc_taxi.cpp | 326 + src/server/scripts/world/npcs_special.cpp | 2600 ++ src/server/shared/Auth/AuthCrypt.cpp | 81 + src/server/shared/Auth/AuthCrypt.h | 47 + src/server/shared/Auth/BigNumber.cpp | 201 + src/server/shared/Auth/BigNumber.h | 96 + src/server/shared/Auth/CMakeLists.txt | 27 + src/server/shared/Auth/Hmac.cpp | 62 + src/server/shared/Auth/Hmac.h | 49 + src/server/shared/Auth/SARC4.cpp | 52 + src/server/shared/Auth/SARC4.h | 36 + src/server/shared/Auth/Sha1.cpp | 69 + src/server/shared/Auth/Sha1.h | 53 + src/server/shared/Auth/md5.c | 385 + src/server/shared/Auth/md5.h | 92 + src/server/shared/ByteBuffer.h | 601 + src/server/shared/CMakeLists.txt | 42 + src/server/shared/Common.cpp | 43 + src/server/shared/Common.h | 216 + src/server/shared/Config/CMakeLists.txt | 22 + src/server/shared/Config/Config.cpp | 105 + src/server/shared/Config/Config.h | 53 + src/server/shared/Config/ConfigEnv.h | 30 + src/server/shared/Config/ConfigLibrary.vcproj | 137 + src/server/shared/Config/dotconfpp/dotconfpp.cpp | 599 + src/server/shared/Config/dotconfpp/dotconfpp.h | 97 + src/server/shared/Config/dotconfpp/mempool.cpp | 100 + src/server/shared/Config/dotconfpp/mempool.h | 46 + src/server/shared/Database/CMakeLists.txt | 30 + src/server/shared/Database/DBCFileLoader.cpp | 270 + src/server/shared/Database/DBCFileLoader.h | 110 + src/server/shared/Database/DBCStore.h | 265 + src/server/shared/Database/Database.cpp | 657 + src/server/shared/Database/Database.h | 155 + src/server/shared/Database/DatabaseEnv.h | 43 + src/server/shared/Database/DatabaseImpl.h | 234 + src/server/shared/Database/Field.cpp | 77 + src/server/shared/Database/Field.h | 89 + src/server/shared/Database/QueryResult.cpp | 105 + src/server/shared/Database/QueryResult.h | 102 + src/server/shared/Database/SQLStorage.cpp | 77 + src/server/shared/Database/SQLStorage.h | 118 + src/server/shared/Database/SQLStorageImpl.h | 210 + src/server/shared/Database/SqlDelayThread.cpp | 61 + src/server/shared/Database/SqlDelayThread.h | 50 + src/server/shared/Database/SqlOperations.cpp | 209 + src/server/shared/Database/SqlOperations.h | 168 + src/server/shared/DelayExecutor.cpp | 111 + src/server/shared/DelayExecutor.h | 37 + src/server/shared/Errors.h | 48 + src/server/shared/LockedQueue.h | 139 + src/server/shared/Log.cpp | 1005 + src/server/shared/Log.h | 190 + src/server/shared/MemoryLeaks.cpp | 32 + src/server/shared/MemoryLeaks.h | 48 + src/server/shared/PacketLog.cpp | 160 + src/server/shared/PacketLog.h | 49 + src/server/shared/ProgressBar.cpp | 85 + src/server/shared/ProgressBar.h | 42 + src/server/shared/ServiceWin32.cpp | 263 + src/server/shared/ServiceWin32.h | 31 + src/server/shared/SignalHandler.h | 43 + src/server/shared/SystemConfig.h | 54 + src/server/shared/Threading.cpp | 232 + src/server/shared/Threading.h | 108 + src/server/shared/Timer.h | 215 + src/server/shared/Util.cpp | 468 + src/server/shared/Util.h | 536 + src/server/shared/WheatyExceptionReport.cpp | 1013 + src/server/shared/WheatyExceptionReport.h | 118 + src/server/shared/WorldPacket.h | 54 + src/server/shared/vmap/BIH.cpp | 304 + src/server/shared/vmap/BIH.h | 391 + src/server/shared/vmap/CMakeLists.txt | 30 + src/server/shared/vmap/IVMapManager.h | 106 + src/server/shared/vmap/MapTree.cpp | 450 + src/server/shared/vmap/MapTree.h | 97 + src/server/shared/vmap/ModelInstance.cpp | 219 + src/server/shared/vmap/ModelInstance.h | 81 + src/server/shared/vmap/TileAssembler.cpp | 494 + src/server/shared/vmap/TileAssembler.h | 89 + src/server/shared/vmap/VMapDefinitions.h | 42 + src/server/shared/vmap/VMapFactory.cpp | 136 + src/server/shared/vmap/VMapFactory.h | 43 + src/server/shared/vmap/VMapManager2.cpp | 336 + src/server/shared/vmap/VMapManager2.h | 114 + src/server/shared/vmap/VMapTools.h | 150 + src/server/shared/vmap/WorldModel.cpp | 535 + src/server/shared/vmap/WorldModel.h | 123 + src/server/trinitycore/CMakeLists.txt | 79 + src/server/trinitycore/CliRunnable.cpp | 445 + src/server/trinitycore/CliRunnable.h | 35 + src/server/trinitycore/Main.cpp | 154 + src/server/trinitycore/Master.cpp | 536 + src/server/trinitycore/Master.h | 48 + src/server/trinitycore/RASocket.cpp | 265 + src/server/trinitycore/RASocket.h | 68 + src/server/trinitycore/TrinityCore.ico | Bin 0 -> 136606 bytes src/server/trinitycore/TrinityCore.rc | 86 + src/server/trinitycore/WorldRunnable.cpp | 97 + src/server/trinitycore/WorldRunnable.h | 35 + src/server/trinitycore/resource.h | 15 + src/server/trinitycore/trinitycore.conf.dist | 2212 ++ src/server/trinityrealm/AuthCodes.cpp | 37 + src/server/trinityrealm/AuthCodes.h | 97 + src/server/trinityrealm/AuthSocket.cpp | 1069 + src/server/trinityrealm/AuthSocket.h | 97 + src/server/trinityrealm/CMakeLists.txt | 60 + src/server/trinityrealm/Main.cpp | 352 + src/server/trinityrealm/RealmAcceptor.h | 52 + src/server/trinityrealm/RealmList.cpp | 100 + src/server/trinityrealm/RealmList.h | 79 + src/server/trinityrealm/RealmSocket.cpp | 324 + src/server/trinityrealm/RealmSocket.h | 85 + src/server/trinityrealm/TrinityRealm.ico | Bin 0 -> 136606 bytes src/server/trinityrealm/TrinityRealm.rc | 86 + src/server/trinityrealm/resource.h | 15 + src/server/trinityrealm/trinityrealm.conf.dist | 146 + 1048 files changed, 427073 insertions(+) create mode 100644 src/server/framework/CMakeLists.txt create mode 100644 src/server/framework/Dynamic/FactoryHolder.h create mode 100644 src/server/framework/Dynamic/ObjectRegistry.h create mode 100644 src/server/framework/GameSystem/Grid.h create mode 100644 src/server/framework/GameSystem/GridLoader.h create mode 100644 src/server/framework/GameSystem/GridRefManager.h create mode 100644 src/server/framework/GameSystem/GridReference.h create mode 100644 src/server/framework/GameSystem/NGrid.h create mode 100644 src/server/framework/GameSystem/TypeContainer.h create mode 100644 src/server/framework/GameSystem/TypeContainerFunctions.h create mode 100644 src/server/framework/GameSystem/TypeContainerFunctionsPtr.h create mode 100644 src/server/framework/GameSystem/TypeContainerVisitor.h create mode 100644 src/server/framework/Network/SocketDefines.h create mode 100644 src/server/framework/Platform/CompilerDefs.h create mode 100644 src/server/framework/Platform/Define.h create mode 100644 src/server/framework/Policies/CreationPolicy.h create mode 100644 src/server/framework/Policies/ObjectLifeTime.cpp create mode 100644 src/server/framework/Policies/ObjectLifeTime.h create mode 100644 src/server/framework/Policies/Singleton.h create mode 100644 src/server/framework/Policies/SingletonImp.h create mode 100644 src/server/framework/Policies/ThreadingModel.h create mode 100644 src/server/framework/Utilities/ByteConverter.h create mode 100644 src/server/framework/Utilities/Callback.h create mode 100644 src/server/framework/Utilities/CountedReference/Reference.h create mode 100644 src/server/framework/Utilities/CountedReference/ReferenceHolder.h create mode 100644 src/server/framework/Utilities/CountedReference/ReferenceImpl.h create mode 100644 src/server/framework/Utilities/EventProcessor.cpp create mode 100644 src/server/framework/Utilities/EventProcessor.h create mode 100644 src/server/framework/Utilities/LinkedList.h create mode 100644 src/server/framework/Utilities/LinkedReference/RefManager.h create mode 100644 src/server/framework/Utilities/LinkedReference/Reference.h create mode 100644 src/server/framework/Utilities/TypeList.h create mode 100644 src/server/framework/Utilities/UnorderedMap.h create mode 100644 src/server/game/AccountMgr.cpp create mode 100644 src/server/game/AccountMgr.h create mode 100644 src/server/game/AchievementMgr.cpp create mode 100644 src/server/game/AchievementMgr.h create mode 100644 src/server/game/AddonHandler.cpp create mode 100644 src/server/game/AddonHandler.h create mode 100644 src/server/game/AddonMgr.cpp create mode 100644 src/server/game/AddonMgr.h create mode 100644 src/server/game/ArenaTeam.cpp create mode 100644 src/server/game/ArenaTeam.h create mode 100644 src/server/game/ArenaTeamHandler.cpp create mode 100644 src/server/game/AuctionHouseBot.cpp create mode 100644 src/server/game/AuctionHouseBot.h create mode 100644 src/server/game/AuctionHouseHandler.cpp create mode 100755 src/server/game/AuctionHouseMgr.cpp create mode 100644 src/server/game/AuctionHouseMgr.h create mode 100644 src/server/game/Bag.cpp create mode 100644 src/server/game/Bag.h create mode 100644 src/server/game/BattleGround.cpp create mode 100644 src/server/game/BattleGround.h create mode 100644 src/server/game/BattleGroundAA.cpp create mode 100644 src/server/game/BattleGroundAA.h create mode 100644 src/server/game/BattleGroundAB.cpp create mode 100644 src/server/game/BattleGroundAB.h create mode 100644 src/server/game/BattleGroundAV.cpp create mode 100644 src/server/game/BattleGroundAV.h create mode 100644 src/server/game/BattleGroundBE.cpp create mode 100644 src/server/game/BattleGroundBE.h create mode 100644 src/server/game/BattleGroundDS.cpp create mode 100644 src/server/game/BattleGroundDS.h create mode 100644 src/server/game/BattleGroundEY.cpp create mode 100644 src/server/game/BattleGroundEY.h create mode 100644 src/server/game/BattleGroundHandler.cpp create mode 100644 src/server/game/BattleGroundIC.cpp create mode 100644 src/server/game/BattleGroundIC.h create mode 100644 src/server/game/BattleGroundMgr.cpp create mode 100644 src/server/game/BattleGroundMgr.h create mode 100644 src/server/game/BattleGroundNA.cpp create mode 100644 src/server/game/BattleGroundNA.h create mode 100644 src/server/game/BattleGroundRB.cpp create mode 100644 src/server/game/BattleGroundRB.h create mode 100644 src/server/game/BattleGroundRL.cpp create mode 100644 src/server/game/BattleGroundRL.h create mode 100644 src/server/game/BattleGroundRV.cpp create mode 100644 src/server/game/BattleGroundRV.h create mode 100644 src/server/game/BattleGroundSA.cpp create mode 100644 src/server/game/BattleGroundSA.h create mode 100644 src/server/game/BattleGroundWS.cpp create mode 100644 src/server/game/BattleGroundWS.h create mode 100644 src/server/game/CMakeLists.txt create mode 100644 src/server/game/Calendar.cpp create mode 100644 src/server/game/Calendar.h create mode 100644 src/server/game/CalendarHandler.cpp create mode 100644 src/server/game/Cell.h create mode 100644 src/server/game/CellImpl.h create mode 100644 src/server/game/Channel.cpp create mode 100644 src/server/game/Channel.h create mode 100644 src/server/game/ChannelHandler.cpp create mode 100644 src/server/game/ChannelMgr.cpp create mode 100644 src/server/game/ChannelMgr.h create mode 100644 src/server/game/CharacterHandler.cpp create mode 100644 src/server/game/Chat.cpp create mode 100644 src/server/game/Chat.h create mode 100644 src/server/game/ChatHandler.cpp create mode 100644 src/server/game/CombatAI.cpp create mode 100644 src/server/game/CombatAI.h create mode 100644 src/server/game/CombatHandler.cpp create mode 100644 src/server/game/ConditionMgr.cpp create mode 100644 src/server/game/ConditionMgr.h create mode 100644 src/server/game/ConfusedMovementGenerator.cpp create mode 100644 src/server/game/ConfusedMovementGenerator.h create mode 100644 src/server/game/Corpse.cpp create mode 100644 src/server/game/Corpse.h create mode 100644 src/server/game/Creature.cpp create mode 100644 src/server/game/Creature.h create mode 100644 src/server/game/CreatureAI.cpp create mode 100644 src/server/game/CreatureAI.h create mode 100644 src/server/game/CreatureAIFactory.h create mode 100644 src/server/game/CreatureAIImpl.h create mode 100644 src/server/game/CreatureAIRegistry.cpp create mode 100644 src/server/game/CreatureAIRegistry.h create mode 100644 src/server/game/CreatureAISelector.cpp create mode 100644 src/server/game/CreatureAISelector.h create mode 100644 src/server/game/CreatureEventAI.cpp create mode 100644 src/server/game/CreatureEventAI.h create mode 100644 src/server/game/CreatureEventAIMgr.cpp create mode 100644 src/server/game/CreatureEventAIMgr.h create mode 100644 src/server/game/CreatureGroups.cpp create mode 100644 src/server/game/CreatureGroups.h create mode 100644 src/server/game/DBCEnums.h create mode 100644 src/server/game/DBCStores.cpp create mode 100644 src/server/game/DBCStores.h create mode 100644 src/server/game/DBCStructure.h create mode 100644 src/server/game/DBCfmt.h create mode 100644 src/server/game/Debugcmds.cpp create mode 100644 src/server/game/DestinationHolder.cpp create mode 100644 src/server/game/DestinationHolder.h create mode 100644 src/server/game/DestinationHolderImp.h create mode 100644 src/server/game/DuelHandler.cpp create mode 100644 src/server/game/DynamicObject.cpp create mode 100644 src/server/game/DynamicObject.h create mode 100644 src/server/game/FleeingMovementGenerator.cpp create mode 100644 src/server/game/FleeingMovementGenerator.h create mode 100644 src/server/game/FollowerRefManager.h create mode 100644 src/server/game/FollowerReference.cpp create mode 100644 src/server/game/FollowerReference.h create mode 100644 src/server/game/Formulas.h create mode 100644 src/server/game/GameEventMgr.cpp create mode 100644 src/server/game/GameEventMgr.h create mode 100644 src/server/game/GameObject.cpp create mode 100644 src/server/game/GameObject.h create mode 100644 src/server/game/GlobalEvents.cpp create mode 100644 src/server/game/GlobalEvents.h create mode 100644 src/server/game/GossipDef.cpp create mode 100644 src/server/game/GossipDef.h create mode 100644 src/server/game/GridDefines.h create mode 100644 src/server/game/GridNotifiers.cpp create mode 100644 src/server/game/GridNotifiers.h create mode 100644 src/server/game/GridNotifiersImpl.h create mode 100644 src/server/game/GridStates.cpp create mode 100644 src/server/game/GridStates.h create mode 100644 src/server/game/Group.cpp create mode 100644 src/server/game/Group.h create mode 100644 src/server/game/GroupHandler.cpp create mode 100644 src/server/game/GroupRefManager.h create mode 100644 src/server/game/GroupReference.cpp create mode 100644 src/server/game/GroupReference.h create mode 100644 src/server/game/GuardAI.cpp create mode 100644 src/server/game/GuardAI.h create mode 100644 src/server/game/Guild.cpp create mode 100644 src/server/game/Guild.h create mode 100644 src/server/game/GuildHandler.cpp create mode 100644 src/server/game/HomeMovementGenerator.cpp create mode 100644 src/server/game/HomeMovementGenerator.h create mode 100644 src/server/game/HostileRefManager.cpp create mode 100644 src/server/game/HostileRefManager.h create mode 100644 src/server/game/IdleMovementGenerator.cpp create mode 100644 src/server/game/IdleMovementGenerator.h create mode 100644 src/server/game/InstanceData.cpp create mode 100644 src/server/game/InstanceData.h create mode 100644 src/server/game/InstanceSaveMgr.cpp create mode 100644 src/server/game/InstanceSaveMgr.h create mode 100644 src/server/game/Item.cpp create mode 100644 src/server/game/Item.h create mode 100644 src/server/game/ItemEnchantmentMgr.cpp create mode 100644 src/server/game/ItemEnchantmentMgr.h create mode 100644 src/server/game/ItemHandler.cpp create mode 100644 src/server/game/ItemPrototype.h create mode 100644 src/server/game/LFG.h create mode 100644 src/server/game/LFGHandler.cpp create mode 100644 src/server/game/LFGMgr.cpp create mode 100644 src/server/game/LFGMgr.h create mode 100644 src/server/game/Language.h create mode 100644 src/server/game/Level0.cpp create mode 100644 src/server/game/Level1.cpp create mode 100644 src/server/game/Level2.cpp create mode 100644 src/server/game/Level3.cpp create mode 100644 src/server/game/LootHandler.cpp create mode 100644 src/server/game/LootMgr.cpp create mode 100644 src/server/game/LootMgr.h create mode 100644 src/server/game/Mail.cpp create mode 100644 src/server/game/Mail.h create mode 100644 src/server/game/Map.cpp create mode 100644 src/server/game/Map.h create mode 100644 src/server/game/MapInstanced.cpp create mode 100644 src/server/game/MapInstanced.h create mode 100644 src/server/game/MapManager.cpp create mode 100644 src/server/game/MapManager.h create mode 100644 src/server/game/MapRefManager.h create mode 100644 src/server/game/MapReference.h create mode 100644 src/server/game/MapUpdater.cpp create mode 100644 src/server/game/MapUpdater.h create mode 100644 src/server/game/MiscHandler.cpp create mode 100644 src/server/game/MotionMaster.cpp create mode 100644 src/server/game/MotionMaster.h create mode 100644 src/server/game/MovementGenerator.cpp create mode 100644 src/server/game/MovementGenerator.h create mode 100644 src/server/game/MovementGeneratorImpl.h create mode 100644 src/server/game/MovementHandler.cpp create mode 100644 src/server/game/NPCHandler.cpp create mode 100644 src/server/game/NPCHandler.h create mode 100644 src/server/game/Object.cpp create mode 100644 src/server/game/Object.h create mode 100644 src/server/game/ObjectAccessor.cpp create mode 100644 src/server/game/ObjectAccessor.h create mode 100644 src/server/game/ObjectDefines.h create mode 100644 src/server/game/ObjectGridLoader.cpp create mode 100644 src/server/game/ObjectGridLoader.h create mode 100644 src/server/game/ObjectMgr.cpp create mode 100644 src/server/game/ObjectMgr.h create mode 100644 src/server/game/ObjectPosSelector.cpp create mode 100644 src/server/game/ObjectPosSelector.h create mode 100644 src/server/game/Opcodes.cpp create mode 100644 src/server/game/Opcodes.h create mode 100644 src/server/game/OutdoorPvP.cpp create mode 100644 src/server/game/OutdoorPvP.h create mode 100644 src/server/game/OutdoorPvPEP.cpp create mode 100644 src/server/game/OutdoorPvPEP.h create mode 100644 src/server/game/OutdoorPvPHP.cpp create mode 100644 src/server/game/OutdoorPvPHP.h create mode 100644 src/server/game/OutdoorPvPImpl.h create mode 100644 src/server/game/OutdoorPvPMgr.cpp create mode 100644 src/server/game/OutdoorPvPMgr.h create mode 100644 src/server/game/OutdoorPvPNA.cpp create mode 100644 src/server/game/OutdoorPvPNA.h create mode 100644 src/server/game/OutdoorPvPSI.cpp create mode 100644 src/server/game/OutdoorPvPSI.h create mode 100644 src/server/game/OutdoorPvPTF.cpp create mode 100644 src/server/game/OutdoorPvPTF.h create mode 100644 src/server/game/OutdoorPvPZM.cpp create mode 100644 src/server/game/OutdoorPvPZM.h create mode 100644 src/server/game/PassiveAI.cpp create mode 100644 src/server/game/PassiveAI.h create mode 100644 src/server/game/Path.h create mode 100644 src/server/game/Pet.cpp create mode 100644 src/server/game/Pet.h create mode 100644 src/server/game/PetAI.cpp create mode 100644 src/server/game/PetAI.h create mode 100644 src/server/game/PetHandler.cpp create mode 100644 src/server/game/PetitionsHandler.cpp create mode 100644 src/server/game/Player.cpp create mode 100644 src/server/game/Player.h create mode 100644 src/server/game/PlayerDump.cpp create mode 100644 src/server/game/PlayerDump.h create mode 100644 src/server/game/PointMovementGenerator.cpp create mode 100644 src/server/game/PointMovementGenerator.h create mode 100644 src/server/game/PoolHandler.cpp create mode 100644 src/server/game/PoolHandler.h create mode 100644 src/server/game/QueryHandler.cpp create mode 100644 src/server/game/QuestDef.cpp create mode 100644 src/server/game/QuestDef.h create mode 100644 src/server/game/QuestHandler.cpp create mode 100644 src/server/game/RandomMovementGenerator.cpp create mode 100644 src/server/game/RandomMovementGenerator.h create mode 100644 src/server/game/ReactorAI.cpp create mode 100644 src/server/game/ReactorAI.h create mode 100644 src/server/game/ReputationMgr.cpp create mode 100644 src/server/game/ReputationMgr.h create mode 100644 src/server/game/ScriptLoader.cpp create mode 100644 src/server/game/ScriptLoader.h create mode 100644 src/server/game/ScriptMgr.cpp create mode 100644 src/server/game/ScriptMgr.h create mode 100644 src/server/game/ScriptSystem.cpp create mode 100644 src/server/game/ScriptSystem.h create mode 100644 src/server/game/ScriptedCreature.cpp create mode 100644 src/server/game/ScriptedCreature.h create mode 100644 src/server/game/ScriptedEscortAI.cpp create mode 100644 src/server/game/ScriptedEscortAI.h create mode 100644 src/server/game/ScriptedFollowerAI.cpp create mode 100644 src/server/game/ScriptedFollowerAI.h create mode 100644 src/server/game/ScriptedGossip.h create mode 100644 src/server/game/ScriptedGuardAI.cpp create mode 100644 src/server/game/ScriptedGuardAI.h create mode 100644 src/server/game/ScriptedInstance.h create mode 100644 src/server/game/ScriptedPch.cpp create mode 100644 src/server/game/ScriptedPch.h create mode 100644 src/server/game/ScriptedSimpleAI.cpp create mode 100644 src/server/game/ScriptedSimpleAI.h create mode 100644 src/server/game/ScriptedSmartAI.cpp create mode 100644 src/server/game/ScriptedSmartAI.h create mode 100644 src/server/game/SharedDefines.h create mode 100644 src/server/game/SkillDiscovery.cpp create mode 100644 src/server/game/SkillDiscovery.h create mode 100644 src/server/game/SkillExtraItems.cpp create mode 100644 src/server/game/SkillExtraItems.h create mode 100644 src/server/game/SkillHandler.cpp create mode 100644 src/server/game/SocialMgr.cpp create mode 100644 src/server/game/SocialMgr.h create mode 100644 src/server/game/Spell.cpp create mode 100644 src/server/game/Spell.h create mode 100644 src/server/game/SpellAuraDefines.h create mode 100644 src/server/game/SpellAuraEffects.cpp create mode 100644 src/server/game/SpellAuraEffects.h create mode 100644 src/server/game/SpellAuras.cpp create mode 100644 src/server/game/SpellAuras.h create mode 100644 src/server/game/SpellEffects.cpp create mode 100644 src/server/game/SpellHandler.cpp create mode 100644 src/server/game/SpellMgr.cpp create mode 100644 src/server/game/SpellMgr.h create mode 100644 src/server/game/StatSystem.cpp create mode 100644 src/server/game/TargetedMovementGenerator.cpp create mode 100644 src/server/game/TargetedMovementGenerator.h create mode 100644 src/server/game/TaxiHandler.cpp create mode 100644 src/server/game/TemporarySummon.cpp create mode 100644 src/server/game/TemporarySummon.h create mode 100644 src/server/game/ThreatManager.cpp create mode 100644 src/server/game/ThreatManager.h create mode 100644 src/server/game/TicketHandler.cpp create mode 100644 src/server/game/TimeMgr.cpp create mode 100644 src/server/game/TimeMgr.h create mode 100644 src/server/game/Tools.cpp create mode 100644 src/server/game/Tools.h create mode 100644 src/server/game/Totem.cpp create mode 100644 src/server/game/Totem.h create mode 100644 src/server/game/TotemAI.cpp create mode 100644 src/server/game/TotemAI.h create mode 100644 src/server/game/TradeHandler.cpp create mode 100644 src/server/game/Transports.cpp create mode 100644 src/server/game/Transports.h create mode 100644 src/server/game/Traveller.h create mode 100644 src/server/game/Unit.cpp create mode 100644 src/server/game/Unit.h create mode 100644 src/server/game/UnitAI.cpp create mode 100644 src/server/game/UnitAI.h create mode 100644 src/server/game/UnitEvents.h create mode 100644 src/server/game/UpdateData.cpp create mode 100644 src/server/game/UpdateData.h create mode 100644 src/server/game/UpdateFields.h create mode 100644 src/server/game/UpdateMask.h create mode 100644 src/server/game/Vehicle.cpp create mode 100644 src/server/game/Vehicle.h create mode 100644 src/server/game/VoiceChatHandler.cpp create mode 100644 src/server/game/WaypointManager.cpp create mode 100644 src/server/game/WaypointManager.h create mode 100644 src/server/game/WaypointMovementGenerator.cpp create mode 100644 src/server/game/WaypointMovementGenerator.h create mode 100644 src/server/game/Weather.cpp create mode 100644 src/server/game/Weather.h create mode 100644 src/server/game/World.cpp create mode 100644 src/server/game/World.h create mode 100644 src/server/game/WorldLog.cpp create mode 100644 src/server/game/WorldLog.h create mode 100644 src/server/game/WorldSession.cpp create mode 100644 src/server/game/WorldSession.h create mode 100644 src/server/game/WorldSocket.cpp create mode 100644 src/server/game/WorldSocket.h create mode 100644 src/server/game/WorldSocketMgr.cpp create mode 100644 src/server/game/WorldSocketMgr.h create mode 100644 src/server/game/ZoneScript.h create mode 100644 src/server/game/pchdef.cpp create mode 100644 src/server/game/pchdef.h create mode 100644 src/server/game/pchlinux.cpp create mode 100644 src/server/game/pchlinux.h create mode 100644 src/server/scripts/CMakeLists.txt create mode 100644 src/server/scripts/custom/custom_example.cpp create mode 100644 src/server/scripts/custom/custom_gossip_codebox.cpp create mode 100644 src/server/scripts/custom/npc_acherus_taxi.cpp create mode 100644 src/server/scripts/custom/npc_wyrmresttempel_taxi.cpp create mode 100644 src/server/scripts/custom/on_events.cpp create mode 100644 src/server/scripts/custom/test.cpp create mode 100644 src/server/scripts/eastern_kingdoms/alterac_mountains.cpp create mode 100644 src/server/scripts/eastern_kingdoms/alterac_valley/alterac_valley.cpp create mode 100644 src/server/scripts/eastern_kingdoms/alterac_valley/boss_balinda.cpp create mode 100644 src/server/scripts/eastern_kingdoms/alterac_valley/boss_drekthar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/alterac_valley/boss_galvangar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/alterac_valley/boss_vanndar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/arathi_highlands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.h create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/blackrock_spire.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/blackrock_spire.h create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackrock_spire/instance_blackrock_spire.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp create mode 100644 src/server/scripts/eastern_kingdoms/blasted_lands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/boss_kruul.cpp create mode 100644 src/server/scripts/eastern_kingdoms/burning_steppes.cpp create mode 100644 src/server/scripts/eastern_kingdoms/deadmines/boss_mr_smite.cpp create mode 100644 src/server/scripts/eastern_kingdoms/deadmines/deadmines.cpp create mode 100644 src/server/scripts/eastern_kingdoms/deadmines/deadmines.h create mode 100644 src/server/scripts/eastern_kingdoms/deadmines/instance_deadmines.cpp create mode 100644 src/server/scripts/eastern_kingdoms/dun_morogh.cpp create mode 100644 src/server/scripts/eastern_kingdoms/duskwood.cpp create mode 100644 src/server/scripts/eastern_kingdoms/eastern_plaguelands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/elwynn_forest.cpp create mode 100644 src/server/scripts/eastern_kingdoms/eversong_woods.cpp create mode 100644 src/server/scripts/eastern_kingdoms/ghostlands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/gnomeregan/gnomeregan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/gnomeregan/gnomeregan.h create mode 100644 src/server/scripts/eastern_kingdoms/gnomeregan/instance_gnomeregan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/hinterlands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/ironforge.cpp create mode 100644 src/server/scripts/eastern_kingdoms/isle_of_queldanas.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_curator.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/karazhan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/karazhan/karazhan.h create mode 100644 src/server/scripts/eastern_kingdoms/loch_modan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp create mode 100644 src/server/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.h create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_garr.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/molten_core.cpp create mode 100644 src/server/scripts/eastern_kingdoms/molten_core/molten_core.h create mode 100644 src/server/scripts/eastern_kingdoms/redridge_mountains.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scarlet_monastery/scarlet_monastery.h create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp create mode 100644 src/server/scripts/eastern_kingdoms/scholomance/scholomance.h create mode 100644 src/server/scripts/eastern_kingdoms/searing_gorge.cpp create mode 100644 src/server/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp create mode 100644 src/server/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp create mode 100644 src/server/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.h create mode 100644 src/server/scripts/eastern_kingdoms/silvermoon_city.cpp create mode 100644 src/server/scripts/eastern_kingdoms/silverpine_forest.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stormwind_city.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stranglethorn_vale.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/stratholme.cpp create mode 100644 src/server/scripts/eastern_kingdoms/stratholme/stratholme.h create mode 100644 src/server/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunken_temple/sunken_temple.h create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp create mode 100644 src/server/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.h create mode 100644 src/server/scripts/eastern_kingdoms/tirisfal_glades.cpp create mode 100644 src/server/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp create mode 100644 src/server/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp create mode 100644 src/server/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp create mode 100644 src/server/scripts/eastern_kingdoms/uldaman/uldaman.cpp create mode 100644 src/server/scripts/eastern_kingdoms/undercity.cpp create mode 100644 src/server/scripts/eastern_kingdoms/western_plaguelands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/westfall.cpp create mode 100644 src/server/scripts/eastern_kingdoms/wetlands.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/zulaman.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulaman/zulaman.h create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp create mode 100644 src/server/scripts/eastern_kingdoms/zulgurub/zulgurub.h create mode 100644 src/server/scripts/examples/example_creature.cpp create mode 100644 src/server/scripts/examples/example_escort.cpp create mode 100644 src/server/scripts/examples/example_gossip_codebox.cpp create mode 100644 src/server/scripts/examples/example_misc.cpp create mode 100644 src/server/scripts/kalimdor/ashenvale.cpp create mode 100644 src/server/scripts/kalimdor/azshara.cpp create mode 100644 src/server/scripts/kalimdor/azuremyst_isle.cpp create mode 100644 src/server/scripts/kalimdor/blackfathom_depths/blackfathom_deeps.cpp create mode 100644 src/server/scripts/kalimdor/blackfathom_depths/blackfathom_deeps.h create mode 100644 src/server/scripts/kalimdor/blackfathom_depths/boss_aku_mai.cpp create mode 100644 src/server/scripts/kalimdor/blackfathom_depths/boss_gelihast.cpp create mode 100644 src/server/scripts/kalimdor/blackfathom_depths/boss_kelris.cpp create mode 100644 src/server/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp create mode 100644 src/server/scripts/kalimdor/bloodmyst_isle.cpp create mode 100644 src/server/scripts/kalimdor/boss_azuregos.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_infinite.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/culling_of_stratholme.h create mode 100644 src/server/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.h create mode 100644 src/server/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal.h create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h create mode 100644 src/server/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp create mode 100644 src/server/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.h create mode 100644 src/server/scripts/kalimdor/darkshore.cpp create mode 100644 src/server/scripts/kalimdor/desolace.cpp create mode 100644 src/server/scripts/kalimdor/durotar.cpp create mode 100644 src/server/scripts/kalimdor/dustwallow_marsh.cpp create mode 100644 src/server/scripts/kalimdor/felwood.cpp create mode 100644 src/server/scripts/kalimdor/feralas.cpp create mode 100644 src/server/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp create mode 100644 src/server/scripts/kalimdor/maraudon/boss_landslide.cpp create mode 100644 src/server/scripts/kalimdor/maraudon/boss_noxxion.cpp create mode 100644 src/server/scripts/kalimdor/maraudon/boss_princess_theradras.cpp create mode 100644 src/server/scripts/kalimdor/moonglade.cpp create mode 100644 src/server/scripts/kalimdor/mulgore.cpp create mode 100644 src/server/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp create mode 100644 src/server/scripts/kalimdor/onyxias_lair/instance_onyxias_lair.cpp create mode 100644 src/server/scripts/kalimdor/onyxias_lair/onyxias_lair.h create mode 100644 src/server/scripts/kalimdor/orgrimmar.cpp create mode 100644 src/server/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp create mode 100644 src/server/scripts/kalimdor/razorfen_downs/instance_razorfen_downs.cpp create mode 100644 src/server/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp create mode 100644 src/server/scripts/kalimdor/razorfen_downs/razorfen_downs.h create mode 100644 src/server/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp create mode 100644 src/server/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp create mode 100644 src/server/scripts/kalimdor/razorfen_kraul/razorfen_kraul.h create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp create mode 100644 src/server/scripts/kalimdor/ruins_of_ahnqiraj/ruins_of_ahnqiraj.h create mode 100644 src/server/scripts/kalimdor/silithus.cpp create mode 100644 src/server/scripts/kalimdor/stonetalon_mountains.cpp create mode 100644 src/server/scripts/kalimdor/tanaris.cpp create mode 100644 src/server/scripts/kalimdor/teldrassil.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp create mode 100644 src/server/scripts/kalimdor/temple_of_ahnqiraj/temple_of_ahnqiraj.h create mode 100644 src/server/scripts/kalimdor/the_barrens.cpp create mode 100644 src/server/scripts/kalimdor/thousand_needles.cpp create mode 100644 src/server/scripts/kalimdor/thunder_bluff.cpp create mode 100644 src/server/scripts/kalimdor/ungoro_crater.cpp create mode 100644 src/server/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp create mode 100644 src/server/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp create mode 100644 src/server/scripts/kalimdor/wailing_caverns/wailing_caverns.h create mode 100644 src/server/scripts/kalimdor/winterspring.cpp create mode 100644 src/server/scripts/kalimdor/zulfarrak/instance_zulfarrak.cpp create mode 100644 src/server/scripts/kalimdor/zulfarrak/zulfarrak.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/ahnkahet.h create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/azjol_nerub/azjol_nerub.h create mode 100644 src/server/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp create mode 100644 src/server/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp create mode 100644 src/server/scripts/northrend/borean_tundra.cpp create mode 100644 src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_argent_challenge.cpp create mode 100644 src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_black_knight.cpp create mode 100644 src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/boss_grand_champions.cpp create mode 100644 src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/instance_trial_of_the_champion.cpp create mode 100644 src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.cpp create mode 100644 src/server/scripts/northrend/crusaders_coliseum/trial_of_the_champion/trial_of_the_champion.h create mode 100644 src/server/scripts/northrend/crystalsong_forest.cpp create mode 100644 src/server/scripts/northrend/dalaran.cpp create mode 100644 src/server/scripts/northrend/dragonblight.cpp create mode 100644 src/server/scripts/northrend/draktharon_keep/boss_dred.cpp create mode 100644 src/server/scripts/northrend/draktharon_keep/boss_novos.cpp create mode 100644 src/server/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp create mode 100644 src/server/scripts/northrend/draktharon_keep/boss_trollgore.cpp create mode 100644 src/server/scripts/northrend/draktharon_keep/drak_tharon_keep.h create mode 100644 src/server/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/forge_of_souls/boss_bronjahm.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/forge_of_souls/boss_devourer_of_souls.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/forge_of_souls/forge_of_souls.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/forge_of_souls/forge_of_souls.h create mode 100644 src/server/scripts/northrend/frozen_halls/forge_of_souls/instance_forge_of_souls.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/halls_of_reflection/boss_falric.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/halls_of_reflection/boss_marwyn.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/halls_of_reflection/halls_of_reflection.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/halls_of_reflection/halls_of_reflection.h create mode 100644 src/server/scripts/northrend/frozen_halls/halls_of_reflection/instance_halls_of_reflection.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/pit_of_saron/boss_forgemaster_garfrost.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/pit_of_saron/boss_krickandick.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/pit_of_saron/boss_scourgelord_tyrannus.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/pit_of_saron/instance_pit_of_saron.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/pit_of_saron/pit_of_saron.cpp create mode 100644 src/server/scripts/northrend/frozen_halls/pit_of_saron/pit_of_saron.h create mode 100644 src/server/scripts/northrend/grizzly_hills.cpp create mode 100644 src/server/scripts/northrend/gundrak/boss_drakkari_colossus.cpp create mode 100644 src/server/scripts/northrend/gundrak/boss_eck.cpp create mode 100644 src/server/scripts/northrend/gundrak/boss_gal_darah.cpp create mode 100644 src/server/scripts/northrend/gundrak/boss_moorabi.cpp create mode 100644 src/server/scripts/northrend/gundrak/boss_slad_ran.cpp create mode 100644 src/server/scripts/northrend/gundrak/gundrak.h create mode 100644 src/server/scripts/northrend/gundrak/instance_gundrak.cpp create mode 100644 src/server/scripts/northrend/howling_fjord.cpp create mode 100644 src/server/scripts/northrend/icecrown.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_anubrekhan.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_faerlina.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_four_horsemen.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_gluth.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_gothik.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_grobbulus.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_heigan.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_highlord_mograine.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_kelthuzad.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_loatheb.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_maexxna.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_noth.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_patchwerk.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_razuvious.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_sapphiron.cpp create mode 100644 src/server/scripts/northrend/naxxramas/boss_thaddius.cpp create mode 100644 src/server/scripts/northrend/naxxramas/instance_naxxramas.cpp create mode 100644 src/server/scripts/northrend/naxxramas/naxxramas.h create mode 100644 src/server/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp create mode 100644 src/server/scripts/northrend/nexus/eye_of_eternity/eye_of_eternity.h create mode 100644 src/server/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/boss_anomalus.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/boss_keristrasza.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/boss_ormorok.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/commander_kolurg.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/instance_nexus.cpp create mode 100644 src/server/scripts/northrend/nexus/nexus/nexus.h create mode 100644 src/server/scripts/northrend/nexus/oculus/boss_drakos.cpp create mode 100644 src/server/scripts/northrend/nexus/oculus/boss_eregos.cpp create mode 100644 src/server/scripts/northrend/nexus/oculus/boss_urom.cpp create mode 100644 src/server/scripts/northrend/nexus/oculus/boss_varos.cpp create mode 100644 src/server/scripts/northrend/nexus/oculus/instance_oculus.cpp create mode 100644 src/server/scripts/northrend/nexus/oculus/oculus.cpp create mode 100644 src/server/scripts/northrend/nexus/oculus/oculus.h create mode 100644 src/server/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp create mode 100644 src/server/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp create mode 100644 src/server/scripts/northrend/obsidian_sanctum/obsidian_sanctum.h create mode 100644 src/server/scripts/northrend/sholazar_basin.cpp create mode 100644 src/server/scripts/northrend/storm_peaks.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_lightning/halls_of_lightning.h create mode 100644 src/server/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp create mode 100644 src/server/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.h create mode 100644 src/server/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_algalon.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_freya.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_hodir.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_ignis.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_thorim.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_xt002.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp create mode 100644 src/server/scripts/northrend/ulduar/ulduar/ulduar.h create mode 100644 src/server/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.h create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp create mode 100644 src/server/scripts/northrend/utgarde_keep/utgarde_pinnacle/utgarde_pinnacle.h create mode 100644 src/server/scripts/northrend/vault_of_archavon/boss_archavon.cpp create mode 100644 src/server/scripts/northrend/vault_of_archavon/boss_emalon.cpp create mode 100644 src/server/scripts/northrend/vault_of_archavon/boss_koralon.cpp create mode 100644 src/server/scripts/northrend/vault_of_archavon/boss_toravon.cpp create mode 100644 src/server/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp create mode 100644 src/server/scripts/northrend/vault_of_archavon/vault_of_archavon.h create mode 100644 src/server/scripts/northrend/violet_hold/boss_cyanigosa.cpp create mode 100644 src/server/scripts/northrend/violet_hold/boss_erekem.cpp create mode 100644 src/server/scripts/northrend/violet_hold/boss_ichoron.cpp create mode 100644 src/server/scripts/northrend/violet_hold/boss_lavanthor.cpp create mode 100644 src/server/scripts/northrend/violet_hold/boss_moragg.cpp create mode 100644 src/server/scripts/northrend/violet_hold/boss_xevozz.cpp create mode 100644 src/server/scripts/northrend/violet_hold/boss_zuramat.cpp create mode 100644 src/server/scripts/northrend/violet_hold/instance_violet_hold.cpp create mode 100644 src/server/scripts/northrend/violet_hold/violet_hold.cpp create mode 100644 src/server/scripts/northrend/violet_hold/violet_hold.h create mode 100644 src/server/scripts/northrend/zuldrak.cpp create mode 100644 src/server/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp create mode 100644 src/server/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp create mode 100644 src/server/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp create mode 100644 src/server/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp create mode 100644 src/server/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp create mode 100644 src/server/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp create mode 100644 src/server/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp create mode 100644 src/server/scripts/outland/auchindoun/sethekk_halls/sethekk_halls.h create mode 100644 src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp create mode 100644 src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp create mode 100644 src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp create mode 100644 src/server/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp create mode 100644 src/server/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp create mode 100644 src/server/scripts/outland/auchindoun/shadow_labyrinth/shadow_labyrinth.h create mode 100644 src/server/scripts/outland/black_temple/black_temple.cpp create mode 100644 src/server/scripts/outland/black_temple/black_temple.h create mode 100644 src/server/scripts/outland/black_temple/boss_bloodboil.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_illidan.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_mother_shahraz.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_reliquary_of_souls.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_shade_of_akama.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_supremus.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_teron_gorefiend.cpp create mode 100644 src/server/scripts/outland/black_temple/boss_warlord_najentus.cpp create mode 100644 src/server/scripts/outland/black_temple/illidari_council.cpp create mode 100644 src/server/scripts/outland/black_temple/instance_black_temple.cpp create mode 100644 src/server/scripts/outland/blades_edge_mountains.cpp create mode 100644 src/server/scripts/outland/boss_doomlord_kazzak.cpp create mode 100644 src/server/scripts/outland/boss_doomwalker.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/serpent_shrine/serpent_shrine.h create mode 100644 src/server/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/steam_vault/steam_vault.h create mode 100644 src/server/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp create mode 100644 src/server/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp create mode 100644 src/server/scripts/outland/gruuls_lair/boss_gruul.cpp create mode 100644 src/server/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp create mode 100644 src/server/scripts/outland/gruuls_lair/gruuls_lair.h create mode 100644 src/server/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/blood_furnace/blood_furnace.h create mode 100644 src/server/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/hellfire_ramparts.h create mode 100644 src/server/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/magtheridons_lair/magtheridons_lair.h create mode 100644 src/server/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp create mode 100644 src/server/scripts/outland/hellfire_citadel/shattered_halls/shattered_halls.h create mode 100644 src/server/scripts/outland/hellfire_peninsula.cpp create mode 100644 src/server/scripts/outland/nagrand.cpp create mode 100644 src/server/scripts/outland/netherstorm.cpp create mode 100644 src/server/scripts/outland/shadowmoon_valley.cpp create mode 100644 src/server/scripts/outland/shattrath_city.cpp create mode 100644 src/server/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp create mode 100644 src/server/scripts/outland/tempest_keep/arcatraz/arcatraz.h create mode 100644 src/server/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp create mode 100644 src/server/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp create mode 100644 src/server/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp create mode 100644 src/server/scripts/outland/tempest_keep/botanica/boss_laj.cpp create mode 100644 src/server/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/boss_alar.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/the_eye.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_eye/the_eye.h create mode 100644 src/server/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp create mode 100644 src/server/scripts/outland/tempest_keep/the_mechanar/mechanar.h create mode 100644 src/server/scripts/outland/terokkar_forest.cpp create mode 100644 src/server/scripts/outland/zangarmarsh.cpp create mode 100644 src/server/scripts/world/areatrigger_scripts.cpp create mode 100644 src/server/scripts/world/boss_emeriss.cpp create mode 100644 src/server/scripts/world/boss_lethon.cpp create mode 100644 src/server/scripts/world/boss_taerar.cpp create mode 100644 src/server/scripts/world/boss_ysondre.cpp create mode 100644 src/server/scripts/world/go_scripts.cpp create mode 100644 src/server/scripts/world/guards.cpp create mode 100644 src/server/scripts/world/item_scripts.cpp create mode 100644 src/server/scripts/world/mob_generic_creature.cpp create mode 100644 src/server/scripts/world/npc_innkeeper.cpp create mode 100644 src/server/scripts/world/npc_professions.cpp create mode 100644 src/server/scripts/world/npc_taxi.cpp create mode 100644 src/server/scripts/world/npcs_special.cpp create mode 100644 src/server/shared/Auth/AuthCrypt.cpp create mode 100644 src/server/shared/Auth/AuthCrypt.h create mode 100644 src/server/shared/Auth/BigNumber.cpp create mode 100644 src/server/shared/Auth/BigNumber.h create mode 100644 src/server/shared/Auth/CMakeLists.txt create mode 100644 src/server/shared/Auth/Hmac.cpp create mode 100644 src/server/shared/Auth/Hmac.h create mode 100644 src/server/shared/Auth/SARC4.cpp create mode 100644 src/server/shared/Auth/SARC4.h create mode 100644 src/server/shared/Auth/Sha1.cpp create mode 100644 src/server/shared/Auth/Sha1.h create mode 100644 src/server/shared/Auth/md5.c create mode 100644 src/server/shared/Auth/md5.h create mode 100644 src/server/shared/ByteBuffer.h create mode 100644 src/server/shared/CMakeLists.txt create mode 100644 src/server/shared/Common.cpp create mode 100644 src/server/shared/Common.h create mode 100644 src/server/shared/Config/CMakeLists.txt create mode 100644 src/server/shared/Config/Config.cpp create mode 100644 src/server/shared/Config/Config.h create mode 100644 src/server/shared/Config/ConfigEnv.h create mode 100644 src/server/shared/Config/ConfigLibrary.vcproj create mode 100644 src/server/shared/Config/dotconfpp/dotconfpp.cpp create mode 100644 src/server/shared/Config/dotconfpp/dotconfpp.h create mode 100644 src/server/shared/Config/dotconfpp/mempool.cpp create mode 100644 src/server/shared/Config/dotconfpp/mempool.h create mode 100644 src/server/shared/Database/CMakeLists.txt create mode 100644 src/server/shared/Database/DBCFileLoader.cpp create mode 100644 src/server/shared/Database/DBCFileLoader.h create mode 100644 src/server/shared/Database/DBCStore.h create mode 100644 src/server/shared/Database/Database.cpp create mode 100644 src/server/shared/Database/Database.h create mode 100644 src/server/shared/Database/DatabaseEnv.h create mode 100644 src/server/shared/Database/DatabaseImpl.h create mode 100644 src/server/shared/Database/Field.cpp create mode 100644 src/server/shared/Database/Field.h create mode 100644 src/server/shared/Database/QueryResult.cpp create mode 100644 src/server/shared/Database/QueryResult.h create mode 100644 src/server/shared/Database/SQLStorage.cpp create mode 100644 src/server/shared/Database/SQLStorage.h create mode 100644 src/server/shared/Database/SQLStorageImpl.h create mode 100644 src/server/shared/Database/SqlDelayThread.cpp create mode 100644 src/server/shared/Database/SqlDelayThread.h create mode 100644 src/server/shared/Database/SqlOperations.cpp create mode 100644 src/server/shared/Database/SqlOperations.h create mode 100644 src/server/shared/DelayExecutor.cpp create mode 100644 src/server/shared/DelayExecutor.h create mode 100644 src/server/shared/Errors.h create mode 100644 src/server/shared/LockedQueue.h create mode 100644 src/server/shared/Log.cpp create mode 100644 src/server/shared/Log.h create mode 100644 src/server/shared/MemoryLeaks.cpp create mode 100644 src/server/shared/MemoryLeaks.h create mode 100644 src/server/shared/PacketLog.cpp create mode 100644 src/server/shared/PacketLog.h create mode 100644 src/server/shared/ProgressBar.cpp create mode 100644 src/server/shared/ProgressBar.h create mode 100644 src/server/shared/ServiceWin32.cpp create mode 100644 src/server/shared/ServiceWin32.h create mode 100644 src/server/shared/SignalHandler.h create mode 100644 src/server/shared/SystemConfig.h create mode 100644 src/server/shared/Threading.cpp create mode 100644 src/server/shared/Threading.h create mode 100644 src/server/shared/Timer.h create mode 100644 src/server/shared/Util.cpp create mode 100644 src/server/shared/Util.h create mode 100644 src/server/shared/WheatyExceptionReport.cpp create mode 100644 src/server/shared/WheatyExceptionReport.h create mode 100644 src/server/shared/WorldPacket.h create mode 100644 src/server/shared/vmap/BIH.cpp create mode 100644 src/server/shared/vmap/BIH.h create mode 100644 src/server/shared/vmap/CMakeLists.txt create mode 100644 src/server/shared/vmap/IVMapManager.h create mode 100644 src/server/shared/vmap/MapTree.cpp create mode 100644 src/server/shared/vmap/MapTree.h create mode 100644 src/server/shared/vmap/ModelInstance.cpp create mode 100644 src/server/shared/vmap/ModelInstance.h create mode 100644 src/server/shared/vmap/TileAssembler.cpp create mode 100644 src/server/shared/vmap/TileAssembler.h create mode 100644 src/server/shared/vmap/VMapDefinitions.h create mode 100644 src/server/shared/vmap/VMapFactory.cpp create mode 100644 src/server/shared/vmap/VMapFactory.h create mode 100644 src/server/shared/vmap/VMapManager2.cpp create mode 100644 src/server/shared/vmap/VMapManager2.h create mode 100644 src/server/shared/vmap/VMapTools.h create mode 100644 src/server/shared/vmap/WorldModel.cpp create mode 100644 src/server/shared/vmap/WorldModel.h create mode 100644 src/server/trinitycore/CMakeLists.txt create mode 100644 src/server/trinitycore/CliRunnable.cpp create mode 100644 src/server/trinitycore/CliRunnable.h create mode 100644 src/server/trinitycore/Main.cpp create mode 100644 src/server/trinitycore/Master.cpp create mode 100644 src/server/trinitycore/Master.h create mode 100644 src/server/trinitycore/RASocket.cpp create mode 100644 src/server/trinitycore/RASocket.h create mode 100644 src/server/trinitycore/TrinityCore.ico create mode 100644 src/server/trinitycore/TrinityCore.rc create mode 100644 src/server/trinitycore/WorldRunnable.cpp create mode 100644 src/server/trinitycore/WorldRunnable.h create mode 100644 src/server/trinitycore/resource.h create mode 100644 src/server/trinitycore/trinitycore.conf.dist create mode 100644 src/server/trinityrealm/AuthCodes.cpp create mode 100644 src/server/trinityrealm/AuthCodes.h create mode 100644 src/server/trinityrealm/AuthSocket.cpp create mode 100644 src/server/trinityrealm/AuthSocket.h create mode 100644 src/server/trinityrealm/CMakeLists.txt create mode 100644 src/server/trinityrealm/Main.cpp create mode 100644 src/server/trinityrealm/RealmAcceptor.h create mode 100644 src/server/trinityrealm/RealmList.cpp create mode 100644 src/server/trinityrealm/RealmList.h create mode 100644 src/server/trinityrealm/RealmSocket.cpp create mode 100644 src/server/trinityrealm/RealmSocket.h create mode 100644 src/server/trinityrealm/TrinityRealm.ico create mode 100644 src/server/trinityrealm/TrinityRealm.rc create mode 100644 src/server/trinityrealm/resource.h create mode 100644 src/server/trinityrealm/trinityrealm.conf.dist (limited to 'src/server') diff --git a/src/server/framework/CMakeLists.txt b/src/server/framework/CMakeLists.txt new file mode 100644 index 00000000000..bcf3603cb5e --- /dev/null +++ b/src/server/framework/CMakeLists.txt @@ -0,0 +1,11 @@ +SET(trinityframework_STAT_SRCS + Policies/ObjectLifeTime.cpp + Utilities/EventProcessor.cpp +) + +include_directories( + ${ACE_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/src/framework +) + +add_library(trinityframework STATIC ${trinityframework_STAT_SRCS}) diff --git a/src/server/framework/Dynamic/FactoryHolder.h b/src/server/framework/Dynamic/FactoryHolder.h new file mode 100644 index 00000000000..282968d6097 --- /dev/null +++ b/src/server/framework/Dynamic/FactoryHolder.h @@ -0,0 +1,63 @@ +/* + * 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_FACTORY_HOLDER +#define TRINITY_FACTORY_HOLDER + +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include "ObjectRegistry.h" +#include "Policies/SingletonImp.h" + +/** FactoryHolder holds a factory object of a specific type + */ +template +class FactoryHolder +{ + public: + typedef ObjectRegistry, Key > FactoryHolderRegistry; + typedef Trinity::Singleton FactoryHolderRepository; + + FactoryHolder(Key k) : i_key(k) {} + virtual ~FactoryHolder() {} + inline Key key() const { return i_key; } + + void RegisterSelf(void) { FactoryHolderRepository::Instance().InsertItem(this, i_key); } + void DeregisterSelf(void) { FactoryHolderRepository::Instance().RemoveItem(this, false); } + + /// Abstract Factory create method + virtual T* Create(void *data = NULL) const = 0; + private: + Key i_key; +}; + +/** Permissible is a classic way of letting the object decide + * whether how good they handle things. This is not retricted + * to factory selectors. + */ +template +class Permissible +{ + public: + virtual ~Permissible() {} + virtual int Permit(const T *) const = 0; +}; +#endif + diff --git a/src/server/framework/Dynamic/ObjectRegistry.h b/src/server/framework/Dynamic/ObjectRegistry.h new file mode 100644 index 00000000000..e6619427885 --- /dev/null +++ b/src/server/framework/Dynamic/ObjectRegistry.h @@ -0,0 +1,111 @@ +/* + * 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_OBJECTREGISTRY_H +#define TRINITY_OBJECTREGISTRY_H + +#include "Platform/Define.h" +#include "Utilities/UnorderedMap.h" +#include "Policies/Singleton.h" + +#include +#include +#include + +/** ObjectRegistry holds all registry item of the same type + */ +template +class ObjectRegistry +{ + public: + typedef std::map RegistryMapType; + + /// Returns a registry item + const T* GetRegistryItem(Key key) const + { + typename RegistryMapType::const_iterator iter = i_registeredObjects.find(key); + return( iter == i_registeredObjects.end() ? NULL : iter->second ); + } + + /// Inserts a registry item + bool InsertItem(T *obj, Key key, bool override = false) + { + typename RegistryMapType::iterator iter = i_registeredObjects.find(key); + if( iter != i_registeredObjects.end() ) + { + if( !override ) + return false; + delete iter->second; + i_registeredObjects.erase(iter); + } + + i_registeredObjects[key] = obj; + return true; + } + + /// Removes a registry item + void RemoveItem(Key key, bool delete_object = true) + { + typename RegistryMapType::iterator iter = i_registeredObjects.find(key); + if( iter != i_registeredObjects.end() ) + { + if( delete_object ) + delete iter->second; + i_registeredObjects.erase(iter); + } + } + + /// Returns true if registry contains an item + bool HasItem(Key key) const + { + return (i_registeredObjects.find(key) != i_registeredObjects.end()); + } + + /// Inefficiently return a vector of registered items + unsigned int GetRegisteredItems(std::vector &l) const + { + unsigned int sz = l.size(); + l.resize(sz + i_registeredObjects.size()); + for (typename RegistryMapType::const_iterator iter = i_registeredObjects.begin(); iter != i_registeredObjects.end(); ++iter) + l[sz++] = iter->first; + return i_registeredObjects.size(); + } + + /// Return the map of registered items + RegistryMapType const &GetRegisteredItems() const + { + return i_registeredObjects; + } + + private: + RegistryMapType i_registeredObjects; + friend class Trinity::OperatorNew >; + + // protected for friend use since it should be a singleton + ObjectRegistry() {} + ~ObjectRegistry() + { + for (typename RegistryMapType::iterator iter=i_registeredObjects.begin(); iter != i_registeredObjects.end(); ++iter) + delete iter->second; + i_registeredObjects.clear(); + } +}; +#endif + diff --git a/src/server/framework/GameSystem/Grid.h b/src/server/framework/GameSystem/Grid.h new file mode 100644 index 00000000000..65bf3c92f9d --- /dev/null +++ b/src/server/framework/GameSystem/Grid.h @@ -0,0 +1,143 @@ +/* + * 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_GRID_H +#define TRINITY_GRID_H + +/* + @class Grid + Grid is a logical segment of the game world represented inside TrinIty. + Grid is bind at compile time to a particular type of object which + we call it the object of interested. There are many types of loader, + specially, dynamic loader, static loader, or on-demand loader. There's + a subtle difference between dynamic loader and on-demand loader but + this is implementation specific to the loader class. From the + Grid's perspective, the loader meets its API requirement is suffice. +*/ + +#include "Platform/Define.h" +#include "Policies/ThreadingModel.h" +#include "TypeContainer.h" +#include "TypeContainerVisitor.h" + +// forward declaration +template class GridLoader; + +template +< +class ACTIVE_OBJECT, +class WORLD_OBJECT_TYPES, +class GRID_OBJECT_TYPES, +class ThreadModel = Trinity::SingleThreaded +> +class Grid +{ + // allows the GridLoader to access its internals + template friend class GridLoader; + public: + + /** destructor to clean up its resources. This includes unloading the + grid if it has not been unload. + */ + ~Grid() {} + + /** an object of interested enters the grid + */ + template void AddWorldObject(SPECIFIC_OBJECT *obj) + { + if(!i_objects.template insert(obj)) + assert(false); + } + + /** an object of interested exits the grid + */ + template void RemoveWorldObject(SPECIFIC_OBJECT *obj) + { + if(!i_objects.template remove(obj)) + assert(false); + } + + /** Refreshes/update the grid. This required for remote grids. + */ + void RefreshGrid(void) { /* TBI */} + + /** Locks a grid. Any object enters must wait until the grid is unlock. + */ + void LockGrid(void) { /* TBI */ } + + /** Unlocks the grid. + */ + void UnlockGrid(void) { /* TBI */ } + + /** Grid visitor for grid objects + */ + template void Visit(TypeContainerVisitor > &visitor) + { + visitor.Visit(i_container); + } + + /** Grid visitor for world objects + */ + template void Visit(TypeContainerVisitor > &visitor) + { + visitor.Visit(i_objects); + } + + /** Returns the number of object within the grid. + */ + unsigned int ActiveObjectsInGrid(void) const { return /*m_activeGridObjects.size()+*/i_objects.template Count(); } + + /** Inserts a container type object into the grid. + */ + template void AddGridObject(SPECIFIC_OBJECT *obj) + { + if(!i_container.template insert(obj)) + assert(false); + } + + /** Removes a containter type object from the grid + */ + template void RemoveGridObject(SPECIFIC_OBJECT *obj) + { + if(!i_container.template remove(obj)) + assert(false); + } + + /*bool NoWorldObjectInGrid() const + { + return i_objects.GetElements().isEmpty(); + } + + bool NoGridObjectInGrid() const + { + return i_container.GetElements().isEmpty(); + }*/ + private: + + typedef typename ThreadModel::Lock Guard; + typedef typename ThreadModel::VolatileType VolatileType; + + TypeMapContainer i_container; + TypeMapContainer i_objects; + //typedef std::set ActiveGridObjects; + //ActiveGridObjects m_activeGridObjects; +}; +#endif + diff --git a/src/server/framework/GameSystem/GridLoader.h b/src/server/framework/GameSystem/GridLoader.h new file mode 100644 index 00000000000..03fa0f5b813 --- /dev/null +++ b/src/server/framework/GameSystem/GridLoader.h @@ -0,0 +1,79 @@ +/* + * 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_GRIDLOADER_H +#define TRINITY_GRIDLOADER_H + +/** + @class GridLoader + The GridLoader is working in conjuction with the Grid and responsible + for loading and unloading object-types (one or more) when objects + enters a grid. Unloading is scheduled and might be canceled if + an interested object re-enters. GridLoader does not do the actuall + loading and unloading but implements as a template pattern that + delicate its loading and unloading for the actualy loader and unloader. + GridLoader manages the grid (both local and remote). + */ + +#include "Platform/Define.h" +#include "Grid.h" +#include "TypeContainerVisitor.h" + +template +< +class ACTIVE_OBJECT, +class WORLD_OBJECT_TYPES, +class GRID_OBJECT_TYPES +> +class GridLoader +{ + public: + + /** Loads the grid + */ + template + void Load(Grid &grid, LOADER &loader) + { + grid.LockGrid(); + loader.Load(grid); + grid.UnlockGrid(); + } + + /** Stop the grid + */ + template + void Stop(Grid &grid, STOPER &stoper) + { + grid.LockGrid(); + stoper.Stop(grid); + grid.UnlockGrid(); + } + /** Unloads the grid + */ + template + void Unload(Grid &grid, UNLOADER &unloader) + { + grid.LockGrid(); + unloader.Unload(grid); + grid.UnlockGrid(); + } +}; +#endif + diff --git a/src/server/framework/GameSystem/GridRefManager.h b/src/server/framework/GameSystem/GridRefManager.h new file mode 100644 index 00000000000..79799105fb7 --- /dev/null +++ b/src/server/framework/GameSystem/GridRefManager.h @@ -0,0 +1,44 @@ +/* + * 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 _GRIDREFMANAGER +#define _GRIDREFMANAGER + +#include "Utilities/LinkedReference/RefManager.h" + +template +class GridReference; + +template +class GridRefManager : public RefManager, OBJECT> +{ + public: + typedef LinkedListHead::Iterator< GridReference > iterator; + + GridReference* getFirst() { return (GridReference*)RefManager, OBJECT>::getFirst(); } + GridReference* getLast() { return (GridReference*)RefManager, OBJECT>::getLast(); } + + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(NULL); } + iterator rbegin() { return iterator(getLast()); } + iterator rend() { return iterator(NULL); } +}; +#endif + diff --git a/src/server/framework/GameSystem/GridReference.h b/src/server/framework/GameSystem/GridReference.h new file mode 100644 index 00000000000..d2e3a455895 --- /dev/null +++ b/src/server/framework/GameSystem/GridReference.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 _GRIDREFERENCE_H +#define _GRIDREFERENCE_H + +#include "Utilities/LinkedReference/Reference.h" + +template +class GridRefManager; + +template +class GridReference : public Reference, OBJECT> +{ + protected: + void targetObjectBuildLink() + { + // called from link() + this->getTarget()->insertFirst(this); + this->getTarget()->incSize(); + } + void targetObjectDestroyLink() + { + // called from unlink() + if(this->isValid()) this->getTarget()->decSize(); + } + void sourceObjectDestroyLink() + { + // called from invalidate() + this->getTarget()->decSize(); + } + public: + GridReference() : Reference, OBJECT>() {} + ~GridReference() { this->unlink(); } + GridReference *next() { return (GridReference*)Reference, OBJECT>::next(); } +}; +#endif + diff --git a/src/server/framework/GameSystem/NGrid.h b/src/server/framework/GameSystem/NGrid.h new file mode 100644 index 00000000000..3810286e123 --- /dev/null +++ b/src/server/framework/GameSystem/NGrid.h @@ -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 + */ + +#ifndef TRINITY_NGRID_H +#define TRINITY_NGRID_H + +/** NGrid is nothing more than a wrapper of the Grid with an NxN cells + */ + +#include "GameSystem/Grid.h" +#include "GameSystem/GridReference.h" +#include "Timer.h" +#include "Util.h" + +#define DEFAULT_VISIBILITY_NOTIFY_PERIOD 1000 + +class GridInfo +{ +public: + GridInfo() + : i_timer(0), i_unloadActiveLockCount(0), i_unloadExplicitLock(false), i_unloadReferenceLock(false), + vis_Update(0, irand(0,DEFAULT_VISIBILITY_NOTIFY_PERIOD)) {} + GridInfo(time_t expiry, bool unload = true ) + : i_timer(expiry), i_unloadActiveLockCount(0), i_unloadExplicitLock(!unload), i_unloadReferenceLock(false), + vis_Update(0, irand(0,DEFAULT_VISIBILITY_NOTIFY_PERIOD)) {} + const TimeTracker& getTimeTracker() const { return i_timer; } + bool getUnloadLock() const { return i_unloadActiveLockCount || i_unloadExplicitLock || i_unloadReferenceLock; } + void setUnloadExplicitLock( bool on ) { i_unloadExplicitLock = on; } + void setUnloadReferenceLock( bool on ) { i_unloadReferenceLock = on; } + void incUnloadActiveLock() { ++i_unloadActiveLockCount; } + void decUnloadActiveLock() { if(i_unloadActiveLockCount) --i_unloadActiveLockCount; } + + void setTimer(const TimeTracker& pTimer) { i_timer = pTimer; } + void ResetTimeTracker(time_t interval) { i_timer.Reset(interval); } + void UpdateTimeTracker(time_t diff) { i_timer.Update(diff); } + PeriodicTimer& getRelocationTimer() { return vis_Update; } +private: + TimeTracker i_timer; + PeriodicTimer vis_Update; + + uint16 i_unloadActiveLockCount : 16; // lock from active object spawn points (prevent clone loading) + bool i_unloadExplicitLock : 1; // explicit manual lock or config setting + bool i_unloadReferenceLock : 1; // lock from instance map copy +}; + +typedef enum +{ + GRID_STATE_INVALID = 0, + GRID_STATE_ACTIVE = 1, + GRID_STATE_IDLE = 2, + GRID_STATE_REMOVAL= 3, + MAX_GRID_STATE = 4 +} grid_state_t; + +template +< +unsigned int N, +class ACTIVE_OBJECT, +class WORLD_OBJECT_TYPES, +class GRID_OBJECT_TYPES, +class ThreadModel = Trinity::SingleThreaded +> +class NGrid +{ + public: + + typedef Grid GridType; + NGrid(uint32 id, int32 x, int32 y, time_t expiry, bool unload = true) + : i_gridId(id), i_x(x), i_y(y), i_cellstate(GRID_STATE_INVALID), i_GridObjectDataLoaded(false) + { + i_GridInfo = GridInfo(expiry, unload); + } + + const GridType& operator()(unsigned short x, unsigned short y) const + { + ASSERT(x < N); + ASSERT(y < N); + return i_cells[x][y]; + } + + GridType& operator()(unsigned short x, unsigned short y) + { + ASSERT(x < N); + ASSERT(y < N); + return i_cells[x][y]; + } + + const uint32& GetGridId(void) const { return i_gridId; } + void SetGridId(const uint32 id) const { i_gridId = id; } + grid_state_t GetGridState(void) const { return i_cellstate; } + void SetGridState(grid_state_t s) { i_cellstate = s; } + int32 getX() const { return i_x; } + int32 getY() const { return i_y; } + + void link(GridRefManager >* pTo) + { + i_Reference.link(pTo, this); + } + bool isGridObjectDataLoaded() const { return i_GridObjectDataLoaded; } + void setGridObjectDataLoaded(bool pLoaded) { i_GridObjectDataLoaded = pLoaded; } + + GridInfo* getGridInfoRef() { return &i_GridInfo; } + const TimeTracker& getTimeTracker() const { return i_GridInfo.getTimeTracker(); } + bool getUnloadLock() const { return i_GridInfo.getUnloadLock(); } + void setUnloadExplicitLock( bool on ) { i_GridInfo.setUnloadExplicitLock(on); } + void setUnloadReferenceLock( bool on ) { i_GridInfo.setUnloadReferenceLock(on); } + void incUnloadActiveLock() { i_GridInfo.incUnloadActiveLock(); } + void decUnloadActiveLock() { i_GridInfo.decUnloadActiveLock(); } + void ResetTimeTracker(time_t interval) { i_GridInfo.ResetTimeTracker(interval); } + void UpdateTimeTracker(time_t diff) { i_GridInfo.UpdateTimeTracker(diff); } + + template void AddWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj) + { + getGridType(x, y).AddWorldObject(obj); + } + + template void RemoveWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj) + { + getGridType(x, y).RemoveWorldObject(obj); + } + + template void Visit(TypeContainerVisitor > &visitor) + { + for (unsigned int x=0; x < N; ++x) + for (unsigned int y=0; y < N; ++y) + getGridType(x, y).Visit(visitor); + } + + template void Visit(const uint32 &x, const uint32 &y, TypeContainerVisitor > &visitor) + { + getGridType(x, y).Visit(visitor); + } + + unsigned int ActiveObjectsInGrid(void) const + { + unsigned int count=0; + for (unsigned int x=0; x < N; ++x) + for (unsigned int y=0; y < N; ++y) + count += i_cells[x][y].ActiveObjectsInGrid(); + return count; + } + + template bool AddGridObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj) + { + return getGridType(x, y).AddGridObject(obj); + } + + template bool RemoveGridObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj) + { + return getGridType(x, y).RemoveGridObject(obj); + } + + private: + + GridType& getGridType(const uint32& x, const uint32& y) + { + ASSERT(x < N); + ASSERT(y < N); + return i_cells[x][y]; + } + + uint32 i_gridId; + GridInfo i_GridInfo; + GridReference > i_Reference; + int32 i_x; + int32 i_y; + grid_state_t i_cellstate; + GridType i_cells[N][N]; + bool i_GridObjectDataLoaded; +}; +#endif + diff --git a/src/server/framework/GameSystem/TypeContainer.h b/src/server/framework/GameSystem/TypeContainer.h new file mode 100644 index 00000000000..c2c9b4fcdea --- /dev/null +++ b/src/server/framework/GameSystem/TypeContainer.h @@ -0,0 +1,126 @@ +/* + * 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_TYPECONTAINER_H +#define TRINITY_TYPECONTAINER_H + +/* + * Here, you'll find a series of containers that allow you to hold multiple + * types of object at the same time. + */ + +#include +#include +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include "GameSystem/GridRefManager.h" + +/* + * @class ContainerMapList is a mulit-type container for map elements + * By itself its meaningless but collaborate along with TypeContainers, + * it become the most powerfully container in the whole system. + */ +template struct ContainerMapList +{ + //std::map _element; + GridRefManager _element; +}; + +template<> struct ContainerMapList /* nothing is in type null */ +{ +}; +template struct ContainerMapList > +{ + ContainerMapList _elements; + ContainerMapList _TailElements; +}; + +/* + * @class ContaierArrayList is a multi-type container for + * array of elements. + */ +template struct ContainerArrayList +{ + std::vector _element; +}; + +// termination condition +template<> struct ContainerArrayList {}; +// recursion +template struct ContainerArrayList > +{ + ContainerArrayList _elements; + ContainerArrayList _TailElements; +}; + +/* + * @class ContainerList is a simple list of different types of elements + * + */ +template struct ContainerList +{ + OBJECT _element; +}; + +/* TypeNull is underfined */ +template<> struct ContainerList {}; +template struct ContainerList > +{ + ContainerList _elements; + ContainerMapList _TailElements; +}; + +#include "TypeContainerFunctions.h" + +/* + * @class TypeMapContainer contains a fixed number of types and is + * determined at compile time. This is probably the most complicated + * class and do its simplest thing, that is, holds objects + * of different types. + */ + +template +class TypeMapContainer +{ + public: + template size_t Count() const { return Trinity::Count(i_elements, (SPECIFIC_TYPE*)NULL); } + + /// inserts a specific object into the container + template bool insert(SPECIFIC_TYPE *obj) + { + SPECIFIC_TYPE* t = Trinity::Insert(i_elements, obj); + return (t != NULL); + } + + /// Removes the object from the container, and returns the removed object + template bool remove(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Trinity::Remove(i_elements, obj); + return (t != NULL); + } + + ContainerMapList & GetElements(void) { return i_elements; } + const ContainerMapList & GetElements(void) const { return i_elements;} + + private: + ContainerMapList i_elements; +}; +#endif + diff --git a/src/server/framework/GameSystem/TypeContainerFunctions.h b/src/server/framework/GameSystem/TypeContainerFunctions.h new file mode 100644 index 00000000000..edfbb40e659 --- /dev/null +++ b/src/server/framework/GameSystem/TypeContainerFunctions.h @@ -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 + */ + +#ifndef TYPECONTAINER_FUNCTIONS_H +#define TYPECONTAINER_FUNCTIONS_H + +/* + * Here you'll find a list of helper functions to make + * the TypeContainer usefull. Without it, its hard + * to access or mutate the container. + */ + +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include + +namespace Trinity +{ + /* ContainerMapList Helpers */ + // count functions + template size_t Count(const ContainerMapList &elements, SPECIFIC_TYPE* /*fake*/) + { + return elements._element.getSize(); + }; + + template size_t Count(const ContainerMapList &/*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template size_t Count(const ContainerMapList &/*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + { + return Count(elements._elements,fake); + } + + template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + { + return Count(elements._TailElements, fake); + } + + // non-const insert functions + template SPECIFIC_TYPE* Insert(ContainerMapList &elements, SPECIFIC_TYPE *obj) + { + //elements._element[hdl] = obj; + obj->GetGridRef().link(&elements._element, obj); + return obj; + }; + + template SPECIFIC_TYPE* Insert(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return NULL; + } + + // this is a missed + template SPECIFIC_TYPE* Insert(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return NULL; // a missed + } + + // Recursion + template SPECIFIC_TYPE* Insert(ContainerMapList >&elements, SPECIFIC_TYPE *obj) + { + SPECIFIC_TYPE* t= Insert(elements._elements, obj); + return (t != NULL ? t : Insert(elements._TailElements, obj)); + } + + // non-const remove method + template SPECIFIC_TYPE* Remove(ContainerMapList & /*elements*/, SPECIFIC_TYPE *obj) + { + obj->GetGridRef().unlink(); + return obj; + } + + template SPECIFIC_TYPE* Remove(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return NULL; + } + + // this is a missed + template SPECIFIC_TYPE* Remove(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return NULL; // a missed + } + + template SPECIFIC_TYPE* Remove(ContainerMapList > &elements, SPECIFIC_TYPE *obj) + { + // The head element is bad + SPECIFIC_TYPE* t = Remove(elements._elements, obj); + return ( t != NULL ? t : Remove(elements._TailElements, obj) ); + } + +} +#endif + diff --git a/src/server/framework/GameSystem/TypeContainerFunctionsPtr.h b/src/server/framework/GameSystem/TypeContainerFunctionsPtr.h new file mode 100644 index 00000000000..2affcc457cc --- /dev/null +++ b/src/server/framework/GameSystem/TypeContainerFunctionsPtr.h @@ -0,0 +1,170 @@ +/* + * 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 TYPECONTAINER_FUNCTIONS_PTR_H +#define TYPECONTAINER_FUNCTIONS_PTR_H + +/* + * Here you'll find a list of helper functions to make + * the TypeContainer usefull. Without it, its hard + * to access or mutate the container. + */ + +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include + +namespace Trinity +{ + /* ContainerMapList Helpers */ + // count functions + // template size_t Count(const ContainerMapList &elements, CountedPtr* /*fake*/) + // { + // return elements._element.size(); + // }; + // + // template size_t Count(const ContainerMapList &elements, CountedPtr* /*fake*/) + // { + // return 0; + // } + // + // template size_t Count(const ContainerMapList &elements, CountedPtr* /*fake*/) + // { + // return 0; + // } + // + // template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + // { + // return Count(elements._elements,fake); + // } + // + // template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + // { + // return Count(elements._TailElements, fake); + // } + + // non-const find functions + template CountedPtr& Find(ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + typename std::map >::iterator iter = elements._element.find(hdl); + return (iter == elements._element.end() ? NullPtr((SPECIFIC_TYPE*)NULL) : iter->second); + }; + + template CountedPtr& Find(ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL);// terminate recursion + } + + template CountedPtr& Find(ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL);// this is a missed + } + + template CountedPtr& Find(ContainerMapList >&elements, OBJECT_HANDLE hdl, CountedPtr* fake) + { + CountedPtr &t = Find(elements._elements, hdl,fake); + return (!t ? Find(elements._TailElements, hdl,fake) : t); + } + + // const find functions + template const CountedPtr& Find(const ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + typename CountedPtr::iterator iter = elements._element.find(hdl); + return (iter == elements._element.end() ? NullPtr((SPECIFIC_TYPE*)NULL) : iter->second); + }; + + template const CountedPtr& Find(const ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL); + } + + template const CountedPtr& Find(const ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL); + } + + template CountedPtr& Find(const ContainerMapList >&elements, OBJECT_HANDLE hdl, CountedPtr* fake) + { + CountedPtr &t = Find(elements._elements, hdl,fake); + if(!t) + t = Find(elements._TailElement, hdl,fake); + + return t; + } + + // non-const insert functions + template CountedPtr& Insert(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + elements._element[hdl] = obj; + return obj; + }; + + template CountedPtr& Insert(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return NullPtr((SPECIFIC_TYPE*)NULL); + } + + // this is a missed + template CountedPtr& Insert(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return NullPtr((SPECIFIC_TYPE*)NULL);// a missed + } + + // Recursion + template CountedPtr& Insert(ContainerMapList >&elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + CountedPtr &t= Insert(elements._elements, obj, hdl); + return (!t ? Insert(elements._TailElements, obj, hdl) : t); + } + + // non-const remove method + template bool Remove(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + typename std::map >::iterator iter = elements._element.find(hdl); + if( iter != elements._element.end() ) + { + elements._element.erase(iter); + return true; + } + + return false; // found... terminate the search + } + + template bool Remove(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return false; + } + + // this is a missed + template bool Remove(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return false; + } + + template bool Remove(ContainerMapList > &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + // The head element is bad + bool t = Remove(elements._elements, obj, hdl); + return ( !t ? Remove(elements._TailElements, obj, hdl) : t ); + } + +} +#endif + diff --git a/src/server/framework/GameSystem/TypeContainerVisitor.h b/src/server/framework/GameSystem/TypeContainerVisitor.h new file mode 100644 index 00000000000..f15075e5afd --- /dev/null +++ b/src/server/framework/GameSystem/TypeContainerVisitor.h @@ -0,0 +1,119 @@ +/* + * 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_TYPECONTAINERVISITOR_H +#define TRINITY_TYPECONTAINERVISITOR_H + +/* + * @class TypeContainerVisitor is implemented as a visitor pattern. It is + * a visitor to the TypeContainerList or TypeContainerMapList. The visitor has + * to overload its types as a visit method is called. + */ + +#include "Platform/Define.h" +#include "TypeContainer.h" + +// forward declaration +template class TypeContainerVisitor; + +// visitor helper +template void VisitorHelper(VISITOR &v, TYPE_CONTAINER &c) +{ + v.Visit(c); +}; + +// terminate condition for container list +template void VisitorHelper(VISITOR &v, ContainerList &c) +{ +} + +template void VisitorHelper(VISITOR &v, ContainerList &c) +{ + v.Visit(c._element); +} + +// recursion for container list +template void VisitorHelper(VISITOR &v, ContainerList > &c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// terminate condition container map list +template void VisitorHelper(VISITOR &/*v*/, ContainerMapList &/*c*/) +{ +} + +template void VisitorHelper(VISITOR &v, ContainerMapList &c) +{ + v.Visit(c._element); +} + +// recursion container map list +template void VisitorHelper(VISITOR &v, ContainerMapList > &c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// array list +template void VisitorHelper(VISITOR &v, ContainerArrayList &c) +{ + v.Visit(c._element); +} + +template void VisitorHelper(VISITOR &/*v*/, ContainerArrayList &/*c*/) +{ +} + +// recursion +template void VisitorHelper(VISITOR &v, ContainerArrayList > &c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// for TypeMapContainer +template void VisitorHelper(VISITOR &v, TypeMapContainer &c) +{ + VisitorHelper(v, c.GetElements()); +} + +template +class TypeContainerVisitor +{ + public: + TypeContainerVisitor(VISITOR &v) : i_visitor(v) {} + + void Visit(TYPE_CONTAINER &c) + { + VisitorHelper(i_visitor, c); + } + + void Visit(const TYPE_CONTAINER &c) const + { + VisitorHelper(i_visitor, c); + } + + private: + VISITOR &i_visitor; +}; +#endif + diff --git a/src/server/framework/Network/SocketDefines.h b/src/server/framework/Network/SocketDefines.h new file mode 100644 index 00000000000..49366097ae6 --- /dev/null +++ b/src/server/framework/Network/SocketDefines.h @@ -0,0 +1,49 @@ +/* + * 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_SOCKETDEFINES_H +#define TRINITY_SOCKETDEFINES_H + +#ifdef WIN32 + +/* Windows socket definitions + */ +#define FD_SETSIZE 1024 +#include +#include + +typedef SOCKET SocketHandle; +typedef fd_set SelectSet; + +#else + +/* The unix socket definitions + */ +#include +#include +#ifdef __APPLE_CC__ +#include +#endif + +typedef int SocketHandle; +typedef fd_set SelectSet; +#endif +#endif + diff --git a/src/server/framework/Platform/CompilerDefs.h b/src/server/framework/Platform/CompilerDefs.h new file mode 100644 index 00000000000..fb7dbfe4caa --- /dev/null +++ b/src/server/framework/Platform/CompilerDefs.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_COMPILERDEFS_H +#define TRINITY_COMPILERDEFS_H + +#define PLATFORM_WINDOWS 0 +#define PLATFORM_UNIX 1 +#define PLATFORM_APPLE 2 +#define PLATFORM_INTEL 3 + +// must be first (win 64 also define WIN32) +#if defined( _WIN64 ) +# define PLATFORM PLATFORM_WINDOWS +#elif defined( __WIN32__ ) || defined( WIN32 ) || defined( _WIN32 ) +# define PLATFORM PLATFORM_WINDOWS +#elif defined( __APPLE_CC__ ) +# define PLATFORM PLATFORM_APPLE +#elif defined( __INTEL_COMPILER ) +# define PLATFORM PLATFORM_INTEL +#else +# define PLATFORM PLATFORM_UNIX +#endif + +#define COMPILER_MICROSOFT 0 +#define COMPILER_GNU 1 +#define COMPILER_BORLAND 2 +#define COMPILER_INTEL 3 + +#ifdef _MSC_VER +# define COMPILER COMPILER_MICROSOFT +#elif defined( __BORLANDC__ ) +# define COMPILER COMPILER_BORLAND +#elif defined( __INTEL_COMPILER ) +# define COMPILER COMPILER_INTEL +#elif defined( __GNUC__ ) +# define COMPILER COMPILER_GNU +#else +# pragma error "FATAL ERROR: Unknown compiler." +#endif + +#if COMPILER == COMPILER_MICROSOFT +# pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data +# pragma warning( disable : 4786 ) // identifier was truncated to '255' characters in the debug information +#endif +#endif + diff --git a/src/server/framework/Platform/Define.h b/src/server/framework/Platform/Define.h new file mode 100644 index 00000000000..9285bf289f9 --- /dev/null +++ b/src/server/framework/Platform/Define.h @@ -0,0 +1,85 @@ +/* + * 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_DEFINE_H +#define TRINITY_DEFINE_H + +#include + +#include +#include + +#include "Platform/CompilerDefs.h" + +#define TRINITY_LITTLEENDIAN 0 +#define TRINITY_BIGENDIAN 1 + +#if !defined(TRINITY_ENDIAN) +# if defined (ACE_BIG_ENDIAN) +# define TRINITY_ENDIAN TRINITY_BIGENDIAN +# else //ACE_BYTE_ORDER != ACE_BIG_ENDIAN +# define TRINITY_ENDIAN TRINITY_LITTLEENDIAN +# endif //ACE_BYTE_ORDER +#endif //TRINITY_ENDIAN + +#if PLATFORM == PLATFORM_WINDOWS +# define TRINITY_PATH_MAX MAX_PATH +# ifndef DECLSPEC_NORETURN +# define DECLSPEC_NORETURN __declspec(noreturn) +# endif //DECLSPEC_NORETURN +#else //PLATFORM != PLATFORM_WINDOWS +# define TRINITY_PATH_MAX PATH_MAX +# define DECLSPEC_NORETURN +#endif //PLATFORM + +#if !defined(DEBUG) +# define TRINITY_INLINE inline +#else //DEBUG +# if !defined(TRINITY_DEBUG) +# define TRINITY_DEBUG +# endif //TRINITY_DEBUG +# define TRINITY_INLINE +#endif //!DEBUG + +#if COMPILER == COMPILER_GNU +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_PRINTF(F,V) __attribute__ ((format (printf, F, V))) +#else //COMPILER != COMPILER_GNU +# define ATTR_NORETURN +# define ATTR_PRINTF(F,V) +#endif //COMPILER == COMPILER_GNU + +typedef ACE_INT64 int64; +typedef ACE_INT32 int32; +typedef ACE_INT16 int16; +typedef ACE_INT8 int8; +typedef ACE_UINT64 uint64; +typedef ACE_UINT32 uint32; +typedef ACE_UINT16 uint16; +typedef ACE_UINT8 uint8; + +#if COMPILER != COMPILER_MICROSOFT +typedef uint16 WORD; +typedef uint32 DWORD; +#endif //COMPILER + +typedef uint64 OBJECT_HANDLE; + +#endif //TRINITY_DEFINE_H diff --git a/src/server/framework/Policies/CreationPolicy.h b/src/server/framework/Policies/CreationPolicy.h new file mode 100644 index 00000000000..8552ce7da52 --- /dev/null +++ b/src/server/framework/Policies/CreationPolicy.h @@ -0,0 +1,110 @@ +/* + * 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_CREATIONPOLICY_H +#define TRINITY_CREATIONPOLICY_H + +#include +#include "Platform/Define.h" + +namespace Trinity +{ + /** + * OperatorNew policy creates an object on the heap using new. + */ + template + class OperatorNew + { + public: + static T* Create(void) { return (new T); } + static void Destroy(T *obj) { delete obj; } + }; + + /** + * LocalStaticCreation policy creates an object on the stack + * the first time call Create. + */ + template + class LocalStaticCreation + { + union MaxAlign + { + char t_[sizeof(T)]; + short int shortInt_; + int int_; + long int longInt_; + float float_; + double double_; + long double longDouble_; + struct Test; + int Test::* pMember_; + int (Test::*pMemberFn_)(int); + }; + public: + static T* Create(void) + { + static MaxAlign si_localStatic; + return new(&si_localStatic) T; + } + + static void Destroy(T *obj) { obj->~T(); } + }; + + /** + * CreateUsingMalloc by pass the memory manger. + */ + template + class CreateUsingMalloc + { + public: + static T* Create() + { + void* p = ::malloc(sizeof(T)); + if (!p) return 0; + return new(p) T; + } + + static void Destroy(T* p) + { + p->~T(); + ::free(p); + } + }; + + /** + * CreateOnCallBack creates the object base on the call back. + */ + template + class CreateOnCallBack + { + public: + static T* Create() + { + return CALL_BACK::createCallBack(); + } + + static void Destroy(T *p) + { + CALL_BACK::destroyCallBack(p); + } + }; +} +#endif + diff --git a/src/server/framework/Policies/ObjectLifeTime.cpp b/src/server/framework/Policies/ObjectLifeTime.cpp new file mode 100644 index 00000000000..fd16873ae92 --- /dev/null +++ b/src/server/framework/Policies/ObjectLifeTime.cpp @@ -0,0 +1,36 @@ +/* + * 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 +#include "ObjectLifeTime.h" + +namespace Trinity +{ + extern "C" void external_wrapper(void *p) + { + std::atexit( (void (*)())p ); + } + + void at_exit( void (*func)() ) + { + external_wrapper((void*)func); + } +} + diff --git a/src/server/framework/Policies/ObjectLifeTime.h b/src/server/framework/Policies/ObjectLifeTime.h new file mode 100644 index 00000000000..61b90b59f6e --- /dev/null +++ b/src/server/framework/Policies/ObjectLifeTime.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_OBJECTLIFETIME_H +#define TRINITY_OBJECTLIFETIME_H + +#include +#include "Platform/Define.h" + +typedef void (* Destroyer)(void); + +namespace Trinity +{ + void at_exit( void (*func)() ); + + template + class ObjectLifeTime + { + public: + static void ScheduleCall(void (*destroyer)() ) + { + at_exit( destroyer ); + } + + DECLSPEC_NORETURN static void OnDeadReference(void) ATTR_NORETURN; + + }; + + template + void ObjectLifeTime::OnDeadReference(void) // We don't handle Dead Reference for now + { + throw std::runtime_error("Dead Reference"); + } +} +#endif + diff --git a/src/server/framework/Policies/Singleton.h b/src/server/framework/Policies/Singleton.h new file mode 100644 index 00000000000..da898558ca5 --- /dev/null +++ b/src/server/framework/Policies/Singleton.h @@ -0,0 +1,65 @@ +/* + * 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_SINGLETON_H +#define TRINITY_SINGLETON_H + +/** + * @brief class Singleton + */ + +#include "CreationPolicy.h" +#include "ThreadingModel.h" +#include "ObjectLifeTime.h" + +namespace Trinity +{ + template + < + typename T, + class ThreadingModel = Trinity::SingleThreaded, + class CreatePolicy = Trinity::OperatorNew, + class LifeTimePolicy = Trinity::ObjectLifeTime + > + class Singleton + { + public: + static T& Instance(); + + protected: + Singleton() {}; + + private: + + // Prohibited actions...this does not prevent hijacking. + Singleton(const Singleton &); + Singleton& operator=(const Singleton &); + + // Singleton Helpers + static void DestroySingleton(); + + // data structure + typedef typename ThreadingModel::Lock Guard; + static T *si_instance; + static bool si_destroyed; + }; +} +#endif + diff --git a/src/server/framework/Policies/SingletonImp.h b/src/server/framework/Policies/SingletonImp.h new file mode 100644 index 00000000000..3e985cd5c64 --- /dev/null +++ b/src/server/framework/Policies/SingletonImp.h @@ -0,0 +1,93 @@ +/* + * 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_SINGLETONIMPL_H +#define TRINITY_SINGLETONIMPL_H + +#include "Singleton.h" + +// avoid the using namespace here cuz +// its a .h file afterall + +template +< +typename T, +class ThreadingModel, +class CreatePolicy, +class LifeTimePolicy +> +T& +Trinity::Singleton::Instance() +{ + if( !si_instance ) + { + // double-checked Locking pattern + Guard(); + if( !si_instance ) + { + if( si_destroyed ) + { + si_destroyed = false; + LifeTimePolicy::OnDeadReference(); + } + si_instance = CreatePolicy::Create(); + LifeTimePolicy::ScheduleCall(&DestroySingleton); + } + } + + return *si_instance; +} + +template +< +typename T, +class ThreadingModel, +class CreatePolicy, +class LifeTimePolicy +> +void +Trinity::Singleton::DestroySingleton() +{ + CreatePolicy::Destroy(si_instance); + si_instance = NULL; + si_destroyed = true; +} + +#define INSTANTIATE_SINGLETON_1(TYPE) \ + template class Trinity::Singleton, Trinity::OperatorNew, Trinity::ObjectLifeTime >; \ + template<> TYPE* Trinity::Singleton, Trinity::OperatorNew, Trinity::ObjectLifeTime >::si_instance = 0; \ + template<> bool Trinity::Singleton, Trinity::OperatorNew, Trinity::ObjectLifeTime >::si_destroyed = false + +#define INSTANTIATE_SINGLETON_2(TYPE, THREADINGMODEL) \ + template class Trinity::Singleton, Trinity::ObjectLifeTime >; \ + template<> TYPE* Trinity::Singleton, Trinity::ObjectLifeTime >::si_instance = 0; \ + template<> bool Trinity::Singleton, Trinity::ObjectLifeTime >::si_destroyed = false + +#define INSTANTIATE_SINGLETON_3(TYPE, THREADINGMODEL, CREATIONPOLICY ) \ + template class Trinity::Singleton >; \ + template<> TYPE* Trinity::Singleton >::si_instance = 0; \ + template<> bool Trinity::Singleton >::si_destroyed = false + +#define INSTANTIATE_SINGLETON_4(TYPE, THREADINGMODEL, CREATIONPOLICY, OBJECTLIFETIME) \ + template class Trinity::Singleton; \ + template<> TYPE* Trinity::Singleton::si_instance = 0; \ + template<> bool Trinity::Singleton::si_destroyed = false +#endif + diff --git a/src/server/framework/Policies/ThreadingModel.h b/src/server/framework/Policies/ThreadingModel.h new file mode 100644 index 00000000000..d4c5e9a2333 --- /dev/null +++ b/src/server/framework/Policies/ThreadingModel.h @@ -0,0 +1,130 @@ +/* + * 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_THREADINGMODEL_H +#define TRINITY_THREADINGMODEL_H + +/** + * @class ThreadingModel + * + */ + +#include "Platform/Define.h" + +namespace Trinity +{ + inline void Guard(void *) {} + + template class GeneralLock + { + public: + GeneralLock(MUTEX &m) : i_mutex(m) + { + i_mutex.acquire(); + } + + ~GeneralLock() + { + i_mutex.release(); + } + private: + GeneralLock(const GeneralLock &); + GeneralLock& operator=(const GeneralLock &); + MUTEX &i_mutex; + }; + + template + class SingleThreaded + { + public: + + struct Lock // empty object + { + Lock() {} + Lock(const T &) {} + Lock(const SingleThreaded &) // for single threaded we ignore this + { + } + }; + + typedef T VolatileType; + }; + + // object level lockable + template + class ObjectLevelLockable + { + public: + ObjectLevelLockable() : i_mtx() {} + + friend class Lock; + + class Lock + { + public: + Lock(ObjectLevelLockable &host) : i_lock(host.i_mtx) + { + } + + private: + GeneralLock i_lock; + }; + + typedef volatile T VolatileType; + + private: + // prevent the compiler creating a copy construct + ObjectLevelLockable(const ObjectLevelLockable &); + ObjectLevelLockable& operator=(const ObjectLevelLockable &); + + MUTEX i_mtx; + }; + + template + class ClassLevelLockable + { + public: + class Lock; + friend class Lock; + typedef volatile T VolatileType; + + ClassLevelLockable() {} + + class Lock + { + public: + Lock(T& /*host*/) { ClassLevelLockable::si_mtx.acquire(); } + Lock(ClassLevelLockable &) { ClassLevelLockable::si_mtx.acquire(); } + Lock() { ClassLevelLockable::si_mtx.acquire(); } + ~Lock() { ClassLevelLockable::si_mtx.release(); } + }; + + private: + static MUTEX si_mtx; + }; + +} + +template MUTEX Trinity::ClassLevelLockable::si_mtx; + +#define INSTANTIATE_CLASS_MUTEX(CTYPE,MUTEX) \ + template class Trinity::ClassLevelLockable +#endif + diff --git a/src/server/framework/Utilities/ByteConverter.h b/src/server/framework/Utilities/ByteConverter.h new file mode 100644 index 00000000000..f8b6bd72498 --- /dev/null +++ b/src/server/framework/Utilities/ByteConverter.h @@ -0,0 +1,66 @@ +/* + * 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_BYTECONVERTER_H +#define TRINITY_BYTECONVERTER_H + +/** ByteConverter reverse your byte order. This is use + for cross platform where they have different endians. + */ + +#include +#include + +namespace ByteConverter +{ + template + inline void convert(char *val) + { + std::swap(*val, *(val + T - 1)); + convert(val + 1); + } + + template<> inline void convert<0>(char *) {} + template<> inline void convert<1>(char *) {} // ignore central byte + + template inline void apply(T *val) + { + convert((char *)(val)); + } +} + +#if TRINITY_ENDIAN == TRINITY_BIGENDIAN +template inline void EndianConvert(T& val) { ByteConverter::apply(&val); } +template inline void EndianConvertReverse(T&) { } +#else +template inline void EndianConvert(T&) { } +template inline void EndianConvertReverse(T& val) { ByteConverter::apply(&val); } +#endif + +template void EndianConvert(T*); // will generate link error +template void EndianConvertReverse(T*); // will generate link error + +inline void EndianConvert(uint8&) { } +inline void EndianConvert( int8&) { } +inline void EndianConvertReverse(uint8&) { } +inline void EndianConvertReverse( int8&) { } + +#endif + diff --git a/src/server/framework/Utilities/Callback.h b/src/server/framework/Utilities/Callback.h new file mode 100644 index 00000000000..d2e2c36851a --- /dev/null +++ b/src/server/framework/Utilities/Callback.h @@ -0,0 +1,386 @@ +/* + * 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_CALLBACK_H +#define TRINITY_CALLBACK_H + +/// ------------ BASE CLASSES ------------ + +namespace Trinity +{ + template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void > + class _Callback + { + protected: + typedef void (Class::*Method)(ParamType1, ParamType2, ParamType3, ParamType4); + Class *m_object; + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + ParamType4 m_param4; + void _Execute() { (m_object->*m_method)(m_param1, m_param2, m_param3, m_param4); } + public: + _Callback(Class *object, Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4) + : m_object(object), m_method(method), m_param1(param1), m_param2(param2), m_param3(param3), m_param4(param4) {} + _Callback(_Callback < Class, ParamType1, ParamType2, ParamType3, ParamType4> const& cb) + : m_object(cb.object), m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3), m_param4(cb.m_param4) {} + }; + + template < class Class, typename ParamType1, typename ParamType2, typename ParamType3 > + class _Callback < Class, ParamType1, ParamType2, ParamType3 > + { + protected: + typedef void (Class::*Method)(ParamType1, ParamType2, ParamType3); + Class *m_object; + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + void _Execute() { (m_object->*m_method)(m_param1, m_param2, m_param3); } + public: + _Callback(Class *object, Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : m_object(object), m_method(method), m_param1(param1), m_param2(param2) {} + _Callback(_Callback < Class, ParamType1, ParamType2, ParamType3 > const& cb) + : m_object(cb.object), m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3) {} + }; + + template < class Class, typename ParamType1, typename ParamType2 > + class _Callback < Class, ParamType1, ParamType2 > + { + protected: + typedef void (Class::*Method)(ParamType1, ParamType2); + Class *m_object; + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + void _Execute() { (m_object->*m_method)(m_param1, m_param2); } + public: + _Callback(Class *object, Method method, ParamType1 param1, ParamType2 param2) + : m_object(object), m_method(method), m_param1(param1), m_param2(param2) {} + _Callback(_Callback < Class, ParamType1, ParamType2 > const& cb) + : m_object(cb.m_object), m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2) {} + }; + + template < class Class, typename ParamType1 > + class _Callback < Class, ParamType1 > + { + protected: + typedef void (Class::*Method)(ParamType1); + Class *m_object; + Method m_method; + ParamType1 m_param1; + void _Execute() { (m_object->*m_method)(m_param1); } + public: + _Callback(Class *object, Method method, ParamType1 param1) + : m_object(object), m_method(method), m_param1(param1) {} + _Callback(_Callback < Class, ParamType1 > const& cb) + : m_object(cb.m_object), m_method(cb.m_method), m_param1(cb.m_param1) {} + }; + + template < class Class > + class _Callback < Class > + { + protected: + typedef void (Class::*Method)(); + Class *m_object; + Method m_method; + void _Execute() { (m_object->*m_method)(); } + public: + _Callback(Class *object, Method method) + : m_object(object), m_method(method) {} + _Callback(_Callback < Class > const& cb) + : m_object(cb.m_object), m_method(cb.m_method) {} + }; + + /// ---- Statics ---- + + template < typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void > + class _SCallback + { + protected: + typedef void (*Method)(ParamType1, ParamType2, ParamType3, ParamType4); + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + ParamType4 m_param4; + void _Execute() { (*m_method)(m_param1, m_param2, m_param3, m_param4); } + public: + _SCallback(Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4) + : m_method(method), m_param1(param1), m_param2(param2), m_param3(param3), m_param4(param4) {} + _SCallback(_SCallback < ParamType1, ParamType2, ParamType3, ParamType4> const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3), m_param4(cb.m_param4) {} + }; + + template < typename ParamType1, typename ParamType2, typename ParamType3 > + class _SCallback < ParamType1, ParamType2, ParamType3 > + { + protected: + typedef void (*Method)(ParamType1, ParamType2, ParamType3); + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + void _Execute() { (*m_method)(m_param1, m_param2, m_param3); } + public: + _SCallback(Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : m_method(method), m_param1(param1), m_param2(param2), m_param3(param3) {} + _SCallback(_SCallback < ParamType1, ParamType2, ParamType3 > const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3) {} + }; + + template < typename ParamType1, typename ParamType2 > + class _SCallback < ParamType1, ParamType2 > + { + protected: + typedef void (*Method)(ParamType1, ParamType2); + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + void _Execute() { (*m_method)(m_param1, m_param2); } + public: + _SCallback(Method method, ParamType1 param1, ParamType2 param2) + : m_method(method), m_param1(param1), m_param2(param2) {} + _SCallback(_SCallback < ParamType1, ParamType2 > const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2) {} + }; + + template < typename ParamType1 > + class _SCallback < ParamType1 > + { + protected: + typedef void (*Method)(ParamType1); + Method m_method; + ParamType1 m_param1; + void _Execute() { (*m_method)(m_param1); } + public: + _SCallback(Method method, ParamType1 param1) + : m_method(method), m_param1(param1) {} + _SCallback(_SCallback < ParamType1 > const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1) {} + }; + + template < > + class _SCallback < > + { + protected: + typedef void (*Method)(); + Method m_method; + void _Execute() { (*m_method)(); } + public: + _SCallback(Method method) + : m_method(method) {} + _SCallback(_SCallback <> const& cb) + : m_method(cb.m_method) {} + }; +} + +/// --------- GENERIC CALLBACKS ---------- + +namespace Trinity +{ + class ICallback + { + public: + virtual void Execute() = 0; + virtual ~ICallback() {} + }; + + template < class CB > + class _ICallback : public CB, public ICallback + { + public: + _ICallback(CB const& cb) : CB(cb) {} + void Execute() { CB::_Execute(); } + }; + + template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void > + class Callback : + public _ICallback< _Callback < Class, ParamType1, ParamType2, ParamType3, ParamType4 > > + { + private: + typedef _Callback < Class, ParamType1, ParamType2, ParamType3, ParamType4 > C4; + public: + Callback(Class *object, typename C4::Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4) + : _ICallback< C4 >(C4(object, method, param1, param2, param3, param4)) {} + }; + + template < class Class, typename ParamType1, typename ParamType2, typename ParamType3 > + class Callback < Class, ParamType1, ParamType2, ParamType3 > : + public _ICallback< _Callback < Class, ParamType1, ParamType2, ParamType3 > > + { + private: + typedef _Callback < Class, ParamType1, ParamType2, ParamType3 > C3; + public: + Callback(Class *object, typename C3::Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : _ICallback< C3 >(C3(object, method, param1, param2, param3)) {} + }; + + template < class Class, typename ParamType1, typename ParamType2 > + class Callback < Class, ParamType1, ParamType2 > : + public _ICallback< _Callback < Class, ParamType1, ParamType2 > > + { + private: + typedef _Callback < Class, ParamType1, ParamType2 > C2; + public: + Callback(Class *object, typename C2::Method method, ParamType1 param1, ParamType2 param2) + : _ICallback< C2 >(C2(object, method, param1, param2)) {} + }; + + template < class Class, typename ParamType1 > + class Callback < Class, ParamType1 > : + public _ICallback< _Callback < Class, ParamType1 > > + { + private: + typedef _Callback < Class, ParamType1 > C1; + public: + Callback(Class *object, typename C1::Method method, ParamType1 param1) + : _ICallback< C1 >(C1(object, method, param1)) {} + }; + + template < class Class > + class Callback < Class > : public _ICallback< _Callback < Class > > + { + private: + typedef _Callback < Class > C0; + public: + Callback(Class *object, typename C0::Method method) + : _ICallback< C0 >(C0(object, method)) {} + }; +} + +/// ---------- QUERY CALLBACKS ----------- + +#include "QueryResult.h" +class QueryResult; + +namespace Trinity +{ + class IQueryCallback + { + public: + virtual void Execute() = 0; + virtual ~IQueryCallback() {} + virtual void SetResult(QueryResult_AutoPtr result) = 0; + virtual QueryResult_AutoPtr GetResult() = 0; + }; + + template < class CB > + class _IQueryCallback : public CB, public IQueryCallback + { + public: + _IQueryCallback(CB const& cb) : CB(cb) {} + void Execute() { CB::_Execute(); } + void SetResult(QueryResult_AutoPtr result) { CB::m_param1 = result; } + QueryResult_AutoPtr GetResult() { return CB::m_param1; } + }; + + template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void > + class QueryCallback : + public _IQueryCallback< _Callback < Class, QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3 > > + { + private: + typedef _Callback < Class, QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3 > QC3; + public: + QueryCallback(Class *object, typename QC3::Method method, QueryResult_AutoPtr result, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : _IQueryCallback< QC3 >(QC3(object, method, result, param1, param2, param3)) {} + }; + + template < class Class, typename ParamType1, typename ParamType2 > + class QueryCallback < Class, ParamType1, ParamType2 > : + public _IQueryCallback< _Callback < Class, QueryResult_AutoPtr, ParamType1, ParamType2 > > + { + private: + typedef _Callback < Class, QueryResult_AutoPtr, ParamType1, ParamType2 > QC2; + public: + QueryCallback(Class *object, typename QC2::Method method, QueryResult_AutoPtr result, ParamType1 param1, ParamType2 param2) + : _IQueryCallback< QC2 >(QC2(object, method, result, param1, param2)) {} + }; + + template < class Class, typename ParamType1 > + class QueryCallback < Class, ParamType1 > : + public _IQueryCallback< _Callback < Class, QueryResult_AutoPtr, ParamType1 > > + { + private: + typedef _Callback < Class, QueryResult_AutoPtr, ParamType1 > QC1; + public: + QueryCallback(Class *object, typename QC1::Method method, QueryResult_AutoPtr result, ParamType1 param1) + : _IQueryCallback< QC1 >(QC1(object, method, result, param1)) {} + }; + + template < class Class > + class QueryCallback < Class > : public _IQueryCallback< _Callback < Class, QueryResult_AutoPtr > > + { + private: + typedef _Callback < Class, QueryResult_AutoPtr > QC0; + public: + QueryCallback(Class *object, typename QC0::Method method, QueryResult_AutoPtr result) + : _IQueryCallback< QC0 >(QC0(object, method, result)) {} + }; + + /// ---- Statics ---- + + template < typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void > + class SQueryCallback : + public _IQueryCallback< _SCallback < QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3 > > + { + private: + typedef _SCallback < QueryResult_AutoPtr, ParamType1, ParamType2, ParamType3 > QC3; + public: + SQueryCallback(typename QC3::Method method, QueryResult_AutoPtr result, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : _IQueryCallback< QC3 >(QC3(method, result, param1, param2, param3)) {} + }; + + template < typename ParamType1, typename ParamType2 > + class SQueryCallback < ParamType1, ParamType2 > : + public _IQueryCallback< _SCallback < QueryResult_AutoPtr, ParamType1, ParamType2 > > + { + private: + typedef _SCallback < QueryResult_AutoPtr, ParamType1, ParamType2 > QC2; + public: + SQueryCallback(typename QC2::Method method, QueryResult_AutoPtr result, ParamType1 param1, ParamType2 param2) + : _IQueryCallback< QC2 >(QC2(method, result, param1, param2)) {} + }; + + template < typename ParamType1 > + class SQueryCallback < ParamType1 > : + public _IQueryCallback< _SCallback < QueryResult_AutoPtr, ParamType1 > > + { + private: + typedef _SCallback < QueryResult_AutoPtr, ParamType1 > QC1; + public: + SQueryCallback(typename QC1::Method method, QueryResult_AutoPtr result, ParamType1 param1) + : _IQueryCallback< QC1 >(QC1(method, result, param1)) {} + }; + + template < > + class SQueryCallback < > : public _IQueryCallback< _SCallback < QueryResult_AutoPtr > > + { + private: + typedef _SCallback < QueryResult_AutoPtr > QC0; + public: + SQueryCallback(QC0::Method method, QueryResult_AutoPtr result) + : _IQueryCallback< QC0 >(QC0(method, result)) {} + }; +} + +#endif + diff --git a/src/server/framework/Utilities/CountedReference/Reference.h b/src/server/framework/Utilities/CountedReference/Reference.h new file mode 100644 index 00000000000..d3cfe55ffc0 --- /dev/null +++ b/src/server/framework/Utilities/CountedReference/Reference.h @@ -0,0 +1,100 @@ +/* + * 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_REFERENCE_H +#define TRINITY_REFERENCE_H + +/** + * Referencer + * Referencer is an object that holds a reference holder that hold a reference + * counted object. When an object's reference count drop to zero, it removes + * the object. This is a non intrusive mechanism and any object at any point + * in time can be referenced. When and object is reference counted, do not + * pass the object directly to other methods but rather, pass its + * reference around. Objects can be reference counted in both single threaded + * model and multi-threaded model + */ + +#include +#include "Platform/Define.h" +#include "Policies/ThreadingModel.h" +#include "ReferenceHolder.h" + +template +< +typename T, +class THREADING_MODEL = Trinity::SingleThreaded +> +class Referencer +{ + typedef typename THREADING_MODEL::Lock Lock; + typedef ReferenceHolder ReferenceeHolder; + public: + + /// Constructs a referencer. + Referencer(T *ref = NULL); + + /// Copy constructor + Referencer(const Referencer &obj) : i_holder(NULL) { *this = obj; } + + /// Destructor + ~Referencer(); + + /// Referencee accessor + T* referencee(void) { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + const T* referencee(void) const { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + + //T& referencee(void){ return _referencee(); } + //const T& referencee(void) const { return const_cast(this)->_referencee(); } + operator T&(void) { return _referencee(); } + operator const T&(void) const { return *const_cast(this)->_referencee(); } + + /// cast operators + T* operator*() { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + T const * operator*() const { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + + /// overload operators + T* operator->() { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + const T * operator->() const { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + + /// operator = + Referencer& operator=(const Referencer &obj); + Referencer& operator=(T *); + + /// returns true if i_referencee is null + bool isNull(void) const { return i_holder == NULL; } + + private: + + T& _referencee(void) + { + if( i_holder == NULL ) + throw std::runtime_error("Invalid access to null pointer"); + return *i_holder->i_referencee; + } + + void deReference(ReferenceeHolder *); + void addReference(ReferenceeHolder *); + + // private data + ReferenceeHolder *i_holder; +}; +#endif + diff --git a/src/server/framework/Utilities/CountedReference/ReferenceHolder.h b/src/server/framework/Utilities/CountedReference/ReferenceHolder.h new file mode 100644 index 00000000000..597e9854be0 --- /dev/null +++ b/src/server/framework/Utilities/CountedReference/ReferenceHolder.h @@ -0,0 +1,42 @@ +/* + * 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_REFERENCEHOLDER_H +#define TRINITY_REFERENCEHOLDER_H + +/** ReferenceHolder holds the actualy referenced obejct as well the refence + count. The ReferenecHolder implements as a policy base object and + will decided by the Reference class to be consnsitent. + */ + +template +< +typename T, +class THREADING_MODEL +> +struct ReferenceHolder : public THREADING_MODEL +{ + explicit ReferenceHolder(T *ref) : i_referencee(ref), i_referenceCount(0) {} + T *i_referencee; + unsigned int i_referenceCount; + typedef typename THREADING_MODEL::Lock Lock; +}; +#endif + diff --git a/src/server/framework/Utilities/CountedReference/ReferenceImpl.h b/src/server/framework/Utilities/CountedReference/ReferenceImpl.h new file mode 100644 index 00000000000..cde330179e3 --- /dev/null +++ b/src/server/framework/Utilities/CountedReference/ReferenceImpl.h @@ -0,0 +1,133 @@ +/* + * 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_REFERENCEIMPL_H +#define TRINITY_REFERENCEIMPL_H + +#include "Reference.h" + +template +< +typename T, +class THREADING_MODEL +> +Referencer::Referencer(T *ref) +: i_holder(NULL) +{ + if( ref != NULL ) + { + i_holder = new ReferenceeHolder(ref); + ++i_holder->i_referenceCount; + } +} + +template +< +typename T, +class THREADING_MODEL +> +Referencer::~Referencer() +{ + if( i_holder != NULL ) + deReference(i_holder); + i_holder = NULL; +} + +template +< +typename T, +class THREADING_MODEL +> +Referencer& +Referencer::operator=(const Referencer &obj) +{ + if( i_holder != NULL ) + deReference(i_holder); + if( obj.i_holder != NULL ) + addReference(obj.i_holder); + i_holder = obj.i_holder; + return *this; +} + +template +< +typename T, +class THREADING_MODEL +> +Referencer& +Referencer::operator=(T *ref) +{ + if( i_holder != NULL ) + deReference(i_holder); + i_holder = NULL; + if( ref != NULL ) + { + i_holder = new ReferenceeHolder(ref); + ++i_holder->i_referenceCount; + } + + return *this; +} + +template +< +typename T, +class THREADING_MODEL +> +void +Referencer::deReference(ReferenceHolder *holder) +{ + assert( holder != NULL && holder->i_referenceCount > 0); + bool delete_object = false; + + { + // The guard is within the scope due to the guard + // must release earlier than expected. + Lock guard(*holder); + Guard(&guard); + + --holder->i_referenceCount; + if( holder->i_referenceCount == 0 ) + delete_object = true; + } + + if( delete_object ) + { + delete holder->i_referencee; + delete holder; + } +} + +template +< +typename T, +class THREADING_MODEL +> +void +Referencer::addReference(ReferenceHolder *holder) +{ + assert( i_holder != NULL ); + Lock guard(*holder); + Guard(&guard); + + ++holder->i_referenceCount; +} +#endif + diff --git a/src/server/framework/Utilities/EventProcessor.cpp b/src/server/framework/Utilities/EventProcessor.cpp new file mode 100644 index 00000000000..c695b43313a --- /dev/null +++ b/src/server/framework/Utilities/EventProcessor.cpp @@ -0,0 +1,101 @@ +/* + * 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 "EventProcessor.h" + +EventProcessor::EventProcessor() +{ + m_time = 0; + m_aborting = false; +} + +EventProcessor::~EventProcessor() +{ + KillAllEvents(true); +} + +void EventProcessor::Update(uint32 p_time) +{ + // update time + m_time += p_time; + + // main event loop + EventList::iterator i; + while (((i = m_events.begin()) != m_events.end()) && i->first <= m_time) + { + // get and remove event from queue + BasicEvent* Event = i->second; + m_events.erase(i); + + if (!Event->to_Abort) + { + if (Event->Execute(m_time, p_time)) + { + // completely destroy event if it is not re-added + delete Event; + } + } + else + { + Event->Abort(m_time); + delete Event; + } + } +} + +void EventProcessor::KillAllEvents(bool force) +{ + // prevent event insertions + m_aborting = true; + + // first, abort all existing events + for (EventList::iterator i = m_events.begin(); i != m_events.end();) + { + EventList::iterator i_old = i; + ++i; + + i_old->second->to_Abort = true; + i_old->second->Abort(m_time); + if(force || i_old->second->IsDeletable()) + { + delete i_old->second; + + if(!force) // need per-element cleanup + m_events.erase (i_old); + } + } + + // fast clear event list (in force case) + if(force) + m_events.clear(); +} + +void EventProcessor::AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime) +{ + if (set_addtime) Event->m_addTime = m_time; + Event->m_execTime = e_time; + m_events.insert(std::pair(e_time, Event)); +} + +uint64 EventProcessor::CalculateTime(uint64 t_offset) +{ + return(m_time + t_offset); +} + diff --git a/src/server/framework/Utilities/EventProcessor.h b/src/server/framework/Utilities/EventProcessor.h new file mode 100644 index 00000000000..2712967e1b7 --- /dev/null +++ b/src/server/framework/Utilities/EventProcessor.h @@ -0,0 +1,73 @@ +/* + * 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 __EVENTPROCESSOR_H +#define __EVENTPROCESSOR_H + +#include "Platform/Define.h" + +#include + +// Note. All times are in milliseconds here. + +class BasicEvent +{ + public: + BasicEvent() { to_Abort = false; } + virtual ~BasicEvent() // override destructor to perform some actions on event removal + { + }; + + // this method executes when the event is triggered + // return false if event does not want to be deleted + // e_time is execution time, p_time is update interval + virtual bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) { return true; } + + virtual bool IsDeletable() const { return true; } // this event can be safely deleted + + virtual void Abort(uint64 /*e_time*/) {} // this method executes when the event is aborted + + bool to_Abort; // set by externals when the event is aborted, aborted events don't execute + // and get Abort call when deleted + + // these can be used for time offset control + uint64 m_addTime; // time when the event was added to queue, filled by event handler + uint64 m_execTime; // planned time of next execution, filled by event handler +}; + +typedef std::multimap EventList; + +class EventProcessor +{ + public: + EventProcessor(); + ~EventProcessor(); + + void Update(uint32 p_time); + void KillAllEvents(bool force); + void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true); + uint64 CalculateTime(uint64 t_offset); + protected: + uint64 m_time; + EventList m_events; + bool m_aborting; +}; +#endif + diff --git a/src/server/framework/Utilities/LinkedList.h b/src/server/framework/Utilities/LinkedList.h new file mode 100644 index 00000000000..b26687767b3 --- /dev/null +++ b/src/server/framework/Utilities/LinkedList.h @@ -0,0 +1,247 @@ +/* + * 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 _LINKEDLIST +#define _LINKEDLIST + +#include "Common.h" + +//============================================ +class LinkedListHead; + +class LinkedListElement +{ + private: + friend class LinkedListHead; + + LinkedListElement* iNext; + LinkedListElement* iPrev; + public: + LinkedListElement() { iNext = NULL; iPrev = NULL; } + ~LinkedListElement() { delink(); } + + bool hasNext() const { return(iNext && iNext->iNext != NULL); } + bool hasPrev() const { return(iPrev && iPrev->iPrev != NULL); } + bool isInList() const { return(iNext != NULL && iPrev != NULL); } + + LinkedListElement * next() { return hasNext() ? iNext : NULL; } + LinkedListElement const* next() const { return hasNext() ? iNext : NULL; } + LinkedListElement * prev() { return hasPrev() ? iPrev : NULL; } + LinkedListElement const* prev() const { return hasPrev() ? iPrev : NULL; } + + LinkedListElement * nocheck_next() { return iNext; } + LinkedListElement const* nocheck_next() const { return iNext; } + LinkedListElement * nocheck_prev() { return iPrev; } + LinkedListElement const* nocheck_prev() const { return iPrev; } + + void delink() + { + if(isInList()) + { + iNext->iPrev = iPrev; iPrev->iNext = iNext; iNext = NULL; iPrev = NULL; + } + } + + void insertBefore(LinkedListElement* pElem) + { + pElem->iNext = this; + pElem->iPrev = iPrev; + iPrev->iNext = pElem; + iPrev = pElem; + } + + void insertAfter(LinkedListElement* pElem) + { + pElem->iPrev = this; + pElem->iNext = iNext; + iNext->iPrev = pElem; + iNext = pElem; + } +}; + +//============================================ + +class LinkedListHead +{ + private: + LinkedListElement iFirst; + LinkedListElement iLast; + uint32 iSize; + public: + LinkedListHead() + { + // create empty list + + iFirst.iNext = &iLast; + iLast.iPrev = &iFirst; + iSize = 0; + } + + bool isEmpty() const { return(!iFirst.iNext->isInList()); } + + LinkedListElement * getFirst() { return(isEmpty() ? NULL : iFirst.iNext); } + LinkedListElement const* getFirst() const { return(isEmpty() ? NULL : iFirst.iNext); } + + LinkedListElement * getLast() { return(isEmpty() ? NULL : iLast.iPrev); } + LinkedListElement const* getLast() const { return(isEmpty() ? NULL : iLast.iPrev); } + + void insertFirst(LinkedListElement* pElem) + { + iFirst.insertAfter(pElem); + } + + void insertLast(LinkedListElement* pElem) + { + iLast.insertBefore(pElem); + } + + uint32 getSize() const + { + if(!iSize) + { + uint32 result = 0; + LinkedListElement const* e = getFirst(); + while(e) + { + ++result; + e = e->next(); + } + return result; + } + else + return iSize; + } + + void incSize() { ++iSize; } + void decSize() { --iSize; } + + template + class Iterator + { + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef _Ty value_type; + typedef ptrdiff_t difference_type; + typedef ptrdiff_t distance_type; + typedef _Ty* pointer; + typedef _Ty const* const_pointer; + typedef _Ty& reference; + typedef _Ty const & const_reference; + + Iterator() : _Ptr(0) + { // construct with null node pointer + } + + Iterator(pointer _Pnode) : _Ptr(_Pnode) + { // construct with node pointer _Pnode + } + + Iterator& operator=(Iterator const &_Right) + { + return (*this) = _Right._Ptr; + } + + Iterator& operator=(const_pointer const &_Right) + { + _Ptr = (pointer)_Right; + return (*this); + } + + reference operator*() + { // return designated value + return *_Ptr; + } + + pointer operator->() + { // return pointer to class object + return _Ptr; + } + + Iterator& operator++() + { // preincrement + _Ptr = _Ptr->next(); + return (*this); + } + + Iterator operator++(int) + { // postincrement + iterator _Tmp = *this; + ++*this; + return (_Tmp); + } + + Iterator& operator--() + { // predecrement + _Ptr = _Ptr->prev(); + return (*this); + } + + Iterator operator--(int) + { // postdecrement + iterator _Tmp = *this; + --*this; + return (_Tmp); + } + + bool operator==(Iterator const &_Right) const + { // test for iterator equality + return (_Ptr == _Right._Ptr); + } + + bool operator!=(Iterator const &_Right) const + { // test for iterator inequality + return (!(*this == _Right)); + } + + bool operator==(pointer const &_Right) const + { // test for pointer equality + return (_Ptr != _Right); + } + + bool operator!=(pointer const &_Right) const + { // test for pointer equality + return (!(*this == _Right)); + } + + bool operator==(const_reference _Right) const + { // test for reference equality + return (_Ptr == &_Right); + } + + bool operator!=(const_reference _Right) const + { // test for reference equality + return (_Ptr != &_Right); + } + + pointer _Mynode() + { // return node pointer + return (_Ptr); + } + + protected: + pointer _Ptr; // pointer to node + }; + + typedef Iterator iterator; +}; + +//============================================ +#endif + diff --git a/src/server/framework/Utilities/LinkedReference/RefManager.h b/src/server/framework/Utilities/LinkedReference/RefManager.h new file mode 100644 index 00000000000..7e294b4f5f0 --- /dev/null +++ b/src/server/framework/Utilities/LinkedReference/RefManager.h @@ -0,0 +1,58 @@ +/* + * 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 _REFMANAGER_H +#define _REFMANAGER_H +//===================================================== + +#include "Utilities/LinkedList.h" +#include "Utilities/LinkedReference/Reference.h" + +template class RefManager : public LinkedListHead +{ + public: + typedef LinkedListHead::Iterator< Reference > iterator; + RefManager() { } + virtual ~RefManager() { clearReferences(); } + + Reference* getFirst() { return ((Reference*) LinkedListHead::getFirst()); } + Reference const* getFirst() const { return ((Reference const*) LinkedListHead::getFirst()); } + Reference* getLast() { return ((Reference*) LinkedListHead::getLast()); } + Reference const* getLast() const { return ((Reference const*) LinkedListHead::getLast()); } + + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(NULL); } + iterator rbegin() { return iterator(getLast()); } + iterator rend() { return iterator(NULL); } + + void clearReferences() + { + LinkedListElement* ref; + while((ref = getFirst()) != NULL) + { + ((Reference*) ref)->invalidate(); + ref->delink(); // the delink might be already done by invalidate(), but doing it here again does not hurt and insures an empty list + } + } +}; + +//===================================================== +#endif + diff --git a/src/server/framework/Utilities/LinkedReference/Reference.h b/src/server/framework/Utilities/LinkedReference/Reference.h new file mode 100644 index 00000000000..4a1545f8f12 --- /dev/null +++ b/src/server/framework/Utilities/LinkedReference/Reference.h @@ -0,0 +1,94 @@ +/* + * 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 _REFERENCE_H +#define _REFERENCE_H + +#include "Utilities/LinkedList.h" + +//===================================================== + +template class Reference : public LinkedListElement +{ + private: + TO* iRefTo; + FROM* iRefFrom; + protected: + // Tell our refTo (target) object that we have a link + virtual void targetObjectBuildLink() = 0; + + // Tell our refTo (taget) object, that the link is cut + virtual void targetObjectDestroyLink() = 0; + + // Tell our refFrom (source) object, that the link is cut (Target destroyed) + virtual void sourceObjectDestroyLink() = 0; + public: + Reference() { iRefTo = NULL; iRefFrom = NULL; } + virtual ~Reference() {} + + // Create new link + void link(TO* toObj, FROM* fromObj) + { + assert(fromObj); // fromObj MUST not be NULL + if(isValid()) + unlink(); + if(toObj != NULL) + { + iRefTo = toObj; + iRefFrom = fromObj; + targetObjectBuildLink(); + } + } + + // We don't need the reference anymore. Call comes from the refFrom object + // Tell our refTo object, that the link is cut + void unlink() { targetObjectDestroyLink(); delink(); iRefTo = NULL; iRefFrom = NULL; } + + // Link is invalid due to destruction of referenced target object. Call comes from the refTo object + // Tell our refFrom object, that the link is cut + void invalidate() // the iRefFrom MUST remain!! + { + sourceObjectDestroyLink(); delink(); iRefTo = NULL; + } + + bool isValid() const // Only check the iRefTo + { + return iRefTo != NULL; + } + + Reference * next() { return((Reference *) LinkedListElement::next()); } + Reference const * next() const { return((Reference const *) LinkedListElement::next()); } + Reference * prev() { return((Reference *) LinkedListElement::prev()); } + Reference const * prev() const { return((Reference const *) LinkedListElement::prev()); } + + Reference * nocheck_next() { return((Reference *) LinkedListElement::nocheck_next()); } + Reference const * nocheck_next() const { return((Reference const *) LinkedListElement::nocheck_next()); } + Reference * nocheck_prev() { return((Reference *) LinkedListElement::nocheck_prev()); } + Reference const * nocheck_prev() const { return((Reference const *) LinkedListElement::nocheck_prev()); } + + TO* operator ->() const { return iRefTo; } + TO* getTarget() const { return iRefTo; } + + FROM* getSource() const { return iRefFrom; } +}; + +//===================================================== +#endif + diff --git a/src/server/framework/Utilities/TypeList.h b/src/server/framework/Utilities/TypeList.h new file mode 100644 index 00000000000..02bc08083c2 --- /dev/null +++ b/src/server/framework/Utilities/TypeList.h @@ -0,0 +1,46 @@ +/* + * 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_TYPELIST_H +#define TRINITY_TYPELIST_H + +/* + @struct TypeList + TypeList is the most simple but yet the most powerfull class of all. It holds + at compile time the different type of objects in a linked list. + */ + +class TypeNull; + +template +struct TypeList +{ + typedef HEAD Head; + typedef TAIL Tail; +}; + +// enough for now.. can be expand at any point in time as needed +#define TYPELIST_1(T1) TypeList +#define TYPELIST_2(T1, T2) TypeList +#define TYPELIST_3(T1, T2, T3) TypeList +#define TYPELIST_4(T1, T2, T3, T4) TypeList +#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList +#endif + diff --git a/src/server/framework/Utilities/UnorderedMap.h b/src/server/framework/Utilities/UnorderedMap.h new file mode 100644 index 00000000000..fce5ec82bfc --- /dev/null +++ b/src/server/framework/Utilities/UnorderedMap.h @@ -0,0 +1,73 @@ +/* + * 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_UNORDERED_MAP_H +#define TRINITY_UNORDERED_MAP_H + +#include "Platform/CompilerDefs.h" +#include "Platform/Define.h" + +#if COMPILER == COMPILER_INTEL +#include +#elif COMPILER == COMPILER_GNU && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 3) +#include +#elif COMPILER == COMPILER_GNU && __GNUC__ >= 3 +#include +#elif COMPILER == COMPILER_MICROSOFT && _MSC_VER >= 1500 && _HAS_TR1 // VC9.0 and later +#include +#else +#include +#endif + +#ifdef _STLPORT_VERSION +#define UNORDERED_MAP std::hash_map +using std::hash_map; +#elif COMPILER == COMPILER_MICROSOFT && _MSC_VER >= 1500 && _HAS_TR1 +#define UNORDERED_MAP std::tr1::unordered_map +#elif COMPILER == COMPILER_MICROSOFT && _MSC_VER >= 1300 +#define UNORDERED_MAP stdext::hash_map +using stdext::hash_map; +#elif COMPILER == COMPILER_INTEL +#define UNORDERED_MAP std::hash_map +using std::hash_map; +#elif COMPILER == COMPILER_GNU && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 3) +#define UNORDERED_MAP std::tr1::unordered_map +#elif COMPILER == COMPILER_GNU && __GNUC__ >= 3 +#define UNORDERED_MAP __gnu_cxx::hash_map + +namespace __gnu_cxx +{ + template<> struct hash + { + size_t operator()(const unsigned long long &__x) const { return (size_t)__x; } + }; + template struct hash + { + size_t operator()(T * const &__x) const { return (size_t)__x; } + }; + +}; + +#else +#define UNORDERED_MAP std::hash_map +using std::hash_map; +#endif +#endif + diff --git a/src/server/game/AccountMgr.cpp b/src/server/game/AccountMgr.cpp new file mode 100644 index 00000000000..52906fb5454 --- /dev/null +++ b/src/server/game/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/AccountMgr.h b/src/server/game/AccountMgr.h new file mode 100644 index 00000000000..b017bf1836a --- /dev/null +++ b/src/server/game/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/AchievementMgr.cpp b/src/server/game/AchievementMgr.cpp new file mode 100644 index 00000000000..e0a79fc71ed --- /dev/null +++ b/src/server/game/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/AchievementMgr.h b/src/server/game/AchievementMgr.h new file mode 100644 index 00000000000..ea9e5e95142 --- /dev/null +++ b/src/server/game/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 new file mode 100644 index 00000000000..a9c8101d7b1 --- /dev/null +++ b/src/server/game/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/AddonHandler.h b/src/server/game/AddonHandler.h new file mode 100644 index 00000000000..999785339bc --- /dev/null +++ b/src/server/game/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/AddonMgr.cpp b/src/server/game/AddonMgr.cpp new file mode 100644 index 00000000000..66e4fbc8765 --- /dev/null +++ b/src/server/game/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/AddonMgr.h b/src/server/game/AddonMgr.h new file mode 100644 index 00000000000..8f5eed4918f --- /dev/null +++ b/src/server/game/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 new file mode 100644 index 00000000000..4224ff6357a --- /dev/null +++ b/src/server/game/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/ArenaTeam.h b/src/server/game/ArenaTeam.h new file mode 100644 index 00000000000..e6f85cf8d9e --- /dev/null +++ b/src/server/game/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/ArenaTeamHandler.cpp b/src/server/game/ArenaTeamHandler.cpp new file mode 100644 index 00000000000..3a9a14524f9 --- /dev/null +++ b/src/server/game/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/AuctionHouseBot.cpp b/src/server/game/AuctionHouseBot.cpp new file mode 100644 index 00000000000..ebbf48e6476 --- /dev/null +++ b/src/server/game/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/AuctionHouseBot.h b/src/server/game/AuctionHouseBot.h new file mode 100644 index 00000000000..208a09aa0b2 --- /dev/null +++ b/src/server/game/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/AuctionHouseHandler.cpp b/src/server/game/AuctionHouseHandler.cpp new file mode 100644 index 00000000000..8fec3b7df1d --- /dev/null +++ b/src/server/game/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/AuctionHouseMgr.cpp b/src/server/game/AuctionHouseMgr.cpp new file mode 100755 index 00000000000..ddd44bbefa2 --- /dev/null +++ b/src/server/game/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/AuctionHouseMgr.h b/src/server/game/AuctionHouseMgr.h new file mode 100644 index 00000000000..b92cec986c7 --- /dev/null +++ b/src/server/game/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/Bag.cpp b/src/server/game/Bag.cpp new file mode 100644 index 00000000000..aa78126b198 --- /dev/null +++ b/src/server/game/Bag.cpp @@ -0,0 +1,235 @@ +/* + * 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 new file mode 100644 index 00000000000..5bc2480fc47 --- /dev/null +++ b/src/server/game/Bag.h @@ -0,0 +1,75 @@ +/* + * 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 new file mode 100644 index 00000000000..5bfe2e139b6 --- /dev/null +++ b/src/server/game/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/BattleGround.h b/src/server/game/BattleGround.h new file mode 100644 index 00000000000..6f46b6f8f6d --- /dev/null +++ b/src/server/game/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/BattleGroundAA.cpp b/src/server/game/BattleGroundAA.cpp new file mode 100644 index 00000000000..56cf3ebed15 --- /dev/null +++ b/src/server/game/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/BattleGroundAA.h b/src/server/game/BattleGroundAA.h new file mode 100644 index 00000000000..a13833697cf --- /dev/null +++ b/src/server/game/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/BattleGroundAB.cpp b/src/server/game/BattleGroundAB.cpp new file mode 100644 index 00000000000..38671e85597 --- /dev/null +++ b/src/server/game/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/BattleGroundAB.h b/src/server/game/BattleGroundAB.h new file mode 100644 index 00000000000..3072f8beafd --- /dev/null +++ b/src/server/game/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/BattleGroundAV.cpp b/src/server/game/BattleGroundAV.cpp new file mode 100644 index 00000000000..7f5482cbf16 --- /dev/null +++ b/src/server/game/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/BattleGroundAV.h b/src/server/game/BattleGroundAV.h new file mode 100644 index 00000000000..6d95c7bbc5d --- /dev/null +++ b/src/server/game/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/BattleGroundBE.cpp b/src/server/game/BattleGroundBE.cpp new file mode 100644 index 00000000000..d6debe45ae3 --- /dev/null +++ b/src/server/game/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/BattleGroundBE.h b/src/server/game/BattleGroundBE.h new file mode 100644 index 00000000000..760d4d278c9 --- /dev/null +++ b/src/server/game/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/BattleGroundDS.cpp b/src/server/game/BattleGroundDS.cpp new file mode 100644 index 00000000000..9036ef83f93 --- /dev/null +++ b/src/server/game/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/BattleGroundDS.h b/src/server/game/BattleGroundDS.h new file mode 100644 index 00000000000..2ced5c88fd1 --- /dev/null +++ b/src/server/game/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/BattleGroundEY.cpp b/src/server/game/BattleGroundEY.cpp new file mode 100644 index 00000000000..20f023e4c2a --- /dev/null +++ b/src/server/game/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/BattleGroundEY.h b/src/server/game/BattleGroundEY.h new file mode 100644 index 00000000000..4fe23c4c821 --- /dev/null +++ b/src/server/game/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/BattleGroundHandler.cpp b/src/server/game/BattleGroundHandler.cpp new file mode 100644 index 00000000000..e779f2a8ab1 --- /dev/null +++ b/src/server/game/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/BattleGroundIC.cpp b/src/server/game/BattleGroundIC.cpp new file mode 100644 index 00000000000..8dbcc81e5c6 --- /dev/null +++ b/src/server/game/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/BattleGroundIC.h b/src/server/game/BattleGroundIC.h new file mode 100644 index 00000000000..e49ea01e850 --- /dev/null +++ b/src/server/game/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/BattleGroundMgr.cpp b/src/server/game/BattleGroundMgr.cpp new file mode 100644 index 00000000000..3bbe8c3cf31 --- /dev/null +++ b/src/server/game/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/BattleGroundMgr.h b/src/server/game/BattleGroundMgr.h new file mode 100644 index 00000000000..569651a95b3 --- /dev/null +++ b/src/server/game/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/BattleGroundNA.cpp b/src/server/game/BattleGroundNA.cpp new file mode 100644 index 00000000000..793cf13b3cb --- /dev/null +++ b/src/server/game/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/BattleGroundNA.h b/src/server/game/BattleGroundNA.h new file mode 100644 index 00000000000..a11d311515d --- /dev/null +++ b/src/server/game/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/BattleGroundRB.cpp b/src/server/game/BattleGroundRB.cpp new file mode 100644 index 00000000000..cf22154ed11 --- /dev/null +++ b/src/server/game/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/BattleGroundRB.h b/src/server/game/BattleGroundRB.h new file mode 100644 index 00000000000..a40ade5adfe --- /dev/null +++ b/src/server/game/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/BattleGroundRL.cpp b/src/server/game/BattleGroundRL.cpp new file mode 100644 index 00000000000..ef2ec3cfa94 --- /dev/null +++ b/src/server/game/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/BattleGroundRL.h b/src/server/game/BattleGroundRL.h new file mode 100644 index 00000000000..3b4c10a7c9a --- /dev/null +++ b/src/server/game/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/BattleGroundRV.cpp b/src/server/game/BattleGroundRV.cpp new file mode 100644 index 00000000000..fcc53dbbcf9 --- /dev/null +++ b/src/server/game/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/BattleGroundRV.h b/src/server/game/BattleGroundRV.h new file mode 100644 index 00000000000..bf06478d364 --- /dev/null +++ b/src/server/game/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/BattleGroundSA.cpp b/src/server/game/BattleGroundSA.cpp new file mode 100644 index 00000000000..ccde43ce948 --- /dev/null +++ b/src/server/game/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/BattleGroundSA.h b/src/server/game/BattleGroundSA.h new file mode 100644 index 00000000000..760be3ca02e --- /dev/null +++ b/src/server/game/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/BattleGroundWS.cpp b/src/server/game/BattleGroundWS.cpp new file mode 100644 index 00000000000..71872511274 --- /dev/null +++ b/src/server/game/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/BattleGroundWS.h b/src/server/game/BattleGroundWS.h new file mode 100644 index 00000000000..1a733c14570 --- /dev/null +++ b/src/server/game/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/CMakeLists.txt b/src/server/game/CMakeLists.txt new file mode 100644 index 00000000000..1ec450e9468 --- /dev/null +++ b/src/server/game/CMakeLists.txt @@ -0,0 +1,358 @@ +# Enable precompiled headers when using the GCC compiler. +IF(DO_PCH AND CMAKE_COMPILER_IS_GNUCXX) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +ENDIF(DO_PCH AND CMAKE_COMPILER_IS_GNUCXX) + +SET(game_STAT_SRCS + AccountMgr.cpp + AccountMgr.h + AddonMgr.cpp + AddonMgr.h + AchievementMgr.h + AchievementMgr.cpp + AddonHandler.cpp + AddonHandler.h + CombatAI.cpp + CombatAI.h + ArenaTeam.cpp + ArenaTeam.h + ArenaTeamHandler.cpp + AuctionHouseHandler.cpp + AuctionHouseBot.cpp + AuctionHouseBot.h + AuctionHouseMgr.cpp + AuctionHouseMgr.h + Bag.cpp + Bag.h + BattleGround.cpp + BattleGroundAA.cpp + BattleGroundAB.cpp + BattleGroundRB.cpp + BattleGroundAV.cpp + BattleGroundBE.cpp + BattleGroundDS.cpp + BattleGroundEY.cpp + BattleGroundIC.cpp + BattleGroundNA.cpp + BattleGroundRL.cpp + BattleGroundRV.cpp + BattleGroundSA.cpp + BattleGroundWS.cpp + BattleGround.h + BattleGroundAA.h + BattleGroundAB.h + BattleGroundRB.h + BattleGroundAV.h + BattleGroundBE.h + BattleGroundDS.h + BattleGroundEY.h + BattleGroundIC.h + BattleGroundNA.h + BattleGroundRL.h + BattleGroundRV.h + BattleGroundSA.h + BattleGroundWS.h + BattleGroundHandler.cpp + BattleGroundMgr.cpp + BattleGroundMgr.h + Calendar.cpp + Calendar.h + CalendarHandler.cpp + Cell.h + CellImpl.h + Channel.cpp + Channel.h + ChannelHandler.cpp + CharacterHandler.cpp + ChannelMgr.h + ChannelMgr.cpp + Chat.cpp + Chat.h + ChatHandler.cpp + CombatHandler.cpp + ConditionMgr.cpp + ConditionMgr.h + ConfusedMovementGenerator.cpp + ConfusedMovementGenerator.h + Corpse.cpp + Corpse.h + CreatureAI.cpp + CreatureAI.h + CreatureAIFactory.h + CreatureAIImpl.h + CreatureAIRegistry.cpp + CreatureAIRegistry.h + CreatureAISelector.cpp + CreatureAISelector.h + CreatureEventAI.cpp + CreatureEventAIMgr.cpp + Creature.cpp + Creature.h + CreatureGroups.cpp + CreatureGroups.h + DBCEnums.h + DBCStores.cpp + DBCStores.h + DBCStructure.h + DBCfmt.h + Debugcmds.cpp + DestinationHolder.cpp + DestinationHolder.h + DestinationHolderImp.h + DuelHandler.cpp + DynamicObject.cpp + DynamicObject.h + FleeingMovementGenerator.cpp + FleeingMovementGenerator.h + Formulas.h + GameEventMgr.cpp + GameEventMgr.h + GameObject.cpp + GameObject.h + GlobalEvents.cpp + GlobalEvents.h + GossipDef.cpp + GossipDef.h + GridDefines.h + GridNotifiers.cpp + GridNotifiers.h + GridNotifiersImpl.h + GridStates.cpp + GridStates.h + Group.cpp + Group.h + GroupHandler.cpp + GuardAI.cpp + GuardAI.h + Guild.cpp + Guild.h + GuildHandler.cpp + HomeMovementGenerator.cpp + HomeMovementGenerator.h + HostileRefManager.cpp + HostileRefManager.h + IdleMovementGenerator.cpp + IdleMovementGenerator.h + InstanceData.cpp + InstanceData.h + InstanceSaveMgr.cpp + InstanceSaveMgr.h + Item.cpp + Item.h + ItemEnchantmentMgr.cpp + ItemEnchantmentMgr.h + ItemHandler.cpp + ItemPrototype.h + Language.h + Level0.cpp + Level1.cpp + Level2.cpp + Level3.cpp + LFG.h + LFGHandler.cpp + LFGMgr.cpp + LFGMgr.h + LootHandler.cpp + LootMgr.cpp + LootMgr.h + Mail.cpp + Mail.h + Map.cpp + Map.h + MapInstanced.cpp + MapInstanced.h + MapManager.cpp + MapManager.h + MapUpdater.cpp + MapUpdater.h + MapReference.h + MapRefManager.h + MiscHandler.cpp + MotionMaster.cpp + MotionMaster.h + MovementGenerator.cpp + MovementGenerator.h + MovementGeneratorImpl.h + MovementHandler.cpp + NPCHandler.cpp + NPCHandler.h + PassiveAI.cpp + PassiveAI.h + ObjectAccessor.cpp + ObjectAccessor.h + Object.cpp + ObjectDefines.h + ObjectGridLoader.cpp + ObjectGridLoader.h + Object.h + ObjectMgr.cpp + ObjectMgr.h + ObjectPosSelector.cpp + ObjectPosSelector.h + Opcodes.cpp + Opcodes.h + OutdoorPvP.cpp + OutdoorPvP.h + OutdoorPvPEP.cpp + OutdoorPvPEP.h + OutdoorPvPHP.cpp + OutdoorPvPHP.h + OutdoorPvPMgr.cpp + OutdoorPvPMgr.h + OutdoorPvPNA.cpp + OutdoorPvPNA.h + OutdoorPvPSI.cpp + OutdoorPvPSI.h + OutdoorPvPTF.cpp + OutdoorPvPTF.h + OutdoorPvPZM.cpp + OutdoorPvPZM.h + Path.h + PetAI.cpp + PetAI.h + Pet.cpp + Pet.h + PetHandler.cpp + PetitionsHandler.cpp + Player.cpp + Player.h + PlayerDump.cpp + PlayerDump.h + PointMovementGenerator.cpp + PointMovementGenerator.h + PoolHandler.cpp + PoolHandler.h + QueryHandler.cpp + QuestDef.cpp + QuestDef.h + QuestHandler.cpp + RandomMovementGenerator.cpp + RandomMovementGenerator.h + ReactorAI.cpp + ReactorAI.h + ReputationMgr.cpp + ReputationMgr.h + ScriptedSmartAI.cpp + ScriptedSmartAI.h + ScriptLoader.cpp + ScriptLoader.h + ScriptMgr.cpp + ScriptMgr.h + ScriptSystem.cpp + ScriptSystem.h + SharedDefines.h + SkillHandler.cpp + SpellAuraDefines.h + SpellAuras.cpp + SpellAuras.h + SpellAuraEffects.cpp + SpellAuraEffects.h + Spell.cpp + SpellEffects.cpp + Spell.h + SkillDiscovery.cpp + SkillDiscovery.h + SkillExtraItems.cpp + SkillExtraItems.h + SpellHandler.cpp + SocialMgr.cpp + SocialMgr.h + SpellMgr.cpp + SpellMgr.h + StatSystem.cpp + TargetedMovementGenerator.cpp + TargetedMovementGenerator.h + TaxiHandler.cpp + TemporarySummon.cpp + TemporarySummon.h + TicketHandler.cpp + Tools.cpp + Tools.h + TotemAI.cpp + TotemAI.h + Totem.cpp + Totem.h + TradeHandler.cpp + Transports.cpp + Transports.h + ThreatManager.cpp + ThreatManager.h + Traveller.h + Unit.cpp + Unit.h + UnitAI.cpp + UnitAI.h + UnitEvents.h + UpdateData.cpp + UpdateData.h + UpdateFields.h + UpdateMask.h + Vehicle.cpp + Vehicle.h + VoiceChatHandler.cpp + WaypointManager.cpp + WaypointManager.h + WaypointMovementGenerator.cpp + WaypointMovementGenerator.h + Weather.cpp + Weather.h + World.cpp + World.h + WorldLog.cpp + WorldLog.h + WorldSession.cpp + WorldSession.h + WorldSocket.cpp + WorldSocket.h + WorldSocketMgr.cpp + WorldSocketMgr.h + FollowerReference.cpp + FollowerReference.h + FollowerRefManager.h + GroupReference.cpp + GroupReference.h + GroupRefManager.h + OutdoorPvPImpl.h + ZoneScript.h +) + +include_directories( + ${ACE_INCLUDE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/dep/include + ${CMAKE_SOURCE_DIR}/src/framework + ${CMAKE_SOURCE_DIR}/src/shared + ${CMAKE_SOURCE_DIR}/src/shared/vmap + ${CMAKE_SOURCE_DIR}/src/shared/Database + ${CMAKE_SOURCE_DIR}/src/game + ${MYSQL_INCLUDE_DIR} +) + +if(NOT DO_SCRIPTS) + SET(game_STAT_SRCS ${game_STAT_SRCS} + ScriptedEscortAI.cpp + ScriptedEscortAI.h + ScriptedPch.cpp + ScriptedPch.h + ScriptedCreature.cpp + ScriptedCreature.h + ScriptedFollowerAI.cpp + ScriptedFollowerAI.h + ScriptedGossip.h + ScriptedGuardAI.cpp + ScriptedGuardAI.h + ScriptedInstance.h + ScriptedSimpleAI.cpp + ScriptedSimpleAI.h + ) + message("-- Added Script Engine to GAME lib") +endif(NOT DO_SCRIPTS) + +add_library(game STATIC ${game_STAT_SRCS}) +ADD_DEPENDENCIES(game revision.h) + +# Generate precompiled header +IF(DO_PCH AND CMAKE_COMPILER_IS_GNUCXX) + ADD_PRECOMPILED_HEADER(game ${CMAKE_SOURCE_DIR}/src/game/pchlinux.h) +ENDIF(DO_PCH AND CMAKE_COMPILER_IS_GNUCXX) + diff --git a/src/server/game/Calendar.cpp b/src/server/game/Calendar.cpp new file mode 100644 index 00000000000..0c1efb20f87 --- /dev/null +++ b/src/server/game/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/Calendar.h b/src/server/game/Calendar.h new file mode 100644 index 00000000000..2d35a6c23c7 --- /dev/null +++ b/src/server/game/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/CalendarHandler.cpp b/src/server/game/CalendarHandler.cpp new file mode 100644 index 00000000000..f6679c5d5ac --- /dev/null +++ b/src/server/game/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 new file mode 100644 index 00000000000..49e0329ace6 --- /dev/null +++ b/src/server/game/Cell.h @@ -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 + */ + +#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 new file mode 100644 index 00000000000..d906e81a5c9 --- /dev/null +++ b/src/server/game/CellImpl.h @@ -0,0 +1,297 @@ +/* + * 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 new file mode 100644 index 00000000000..0047892972b --- /dev/null +++ b/src/server/game/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/Channel.h b/src/server/game/Channel.h new file mode 100644 index 00000000000..d0b5923e30e --- /dev/null +++ b/src/server/game/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/ChannelHandler.cpp b/src/server/game/ChannelHandler.cpp new file mode 100644 index 00000000000..0f615579cb6 --- /dev/null +++ b/src/server/game/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/ChannelMgr.cpp b/src/server/game/ChannelMgr.cpp new file mode 100644 index 00000000000..f31d3ffde50 --- /dev/null +++ b/src/server/game/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/ChannelMgr.h b/src/server/game/ChannelMgr.h new file mode 100644 index 00000000000..6f3b7c415ae --- /dev/null +++ b/src/server/game/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/CharacterHandler.cpp b/src/server/game/CharacterHandler.cpp new file mode 100644 index 00000000000..416827d73ea --- /dev/null +++ b/src/server/game/CharacterHandler.cpp @@ -0,0 +1,1406 @@ +/* + * 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 new file mode 100644 index 00000000000..6e1adffa625 --- /dev/null +++ b/src/server/game/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.h b/src/server/game/Chat.h new file mode 100644 index 00000000000..58e6f6214c8 --- /dev/null +++ b/src/server/game/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/ChatHandler.cpp b/src/server/game/ChatHandler.cpp new file mode 100644 index 00000000000..88e2b5473a5 --- /dev/null +++ b/src/server/game/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/CombatAI.cpp b/src/server/game/CombatAI.cpp new file mode 100644 index 00000000000..0d0ff17ffd7 --- /dev/null +++ b/src/server/game/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/CombatAI.h b/src/server/game/CombatAI.h new file mode 100644 index 00000000000..8626b38dd37 --- /dev/null +++ b/src/server/game/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/CombatHandler.cpp b/src/server/game/CombatHandler.cpp new file mode 100644 index 00000000000..404b70c1c84 --- /dev/null +++ b/src/server/game/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/ConditionMgr.cpp b/src/server/game/ConditionMgr.cpp new file mode 100644 index 00000000000..8e5a7e5677e --- /dev/null +++ b/src/server/game/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.h b/src/server/game/ConditionMgr.h new file mode 100644 index 00000000000..bc2ce8d01a2 --- /dev/null +++ b/src/server/game/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 new file mode 100644 index 00000000000..43c6052d2d3 --- /dev/null +++ b/src/server/game/ConfusedMovementGenerator.cpp @@ -0,0 +1,181 @@ +/* + * 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 new file mode 100644 index 00000000000..e1a71151d37 --- /dev/null +++ b/src/server/game/ConfusedMovementGenerator.h @@ -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 + */ + +#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 new file mode 100644 index 00000000000..510ea13e78b --- /dev/null +++ b/src/server/game/Corpse.cpp @@ -0,0 +1,243 @@ +/* + * 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 new file mode 100644 index 00000000000..bab95e99d14 --- /dev/null +++ b/src/server/game/Corpse.h @@ -0,0 +1,97 @@ +/* + * 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 new file mode 100644 index 00000000000..fbfae17a48b --- /dev/null +++ b/src/server/game/Creature.cpp @@ -0,0 +1,2423 @@ +/* + * 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 new file mode 100644 index 00000000000..d2d93d787da --- /dev/null +++ b/src/server/game/Creature.h @@ -0,0 +1,738 @@ +/* + * 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 new file mode 100644 index 00000000000..fb8f37ae492 --- /dev/null +++ b/src/server/game/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/CreatureAI.h b/src/server/game/CreatureAI.h new file mode 100644 index 00000000000..c03d3dd09d0 --- /dev/null +++ b/src/server/game/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/CreatureAIFactory.h b/src/server/game/CreatureAIFactory.h new file mode 100644 index 00000000000..6aa69eaaa29 --- /dev/null +++ b/src/server/game/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/CreatureAIImpl.h b/src/server/game/CreatureAIImpl.h new file mode 100644 index 00000000000..d2f96ffcabc --- /dev/null +++ b/src/server/game/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/CreatureAIRegistry.cpp b/src/server/game/CreatureAIRegistry.cpp new file mode 100644 index 00000000000..9db30a0a5c4 --- /dev/null +++ b/src/server/game/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/CreatureAIRegistry.h b/src/server/game/CreatureAIRegistry.h new file mode 100644 index 00000000000..0d83d29cf45 --- /dev/null +++ b/src/server/game/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/CreatureAISelector.cpp b/src/server/game/CreatureAISelector.cpp new file mode 100644 index 00000000000..d3fd5a8aed9 --- /dev/null +++ b/src/server/game/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/CreatureAISelector.h b/src/server/game/CreatureAISelector.h new file mode 100644 index 00000000000..98eeee809d2 --- /dev/null +++ b/src/server/game/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/CreatureEventAI.cpp b/src/server/game/CreatureEventAI.cpp new file mode 100644 index 00000000000..47c8e9e6ad8 --- /dev/null +++ b/src/server/game/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/CreatureEventAI.h b/src/server/game/CreatureEventAI.h new file mode 100644 index 00000000000..2fc5de46089 --- /dev/null +++ b/src/server/game/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/CreatureEventAIMgr.cpp b/src/server/game/CreatureEventAIMgr.cpp new file mode 100644 index 00000000000..83d62ca74dc --- /dev/null +++ b/src/server/game/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/CreatureEventAIMgr.h b/src/server/game/CreatureEventAIMgr.h new file mode 100644 index 00000000000..ef191b22463 --- /dev/null +++ b/src/server/game/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/CreatureGroups.cpp b/src/server/game/CreatureGroups.cpp new file mode 100644 index 00000000000..c2af59458f7 --- /dev/null +++ b/src/server/game/CreatureGroups.cpp @@ -0,0 +1,256 @@ +/* + * 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 new file mode 100644 index 00000000000..521586b7457 --- /dev/null +++ b/src/server/game/CreatureGroups.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 _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 new file mode 100644 index 00000000000..bd72026d753 --- /dev/null +++ b/src/server/game/DBCEnums.h @@ -0,0 +1,397 @@ +/* +* 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 new file mode 100644 index 00000000000..ba0e3af9a64 --- /dev/null +++ b/src/server/game/DBCStores.cpp @@ -0,0 +1,836 @@ +/* + * 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 new file mode 100644 index 00000000000..b14814e07a1 --- /dev/null +++ b/src/server/game/DBCStores.h @@ -0,0 +1,177 @@ +/* + * 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 new file mode 100644 index 00000000000..0fc0d1251f5 --- /dev/null +++ b/src/server/game/DBCStructure.h @@ -0,0 +1,1930 @@ +/* + * 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<