aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/CMakeLists.txt1
-rw-r--r--src/server/authserver/Realms/RealmList.cpp42
-rw-r--r--src/server/authserver/Realms/RealmList.h6
-rw-r--r--src/server/authserver/Server/AuthSocket.cpp32
-rw-r--r--src/server/authserver/Server/AuthSocket.h5
-rw-r--r--src/server/authserver/authserver.conf.dist11
-rw-r--r--src/server/collision/BoundingIntervalHierarchy.cpp12
-rw-r--r--src/server/collision/BoundingIntervalHierarchy.h33
-rw-r--r--src/server/collision/BoundingIntervalHierarchyWrapper.h22
-rw-r--r--src/server/collision/CMakeLists.txt2
-rw-r--r--src/server/collision/DynamicTree.cpp61
-rw-r--r--src/server/collision/DynamicTree.h26
-rw-r--r--src/server/collision/Management/MMapFactory.cpp52
-rw-r--r--src/server/collision/Management/MMapFactory.h50
-rw-r--r--src/server/collision/Management/MMapManager.cpp302
-rw-r--r--src/server/collision/Management/MMapManager.h84
-rw-r--r--src/server/collision/Management/VMapManager2.cpp12
-rw-r--r--src/server/collision/Management/VMapManager2.h2
-rw-r--r--src/server/collision/Maps/MapTree.cpp22
-rw-r--r--src/server/collision/Maps/MapTree.h3
-rw-r--r--src/server/collision/Models/GameObjectModel.cpp16
-rw-r--r--src/server/collision/Models/ModelInstance.h2
-rw-r--r--src/server/collision/Models/WorldModel.h30
-rw-r--r--src/server/collision/RegularGrid.h16
-rw-r--r--src/server/collision/VMapDefinitions.h12
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp35
-rw-r--r--src/server/game/AI/CoreAI/ReactorAI.cpp23
-rw-r--r--src/server/game/AI/CoreAI/ReactorAI.h4
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp11
-rw-r--r--src/server/game/AI/CreatureAIImpl.h330
-rw-r--r--src/server/game/AI/CreatureAISelector.cpp6
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp19
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h18
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp11
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp122
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h1
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp1
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h8
-rw-r--r--src/server/game/Accounts/AccountMgr.cpp225
-rw-r--r--src/server/game/Accounts/AccountMgr.h27
-rw-r--r--src/server/game/Accounts/RBAC.cpp336
-rw-r--r--src/server/game/Accounts/RBAC.h410
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp6
-rw-r--r--src/server/game/Battlefield/Battlefield.cpp13
-rw-r--r--src/server/game/Battlefield/Battlefield.h2
-rw-r--r--src/server/game/Battlegrounds/ArenaTeam.cpp58
-rw-r--r--src/server/game/Battlegrounds/ArenaTeam.h56
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.cpp25
-rw-r--r--src/server/game/CMakeLists.txt2
-rw-r--r--src/server/game/Chat/Chat.cpp26
-rw-r--r--src/server/game/Combat/ThreatManager.cpp19
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp2
-rw-r--r--src/server/game/Conditions/DisableMgr.cpp31
-rw-r--r--src/server/game/Conditions/DisableMgr.h8
-rw-r--r--src/server/game/DataStores/DBCStores.cpp7
-rw-r--r--src/server/game/DungeonFinding/LFG.cpp108
-rw-r--r--src/server/game/DungeonFinding/LFG.h9
-rw-r--r--src/server/game/DungeonFinding/LFGGroupData.cpp5
-rw-r--r--src/server/game/DungeonFinding/LFGGroupData.h5
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp173
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.h173
-rw-r--r--src/server/game/DungeonFinding/LFGPlayerData.cpp5
-rw-r--r--src/server/game/DungeonFinding/LFGPlayerData.h5
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.cpp11
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.h5
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.cpp23
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.h6
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp10
-rw-r--r--src/server/game/Entities/Creature/Creature.h1
-rw-r--r--src/server/game/Entities/Creature/GossipDef.cpp35
-rw-r--r--src/server/game/Entities/Creature/GossipDef.h2
-rw-r--r--src/server/game/Entities/Object/Object.cpp126
-rw-r--r--src/server/game/Entities/Object/Object.h9
-rw-r--r--src/server/game/Entities/Player/Player.cpp351
-rw-r--r--src/server/game/Entities/Player/Player.h81
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp6
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp601
-rw-r--r--src/server/game/Entities/Unit/Unit.h46
-rwxr-xr-x[-rw-r--r--]src/server/game/Entities/Vehicle/Vehicle.cpp3
-rw-r--r--src/server/game/Entities/Vehicle/VehicleDefines.h2
-rw-r--r--src/server/game/Events/GameEventMgr.cpp1
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp155
-rw-r--r--src/server/game/Globals/ObjectMgr.h14
-rw-r--r--src/server/game/Groups/Group.cpp4
-rw-r--r--src/server/game/Guilds/Guild.cpp16
-rw-r--r--src/server/game/Handlers/AddonHandler.cpp28
-rw-r--r--src/server/game/Handlers/BattleGroundHandler.cpp6
-rw-r--r--src/server/game/Handlers/ChatHandler.cpp6
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp4
-rw-r--r--src/server/game/Handlers/LFGHandler.cpp127
-rw-r--r--src/server/game/Handlers/MailHandler.cpp9
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp6
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp2
-rw-r--r--src/server/game/Handlers/PetHandler.cpp7
-rw-r--r--src/server/game/Handlers/TicketHandler.cpp2
-rw-r--r--src/server/game/Handlers/TradeHandler.cpp15
-rw-r--r--src/server/game/Loot/LootMgr.cpp426
-rw-r--r--src/server/game/Loot/LootMgr.h14
-rw-r--r--src/server/game/Maps/Map.cpp49
-rw-r--r--src/server/game/Maps/Map.h3
-rw-r--r--src/server/game/Maps/MapInstanced.cpp2
-rw-r--r--src/server/game/Miscellaneous/Language.h71
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h55
-rw-r--r--src/server/game/Movement/FollowerRefManager.h1
-rw-r--r--src/server/game/Movement/FollowerReference.cpp1
-rw-r--r--src/server/game/Movement/FollowerReference.h1
-rw-r--r--src/server/game/Movement/MotionMaster.cpp72
-rw-r--r--src/server/game/Movement/MotionMaster.h7
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerator.h31
-rw-r--r--src/server/game/Movement/MovementGeneratorImpl.h1
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp150
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h15
-rw-r--r--[-rwxr-xr-x]src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp387
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h29
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp47
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h11
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp55
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h27
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp107
-rw-r--r--src/server/game/Movement/MovementGenerators/PointMovementGenerator.h30
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp52
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h13
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp302
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h63
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp426
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h33
-rw-r--r--src/server/game/Movement/PathGenerator.cpp803
-rw-r--r--src/server/game/Movement/PathGenerator.h135
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.h3
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.cpp87
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.h21
-rw-r--r--src/server/game/Movement/Spline/MovementPacketBuilder.cpp2
-rw-r--r--src/server/game/Movement/Waypoints/Path.h25
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.cpp2
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.h1
-rw-r--r--src/server/game/Scripting/ScriptLoader.cpp4
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp2
-rw-r--r--src/server/game/Scripting/ScriptMgr.h3
-rw-r--r--src/server/game/Server/Protocol/PacketLog.cpp2
-rw-r--r--src/server/game/Server/WorldSession.cpp31
-rw-r--r--src/server/game/Server/WorldSession.h29
-rw-r--r--src/server/game/Server/WorldSocket.cpp19
-rw-r--r--src/server/game/Skills/SkillDiscovery.cpp2
-rw-r--r--src/server/game/Skills/SkillExtraItems.cpp10
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp311
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp15
-rw-r--r--src/server/game/Spells/Spell.cpp34
-rw-r--r--src/server/game/Spells/Spell.h2
-rw-r--r--src/server/game/Spells/SpellEffects.cpp237
-rw-r--r--src/server/game/Spells/SpellInfo.cpp33
-rw-r--r--src/server/game/Spells/SpellMgr.cpp2
-rw-r--r--src/server/game/Spells/SpellScript.cpp2
-rw-r--r--src/server/game/Tickets/TicketMgr.cpp2
-rw-r--r--src/server/game/Tools/CharacterDatabaseCleaner.cpp1
-rw-r--r--src/server/game/Warden/Warden.cpp2
-rw-r--r--src/server/game/Warden/WardenCheckMgr.cpp10
-rw-r--r--src/server/game/Warden/WardenMac.cpp2
-rw-r--r--src/server/game/Warden/WardenWin.cpp2
-rw-r--r--src/server/game/Weather/WeatherMgr.cpp2
-rw-r--r--src/server/game/World/World.cpp53
-rw-r--r--src/server/game/World/World.h3
-rw-r--r--src/server/scripts/CMakeLists.txt2
-rw-r--r--src/server/scripts/Commands/CMakeLists.txt2
-rw-r--r--src/server/scripts/Commands/cs_account.cpp34
-rw-r--r--src/server/scripts/Commands/cs_disable.cpp32
-rw-r--r--src/server/scripts/Commands/cs_lfg.cpp10
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp168
-rw-r--r--src/server/scripts/Commands/cs_mmaps.cpp294
-rw-r--r--src/server/scripts/Commands/cs_modify.cpp9
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp44
-rw-r--r--src/server/scripts/Commands/cs_rbac.cpp780
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp91
-rw-r--r--src/server/scripts/EasternKingdoms/BaradinHold/boss_alizabal.cpp13
-rw-r--r--src/server/scripts/EasternKingdoms/BaradinHold/instance_baradin_hold.cpp7
-rw-r--r--src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp11
-rw-r--r--src/server/scripts/EasternKingdoms/BlackwingLair/boss_firemaw.cpp14
-rw-r--r--src/server/scripts/EasternKingdoms/BlackwingLair/boss_flamegor.cpp12
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp75
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp5
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp31
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp7
-rw-r--r--src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp7
-rw-r--r--src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp7
-rw-r--r--src/server/scripts/Kalimdor/CMakeLists.txt6
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp9
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp (renamed from src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_epoch.cpp)0
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp (renamed from src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite.cpp)0
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp (renamed from src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm.cpp)0
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/boss_chrono_lord_deja.cpp4
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/instance_dark_portal.cpp2
-rw-r--r--src/server/scripts/Kalimdor/Firelands/boss_alysrazor.cpp2
-rw-r--r--src/server/scripts/Kalimdor/Firelands/firelands.h2
-rw-r--r--src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp4
-rw-r--r--src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp5
-rw-r--r--src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp9
-rw-r--r--src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp32
-rw-r--r--src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_fankriss.cpp4
-rw-r--r--src/server/scripts/Kalimdor/zone_desolace.cpp108
-rw-r--r--src/server/scripts/Kalimdor/zone_thunder_bluff.cpp3
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp4
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp4
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_herald_volazj.cpp2
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp6
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp6
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp2
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp2
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp2
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp2
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp11
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp11
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp41
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp8
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp68
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp9
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp6
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp2
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp22
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp8
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp38
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp27
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp13
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp259
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp13
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp5
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp73
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp5
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp6
-rw-r--r--src/server/scripts/Northrend/Nexus/Nexus/boss_anomalus.cpp5
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp3
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp13
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp34
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp10
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp5
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp4
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp8
-rw-r--r--src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp6
-rw-r--r--src/server/scripts/Northrend/zone_borean_tundra.cpp4
-rw-r--r--src/server/scripts/Northrend/zone_crystalsong_forest.cpp7
-rw-r--r--src/server/scripts/Northrend/zone_dalaran.cpp4
-rw-r--r--src/server/scripts/Northrend/zone_icecrown.cpp20
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp10
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_illidan.cpp3
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_supremus.cpp7
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp3
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp62
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_hydross_the_unstable.cpp3
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp13
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp7
-rw-r--r--src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp3
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp10
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp7
-rw-r--r--src/server/scripts/Outland/zone_shadowmoon_valley.cpp75
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp161
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp280
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp555
-rw-r--r--src/server/scripts/Spells/spell_holiday.cpp87
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp78
-rw-r--r--src/server/scripts/Spells/spell_item.cpp576
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp362
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp251
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp269
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp207
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp98
-rw-r--r--src/server/scripts/Spells/spell_warlock.cpp329
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp373
-rw-r--r--src/server/scripts/World/achievement_scripts.cpp25
-rw-r--r--src/server/scripts/World/npcs_special.cpp11
-rw-r--r--src/server/shared/CMakeLists.txt1
-rw-r--r--src/server/shared/Database/DatabaseWorkerPool.h6
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp4
-rwxr-xr-xsrc/server/shared/Database/Implementation/CharacterDatabase.h2
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.cpp29
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.h11
-rw-r--r--src/server/shared/Database/Implementation/WorldDatabase.cpp4
-rw-r--r--src/server/shared/Database/Implementation/WorldDatabase.h2
-rw-r--r--src/server/shared/Debugging/Errors.h2
-rw-r--r--src/server/shared/Debugging/WheatyExceptionReport.cpp187
-rw-r--r--src/server/shared/Logging/Appender.h6
-rw-r--r--src/server/shared/Logging/AppenderFile.cpp70
-rw-r--r--src/server/shared/Logging/AppenderFile.h5
-rw-r--r--src/server/shared/Logging/Log.cpp28
-rw-r--r--src/server/shared/Memory.h19
-rw-r--r--src/server/shared/Packets/ByteBuffer.h2
-rw-r--r--src/server/shared/Utilities/Util.cpp16
-rw-r--r--src/server/shared/Utilities/Util.h8
-rw-r--r--src/server/worldserver/CMakeLists.txt2
-rw-r--r--src/server/worldserver/RemoteAccess/RASocket.cpp23
-rw-r--r--src/server/worldserver/TCSoap/TCSoap.cpp12
-rw-r--r--src/server/worldserver/worldserver.conf.dist53
-rw-r--r--src/tools/CMakeLists.txt2
-rw-r--r--src/tools/map_extractor/CMakeLists.txt2
-rw-r--r--src/tools/map_extractor/System.cpp44
-rw-r--r--src/tools/map_extractor/adt.cpp21
-rw-r--r--src/tools/map_extractor/loadlib.cpp4
-rw-r--r--src/tools/map_extractor/loadlib/loadlib.h6
-rw-r--r--src/tools/map_extractor/wdt.cpp12
-rw-r--r--src/tools/mesh_extractor/ADT.cpp53
-rw-r--r--src/tools/mesh_extractor/ADT.h29
-rw-r--r--src/tools/mesh_extractor/CMakeLists.txt50
-rw-r--r--src/tools/mesh_extractor/Cache.h63
-rw-r--r--src/tools/mesh_extractor/Chunk.cpp31
-rw-r--r--src/tools/mesh_extractor/Chunk.h20
-rw-r--r--src/tools/mesh_extractor/ChunkedData.cpp74
-rw-r--r--src/tools/mesh_extractor/ChunkedData.h21
-rw-r--r--src/tools/mesh_extractor/Constants.h57
-rw-r--r--src/tools/mesh_extractor/ContinentBuilder.cpp144
-rw-r--r--src/tools/mesh_extractor/ContinentBuilder.h30
-rw-r--r--src/tools/mesh_extractor/DBC.cpp70
-rw-r--r--src/tools/mesh_extractor/DBC.h53
-rw-r--r--src/tools/mesh_extractor/DoodadHandler.cpp109
-rw-r--r--src/tools/mesh_extractor/DoodadHandler.h57
-rw-r--r--src/tools/mesh_extractor/Geometry.cpp123
-rw-r--r--src/tools/mesh_extractor/Geometry.h23
-rw-r--r--src/tools/mesh_extractor/LiquidHandler.cpp102
-rw-r--r--src/tools/mesh_extractor/LiquidHandler.h21
-rw-r--r--src/tools/mesh_extractor/MPQ.cpp118
-rw-r--r--src/tools/mesh_extractor/MPQ.h88
-rw-r--r--src/tools/mesh_extractor/MPQManager.cpp108
-rw-r--r--src/tools/mesh_extractor/MPQManager.h36
-rw-r--r--src/tools/mesh_extractor/MapChunk.cpp74
-rw-r--r--src/tools/mesh_extractor/MapChunk.h24
-rw-r--r--src/tools/mesh_extractor/MeshExtractor.cpp428
-rw-r--r--src/tools/mesh_extractor/Model.cpp67
-rw-r--r--src/tools/mesh_extractor/Model.h23
-rw-r--r--src/tools/mesh_extractor/ObjectDataHandler.cpp21
-rw-r--r--src/tools/mesh_extractor/ObjectDataHandler.h15
-rw-r--r--src/tools/mesh_extractor/TileBuilder.cpp311
-rw-r--r--src/tools/mesh_extractor/TileBuilder.h30
-rw-r--r--src/tools/mesh_extractor/Utils.cpp568
-rw-r--r--src/tools/mesh_extractor/Utils.h381
-rw-r--r--src/tools/mesh_extractor/WDT.cpp60
-rw-r--r--src/tools/mesh_extractor/WDT.h27
-rw-r--r--src/tools/mesh_extractor/WorldModelGroup.cpp143
-rw-r--r--src/tools/mesh_extractor/WorldModelGroup.h38
-rw-r--r--src/tools/mesh_extractor/WorldModelHandler.cpp204
-rw-r--r--src/tools/mesh_extractor/WorldModelHandler.h47
-rw-r--r--src/tools/mesh_extractor/WorldModelRoot.cpp74
-rw-r--r--src/tools/mesh_extractor/WorldModelRoot.h26
-rw-r--r--src/tools/mesh_extractor/readme6
-rw-r--r--src/tools/mmaps_generator/CMakeLists.txt55
-rw-r--r--src/tools/mmaps_generator/Info/readme.txt66
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.cpp277
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.h53
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp969
-rw-r--r--src/tools/mmaps_generator/MapBuilder.h187
-rw-r--r--src/tools/mmaps_generator/PathCommon.h161
-rw-r--r--src/tools/mmaps_generator/PathGenerator.cpp296
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.cpp933
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.h132
-rw-r--r--src/tools/mmaps_generator/VMapExtensions.cpp70
-rw-r--r--src/tools/vmap4_assembler/CMakeLists.txt1
-rw-r--r--src/tools/vmap4_extractor/adtfile.cpp3
-rw-r--r--src/tools/vmap4_extractor/model.cpp4
-rw-r--r--src/tools/vmap4_extractor/vmapexport.cpp3
-rw-r--r--src/tools/vmap4_extractor/wmo.cpp4
359 files changed, 18647 insertions, 4989 deletions
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index 7d85cdb6e21..e8816ea8816 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -30,5 +30,6 @@ if( SERVERS )
else()
if( TOOLS )
add_subdirectory(collision)
+ add_subdirectory(shared)
endif()
endif()
diff --git a/src/server/authserver/Realms/RealmList.cpp b/src/server/authserver/Realms/RealmList.cpp
index 72873e40ce5..b4becc96451 100644
--- a/src/server/authserver/Realms/RealmList.cpp
+++ b/src/server/authserver/Realms/RealmList.cpp
@@ -31,12 +31,12 @@ void RealmList::Initialize(uint32 updateInterval)
UpdateRealms(true);
}
-void RealmList::UpdateRealm(uint32 ID, const std::string& name, ACE_INET_Addr const& address, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build)
+void RealmList::UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build)
{
// Create new if not exist or update existed
Realm& realm = m_realms[name];
- realm.m_ID = ID;
+ realm.m_ID = id;
realm.name = name;
realm.icon = icon;
realm.flag = flag;
@@ -45,7 +45,9 @@ void RealmList::UpdateRealm(uint32 ID, const std::string& name, ACE_INET_Addr co
realm.populationLevel = popu;
// Append port to IP address.
- address.addr_to_string(realm.address, ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16);
+ realm.ExternalAddress = address;
+ realm.LocalAddress = localAddr;
+ realm.LocalSubnetMask = localSubmask;
realm.gamebuild = build;
}
@@ -77,23 +79,27 @@ void RealmList::UpdateRealms(bool init)
do
{
Field* fields = result->Fetch();
- uint32 realmId = fields[0].GetUInt32();
- std::string name = fields[1].GetString();
- std::string address = fields[2].GetString();
- uint16 port = fields[3].GetUInt16();
- uint8 icon = fields[4].GetUInt8();
- RealmFlags flag = RealmFlags(fields[5].GetUInt8());
- uint8 timezone = fields[6].GetUInt8();
- uint8 allowedSecurityLevel = fields[7].GetUInt8();
- float pop = fields[8].GetFloat();
- uint32 build = fields[9].GetUInt32();
-
- ACE_INET_Addr addr(port, address.c_str(), AF_INET);
-
- UpdateRealm(realmId, name, addr, icon, flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build);
+ uint32 realmId = fields[0].GetUInt32();
+ std::string name = fields[1].GetString();
+ std::string externalAddress = fields[2].GetString();
+ std::string localAddress = fields[3].GetString();
+ std::string localSubmask = fields[4].GetString();
+ uint16 port = fields[5].GetUInt16();
+ uint8 icon = fields[6].GetUInt8();
+ RealmFlags flag = RealmFlags(fields[7].GetUInt8());
+ uint8 timezone = fields[8].GetUInt8();
+ uint8 allowedSecurityLevel = fields[9].GetUInt8();
+ float pop = fields[10].GetFloat();
+ uint32 build = fields[11].GetUInt32();
+
+ ACE_INET_Addr externalAddr(port, externalAddress.c_str(), AF_INET);
+ ACE_INET_Addr localAddr(port, localAddress.c_str(), AF_INET);
+ ACE_INET_Addr submask(0, localSubmask.c_str(), AF_INET);
+
+ UpdateRealm(realmId, name, externalAddr, localAddr, submask, icon, flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build);
if (init)
- sLog->outInfo(LOG_FILTER_AUTHSERVER, "Added realm \"%s\" at %s.", name.c_str(), m_realms[name].address);
+ sLog->outInfo(LOG_FILTER_AUTHSERVER, "Added realm \"%s\" at %s:%u.", name.c_str(), m_realms[name].ExternalAddress.get_host_addr(), port);
}
while (result->NextRow());
}
diff --git a/src/server/authserver/Realms/RealmList.h b/src/server/authserver/Realms/RealmList.h
index 1949c34df9a..68e6524c334 100644
--- a/src/server/authserver/Realms/RealmList.h
+++ b/src/server/authserver/Realms/RealmList.h
@@ -40,7 +40,9 @@ enum RealmFlags
// Storage object for a realm
struct Realm
{
- char address[ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16];
+ ACE_INET_Addr ExternalAddress;
+ ACE_INET_Addr LocalAddress;
+ ACE_INET_Addr LocalSubnetMask;
std::string name;
uint8 icon;
RealmFlags flag;
@@ -72,7 +74,7 @@ public:
private:
void UpdateRealms(bool init=false);
- void UpdateRealm(uint32 ID, const std::string& name, ACE_INET_Addr const& address, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build);
+ void UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build);
RealmMap m_realms;
uint32 m_UpdateInterval;
diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp
index 8ab4ab8a1a2..32ddf029f1c 100644
--- a/src/server/authserver/Server/AuthSocket.cpp
+++ b/src/server/authserver/Server/AuthSocket.cpp
@@ -207,7 +207,7 @@ AuthSocket::AuthSocket(RealmSocket& socket) : pPatch(NULL), socket_(socket)
// Close patch file descriptor before leaving
AuthSocket::~AuthSocket(void) {}
-// Accept the connection and set the s random value for SRP6
+// Accept the connection
void AuthSocket::OnAccept(void)
{
sLog->outDebug(LOG_FILTER_AUTHSERVER, "'%s:%d' Accepting connection", socket().getRemoteAddress().c_str(), socket().getRemotePort());
@@ -818,6 +818,28 @@ bool AuthSocket::_HandleReconnectProof()
}
}
+ACE_INET_Addr const& AuthSocket::GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr)
+{
+ // Attempt to send best address for client
+ if (clientAddr.is_loopback())
+ {
+ // Try guessing if realm is also connected locally
+ if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback())
+ return clientAddr;
+
+ // Assume that user connecting from the machine that authserver is located on
+ // has all realms available in his local network
+ return realm.LocalAddress;
+ }
+
+ // Check if connecting client is in the same network
+ if (IsIPAddrInNetwork(realm.LocalAddress, clientAddr, realm.LocalSubnetMask))
+ return realm.LocalAddress;
+
+ // Return external IP
+ return realm.ExternalAddress;
+}
+
// Realm List command handler
bool AuthSocket::_HandleRealmList()
{
@@ -845,6 +867,9 @@ bool AuthSocket::_HandleRealmList()
// Update realm list if need
sRealmList->UpdateIfNeed();
+ ACE_INET_Addr clientAddr;
+ socket().peer().get_remote_addr(clientAddr);
+
// Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
ByteBuffer pkt;
@@ -876,6 +901,9 @@ bool AuthSocket::_HandleRealmList()
name = ss.str();
}
+ // We don't need the port number from which client connects with but the realm's port
+ clientAddr.set_port_number(i->second.ExternalAddress.get_port_number());
+
uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
uint8 AmountOfCharacters = 0;
@@ -891,7 +919,7 @@ bool AuthSocket::_HandleRealmList()
pkt << lock; // if 1, then realm locked
pkt << uint8(flag); // RealmFlags
pkt << name;
- pkt << i->second.address;
+ pkt << GetAddressString(GetAddressForClient(i->second, clientAddr));
pkt << i->second.populationLevel;
pkt << AmountOfCharacters;
pkt << i->second.timezone; // realm category
diff --git a/src/server/authserver/Server/AuthSocket.h b/src/server/authserver/Server/AuthSocket.h
index 87fd092381e..6c13f85a022 100644
--- a/src/server/authserver/Server/AuthSocket.h
+++ b/src/server/authserver/Server/AuthSocket.h
@@ -23,6 +23,9 @@
#include "BigNumber.h"
#include "RealmSocket.h"
+class ACE_INET_Addr;
+struct Realm;
+
// Handle login commands
class AuthSocket: public RealmSocket::Session
{
@@ -36,6 +39,8 @@ public:
virtual void OnAccept(void);
virtual void OnClose(void);
+ static ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr);
+
bool _HandleLogonChallenge();
bool _HandleLogonProof();
bool _HandleReconnectChallenge();
diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist
index 67d22c49da1..dda19c3b849 100644
--- a/src/server/authserver/authserver.conf.dist
+++ b/src/server/authserver/authserver.conf.dist
@@ -154,7 +154,7 @@ LoginDatabase.WorkerThreads = 1
# Appender config values: Given a appender "name"
# Appender.name
# Description: Defines 'where to log'
-# Format: Type,LogLevel,Flags,optional1,optional2
+# Format: Type,LogLevel,Flags,optional1,optional2,optional3
#
# Type
# 0 - (None)
@@ -205,6 +205,13 @@ LoginDatabase.WorkerThreads = 1
# a - (Append)
# w - (Overwrite)
#
+# MaxFileSize: Maximum file size of the log file before creating a new log file
+# (read as optional3 if Type = File)
+# Size is measured in bytes expressed in a 64-bit unsigned integer.
+# Maximum value is 4294967295 (4 gb). Leave blank for no limit.
+# NOTE: Does not work with dynamic filenames.
+# Example: 536870912 (512 mb)
+#
Appender.Console=1,2,0
Appender.Auth=2,2,0,Auth.log,w
@@ -250,4 +257,4 @@ Logger.Root=0,3,Console Auth
Loggers=Root
#
-################################################################################################### \ No newline at end of file
+###################################################################################################
diff --git a/src/server/collision/BoundingIntervalHierarchy.cpp b/src/server/collision/BoundingIntervalHierarchy.cpp
index ad3753ea3c9..bca738d1ff6 100644
--- a/src/server/collision/BoundingIntervalHierarchy.cpp
+++ b/src/server/collision/BoundingIntervalHierarchy.cpp
@@ -18,6 +18,12 @@
#include "BoundingIntervalHierarchy.h"
+#if defined __APPLE__
+ #define isnan std::isnan
+#elif defined _MSC_VER
+ #define isnan _isnan
+#endif
+
void BIH::buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats)
{
// create space for the first node
@@ -51,7 +57,7 @@ void BIH::subdivide(int left, int right, std::vector<uint32> &tempTree, buildDat
prevAxis = axis;
prevSplit = split;
// perform quick consistency checks
- Vector3 d( gridBox.hi - gridBox.lo );
+ G3D::Vector3 d( gridBox.hi - gridBox.lo );
if (d.x < 0 || d.y < 0 || d.z < 0)
throw std::logic_error("negative node extents");
for (int i = 0; i < 3; i++)
@@ -255,11 +261,11 @@ bool BIH::writeToFile(FILE* wf) const
bool BIH::readFromFile(FILE* rf)
{
uint32 treeSize;
- Vector3 lo, hi;
+ G3D::Vector3 lo, hi;
uint32 check=0, count=0;
check += fread(&lo, sizeof(float), 3, rf);
check += fread(&hi, sizeof(float), 3, rf);
- bounds = AABox(lo, hi);
+ bounds = G3D::AABox(lo, hi);
check += fread(&treeSize, sizeof(uint32), 1, rf);
tree.resize(treeSize);
check += fread(&tree[0], sizeof(uint32), treeSize, rf);
diff --git a/src/server/collision/BoundingIntervalHierarchy.h b/src/server/collision/BoundingIntervalHierarchy.h
index 7cbaedbfba6..997f9c99e5f 100644
--- a/src/server/collision/BoundingIntervalHierarchy.h
+++ b/src/server/collision/BoundingIntervalHierarchy.h
@@ -31,20 +31,8 @@
#include <limits>
#include <cmath>
-#ifdef __APPLE__
- #define isnan(x) ( std::isnan(x) )
-#endif
-
#define MAX_STACK_SIZE 64
-#ifdef _MSC_VER
- #define isnan(x) _isnan(x)
-#endif
-
-using G3D::Vector3;
-using G3D::AABox;
-using G3D::Ray;
-
static inline uint32 floatToRawIntBits(float f)
{
union
@@ -69,7 +57,7 @@ static inline float intBitsToFloat(uint32 i)
struct AABound
{
- Vector3 lo, hi;
+ G3D::Vector3 lo, hi;
};
/** Bounding Interval Hierarchy Class.
@@ -105,12 +93,11 @@ class BIH
dat.maxPrims = leafSize;
dat.numPrims = primitives.size();
dat.indices = new uint32[dat.numPrims];
- dat.primBound = new AABox[dat.numPrims];
+ dat.primBound = new G3D::AABox[dat.numPrims];
getBounds(primitives[0], bounds);
for (uint32 i=0; i<dat.numPrims; ++i)
{
dat.indices[i] = i;
- AABox tb;
getBounds(primitives[i], dat.primBound[i]);
bounds.merge(dat.primBound[i]);
}
@@ -131,13 +118,13 @@ class BIH
uint32 primCount() const { return objects.size(); }
template<typename RayCallback>
- void intersectRay(const Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst=false) const
+ void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst=false) const
{
float intervalMin = -1.f;
float intervalMax = -1.f;
- Vector3 org = r.origin();
- Vector3 dir = r.direction();
- Vector3 invDir;
+ G3D::Vector3 org = r.origin();
+ G3D::Vector3 dir = r.direction();
+ G3D::Vector3 invDir;
for (int i=0; i<3; ++i)
{
invDir[i] = 1.f / dir[i];
@@ -270,7 +257,7 @@ class BIH
}
template<typename IsectCallback>
- void intersectPoint(const Vector3 &p, IsectCallback& intersectCallback) const
+ void intersectPoint(const G3D::Vector3 &p, IsectCallback& intersectCallback) const
{
if (!bounds.contains(p))
return;
@@ -353,12 +340,12 @@ class BIH
protected:
std::vector<uint32> tree;
std::vector<uint32> objects;
- AABox bounds;
+ G3D::AABox bounds;
struct buildData
{
uint32 *indices;
- AABox *primBound;
+ G3D::AABox *primBound;
uint32 numPrims;
int maxPrims;
};
@@ -410,4 +397,4 @@ class BIH
void subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats);
};
-#endif // _BIH_H \ No newline at end of file
+#endif // _BIH_H
diff --git a/src/server/collision/BoundingIntervalHierarchyWrapper.h b/src/server/collision/BoundingIntervalHierarchyWrapper.h
index 8a99078caab..305d57b0075 100644
--- a/src/server/collision/BoundingIntervalHierarchyWrapper.h
+++ b/src/server/collision/BoundingIntervalHierarchyWrapper.h
@@ -33,18 +33,23 @@ class BIHWrap
{
const T* const* objects;
RayCallback& _callback;
+ uint32 objects_size;
- MDLCallback(RayCallback& callback, const T* const* objects_array ) : objects(objects_array), _callback(callback) {}
+ MDLCallback(RayCallback& callback, const T* const* objects_array, uint32 objects_size ) : objects(objects_array), _callback(callback), objects_size(objects_size) {}
- bool operator() (const Ray& ray, uint32 Idx, float& MaxDist, bool /*stopAtFirst*/)
+ bool operator() (const G3D::Ray& ray, uint32 Idx, float& MaxDist, bool /*stopAtFirst*/)
{
+ if (Idx >= objects_size)
+ return false;
if (const T* obj = objects[Idx])
return _callback(ray, *obj, MaxDist/*, stopAtFirst*/);
return false;
}
- void operator() (const Vector3& p, uint32 Idx)
+ void operator() (const G3D::Vector3& p, uint32 Idx)
{
+ if (Idx >= objects_size)
+ return false;
if (const T* obj = objects[Idx])
_callback(p, *obj);
}
@@ -87,21 +92,24 @@ public:
m_objects.fastClear();
m_obj2Idx.getKeys(m_objects);
m_objects_to_push.getMembers(m_objects);
+ //assert that m_obj2Idx has all the keys
m_tree.build(m_objects, BoundsFunc::getBounds2);
}
template<typename RayCallback>
- void intersectRay(const Ray& ray, RayCallback& intersectCallback, float& maxDist) const
+ void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& maxDist)
{
- MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray());
+ balance();
+ MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray(), m_objects.size());
m_tree.intersectRay(ray, temp_cb, maxDist, true);
}
template<typename IsectCallback>
- void intersectPoint(const Vector3& point, IsectCallback& intersectCallback) const
+ void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
{
- MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray());
+ balance();
+ MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray(), m_objects.size());
m_tree.intersectPoint(point, callback);
}
};
diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt
index 62af7dd081e..b4012b63812 100644
--- a/src/server/collision/CMakeLists.txt
+++ b/src/server/collision/CMakeLists.txt
@@ -33,7 +33,9 @@ set(collision_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
diff --git a/src/server/collision/DynamicTree.cpp b/src/server/collision/DynamicTree.cpp
index c6754278d17..c70a4b78a03 100644
--- a/src/server/collision/DynamicTree.cpp
+++ b/src/server/collision/DynamicTree.cpp
@@ -27,15 +27,24 @@
#include "GameObjectModel.h"
#include "ModelInstance.h"
+#include <G3D/AABox.h>
+#include <G3D/Ray.h>
+#include <G3D/Vector3.h>
+
using VMAP::ModelInstance;
-using G3D::Ray;
+
+namespace {
+
+int CHECK_TREE_PERIOD = 200;
+
+} // namespace
template<> struct HashTrait< GameObjectModel>{
static size_t hashCode(const GameObjectModel& g) { return (size_t)(void*)&g; }
};
template<> struct PositionTrait< GameObjectModel> {
- static void getPosition(const GameObjectModel& g, Vector3& p) { p = g.getPosition(); }
+ static void getPosition(const GameObjectModel& g, G3D::Vector3& p) { p = g.getPosition(); }
};
template<> struct BoundsTrait< GameObjectModel> {
@@ -49,11 +58,6 @@ static bool operator == (const GameObjectModel& mdl, const GameObjectModel& mdl2
}
*/
-int valuesPerNode = 5, numMeanSplits = 3;
-
-int UNBALANCED_TIMES_LIMIT = 5;
-int CHECK_TREE_PERIOD = 200;
-
typedef RegularGrid2D<GameObjectModel, BIHWrap<GameObjectModel> > ParentTree;
struct DynTreeImpl : public ParentTree/*, public Intersectable*/
@@ -103,43 +107,43 @@ struct DynTreeImpl : public ParentTree/*, public Intersectable*/
int unbalanced_times;
};
-DynamicMapTree::DynamicMapTree() : impl(*new DynTreeImpl())
+DynamicMapTree::DynamicMapTree() : impl(new DynTreeImpl())
{
}
DynamicMapTree::~DynamicMapTree()
{
- delete &impl;
+ delete impl;
}
void DynamicMapTree::insert(const GameObjectModel& mdl)
{
- impl.insert(mdl);
+ impl->insert(mdl);
}
void DynamicMapTree::remove(const GameObjectModel& mdl)
{
- impl.remove(mdl);
+ impl->remove(mdl);
}
bool DynamicMapTree::contains(const GameObjectModel& mdl) const
{
- return impl.contains(mdl);
+ return impl->contains(mdl);
}
void DynamicMapTree::balance()
{
- impl.balance();
+ impl->balance();
}
int DynamicMapTree::size() const
{
- return impl.size();
+ return impl->size();
}
void DynamicMapTree::update(uint32 t_diff)
{
- impl.update(t_diff);
+ impl->update(t_diff);
}
struct DynamicTreeIntersectionCallback
@@ -147,7 +151,7 @@ struct DynamicTreeIntersectionCallback
bool did_hit;
uint32 phase_mask;
DynamicTreeIntersectionCallback(uint32 phasemask) : did_hit(false), phase_mask(phasemask) {}
- bool operator()(const Ray& r, const GameObjectModel& obj, float& distance)
+ bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance)
{
did_hit = obj.intersectRay(r, distance, true, phase_mask);
return did_hit;
@@ -163,7 +167,7 @@ struct DynamicTreeIntersectionCallback_WithLogger
{
sLog->outDebug(LOG_FILTER_MAPS, "Dynamic Intersection log");
}
- bool operator()(const Ray& r, const GameObjectModel& obj, float& distance)
+ bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance)
{
sLog->outDebug(LOG_FILTER_MAPS, "testing intersection with %s", obj.name.c_str());
bool hit = obj.intersectRay(r, distance, true, phase_mask);
@@ -177,17 +181,20 @@ struct DynamicTreeIntersectionCallback_WithLogger
bool didHit() const { return did_hit;}
};
-bool DynamicMapTree::getIntersectionTime(const uint32 phasemask, const G3D::Ray& ray, const Vector3& endPos, float& maxDist) const
+bool DynamicMapTree::getIntersectionTime(const uint32 phasemask, const G3D::Ray& ray,
+ const G3D::Vector3& endPos, float& maxDist) const
{
float distance = maxDist;
DynamicTreeIntersectionCallback callback(phasemask);
- impl.intersectRay(ray, callback, distance, endPos);
+ impl->intersectRay(ray, callback, distance, endPos);
if (callback.didHit())
maxDist = distance;
return callback.didHit();
}
-bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const Vector3& startPos, const Vector3& endPos, Vector3& resultHit, float modifyDist) const
+bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const G3D::Vector3& startPos,
+ const G3D::Vector3& endPos, G3D::Vector3& resultHit,
+ float modifyDist) const
{
bool result = false;
float maxDist = (endPos - startPos).magnitude();
@@ -199,7 +206,7 @@ bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const Vector3& star
resultHit = endPos;
return false;
}
- Vector3 dir = (endPos - startPos)/maxDist; // direction with length of 1
+ G3D::Vector3 dir = (endPos - startPos)/maxDist; // direction with length of 1
G3D::Ray ray(startPos, dir);
float dist = maxDist;
if (getIntersectionTime(phasemask, ray, endPos, dist))
@@ -227,26 +234,26 @@ bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const Vector3& star
bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const
{
- Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
+ G3D::Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
float maxDist = (v2 - v1).magnitude();
if (!G3D::fuzzyGt(maxDist, 0) )
return true;
- Ray r(v1, (v2-v1) / maxDist);
+ G3D::Ray r(v1, (v2-v1) / maxDist);
DynamicTreeIntersectionCallback callback(phasemask);
- impl.intersectRay(r, callback, maxDist, v2);
+ impl->intersectRay(r, callback, maxDist, v2);
return !callback.did_hit;
}
float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
{
- Vector3 v(x, y, z);
- Ray r(v, Vector3(0, 0, -1));
+ G3D::Vector3 v(x, y, z);
+ G3D::Ray r(v, G3D::Vector3(0, 0, -1));
DynamicTreeIntersectionCallback callback(phasemask);
- impl.intersectZAllignedRay(r, callback, maxSearchDist);
+ impl->intersectZAllignedRay(r, callback, maxSearchDist);
if (callback.didHit())
return v.z - maxSearchDist;
diff --git a/src/server/collision/DynamicTree.h b/src/server/collision/DynamicTree.h
index ca199a9cd70..8e541fd453a 100644
--- a/src/server/collision/DynamicTree.h
+++ b/src/server/collision/DynamicTree.h
@@ -20,34 +20,36 @@
#ifndef _DYNTREE_H
#define _DYNTREE_H
-#include <G3D/Matrix3.h>
-#include <G3D/Vector3.h>
-#include <G3D/AABox.h>
-#include <G3D/Ray.h>
-
-//#include "ModelInstance.h"
#include "Define.h"
-//#include "GameObjectModel.h"
namespace G3D
{
+ class Ray;
class Vector3;
}
-using G3D::Vector3;
class GameObjectModel;
+struct DynTreeImpl;
class DynamicMapTree
{
- struct DynTreeImpl& impl;
+ DynTreeImpl *impl;
+
public:
DynamicMapTree();
~DynamicMapTree();
- bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const;
- bool getIntersectionTime(uint32 phasemask, const G3D::Ray& ray, const Vector3& endPos, float& maxDist) const;
- bool getObjectHitPos(uint32 phasemask, const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const;
+ bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2,
+ float z2, uint32 phasemask) const;
+
+ bool getIntersectionTime(uint32 phasemask, const G3D::Ray& ray,
+ const G3D::Vector3& endPos, float& maxDist) const;
+
+ bool getObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1,
+ const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos,
+ float pModifyDist) const;
+
float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
void insert(const GameObjectModel&);
diff --git a/src/server/collision/Management/MMapFactory.cpp b/src/server/collision/Management/MMapFactory.cpp
new file mode 100644
index 00000000000..7adf7fbfa66
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapFactory.h"
+#include "World.h"
+#include "Config.h"
+#include "DisableMgr.h"
+
+namespace MMAP
+{
+ // ######################## MMapFactory ########################
+ // our global singleton copy
+ MMapManager *g_MMapManager = NULL;
+
+ MMapManager* MMapFactory::createOrGetMMapManager()
+ {
+ if (g_MMapManager == NULL)
+ g_MMapManager = new MMapManager();
+
+ return g_MMapManager;
+ }
+
+ bool MMapFactory::IsPathfindingEnabled(uint32 mapId)
+ {
+ return sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS)
+ && !DisableMgr::IsDisabledFor(DISABLE_TYPE_MMAP, mapId, NULL, MMAP_DISABLE_PATHFINDING);
+ }
+
+ void MMapFactory::clear()
+ {
+ if (g_MMapManager)
+ {
+ delete g_MMapManager;
+ g_MMapManager = NULL;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/server/collision/Management/MMapFactory.h b/src/server/collision/Management/MMapFactory.h
new file mode 100644
index 00000000000..00f19a194d3
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_FACTORY_H
+#define _MMAP_FACTORY_H
+
+#include "MMapManager.h"
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+namespace MMAP
+{
+ enum MMAP_LOAD_RESULT
+ {
+ MMAP_LOAD_RESULT_ERROR,
+ MMAP_LOAD_RESULT_OK,
+ MMAP_LOAD_RESULT_IGNORED,
+ };
+
+ // static class
+ // holds all mmap global data
+ // access point to MMapManager singleton
+ class MMapFactory
+ {
+ public:
+ static MMapManager* createOrGetMMapManager();
+ static void clear();
+ static bool IsPathfindingEnabled(uint32 mapId);
+ };
+}
+
+#endif
+
diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp
new file mode 100644
index 00000000000..e3ed8f3310a
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapManager.h"
+#include "Log.h"
+#include "World.h"
+
+namespace MMAP
+{
+ // ######################## MMapManager ########################
+ MMapManager::~MMapManager()
+ {
+ for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
+ delete i->second;
+
+ // by now we should not have maps loaded
+ // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
+ }
+
+ bool MMapManager::loadMapData(uint32 mapId)
+ {
+ // we already have this map loaded?
+ if (loadedMMaps.find(mapId) != loadedMMaps.end())
+ return true;
+
+ // load and init dtNavMesh - read parameters from file
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1;
+ char *fileName = new char[pathLen];
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMeshParams params;
+ int count = fread(&params, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ if (count != 1)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMesh* mesh = dtAllocNavMesh();
+ ASSERT(mesh);
+ if (DT_SUCCESS != mesh->init(&params))
+ {
+ dtFreeNavMesh(mesh);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ delete [] fileName;
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMapData: Loaded %03i.mmap", mapId);
+
+ // store inside our map list
+ MMapData* mmap_data = new MMapData(mesh);
+ mmap_data->mmapLoadedTiles.clear();
+
+ loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data));
+ return true;
+ }
+
+ uint32 MMapManager::packTileID(int32 x, int32 y)
+ {
+ return uint32(x << 16 | y);
+ }
+
+ bool MMapManager::loadMap(const std::string& /*basePath*/, uint32 mapId, int32 x, int32 y)
+ {
+ // make sure the mmap is loaded and ready to load tiles
+ if (!loadMapData(mapId))
+ return false;
+
+ // get this mmap data
+ MMapData* mmap = loadedMMaps[mapId];
+ ASSERT(mmap->navMesh);
+
+ // check if we already have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
+ return false;
+
+ // load this tile :: mmaps/MMMXXYY.mmtile
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1;
+ char *fileName = new char[pathLen];
+
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y);
+
+ FILE *file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+ delete [] fileName;
+
+ // read header
+ MmapTileHeader fileHeader;
+ if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ if (fileHeader.mmapVersion != MMAP_VERSION)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
+ mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
+ return false;
+ }
+
+ unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
+ ASSERT(data);
+
+ size_t result = fread(data, fileHeader.size, 1, file);
+ if (!result)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ dtTileRef tileRef = 0;
+
+ // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
+ if (DT_SUCCESS == mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))
+ {
+ mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ ++loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMap: Loaded mmtile %03i[%02i,%02i] into %03i[%02i,%02i]", mapId, x, y, mapId, header->x, header->y);
+ return true;
+ }
+ else
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y);
+ dtFree(data);
+ return false;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+
+ // check if we have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
+
+ // unload, and mark as non loaded
+ if (DT_SUCCESS != mmap->navMesh->removeTile(tileRef, NULL, NULL))
+ {
+ // this is technically a memory leak
+ // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
+ // we cannot recover from this error - assert out
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ ASSERT(false);
+ }
+ else
+ {
+ mmap->mmapLoadedTiles.erase(packedGridPos);
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ // unload all tiles from given map
+ MMapData* mmap = loadedMMaps[mapId];
+ for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
+ {
+ uint32 x = (i->first >> 16);
+ uint32 y = (i->first & 0x0000FFFF);
+ if (DT_SUCCESS != mmap->navMesh->removeTile(i->second, NULL, NULL))
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ else
+ {
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
+ }
+ }
+
+ delete mmap;
+ loadedMMaps.erase(mapId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded %03i.mmap", mapId);
+
+ return true;
+ }
+
+ bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
+ return false;
+ }
+
+ dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
+
+ dtFreeNavMeshQuery(query);
+ mmap->navMeshQueries.erase(instanceId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
+
+ return true;
+ }
+
+ dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ return loadedMMaps[mapId]->navMesh;
+ }
+
+ dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ // allocate mesh query
+ dtNavMeshQuery* query = dtAllocNavMeshQuery();
+ ASSERT(query);
+ if (DT_SUCCESS != query->init(mmap->navMesh, 1024))
+ {
+ dtFreeNavMeshQuery(query);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ return NULL;
+ }
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
+ }
+
+ return mmap->navMeshQueries[instanceId];
+ }
+}
diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h
new file mode 100644
index 00000000000..56b1b856d65
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_MANAGER_H
+#define _MMAP_MANAGER_H
+
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+// move map related classes
+namespace MMAP
+{
+ typedef UNORDERED_MAP<uint32, dtTileRef> MMapTileSet;
+ typedef UNORDERED_MAP<uint32, dtNavMeshQuery*> NavMeshQuerySet;
+
+ // dummy struct to hold map's mmap data
+ struct MMapData
+ {
+ MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
+ ~MMapData()
+ {
+ for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
+ dtFreeNavMeshQuery(i->second);
+
+ if (navMesh)
+ dtFreeNavMesh(navMesh);
+ }
+
+ dtNavMesh* navMesh;
+
+ // we have to use single dtNavMeshQuery for every instance, since those are not thread safe
+ NavMeshQuerySet navMeshQueries; // instanceId to query
+ MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
+ };
+
+
+ typedef UNORDERED_MAP<uint32, MMapData*> MMapDataSet;
+
+ // singleton class
+ // holds all all access to mmap loading unloading and meshes
+ class MMapManager
+ {
+ public:
+ MMapManager() : loadedTiles(0) {}
+ ~MMapManager();
+
+ bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId);
+ bool unloadMapInstance(uint32 mapId, uint32 instanceId);
+
+ // the returned [dtNavMeshQuery const*] is NOT threadsafe
+ dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
+ dtNavMesh const* GetNavMesh(uint32 mapId);
+
+ uint32 getLoadedTilesCount() const { return loadedTiles; }
+ uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ private:
+ bool loadMapData(uint32 mapId);
+ uint32 packTileID(int32 x, int32 y);
+
+ MMapDataSet loadedMMaps;
+ uint32 loadedTiles;
+ };
+}
+
+#endif \ No newline at end of file
diff --git a/src/server/collision/Management/VMapManager2.cpp b/src/server/collision/Management/VMapManager2.cpp
index 5e3741ca753..8a1bd346957 100644
--- a/src/server/collision/Management/VMapManager2.cpp
+++ b/src/server/collision/Management/VMapManager2.cpp
@@ -24,13 +24,13 @@
#include "MapTree.h"
#include "ModelInstance.h"
#include "WorldModel.h"
-#include "VMapDefinitions.h"
-#include "Log.h"
#include <G3D/Vector3.h>
#include <ace/Null_Mutex.h>
#include <ace/Singleton.h>
#include "DisableMgr.h"
#include "DBCStores.h"
+#include "Log.h"
+#include "VMapDefinitions.h"
using G3D::Vector3;
@@ -257,11 +257,11 @@ namespace VMAP
WorldModel* worldmodel = new WorldModel();
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
- sLog->outError(LOG_FILTER_GENERAL, "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str());
delete worldmodel;
return NULL;
}
- sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str());
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
model->second.setModel(worldmodel);
}
@@ -277,12 +277,12 @@ namespace VMAP
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
- sLog->outError(LOG_FILTER_GENERAL, "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str());
return;
}
if (model->second.decRefCount() == 0)
{
- sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str());
delete model->second.getModel();
iLoadedModelFiles.erase(model);
}
diff --git a/src/server/collision/Management/VMapManager2.h b/src/server/collision/Management/VMapManager2.h
index e2e9965dbfc..51f15f0fda4 100644
--- a/src/server/collision/Management/VMapManager2.h
+++ b/src/server/collision/Management/VMapManager2.h
@@ -112,6 +112,8 @@ namespace VMAP
return getMapFileName(mapId);
}
virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y);
+ public:
+ void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
};
}
diff --git a/src/server/collision/Maps/MapTree.cpp b/src/server/collision/Maps/MapTree.cpp
index 5f4e2b6aa8b..eb4b4555cb4 100644
--- a/src/server/collision/Maps/MapTree.cpp
+++ b/src/server/collision/Maps/MapTree.cpp
@@ -21,18 +21,13 @@
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include "Log.h"
+#include "Errors.h"
#include <string>
#include <sstream>
#include <iomanip>
#include <limits>
-#ifndef NO_CORE_FUNCS
- #include "Errors.h"
-#else
- #define ASSERT(x)
-#endif
-
using G3D::Vector3;
namespace VMAP
@@ -277,7 +272,7 @@ namespace VMAP
bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
{
- sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
bool success = true;
std::string fullname = iBasePath + fname;
FILE* rf = fopen(fullname.c_str(), "rb");
@@ -310,7 +305,7 @@ namespace VMAP
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
{
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
- sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
if (model)
{
// assume that global model always is the first and only tree value (could be improved...)
@@ -320,7 +315,7 @@ namespace VMAP
else
{
success = false;
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
}
}
@@ -356,7 +351,7 @@ namespace VMAP
}
if (!iTreeValues)
{
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
return false;
}
bool result = true;
@@ -382,7 +377,7 @@ namespace VMAP
// acquire model instance
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
if (!model)
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
// update tree
uint32 referencedVal;
@@ -432,7 +427,7 @@ namespace VMAP
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
if (tile == iLoadedTiles.end())
{
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
return;
}
if (tile->second) // file associated with tile
@@ -466,7 +461,7 @@ namespace VMAP
else
{
if (!iLoadedSpawns.count(referencedNode))
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
else if (--iLoadedSpawns[referencedNode] == 0)
{
iTreeValues[referencedNode].setUnloaded();
@@ -480,5 +475,4 @@ namespace VMAP
}
iLoadedTiles.erase(tile);
}
-
}
diff --git a/src/server/collision/Maps/MapTree.h b/src/server/collision/Maps/MapTree.h
index bd928d85c5a..5de8e616d2b 100644
--- a/src/server/collision/Maps/MapTree.h
+++ b/src/server/collision/Maps/MapTree.h
@@ -72,7 +72,7 @@ namespace VMAP
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
- bool GetLocationInfo(const Vector3 &pos, LocationInfo &info) const;
+ bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const;
bool InitMap(const std::string &fname, VMapManager2* vm);
void UnloadMap(VMapManager2* vm);
@@ -80,6 +80,7 @@ namespace VMAP
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
+ void getModelInstances(ModelInstance* &models, uint32 &count);
};
struct AreaInfo
diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp
index 0ecf02648f9..e166a860cd2 100644
--- a/src/server/collision/Models/GameObjectModel.cpp
+++ b/src/server/collision/Models/GameObjectModel.cpp
@@ -34,8 +34,6 @@ using G3D::Vector3;
using G3D::Ray;
using G3D::AABox;
-#ifndef NO_CORE_FUNCS
-
struct GameobjectModelData
{
GameobjectModelData(const std::string& name_, const AABox& box) :
@@ -50,11 +48,14 @@ ModelList model_list;
void LoadGameObjectModelList()
{
+#ifndef NO_CORE_FUNCS
uint32 oldMSTime = getMSTime();
+#endif
+
FILE* model_list_file = fopen((sWorld->GetDataPath() + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
if (!model_list_file)
{
- sLog->outError(LOG_FILTER_GENERAL, "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
return;
}
@@ -73,7 +74,7 @@ void LoadGameObjectModelList()
|| fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
|| fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
{
- sLog->outError(LOG_FILTER_GENERAL, "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
break;
}
@@ -84,8 +85,7 @@ void LoadGameObjectModelList()
}
fclose(model_list_file);
- sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
-
+ VMAP_INFO_LOG(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
}
GameObjectModel::~GameObjectModel()
@@ -104,7 +104,7 @@ bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayIn
// ignore models with no bounds
if (mdl_box == G3D::AABox::zero())
{
- sLog->outError(LOG_FILTER_GENERAL, "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
return false;
}
@@ -184,5 +184,3 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto
}
return hit;
}
-
-#endif
diff --git a/src/server/collision/Models/ModelInstance.h b/src/server/collision/Models/ModelInstance.h
index 5af02a1d1b1..f26089bb46c 100644
--- a/src/server/collision/Models/ModelInstance.h
+++ b/src/server/collision/Models/ModelInstance.h
@@ -74,6 +74,8 @@ namespace VMAP
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel* iModel;
+ public:
+ WorldModel* getWorldModel();
};
} // namespace VMAP
diff --git a/src/server/collision/Models/WorldModel.h b/src/server/collision/Models/WorldModel.h
index 8e046e561f7..cea32cfedfb 100644
--- a/src/server/collision/Models/WorldModel.h
+++ b/src/server/collision/Models/WorldModel.h
@@ -47,11 +47,11 @@ namespace VMAP
class WmoLiquid
{
public:
- WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type);
+ WmoLiquid(uint32 width, uint32 height, const G3D::Vector3 &corner, uint32 type);
WmoLiquid(const WmoLiquid &other);
~WmoLiquid();
WmoLiquid& operator=(const WmoLiquid &other);
- bool GetLiquidHeight(const Vector3 &pos, float &liqHeight) const;
+ bool GetLiquidHeight(const G3D::Vector3 &pos, float &liqHeight) const;
uint32 GetType() const { return iType; }
float *GetHeightStorage() { return iHeight; }
uint8 *GetFlagsStorage() { return iFlags; }
@@ -60,12 +60,14 @@ namespace VMAP
static bool readFromFile(FILE* rf, WmoLiquid* &liquid);
private:
WmoLiquid(): iHeight(0), iFlags(0) {};
- uint32 iTilesX; //!< number of tiles in x direction, each
+ uint32 iTilesX; //!< number of tiles in x direction, each
uint32 iTilesY;
- Vector3 iCorner; //!< the lower corner
- uint32 iType; //!< liquid type
- float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
- uint8 *iFlags; //!< info if liquid tile is used
+ G3D::Vector3 iCorner; //!< the lower corner
+ uint32 iType; //!< liquid type
+ float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
+ uint8 *iFlags; //!< info if liquid tile is used
+ public:
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const;
};
/*! holding additional info for WMO group files */
@@ -74,16 +76,16 @@ namespace VMAP
public:
GroupModel(): iLiquid(0) {}
GroupModel(const GroupModel &other);
- GroupModel(uint32 mogpFlags, uint32 groupWMOID, const AABox &bound):
+ GroupModel(uint32 mogpFlags, uint32 groupWMOID, const G3D::AABox &bound):
iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(0) {}
~GroupModel() { delete iLiquid; }
//! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
- void setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri);
+ void setMeshData(std::vector<G3D::Vector3> &vert, std::vector<MeshTriangle> &tri);
void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = NULL; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
- bool IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const;
- bool GetLiquidLevel(const Vector3 &pos, float &liqHeight) const;
+ bool IsInsideObject(const G3D::Vector3 &pos, const G3D::Vector3 &down, float &z_dist) const;
+ bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const;
uint32 GetLiquidType() const;
bool writeToFile(FILE* wf);
bool readFromFile(FILE* rf);
@@ -94,10 +96,12 @@ namespace VMAP
G3D::AABox iBound;
uint32 iMogpFlags;// 0x8 outdor; 0x2000 indoor
uint32 iGroupWMOID;
- std::vector<Vector3> vertices;
+ std::vector<G3D::Vector3> vertices;
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid* iLiquid;
+ public:
+ void getMeshData(std::vector<G3D::Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
@@ -117,6 +121,8 @@ namespace VMAP
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
+ public:
+ void getGroupModels(std::vector<GroupModel> &groupModels);
};
} // namespace VMAP
diff --git a/src/server/collision/RegularGrid.h b/src/server/collision/RegularGrid.h
index f38bf357a19..d1832c1ea06 100644
--- a/src/server/collision/RegularGrid.h
+++ b/src/server/collision/RegularGrid.h
@@ -3,18 +3,12 @@
#include <G3D/Ray.h>
-#include <G3D/AABox.h>
#include <G3D/Table.h>
#include <G3D/BoundsTrait.h>
#include <G3D/PositionTrait.h>
#include "Errors.h"
-using G3D::Vector2;
-using G3D::Vector3;
-using G3D::AABox;
-using G3D::Ray;
-
template<class Node>
struct NodeCreator{
static Node * makeNode(int /*x*/, int /*y*/) { return new Node();}
@@ -54,7 +48,7 @@ public:
void insert(const T& value)
{
- Vector3 pos;
+ G3D::Vector3 pos;
PositionFunc::getPosition(value, pos);
Node& node = getGridFor(pos.x, pos.y);
node.insert(value);
@@ -109,13 +103,13 @@ public:
}
template<typename RayCallback>
- void intersectRay(const Ray& ray, RayCallback& intersectCallback, float max_dist)
+ void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float max_dist)
{
intersectRay(ray, intersectCallback, max_dist, ray.origin() + ray.direction() * max_dist);
}
template<typename RayCallback>
- void intersectRay(const Ray& ray, RayCallback& intersectCallback, float& max_dist, const Vector3& end)
+ void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist, const G3D::Vector3& end)
{
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
if (!cell.isValid())
@@ -191,7 +185,7 @@ public:
}
template<typename IsectCallback>
- void intersectPoint(const Vector3& point, IsectCallback& intersectCallback)
+ void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
{
Cell cell = Cell::ComputeCell(point.x, point.y);
if (!cell.isValid())
@@ -202,7 +196,7 @@ public:
// Optimized verson of intersectRay function for rays with vertical directions
template<typename RayCallback>
- void intersectZAllignedRay(const Ray& ray, RayCallback& intersectCallback, float& max_dist)
+ void intersectZAllignedRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist)
{
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
if (!cell.isValid())
diff --git a/src/server/collision/VMapDefinitions.h b/src/server/collision/VMapDefinitions.h
index 609d00cd00f..bb0766dc8ff 100644
--- a/src/server/collision/VMapDefinitions.h
+++ b/src/server/collision/VMapDefinitions.h
@@ -31,4 +31,16 @@ namespace VMAP
// defined in TileAssembler.cpp currently...
bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len);
}
+
+// Set of helper macros for extractors (VMAP and MMAP)
+#ifndef NO_CORE_FUNCS
+#define VMAP_ERROR_LOG(FILTER, ...) sLog->outError(FILTER, __VA_ARGS__)
+#define VMAP_DEBUG_LOG(FILTER, ...) sLog->outDebug(FILTER, __VA_ARGS__)
+#define VMAP_INFO_LOG(FILTER, ...) sLog->outInfo(FILTER, __VA_ARGS__)
+#else
+#define VMAP_ERROR_LOG(FILTER, ...) (void)sizeof(FILTER)
+#define VMAP_DEBUG_LOG(FILTER, ...) (void)sizeof(FILTER)
+#define VMAP_INFO_LOG(FILTER, ...) (void)sizeof(FILTER)
+#endif
+
#endif
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index a8d2a2248ad..2792f68004f 100644
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -432,29 +432,23 @@ void PetAI::HandleReturnMovement()
if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning())
{
// Return to previous position where stay was clicked
- if (!me->GetCharmInfo()->IsCommandAttack())
- {
- float x, y, z;
+ float x, y, z;
- me->GetCharmInfo()->GetStayPosition(x, y, z);
- ClearCharmInfoFlags();
- me->GetCharmInfo()->SetIsReturning(true);
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MovePoint(me->GetGUIDLow(), x, y, z);
- }
+ me->GetCharmInfo()->GetStayPosition(x, y, z);
+ ClearCharmInfoFlags();
+ me->GetCharmInfo()->SetIsReturning(true);
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MovePoint(me->GetGUIDLow(), x, y, z);
}
}
else // COMMAND_FOLLOW
{
if (!me->GetCharmInfo()->IsFollowing() && !me->GetCharmInfo()->IsReturning())
{
- if (!me->GetCharmInfo()->IsCommandAttack())
- {
- ClearCharmInfoFlags();
- me->GetCharmInfo()->SetIsReturning(true);
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle());
- }
+ ClearCharmInfoFlags();
+ me->GetCharmInfo()->SetIsReturning(true);
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle());
}
}
}
@@ -473,12 +467,13 @@ void PetAI::DoAttack(Unit* target, bool chase)
if (me->HasReactState(REACT_AGGRESSIVE) && !me->GetCharmInfo()->IsCommandAttack())
me->SendPetAIReaction(me->GetGUID());
-
if (chase)
{
- ClearCharmInfoFlags();
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveChase(target);
+ bool oldCmdAttack = me->GetCharmInfo()->IsCommandAttack(); // This needs to be reset after other flags are cleared
+ ClearCharmInfoFlags();
+ me->GetCharmInfo()->SetIsCommandAttack(oldCmdAttack); // For passive pets commanded to attack so they will use spells
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveChase(target);
}
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
{
diff --git a/src/server/game/AI/CoreAI/ReactorAI.cpp b/src/server/game/AI/CoreAI/ReactorAI.cpp
index 31b9a82c534..b9c235176bc 100644
--- a/src/server/game/AI/CoreAI/ReactorAI.cpp
+++ b/src/server/game/AI/CoreAI/ReactorAI.cpp
@@ -23,10 +23,7 @@
#include "ObjectAccessor.h"
#include "CreatureAIImpl.h"
-#define REACTOR_VISIBLE_RANGE (26.46f)
-
-int
-ReactorAI::Permissible(const Creature* creature)
+int ReactorAI::Permissible(const Creature* creature)
{
if (creature->isCivilian() || creature->IsNeutralToAll())
return PERMIT_BASE_REACTIVE;
@@ -34,24 +31,10 @@ ReactorAI::Permissible(const Creature* creature)
return PERMIT_BASE_NO;
}
-void
-ReactorAI::MoveInLineOfSight(Unit*)
-{
-}
-
-void
-ReactorAI::UpdateAI(const uint32 /*time_diff*/)
+void ReactorAI::UpdateAI(uint32 const /*diff*/)
{
- // update i_victimGuid if me->getVictim() !=0 and changed
if (!UpdateVictim())
return;
- if (me->isAttackReady())
- {
- if (me->IsWithinMeleeRange(me->getVictim()))
- {
- me->AttackerStateUpdate(me->getVictim());
- me->resetAttackTimer();
- }
- }
+ DoMeleeAttackIfReady();
}
diff --git a/src/server/game/AI/CoreAI/ReactorAI.h b/src/server/game/AI/CoreAI/ReactorAI.h
index 39af09c4a9d..f7de3b99565 100644
--- a/src/server/game/AI/CoreAI/ReactorAI.h
+++ b/src/server/game/AI/CoreAI/ReactorAI.h
@@ -29,9 +29,9 @@ class ReactorAI : public CreatureAI
explicit ReactorAI(Creature* c) : CreatureAI(c) {}
- void MoveInLineOfSight(Unit*);
+ void MoveInLineOfSight(Unit*) {}
+ void UpdateAI(uint32 const diff);
- void UpdateAI(const uint32);
static int Permissible(const Creature*);
};
#endif
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index 09ba2cc19b1..7f0d387c2f1 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -103,8 +103,7 @@ void UnitAI::DoAddAuraToAllHostilePlayers(uint32 spellid)
if (unit->GetTypeId() == TYPEID_PLAYER)
me->AddAura(spellid, unit);
}
- }else
- return;
+ }
}
void UnitAI::DoCastToAllHostilePlayers(uint32 spellid, bool triggered)
@@ -118,8 +117,7 @@ void UnitAI::DoCastToAllHostilePlayers(uint32 spellid, bool triggered)
if (unit->GetTypeId() == TYPEID_PLAYER)
me->CastSpell(unit, spellid, triggered);
}
- }else
- return;
+ }
}
void UnitAI::DoCast(uint32 spellId)
@@ -240,7 +238,10 @@ void UnitAI::FillAISpellInfo()
}
//Enable PlayerAI when charmed
-void PlayerAI::OnCharmed(bool apply) { me->IsAIEnabled = apply; }
+void PlayerAI::OnCharmed(bool apply)
+{
+ me->IsAIEnabled = apply;
+}
void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/)
{
diff --git a/src/server/game/AI/CreatureAIImpl.h b/src/server/game/AI/CreatureAIImpl.h
index 559240c4a3f..6c5cb5622b3 100644
--- a/src/server/game/AI/CreatureAIImpl.h
+++ b/src/server/game/AI/CreatureAIImpl.h
@@ -313,96 +313,177 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
class EventMap
{
- typedef std::map<uint32, uint32> StorageType;
+ /**
+ * Internal storage type.
+ * Key: Time as uint32 when the event should occur.
+ * Value: The event data as uint32.
+ *
+ * Structure of event data:
+ * - Bit 0 - 15: Event Id.
+ * - Bit 16 - 23: Group
+ * - Bit 24 - 31: Phase
+ * - Pattern: 0xPPGGEEEE
+ */
+ typedef std::multimap<uint32, uint32> EventStore;
public:
EventMap() : _time(0), _phase(0) {}
- // Returns current timer value, does not represent real dates/times
- uint32 GetTimer() const { return _time; }
-
- // Removes all events and clears phase
+ /**
+ * @name Reset
+ * @brief Removes all scheduled events and resets time and phase.
+ */
void Reset()
{
- _eventMap.clear(); _time = 0; _phase = 0;
+ _eventMap.clear();
+ _time = 0;
+ _phase = 0;
+ }
+
+ /**
+ * @name Update
+ * @brief Updates the timer of the event map.
+ * @param time Value to be added to time.
+ */
+ void Update(uint32 time)
+ {
+ _time += time;
}
- void Update(uint32 time) { _time += time; }
+ /**
+ * @name GetTimer
+ * @return Current timer value.
+ */
+ uint32 GetTimer() const
+ {
+ return _time;
+ }
- uint32 GetPhaseMask() const { return (_phase >> 24) & 0xFF; }
+ /**
+ * @name GetPhaseMask
+ * @return Active phases as mask.
+ */
+ uint8 GetPhaseMask() const
+ {
+ return _phase;
+ }
- bool Empty() const { return _eventMap.empty(); }
+ /**
+ * @name Empty
+ * @return True, if there are no events scheduled.
+ */
+ bool Empty() const
+ {
+ return _eventMap.empty();
+ }
- // Sets event phase, must be in range 1 - 8
- void SetPhase(uint32 phase)
+ /**
+ * @name SetPhase
+ * @brief Sets the phase of the map (absolute).
+ * @param phase Phase which should be set. Values: 1 - 8. 0 resets phase.
+ */
+ void SetPhase(uint8 phase)
{
- if (phase && phase < 8)
- _phase = (1 << (phase + 24));
- else if (!phase)
+ if (!phase)
_phase = 0;
+ else if (phase <= 8)
+ _phase = (1 << (phase - 1));
}
- // Creates new event entry in map with given id, time, group if given (1 - 8) and phase if given (1 - 8)
- // 0 for group/phase means it belongs to no group or runs in all phases
- void ScheduleEvent(uint32 eventId, uint32 time, uint32 groupId = 0, uint32 phase = 0)
+ /**
+ * @name AddPhase
+ * @brief Activates the given phase (bitwise).
+ * @param phase Phase which should be activated. Values: 1 - 8
+ */
+ void AddPhase(uint8 phase)
{
- time += _time;
- if (groupId && groupId < 9)
- eventId |= (1 << (groupId + 16));
- if (phase && phase < 8)
- eventId |= (1 << (phase + 24));
- StorageType::const_iterator itr = _eventMap.find(time);
- while (itr != _eventMap.end())
- {
- ++time;
- itr = _eventMap.find(time);
- }
+ if (phase && phase <= 8)
+ _phase |= (1 << (phase - 1));
+ }
- _eventMap.insert(StorageType::value_type(time, eventId));
+ /**
+ * @name RemovePhase
+ * @brief Deactivates the given phase (bitwise).
+ * @param phase Phase which should be deactivated. Values: 1 - 8.
+ */
+ void RemovePhase(uint8 phase)
+ {
+ if (phase && phase <= 8)
+ _phase &= ~(1 << (phase - 1));
}
- // Removes event with specified id and creates new entry for it
- void RescheduleEvent(uint32 eventId, uint32 time, uint32 groupId = 0, uint32 phase = 0)
+ /**
+ * @name ScheduleEvent
+ * @brief Creates new event entry in map.
+ * @param eventId The id of the new event.
+ * @param time The time in milliseconds until the event occurs.
+ * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group.
+ * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases.
+ */
+ void ScheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0)
+ {
+ if (group && group <= 8)
+ eventId |= (1 << (group + 15));
+
+ if (phase && phase <= 8)
+ eventId |= (1 << (phase + 23));
+
+ _eventMap.insert(EventStore::value_type(_time + time, eventId));
+ }
+
+ /**
+ * @name RescheduleEvent
+ * @brief Cancels the given event and reschedules it.
+ * @param eventId The id of the event.
+ * @param time The time in milliseconds until the event occurs.
+ * @param group The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group.
+ * @param phase The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases.
+ */
+ void RescheduleEvent(uint32 eventId, uint32 time, uint32 group = 0, uint8 phase = 0)
{
CancelEvent(eventId);
- ScheduleEvent(eventId, time, groupId, phase);
+ ScheduleEvent(eventId, time, group, phase);
}
- // Reschedules closest event
+ /**
+ * @name RepeatEvent
+ * @brief Cancels the closest event and reschedules it.
+ * @param time Time until the event occurs.
+ */
void RepeatEvent(uint32 time)
{
- if (_eventMap.empty())
+ if (Empty())
return;
uint32 eventId = _eventMap.begin()->second;
_eventMap.erase(_eventMap.begin());
- time += _time;
- StorageType::const_iterator itr = _eventMap.find(time);
- while (itr != _eventMap.end())
- {
- ++time;
- itr = _eventMap.find(time);
- }
-
- _eventMap.insert(StorageType::value_type(time, eventId));
+ ScheduleEvent(eventId, time);
}
- // Removes first event
+ /**
+ * @name PopEvent
+ * @brief Remove the first event in the map.
+ */
void PopEvent()
{
- if (!_eventMap.empty())
+ if (!Empty())
_eventMap.erase(_eventMap.begin());
}
- // Gets next event id to execute and removes it from map
+ /**
+ * @name ExecuteEvent
+ * @brief Returns the next event to execute and removes it from map.
+ * @return Id of the event to execute.
+ */
uint32 ExecuteEvent()
{
- while (!_eventMap.empty())
+ while (!Empty())
{
- StorageType::iterator itr = _eventMap.begin();
+ EventStore::iterator itr = _eventMap.begin();
+
if (itr->first > _time)
return 0;
- else if (_phase && (itr->second & 0xFF000000) && !(itr->second & _phase))
+ else if (_phase && (itr->second & 0xFF000000) && !((itr->second >> 24) & _phase))
_eventMap.erase(itr);
else
{
@@ -411,18 +492,24 @@ class EventMap
return eventId;
}
}
+
return 0;
}
- // Gets next event id to execute
+ /**
+ * @name GetEvent
+ * @brief Returns the next event to execute.
+ * @return Id of the event to execute.
+ */
uint32 GetEvent()
{
- while (!_eventMap.empty())
+ while (!Empty())
{
- StorageType::iterator itr = _eventMap.begin();
+ EventStore::iterator itr = _eventMap.begin();
+
if (itr->first > _time)
return 0;
- else if (_phase && (itr->second & 0xFF000000) && !(itr->second & _phase))
+ else if (_phase && (itr->second & 0xFF000000) && !(itr->second & (_phase << 24)))
_eventMap.erase(itr);
else
return (itr->second & 0x0000FFFF);
@@ -431,81 +518,150 @@ class EventMap
return 0;
}
- // Delay all events
+ /**
+ * @name DelayEvents
+ * @brief Delays all events in the map. If delay is greater than or equal internal timer, delay will be 0.
+ * @param delay Amount of delay.
+ */
void DelayEvents(uint32 delay)
{
- if (delay < _time)
- _time -= delay;
- else
- _time = 0;
+ _time = delay < _time ? _time - delay : 0;
}
- // Delay all events having the specified Group
- void DelayEvents(uint32 delay, uint32 groupId)
+ /**
+ * @name DelayEvents
+ * @brief Delay all events of the same group.
+ * @param delay Amount of delay.
+ * @param group Group of the events.
+ */
+ void DelayEvents(uint32 delay, uint32 group)
{
- uint32 nextTime = _time + delay;
- uint32 groupMask = (1 << (groupId + 16));
- for (StorageType::iterator itr = _eventMap.begin(); itr != _eventMap.end() && itr->first < nextTime;)
+ if (!group || group > 8 || Empty())
+ return;
+
+ EventStore delayed;
+
+ for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
{
- if (itr->second & groupMask)
+ if (itr->second & (1 << (group + 15)))
{
- ScheduleEvent(itr->second, itr->first - _time + delay);
- _eventMap.erase(itr);
- itr = _eventMap.begin();
+ delayed.insert(EventStore::value_type(itr->first + delay, itr->second));
+ _eventMap.erase(itr++);
}
else
++itr;
}
+
+ _eventMap.insert(delayed.begin(), delayed.end());
}
- // Cancel events with specified id
+ /**
+ * @name CancelEvent
+ * @brief Cancels all events of the specified id.
+ * @param eventId Event id to cancel.
+ */
void CancelEvent(uint32 eventId)
{
- for (StorageType::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
+ if (Empty())
+ return;
+
+ for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
{
if (eventId == (itr->second & 0x0000FFFF))
- {
- _eventMap.erase(itr);
- itr = _eventMap.begin();
- }
+ _eventMap.erase(itr++);
else
++itr;
}
}
- // Cancel events belonging to specified group
- void CancelEventGroup(uint32 groupId)
+ /**
+ * @name CancelEventGroup
+ * @brief Cancel events belonging to specified group.
+ * @param group Group to cancel.
+ */
+ void CancelEventGroup(uint32 group)
{
- uint32 groupMask = (1 << (groupId + 16));
+ if (!group || group > 8 || Empty())
+ return;
- for (StorageType::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
+ for (EventStore::iterator itr = _eventMap.begin(); itr != _eventMap.end();)
{
- if (itr->second & groupMask)
- {
- _eventMap.erase(itr);
- itr = _eventMap.begin();
- }
+ if (itr->second & (1 << (group + 15)))
+ _eventMap.erase(itr++);
else
++itr;
}
}
- // Returns time of next event to execute
- // To get how much time remains substract _time
+ /**
+ * @name GetNextEventTime
+ * @brief Returns closest occurence of specified event.
+ * @param eventId Wanted event id.
+ * @return Time of found event.
+ */
uint32 GetNextEventTime(uint32 eventId) const
{
- for (StorageType::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr)
+ if (Empty())
+ return 0;
+
+ for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr)
if (eventId == (itr->second & 0x0000FFFF))
return itr->first;
return 0;
}
+ /**
+ * @name GetNextEventTime
+ * @return Time of next event.
+ */
+ uint32 GetNextEventTime() const
+ {
+ return Empty() ? 0 : _eventMap.begin()->first;
+ }
+
+ /**
+ * @name IsInPhase
+ * @brief Returns wether event map is in specified phase or not.
+ * @param phase Wanted phase.
+ * @return True, if phase of event map contains specified phase.
+ */
+ bool IsInPhase(uint8 phase)
+ {
+ return phase <= 8 && (!phase || _phase & (1 << (phase - 1)));
+ }
+
private:
+ /**
+ * @name _time
+ * @brief Internal timer.
+ *
+ * This does not represent the real date/time value.
+ * It's more like a stopwatch: It can run, it can be stopped,
+ * it can be resetted and so on. Events occur when this timer
+ * has reached their time value. Its value is changed in the
+ * Update method.
+ */
uint32 _time;
- uint32 _phase;
- StorageType _eventMap;
+ /**
+ * @name _phase
+ * @brief Phase mask of the event map.
+ *
+ * Contains the phases the event map is in. Multiple
+ * phases from 1 to 8 can be set with SetPhase or
+ * AddPhase. RemovePhase deactives a phase.
+ */
+ uint8 _phase;
+
+ /**
+ * @name _eventMap
+ * @brief Internal event storage map. Contains the scheduled events.
+ *
+ * See typedef at the beginning of the class for more
+ * details.
+ */
+ EventStore _eventMap;
};
enum AITarget
diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp
index 920c1c25533..a09feb6e00f 100644
--- a/src/server/game/AI/CreatureAISelector.cpp
+++ b/src/server/game/AI/CreatureAISelector.cpp
@@ -132,16 +132,12 @@ namespace FactorySelector
const GameObjectAICreator* ai_factory = NULL;
GameObjectAIRegistry& ai_registry(*GameObjectAIRepository::instance());
+ // scriptname in db
if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go))
return scriptedAI;
ai_factory = ai_registry.GetRegistryItem(go->GetAIName());
- // scriptname in db
- if (!ai_factory)
- if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go))
- return scriptedAI;
-
//future goAI types go here
std::string ainame = (ai_factory == NULL || go->GetScriptId()) ? "NullGameObjectAI" : ai_factory->key();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 72f4d011948..23620bf1174 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -104,10 +104,18 @@ void ScriptedAI::AttackStartNoMove(Unit* who)
if (!who)
return;
- if (me->Attack(who, false))
+ if (me->Attack(who, true))
DoStartNoMovement(who);
}
+void ScriptedAI::AttackStart(Unit* who)
+{
+ if (IsCombatMovementAllowed())
+ CreatureAI::AttackStart(who);
+ else
+ AttackStartNoMove(who);
+}
+
void ScriptedAI::UpdateAI(uint32 const /*diff*/)
{
//Check if we have a current target
@@ -439,15 +447,6 @@ bool ScriptedAI::EnterEvadeIfOutOfCombatArea(uint32 const diff)
return true;
}
-void Scripted_NoMovementAI::AttackStart(Unit* target)
-{
- if (!target)
- return;
-
- if (me->Attack(target, true))
- DoStartNoMovement(target);
-}
-
// BossAI - for instanced bosses
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index f3a13060ad8..a9da02fda38 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -136,6 +136,9 @@ struct ScriptedAI : public CreatureAI
//Called at creature aggro either by MoveInLOS or Attack Start
void EnterCombat(Unit* /*victim*/) {}
+ // Called before EnterCombat even before the creature is in combat.
+ void AttackStart(Unit* /*target*/);
+
// *************
//AI Helper Functions
// *************
@@ -191,7 +194,11 @@ struct ScriptedAI : public CreatureAI
void SetEquipmentSlots(bool loadDefault, int32 mainHand = EQUIP_NO_CHANGE, int32 offHand = EQUIP_NO_CHANGE, int32 ranged = EQUIP_NO_CHANGE);
- //Generally used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
+ // Used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
+ // NOTE: If you use SetCombatMovement while the creature is in combat, it will do NOTHING - This only affects AttackStart
+ // You should make the necessary to make it happen so.
+ // Remember that if you modified _isCombatMovementAllowed (e.g: using SetCombatMovement) it will not be reset at Reset().
+ // It will keep the last value you set.
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
@@ -269,15 +276,6 @@ struct ScriptedAI : public CreatureAI
bool _isHeroic;
};
-struct Scripted_NoMovementAI : public ScriptedAI
-{
- Scripted_NoMovementAI(Creature* creature) : ScriptedAI(creature) {}
- virtual ~Scripted_NoMovementAI() {}
-
- //Called at each attack of me by any victim
- void AttackStart(Unit* target);
-};
-
class BossAI : public ScriptedAI
{
public:
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index e568a36abc4..501bc35b77a 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -430,13 +430,14 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
void SmartAI::RemoveAuras()
{
- // Only loop throught the applied auras, because here is where all auras on the current unit are stored
- Unit::AuraApplicationMap appliedAuras = me->GetAppliedAuras();
- for (Unit::AuraApplicationMap::iterator iter = appliedAuras.begin(); iter != appliedAuras.end(); ++iter)
+ Unit::AuraApplicationMap& appliedAuras = me->GetAppliedAuras();
+ for (Unit::AuraApplicationMap::iterator iter = appliedAuras.begin(); iter != appliedAuras.end();)
{
Aura const* aura = iter->second->GetBase();
- if (!aura->GetSpellInfo()->IsPassive() && !aura->GetSpellInfo()->HasAura(SPELL_AURA_CONTROL_VEHICLE) && aura->GetCaster() != me)
- me->RemoveAurasDueToSpell(aura->GetId());
+ if (!aura->IsPassive() && !aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE) && aura->GetCasterGUID() != me->GetGUID())
+ me->RemoveAura(iter);
+ else
+ ++iter;
}
}
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 84c9dffabd2..e37b9e777d8 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -1893,12 +1893,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_JUMP_TO_POS:
{
- if (!me)
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
break;
- me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveJump(e.target.x, e.target.y, e.target.z, (float)e.action.jump.speedxy, (float)e.action.jump.speedz);
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (Creature* creature = (*itr)->ToCreature())
+ {
+ creature->GetMotionMaster()->Clear();
+ creature->GetMotionMaster()->MoveJump(e.target.x, e.target.y, e.target.z, (float)e.action.jump.speedxy, (float)e.action.jump.speedz);
+ }
// TODO: Resume path when reached jump location
+
+ delete targets;
break;
}
case SMART_ACTION_GO_SET_LOOT_STATE:
@@ -1977,23 +1984,48 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_SET_HOME_POS:
{
- if (!me)
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
break;
- if (e.GetTargetType() == SMART_TARGET_SELF)
- me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
- else if (e.GetTargetType() == SMART_TARGET_POSITION)
- me->SetHomePosition(e.target.x, e.target.y, e.target.z, e.target.o);
- else
- sLog->outError(LOG_FILTER_SQL, "SmartScript: Action target for SMART_ACTION_SET_HOME_POS is not using SMART_TARGET_SELF or SMART_TARGET_POSITION, skipping");
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ {
+ if (e.GetTargetType() == SMART_TARGET_SELF)
+ (*itr)->ToCreature()->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
+ else if (e.GetTargetType() == SMART_TARGET_POSITION)
+ (*itr)->ToCreature()->SetHomePosition(e.target.x, e.target.y, e.target.z, e.target.o);
+ else
+ sLog->outError(LOG_FILTER_SQL, "SmartScript: Action target for SMART_ACTION_SET_HOME_POS is not using SMART_TARGET_SELF or SMART_TARGET_POSITION, skipping");
+ }
- break;
+ delete targets;
+ break;
}
case SMART_ACTION_SET_HEALTH_REGEN:
{
- if (!me || me->GetTypeId() != TYPEID_UNIT)
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ (*itr)->ToCreature()->setRegeneratingHealth(e.action.setHealthRegen.regenHealth ? true : false);
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_SET_ROOT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
break;
- me->setRegeneratingHealth(e.action.setHealthRegen.regenHealth ? true : false);
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ (*itr)->ToCreature()->SetControlled(e.action.setRoot.root ? true : false, UNIT_STATE_ROOT);
+
+ delete targets;
break;
}
default:
@@ -2011,6 +2043,17 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
}
+void SmartScript::ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellInfo* spell, GameObject* gob)
+{
+ ConditionList const conds = sConditionMgr->GetConditionsForSmartEvent(e.entryOrGuid, e.event_id, e.source_type);
+ ConditionSourceInfo info = ConditionSourceInfo(unit, GetBaseObject());
+
+ if (sConditionMgr->IsObjectMeetToConditions(info, conds))
+ ProcessAction(e, unit, var0, var1, bvar, spell, gob);
+
+ RecalcTimer(e, min, max);
+}
+
void SmartScript::InstallTemplate(SmartScriptHolder const& e)
{
if (!GetBaseObject())
@@ -2409,20 +2452,17 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
break;
//called from Update tick
case SMART_EVENT_UPDATE:
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e);
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
case SMART_EVENT_UPDATE_OOC:
if (me && me->isInCombat())
return;
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e);
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
case SMART_EVENT_UPDATE_IC:
if (!me || !me->isInCombat())
return;
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e);
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
case SMART_EVENT_HEALT_PCT:
{
@@ -2431,8 +2471,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
uint32 perc = (uint32)me->GetHealthPct();
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
return;
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e);
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
}
case SMART_EVENT_TARGET_HEALTH_PCT:
@@ -2442,8 +2481,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
uint32 perc = (uint32)me->getVictim()->GetHealthPct();
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
return;
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e, me->getVictim());
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim());
break;
}
case SMART_EVENT_MANA_PCT:
@@ -2453,8 +2491,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
uint32 perc = uint32(100.0f * me->GetPower(POWER_MANA) / me->GetMaxPower(POWER_MANA));
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
return;
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e);
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
}
case SMART_EVENT_TARGET_MANA_PCT:
@@ -2464,8 +2501,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
uint32 perc = uint32(100.0f * me->getVictim()->GetPower(POWER_MANA) / me->getVictim()->GetMaxPower(POWER_MANA));
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
return;
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- ProcessAction(e, me->getVictim());
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim());
break;
}
case SMART_EVENT_RANGE:
@@ -2474,18 +2510,15 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
return;
if (me->IsInRange(me->getVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
- {
- ProcessAction(e, me->getVictim());
- RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
- }
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim());
break;
}
case SMART_EVENT_TARGET_CASTING:
{
if (!me || !me->isInCombat() || !me->getVictim() || !me->getVictim()->IsNonMeleeSpellCasted(false, false, true))
return;
- ProcessAction(e, me->getVictim());
- RecalcTimer(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax);
+
+ ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim());
}
case SMART_EVENT_FRIENDLY_HEALTH:
{
@@ -2495,8 +2528,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealt.radius, e.event.friendlyHealt.hpDeficit);
if (!target)
return;
- ProcessAction(e, target);
- RecalcTimer(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax);
+ ProcessTimedAction(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax, target);
break;
}
case SMART_EVENT_FRIENDLY_IS_CC:
@@ -2508,8 +2540,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
DoFindFriendlyCC(pList, (float)e.event.friendlyCC.radius);
if (pList.empty())
return;
- ProcessAction(e, *(pList.begin()));
- RecalcTimer(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax);
+ ProcessTimedAction(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax, *pList.begin());
break;
}
case SMART_EVENT_FRIENDLY_MISSING_BUFF:
@@ -2519,8 +2550,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (pList.empty())
return;
- ProcessAction(e, *(pList.begin()));
- RecalcTimer(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax);
+
+ ProcessTimedAction(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax, *pList.begin());
break;
}
case SMART_EVENT_HAS_AURA:
@@ -2529,10 +2560,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
return;
uint32 count = me->GetAuraCount(e.event.aura.spell);
if ((!e.event.aura.count && !count) || (e.event.aura.count && count >= e.event.aura.count))
- {
- ProcessAction(e);
- RecalcTimer(e, e.event.aura.repeatMin, e.event.aura.repeatMax);
- }
+ ProcessTimedAction(e, e.event.aura.repeatMin, e.event.aura.repeatMax);
break;
}
case SMART_EVENT_TARGET_BUFFED:
@@ -2542,8 +2570,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
uint32 count = me->getVictim()->GetAuraCount(e.event.aura.spell);
if (count < e.event.aura.count)
return;
- ProcessAction(e);
- RecalcTimer(e, e.event.aura.repeatMin, e.event.aura.repeatMax);
+ ProcessTimedAction(e, e.event.aura.repeatMin, e.event.aura.repeatMax);
break;
}
//no params
@@ -2578,10 +2605,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (Unit* victim = me->getVictim())
{
if (!victim->HasInArc(static_cast<float>(M_PI), me))
- {
- ProcessAction(e, victim);
- RecalcTimer(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax);
- }
+ ProcessTimedAction(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax, victim);
}
break;
}
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index d0d0221493f..28b328a2947 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -45,6 +45,7 @@ class SmartScript
void UpdateTimer(SmartScriptHolder& e, uint32 const diff);
void InitTimer(SmartScriptHolder& e);
void ProcessAction(SmartScriptHolder& e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
+ void ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
ObjectList* GetTargets(SmartScriptHolder const& e, Unit* invoker = NULL);
ObjectList* GetWorldObjectsInDist(float dist);
void InstallTemplate(SmartScriptHolder const& e);
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 76fcb7bad00..69902954fde 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -906,6 +906,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_SEND_TARGET_TO_TARGET:
case SMART_ACTION_SET_HOME_POS:
case SMART_ACTION_SET_HEALTH_REGEN:
+ case SMART_ACTION_SET_ROOT:
break;
default:
sLog->outError(LOG_FILTER_SQL, "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index a909cb56098..a3420071f5e 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -487,8 +487,9 @@ enum SMART_ACTION
SMART_ACTION_SEND_TARGET_TO_TARGET = 100, // id
SMART_ACTION_SET_HOME_POS = 101, // none
SMART_ACTION_SET_HEALTH_REGEN = 102, // 0/1
+ SMART_ACTION_SET_ROOT = 103, // off/on
- SMART_ACTION_END = 103
+ SMART_ACTION_END = 104
};
struct SmartAction
@@ -920,6 +921,11 @@ struct SmartAction
uint32 regenHealth;
} setHealthRegen;
+ struct
+ {
+ uint32 root;
+ } setRoot;
+
//! Note for any new future actions
//! All parameters must have type uint32
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp
index 3c3eded1f68..b1d0087c32c 100644
--- a/src/server/game/Accounts/AccountMgr.cpp
+++ b/src/server/game/Accounts/AccountMgr.cpp
@@ -17,6 +17,7 @@
*/
#include "AccountMgr.h"
+#include "Config.h"
#include "DatabaseEnv.h"
#include "ObjectAccessor.h"
#include "Player.h"
@@ -26,6 +27,7 @@
AccountMgr::AccountMgr()
{
+
}
AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password)
@@ -44,12 +46,22 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass
stmt->setString(0, username);
stmt->setString(1, CalculateShaPassHash(username, password));
- LoginDatabase.Execute(stmt);
+ LoginDatabase.DirectExecute(stmt); // Enforce saving, otherwise AddGroup can fail
stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS_INIT);
LoginDatabase.Execute(stmt);
+ // Add default rbac groups for that security level
+ RBACData* rbac = new RBACData(GetId(username), username, -1);
+ // No need to Load From DB, as it's new data
+
+ RBACGroupContainer const& groupsToAdd = _defaultGroups[0]; // 0: Default sec level
+ for (RBACGroupContainer::const_iterator it = groupsToAdd.begin(); it != groupsToAdd.end(); ++it)
+ rbac->AddGroup(*it, -1);
+
+ delete rbac;
+
return AOR_OK; // everything's fine
}
@@ -175,6 +187,14 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
LoginDatabase.Execute(stmt);
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS);
+
+ stmt->setString(0, "");
+ stmt->setString(1, "");
+ stmt->setString(2, username);
+
+ LoginDatabase.Execute(stmt);
+
return AOR_OK;
}
@@ -303,3 +323,206 @@ bool AccountMgr::IsConsoleAccount(uint32 gmlevel)
{
return gmlevel == SEC_CONSOLE;
}
+
+void AccountMgr::LoadRBAC()
+{
+ uint32 oldMSTime = getMSTime();
+ uint32 count1 = 0;
+ uint32 count2 = 0;
+ uint32 count3 = 0;
+
+ QueryResult result = LoginDatabase.Query("SELECT id, name FROM rbac_permissions");
+ if (!result)
+ {
+ sLog->outInfo(LOG_FILTER_SQL, ">> Loaded 0 account permission definitions. DB table `rbac_permissions` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* field = result->Fetch();
+ uint32 id = field[0].GetUInt32();
+ _permissions[id] = new RBACPermission(id, field[1].GetString());
+ ++count1;
+ }
+ while (result->NextRow());
+
+ result = LoginDatabase.Query("SELECT id, name FROM rbac_roles");
+ if (!result)
+ {
+ sLog->outInfo(LOG_FILTER_SQL, ">> Loaded 0 account role definitions. DB table `rbac_roles` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* field = result->Fetch();
+ uint32 id = field[0].GetUInt32();
+ _roles[id] = new RBACRole(id, field[1].GetString());
+ ++count2;
+ }
+ while (result->NextRow());
+
+ result = LoginDatabase.Query("SELECT roleId, permissionId FROM rbac_role_permissions");
+ if (!result)
+ {
+ sLog->outInfo(LOG_FILTER_SQL, ">> Loaded 0 account role-permission definitions. DB table `rbac_role_permissions` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* field = result->Fetch();
+ uint32 id = field[0].GetUInt32();
+ RBACRole* role = _roles[id];
+ role->GrantPermission(field[1].GetUInt32());
+ }
+ while (result->NextRow());
+
+ result = LoginDatabase.Query("SELECT id, name FROM rbac_groups");
+ if (!result)
+ {
+ sLog->outInfo(LOG_FILTER_SQL, ">> Loaded 0 account group definitions. DB table `rbac_groups` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* field = result->Fetch();
+ uint32 id = field[0].GetUInt32();
+ _groups[id] = new RBACGroup(id, field[1].GetString());
+ ++count3;
+ }
+ while (result->NextRow());
+
+ result = LoginDatabase.Query("SELECT groupId, roleId FROM rbac_group_roles");
+ if (!result)
+ {
+ sLog->outInfo(LOG_FILTER_SQL, ">> Loaded 0 account group-role definitions. DB table `rbac_group_roles` is empty.");
+ return;
+ }
+
+ do
+ {
+ Field* field = result->Fetch();
+ uint32 id = field[0].GetUInt32();
+ RBACGroup* group = _groups[id];
+ group->GrantRole(field[1].GetUInt32());
+ }
+ while (result->NextRow());
+
+ result = LoginDatabase.Query("SELECT secId, groupId FROM rbac_security_level_groups ORDER by secId ASC");
+ if (!result)
+ {
+ sLog->outInfo(LOG_FILTER_SQL, ">> Loaded 0 account default groups for security levels definitions. DB table `rbac_security_level_groups` is empty.");
+ return;
+ }
+
+ uint8 lastSecId = 255;
+ RBACGroupContainer* groups = NULL;
+ do
+ {
+ Field* field = result->Fetch();
+ uint8 secId = field[0].GetUInt8();
+
+ if (lastSecId != secId)
+ groups = &_defaultGroups[secId];
+
+ groups->insert(field[1].GetUInt32());
+ }
+ while (result->NextRow());
+
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u permission definitions, %u role definitions and %u group definitions in %u ms", count1, count2, count3, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void AccountMgr::UpdateAccountAccess(RBACData* rbac, uint32 accountId, uint8 securityLevel, int32 realmId)
+{
+ int32 serverRealmId = realmId != -1 ? realmId : ConfigMgr::GetIntDefault("RealmID", 0);
+ bool needDelete = false;
+ if (!rbac)
+ {
+ needDelete = true;
+ rbac = new RBACData(accountId, "", serverRealmId);
+ rbac->LoadFromDB();
+ }
+
+ // Get max security level and realm (checking current realm and -1)
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID);
+ stmt->setUInt32(0, accountId);
+ stmt->setInt32(1, serverRealmId);
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+ if (result)
+ {
+ do
+ {
+ Field* field = result->Fetch();
+ uint8 secLevel = field[0].GetUInt8();
+ int32 realmId = field[1].GetUInt32();
+
+ RBACGroupContainer const& groupsToRemove = _defaultGroups[secLevel];
+ for (RBACGroupContainer::const_iterator it = groupsToRemove.begin(); it != groupsToRemove.end(); ++it)
+ rbac->RemoveGroup(*it, realmId);
+ }
+ while (result->NextRow());
+ }
+
+ // Add new groups depending on the new security Level
+ RBACGroupContainer const& groupsToAdd = _defaultGroups[securityLevel];
+ for (RBACGroupContainer::const_iterator it = groupsToAdd.begin(); it != groupsToAdd.end(); ++it)
+ rbac->AddGroup(*it, realmId);
+
+ if (needDelete)
+ delete rbac;
+
+ // Delete old security level from DB
+ if (realmId == -1)
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS);
+ stmt->setUInt32(0, accountId);
+ LoginDatabase.Execute(stmt);
+ }
+ else
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM);
+ stmt->setUInt32(0, accountId);
+ stmt->setUInt32(1, realmId);
+ LoginDatabase.Execute(stmt);
+ }
+
+ // Add new security level
+ if (securityLevel)
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_ACCESS);
+ stmt->setUInt32(0, accountId);
+ stmt->setUInt8(1, securityLevel);
+ stmt->setInt32(2, realmId);
+ LoginDatabase.Execute(stmt);
+ }
+}
+
+RBACGroup const* AccountMgr::GetRBACGroup(uint32 group) const
+{
+ RBACGroupsContainer::const_iterator it = _groups.find(group);
+ if (it != _groups.end())
+ return it->second;
+
+ return NULL;
+}
+
+RBACRole const* AccountMgr::GetRBACRole(uint32 role) const
+{
+ RBACRolesContainer::const_iterator it = _roles.find(role);
+ if (it != _roles.end())
+ return it->second;
+
+ return NULL;
+}
+
+RBACPermission const* AccountMgr::GetRBACPermission(uint32 permission) const
+{
+ RBACPermissionsContainer::const_iterator it = _permissions.find(permission);
+ if (it != _permissions.end())
+ return it->second;
+
+ return NULL;
+}
diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h
index c8de5688e73..90c533ca5fa 100644
--- a/src/server/game/Accounts/AccountMgr.h
+++ b/src/server/game/Accounts/AccountMgr.h
@@ -19,8 +19,7 @@
#ifndef _ACCMGR_H
#define _ACCMGR_H
-#include "Define.h"
-#include <string>
+#include "RBAC.h"
#include <ace/Singleton.h>
enum AccountOpResult
@@ -35,6 +34,11 @@ enum AccountOpResult
#define MAX_ACCOUNT_STR 16
+typedef std::map<uint32, RBACPermission*> RBACPermissionsContainer;
+typedef std::map<uint32, RBACRole*> RBACRolesContainer;
+typedef std::map<uint32, RBACGroup*> RBACGroupsContainer;
+typedef std::map<uint32, RBACGroupContainer> RBACDefaultSecurityGroupContainer;
+
class AccountMgr
{
friend class ACE_Singleton<AccountMgr, ACE_Null_Mutex>;
@@ -43,7 +47,7 @@ class AccountMgr
AccountMgr();
public:
- static AccountOpResult CreateAccount(std::string username, std::string password);
+ AccountOpResult CreateAccount(std::string username, std::string password);
static AccountOpResult DeleteAccount(uint32 accountId);
static AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword);
static AccountOpResult ChangePassword(uint32 accountId, std::string newPassword);
@@ -62,6 +66,23 @@ class AccountMgr
static bool IsGMAccount(uint32 gmlevel);
static bool IsAdminAccount(uint32 gmlevel);
static bool IsConsoleAccount(uint32 gmlevel);
+
+ void UpdateAccountAccess(RBACData* rbac, uint32 accountId, uint8 securityLevel, int32 realmId);
+
+ void LoadRBAC();
+ RBACGroup const* GetRBACGroup(uint32 group) const;
+ RBACRole const* GetRBACRole(uint32 role) const;
+ RBACPermission const* GetRBACPermission(uint32 permission) const;
+
+ RBACGroupsContainer const& GetRBACGroupList() const { return _groups; }
+ RBACRolesContainer const& GetRBACRoleList() const { return _roles; }
+ RBACPermissionsContainer const& GetRBACPermissionList() const { return _permissions; }
+
+ private:
+ RBACPermissionsContainer _permissions;
+ RBACRolesContainer _roles;
+ RBACGroupsContainer _groups;
+ RBACDefaultSecurityGroupContainer _defaultGroups;
};
#define sAccountMgr ACE_Singleton<AccountMgr, ACE_Null_Mutex>::instance()
diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp
new file mode 100644
index 00000000000..4a069df05cd
--- /dev/null
+++ b/src/server/game/Accounts/RBAC.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "RBAC.h"
+#include "AccountMgr.h"
+#include "DatabaseEnv.h"
+
+RBACCommandResult RBACData::AddGroup(uint32 groupId, int32 realmId /* = 0 */)
+{
+ // Check if group Id exists
+ RBACGroup const* group = sAccountMgr->GetRBACGroup(groupId);
+ if (!group)
+ return RBAC_ID_DOES_NOT_EXISTS;
+
+ // Already added?
+ std::pair<std::set<uint32>::iterator, bool> ret = _groups.insert(groupId);
+ if (!ret.second)
+ return RBAC_CANT_ADD_ALREADY_ADDED;
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_RBAC_ACCOUNT_GROUP);
+ stmt->setUInt32(0, GetId());
+ stmt->setUInt32(1, groupId);
+ stmt->setInt32(2, realmId);
+ LoginDatabase.Execute(stmt);
+
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+RBACCommandResult RBACData::RemoveGroup(uint32 groupId, int32 realmId /* = 0 */)
+{
+ // could remove it?
+ if (!_groups.erase(groupId))
+ return RBAC_CANT_REVOKE_NOT_IN_LIST;
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_RBAC_ACCOUNT_GROUP);
+ stmt->setUInt32(0, GetId());
+ stmt->setUInt32(1, groupId);
+ stmt->setInt32(2, realmId);
+ LoginDatabase.Execute(stmt);
+
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+RBACCommandResult RBACData::GrantRole(uint32 roleId, int32 realmId /* = 0*/)
+{
+ // Check if role Id exists
+ RBACRole const* role = sAccountMgr->GetRBACRole(roleId);
+ if (!role)
+ return RBAC_ID_DOES_NOT_EXISTS;
+
+ // Check if already added in denied list
+ if (_deniedRoles.find(roleId) != _deniedRoles.end())
+ return RBAC_IN_DENIED_LIST;
+
+ // Already added?
+ std::pair<std::set<uint32>::iterator, bool> ret = _grantedRoles.insert(roleId);
+ if (!ret.second)
+ return RBAC_CANT_ADD_ALREADY_ADDED;
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ SaveRole(roleId, true, realmId);
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+RBACCommandResult RBACData::DenyRole(uint32 roleId, int32 realmId /* = 0*/)
+{
+ // Check if role Id exists
+ RBACRole const* role = sAccountMgr->GetRBACRole(roleId);
+ if (!role)
+ return RBAC_ID_DOES_NOT_EXISTS;
+
+ // Check if already added in granted list
+ if (_grantedRoles.find(roleId) != _grantedRoles.end())
+ return RBAC_IN_GRANTED_LIST;
+
+ // Already added?
+ std::pair<std::set<uint32>::iterator, bool> ret = _deniedRoles.insert(roleId);
+ if (!ret.second)
+ return RBAC_CANT_ADD_ALREADY_ADDED;
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ SaveRole(roleId, false, realmId);
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+void RBACData::SaveRole(uint32 roleId, bool granted, int32 realmId)
+{
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_RBAC_ACCOUNT_ROLE);
+ stmt->setUInt32(0, GetId());
+ stmt->setUInt32(1, roleId);
+ stmt->setBool(2, granted);
+ stmt->setInt32(3, realmId);
+ LoginDatabase.Execute(stmt);
+}
+
+RBACCommandResult RBACData::RevokeRole(uint32 roleId, int32 realmId /* = 0*/)
+{
+ uint8 revoked = _grantedRoles.erase(roleId) + _deniedRoles.erase(roleId);
+
+ // could remove it?
+ if (!revoked)
+ return RBAC_CANT_REVOKE_NOT_IN_LIST;
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_RBAC_ACCOUNT_ROLE);
+ stmt->setUInt32(0, GetId());
+ stmt->setUInt32(1, roleId);
+ stmt->setInt32(2, realmId);
+ LoginDatabase.Execute(stmt);
+
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+RBACCommandResult RBACData::GrantPermission(uint32 permissionId, int32 realmId /* = 0*/)
+{
+ // Check if permission Id exists
+ RBACPermission const* perm = sAccountMgr->GetRBACPermission(permissionId);
+ if (!perm)
+ return RBAC_ID_DOES_NOT_EXISTS;
+
+ // Check if already added in denied list
+ if (_deniedPerms.test(permissionId))
+ return RBAC_IN_DENIED_LIST;
+
+ // Already added?
+ if (_grantedPerms.test(permissionId))
+ return RBAC_CANT_ADD_ALREADY_ADDED;
+
+ _grantedPerms.set(permissionId);
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ SavePermission(permissionId, true, realmId);
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+RBACCommandResult RBACData::DenyPermission(uint32 permissionId, int32 realmId /* = 0*/)
+{
+ // Check if permission Id exists
+ RBACPermission const* perm = sAccountMgr->GetRBACPermission(permissionId);
+ if (!perm)
+ return RBAC_ID_DOES_NOT_EXISTS;
+
+ // Check if already added in granted list
+ if (_grantedPerms.test(permissionId))
+ return RBAC_IN_GRANTED_LIST;
+
+ // Already added?
+ if (_deniedPerms.test(permissionId))
+ return RBAC_CANT_ADD_ALREADY_ADDED;
+
+ _deniedPerms.set(permissionId);
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ SavePermission(permissionId, false, realmId);
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+void RBACData::SavePermission(uint32 permission, bool granted, int32 realmId)
+{
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION);
+ stmt->setUInt32(0, GetId());
+ stmt->setUInt32(1, permission);
+ stmt->setBool(2, granted);
+ stmt->setInt32(3, realmId);
+ LoginDatabase.Execute(stmt);
+}
+
+RBACCommandResult RBACData::RevokePermission(uint32 permission, int32 realmId /* = 0*/)
+{
+ // Check if it's present in any list
+ if (!_grantedPerms.test(permission) && !_deniedPerms.test(permission))
+ return RBAC_CANT_REVOKE_NOT_IN_LIST;
+
+ _grantedPerms.reset(permission);
+ _deniedPerms.reset(permission);
+
+ // Do not save to db when loading data from DB (realmId = 0)
+ if (realmId)
+ {
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION);
+ stmt->setUInt32(0, GetId());
+ stmt->setUInt32(1, permission);
+ stmt->setInt32(2, realmId);
+ LoginDatabase.Execute(stmt);
+
+ CalculateNewPermissions();
+ }
+
+ return RBAC_OK;
+}
+
+void RBACData::LoadFromDB()
+{
+ // Load account group that affect current realm
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_GROUPS);
+ stmt->setUInt32(0, GetId());
+ stmt->setInt32(1, GetRealmId());
+ PreparedQueryResult result = LoginDatabase.Query(stmt);
+
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ AddGroup(fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ }
+
+ // Load account roles (granted and denied) that affect current realm
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_ROLES);
+ stmt->setUInt32(0, GetId());
+ stmt->setInt32(1, GetRealmId());
+ result = LoginDatabase.Query(stmt);
+
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ if (fields[1].GetBool())
+ GrantRole(fields[0].GetUInt32());
+ else
+ DenyRole(fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ }
+
+ // Load account permissions (granted and denied) that affect current realm
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS);
+ stmt->setUInt32(0, GetId());
+ stmt->setInt32(1, GetRealmId());
+
+ result = LoginDatabase.Query(stmt);
+ if (result)
+ {
+ do
+ {
+ Field* fields = result->Fetch();
+ if (fields[1].GetBool())
+ GrantPermission(fields[0].GetUInt32());
+ else
+ DenyPermission(fields[0].GetUInt32());
+ }
+ while (result->NextRow());
+ }
+
+ // Force calculation of permissions, it wasn't performed at load time
+ // while adding groups, roles and permissions
+ CalculateNewPermissions();
+}
+
+void RBACData::CalculateNewPermissions()
+{
+ // Get the list of directly granted roles
+ RBACRoleContainer tempGrantedRoles = GetGrantedRoles();
+
+ // Add those roles inherited from groups
+ for (RBACGroupContainer::const_iterator itGroup = _groups.begin(); itGroup != _groups.end(); ++itGroup)
+ {
+ RBACGroup const* group = sAccountMgr->GetRBACGroup(*itGroup);
+ if (!group) // Should never happen due to foreign keys in DB
+ continue;
+
+ RBACRoleContainer const& roles = group->GetRoles();
+ for (RBACRoleContainer::const_iterator it = roles.begin(); it != roles.end(); ++it)
+ tempGrantedRoles.insert(*it);
+ }
+
+ // Get the list of granted permissions
+ _globalPerms = GetGrantedPermissions();
+
+ // Add those permissions inherited from roles granted
+ for (RBACRoleContainer::const_iterator it = tempGrantedRoles.begin(); it != tempGrantedRoles.end(); ++it)
+ if (RBACRole const* role = sAccountMgr->GetRBACRole(*it))
+ _globalPerms |= role->GetPermissions();
+
+ // Remove denied permissions from the list
+ _globalPerms &= ~GetDeniedPermissions();
+
+ // Remove those permissions inherited from denied roles
+ for (RBACRoleContainer::const_iterator it = _deniedRoles.begin(); it != _deniedRoles.end(); ++it)
+ if (RBACRole const* role = sAccountMgr->GetRBACRole(*it))
+ _globalPerms &= ~role->GetPermissions();
+}
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
new file mode 100644
index 00000000000..d2c76b71801
--- /dev/null
+++ b/src/server/game/Accounts/RBAC.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+* @file RBAC.h
+* @brief Role Based Access Control related classes definition
+*
+* This file contains all the classes and enums used to implement
+* Role Based Access Control
+*
+* RBAC Rules:
+* - Pemission: Defines an autorization to perform certain operation.
+* - Role: Set of permissions.
+* - Group: Set of roles.
+* - An Account can have multiple groups, roles and permissions.
+* - Account Groups can only be granted or revoked
+* - Account Roles and Permissions can be granted, denied or revoked
+* - Grant: Assignment of the object (role/permission) and allow it
+* - Deny: Assignment of the object (role/permission) and deny it
+* - Revoke: Removal of the object (role/permission) no matter if it was granted or denied
+* - Global Permissions are computed as:
+* Group Grants + Role Grants + User Grans - Role Grants - User Grants
+* - Groups, Roles and Permissions can be assigned by realm
+*/
+
+#ifndef _RBAC_H
+#define _RBAC_H
+
+#include "Define.h"
+#include <string>
+#include <bitset>
+#include <set>
+#include <map>
+
+enum RBACPermissions
+{
+ RBAC_PERM_INSTANT_LOGOUT = 1,
+ RBAC_PERM_SKIP_QUEUE,
+ RBAC_PERM_JOIN_NORMAL_BG,
+ RBAC_PERM_JOIN_RANDOM_BG,
+ RBAC_PERM_JOIN_ARENAS,
+ RBAC_PERM_JOIN_DUNGEON_FINDER,
+ RBAC_PERM_PLAYER_COMMANDS,
+ RBAC_PERM_MODERATOR_COMMANDS,
+ RBAC_PERM_GAMEMASTER_COMMANDS,
+ RBAC_PERM_ADMINISTRATOR_COMMANDS,
+ RBAC_PERM_MAX
+};
+
+enum RBACCommandResult
+{
+ RBAC_OK,
+ RBAC_CANT_ADD_ALREADY_ADDED,
+ RBAC_CANT_REVOKE_NOT_IN_LIST,
+ RBAC_IN_GRANTED_LIST,
+ RBAC_IN_DENIED_LIST,
+ RBAC_ID_DOES_NOT_EXISTS
+};
+
+typedef std::bitset<RBAC_PERM_MAX> RBACPermissionContainer;
+typedef std::set<uint32> RBACRoleContainer;
+typedef std::set<uint32> RBACGroupContainer;
+
+class RBACObject
+{
+ public:
+ RBACObject(uint32 id = 0, std::string const& name = ""):
+ _id(id), _name(name) { }
+
+ /// Gets the Name of the Object
+ std::string const& GetName() const { return _name; }
+ /// Gets the Id of the Object
+ uint32 GetId() const { return _id; }
+
+ private:
+ uint32 _id; ///> id of the object
+ std::string _name; ///> name of the object
+};
+
+/// Permission: Defines an autorization to perform certain operation
+class RBACPermission: public RBACObject
+{
+ public:
+ RBACPermission(uint32 id = 0, std::string const& name = ""):
+ RBACObject(id, name) { }
+};
+
+/// Set of Permissions
+class RBACRole: public RBACObject
+{
+ public:
+ RBACRole(uint32 id = 0, std::string const& name = ""):
+ RBACObject(id, name) { }
+
+ /// Gets the Permissions assigned to this role
+ RBACPermissionContainer const& GetPermissions() const { return _perms; }
+ /// Grants a Permission (Adds)
+ void GrantPermission(uint32 id) { _perms.set(id); }
+ /// Revokes a Permission (Removes)
+ void RevokePermission(uint32 id) { _perms.reset(id); }
+
+ private:
+ RBACPermissionContainer _perms; ///> Set of permissions
+};
+
+/// Set of Roles
+class RBACGroup: public RBACObject
+{
+ public:
+ RBACGroup(uint32 id = 0, std::string const& name = ""):
+ RBACObject(id, name) { }
+
+ /// Gets the Roles assigned to this group
+ RBACRoleContainer const& GetRoles() const { return _roles; }
+ /// Grants a Role (Adds)
+ void GrantRole(uint32 role) { _roles.insert(role); }
+ /// Revokes a Role (Removes)
+ void RevokeRole(uint32 role) { _roles.erase(role); }
+
+ private:
+ RBACRoleContainer _roles; ///> Set of Roles
+};
+
+/*
+ * @name RBACData
+ * @brief Contains all needed information about the acccount
+ *
+ * This class contains all the data needed to calculate the account permissions.
+ * RBACDAta is formed by group permissions and user permissions through:
+ * - Granted Groups, which contains roles, which contains permissions: Set of granted permissions
+ * - Granted Roles, which contains permissions: Set of granted permissions
+ * - Denied Roles, which contains permissions: Set of denied permissions
+ * - Granted Permissions
+ * - Denied Permissions
+ *
+ * Calculation of current Permissions: Granted permissions - Denied permissions
+ * - Granted permissions: through groups, through roles and directly assigned
+ * - Denied permissions: through roles and directly assigned
+ */
+class RBACData: public RBACObject
+{
+ public:
+ RBACData(uint32 id, std::string const& name, int32 realmId):
+ RBACObject(id, name), _realmId(realmId) { }
+
+ /**
+ * @name HasPermission
+ * @brief Checks if certain action is allowed
+ *
+ * Checks if certain action can be performed.
+ *
+ * @return grant or deny action
+ *
+ * Example Usage:
+ * @code
+ * bool Player::CanJoinArena(Battleground* bg)
+ * {
+ * return bg->isArena() && HasPermission(RBAC_PERM_JOIN_ARENA);
+ * }
+ * @endcode
+ */
+ bool HasPermission(uint32 permission) { return _globalPerms.test(permission); }
+
+ // Functions enabled to be used by command system
+ /// Returns all the granted permissions (after computation)
+ RBACPermissionContainer const& GetPermissions() const { return _globalPerms; }
+ /// Returns all the granted permissions
+ RBACPermissionContainer const& GetGrantedPermissions() const { return _grantedPerms; }
+ /// Returns all the denied permissions
+ RBACPermissionContainer const& GetDeniedPermissions() const { return _deniedPerms; }
+ /// Returns all the granted roles
+ RBACRoleContainer const& GetGrantedRoles() const { return _grantedRoles; }
+ /// Returns all the denied roles
+ RBACRoleContainer const& GetDeniedRoles() const { return _deniedRoles; }
+ /// Returns all the granted groups
+ RBACGroupContainer const& GetGroups() const { return _groups; }
+
+ /**
+ * @name AddGroup
+ * @brief Adds new group
+ *
+ * Add a new group to the account. If realm is 0 or the group can not be added
+ * No save to db action will be performed.
+ *
+ * Fails if group Id does not exists or group already present
+ *
+ * @param groupId group to be added
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to add the group
+ *
+ * Example Usage:
+ * @code
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 groupId = 2;
+ * if (rbac->AddGroup(groupId) == RBAC_OK)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Group %u succesfully added", groupId);
+ * @endcode
+ */
+ RBACCommandResult AddGroup(uint32 groupId, int32 realmId = 0);
+
+ /**
+ * @name RemoveGroup
+ * @brief Removes a group
+ *
+ * Removes a group from the account. If realm is 0 or the group can not be removed
+ * No save to db action will be performed. Any delete operation will always affect
+ * "all realms (-1)" in addition to the realm specified
+ *
+ * Fails if group not present
+ *
+ * @param groupId group to be removed
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to remove the group
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 groupId = 2;
+ * if (rbac->RemoveGroup(groupId) == RBAC_OK)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Group %u succesfully removed", groupId);
+ * @endcode
+ */
+ RBACCommandResult RemoveGroup(uint32 groupId, int32 realmId = 0);
+
+ /**
+ * @name GrantRole
+ * @brief Grants a role
+ *
+ * Grants a role to the account. If realm is 0 or the role can not be added
+ * No save to db action will be performed.
+ *
+ * Fails if role Id does not exists or role already granted or denied
+ *
+ * @param roleId role to be granted
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to grant the role
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 roleId = 2;
+ * if (rbac->GrantRole(roleId) == RBAC_IN_DENIED_LIST)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Failed to grant role %u, already denied", roleId);
+ * @endcode
+ */
+ RBACCommandResult GrantRole(uint32 roleId, int32 realmId = 0);
+
+ /**
+ * @name DenyRole
+ * @brief Denies a role
+ *
+ * Denied a role to the account. If realm is 0 or the role can not be added
+ * No save to db action will be performed.
+ *
+ * Fails if role Id does not exists or role already granted or denied
+ *
+ * @param roleId role to be denied
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to deny the role
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 roleId = 2;
+ * if (rbac->DenyRole(roleId) == RBAC_ID_DOES_NOT_EXISTS)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Role Id %u does not exists", roleId);
+ * @endcode
+ */
+ RBACCommandResult DenyRole(uint32 roleId, int32 realmId = 0);
+
+ /**
+ * @name RevokeRole
+ * @brief Removes a role
+ *
+ * Removes a role from the account. If realm is 0 or the role can not be removed
+ * No save to db action will be performed. Any delete operation will always affect
+ * "all realms (-1)" in addition to the realm specified
+ *
+ * Fails if role not present
+ *
+ * @param roleId role to be removed
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to remove the role
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 roleId = 2;
+ * if (rbac->RevokeRole(roleId) == RBAC_OK)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Role %u succesfully removed", roleId);
+ * @endcode
+ */
+ RBACCommandResult RevokeRole(uint32 roleId, int32 realmId = 0);
+
+ /**
+ * @name GrantRole
+ * @brief Grants a permission
+ *
+ * Grants a permission to the account. If realm is 0 or the permission can not be added
+ * No save to db action will be performed.
+ *
+ * Fails if permission Id does not exists or permission already granted or denied
+ *
+ * @param permissionId permission to be granted
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to grant the permission
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 permissionId = 2;
+ * if (rbac->GrantRole(permissionId) == RBAC_IN_DENIED_LIST)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Failed to grant permission %u, already denied", permissionId);
+ * @endcode
+ */
+ RBACCommandResult GrantPermission(uint32 permissionId, int32 realmId = 0);
+
+ /**
+ * @name DenyPermission
+ * @brief Denies a permission
+ *
+ * Denied a permission to the account. If realm is 0 or the permission can not be added
+ * No save to db action will be performed.
+ *
+ * Fails if permission Id does not exists or permission already granted or denied
+ *
+ * @param permissionId permission to be denied
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to deny the permission
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 permissionId = 2;
+ * if (rbac->DenyRole(permissionId) == RBAC_ID_DOES_NOT_EXISTS)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Role Id %u does not exists", permissionId);
+ * @endcode
+ */
+ RBACCommandResult DenyPermission(uint32 permissionId, int32 realmId = 0);
+
+ /**
+ * @name RevokePermission
+ * @brief Removes a permission
+ *
+ * Removes a permission from the account. If realm is 0 or the permission can not be removed
+ * No save to db action will be performed. Any delete operation will always affect
+ * "all realms (-1)" in addition to the realm specified
+ *
+ * Fails if permission not present
+ *
+ * @param permissionId permission to be removed
+ * @param realmId realm affected
+ *
+ * @return Success or failure (with reason) to remove the permission
+ *
+ * Example Usage:
+ * // previously defined "RBACData* rbac" with proper initialization
+ * uint32 permissionId = 2;
+ * if (rbac->RevokeRole(permissionId) == RBAC_OK)
+ * sLog->outDebug(LOG_FILTER_PLAYER, "Permission %u succesfully removed", permissionId);
+ * @endcode
+ */
+ RBACCommandResult RevokePermission(uint32 permissionId, int32 realmId = 0);
+
+ /// Loads all permissions, groups and roles assigned to current account
+ void LoadFromDB();
+ private:
+ /// Saves a role to DB, Granted or Denied
+ void SaveRole(uint32 role, bool granted, int32 realm);
+ /// Saves a permission to DB, Granted or Denied
+ void SavePermission(uint32 role, bool granted, int32 realm);
+
+ /**
+ * @name CalculateNewPermissions
+ * @brief Calculates new permissions
+ *
+ * Calculates new permissions after some change in groups, roles or permissions.
+ * The calculation is done Granted - Denied:
+ * - Granted permissions: through groups, through roles and directly assigned
+ * - Denied permissions: through roles and directly assigned
+ */
+ void CalculateNewPermissions();
+
+ int32 GetRealmId() { return _realmId; }
+
+ int32 _realmId; ///> RealmId Affected
+ RBACGroupContainer _groups; ///> Granted groups
+ RBACRoleContainer _grantedRoles; ///> Granted roles
+ RBACRoleContainer _deniedRoles; ///> Denied roles
+ RBACPermissionContainer _grantedPerms; ///> Granted permissions
+ RBACPermissionContainer _deniedPerms; ///> Denied permissions
+ RBACPermissionContainer _globalPerms; ///> Calculated permissions
+};
+
+#endif
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index d754ef967e6..c71b85b2d27 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -3151,14 +3151,14 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
}
uint32 dataType = fields[1].GetUInt8();
- const char* scriptName = fields[4].GetCString();
+ std::string scriptName = fields[4].GetString();
uint32 scriptId = 0;
- if (strcmp(scriptName, "")) // not empty
+ if (scriptName.length()) // not empty
{
if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT)
sLog->outError(LOG_FILTER_SQL, "Table `achievement_criteria_data` has ScriptName set for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType);
else
- scriptId = sObjectMgr->GetScriptId(scriptName);
+ scriptId = sObjectMgr->GetScriptId(scriptName.c_str());
}
AchievementCriteriaData data(dataType, fields[2].GetUInt32(), fields[3].GetUInt32(), scriptId);
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index ec118b44f59..393206f4202 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -162,16 +162,17 @@ bool Battlefield::Update(uint32 diff)
// Kick players who chose not to accept invitation to the battle
if (m_uiKickDontAcceptTimer <= diff)
{
+ time_t now = time(NULL);
for (int team = 0; team < 2; team++)
for (PlayerTimerMap::iterator itr = m_InvitedPlayers[team].begin(); itr != m_InvitedPlayers[team].end(); ++itr)
- if ((*itr).second <= time(NULL))
- KickPlayerFromBattlefield((*itr).first);
+ if (itr->second <= now)
+ KickPlayerFromBattlefield(itr->first);
InvitePlayersInZoneToWar();
for (int team = 0; team < 2; team++)
for (PlayerTimerMap::iterator itr = m_PlayersWillBeKick[team].begin(); itr != m_PlayersWillBeKick[team].end(); ++itr)
- if ((*itr).second <= time(NULL))
- KickPlayerFromBattlefield((*itr).first);
+ if (itr->second <= now)
+ KickPlayerFromBattlefield(itr->first);
m_uiKickDontAcceptTimer = 1000;
}
@@ -819,7 +820,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, Position pos, TeamId team)
Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, float o, TeamId team)
{
//Get map object
- Map* map = const_cast<Map*>(sMapMgr->CreateBaseMap(m_MapId));
+ Map* map = sMapMgr->CreateBaseMap(m_MapId);
if (!map)
{
sLog->outError(LOG_FILTER_BATTLEFIELD, "Battlefield::SpawnCreature: Can't create creature entry: %u map not found", entry);
@@ -857,7 +858,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl
GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z, float o)
{
// Get map object
- Map* map = const_cast<Map*>(sMapMgr->CreateBaseMap(571)); // *vomits*
+ Map* map = sMapMgr->CreateBaseMap(571); // *vomits*
if (!map)
return 0;
diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h
index fea5ee2e7db..9b3542fca65 100644
--- a/src/server/game/Battlefield/Battlefield.h
+++ b/src/server/game/Battlefield/Battlefield.h
@@ -72,7 +72,7 @@ class BfGraveyard;
typedef std::set<uint64> GuidSet;
typedef std::vector<BfGraveyard*> GraveyardVect;
-typedef std::map<uint64, uint32> PlayerTimerMap;
+typedef std::map<uint64, time_t> PlayerTimerMap;
class BfCapturePoint
{
diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp
index 1d99b5fd5fe..4fe00ff123c 100644
--- a/src/server/game/Battlegrounds/ArenaTeam.cpp
+++ b/src/server/game/Battlegrounds/ArenaTeam.cpp
@@ -41,14 +41,16 @@ ArenaTeam::ArenaTeam()
ArenaTeam::~ArenaTeam()
{ }
-bool ArenaTeam::Create(uint64 captainGuid, uint8 type, std::string const& teamName, uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor, uint8 borderStyle, uint32 borderColor)
+bool ArenaTeam::Create(uint64 captainGuid, uint8 type, std::string const& arenaTeamName,
+ uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor,
+ uint8 borderStyle, uint32 borderColor)
{
// Check if captain is present
if (!ObjectAccessor::FindPlayer(captainGuid))
return false;
// Check if arena team name is already taken
- if (sArenaTeamMgr->GetArenaTeamByName(teamName))
+ if (sArenaTeamMgr->GetArenaTeamByName(arenaTeamName))
return false;
// Generate new arena team id
@@ -57,7 +59,7 @@ bool ArenaTeam::Create(uint64 captainGuid, uint8 type, std::string const& teamNa
// Assign member variables
CaptainGuid = captainGuid;
Type = type;
- TeamName = teamName;
+ TeamName = arenaTeamName;
BackgroundColor = backgroundColor;
EmblemStyle = emblemStyle;
EmblemColor = emblemColor;
@@ -82,7 +84,7 @@ bool ArenaTeam::Create(uint64 captainGuid, uint8 type, std::string const& teamNa
// Add captain as member
AddMember(CaptainGuid);
- sLog->outInfo(LOG_FILTER_ARENAS, "New ArenaTeam created [Id: %u] [Type: %u] [Captain low GUID: %u]", GetId(), GetType(), captainLowGuid);
+ sLog->outDebug(LOG_FILTER_ARENAS, "New ArenaTeam created [Id: %u] [Type: %u] [Captain low GUID: %u]", GetId(), GetType(), captainLowGuid);
return true;
}
@@ -179,7 +181,7 @@ bool ArenaTeam::AddMember(uint64 playerGuid)
player->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1);
}
- sLog->outInfo(LOG_FILTER_ARENAS, "Player: %s [GUID: %u] joined arena team type: %u [Id: %u, Name: %s].", playerName.c_str(), GUID_LOPART(playerGuid), GetType(), GetId(), GetName().c_str());
+ sLog->outDebug(LOG_FILTER_ARENAS, "Player: %s [GUID: %u] joined arena team type: %u [Id: %u, Name: %s].", playerName.c_str(), GUID_LOPART(playerGuid), GetType(), GetId(), GetName().c_str());
return true;
}
@@ -291,7 +293,7 @@ void ArenaTeam::SetCaptain(uint64 guid)
newCaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 0);
if (oldCaptain)
{
- sLog->outDebug(LOG_FILTER_BATTLEGROUND, "Player: %s [GUID: %u] promoted player: %s [GUID: %u] to leader of arena team [Id: %u] [Type: %u].",
+ sLog->outDebug(LOG_FILTER_ARENAS, "Player: %s [GUID: %u] promoted player: %s [GUID: %u] to leader of arena team [Id: %u] [Type: %u].",
oldCaptain->GetName().c_str(), oldCaptain->GetGUIDLow(), newCaptain->GetName().c_str(),
newCaptain->GetGUIDLow(), GetId(), GetType());
}
@@ -308,10 +310,9 @@ void ArenaTeam::DelMember(uint64 guid, bool cleanDb)
break;
}
- // Inform player and remove arena team info from player data
+ // Remove arena team info from player data
if (Player* player = ObjectAccessor::FindPlayer(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);
@@ -330,19 +331,18 @@ void ArenaTeam::DelMember(uint64 guid, bool cleanDb)
void ArenaTeam::Disband(WorldSession* session)
{
- // Remove all members from arena team
- while (!Members.empty())
- DelMember(Members.front().Guid, false);
-
// Broadcast update
if (session)
{
BroadcastEvent(ERR_ARENA_TEAM_DISBANDED_S, 0, 2, session->GetPlayerName(), GetName(), "");
-
if (Player* player = session->GetPlayer())
sLog->outDebug(LOG_FILTER_ARENAS, "Player: %s [GUID: %u] disbanded arena team type: %u [Id: %u].", player->GetName().c_str(), player->GetGUIDLow(), GetType(), GetId());
}
+ // Remove all members from arena team
+ while (!Members.empty())
+ DelMember(Members.front().Guid, false);
+
// Update database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
@@ -580,7 +580,7 @@ uint32 ArenaTeam::GetAverageMMR(Group* group) const
if (!ObjectAccessor::FindPlayer(itr->Guid))
continue;
- // Skip if player is not member of group
+ // Skip if player is not a member of group
if (!group->IsMember(itr->Guid))
continue;
@@ -604,7 +604,7 @@ float ArenaTeam::GetChanceAgainst(uint32 ownRating, uint32 opponentRating)
return 1.0f / (1.0f + exp(log(10.0f) * (float)((float)opponentRating - (float)ownRating) / 650.0f));
}
-int32 ArenaTeam::GetMatchmakerRatingMod(uint32 ownRating, uint32 opponentRating, bool won /*, float& confidence_factor*/)
+int32 ArenaTeam::GetMatchmakerRatingMod(uint32 ownRating, uint32 opponentRating, bool won)
{
// 'Chance' calculation - to beat the opponent
// This is a simulation. Not much info on how it really works
@@ -683,17 +683,17 @@ void ArenaTeam::FinishGame(int32 mod)
}
}
-int32 ArenaTeam::WonAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32& rating_change)
+int32 ArenaTeam::WonAgainst(uint32 ownMMRating, uint32 opponentMMRating, int32& ratingChange)
{
// Called when the team has won
// Change in Matchmaker rating
- int32 mod = GetMatchmakerRatingMod(Own_MMRating, Opponent_MMRating, true);
+ int32 mod = GetMatchmakerRatingMod(ownMMRating, opponentMMRating, true);
// Change in Team Rating
- rating_change = GetRatingMod(Stats.Rating, Opponent_MMRating, true);
+ ratingChange = GetRatingMod(Stats.Rating, opponentMMRating, true);
// Modify the team stats accordingly
- FinishGame(rating_change);
+ FinishGame(ratingChange);
// Update number of wins per season and week
Stats.WeekWins += 1;
@@ -703,23 +703,23 @@ int32 ArenaTeam::WonAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32
return mod;
}
-int32 ArenaTeam::LostAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32& rating_change)
+int32 ArenaTeam::LostAgainst(uint32 ownMMRating, uint32 opponentMMRating, int32& ratingChange)
{
// Called when the team has lost
// Change in Matchmaker Rating
- int32 mod = GetMatchmakerRatingMod(Own_MMRating, Opponent_MMRating, false);
+ int32 mod = GetMatchmakerRatingMod(ownMMRating, opponentMMRating, false);
// Change in Team Rating
- rating_change = GetRatingMod(Stats.Rating, Opponent_MMRating, false);
+ ratingChange = GetRatingMod(Stats.Rating, opponentMMRating, false);
// Modify the team stats accordingly
- FinishGame(rating_change);
+ FinishGame(ratingChange);
// return the rating change, used to display it on the results screen
return mod;
}
-void ArenaTeam::MemberLost(Player* player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
+void ArenaTeam::MemberLost(Player* player, uint32 againstMatchmakerRating, int32 matchmakerRatingChange)
{
// Called for each participant of a match after losing
for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
@@ -731,7 +731,7 @@ void ArenaTeam::MemberLost(Player* player, uint32 againstMatchmakerRating, int32
itr->ModifyPersonalRating(player, mod, GetType());
// Update matchmaker rating
- itr->ModifyMatchmakerRating(MatchmakerRatingChange, GetSlot());
+ itr->ModifyMatchmakerRating(matchmakerRatingChange, GetSlot());
// Update personal played stats
itr->WeekGames +=1;
@@ -745,7 +745,7 @@ void ArenaTeam::MemberLost(Player* player, uint32 againstMatchmakerRating, int32
}
}
-void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
+void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstMatchmakerRating, int32 matchmakerRatingChange)
{
// Called for offline player after ending rated arena match!
for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
@@ -757,7 +757,7 @@ void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstMatchmakerRating, i
itr->ModifyPersonalRating(NULL, mod, GetType());
// update matchmaker rating
- itr->ModifyMatchmakerRating(MatchmakerRatingChange, GetSlot());
+ itr->ModifyMatchmakerRating(matchmakerRatingChange, GetSlot());
// update personal played stats
itr->WeekGames += 1;
@@ -767,7 +767,7 @@ void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstMatchmakerRating, i
}
}
-void ArenaTeam::MemberWon(Player* player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange)
+void ArenaTeam::MemberWon(Player* player, uint32 againstMatchmakerRating, int32 matchmakerRatingChange)
{
// called for each participant after winning a match
for (MemberList::iterator itr = Members.begin(); itr != Members.end(); ++itr)
@@ -779,7 +779,7 @@ void ArenaTeam::MemberWon(Player* player, uint32 againstMatchmakerRating, int32
itr->ModifyPersonalRating(player, mod, GetType());
// update matchmaker rating
- itr->ModifyMatchmakerRating(MatchmakerRatingChange, GetSlot());
+ itr->ModifyMatchmakerRating(matchmakerRatingChange, GetSlot());
// update personal stats
itr->WeekGames +=1;
diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h
index dc75ff64575..59b1275a549 100644
--- a/src/server/game/Battlegrounds/ArenaTeam.h
+++ b/src/server/game/Battlegrounds/ArenaTeam.h
@@ -19,9 +19,10 @@
#ifndef TRINITYCORE_ARENATEAM_H
#define TRINITYCORE_ARENATEAM_H
+#include "Define.h"
#include "QueryResult.h"
-#include <ace/Singleton.h>
#include <list>
+#include <string>
#include <map>
class WorldSession;
@@ -72,14 +73,6 @@ enum ArenaTeamEvents
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 ArenaTeamTypes
{
ARENA_TEAM_2v2 = 2,
@@ -121,18 +114,20 @@ class ArenaTeam
ArenaTeam();
~ArenaTeam();
- bool Create(uint64 captainGuid, uint8 type, std::string const& teamName, uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor, uint8 borderStyle, uint32 borderColor);
+ bool Create(uint64 captainGuid, uint8 type, std::string const& teamName,
+ uint32 backgroundColor, uint8 emblemStyle, uint32 emblemColor,
+ uint8 borderStyle, uint32 borderColor);
void Disband(WorldSession* session);
typedef std::list<ArenaTeamMember> MemberList;
- uint32 GetId() const { return TeamId; }
- uint32 GetType() const { return Type; }
- uint8 GetSlot() const { return GetSlotByType(GetType()); }
+ uint32 GetId() const { return TeamId; }
+ uint32 GetType() const { return Type; }
+ uint8 GetSlot() const { return GetSlotByType(GetType()); }
static uint8 GetSlotByType(uint32 type);
static uint8 GetTypeBySlot(uint8 slot);
- uint64 GetCaptain() const { return CaptainGuid; }
- std::string const& GetName() const { return TeamName; }
+ uint64 GetCaptain() const { return CaptainGuid; }
+ std::string const& GetName() const { return TeamName; }
const ArenaTeamStats& GetStats() const { return Stats; }
uint32 GetRating() const { return Stats.Rating; }
@@ -140,15 +135,10 @@ class ArenaTeam
void SetCaptain(uint64 guid);
bool AddMember(uint64 PlayerGuid);
-
- // Shouldn't be 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, bool cleanDb);
size_t GetMembersSize() const { return Members.size(); }
bool Empty() const { return Members.empty(); }
- MemberList::iterator m_membersBegin() { return Members.begin(); }
- MemberList::iterator m_membersEnd() { return Members.end(); }
bool IsMember(uint64 guid) const;
ArenaTeamMember* GetMember(uint64 guid);
@@ -172,24 +162,26 @@ class ArenaTeam
void SendStats(WorldSession* session);
void Inspect(WorldSession* session, uint64 guid);
- int32 GetMatchmakerRatingMod(uint32 ownRating, uint32 opponentRating, bool won);
- int32 GetRatingMod(uint32 ownRating, uint32 opponentRating, bool won);
- float GetChanceAgainst(uint32 ownRating, uint32 opponentRating);
- int32 WonAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32& rating_change);
- void MemberWon(Player* player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange);
- int32 LostAgainst(uint32 Own_MMRating, uint32 Opponent_MMRating, int32& rating_change);
- void MemberLost(Player* player, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange = -12);
- void OfflineMemberLost(uint64 guid, uint32 againstMatchmakerRating, int32 MatchmakerRatingChange = -12);
+ static int32 GetMatchmakerRatingMod(uint32 ownRating, uint32 opponentRating, bool won);
+ static int32 GetRatingMod(uint32 ownRating, uint32 opponentRating, bool won);
+ static float GetChanceAgainst(uint32 ownRating, uint32 opponentRating);
+
+ int32 WonAgainst(uint32 ownMMRating, uint32 opponentMMRating, int32& rating_change);
+ void MemberWon(Player* player, uint32 againstMatchmakerRating, int32 matchmakerRatingChange = 12);
+
+ int32 LostAgainst(uint32 ownMMRating, uint32 opponentMMRating, int32& rating_change);
+ void MemberLost(Player* player, uint32 againstMatchmakerRating, int32 matchmakerRatingChange = -12);
+ void OfflineMemberLost(uint64 guid, uint32 againstMatchmakerRating, int32 matchmakerRatingChange = -12);
void FinishWeek();
void FinishGame(int32 mod);
protected:
- uint32 TeamId;
- uint8 Type;
+ uint32 TeamId;
+ uint8 Type;
std::string TeamName;
- uint64 CaptainGuid;
+ uint64 CaptainGuid;
uint32 BackgroundColor; // ARGB format
uint8 EmblemStyle; // icon id
@@ -197,7 +189,7 @@ class ArenaTeam
uint8 BorderStyle; // border image id
uint32 BorderColor; // ARGB format
- MemberList Members;
+ MemberList Members;
ArenaTeamStats Stats;
};
#endif
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
index c370b3a26ce..1061f8aadba 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
@@ -891,6 +891,27 @@ Battleground* BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId original
bg->SetRandom(isRandom);
bg->SetGuid(MAKE_NEW_GUID(bgTypeId, 0, HIGHGUID_BATTLEGROUND));
+ // Set up correct min/max player counts for scoreboards
+ if (bg->isArena())
+ {
+ uint32 maxPlayersPerTeam = 0;
+ switch (arenaType)
+ {
+ case ARENA_TYPE_2v2:
+ maxPlayersPerTeam = 2;
+ break;
+ case ARENA_TYPE_3v3:
+ maxPlayersPerTeam = 3;
+ break;
+ case ARENA_TYPE_5v5:
+ maxPlayersPerTeam = 5;
+ break;
+ }
+
+ bg->SetMaxPlayersPerTeam(maxPlayersPerTeam);
+ bg->SetMaxPlayers(maxPlayersPerTeam * 2);
+ }
+
return bg;
}
@@ -957,8 +978,8 @@ bool BattlegroundMgr::CreateBattleground(CreateBattlegroundData& data)
bg->SetArenaorBGType(data.IsArena);
bg->SetMinPlayersPerTeam(data.MinPlayersPerTeam);
bg->SetMaxPlayersPerTeam(data.MaxPlayersPerTeam);
- bg->SetMinPlayers(data.MinPlayersPerTeam* 2);
- bg->SetMaxPlayers(data.MaxPlayersPerTeam* 2);
+ bg->SetMinPlayers(data.MinPlayersPerTeam * 2);
+ bg->SetMaxPlayers(data.MaxPlayersPerTeam * 2);
bg->SetName(data.BattlegroundName);
bg->SetTeamStartLoc(ALLIANCE, data.Team1StartLocX, data.Team1StartLocY, data.Team1StartLocZ, data.Team1StartLocO);
bg->SetTeamStartLoc(HORDE, data.Team2StartLocX, data.Team2StartLocY, data.Team2StartLocZ, data.Team2StartLocO);
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index 12de44ac57f..be3997243e4 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -102,6 +102,8 @@ set(game_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 1cbea4df7ed..12a98126dbf 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -122,8 +122,28 @@ const char *ChatHandler::GetTrinityString(int32 entry) const
bool ChatHandler::isAvailable(ChatCommand const& cmd) const
{
- // check security level only for simple command (without child commands)
- return m_session->GetSecurity() >= AccountTypes(cmd.SecurityLevel);
+ uint32 permission = 0;
+
+ ///@Workaround:: Fast adaptation to RBAC system till all commands are moved to permissions
+ switch (AccountTypes(cmd.SecurityLevel))
+ {
+ case SEC_ADMINISTRATOR:
+ permission = RBAC_PERM_ADMINISTRATOR_COMMANDS;
+ break;
+ case SEC_GAMEMASTER:
+ permission = RBAC_PERM_GAMEMASTER_COMMANDS;
+ break;
+ case SEC_MODERATOR:
+ permission = RBAC_PERM_MODERATOR_COMMANDS;
+ break;
+ case SEC_PLAYER:
+ permission = RBAC_PERM_PLAYER_COMMANDS;
+ break;
+ default: // Allow custom security levels for commands
+ return m_session->GetSecurity() >= AccountTypes(cmd.SecurityLevel);
+ }
+
+ return m_session->HasPermission(permission);
}
bool ChatHandler::HasLowerSecurity(Player* target, uint64 guid, bool strong)
@@ -431,7 +451,7 @@ bool ChatHandler::ParseCommands(char const* text)
std::string fullcmd = text;
- if (m_session && AccountMgr::IsPlayerAccount(m_session->GetSecurity()) && !sWorld->getBoolConfig(CONFIG_ALLOW_PLAYER_COMMANDS))
+ if (m_session && !m_session->HasPermission(RBAC_PERM_PLAYER_COMMANDS))
return false;
/// chat case (.command or !command format)
diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp
index bedc167b711..249c1696348 100644
--- a/src/server/game/Combat/ThreatManager.cpp
+++ b/src/server/game/Combat/ThreatManager.cpp
@@ -416,20 +416,17 @@ void ThreatManager::addThreat(Unit* victim, float threat, SpellSchoolMask school
void ThreatManager::doAddThreat(Unit* victim, float threat)
{
- uint32 reducedThreadPercent = victim->GetReducedThreatPercent();
+ uint32 redirectThreadPct = victim->GetRedirectThreatPercent();
// must check > 0.0f, otherwise dead loop
- if (threat > 0.0f && reducedThreadPercent)
+ if (threat > 0.0f && redirectThreadPct)
{
- Unit* redirectTarget = victim->GetMisdirectionTarget();
- if (redirectTarget)
- if (Aura* glyphAura = redirectTarget->GetAura(63326)) // Glyph of Vigilance
- reducedThreadPercent += glyphAura->GetSpellInfo()->Effects[0].CalcValue(glyphAura->GetCaster());
-
- float reducedThreat = threat * reducedThreadPercent / 100.0f;
- threat -= reducedThreat;
- if (redirectTarget)
- _addThreat(redirectTarget, reducedThreat);
+ if (Unit* redirectTarget = victim->GetRedirectThreatTarget())
+ {
+ float redirectThreat = CalculatePct(threat, redirectThreadPct);
+ threat -= redirectThreat;
+ _addThreat(redirectTarget, redirectThreat);
+ }
}
_addThreat(victim, threat);
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 06c7cd17492..bc1fd27e093 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -794,7 +794,7 @@ void ConditionMgr::LoadConditions(bool isReload)
if (!result)
{
- sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 conditions. DB table `conditions` is empty!");
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 conditions. DB table `conditions` is empty!");
return;
}
diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp
index fefb51323c4..7e379fdaded 100644
--- a/src/server/game/Conditions/DisableMgr.cpp
+++ b/src/server/game/Conditions/DisableMgr.cpp
@@ -42,7 +42,7 @@ namespace
DisableMap m_DisableMap;
- uint8 MAX_DISABLE_TYPES = 7;
+ uint8 MAX_DISABLE_TYPES = 8;
}
void LoadDisables()
@@ -222,6 +222,34 @@ void LoadDisables()
}
break;
}
+ case DISABLE_TYPE_MMAP:
+ {
+ MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
+ if (!mapEntry)
+ {
+ sLog->outError(LOG_FILTER_SQL, "Map entry %u from `disables` doesn't exist in dbc, skipped.", entry);
+ continue;
+ }
+ switch (mapEntry->map_type)
+ {
+ case MAP_COMMON:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for world map %u.", entry);
+ break;
+ case MAP_INSTANCE:
+ case MAP_RAID:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for instance map %u.", entry);
+ break;
+ case MAP_BATTLEGROUND:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for battleground map %u.", entry);
+ break;
+ case MAP_ARENA:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for arena map %u.", entry);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
default:
break;
}
@@ -350,6 +378,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags
case DISABLE_TYPE_BATTLEGROUND:
case DISABLE_TYPE_OUTDOORPVP:
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
+ case DISABLE_TYPE_MMAP:
return true;
case DISABLE_TYPE_VMAP:
return flags & itr->second.flags;
diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h
index 89761931048..38751b8a89f 100644
--- a/src/server/game/Conditions/DisableMgr.h
+++ b/src/server/game/Conditions/DisableMgr.h
@@ -31,7 +31,8 @@ enum DisableType
DISABLE_TYPE_BATTLEGROUND = 3,
DISABLE_TYPE_ACHIEVEMENT_CRITERIA = 4,
DISABLE_TYPE_OUTDOORPVP = 5,
- DISABLE_TYPE_VMAP = 6
+ DISABLE_TYPE_VMAP = 6,
+ DISABLE_TYPE_MMAP = 7
};
enum SpellDisableTypes
@@ -56,6 +57,11 @@ enum VmapDisableTypes
VMAP_DISABLE_LIQUIDSTATUS = 0x8
};
+enum MMapDisableTypes
+{
+ MMAP_DISABLE_PATHFINDING = 0x0
+};
+
namespace DisableMgr
{
void LoadDisables();
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index c168f0e1acf..de05fd200b3 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -288,9 +288,10 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt
// sort problematic dbc to (1) non compatible and (2) non-existed
if (FILE* f = fopen(dbcFilename.c_str(), "rb"))
{
- char buf[100];
- snprintf(buf, 100, " exists, and has %u field(s) (expected " SIZEFMTD "). Extracted file might be from wrong client version or a database-update has been forgotten.", storage.GetFieldCount(), strlen(storage.GetFormat()));
- errors.push_back(dbcFilename + buf);
+ std::ostringstream stream;
+ stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten.";
+ std::string buf = stream.str();
+ errors.push_back(buf);
fclose(f);
}
else
diff --git a/src/server/game/DungeonFinding/LFG.cpp b/src/server/game/DungeonFinding/LFG.cpp
new file mode 100644
index 00000000000..ce16ad5533e
--- /dev/null
+++ b/src/server/game/DungeonFinding/LFG.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "LFG.h"
+#include "Language.h"
+#include "ObjectMgr.h"
+
+namespace lfg
+{
+
+std::string ConcatenateDungeons(LfgDungeonSet const& dungeons)
+{
+ std::string dungeonstr = "";
+ if (!dungeons.empty())
+ {
+ std::ostringstream o;
+ LfgDungeonSet::const_iterator it = dungeons.begin();
+ o << (*it);
+ for (++it; it != dungeons.end(); ++it)
+ o << ", " << uint32(*it);
+ dungeonstr = o.str();
+ }
+ return dungeonstr;
+}
+
+std::string GetRolesString(uint8 roles)
+{
+ std::string rolesstr = "";
+
+ if (roles & PLAYER_ROLE_TANK)
+ rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_TANK));
+
+ if (roles & PLAYER_ROLE_HEALER)
+ {
+ if (!rolesstr.empty())
+ rolesstr.append(", ");
+ rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_HEALER));
+ }
+
+ if (roles & PLAYER_ROLE_DAMAGE)
+ {
+ if (!rolesstr.empty())
+ rolesstr.append(", ");
+ rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_DAMAGE));
+ }
+
+ if (roles & PLAYER_ROLE_LEADER)
+ {
+ if (!rolesstr.empty())
+ rolesstr.append(", ");
+ rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_LEADER));
+ }
+
+ if (rolesstr.empty())
+ rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_NONE));
+
+ return rolesstr;
+}
+
+std::string GetStateString(LfgState state)
+{
+ int32 entry = LANG_LFG_ERROR;
+ switch (state)
+ {
+ case LFG_STATE_NONE:
+ entry = LANG_LFG_STATE_NONE;
+ break;
+ case LFG_STATE_ROLECHECK:
+ entry = LANG_LFG_STATE_ROLECHECK;
+ break;
+ case LFG_STATE_QUEUED:
+ entry = LANG_LFG_STATE_QUEUED;
+ break;
+ case LFG_STATE_PROPOSAL:
+ entry = LANG_LFG_STATE_PROPOSAL;
+ break;
+ case LFG_STATE_DUNGEON:
+ entry = LANG_LFG_STATE_DUNGEON;
+ break;
+ case LFG_STATE_BOOT:
+ entry = LANG_LFG_STATE_BOOT;
+ break;
+ case LFG_STATE_FINISHED_DUNGEON:
+ entry = LANG_LFG_STATE_FINISHED_DUNGEON;
+ break;
+ case LFG_STATE_RAIDBROWSER:
+ entry = LANG_LFG_STATE_RAIDBROWSER;
+ break;
+ }
+
+ return std::string(sObjectMgr->GetTrinityStringForDBCLocale(entry));
+}
+
+} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFG.h b/src/server/game/DungeonFinding/LFG.h
index ebbcfb1d71a..541e4d6bd43 100644
--- a/src/server/game/DungeonFinding/LFG.h
+++ b/src/server/game/DungeonFinding/LFG.h
@@ -20,6 +20,9 @@
#include "Common.h"
+namespace lfg
+{
+
enum LFGEnum
{
LFG_TANKS_NEEDED = 1,
@@ -99,4 +102,10 @@ typedef std::list<uint64> LfgGuidList;
typedef std::map<uint64, uint8> LfgRolesMap;
typedef std::map<uint64, uint64> LfgGroupsMap;
+std::string ConcatenateDungeons(LfgDungeonSet const& dungeons);
+std::string GetRolesString(uint8 roles);
+std::string GetStateString(LfgState state);
+
+} // namespace lfg
+
#endif
diff --git a/src/server/game/DungeonFinding/LFGGroupData.cpp b/src/server/game/DungeonFinding/LFGGroupData.cpp
index 427225bf323..f711514e26d 100644
--- a/src/server/game/DungeonFinding/LFGGroupData.cpp
+++ b/src/server/game/DungeonFinding/LFGGroupData.cpp
@@ -18,6 +18,9 @@
#include "LFG.h"
#include "LFGGroupData.h"
+namespace lfg
+{
+
LfgGroupData::LfgGroupData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE),
m_Leader(0), m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS)
{ }
@@ -122,3 +125,5 @@ uint8 LfgGroupData::GetKicksLeft() const
{
return m_KicksLeft;
}
+
+} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGGroupData.h b/src/server/game/DungeonFinding/LFGGroupData.h
index 2ad020d3bc1..47e562c37aa 100644
--- a/src/server/game/DungeonFinding/LFGGroupData.h
+++ b/src/server/game/DungeonFinding/LFGGroupData.h
@@ -20,6 +20,9 @@
#include "LFG.h"
+namespace lfg
+{
+
enum LfgGroupEnum
{
LFG_GROUP_MAX_KICKS = 3,
@@ -75,4 +78,6 @@ class LfgGroupData
uint8 m_KicksLeft; ///< Number of kicks left
};
+} // namespace lfg
+
#endif
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index a931d61f740..9bb65b63557 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -33,6 +33,9 @@
#include "GameEventMgr.h"
#include "WorldSession.h"
+namespace lfg
+{
+
LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1),
m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK))
{
@@ -95,89 +98,6 @@ void LFGMgr::_SaveToDB(uint64 guid, uint32 db_guid)
CharacterDatabase.Execute(stmt);
}
-std::string LFGMgr::ConcatenateDungeons(LfgDungeonSet const& dungeons)
-{
- std::string dungeonstr = "";
- if (!dungeons.empty())
- {
- std::ostringstream o;
- LfgDungeonSet::const_iterator it = dungeons.begin();
- o << (*it);
- for (++it; it != dungeons.end(); ++it)
- o << ", " << uint32(*it);
- dungeonstr = o.str();
- }
- return dungeonstr;
-}
-
-std::string LFGMgr::GetRolesString(uint8 roles)
-{
- std::string rolesstr = "";
-
- if (roles & PLAYER_ROLE_TANK)
- rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_TANK));
-
- if (roles & PLAYER_ROLE_HEALER)
- {
- if (!rolesstr.empty())
- rolesstr.append(", ");
- rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_HEALER));
- }
-
- if (roles & PLAYER_ROLE_DAMAGE)
- {
- if (!rolesstr.empty())
- rolesstr.append(", ");
- rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_DAMAGE));
- }
-
- if (roles & PLAYER_ROLE_LEADER)
- {
- if (!rolesstr.empty())
- rolesstr.append(", ");
- rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_LEADER));
- }
-
- if (rolesstr.empty())
- rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_NONE));
-
- return rolesstr;
-}
-
-std::string LFGMgr::GetStateString(LfgState state)
-{
- int32 entry = LANG_LFG_ERROR;
- switch (state)
- {
- case LFG_STATE_NONE:
- entry = LANG_LFG_STATE_NONE;
- break;
- case LFG_STATE_ROLECHECK:
- entry = LANG_LFG_STATE_ROLECHECK;
- break;
- case LFG_STATE_QUEUED:
- entry = LANG_LFG_STATE_QUEUED;
- break;
- case LFG_STATE_PROPOSAL:
- entry = LANG_LFG_STATE_PROPOSAL;
- break;
- case LFG_STATE_DUNGEON:
- entry = LANG_LFG_STATE_DUNGEON;
- break;
- case LFG_STATE_BOOT:
- entry = LANG_LFG_STATE_BOOT;
- break;
- case LFG_STATE_FINISHED_DUNGEON:
- entry = LANG_LFG_STATE_FINISHED_DUNGEON;
- break;
- case LFG_STATE_RAIDBROWSER:
- entry = LANG_LFG_STATE_RAIDBROWSER;
- break;
- }
-
- return std::string(sObjectMgr->GetTrinityStringForDBCLocale(entry));
-}
-
/// Load rewards for completing dungeons
void LFGMgr::LoadRewards()
{
@@ -207,7 +127,7 @@ void LFGMgr::LoadRewards()
uint32 firstQuestId = fields[2].GetUInt32();
uint32 otherQuestId = fields[3].GetUInt32();
- if (!GetLFGDungeon(dungeonId))
+ if (!GetLFGDungeonEntry(dungeonId))
{
sLog->outError(LOG_FILTER_SQL, "Dungeon %u specified in table `lfg_dungeon_rewards` does not exist!", dungeonId);
continue;
@@ -248,11 +168,6 @@ LFGDungeonData const* LFGMgr::GetLFGDungeon(uint32 id)
return NULL;
}
-LFGDungeonContainer & LFGMgr::GetLFGDungeonMap()
-{
- return LfgDungeonStore;
-}
-
void LFGMgr::LoadLFGDungeons(bool reload /* = false */)
{
uint32 oldMSTime = getMSTime();
@@ -282,7 +197,7 @@ void LFGMgr::LoadLFGDungeons(bool reload /* = false */)
if (!result)
{
- sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!");
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!");
return;
}
@@ -464,6 +379,7 @@ void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */)
uint8 expansion = player->GetSession()->Expansion();
LfgDungeonSet const& dungeons = GetDungeonsByRandom(0);
LfgLockMap lock;
+ bool denyJoin = !player->GetSession()->HasPermission(RBAC_PERM_JOIN_DUNGEON_FINDER);
for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end(); ++it)
{
@@ -472,7 +388,9 @@ void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */)
continue;
uint32 lockData = 0;
- if (dungeon->expansion > expansion)
+ if (denyJoin)
+ lockData = LFG_LOCKSTATUS_RAID_LOCKED;
+ else if (dungeon->expansion > expansion)
lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION;
else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player))
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
@@ -554,7 +472,9 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
}
// Check player or group member restrictions
- if (player->InBattleground() || player->InArena() || player->InBattlegroundQueue())
+ if (!player->GetSession()->HasPermission(RBAC_PERM_JOIN_DUNGEON_FINDER))
+ joinData.result = LFG_JOIN_NOT_MEET_REQS;
+ else if (player->InBattleground() || player->InArena() || player->InBattlegroundQueue())
joinData.result = LFG_JOIN_USING_BG_SYSTEM;
else if (player->HasAura(LFG_SPELL_DUNGEON_DESERTER))
joinData.result = LFG_JOIN_DESERTER;
@@ -573,6 +493,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const
{
if (Player* plrg = itr->getSource())
{
+ if (!plrg->GetSession()->HasPermission(RBAC_PERM_JOIN_DUNGEON_FINDER))
+ joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS;
if (plrg->HasAura(LFG_SPELL_DUNGEON_DESERTER))
joinData.result = LFG_JOIN_PARTY_DESERTER;
else if (plrg->HasAura(LFG_SPELL_DUNGEON_COOLDOWN))
@@ -1163,7 +1085,6 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept)
break;
}
- teleportStore.push_back(pguid);
SetState(pguid, LFG_STATE_DUNGEON);
}
@@ -1398,10 +1319,7 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false*
sLog->outDebug(LOG_FILTER_LFG, "TeleportPlayer: Player %s is being teleported out. Current Map %u - Expected Map %u",
player->GetName().c_str(), player->GetMapId(), uint32(dungeon->map));
if (player->GetMapId() == uint32(dungeon->map))
- {
- player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW);
player->TeleportToBGEntryPoint();
- }
return;
}
@@ -1675,16 +1593,6 @@ const std::string& LFGMgr::GetComment(uint64 guid)
return PlayersStore[guid].GetComment();
}
-bool LFGMgr::IsTeleported(uint64 pguid)
-{
- if (std::find(teleportStore.begin(), teleportStore.end(), pguid) != teleportStore.end())
- {
- teleportStore.remove(pguid);
- return true;
- }
- return false;
-}
-
LfgDungeonSet const& LFGMgr::GetSelectedDungeons(uint64 guid)
{
sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::GetSelectedDungeons: [" UI64FMTD "]", guid);
@@ -2039,3 +1947,56 @@ void LFGMgr::SetupGroupMember(uint64 guid, uint64 gguid)
SetGroup(guid, gguid);
AddPlayerToGroup(gguid, guid);
}
+
+bool LFGMgr::selectedRandomLfgDungeon(uint64 guid)
+{
+ if (GetState(guid) != LFG_STATE_NONE)
+ {
+ LfgDungeonSet const& dungeons = GetSelectedDungeons(guid);
+ if (!dungeons.empty())
+ {
+ LFGDungeonData const* dungeon = GetLFGDungeon(*dungeons.begin());
+ if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LFGMgr::inLfgDungeonMap(uint64 guid, uint32 map, Difficulty difficulty)
+{
+ if (!IS_GROUP_GUID(guid))
+ guid = GetGroup(guid);
+
+ if (uint32 dungeonId = GetDungeon(guid, true))
+ if (LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId))
+ if (uint32(dungeon->map) == map && dungeon->difficulty == difficulty)
+ return true;
+
+ return false;
+}
+
+uint32 LFGMgr::GetLFGDungeonEntry(uint32 id)
+{
+ if (id)
+ if (LFGDungeonData const* dungeon = GetLFGDungeon(id))
+ return dungeon->Entry();
+
+ return 0;
+}
+
+LfgDungeonSet LFGMgr::GetRandomAndSeasonalDungeons(uint8 level, uint8 expansion)
+{
+ LfgDungeonSet randomDungeons;
+ for (lfg::LFGDungeonContainer::const_iterator itr = LfgDungeonStore.begin(); itr != LfgDungeonStore.end(); ++itr)
+ {
+ lfg::LFGDungeonData const& dungeon = itr->second;
+ if ((dungeon.type == lfg::LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id)))
+ && dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel)
+ randomDungeons.insert(dungeon.Entry());
+ }
+ return randomDungeons;
+}
+
+} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h
index 6f6e770a5bd..0795e6e22d5 100644
--- a/src/server/game/DungeonFinding/LFGMgr.h
+++ b/src/server/game/DungeonFinding/LFGMgr.h
@@ -30,6 +30,9 @@ class Group;
class Player;
class Quest;
+namespace lfg
+{
+
enum LfgOptions
{
LFG_OPTION_ENABLE_DUNGEON_FINDER = 0x01,
@@ -296,91 +299,123 @@ class LFGMgr
~LFGMgr();
public:
+ // Functions used outside lfg namespace
void Update(uint32 diff);
- // Reward
- void LoadRewards();
+ // World.cpp
+ /// Finish the dungeon for the given group. All check are performed using internal lfg data
void FinishDungeon(uint64 gguid, uint32 dungeonId);
- LfgReward const* GetRandomDungeonReward(uint32 dungeon, uint8 level);
-
- // Queue
- void JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string const& comment);
- void LeaveLfg(uint64 guid);
-
- // Role Check
- void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = PLAYER_ROLE_NONE);
-
- // Group Matching
- static bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true);
- void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap);
-
- // Proposals
- uint32 AddProposal(LfgProposal& proposal);
- void UpdateProposal(uint32 proposalId, uint64 guid, bool accept);
+ /// Loads rewards for random dungeons
+ void LoadRewards();
+ /// Loads dungeons from dbc and adds teleport coords
+ void LoadLFGDungeons(bool reload = false);
- // Teleportation
- void TeleportPlayer(Player* player, bool out, bool fromOpcode = false);
+ // Multiple files
+ /// Check if given guid applied for random dungeon
+ bool selectedRandomLfgDungeon(uint64 guid);
+ /// Check if given guid applied for given map and difficulty. Used to know
+ bool inLfgDungeonMap(uint64 guid, uint32 map, Difficulty difficulty);
+ /// Get selected dungeons
+ LfgDungeonSet const& GetSelectedDungeons(uint64 guid);
+ /// Get current lfg state
+ LfgState GetState(uint64 guid);
+ /// Get current dungeon
+ uint32 GetDungeon(uint64 guid, bool asId = true);
+ /// Get the map id of the current dungeon
+ uint32 GetDungeonMapId(uint64 guid);
+ /// Get kicks left in current group
+ uint8 GetKicksLeft(uint64 gguid);
+ /// Load Lfg group info from DB
+ void _LoadFromDB(Field* fields, uint64 guid);
+ /// Initializes player data after loading group data from DB
+ void SetupGroupMember(uint64 guid, uint64 gguid);
+ /// Return Lfg dungeon entry for given dungeon id
+ uint32 GetLFGDungeonEntry(uint32 id);
- // Vote kick
- void InitBoot(uint64 gguid, uint64 kguid, uint64 vguid, std::string const& reason);
- void UpdateBoot(uint64 guid, bool accept);
+ // cs_lfg
+ /// Get current player roles
+ uint8 GetRoles(uint64 guid);
+ /// Get current player comment (used for LFR)
+ std::string const& GetComment(uint64 gguid);
+ /// Gets current lfg options
+ uint32 GetOptions();
+ /// Sets new lfg options
+ void SetOptions(uint32 options);
+ /// Checks if given lfg option is enabled
+ bool isOptionEnabled(uint32 option);
+ /// Clears queue - Only for internal testing
+ void Clean();
+ /// Dumps the state of the queue - Only for internal testing
+ std::string DumpQueueInfo(bool full = false);
+ // LFGScripts
+ /// Get leader of the group (using internal data)
+ uint64 GetLeader(uint64 guid);
+ /// Initializes locked dungeons for given player (called at login or level change)
void InitializeLockedDungeons(Player* player, uint8 level = 0);
-
- void SetRoles(uint64 guid, uint8 roles);
- void SetComment(uint64 guid, std::string const& comment);
+ /// Sets player team
void SetTeam(uint64 guid, uint8 team);
+ /// Sets player group
void SetGroup(uint64 guid, uint64 group);
+ /// Gets player group
+ uint64 GetGroup(uint64 guid);
+ /// Sets the leader of the group
void SetLeader(uint64 gguid, uint64 leader);
- void SetState(uint64 guid, LfgState state);
-
- void _LoadFromDB(Field* fields, uint64 guid);
- void _SaveToDB(uint64 guid, uint32 db_guid);
-
- void RemovePlayerData(uint64 guid);
+ /// Removes saved group data
void RemoveGroupData(uint64 guid);
+ /// Removes a player from a group
uint8 RemovePlayerFromGroup(uint64 gguid, uint64 guid);
+ /// Adds player to group
void AddPlayerToGroup(uint64 gguid, uint64 guid);
+ // LFGHandler
+ /// Get locked dungeons
LfgLockMap const& GetLockedDungeons(uint64 guid);
- LfgDungeonSet const& GetSelectedDungeons(uint64 guid);
- uint32 GetDungeon(uint64 guid, bool asId = true);
- uint32 GetDungeonMapId(uint64 guid);
- LfgState GetState(uint64 guid);
+ /// Returns current lfg status
+ LfgUpdateData GetLfgStatus(uint64 guid);
+ /// Checks if Seasonal dungeon is active
+ bool IsSeasonActive(uint32 dungeonId);
+ /// Gets the random dungeon reward corresponding to given dungeon and player level
+ LfgReward const* GetRandomDungeonReward(uint32 dungeon, uint8 level);
+ /// Returns all random and seasonal dungeons for given level and expansion
+ LfgDungeonSet GetRandomAndSeasonalDungeons(uint8 level, uint8 expansion);
+ /// Teleport a player to/from selected dungeon
+ void TeleportPlayer(Player* player, bool out, bool fromOpcode = false);
+ /// Inits new proposal to boot a player
+ void InitBoot(uint64 gguid, uint64 kguid, uint64 vguid, std::string const& reason);
+ /// Updates player boot proposal with new player answer
+ void UpdateBoot(uint64 guid, bool accept);
+ /// Updates proposal to join dungeon with player answer
+ void UpdateProposal(uint32 proposalId, uint64 guid, bool accept);
+ /// Updates the role check with player answer
+ void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = PLAYER_ROLE_NONE);
+ /// Sets player lfg roles
+ void SetRoles(uint64 guid, uint8 roles);
+ /// Sets player lfr comment
+ void SetComment(uint64 guid, std::string const& comment);
+ /// Join Lfg with selected roles, dungeons and comment
+ void JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string const& comment);
+ /// Leaves lfg
+ void LeaveLfg(uint64 guid);
+
+ // LfgQueue
+ /// Get last lfg state (NONE, DUNGEON or FINISHED_DUNGEON)
LfgState GetOldState(uint64 guid);
- uint8 GetKicksLeft(uint64 gguid);
- uint64 GetLeader(uint64 guid);
+ /// Check if given group guid is lfg
bool IsLfgGroup(uint64 guid);
- uint8 GetRoles(uint64 guid);
- std::string const& GetComment(uint64 gguid);
- LfgGuidSet const& GetPlayers(uint64 guid);
+ /// Gets the player count of given group
uint8 GetPlayerCount(uint64 guid);
-
- bool IsTeleported(uint64 guid);
-
+ /// Add a new Proposal
+ uint32 AddProposal(LfgProposal& proposal);
+ /// Checks if all players are queued
bool AllQueued(LfgGuidList const& check);
- void Clean();
-
+ /// Checks if given roles match, modifies given roles map with new roles
+ static bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true);
+ /// Checks if given players are ignoring each other
static bool HasIgnore(uint64 guid1, uint64 guid2);
+ /// Sends queue status to player
static void SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data);
- bool isOptionEnabled(uint32 option);
- uint32 GetOptions();
- void SetOptions(uint32 options);
- LfgUpdateData GetLfgStatus(uint64 guid);
- bool IsSeasonActive(uint32 dungeonId);
-
- std::string DumpQueueInfo(bool full = false);
- static std::string ConcatenateDungeons(LfgDungeonSet const& dungeons);
- static std::string GetRolesString(uint8 roles);
- static std::string GetStateString(LfgState state);
-
- void LoadLFGDungeons(bool reload = false);
- LFGDungeonData const* GetLFGDungeon(uint32 id);
- LFGDungeonContainer& GetLFGDungeonMap();
- void SetupGroupMember(uint64 guid, uint64 gguid);
- uint64 GetGroup(uint64 guid);
-
private:
uint8 GetTeam(uint64 guid);
void RestoreState(uint64 guid, char const* debugMsg);
@@ -389,6 +424,11 @@ class LFGMgr
void SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons);
void SetLockedDungeons(uint64 guid, LfgLockMap const& lock);
void DecreaseKicksLeft(uint64 guid);
+ void SetState(uint64 guid, LfgState state);
+ void RemovePlayerData(uint64 guid);
+ void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap);
+ void _SaveToDB(uint64 guid, uint32 db_guid);
+ LFGDungeonData const* GetLFGDungeon(uint32 id);
// Proposals
void RemoveProposal(LfgProposalContainer::iterator itProposal, LfgUpdateType type);
@@ -407,6 +447,8 @@ class LFGMgr
void SendLfgUpdatePlayer(uint64 guid, LfgUpdateData const& data);
void SendLfgUpdateProposal(uint64 guid, LfgProposal const& proposal);
+ LfgGuidSet const& GetPlayers(uint64 guid);
+
// General variables
uint32 m_QueueTimer; ///< used to check interval of update
uint32 m_lfgProposalId; ///< used as internal counter for proposals
@@ -423,8 +465,9 @@ class LFGMgr
LfgPlayerBootContainer BootsStore; ///< Current player kicks
LfgPlayerDataContainer PlayersStore; ///< Player data
LfgGroupDataContainer GroupsStore; ///< Group data
- LfgGuidList teleportStore; ///< Players being teleported
};
-#define sLFGMgr ACE_Singleton<LFGMgr, ACE_Null_Mutex>::instance()
+} // namespace lfg
+
+#define sLFGMgr ACE_Singleton<lfg::LFGMgr, ACE_Null_Mutex>::instance()
#endif
diff --git a/src/server/game/DungeonFinding/LFGPlayerData.cpp b/src/server/game/DungeonFinding/LFGPlayerData.cpp
index 410076f7e75..e8ef430bc1f 100644
--- a/src/server/game/DungeonFinding/LFGPlayerData.cpp
+++ b/src/server/game/DungeonFinding/LFGPlayerData.cpp
@@ -17,6 +17,9 @@
#include "LFGPlayerData.h"
+namespace lfg
+{
+
LfgPlayerData::LfgPlayerData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE),
m_Team(0), m_Group(0), m_Roles(0), m_Comment("")
{}
@@ -122,3 +125,5 @@ LfgDungeonSet const& LfgPlayerData::GetSelectedDungeons() const
{
return m_SelectedDungeons;
}
+
+} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGPlayerData.h b/src/server/game/DungeonFinding/LFGPlayerData.h
index 055924fd364..a425ce77bad 100644
--- a/src/server/game/DungeonFinding/LFGPlayerData.h
+++ b/src/server/game/DungeonFinding/LFGPlayerData.h
@@ -20,6 +20,9 @@
#include "LFG.h"
+namespace lfg
+{
+
/**
Stores all lfg data needed about the player.
*/
@@ -68,4 +71,6 @@ class LfgPlayerData
LfgDungeonSet m_SelectedDungeons; ///< Selected Dungeons when joined LFG
};
+} // namespace lfg
+
#endif
diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp
index f1d2dbb313d..6a98fccecdc 100644
--- a/src/server/game/DungeonFinding/LFGQueue.cpp
+++ b/src/server/game/DungeonFinding/LFGQueue.cpp
@@ -27,6 +27,9 @@
#include "World.h"
#include "GroupMgr.h"
+namespace lfg
+{
+
/**
Given a list of guids returns the concatenation using | as delimiter
@@ -431,7 +434,7 @@ LfgCompatibility LFGQueue::CheckCompatibility(LfgGuidList check)
{
std::ostringstream o;
for (LfgRolesMap::const_iterator it = debugRoles.begin(); it != debugRoles.end(); ++it)
- o << ", " << it->first << ": " << sLFGMgr->GetRolesString(it->second);
+ o << ", " << it->first << ": " << GetRolesString(it->second);
sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Roles not compatible%s", strGuids.c_str(), o.str().c_str());
SetCompatibles(strGuids, LFG_INCOMPATIBLES_NO_ROLES);
@@ -441,12 +444,12 @@ LfgCompatibility LFGQueue::CheckCompatibility(LfgGuidList check)
LfgGuidList::iterator itguid = check.begin();
proposalDungeons = QueueDataStore[*itguid].dungeons;
std::ostringstream o;
- o << ", " << *itguid << ": (" << sLFGMgr->ConcatenateDungeons(proposalDungeons) << ")";
+ o << ", " << *itguid << ": (" << ConcatenateDungeons(proposalDungeons) << ")";
for (++itguid; itguid != check.end(); ++itguid)
{
LfgDungeonSet temporal;
LfgDungeonSet &dungeons = QueueDataStore[*itguid].dungeons;
- o << ", " << *itguid << ": (" << sLFGMgr->ConcatenateDungeons(dungeons) << ")";
+ o << ", " << *itguid << ": (" << ConcatenateDungeons(dungeons) << ")";
std::set_intersection(proposalDungeons.begin(), proposalDungeons.end(), dungeons.begin(), dungeons.end(), std::inserter(temporal, temporal.begin()));
proposalDungeons = temporal;
}
@@ -671,3 +674,5 @@ void LFGQueue::UpdateBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQu
--queueData.dps;
}
}
+
+} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGQueue.h b/src/server/game/DungeonFinding/LFGQueue.h
index acb78d2c0f2..db7e7bbf318 100644
--- a/src/server/game/DungeonFinding/LFGQueue.h
+++ b/src/server/game/DungeonFinding/LFGQueue.h
@@ -20,6 +20,9 @@
#include "LFG.h"
+namespace lfg
+{
+
enum LfgCompatibility
{
LFG_COMPATIBILITY_PENDING,
@@ -140,4 +143,6 @@ class LFGQueue
LfgGuidList newToQueueStore; ///< New groups to add to queue
};
+} // namespace lfg
+
#endif
diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp
index 568b61eef2f..a4716de9524 100644
--- a/src/server/game/DungeonFinding/LFGScripts.cpp
+++ b/src/server/game/DungeonFinding/LFGScripts.cpp
@@ -29,6 +29,9 @@
#include "ObjectAccessor.h"
#include "WorldSession.h"
+namespace lfg
+{
+
LFGPlayerScript::LFGPlayerScript() : PlayerScript("LFGPlayerScript")
{
}
@@ -85,6 +88,24 @@ void LFGPlayerScript::OnBindToInstance(Player* player, Difficulty difficulty, ui
sLFGMgr->InitializeLockedDungeons(player);
}
+void LFGPlayerScript::OnMapChanged(Player* player)
+{
+ Map const* map = player->GetMap();
+
+ if (sLFGMgr->inLfgDungeonMap(player->GetGUID(), map->GetId(), map->GetDifficulty()))
+ {
+ Group* group = player->GetGroup();
+ for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
+ if (Player* member = itr->getSource())
+ player->GetSession()->SendNameQueryOpcode(member->GetGUID());
+
+ if (sLFGMgr->selectedRandomLfgDungeon(player->GetGUID()))
+ player->CastSpell(player, LFG_SPELL_LUCK_OF_THE_DRAW, true);
+ }
+ else
+ player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW);
+}
+
LFGGroupScript::LFGGroupScript() : GroupScript("LFGGroupScript")
{
}
@@ -208,3 +229,5 @@ void LFGGroupScript::OnInviteMember(Group* group, uint64 guid)
if (leader && !gguid)
sLFGMgr->LeaveLfg(leader);
}
+
+} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGScripts.h b/src/server/game/DungeonFinding/LFGScripts.h
index 227543e1899..bb1900cad93 100644
--- a/src/server/game/DungeonFinding/LFGScripts.h
+++ b/src/server/game/DungeonFinding/LFGScripts.h
@@ -26,6 +26,9 @@
class Player;
class Group;
+namespace lfg
+{
+
class LFGPlayerScript : public PlayerScript
{
public:
@@ -36,6 +39,7 @@ class LFGPlayerScript : public PlayerScript
void OnLogout(Player* player);
void OnLogin(Player* player);
void OnBindToInstance(Player* player, Difficulty difficulty, uint32 mapId, bool permanent);
+ void OnMapChanged(Player* player);
};
class LFGGroupScript : public GroupScript
@@ -50,3 +54,5 @@ class LFGGroupScript : public GroupScript
void OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 oldLeaderGuid);
void OnInviteMember(Group* group, uint64 guid);
};
+
+} // namespace lfg
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index a2bed013640..0157cdf9130 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -925,15 +925,7 @@ bool Creature::isCanTrainingOf(Player* player, bool msg) const
}
break;
case TRAINER_TYPE_TRADESKILLS:
- if (GetCreatureTemplate()->trainer_spell && !player->HasSpell(GetCreatureTemplate()->trainer_spell))
- {
- if (msg)
- {
- player->PlayerTalkClass->ClearMenus();
- player->PlayerTalkClass->SendGossipMenu(11031, GetGUID());
- }
- return false;
- }
+ // There's no Blacksmith specialization on Cataclysm, conditions are not required for tradeskills
break;
default:
return false; // checked and error output at creature_template loading
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 9ac5deb56f5..255014a7bbf 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -109,7 +109,6 @@ struct CreatureTemplate
uint32 dynamicflags;
uint32 family; // enum CreatureFamily values (optional)
uint32 trainer_type;
- uint32 trainer_spell;
uint32 trainer_class;
uint32 trainer_race;
float minrangedmg;
diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp
index 700b71303ab..3cd0a1223dd 100644
--- a/src/server/game/Entities/Creature/GossipDef.cpp
+++ b/src/server/game/Entities/Creature/GossipDef.cpp
@@ -142,6 +142,9 @@ void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const
data << uint32(_questMenu.GetMenuItemCount()); // max count 0x20
+ // Store this instead of checking the Singleton every loop iteration
+ bool questLevelInTitle = sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS);
+
for (uint8 i = 0; i < _questMenu.GetMenuItemCount(); ++i)
{
QuestMenuItem const& item = _questMenu.GetItem(i);
@@ -160,6 +163,9 @@ void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const
if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(questID))
ObjectMgr::GetLocaleString(localeData->Title, locale, title);
+ if (questLevelInTitle)
+ AddQuestLevelToTitle(title, quest->GetQuestLevel());
+
data << title; // max 0x200
}
@@ -252,6 +258,10 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote eEmote, const std::string& Title
size_t count_pos = data.wpos();
data << uint8 (_questMenu.GetMenuItemCount());
uint32 count = 0;
+
+ // Store this instead of checking the Singleton every loop iteration
+ bool questLevelInTitle = sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS);
+
for (; count < _questMenu.GetMenuItemCount(); ++count)
{
QuestMenuItem const& qmi = _questMenu.GetItem(count);
@@ -267,6 +277,9 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote eEmote, const std::string& Title
if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(questID))
ObjectMgr::GetLocaleString(localeData->Title, locale, title);
+ if (questLevelInTitle)
+ AddQuestLevelToTitle(title, quest->GetQuestLevel());
+
data << uint32(questID);
data << uint32(qmi.QuestIcon);
data << int32(quest->GetQuestLevel());
@@ -318,6 +331,9 @@ void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, uint64 npcGUID,
}
}
+ if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS))
+ AddQuestLevelToTitle(questTitle, quest->GetQuestLevel());
+
WorldPacket data(SMSG_QUESTGIVER_QUEST_DETAILS, 100); // guess size
data << uint64(npcGUID);
data << uint64(0); // either 0 or a npc guid (quest giver)
@@ -467,6 +483,9 @@ void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
data << float(quest->GetPointY());
data << uint32(quest->GetPointOpt());
+ if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS))
+ AddQuestLevelToTitle(questTitle, quest->GetQuestLevel());
+
data << questTitle;
data << questObjectives;
data << questDetails;
@@ -542,6 +561,9 @@ void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, uint64 npcGUID, b
}
}
+ if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS))
+ AddQuestLevelToTitle(questTitle, quest->GetQuestLevel());
+
WorldPacket data(SMSG_QUESTGIVER_OFFER_REWARD, 50); // guess size
data << uint64(npcGUID);
data << uint32(quest->GetQuestId());
@@ -604,6 +626,9 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, uint64 npcGUID,
return;
}
+ if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS))
+ AddQuestLevelToTitle(questTitle, quest->GetQuestLevel());
+
WorldPacket data(SMSG_QUESTGIVER_REQUEST_ITEMS, 50); // guess size
data << uint64(npcGUID);
data << uint32(quest->GetQuestId());
@@ -664,3 +689,13 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, uint64 npcGUID,
_session->SendPacket(&data);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u", GUID_LOPART(npcGUID), quest->GetQuestId());
}
+
+void PlayerMenu::AddQuestLevelToTitle(std::string &title, int32 level)
+{
+ // Adds the quest level to the front of the quest title
+ // example: [13] Westfall Stew
+
+ std::stringstream questTitlePretty;
+ questTitlePretty << "[" << level << "] " << title;
+ title = questTitlePretty.str();
+}
diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h
index b1b45b3d459..7d6d21cd9ad 100644
--- a/src/server/game/Entities/Creature/GossipDef.h
+++ b/src/server/game/Entities/Creature/GossipDef.h
@@ -277,6 +277,8 @@ class PlayerMenu
void SendQuestGiverOfferReward(Quest const* quest, uint64 npcGUID, bool enableNext) const;
void SendQuestGiverRequestItems(Quest const* quest, uint64 npcGUID, bool canComplete, bool closeOnCancel) const;
+ static void AddQuestLevelToTitle(std::string &title, int32 level);
+
private:
GossipMenu _gossipMenu;
QuestMenu _questMenu;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 4c6a13136eb..0be9002e1d8 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -76,7 +76,6 @@ Object::Object() : m_PackGUID(sizeof(uint64)+1)
m_objectType = TYPEMASK_OBJECT;
m_uint32Values = NULL;
- _changedFields = NULL;
m_valuesCount = 0;
_fieldNotifyFlags = UF_FLAG_DYNAMIC;
@@ -120,8 +119,6 @@ Object::~Object()
}
delete [] m_uint32Values;
- delete [] _changedFields;
-
}
void Object::_InitValues()
@@ -129,8 +126,7 @@ void Object::_InitValues()
m_uint32Values = new uint32[m_valuesCount];
memset(m_uint32Values, 0, m_valuesCount*sizeof(uint32));
- _changedFields = new bool[m_valuesCount];
- memset(_changedFields, 0, m_valuesCount*sizeof(bool));
+ _changesMask.SetCount(m_valuesCount);
m_objectUpdated = false;
}
@@ -901,7 +897,7 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask*
void Object::ClearUpdateMask(bool remove)
{
- memset(_changedFields, 0, m_valuesCount*sizeof(bool));
+ _changesMask.Clear();
if (m_objectUpdated)
{
@@ -925,64 +921,57 @@ void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) cons
BuildValuesUpdateBlockForPlayer(&iter->second, iter->first);
}
-void Object::GetUpdateFieldData(Player const* target, uint32*& flags, bool& isOwner, bool& isItemOwner, bool& hasSpecialInfo, bool& isPartyMember) const
+uint32 Object::GetUpdateFieldData(Player const* target, uint32*& flags) const
{
- // This function assumes updatefield index is always valid
+ uint32 visibleFlag = UF_FLAG_PUBLIC;
+
+ if (target == this)
+ visibleFlag |= UF_FLAG_PRIVATE;
+
switch (GetTypeId())
{
case TYPEID_ITEM:
case TYPEID_CONTAINER:
flags = ItemUpdateFieldFlags;
- isOwner = isItemOwner = ((Item*)this)->GetOwnerGUID() == target->GetGUID();
+ if (((Item*)this)->GetOwnerGUID() == target->GetGUID())
+ visibleFlag |= UF_FLAG_OWNER | UF_FLAG_ITEM_OWNER;
break;
case TYPEID_UNIT:
case TYPEID_PLAYER:
{
Player* plr = ToUnit()->GetCharmerOrOwnerPlayerOrPlayerItself();
flags = UnitUpdateFieldFlags;
- isOwner = ToUnit()->GetOwnerGUID() == target->GetGUID();
- hasSpecialInfo = ToUnit()->HasAuraTypeWithCaster(SPELL_AURA_EMPATHY, target->GetGUID());
- isPartyMember = plr && plr->IsInSameGroupWith(target);
+ if (ToUnit()->GetOwnerGUID() == target->GetGUID())
+ visibleFlag |= UF_FLAG_OWNER;
+
+ if (HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO))
+ if (ToUnit()->HasAuraTypeWithCaster(SPELL_AURA_EMPATHY, target->GetGUID()))
+ visibleFlag |= UF_FLAG_SPECIAL_INFO;
+
+ if (plr && plr->IsInSameRaidWith(target))
+ visibleFlag |= UF_FLAG_PARTY_MEMBER;
break;
}
case TYPEID_GAMEOBJECT:
flags = GameObjectUpdateFieldFlags;
- isOwner = ToGameObject()->GetOwnerGUID() == target->GetGUID();
+ if (ToGameObject()->GetOwnerGUID() == target->GetGUID())
+ visibleFlag |= UF_FLAG_OWNER;
break;
case TYPEID_DYNAMICOBJECT:
flags = DynamicObjectUpdateFieldFlags;
- isOwner = ((DynamicObject*)this)->GetCasterGUID() == target->GetGUID();
+ if (((DynamicObject*)this)->GetCasterGUID() == target->GetGUID())
+ visibleFlag |= UF_FLAG_OWNER;
break;
case TYPEID_CORPSE:
flags = CorpseUpdateFieldFlags;
- isOwner = ToCorpse()->GetOwnerGUID() == target->GetGUID();
+ if (ToCorpse()->GetOwnerGUID() == target->GetGUID())
+ visibleFlag |= UF_FLAG_OWNER;
break;
case TYPEID_OBJECT:
break;
}
-}
-
-bool Object::IsUpdateFieldVisible(uint32 flags, bool isSelf, bool isOwner, bool isItemOwner, bool isPartyMember) const
-{
- if (flags == UF_FLAG_NONE)
- return false;
-
- if (flags & UF_FLAG_PUBLIC)
- return true;
-
- if (flags & UF_FLAG_PRIVATE && isSelf)
- return true;
- if (flags & UF_FLAG_OWNER && isOwner)
- return true;
-
- if (flags & UF_FLAG_ITEM_OWNER && isItemOwner)
- return true;
-
- if (flags & UF_FLAG_PARTY_MEMBER && isPartyMember)
- return true;
-
- return false;
+ return visibleFlag;
}
void Object::_LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count)
@@ -998,28 +987,20 @@ void Object::_LoadIntoDataField(std::string const& data, uint32 startOffset, uin
for (uint32 index = 0; index < count; ++index)
{
m_uint32Values[startOffset + index] = atol(tokens[index]);
- _changedFields[startOffset + index] = true;
+ _changesMask.SetBit(startOffset + index);
}
}
void Object::_SetUpdateBits(UpdateMask* updateMask, Player* target) const
{
- bool* indexes = _changedFields;
uint32* flags = NULL;
- bool isSelf = target == this;
- bool isOwner = false;
- bool isItemOwner = false;
- bool hasSpecialInfo = false;
- bool isPartyMember = false;
-
- GetUpdateFieldData(target, flags, isOwner, isItemOwner, hasSpecialInfo, isPartyMember);
-
+ uint32 visibleFlag = GetUpdateFieldData(target, flags);
uint32 valCount = m_valuesCount;
if (GetTypeId() == TYPEID_PLAYER && target != this)
valCount = PLAYER_END_NOT_SELF;
- for (uint16 index = 0; index < valCount; ++index, ++indexes)
- if (_fieldNotifyFlags & flags[index] || (flags[index] & UF_FLAG_SPECIAL_INFO && hasSpecialInfo) || (*indexes && IsUpdateFieldVisible(flags[index], isSelf, isOwner, isItemOwner, isPartyMember)))
+ for (uint16 index = 0; index < valCount; ++index)
+ if (_fieldNotifyFlags & flags[index] || ((flags[index] & visibleFlag) & UF_FLAG_SPECIAL_INFO) || (_changesMask.GetBit(index) && (flags[index] & visibleFlag)))
updateMask->SetBit(index);
}
@@ -1027,20 +1008,13 @@ void Object::_SetCreateBits(UpdateMask* updateMask, Player* target) const
{
uint32* value = m_uint32Values;
uint32* flags = NULL;
- bool isSelf = target == this;
- bool isOwner = false;
- bool isItemOwner = false;
- bool hasSpecialInfo = false;
- bool isPartyMember = false;
-
- GetUpdateFieldData(target, flags, isOwner, isItemOwner, hasSpecialInfo, isPartyMember);
-
+ uint32 visibleFlag = GetUpdateFieldData(target, flags);
uint32 valCount = m_valuesCount;
if (GetTypeId() == TYPEID_PLAYER && target != this)
valCount = PLAYER_END_NOT_SELF;
for (uint16 index = 0; index < valCount; ++index, ++value)
- if (_fieldNotifyFlags & flags[index] || (flags[index] & UF_FLAG_SPECIAL_INFO && hasSpecialInfo) || (*value && IsUpdateFieldVisible(flags[index], isSelf, isOwner, isItemOwner, isPartyMember)))
+ if (_fieldNotifyFlags & flags[index] || ((flags[index] & visibleFlag) & UF_FLAG_SPECIAL_INFO) || (*value && (flags[index] & visibleFlag)))
updateMask->SetBit(index);
}
@@ -1051,7 +1025,7 @@ void Object::SetInt32Value(uint16 index, int32 value)
if (m_int32Values[index] != value)
{
m_int32Values[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1068,7 +1042,7 @@ void Object::SetUInt32Value(uint16 index, uint32 value)
if (m_uint32Values[index] != value)
{
m_uint32Values[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1083,7 +1057,7 @@ void Object::UpdateUInt32Value(uint16 index, uint32 value)
ASSERT(index < m_valuesCount || PrintIndexError(index, true));
m_uint32Values[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
}
void Object::SetUInt64Value(uint16 index, uint64 value)
@@ -1093,8 +1067,8 @@ void Object::SetUInt64Value(uint16 index, uint64 value)
{
m_uint32Values[index] = PAIR64_LOPART(value);
m_uint32Values[index + 1] = PAIR64_HIPART(value);
- _changedFields[index] = true;
- _changedFields[index + 1] = true;
+ _changesMask.SetBit(index);
+ _changesMask.SetBit(index + 1);
if (m_inWorld && !m_objectUpdated)
{
@@ -1111,8 +1085,8 @@ bool Object::AddUInt64Value(uint16 index, uint64 value)
{
m_uint32Values[index] = PAIR64_LOPART(value);
m_uint32Values[index + 1] = PAIR64_HIPART(value);
- _changedFields[index] = true;
- _changedFields[index + 1] = true;
+ _changesMask.SetBit(index);
+ _changesMask.SetBit(index + 1);
if (m_inWorld && !m_objectUpdated)
{
@@ -1133,8 +1107,8 @@ bool Object::RemoveUInt64Value(uint16 index, uint64 value)
{
m_uint32Values[index] = 0;
m_uint32Values[index + 1] = 0;
- _changedFields[index] = true;
- _changedFields[index + 1] = true;
+ _changesMask.SetBit(index);
+ _changesMask.SetBit(index + 1);
if (m_inWorld && !m_objectUpdated)
{
@@ -1155,7 +1129,7 @@ void Object::SetFloatValue(uint16 index, float value)
if (m_floatValues[index] != value)
{
m_floatValues[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1179,7 +1153,7 @@ void Object::SetByteValue(uint16 index, uint8 offset, uint8 value)
{
m_uint32Values[index] &= ~uint32(uint32(0xFF) << (offset * 8));
m_uint32Values[index] |= uint32(uint32(value) << (offset * 8));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1203,7 +1177,7 @@ void Object::SetUInt16Value(uint16 index, uint8 offset, uint16 value)
{
m_uint32Values[index] &= ~uint32(uint32(0xFFFF) << (offset * 16));
m_uint32Values[index] |= uint32(uint32(value) << (offset * 16));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1270,7 +1244,7 @@ void Object::SetFlag(uint16 index, uint32 newFlag)
if (oldval != newval)
{
m_uint32Values[index] = newval;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1291,7 +1265,7 @@ void Object::RemoveFlag(uint16 index, uint32 oldFlag)
if (oldval != newval)
{
m_uint32Values[index] = newval;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1314,7 +1288,7 @@ void Object::SetByteFlag(uint16 index, uint8 offset, uint8 newFlag)
if (!(uint8(m_uint32Values[index] >> (offset * 8)) & newFlag))
{
m_uint32Values[index] |= uint32(uint32(newFlag) << (offset * 8));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1337,7 +1311,7 @@ void Object::RemoveByteFlag(uint16 index, uint8 offset, uint8 oldFlag)
if (uint8(m_uint32Values[index] >> (offset * 8)) & oldFlag)
{
m_uint32Values[index] &= ~uint32(uint32(oldFlag) << (offset * 8));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -2113,7 +2087,7 @@ void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf)
void Object::ForceValuesUpdateAtIndex(uint32 i)
{
- _changedFields[i] = true;
+ _changesMask.SetBit(i);
if (m_inWorld && !m_objectUpdated)
{
sObjectAccessor->AddUpdateObject(this);
@@ -2838,7 +2812,7 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle)
desty = pos.m_positionY + dist * std::sin(angle);
// Prevent invalid coordinates here, position is unchanged
- if (!Trinity::IsValidMapCoord(destx, desty))
+ if (!Trinity::IsValidMapCoord(destx, desty, pos.m_positionZ))
{
sLog->outFatal(LOG_FILTER_GENERAL, "WorldObject::MovePosition invalid coordinates X: %f and Y: %f were passed!", destx, desty);
return;
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index ebe29e0f5a3..8b61b6ec2c8 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -20,7 +20,7 @@
#define _OBJECT_H
#include "Common.h"
-#include "UpdateFields.h"
+#include "UpdateMask.h"
#include "UpdateData.h"
#include "GridReference.h"
#include "ObjectDefines.h"
@@ -107,7 +107,6 @@ class ByteBuffer;
class WorldSession;
class Creature;
class Player;
-class UpdateMask;
class InstanceScript;
class GameObject;
class TempSummon;
@@ -387,9 +386,7 @@ class Object
std::string _ConcatFields(uint16 startIndex, uint16 size) const;
void _LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count);
- void GetUpdateFieldData(Player const* target, uint32*& flags, bool& isOwner, bool& isItemOwner, bool& hasSpecialInfo, bool& isPartyMember) const;
-
- bool IsUpdateFieldVisible(uint32 flags, bool isSelf, bool isOwner, bool isItemOwner, bool isPartyMember) const;
+ uint32 GetUpdateFieldData(Player const* target, uint32*& flags) const;
void _SetUpdateBits(UpdateMask* updateMask, Player* target) const;
void _SetCreateBits(UpdateMask* updateMask, Player* target) const;
@@ -408,7 +405,7 @@ class Object
float *m_floatValues;
};
- bool* _changedFields;
+ UpdateMask _changesMask;
uint16 m_valuesCount;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 1fe980f60b5..a520ae67442 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -859,7 +859,6 @@ Player::Player(WorldSession* session): Unit(true), phaseMgr(this)
_activeCheats = CHEAT_NONE;
_maxPersonalArenaRate = 0;
- _ConquestCurrencyTotalWeekCap = 0;
memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*));
memset(_CUFProfiles, 0, MAX_CUF_PROFILES * sizeof(CUFProfile*));
@@ -1566,7 +1565,9 @@ void Player::Update(uint32 p_time)
GetSession()->m_muteTime = 0;
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
stmt->setInt64(0, 0); // Set the mute time to 0
- stmt->setUInt32(1, GetSession()->GetAccountId());
+ stmt->setString(1, "");
+ stmt->setString(2, "");
+ stmt->setUInt32(3, GetSession()->GetAccountId());
LoginDatabase.Execute(stmt);
}
@@ -2526,7 +2527,7 @@ void Player::Regenerate(Powers power)
return;
// Skip regeneration for power type we cannot have
- uint32 powerIndex = GetPowerIndexByClass(power, getClass());
+ uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS)
return;
@@ -4896,7 +4897,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
stmt->setUInt32(0, guid);
trans->Append(stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER);
stmt->setUInt32(0, guid);
trans->Append(stmt);
@@ -5500,7 +5501,8 @@ void Player::RepopAtGraveyard()
// and don't show spirit healer location
if (ClosestGrave)
{
- TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation());
+ float const* orientation = sObjectMgr->GetGraveyardOrientation(ClosestGrave->ID);
+ TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, orientation ? *orientation : GetOrientation());
if (isDead()) // not send if alive, because it used in TeleportTo()
{
WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
@@ -7205,14 +7207,6 @@ void Player::_LoadCurrency(PreparedQueryResult result)
_currencyStorage.insert(PlayerCurrenciesMap::value_type(currencyID, cur));
- // load total conquest cap. should be after insert.
- if (currency->Category == CURRENCY_CATEGORY_META_CONQUEST)
- {
- uint32 cap = _GetCurrencyWeekCap(currency);
- if (cap > _ConquestCurrencyTotalWeekCap)
- _ConquestCurrencyTotalWeekCap = cap;
- }
-
} while (result->NextRow());
}
@@ -7267,7 +7261,7 @@ void Player::SendNewCurrency(uint32 id) const
uint32 precision = (entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
uint32 weekCount = itr->second.weekCount / precision;
- uint32 weekCap = _GetCurrencyWeekCap(entry) / precision;
+ uint32 weekCap = GetCurrencyWeekCap(entry) / precision;
packet.WriteBit(weekCount);
packet.WriteBits(0, 4); // some flags
@@ -7279,7 +7273,7 @@ void Player::SendNewCurrency(uint32 id) const
currencyData << uint32(weekCap);
//if (seasonTotal)
- // currencyData << uint32(seasonTotal);
+ // currencyData << uint32(seasonTotal / precision);
currencyData << uint32(entry->ID);
if (weekCount)
@@ -7308,7 +7302,7 @@ void Player::SendCurrencies() const
uint32 precision = (entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
uint32 weekCount = itr->second.weekCount / precision;
- uint32 weekCap = _GetCurrencyWeekCap(entry) / precision;
+ uint32 weekCap = GetCurrencyWeekCap(entry) / precision;
packet.WriteBit(weekCount);
packet.WriteBits(0, 4); // some flags
@@ -7320,7 +7314,7 @@ void Player::SendCurrencies() const
currencyData << uint32(weekCap);
//if (seasonTotal)
- // currencyData << uint32(seasonTotal);
+ // currencyData << uint32(seasonTotal / precision);
currencyData << uint32(entry->ID);
if (weekCount)
@@ -7348,36 +7342,28 @@ void Player::SendPvpRewards() const
GetSession()->SendPacket(&packet);
}
-uint32 Player::GetCurrency(uint32 id, bool precision) const
+uint32 Player::GetCurrency(uint32 id, bool usePrecision) const
{
PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id);
if (itr == _currencyStorage.end())
return 0;
- if (!precision)
- return itr->second.totalCount;
-
CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id);
- ASSERT(currency);
+ uint32 precision = (usePrecision && currency->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
- int32 mod = currency->Flags & CURRENCY_FLAG_HIGH_PRECISION ? 100 : 1;
- return itr->second.totalCount / mod;
+ return itr->second.totalCount / precision;
}
-uint32 Player::GetCurrencyOnWeek(uint32 id, bool precision) const
+uint32 Player::GetCurrencyOnWeek(uint32 id, bool usePrecision) const
{
PlayerCurrenciesMap::const_iterator itr = _currencyStorage.find(id);
if (itr == _currencyStorage.end())
return 0;
- if (!precision)
- return itr->second.weekCount;
-
CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(id);
- ASSERT(currency);
+ uint32 precision = (usePrecision && currency->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
- int32 mod = currency->Flags & CURRENCY_FLAG_HIGH_PRECISION ? 100 : 1;
- return itr->second.weekCount / mod;
+ return itr->second.weekCount / precision;
}
bool Player::HasCurrency(uint32 id, uint32 count) const
@@ -7417,12 +7403,12 @@ void Player::ModifyCurrency(uint32 id, int32 count, bool printLog/* = true*/, bo
}
// count can't be more then weekCap if used (weekCap > 0)
- uint32 weekCap = _GetCurrencyWeekCap(currency);
+ uint32 weekCap = GetCurrencyWeekCap(currency);
if (weekCap && count > int32(weekCap))
count = weekCap;
// count can't be more then totalCap if used (totalCap > 0)
- uint32 totalCap = _GetCurrencyTotalCap(currency);
+ uint32 totalCap = GetCurrencyTotalCap(currency);
if (totalCap && count > int32(totalCap))
count = totalCap;
@@ -7457,44 +7443,30 @@ void Player::ModifyCurrency(uint32 id, int32 count, bool printLog/* = true*/, bo
itr->second.totalCount = newTotalCount;
itr->second.weekCount = newWeekCount;
- // probably excessive checks
- if (IsInWorld() && !GetSession()->PlayerLoading())
- {
- if (count > 0)
- UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CURRENCY, id, count);
-
- if (currency->Category == CURRENCY_CATEGORY_META_CONQUEST)
- {
- //original conquest cap is highest of bg/arena conquest cap.
- if (weekCap > _ConquestCurrencyTotalWeekCap)
- _ConquestCurrencyTotalWeekCap = weekCap;
- // count was changed to week limit, now we can modify original points.
- ModifyCurrency(CURRENCY_TYPE_CONQUEST_POINTS, count, printLog );
- return;
- }
+ if (count > 0)
+ UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CURRENCY, id, count);
- // on new case just set init.
- if (itr->second.state == PLAYERCURRENCY_NEW)
- {
- SendNewCurrency(id);
- return;
- }
+ if (currency->Category == CURRENCY_CATEGORY_META_CONQUEST)
+ {
+ // count was changed to week limit, now we can modify original points.
+ ModifyCurrency(CURRENCY_TYPE_CONQUEST_POINTS, count, printLog);
+ return;
+ }
- WorldPacket packet(SMSG_UPDATE_CURRENCY, 12);
+ WorldPacket packet(SMSG_UPDATE_CURRENCY, 12);
- packet.WriteBit(weekCap != 0);
- packet.WriteBit(0); // hasSeasonCount
- packet.WriteBit(printLog); // print in log
+ packet.WriteBit(weekCap != 0);
+ packet.WriteBit(0); // hasSeasonCount
+ packet.WriteBit(!printLog); // print in log
- // if hasSeasonCount packet << uint32(seasontotalearned); TODO: save this in character DB and use it
+ // if hasSeasonCount packet << uint32(seasontotalearned); TODO: save this in character DB and use it
- packet << uint32(newTotalCount / precision);
- packet << uint32(id);
- if (weekCap)
- packet << uint32(newWeekCount / precision);
+ packet << uint32(newTotalCount / precision);
+ packet << uint32(id);
+ if (weekCap)
+ packet << uint32(newWeekCount / precision);
- GetSession()->SendPacket(&packet);
- }
+ GetSession()->SendPacket(&packet);
}
}
@@ -7517,11 +7489,9 @@ uint32 Player::GetCurrencyWeekCap(uint32 id, bool usePrecision) const
if (!entry)
return 0;
- uint32 cap = _GetCurrencyWeekCap(entry);
- if (usePrecision && entry->Flags & CURRENCY_FLAG_HIGH_PRECISION)
- cap /= 100;
+ uint32 precision = (usePrecision && entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
- return cap;
+ return GetCurrencyWeekCap(entry) / precision;
}
void Player::ResetCurrencyWeekCap()
@@ -7547,15 +7517,13 @@ void Player::ResetCurrencyWeekCap()
SendDirectMessage(&data);
}
-uint32 Player::_GetCurrencyWeekCap(const CurrencyTypesEntry* currency) const
+uint32 Player::GetCurrencyWeekCap(CurrencyTypesEntry const* currency) const
{
- uint32 cap = currency->WeekCap;
-
switch (currency->ID)
{
//original conquest not have week cap
case CURRENCY_TYPE_CONQUEST_POINTS:
- return _ConquestCurrencyTotalWeekCap;
+ return std::max(GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_META_ARENA, false), GetCurrencyWeekCap(CURRENCY_TYPE_CONQUEST_META_RBG, false));
case CURRENCY_TYPE_CONQUEST_META_ARENA:
// should add precision mod = 100
return Trinity::Currency::ConquestRatingCalculator(_maxPersonalArenaRate) * CURRENCY_PRECISION;
@@ -7564,18 +7532,10 @@ uint32 Player::_GetCurrencyWeekCap(const CurrencyTypesEntry* currency) const
return Trinity::Currency::BgConquestRatingCalculator(GetRBGPersonalRating()) * CURRENCY_PRECISION;
}
- if (cap != currency->WeekCap && IsInWorld() && !GetSession()->PlayerLoading())
- {
- WorldPacket packet(SMSG_UPDATE_CURRENCY_WEEK_LIMIT, 8);
- packet << uint32(cap / ((currency->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? 100 : 1));
- packet << uint32(currency->ID);
- GetSession()->SendPacket(&packet);
- }
-
- return cap;
+ return currency->WeekCap;
}
-uint32 Player::_GetCurrencyTotalCap(const CurrencyTypesEntry* currency) const
+uint32 Player::GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const
{
uint32 cap = currency->TotalCap;
@@ -7600,6 +7560,26 @@ uint32 Player::_GetCurrencyTotalCap(const CurrencyTypesEntry* currency) const
return cap;
}
+void Player::UpdateConquestCurrencyCap(uint32 currency)
+{
+ uint32 currenciesToUpdate[2] = { currency, CURRENCY_TYPE_CONQUEST_POINTS };
+
+ for (uint32 i = 0; i < 2; ++i)
+ {
+ CurrencyTypesEntry const* currencyEntry = sCurrencyTypesStore.LookupEntry(currenciesToUpdate[i]);
+ if (!currencyEntry)
+ continue;
+
+ uint32 precision = (currencyEntry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? 100 : 1;
+ uint32 cap = GetCurrencyWeekCap(currencyEntry);
+
+ WorldPacket packet(SMSG_UPDATE_CURRENCY_WEEK_LIMIT, 8);
+ packet << uint32(cap / precision);
+ packet << uint32(currenciesToUpdate[i]);
+ GetSession()->SendPacket(&packet);
+ }
+}
+
void Player::SetInGuild(uint32 guildId)
{
if (guildId)
@@ -7635,7 +7615,10 @@ void Player::SetArenaTeamInfoField(uint8 slot, ArenaTeamInfoType type, uint32 v
{
SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + type, value);
if (type == ARENA_TEAM_PERSONAL_RATING && value > _maxPersonalArenaRate)
+ {
_maxPersonalArenaRate = value;
+ UpdateConquestCurrencyCap(CURRENCY_TYPE_CONQUEST_META_ARENA);
+ }
}
uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type)
@@ -7754,14 +7737,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
}
// group update
- if (Group* group = GetGroup())
- {
+ if (GetGroup())
SetGroupUpdateFlag(GROUP_UPDATE_FULL);
- if (GetSession() && group->isLFGGroup() && sLFGMgr->IsTeleported(GetGUID()))
- for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
- if (Player* member = itr->getSource())
- GetSession()->SendNameQueryOpcode(member->GetGUID());
- }
m_zoneUpdateId = newZone;
m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
@@ -10825,7 +10802,7 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &des
{
if (no_space_count)
*no_space_count = count;
- return swap ? EQUIP_ERR_CANT_SWAP :EQUIP_ERR_ITEM_NOT_FOUND;
+ return swap ? EQUIP_ERR_CANT_SWAP : EQUIP_ERR_ITEM_NOT_FOUND;
}
if (pItem)
@@ -11365,8 +11342,37 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool
if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot))
return EQUIP_ERR_NO_SLOT_AVAILABLE;
- // if swap ignore item (equipped also)
- InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? eslot : uint8(NULL_SLOT));
+ // if we are swapping 2 equiped items, CanEquipUniqueItem check
+ // should ignore the item we are trying to swap, and not the
+ // destination item. CanEquipUniqueItem should ignore destination
+ // item only when we are swapping weapon from bag
+ uint8 ignore = uint8(NULL_SLOT);
+ switch (eslot)
+ {
+ case EQUIPMENT_SLOT_MAINHAND:
+ ignore = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case EQUIPMENT_SLOT_OFFHAND:
+ ignore = EQUIPMENT_SLOT_MAINHAND;
+ break;
+ case EQUIPMENT_SLOT_FINGER1:
+ ignore = EQUIPMENT_SLOT_FINGER2;
+ break;
+ case EQUIPMENT_SLOT_FINGER2:
+ ignore = EQUIPMENT_SLOT_FINGER1;
+ break;
+ case EQUIPMENT_SLOT_TRINKET1:
+ ignore = EQUIPMENT_SLOT_TRINKET2;
+ break;
+ case EQUIPMENT_SLOT_TRINKET2:
+ ignore = EQUIPMENT_SLOT_TRINKET1;
+ break;
+ }
+
+ if (ignore == uint8(NULL_SLOT) || pItem != GetItemByPos(INVENTORY_SLOT_BAG_0, ignore))
+ ignore = eslot;
+
+ InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? ignore : uint8(NULL_SLOT));
if (res2 != EQUIP_ERR_OK)
return res2;
@@ -11760,22 +11766,12 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const
InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObject const* lootedObject) const
{
- LfgDungeonSet const& dungeons = sLFGMgr->GetSelectedDungeons(GetGUID());
- if (dungeons.empty())
- return EQUIP_ERR_OK; // not using LFG
-
if (!GetGroup() || !GetGroup()->isLFGGroup())
return EQUIP_ERR_OK; // not in LFG group
// check if looted object is inside the lfg dungeon
- bool lootedObjectInDungeon = false;
Map const* map = lootedObject->GetMap();
- if (uint32 dungeonId = sLFGMgr->GetDungeon(GetGroup()->GetGUID(), true))
- if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId))
- if (uint32(dungeon->map) == map->GetId() && dungeon->difficulty == map->GetDifficulty())
- lootedObjectInDungeon = true;
-
- if (!lootedObjectInDungeon)
+ if (!sLFGMgr->inLfgDungeonMap(GetGroup()->GetGUID(), map->GetId(), map->GetDifficulty()))
return EQUIP_ERR_OK;
if (!proto)
@@ -14275,7 +14271,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
VendorItemData const* vendorItems = creature->GetVendorItems();
if (!vendorItems || vendorItems->Empty())
{
- sLog->outError(LOG_FILTER_SQL, "Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", creature->GetGUIDLow(), creature->GetEntry());
+ sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u, Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", creature->GetGUIDLow(), creature->GetEntry());
canTalk = false;
}
break;
@@ -17334,7 +17330,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
uint32 loadedPowers = 0;
for (uint32 i = 0; i < MAX_POWERS; ++i)
{
- if (GetPowerIndexByClass(Powers(i), getClass()) != MAX_POWERS)
+ if (GetPowerIndex(i) != MAX_POWERS)
{
uint32 savedPower = fields[47+loadedPowers].GetUInt32();
uint32 maxPower = GetUInt32Value(UNIT_FIELD_MAXPOWER1 + loadedPowers);
@@ -18928,7 +18924,7 @@ void Player::SaveToDB(bool create /*=false*/)
uint32 storedPowers = 0;
for (uint32 i = 0; i < MAX_POWERS; ++i)
{
- if (GetPowerIndexByClass(Powers(i), getClass()) != MAX_POWERS)
+ if (GetPowerIndex(i) != MAX_POWERS)
{
stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_POWER1 + storedPowers));
if (++storedPowers >= MAX_POWERS_PER_CLASS)
@@ -19048,7 +19044,7 @@ void Player::SaveToDB(bool create /*=false*/)
uint32 storedPowers = 0;
for (uint32 i = 0; i < MAX_POWERS; ++i)
{
- if (GetPowerIndexByClass(Powers(i), getClass()) != MAX_POWERS)
+ if (GetPowerIndex(i) != MAX_POWERS)
{
stmt->setUInt32(index++, GetUInt32Value(UNIT_FIELD_POWER1 + storedPowers));
if (++storedPowers >= MAX_POWERS_PER_CLASS)
@@ -20917,11 +20913,6 @@ void Player::LeaveAllArenaTeams(uint64 guid)
while (result->NextRow());
}
-uint32 Player::GetRBGPersonalRating() const
-{
- return 0;
-}
-
void Player::SetRestBonus(float rest_bonus_new)
{
// Prevent resting on max level
@@ -21325,6 +21316,7 @@ void Player::InitDisplayIds()
inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot, int32 price, ItemTemplate const* pProto, Creature* pVendor, VendorItem const* crItem, bool bStore)
{
+ uint32 stacks = count / pProto->BuyCount;
ItemPosCountVec vDest;
uint16 uiDest = 0;
InventoryResult msg = bStore ?
@@ -21344,13 +21336,13 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c
for (int i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
{
if (iece->RequiredItem[i])
- DestroyItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i], true);
+ DestroyItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i] * stacks, true);
}
for (int i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
{
if (iece->RequiredCurrency[i])
- ModifyCurrency(iece->RequiredCurrency[i], -int32(iece->RequiredCurrencyCount[i]), true, true);
+ ModifyCurrency(iece->RequiredCurrency[i], -int32(iece->RequiredCurrencyCount[i] * stacks), true, true);
}
}
@@ -21387,8 +21379,6 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c
bool Player::BuyCurrencyFromVendorSlot(uint64 vendorGuid, uint32 vendorSlot, uint32 currency, uint32 count)
{
- vendorSlot += 1;
-
// cheating attempt
if (count < 1) count = 1;
@@ -21431,9 +21421,17 @@ bool Player::BuyCurrencyFromVendorSlot(uint64 vendorGuid, uint32 vendorSlot, uin
return false;
}
+ if (count % crItem->maxcount)
+ {
+ SendEquipError(EQUIP_ERR_CANT_BUY_QUANTITY, NULL, NULL);
+ return false;
+ }
+
+ uint32 stacks = count / crItem->maxcount;
+ ItemExtendedCostEntry const* iece = NULL;
if (crItem->ExtendedCost)
{
- ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
if (!iece)
{
sLog->outError(LOG_FILTER_PLAYER, "Currency %u have wrong ExtendedCost field value %u", currency, crItem->ExtendedCost);
@@ -21442,7 +21440,7 @@ bool Player::BuyCurrencyFromVendorSlot(uint64 vendorGuid, uint32 vendorSlot, uin
for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
{
- if (iece->RequiredItem[i] && !HasItemCount(iece->RequiredItem[i], (iece->RequiredItemCount[i] * count)))
+ if (iece->RequiredItem[i] && !HasItemCount(iece->RequiredItem[i], (iece->RequiredItemCount[i] * stacks)))
{
SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
return false;
@@ -21461,9 +21459,7 @@ bool Player::BuyCurrencyFromVendorSlot(uint64 vendorGuid, uint32 vendorSlot, uin
return false;
}
- uint32 precision = (entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
-
- if (!HasCurrency(iece->RequiredCurrency[i], (iece->RequiredCurrencyCount[i] * count) / precision))
+ if (!HasCurrency(iece->RequiredCurrency[i], (iece->RequiredCurrencyCount[i] * stacks)))
{
SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL); // Find correct error
return false;
@@ -21484,7 +21480,25 @@ bool Player::BuyCurrencyFromVendorSlot(uint64 vendorGuid, uint32 vendorSlot, uin
return false;
}
- ModifyCurrency(currency, crItem->maxcount, true, true);
+ ModifyCurrency(currency, count, true, true);
+ if (iece)
+ {
+ for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
+ {
+ if (!iece->RequiredItem[i])
+ continue;
+
+ DestroyItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i] * stacks, true);
+ }
+
+ for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
+ {
+ if (!iece->RequiredCurrency[i])
+ continue;
+
+ ModifyCurrency(iece->RequiredCurrency[i], -int32(iece->RequiredCurrencyCount[i]) * stacks, false, true);
+ }
+ }
return true;
}
@@ -21557,12 +21571,13 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32
if (crItem->ExtendedCost)
{
// Can only buy full stacks for extended cost
- if (pProto->BuyCount != count)
+ if (count % pProto->BuyCount)
{
SendEquipError(EQUIP_ERR_CANT_BUY_QUANTITY, NULL, NULL);
return false;
}
+ uint32 stacks = count / pProto->BuyCount;
ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
if (!iece)
{
@@ -21572,7 +21587,7 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32
for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i)
{
- if (iece->RequiredItem[i] && !HasItemCount(iece->RequiredItem[i], (iece->RequiredItemCount[i] * count)))
+ if (iece->RequiredItem[i] && !HasItemCount(iece->RequiredItem[i], iece->RequiredItemCount[i] * stacks))
{
SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
return false;
@@ -21591,9 +21606,7 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32
return false;
}
- uint32 precision = (entry->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
-
- if (!HasCurrency(iece->RequiredCurrency[i], (iece->RequiredCurrencyCount[i] * count) / precision))
+ if (!HasCurrency(iece->RequiredCurrency[i], iece->RequiredCurrencyCount[i] * stacks))
{
SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
return false;
@@ -22141,12 +22154,21 @@ void Player::LeaveBattleground(bool teleportToEntryPoint)
}
}
-bool Player::CanJoinToBattleground() const
+bool Player::CanJoinToBattleground(Battleground const* bg) const
{
// check Deserter debuff
if (HasAura(26013))
return false;
+ if (bg->isArena() && !GetSession()->HasPermission(RBAC_PERM_JOIN_ARENAS))
+ return false;
+
+ if (bg->IsRandom() && !GetSession()->HasPermission(RBAC_PERM_JOIN_RANDOM_BG))
+ return false;
+
+ if (!GetSession()->HasPermission(RBAC_PERM_JOIN_NORMAL_BG))
+ return false;
+
return true;
}
@@ -22430,26 +22452,28 @@ void Player::InitPrimaryProfessions()
SetFreePrimaryProfessions(sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
}
-void Player::ModifyMoney(int64 d)
+bool Player::ModifyMoney(int64 amount, bool sendError /*= true*/)
{
- sScriptMgr->OnPlayerMoneyChanged(this, d);
+ if (!amount)
+ return true;
+
+ sScriptMgr->OnPlayerMoneyChanged(this, amount);
- if (d < 0)
- SetMoney (GetMoney() > uint64(-d) ? GetMoney() + d : 0);
+ if (amount < 0)
+ SetMoney (GetMoney() > uint64(-amount) ? GetMoney() + amount : 0);
else
{
- uint64 newAmount = 0;
- if (GetMoney() < uint64(MAX_MONEY_AMOUNT - d))
- newAmount = GetMoney() + d;
+ if (GetMoney() < uint64(MAX_MONEY_AMOUNT - amount))
+ SetMoney(GetMoney() + amount);
else
{
- // "At Gold Limit"
- newAmount = MAX_MONEY_AMOUNT;
- if (d)
+ if (sendError)
SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
+ return false;
}
- SetMoney(newAmount);
}
+
+ return true;
}
Unit* Player::GetSelectedUnit() const
@@ -23811,14 +23835,14 @@ PartyResult Player::CanUninviteFromGroup() const
if (!sLFGMgr->GetKicksLeft(gguid))
return ERR_PARTY_LFG_BOOT_LIMIT;
- LfgState state = sLFGMgr->GetState(gguid);
- if (state == LFG_STATE_BOOT)
+ lfg::LfgState state = sLFGMgr->GetState(gguid);
+ if (state == lfg::LFG_STATE_BOOT)
return ERR_PARTY_LFG_BOOT_IN_PROGRESS;
- if (grp->GetMembersCount() <= LFG_GROUP_KICK_VOTES_NEEDED)
+ if (grp->GetMembersCount() <= lfg::LFG_GROUP_KICK_VOTES_NEEDED)
return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS;
- if (state == LFG_STATE_FINISHED_DUNGEON)
+ if (state == lfg::LFG_STATE_FINISHED_DUNGEON)
return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE;
if (grp->isRollLootActive())
@@ -23848,21 +23872,17 @@ PartyResult Player::CanUninviteFromGroup() const
bool Player::isUsingLfg()
{
- return sLFGMgr->GetState(GetGUID()) != LFG_STATE_NONE;
+ return sLFGMgr->GetState(GetGUID()) != lfg::LFG_STATE_NONE;
}
bool Player::inRandomLfgDungeon()
{
- if (isUsingLfg())
+ if (sLFGMgr->selectedRandomLfgDungeon(GetGUID()))
{
- const LfgDungeonSet& dungeons = sLFGMgr->GetSelectedDungeons(GetGUID());
- if (!dungeons.empty())
- {
- LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(*dungeons.begin());
- if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal))
- return true;
- }
+ Map const* map = GetMap();
+ return sLFGMgr->inLfgDungeonMap(GetGUID(), map->GetId(), map->GetDifficulty());
}
+
return false;
}
@@ -25934,8 +25954,6 @@ void Player::DeleteRefundReference(uint32 it)
void Player::SendRefundInfo(Item* item)
{
- const CurrencyTypesEntry* currencyType;
- uint32 divisor;
// This function call unsets ITEM_FLAGS_REFUNDABLE if played time is over 2 hours.
item->UpdatePlayedTime(this);
@@ -25970,6 +25988,7 @@ void Player::SendRefundInfo(Item* item)
data.WriteBit(guid[0]);
data.WriteBit(guid[1]);
data.FlushBits();
+
data.WriteByteSeq(guid[7]);
data << uint32(GetTotalPlayedTime() - item->GetPlayedTime());
for (uint8 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; ++i) // item cost data
@@ -25984,12 +26003,10 @@ void Player::SendRefundInfo(Item* item)
data.WriteByteSeq(guid[2]);
for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i) // currency cost data
{
- currencyType = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
- if (currencyType && currencyType->Flags & CURRENCY_FLAG_HIGH_PRECISION)
- divisor = 100;
- else
- divisor = 1;
- data << uint32(iece->RequiredCurrencyCount[i] / divisor);
+ CurrencyTypesEntry const* currencyType = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
+ uint32 precision = (currencyType && currencyType->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
+
+ data << uint32(iece->RequiredCurrencyCount[i] / precision);
data << uint32(iece->RequiredCurrency[i]);
}
@@ -26028,8 +26045,6 @@ void Player::SendItemRefundResult(Item* item, ItemExtendedCostEntry const* iece,
{
ObjectGuid guid = item->GetGUID();
WorldPacket data(SMSG_ITEM_REFUND_RESULT, 1 + 1 + 8 + 4*8 + 4 + 4*8 + 1);
- const CurrencyTypesEntry* currencyType;
- uint32 divisor;
data.WriteBit(guid[4]);
data.WriteBit(guid[5]);
data.WriteBit(guid[1]);
@@ -26045,12 +26060,10 @@ void Player::SendItemRefundResult(Item* item, ItemExtendedCostEntry const* iece,
{
for (uint8 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
{
- currencyType = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
- if (currencyType && currencyType->Flags & CURRENCY_FLAG_HIGH_PRECISION)
- divisor = 100;
- else
- divisor = 1;
- data << uint32(iece->RequiredCurrencyCount[i] / divisor);
+ CurrencyTypesEntry const* currencyType = sCurrencyTypesStore.LookupEntry(iece->RequiredCurrency[i]);
+ uint32 precision = (currencyType && currencyType->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
+
+ data << uint32(iece->RequiredCurrencyCount[i] / precision);
data << uint32(iece->RequiredCurrency[i]);
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index b27d66a7654..9034c26d4f1 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -531,53 +531,8 @@ enum PlayerFlags
PLAYER_FLAGS_UNK31 = 0x80000000
};
-// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
-// can't use enum for uint64 values
-#define PLAYER_TITLE_DISABLED UI64LIT(0x0000000000000000)
-#define PLAYER_TITLE_NONE UI64LIT(0x0000000000000001)
-#define PLAYER_TITLE_PRIVATE UI64LIT(0x0000000000000002) // 1
-#define PLAYER_TITLE_CORPORAL UI64LIT(0x0000000000000004) // 2
-#define PLAYER_TITLE_SERGEANT_A UI64LIT(0x0000000000000008) // 3
-#define PLAYER_TITLE_MASTER_SERGEANT UI64LIT(0x0000000000000010) // 4
-#define PLAYER_TITLE_SERGEANT_MAJOR UI64LIT(0x0000000000000020) // 5
-#define PLAYER_TITLE_KNIGHT UI64LIT(0x0000000000000040) // 6
-#define PLAYER_TITLE_KNIGHT_LIEUTENANT UI64LIT(0x0000000000000080) // 7
-#define PLAYER_TITLE_KNIGHT_CAPTAIN UI64LIT(0x0000000000000100) // 8
-#define PLAYER_TITLE_KNIGHT_CHAMPION UI64LIT(0x0000000000000200) // 9
-#define PLAYER_TITLE_LIEUTENANT_COMMANDER UI64LIT(0x0000000000000400) // 10
-#define PLAYER_TITLE_COMMANDER UI64LIT(0x0000000000000800) // 11
-#define PLAYER_TITLE_MARSHAL UI64LIT(0x0000000000001000) // 12
-#define PLAYER_TITLE_FIELD_MARSHAL UI64LIT(0x0000000000002000) // 13
-#define PLAYER_TITLE_GRAND_MARSHAL UI64LIT(0x0000000000004000) // 14
-#define PLAYER_TITLE_SCOUT UI64LIT(0x0000000000008000) // 15
-#define PLAYER_TITLE_GRUNT UI64LIT(0x0000000000010000) // 16
-#define PLAYER_TITLE_SERGEANT_H UI64LIT(0x0000000000020000) // 17
-#define PLAYER_TITLE_SENIOR_SERGEANT UI64LIT(0x0000000000040000) // 18
-#define PLAYER_TITLE_FIRST_SERGEANT UI64LIT(0x0000000000080000) // 19
-#define PLAYER_TITLE_STONE_GUARD UI64LIT(0x0000000000100000) // 20
-#define PLAYER_TITLE_BLOOD_GUARD UI64LIT(0x0000000000200000) // 21
-#define PLAYER_TITLE_LEGIONNAIRE UI64LIT(0x0000000000400000) // 22
-#define PLAYER_TITLE_CENTURION UI64LIT(0x0000000000800000) // 23
-#define PLAYER_TITLE_CHAMPION UI64LIT(0x0000000001000000) // 24
-#define PLAYER_TITLE_LIEUTENANT_GENERAL UI64LIT(0x0000000002000000) // 25
-#define PLAYER_TITLE_GENERAL UI64LIT(0x0000000004000000) // 26
-#define PLAYER_TITLE_WARLORD UI64LIT(0x0000000008000000) // 27
-#define PLAYER_TITLE_HIGH_WARLORD UI64LIT(0x0000000010000000) // 28
-#define PLAYER_TITLE_GLADIATOR UI64LIT(0x0000000020000000) // 29
-#define PLAYER_TITLE_DUELIST UI64LIT(0x0000000040000000) // 30
-#define PLAYER_TITLE_RIVAL UI64LIT(0x0000000080000000) // 31
-#define PLAYER_TITLE_CHALLENGER UI64LIT(0x0000000100000000) // 32
-#define PLAYER_TITLE_SCARAB_LORD UI64LIT(0x0000000200000000) // 33
-#define PLAYER_TITLE_CONQUEROR UI64LIT(0x0000000400000000) // 34
-#define PLAYER_TITLE_JUSTICAR UI64LIT(0x0000000800000000) // 35
-#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU UI64LIT(0x0000001000000000) // 36
-#define PLAYER_TITLE_MERCILESS_GLADIATOR UI64LIT(0x0000002000000000) // 37
-#define PLAYER_TITLE_OF_THE_SHATTERED_SUN UI64LIT(0x0000004000000000) // 38
-#define PLAYER_TITLE_HAND_OF_ADAL UI64LIT(0x0000008000000000) // 39
-#define PLAYER_TITLE_VENGEFUL_GLADIATOR UI64LIT(0x0000010000000000) // 40
-
-#define KNOWN_TITLES_SIZE 3
-#define MAX_TITLE_INDEX (KNOWN_TITLES_SIZE*64) // 3 uint64 fields
+#define KNOWN_TITLES_SIZE 4
+#define MAX_TITLE_INDEX (KNOWN_TITLES_SIZE * 64) // 4 uint64 fields
// used in PLAYER_FIELD_BYTES values
enum PlayerFieldByteFlags
@@ -1476,9 +1431,9 @@ class Player : public Unit, public GridObject<Player>
/// send conquest currency points and their cap week/arena
void SendPvpRewards() const;
/// return count of currency witch has plr
- uint32 GetCurrency(uint32 id, bool precision) const;
+ uint32 GetCurrency(uint32 id, bool usePrecision) const;
/// return count of currency gaind on current week
- uint32 GetCurrencyOnWeek(uint32 id, bool precision) const;
+ uint32 GetCurrencyOnWeek(uint32 id, bool usePrecision) const;
/// return week cap by currency id
uint32 GetCurrencyWeekCap(uint32 id, bool usePrecision) const;
/// return presence related currency
@@ -1776,7 +1731,7 @@ class Player : public Unit, public GridObject<Player>
void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
uint64 GetMoney() const { return GetUInt64Value(PLAYER_FIELD_COINAGE); }
- void ModifyMoney(int64 d);
+ bool ModifyMoney(int64 amount, bool sendError = true);
bool HasEnoughMoney(uint64 amount) const { return GetMoney() >= amount; }
bool HasEnoughMoney(int64 amount) const
{
@@ -2094,7 +2049,7 @@ class Player : public Unit, public GridObject<Player>
uint32 GetArenaPersonalRating(uint8 slot) const { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * ARENA_TEAM_END) + ARENA_TEAM_PERSONAL_RATING); }
void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
- uint32 GetRBGPersonalRating() const;
+ uint32 GetRBGPersonalRating() const { return 0; }
Difficulty GetDifficulty(bool isRaid) const { return isRaid ? m_raidDifficulty : m_dungeonDifficulty; }
Difficulty GetDungeonDifficulty() const { return m_dungeonDifficulty; }
@@ -2469,11 +2424,15 @@ class Player : public Unit, public GridObject<Player>
WorldLocation const& GetBattlegroundEntryPoint() const { return m_bgData.joinPos; }
void SetBattlegroundEntryPoint();
- void SetBGTeam(uint32 team) { m_bgData.bgTeam = team; }
+ void SetBGTeam(uint32 team)
+ {
+ m_bgData.bgTeam = team;
+ SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0));
+ }
uint32 GetBGTeam() const { return m_bgData.bgTeam ? m_bgData.bgTeam : GetTeam(); }
void LeaveBattleground(bool teleportToEntryPoint = true);
- bool CanJoinToBattleground() const;
+ bool CanJoinToBattleground(Battleground const* bg) const;
bool CanReportAfkDueToLimit();
void ReportedAfkBy(Player* reporter);
void ClearAfkReports() { m_bgData.bgAfkReporter.clear(); }
@@ -2928,20 +2887,23 @@ class Player : public Unit, public GridObject<Player>
PlayerCurrenciesMap _currencyStorage;
/**
- * @name _GetCurrencyWeekCap
+ * @name GetCurrencyWeekCap
* @brief return week cap for selected currency
- * @param currency CurrencyTypesEntry witch should get week cap
+ * @param CurrencyTypesEntry for which to retrieve weekly cap
*/
- uint32 _GetCurrencyWeekCap(const CurrencyTypesEntry* currency) const;
+ uint32 GetCurrencyWeekCap(CurrencyTypesEntry const* currency) const;
/*
- * @name _GetCurrencyTotalCap
+ * @name GetCurrencyTotalCap
* @brief return total cap for selected currency
- * @param currency CurrencyTypesEntry witch should get week cap
+ * @param CurrencyTypesEntry for which to retrieve total cap
*/
- uint32 _GetCurrencyTotalCap(const CurrencyTypesEntry* currency) const;
+ uint32 GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const;
+
+ /// Updates weekly conquest point cap (dynamic cap)
+ void UpdateConquestCurrencyCap(uint32 currency);
VoidStorageItem* _voidStorageItems[VOID_STORAGE_MAX_SLOT];
@@ -3145,7 +3107,6 @@ class Player : public Unit, public GridObject<Player>
uint32 _activeCheats;
uint32 _maxPersonalArenaRate;
- uint32 _ConquestCurrencyTotalWeekCap;
PhaseMgr phaseMgr;
};
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index ce761db2dcb..9644d4d5a6c 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -680,8 +680,8 @@ uint32 Transport::AddNPCPassenger(uint32 tguid, uint32 entry, float x, float y,
void Transport::UpdatePosition(MovementInfo* mi)
{
float transport_o = mi->pos.GetOrientation() - mi->t_pos.GetOrientation();
- float transport_x = mi->pos.m_positionX - (mi->t_pos.m_positionX * std::cos(transport_o) - mi->t_pos.m_positionY*sin(transport_o));
- float transport_y = mi->pos.m_positionY - (mi->t_pos.m_positionY * std::cos(transport_o) + mi->t_pos.m_positionX*sin(transport_o));
+ float transport_x = mi->pos.m_positionX - (mi->t_pos.m_positionX * std::cos(transport_o) - mi->t_pos.m_positionY * std::sin(transport_o));
+ float transport_y = mi->pos.m_positionY - (mi->t_pos.m_positionY * std::cos(transport_o) + mi->t_pos.m_positionX * std::sin(transport_o));
float transport_z = mi->pos.m_positionZ - mi->t_pos.m_positionZ;
Relocate(transport_x, transport_y, transport_z, transport_o);
@@ -715,7 +715,7 @@ void Transport::CalculatePassengerPosition(float& x, float& y, float& z, float&
void Transport::CalculatePassengerOffset(float& x, float& y, float& z, float& o)
{
- o = o - GetOrientation();
+ o -= GetOrientation();
z -= GetPositionZ();
y -= GetPositionY(); // y = searchedY * std::cos(o) + searchedX * std::sin(o)
x -= GetPositionX(); // x = searchedX * std::cos(o) + searchedY * std::sin(o + pi)
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index faf6c5dcf0b..f7bc6a99fb1 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -256,8 +256,8 @@ Unit::Unit(bool isWorldObject): WorldObject(isWorldObject)
m_speed_rate[i] = 1.0f;
m_charmInfo = NULL;
- m_reducedThreatPercent = 0;
- m_misdirectionTargetGUID = 0;
+
+ _redirectThreadInfo = RedirectThreatInfo();
// remove aurastates allowing special moves
for (uint8 i = 0; i < MAX_REACTIVE; ++i)
@@ -385,10 +385,10 @@ bool Unit::haveOffhandWeapon() const
return m_canDualWield;
}
-void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
+void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
{
- Movement::MoveSplineInit init(*this);
- init.MoveTo(x, y, z);
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(x, y, z, generatePath, forceDestination);
init.SetVelocity(speed);
init.Launch();
}
@@ -4776,106 +4776,6 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType
SendAttackStateUpdate(&dmgInfo);
}
-bool Unit::HandleHasteAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
-{
- SpellInfo const* hasteSpell = triggeredByAura->GetSpellInfo();
-
- Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER
- ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
- Unit* target = victim;
- int32 basepoints0 = 0;
-
- switch (hasteSpell->SpellFamilyName)
- {
- case SPELLFAMILY_ROGUE:
- {
- switch (hasteSpell->Id)
- {
- // Blade Flurry
- case 13877:
- case 33735:
- {
- target = SelectNearbyTarget(victim);
- if (!target)
- return false;
- basepoints0 = damage;
- triggered_spell_id = 22482;
- break;
- }
- }
- break;
- }
- }
-
- // processed charge only counting case
- if (!triggered_spell_id)
- return true;
-
- SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
-
- if (!triggerEntry)
- {
- sLog->outError(LOG_FILTER_UNITS, "Unit::HandleHasteAuraProc: Spell %u has non-existing triggered spell %u", hasteSpell->Id, triggered_spell_id);
- return false;
- }
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id))
- return false;
-
- if (basepoints0)
- CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura);
- else
- CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown);
-
- return true;
-}
-
-bool Unit::HandleSpellCritChanceAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
-{
- SpellInfo const* triggeredByAuraSpell = triggeredByAura->GetSpellInfo();
-
- Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER
- ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
- Unit* target = victim;
- int32 basepoints0 = 0;
-
- // processed charge only counting case
- if (!triggered_spell_id)
- return true;
-
- SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
-
- if (!triggerEntry)
- {
- sLog->outError(LOG_FILTER_UNITS, "Unit::HandleSpellCritChanceAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
- return false;
- }
-
- // default case
- if (!target || (target != this && !target->isAlive()))
- return false;
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id))
- return false;
-
- if (basepoints0)
- CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura);
- else
- CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown);
-
- return true;
-}
-
bool Unit::HandleAuraProcOnPowerAmount(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 /*procEx*/, uint32 cooldown)
{
// Get triggered aura spell info
@@ -5020,38 +4920,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
{
switch (dummySpell->Id)
{
- // Bloodworms Health Leech
- case 50453:
- {
- if (Unit* owner = GetOwner())
- {
- basepoints0 = int32(damage * 1.50f);
- target = owner;
- triggered_spell_id = 50454;
- break;
- }
- return false;
- }
- // Eye for an Eye
- case 9799:
- case 25988:
- {
- // return damage % to attacker but < 50% own total health
- basepoints0 = int32(std::min(CalculatePct(damage, triggerAmount), CountPctFromMaxHealth(50)));
- triggered_spell_id = 25997;
- break;
- }
- // Sweeping Strikes
- case 18765:
- case 35429:
- {
- target = SelectNearbyTarget(victim);
- if (!target)
- return false;
-
- triggered_spell_id = 26654;
- break;
- }
// Unstable Power
case 24658:
{
@@ -5068,67 +4936,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
RemoveAuraFromStack(24662);
return true;
}
- // Adaptive Warding (Frostfire Regalia set)
- case 28764:
- {
- if (!procSpell)
- return false;
-
- // find Mage Armor
- if (!GetAuraEffect(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000, 0, 0))
- return false;
-
- switch (GetFirstSchoolInMask(procSpell->GetSchoolMask()))
- {
- case SPELL_SCHOOL_NORMAL:
- case SPELL_SCHOOL_HOLY:
- return false; // ignored
- case SPELL_SCHOOL_FIRE: triggered_spell_id = 28765; break;
- case SPELL_SCHOOL_NATURE: triggered_spell_id = 28768; break;
- case SPELL_SCHOOL_FROST: triggered_spell_id = 28766; break;
- case SPELL_SCHOOL_SHADOW: triggered_spell_id = 28769; break;
- case SPELL_SCHOOL_ARCANE: triggered_spell_id = 28770; break;
- default:
- return false;
- }
-
- target = this;
- break;
- }
- // Obsidian Armor (Justice Bearer`s Pauldrons shoulder)
- case 27539:
- {
- if (!procSpell)
- return false;
-
- switch (GetFirstSchoolInMask(procSpell->GetSchoolMask()))
- {
- case SPELL_SCHOOL_NORMAL:
- return false; // ignore
- case SPELL_SCHOOL_HOLY: triggered_spell_id = 27536; break;
- case SPELL_SCHOOL_FIRE: triggered_spell_id = 27533; break;
- case SPELL_SCHOOL_NATURE: triggered_spell_id = 27538; break;
- case SPELL_SCHOOL_FROST: triggered_spell_id = 27534; break;
- case SPELL_SCHOOL_SHADOW: triggered_spell_id = 27535; break;
- case SPELL_SCHOOL_ARCANE: triggered_spell_id = 27540; break;
- default:
- return false;
- }
-
- target = this;
- break;
- }
- // Mana Leech (Passive) (Priest Pet Aura)
- case 28305:
- {
- // Cast on owner
- target = GetOwner();
- if (!target)
- return false;
-
- triggered_spell_id = 34650;
- break;
- }
// Mark of Malice
case 33493:
{
@@ -5319,14 +5126,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
}
return false;
}
- // Living Seed
- case 48504:
- {
- triggered_spell_id = 48503;
- basepoints0 = triggerAmount;
- target = this;
- break;
- }
// Kill command
case 58914:
{
@@ -5486,50 +5285,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
}
break;
}
- case 71875: // Item - Black Bruise: Necrotic Touch Proc
- case 71877:
- {
- basepoints0 = CalculatePct(int32(damage), triggerAmount);
- triggered_spell_id = 71879;
- break;
- }
- // Item - Shadowmourne Legendary
- case 71903:
- {
- if (!victim || !victim->isAlive() || HasAura(73422)) // cant collect shards while under effect of Chaos Bane buff
- return false;
-
- CastSpell(this, 71905, true, NULL, triggeredByAura);
-
- // this can't be handled in AuraScript because we need to know victim
- Aura const* dummy = GetAura(71905);
- if (!dummy || dummy->GetStackAmount() < 10)
- return false;
-
- RemoveAurasDueToSpell(71905);
- triggered_spell_id = 71904;
- target = victim;
- break;
- }
- // Shadow's Fate (Shadowmourne questline)
- case 71169:
- {
- Unit* caster = triggeredByAura->GetCaster();
- if (caster && caster->GetTypeId() == TYPEID_PLAYER && caster->ToPlayer()->GetQuestStatus(24547) == QUEST_STATUS_INCOMPLETE)
- {
- CastSpell(caster, 71203, true);
- return true;
- }
- else
- return false;
- }
- // Essence of the Blood Queen
- case 70871:
- {
- basepoints0 = CalculatePct(int32(damage), triggerAmount);
- CastCustomSpell(70872, SPELLVALUE_BASE_POINT0, basepoints0, this);
- return true;
- }
case 65032: // Boom aura (321 Boombot)
{
if (victim->GetEntry() != 33343) // Scrapbot
@@ -5542,13 +5297,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
instance->DoCastSpellOnPlayers(65037); // Achievement criteria marker
break;
}
- // Dark Hunger (The Lich King encounter)
- case 69383:
- {
- basepoints0 = CalculatePct(int32(damage), 50);
- triggered_spell_id = 69384;
- break;
- }
case 47020: // Enter vehicle XT-002 (Scrapbot)
{
if (GetTypeId() != TYPEID_UNIT)
@@ -5579,22 +5327,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
triggered_spell_id = 29442;
break;
}
- // Master of Elements
- if (dummySpell->SpellIconID == 1920)
- {
- if (!procSpell)
- return false;
-
- // mana cost save
- int32 cost = int32(procSpell->ManaCost + CalculatePct(GetCreateMana(), procSpell->ManaCostPercentage));
- basepoints0 = CalculatePct(cost, triggerAmount);
- if (basepoints0 <= 0)
- return false;
-
- target = this;
- triggered_spell_id = 29077;
- break;
- }
// Arcane Potency
if (dummySpell->SpellIconID == 2120)
{
@@ -5612,7 +5344,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
}
break;
}
-
// Hot Streak & Improved Hot Streak
if (dummySpell->SpellIconID == 2999)
{
@@ -5664,16 +5395,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED);
return true;
}
- // Ignite
- case 11119:
- case 11120:
- case 12846:
- {
- basepoints0 = CalculatePct(damage, triggerAmount);
- triggered_spell_id = 12654;
- basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE);
- break;
- }
// Glyph of Ice Block
case 56372:
{
@@ -5685,25 +5406,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
player->RemoveSpellCooldown(122, true);
break;
}
- // Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings)
- case 64411:
- {
- if (!victim)
- return false;
- basepoints0 = int32(CalculatePct(damage, 15));
- if (AuraEffect* aurEff = victim->GetAuraEffect(64413, 0, GetGUID()))
- {
- // The shield can grow to a maximum size of 20, 000 damage absorbtion
- aurEff->SetAmount(std::min<int32>(aurEff->GetAmount() + basepoints0, 20000));
-
- // Refresh and return to prevent replacing the aura
- aurEff->GetBase()->RefreshDuration();
- return true;
- }
- target = victim;
- triggered_spell_id = 64413;
- break;
- }
// Permafrost
case 11175:
case 12569:
@@ -5725,16 +5427,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
{
switch (dummySpell->Id)
{
- // Sweeping Strikes
- case 12328:
- {
- target = SelectNearbyTarget(victim);
- if (!target)
- return false;
-
- triggered_spell_id = 26654;
- break;
- }
// Victorious
case 32216:
{
@@ -5840,16 +5532,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage);
return true;
}
- // Fel Synergy
- if (dummySpell->SpellIconID == 3222)
- {
- target = GetGuardianPet();
- if (!target)
- return false;
- basepoints0 = CalculatePct(int32(damage), triggerAmount);
- triggered_spell_id = 54181;
- break;
- }
switch (dummySpell->Id)
{
// Glyph of Shadowflame
@@ -5935,24 +5617,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
victim->CastSpell(victim, 57669, true, castItem, triggeredByAura);
return true; // no hidden cooldown
}
- // Divine Aegis
- if (dummySpell->SpellIconID == 2820)
- {
- if (!target)
- return false;
-
- // Multiple effects stack, so let's try to find this aura.
- int32 bonus = 0;
- if (AuraEffect const* aurEff = target->GetAuraEffect(47753, 0))
- bonus = aurEff->GetAmount();
-
- basepoints0 = CalculatePct(int32(damage), triggerAmount) + bonus;
- if (basepoints0 > target->getLevel() * 125)
- basepoints0 = target->getLevel() * 125;
-
- triggered_spell_id = 47753;
- break;
- }
// Body and Soul
if (dummySpell->SpellIconID == 2218)
{
@@ -5992,19 +5656,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
target = this;
break;
}
- // Glyph of Prayer of Healing
- case 55680:
- {
- triggered_spell_id = 56161;
-
- SpellInfo const* GoPoH = sSpellMgr->GetSpellInfo(triggered_spell_id);
- if (!GoPoH)
- return false;
-
- int32 tickcount = GoPoH->GetMaxDuration() / GoPoH->Effects[EFFECT_0].Amplitude;
- basepoints0 = CalculatePct(int32(damage), triggerAmount) / tickcount;
- break;
- }
// Phantasm
case 47569:
case 47570:
@@ -6228,13 +5879,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return true;
}
}
- // Living Seed
- if (dummySpell->SpellIconID == 2860)
- {
- triggered_spell_id = 48504;
- basepoints0 = CalculatePct(int32(damage), triggerAmount);
- break;
- }
break;
}
case SPELLFAMILY_ROGUE:
@@ -6250,16 +5894,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
triggered_spell_id = 32747;
break;
}
- case 57934: // Tricks of the Trade
- {
- Unit* redirectTarget = GetMisdirectionTarget();
- RemoveAura(57934);
- if (!redirectTarget)
- break;
- CastSpell(this, 59628, true);
- CastSpell(redirectTarget, 57933, true);
- break;
- }
}
switch (dummySpell->SpellIconID)
@@ -6321,34 +5955,10 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
break;
}
}
-
- switch (dummySpell->Id)
- {
- case 34477: // Misdirection
- {
- if (!GetMisdirectionTarget())
- return false;
- triggered_spell_id = 35079; // 4 sec buff on self
- target = this;
- break;
- }
- }
break;
}
case SPELLFAMILY_PALADIN:
{
- // Seal of Righteousness - melee proc dummy (addition (MWS * (0.011 * AP.022 * holy spell power) * 100 / 100) damage)
- if (dummySpell->SpellFamilyFlags[0] & 0x8000000)
- {
- if (effIndex != 0)
- return false;
- triggered_spell_id = 25742;
- float ap = GetTotalAttackPowerValue(BASE_ATTACK);
- int32 holy = SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_HOLY) +
- victim->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_HOLY);
- basepoints0 = (int32)GetAttackTime(BASE_ATTACK) * int32(ap * 0.011f + 0.022f * holy) / 1000;
- break;
- }
// Light's Beacon - Beacon of Light
if (dummySpell->Id == 53651)
{
@@ -6472,7 +6082,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
}
break;
}
- // Seal of Truth (damage calc on apply aura)
case 31801:
{
if (effIndex != 0) // effect 2 used by seal unleashing code
@@ -6875,20 +6484,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
target = this;
break;
}
- // Earth Shield
- if (dummySpell->Id == 974)
- {
- // 3.0.8: Now correctly uses the Shaman's own spell critical strike chance to determine the chance of a critical heal.
- originalCaster = triggeredByAura->GetCasterGUID();
- target = this;
- basepoints0 = triggerAmount;
-
- // Glyph of Earth Shield
- if (AuraEffect* aur = GetAuraEffect(63279, 0))
- AddPct(basepoints0, aur->GetAmount());
- triggered_spell_id = 379;
- break;
- }
// Flametongue Weapon (Passive)
if (dummySpell->SpellFamilyFlags[0] & 0x200000)
{
@@ -7143,88 +6738,9 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return true;
}
-bool Unit::HandleObsModEnergyAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
-{
- SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
- //uint32 effIndex = triggeredByAura->GetEffIndex();
- //int32 triggerAmount = triggeredByAura->GetAmount();
-
- Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER
- ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
- Unit* target = victim;
- int32 basepoints0 = 0;
-
- // processed charge only counting case
- if (!triggered_spell_id)
- return true;
-
- SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
-
- // Try handle unknown trigger spells
- if (!triggerEntry)
- {
- sLog->outError(LOG_FILTER_UNITS, "Unit::HandleObsModEnergyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
- return false;
- }
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id))
- return false;
- if (basepoints0)
- CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura);
- else
- CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown);
- return true;
-}
-bool Unit::HandleModDamagePctTakenAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
-{
- SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
- //uint32 effIndex = triggeredByAura->GetEffIndex();
- //int32 triggerAmount = triggeredByAura->GetAmount();
-
- Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER
- ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
- Unit* target = victim;
- int32 basepoints0 = 0;
-
/*
- switch (dummySpell->SpellFamilyName)
- {
-
- }
*/
- // processed charge only counting case
- if (!triggered_spell_id)
- return true;
-
- SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
-
- if (!triggerEntry)
- {
- sLog->outError(LOG_FILTER_UNITS, "Unit::HandleModDamagePctTakenAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
- return false;
- }
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id))
- return false;
-
- if (basepoints0)
- CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura);
- else
- CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);
-
- if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown);
-
- return true;
-}
// Used in case when access to whole aura is needed
// All procs should be handled like this...
@@ -9708,7 +9224,7 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask)
DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
// Check if we are ever using mana - PaperDollFrame.lua
- if (GetPowerIndexByClass(POWER_MANA, getClass()) != MAX_POWERS)
+ if (GetPowerIndex(POWER_MANA) != MAX_POWERS)
DoneAdvertisedBenefit += std::max(0, int32(GetStat(STAT_INTELLECT)) - 10); // spellpower from intellect
// Damage bonus from stats
@@ -10231,7 +9747,7 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask)
AdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
// Check if we are ever using mana - PaperDollFrame.lua
- if (GetPowerIndexByClass(POWER_MANA, getClass()) != MAX_POWERS)
+ if (GetPowerIndex(POWER_MANA) != MAX_POWERS)
AdvertisedBenefit += std::max(0, int32(GetStat(STAT_INTELLECT)) - 10); // spellpower from intellect
// Healing bonus from stats
@@ -12848,7 +12364,7 @@ void Unit::SetMaxHealth(uint32 val)
int32 Unit::GetPower(Powers power) const
{
- uint32 powerIndex = GetPowerIndexByClass(power, getClass());
+ uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS)
return 0;
@@ -12857,7 +12373,7 @@ int32 Unit::GetPower(Powers power) const
int32 Unit::GetMaxPower(Powers power) const
{
- uint32 powerIndex = GetPowerIndexByClass(power, getClass());
+ uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS)
return 0;
@@ -12866,7 +12382,7 @@ int32 Unit::GetMaxPower(Powers power) const
void Unit::SetPower(Powers power, int32 val)
{
- uint32 powerIndex = GetPowerIndexByClass(power, getClass());
+ uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS)
return;
@@ -12905,7 +12421,7 @@ void Unit::SetPower(Powers power, int32 val)
void Unit::SetMaxPower(Powers power, int32 val)
{
- uint32 powerIndex = GetPowerIndexByClass(power, getClass());
+ uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS)
return;
@@ -12932,6 +12448,19 @@ void Unit::SetMaxPower(Powers power, int32 val)
SetPower(power, val);
}
+uint32 Unit::GetPowerIndex(uint32 powerType) const
+{
+ /// This is here because hunter pets are of the warrior class.
+ /// With the current implementation, the core only gives them
+ /// POWER_RAGE, so we enforce the class to hunter so that they
+ /// effectively get focus power.
+ uint32 classId = getClass();
+ if (ToPet() && ToPet()->getPetType() == HUNTER_PET)
+ classId = CLASS_HUNTER;
+
+ return GetPowerIndexByClass(powerType, classId);
+}
+
int32 Unit::GetCreatePowers(Powers power) const
{
switch (power)
@@ -13606,7 +13135,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
SpellInfo const* spellInfo = i->aura->GetSpellInfo();
uint32 Id = i->aura->GetId();
- AuraApplication const* aurApp = i->aura->GetApplicationOfTarget(GetGUID());
+ AuraApplication* aurApp = i->aura->GetApplicationOfTarget(GetGUID());
bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo);
@@ -13660,17 +13189,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
case SPELL_AURA_PROC_TRIGGER_DAMAGE:
{
// target has to be valid
- if (!target)
+ if (!eventInfo.GetProcTarget())
break;
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", triggeredByAura->GetAmount(), spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
- SpellNonMeleeDamage damageInfo(this, target, spellInfo->Id, spellInfo->SchoolMask);
- uint32 newDamage = SpellDamageBonusDone(target, spellInfo, triggeredByAura->GetAmount(), SPELL_DIRECT_DAMAGE);
- newDamage = target->SpellDamageBonusTaken(this, spellInfo, newDamage, SPELL_DIRECT_DAMAGE);
- CalculateSpellDamageTaken(&damageInfo, newDamage, spellInfo);
- DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
- SendSpellNonMeleeDamageLog(&damageInfo);
- DealSpellDamage(&damageInfo, true);
+ triggeredByAura->HandleProcTriggerDamageAuraProc(aurApp, eventInfo); // this function is part of the new proc system
takeCharges = true;
break;
}
@@ -13690,23 +13212,13 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
break;
}
case SPELL_AURA_OBS_MOD_POWER:
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
- if (HandleObsModEnergyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
- takeCharges = true;
- break;
+ case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
- if (HandleModDamagePctTakenAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
- takeCharges = true;
- break;
case SPELL_AURA_MOD_MELEE_HASTE:
case SPELL_AURA_MOD_MELEE_HASTE_3:
- {
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
- if (HandleHasteAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
- takeCharges = true;
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId());
+ takeCharges = true;
break;
- }
case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
{
sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
@@ -13778,11 +13290,6 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
if (triggeredByAura->GetCasterGUID() == target->GetGUID())
takeCharges = true;
break;
- case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s spell crit chance aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
- if (procSpell && HandleSpellCritChanceAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
- takeCharges = true;
- break;
// CC Auras which use their amount amount to drop
// Are there any more auras which need this?
case SPELL_AURA_MOD_CONFUSE:
@@ -13985,9 +13492,10 @@ void Unit::StopMoving()
if (!IsInWorld() || movespline->Finalized())
return;
- // Update position using old spline
- UpdateSplinePosition();
- Movement::MoveSplineInit(*this).Stop();
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
+ init.SetFacing(GetOrientation());
+ init.Launch();
}
void Unit::SendMovementFlagUpdate(bool self /* = false */)
@@ -15039,15 +14547,32 @@ void Unit::SetControlled(bool apply, UnitState state)
{
switch (state)
{
- case UNIT_STATE_STUNNED: if (HasAuraType(SPELL_AURA_MOD_STUN)) return;
- else SetStunned(false); break;
- case UNIT_STATE_ROOT: if (HasAuraType(SPELL_AURA_MOD_ROOT) || GetVehicle()) return;
- else SetRooted(false); break;
- case UNIT_STATE_CONFUSED:if (HasAuraType(SPELL_AURA_MOD_CONFUSE)) return;
- else SetConfused(false); break;
- case UNIT_STATE_FLEEING: if (HasAuraType(SPELL_AURA_MOD_FEAR)) return;
- else SetFeared(false); break;
- default: return;
+ case UNIT_STATE_STUNNED:
+ if (HasAuraType(SPELL_AURA_MOD_STUN))
+ return;
+
+ SetStunned(false);
+ break;
+ case UNIT_STATE_ROOT:
+ if (HasAuraType(SPELL_AURA_MOD_ROOT) || GetVehicle())
+ return;
+
+ SetRooted(false);
+ break;
+ case UNIT_STATE_CONFUSED:
+ if (HasAuraType(SPELL_AURA_MOD_CONFUSE))
+ return;
+
+ SetConfused(false);
+ break;
+ case UNIT_STATE_FLEEING:
+ if (HasAuraType(SPELL_AURA_MOD_FEAR))
+ return;
+
+ SetFeared(false);
+ break;
+ default:
+ return;
}
ClearUnitState(state);
@@ -16659,8 +16184,8 @@ void Unit::_ExitVehicle(Position const* exitPosition)
SendMessageToSet(&data, false);
}
- Movement::MoveSplineInit init(*this);
- init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
init.SetFacing(GetOrientation());
init.SetTransportExit();
init.Launch();
@@ -17669,7 +17194,7 @@ void Unit::SetInFront(Unit const* target)
void Unit::SetFacingTo(float ori)
{
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(ori);
init.Launch();
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 40918dbb611..a8e088b2dd3 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -497,6 +497,7 @@ enum UnitState
UNIT_STATE_FLEEING_MOVE = 0x02000000,
UNIT_STATE_CHASE_MOVE = 0x04000000,
UNIT_STATE_FOLLOW_MOVE = 0x08000000,
+ UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_UNATTACKABLE = (UNIT_STATE_IN_FLIGHT | UNIT_STATE_ONVEHICLE),
// for real move using movegen check and stop (except unstoppable flight)
UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE,
@@ -973,6 +974,28 @@ struct SpellPeriodicAuraLogInfo
uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition);
+struct RedirectThreatInfo
+{
+ RedirectThreatInfo() : _targetGUID(0), _threatPct(0) { }
+ uint64 _targetGUID;
+ uint32 _threatPct;
+
+ uint64 GetTargetGUID() { return _targetGUID; }
+ uint32 GetThreatPct() { return _threatPct; }
+
+ void Set(uint64 guid, uint32 pct)
+ {
+ _targetGUID = guid;
+ _threatPct = pct;
+ }
+
+ void ModifyThreatPct(int32 amount)
+ {
+ amount += _threatPct;
+ _threatPct = uint32(std::max(0, amount));
+ }
+};
+
#define MAX_DECLINED_NAME_CASES 5
struct DeclinedName
@@ -1599,7 +1622,7 @@ class Unit : public WorldObject
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
- void MonsterMoveWithSpeed(float x, float y, float z, float speed);
+ void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
//void SetFacing(float ori, WorldObject* obj = NULL);
//void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
void SendMovementFlagUpdate(bool self = false);
@@ -1854,6 +1877,7 @@ class Unit : public WorldObject
uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); }
void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); }
uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); }
+ uint32 GetPowerIndex(uint32 powerType) const;
int32 GetCreatePowers(Powers power) const;
float GetPosStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_POSSTAT0+stat); }
float GetNegStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_NEGSTAT0+stat); }
@@ -2139,13 +2163,12 @@ class Unit : public WorldObject
uint32 GetModelForForm(ShapeshiftForm form) const;
uint32 GetModelForTotem(PlayerTotemType totemType);
- void SetReducedThreatPercent(uint32 pct, uint64 guid)
- {
- m_reducedThreatPercent = pct;
- m_misdirectionTargetGUID = guid;
- }
- uint32 GetReducedThreatPercent() { return m_reducedThreatPercent; }
- Unit* GetMisdirectionTarget() { return m_misdirectionTargetGUID ? GetUnit(*this, m_misdirectionTargetGUID) : NULL; }
+ // Redirect Threat
+ void SetRedirectThreat(uint64 guid, uint32 pct) { _redirectThreadInfo.Set(guid, pct); }
+ void ResetRedirectThreat() { SetRedirectThreat(0, 0); }
+ void ModifyRedirectThreat(int32 amount) { _redirectThreadInfo.ModifyThreatPct(amount); }
+ uint32 GetRedirectThreatPercent() { return _redirectThreadInfo.GetThreatPct(); }
+ Unit* GetRedirectThreatTarget() { return _redirectThreadInfo.GetTargetGUID() ? GetUnit(*this, _redirectThreadInfo.GetTargetGUID()) : NULL; }
bool IsAIEnabled, NeedChangeAI;
bool CreateVehicleKit(uint32 id, uint32 creatureEntry);
@@ -2298,10 +2321,6 @@ class Unit : public WorldObject
bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent);
bool HandleAuraProcOnPowerAmount(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
- bool HandleHasteAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
- bool HandleSpellCritChanceAuraProc(Unit* victim, uint32 damage, AuraEffect* triggredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
- bool HandleObsModEnergyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
- bool HandleModDamagePctTakenAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled);
bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown);
@@ -2338,8 +2357,7 @@ class Unit : public WorldObject
ComboPointHolderSet m_ComboPointHolders;
- uint32 m_reducedThreatPercent;
- uint64 m_misdirectionTargetGUID;
+ RedirectThreatInfo _redirectThreadInfo;
bool m_cleanupDone; // lock made to not add stuff after cleanup before delete
bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index d81feffa71e..16a90018293 100644..100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -381,7 +381,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
unit->SendClearTarget(); // SMSG_BREAK_TARGET
unit->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures)
// also adds MOVEMENTFLAG_ROOT
- Movement::MoveSplineInit init(*unit);
+ Movement::MoveSplineInit init(unit);
init.DisableTransportPathTransformations();
init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ);
init.SetFacing(0.0f);
@@ -466,7 +466,6 @@ void Vehicle::RelocatePassengers()
float px, py, pz, po;
passenger->m_movementInfo.t_pos.GetPosition(px, py, pz, po);
CalculatePassengerPosition(px, py, pz, po);
-
passenger->UpdatePosition(px, py, pz, po);
}
}
diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h
index 7c4ac090743..d7df1b44b99 100644
--- a/src/server/game/Entities/Vehicle/VehicleDefines.h
+++ b/src/server/game/Entities/Vehicle/VehicleDefines.h
@@ -100,6 +100,8 @@ typedef std::map<int8, VehicleSeat> SeatMap;
class TransportBase
{
public:
+ virtual ~TransportBase() { }
+
/// This method transforms supplied transport offsets into global coordinates
virtual void CalculatePassengerPosition(float& x, float& y, float& z, float& o) = 0;
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index 82bf4702c53..051d76f46ed 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -211,7 +211,6 @@ void GameEventMgr::LoadFromDB()
{
mGameEvent.clear();
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 game events. DB table `game_event` is empty.");
-
return;
}
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 45859290d1f..8597b6098a9 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -268,6 +268,7 @@ ObjectMgr::~ObjectMgr()
itr->second.Clear();
_cacheTrainerSpellStore.clear();
+ _graveyardOrientations.clear();
for (DungeonEncounterContainer::iterator itr =_dungeonEncounterStore.begin(); itr != _dungeonEncounterStore.end(); ++itr)
for (DungeonEncounterList::iterator encounterItr = itr->second.begin(); encounterItr != itr->second.end(); ++encounterItr)
@@ -288,6 +289,34 @@ void ObjectMgr::AddLocaleString(std::string const& s, LocaleConstant locale, Str
}
}
+void ObjectMgr::LoadGraveyardOrientations()
+{
+ uint32 oldMSTime = getMSTime();
+
+ _graveyardOrientations.clear();
+
+ QueryResult result = WorldDatabase.Query("SELECT id, orientation FROM graveyard_orientation");
+
+ if (!result)
+ return;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ if (!sWorldSafeLocsStore.LookupEntry(id))
+ {
+ sLog->outError(LOG_FILTER_SERVER_LOADING, "Graveyard %u referenced in graveyard_orientation doesn't exist.", id);
+ continue;
+ }
+ _graveyardOrientations[id] = fields[1].GetFloat();
+
+ } while (result->NextRow());
+
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu graveyard orientations in %u ms", (unsigned long)_graveyardOrientations.size(), GetMSTimeDiffToNow(oldMSTime));
+}
+
void ObjectMgr::LoadCreatureLocales()
{
uint32 oldMSTime = getMSTime();
@@ -390,15 +419,15 @@ void ObjectMgr::LoadCreatureTemplates()
"modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction_A, faction_H, npcflag, speed_walk, "
// 22 23 24 25 26 27 28 29 30 31 32 33 34
"speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, "
- // 35 36 37 38 39 40 41 42 43 44
- "dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, "
- // 45 46 47 48 49 50 51 52 53 54 55
+ // 35 36 37 38 39 40 41 42 43
+ "dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, "
+ // 44 45 46 47 48 49 50 51 52 53 54
"type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, "
- // 56 57 58 59 60 61 62 63 64 65 66 67 68 69
+ // 55 56 59 60 61 62 63 64 65 66 67 68 69 70
"spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
- // 70 71 72 73 74 75 76 77 78 79 80 81
+ // 71 72 73 74 75 76 77 78 79 80 81 82
"InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, "
- // 82 83 84 85 86 87 88
+ // 83 84 85 86 87 88 89
" questItem6, movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName "
"FROM creature_template;");
@@ -459,48 +488,47 @@ void ObjectMgr::LoadCreatureTemplates()
creatureTemplate.dynamicflags = fields[35].GetUInt32();
creatureTemplate.family = uint32(fields[36].GetUInt8());
creatureTemplate.trainer_type = uint32(fields[37].GetUInt8());
- creatureTemplate.trainer_spell = fields[38].GetUInt32();
- creatureTemplate.trainer_class = uint32(fields[39].GetUInt8());
- creatureTemplate.trainer_race = uint32(fields[40].GetUInt8());
- creatureTemplate.minrangedmg = fields[41].GetFloat();
- creatureTemplate.maxrangedmg = fields[42].GetFloat();
- creatureTemplate.rangedattackpower = uint32(fields[43].GetUInt16());
- creatureTemplate.type = uint32(fields[44].GetUInt8());
- creatureTemplate.type_flags = fields[45].GetUInt32();
- creatureTemplate.type_flags2 = fields[46].GetUInt32();
- creatureTemplate.lootid = fields[47].GetUInt32();
- creatureTemplate.pickpocketLootId = fields[48].GetUInt32();
- creatureTemplate.SkinLootId = fields[49].GetUInt32();
+ creatureTemplate.trainer_class = uint32(fields[38].GetUInt8());
+ creatureTemplate.trainer_race = uint32(fields[39].GetUInt8());
+ creatureTemplate.minrangedmg = fields[40].GetFloat();
+ creatureTemplate.maxrangedmg = fields[41].GetFloat();
+ creatureTemplate.rangedattackpower = uint32(fields[42].GetUInt16());
+ creatureTemplate.type = uint32(fields[43].GetUInt8());
+ creatureTemplate.type_flags = fields[44].GetUInt32();
+ creatureTemplate.type_flags2 = fields[45].GetUInt32();
+ creatureTemplate.lootid = fields[46].GetUInt32();
+ creatureTemplate.pickpocketLootId = fields[47].GetUInt32();
+ creatureTemplate.SkinLootId = fields[48].GetUInt32();
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
- creatureTemplate.resistance[i] = fields[50 + i - 1].GetInt16();
+ creatureTemplate.resistance[i] = fields[49 + i - 1].GetInt16();
for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
- creatureTemplate.spells[i] = fields[56 + i].GetUInt32();
-
- creatureTemplate.PetSpellDataId = fields[64].GetUInt32();
- creatureTemplate.VehicleId = fields[65].GetUInt32();
- creatureTemplate.mingold = fields[66].GetUInt32();
- creatureTemplate.maxgold = fields[67].GetUInt32();
- creatureTemplate.AIName = fields[68].GetString();
- creatureTemplate.MovementType = uint32(fields[69].GetUInt8());
- creatureTemplate.InhabitType = uint32(fields[70].GetUInt8());
- creatureTemplate.HoverHeight = fields[71].GetFloat();
- creatureTemplate.ModHealth = fields[72].GetFloat();
- creatureTemplate.ModMana = fields[73].GetFloat();
- creatureTemplate.ModManaExtra = fields[74].GetFloat();
- creatureTemplate.ModArmor = fields[75].GetFloat();
- creatureTemplate.RacialLeader = fields[76].GetBool();
+ creatureTemplate.spells[i] = fields[55 + i].GetUInt32();
+
+ creatureTemplate.PetSpellDataId = fields[63].GetUInt32();
+ creatureTemplate.VehicleId = fields[64].GetUInt32();
+ creatureTemplate.mingold = fields[65].GetUInt32();
+ creatureTemplate.maxgold = fields[66].GetUInt32();
+ creatureTemplate.AIName = fields[67].GetString();
+ creatureTemplate.MovementType = uint32(fields[68].GetUInt8());
+ creatureTemplate.InhabitType = uint32(fields[69].GetUInt8());
+ creatureTemplate.HoverHeight = fields[70].GetFloat();
+ creatureTemplate.ModHealth = fields[71].GetFloat();
+ creatureTemplate.ModMana = fields[72].GetFloat();
+ creatureTemplate.ModManaExtra = fields[73].GetFloat();
+ creatureTemplate.ModArmor = fields[74].GetFloat();
+ creatureTemplate.RacialLeader = fields[75].GetBool();
for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
- creatureTemplate.questItems[i] = fields[77 + i].GetUInt32();
+ creatureTemplate.questItems[i] = fields[76 + i].GetUInt32();
- creatureTemplate.movementId = fields[83].GetUInt32();
- creatureTemplate.RegenHealth = fields[84].GetBool();
- creatureTemplate.equipmentId = fields[85].GetUInt32();
- creatureTemplate.MechanicImmuneMask = fields[86].GetUInt32();
- creatureTemplate.flags_extra = fields[87].GetUInt32();
- creatureTemplate.ScriptID = GetScriptId(fields[88].GetCString());
+ creatureTemplate.movementId = fields[82].GetUInt32();
+ creatureTemplate.RegenHealth = fields[83].GetBool();
+ creatureTemplate.equipmentId = fields[84].GetUInt32();
+ creatureTemplate.MechanicImmuneMask = fields[85].GetUInt32();
+ creatureTemplate.flags_extra = fields[86].GetUInt32();
+ creatureTemplate.ScriptID = GetScriptId(fields[87].GetCString());
++count;
}
@@ -662,12 +690,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
continue;
}
- if (cInfo->trainer_spell != difficultyInfo->trainer_spell)
- {
- sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
- continue;
- }
-
if (!difficultyInfo->AIName.empty())
{
sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists difficulty %u mode entry %u with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.",
@@ -1193,7 +1215,6 @@ void ObjectMgr::LoadLinkedRespawn()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
-
return;
}
@@ -1424,7 +1445,6 @@ void ObjectMgr::LoadCreatures()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creatures. DB table `creature` is empty.");
-
return;
}
@@ -1732,7 +1752,7 @@ void ObjectMgr::LoadGameobjects()
if (!result)
{
- sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
return;
}
@@ -2633,7 +2653,6 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
-
return;
}
@@ -2728,7 +2747,6 @@ void ObjectMgr::LoadPetLevelInfo()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
-
return;
}
@@ -2866,7 +2884,6 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
-
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
exit(1);
}
@@ -3025,7 +3042,6 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 player create spells. DB table `%s` is empty.", sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell");
-
}
else
{
@@ -3087,7 +3103,6 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
-
}
else
{
@@ -3133,7 +3148,6 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level stats definitions. DB table `player_levelstats` is empty.");
-
exit(1);
}
@@ -3246,7 +3260,6 @@ void ObjectMgr::LoadPlayerInfo()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 xp for level definitions. DB table `player_xp_for_level` is empty.");
-
exit(1);
}
@@ -4672,7 +4685,7 @@ void ObjectMgr::LoadWaypointScripts()
for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
actionSet.insert(itr->first);
- PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WOLRD_SEL_WAYPOINT_DATA_ACTION);
+ PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (result)
@@ -4942,7 +4955,6 @@ void ObjectMgr::LoadInstanceEncounters()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 instance encounters, table is empty!");
-
return;
}
@@ -4962,7 +4974,7 @@ void ObjectMgr::LoadInstanceEncounters()
continue;
}
- if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeon(lastEncounterDungeon))
+ if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeonEntry(lastEncounterDungeon))
{
sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName, lastEncounterDungeon);
continue;
@@ -6403,7 +6415,6 @@ void ObjectMgr::LoadExplorationBaseXP()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
-
return;
}
@@ -6558,7 +6569,7 @@ void ObjectMgr::LoadReputationRewardRate()
QueryResult result = WorldDatabase.Query("SELECT faction, quest_rate, quest_daily_rate, quest_weekly_rate, quest_monthly_rate, creature_rate, spell_rate FROM reputation_reward_rate");
if (!result)
{
- sLog->outError(LOG_FILTER_SQL, ">> Loaded `reputation_reward_rate`, table is empty!");
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded `reputation_reward_rate`, table is empty!");
return;
}
@@ -6647,7 +6658,6 @@ void ObjectMgr::LoadReputationOnKill()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
-
return;
}
@@ -6835,7 +6845,6 @@ void ObjectMgr::LoadPointsOfInterest()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
-
return;
}
@@ -6881,7 +6890,6 @@ void ObjectMgr::LoadQuestPOI()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
-
return;
}
@@ -6949,7 +6957,6 @@ void ObjectMgr::LoadNPCSpellClickSpells()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
-
return;
}
@@ -7426,7 +7433,7 @@ bool ObjectMgr::LoadTrinityStrings(const char* table, int32 min_value, int32 max
if (!result)
{
if (min_value == MIN_TRINITY_STRING_ID) // error only in case internal strings
- sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.", table);
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.", table);
else
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 string templates. DB table `%s` is empty.", table);
@@ -7503,7 +7510,6 @@ void ObjectMgr::LoadFishingBaseSkillLevel()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
-
return;
}
@@ -7629,8 +7635,7 @@ void ObjectMgr::LoadGameTele()
if (!result)
{
- sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
-
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
return;
}
@@ -7769,8 +7774,7 @@ void ObjectMgr::LoadMailLevelRewards()
if (!result)
{
- sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
-
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
return;
}
@@ -8040,7 +8044,6 @@ void ObjectMgr::LoadGossipMenu()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gossip_menu entries. DB table `gossip_menu` is empty!");
-
return;
}
@@ -8086,7 +8089,6 @@ void ObjectMgr::LoadGossipMenuItems()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gossip_menu_option entries. DB table `gossip_menu_option` is empty!");
-
return;
}
@@ -8299,8 +8301,7 @@ void ObjectMgr::LoadScriptNames()
if (!result)
{
-
- sLog->outError(LOG_FILTER_SQL, ">> Loaded empty set of Script Names!");
+ sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded empty set of Script Names!");
return;
}
@@ -8477,7 +8478,6 @@ void ObjectMgr::LoadFactionChangeAchievements()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
-
return;
}
@@ -8548,7 +8548,6 @@ void ObjectMgr::LoadFactionChangeSpells()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
-
return;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index eb56bb09b93..8c14f902c1f 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -561,6 +561,7 @@ struct GraveYardData
};
typedef std::multimap<uint32, GraveYardData> GraveYardContainer;
+typedef UNORDERED_MAP<uint32 /* graveyard Id */, float /* orientation */> GraveyardOrientationContainer;
typedef std::pair<GraveYardContainer::const_iterator, GraveYardContainer::const_iterator> GraveYardMapBounds;
typedef std::pair<GraveYardContainer::iterator, GraveYardContainer::iterator> GraveYardMapBoundsNonConst;
@@ -867,6 +868,7 @@ class ObjectMgr
void LoadDbScriptStrings();
void LoadCreatureClassLevelStats();
void LoadCreatureLocales();
+ void LoadGraveyardOrientations();
void LoadCreatureTemplates();
void LoadCreatureTemplateAddons();
void CheckCreatureTemplate(CreatureTemplate const* cInfo);
@@ -1116,6 +1118,16 @@ class ObjectMgr
return &iter->second;
}
+
+ float const* GetGraveyardOrientation(uint32 id) const
+ {
+ GraveyardOrientationContainer::const_iterator iter = _graveyardOrientations.find(id);
+ if (iter != _graveyardOrientations.end())
+ return &iter->second;
+
+ return NULL;
+ }
+
void AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, uint8 type, bool persist = true); // for event
bool RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist = true); // for event
bool IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 ptime, uint32 ExtendedCost, uint8 type, Player* player = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0) const;
@@ -1314,6 +1326,8 @@ class ObjectMgr
CacheVendorItemContainer _cacheVendorItemStore;
CacheTrainerSpellContainer _cacheTrainerSpellStore;
+ GraveyardOrientationContainer _graveyardOrientations;
+
std::set<uint32> _difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate
std::set<uint32> _hasDifficultyEntries[MAX_DIFFICULTY - 1]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index d70d1c824ec..031a8ee96e7 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -1529,7 +1529,7 @@ void Group::SendUpdateToPlayer(uint64 playerGUID, MemberSlot* slot)
data << uint8(slot->roles);
if (isLFGGroup())
{
- data << uint8(sLFGMgr->GetState(m_guid) == LFG_STATE_FINISHED_DUNGEON ? 2 : 0); // FIXME - Dungeon save status? 2 = done
+ data << uint8(sLFGMgr->GetState(m_guid) == lfg::LFG_STATE_FINISHED_DUNGEON ? 2 : 0); // FIXME - Dungeon save status? 2 = done
data << uint32(sLFGMgr->GetDungeon(m_guid));
data << uint8(0); // 4.x new
}
@@ -1866,7 +1866,7 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const*
if (bgOrTemplate->GetTypeID() == BATTLEGROUND_RB && member->InBattlegroundQueue())
return ERR_IN_NON_RANDOM_BG;
// check for deserter debuff in case not arena queue
- if (bgOrTemplate->GetTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground())
+ if (bgOrTemplate->GetTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground(bgOrTemplate))
return ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS;
// check if member can join any more battleground queues
if (!member->HasFreeBattlegroundQueueId())
diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp
index cef21863b60..b112888d1b3 100644
--- a/src/server/game/Guilds/Guild.cpp
+++ b/src/server/game/Guilds/Guild.cpp
@@ -2035,16 +2035,20 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint64 amount, bool
sScriptMgr->OnGuildMemberWitdrawMoney(this, player, amount, repair);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
- // Update remaining money amount
- member->UpdateBankWithdrawValue(trans, GUILD_BANK_MAX_TABS, amount);
- // Remove money from bank
- _ModifyBankMoney(trans, amount, false);
// Add money to player (if required)
if (!repair)
{
- player->ModifyMoney((int64)amount);
+ if (!player->ModifyMoney(amount))
+ return false;
+
player->SaveGoldToDB(trans);
}
+
+ // Update remaining money amount
+ member->UpdateBankWithdrawValue(trans, GUILD_BANK_MAX_TABS, amount);
+ // Remove money from bank
+ _ModifyBankMoney(trans, amount, false);
+
// Log guild bank event
_LogBankEvent(trans, repair ? GUILD_BANK_LOG_REPAIR_MONEY : GUILD_BANK_LOG_WITHDRAW_MONEY, uint8(0), player->GetGUIDLow(), amount);
CharacterDatabase.CommitTransaction(trans);
@@ -2520,7 +2524,7 @@ void Guild::BroadcastAddonToGuild(WorldSession* session, bool officerOnly, std::
if (session && session->GetPlayer() && _HasRankRight(session->GetPlayer(), officerOnly ? GR_RIGHT_OFFCHATSPEAK : GR_RIGHT_GCHATSPEAK))
{
WorldPacket data;
- ChatHandler::FillMessageData(&data, session, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, CHAT_MSG_ADDON, NULL, 0, msg.c_str(), NULL, prefix.c_str());
+ ChatHandler::FillMessageData(&data, session, officerOnly ? CHAT_MSG_OFFICER : CHAT_MSG_GUILD, uint32(CHAT_MSG_ADDON), NULL, 0, msg.c_str(), NULL, prefix.c_str());
for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
if (Player* player = itr->second->FindPlayer())
if (player->GetSession() && _HasRankRight(player, officerOnly ? GR_RIGHT_OFFCHATLISTEN : GR_RIGHT_GCHATLISTEN) &&
diff --git a/src/server/game/Handlers/AddonHandler.cpp b/src/server/game/Handlers/AddonHandler.cpp
index 7e62280f17c..68c93259fcb 100644
--- a/src/server/game/Handlers/AddonHandler.cpp
+++ b/src/server/game/Handlers/AddonHandler.cpp
@@ -30,7 +30,7 @@ AddonHandler::~AddonHandler()
{
}
-bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
+bool AddonHandler::BuildAddonPacket(WorldPacket* source, WorldPacket* target)
{
ByteBuffer AddOnPacked;
uLongf AddonRealSize;
@@ -38,10 +38,10 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
uint32 TempValue;
// broken addon packet, can't be received from real client
- if (Source->rpos() + 4 > Source->size())
+ if (source->rpos() + 4 > source->size())
return false;
- *Source >> TempValue; // get real size of the packed structure
+ *source >> TempValue; // get real size of the packed structure
// empty addon packet, nothing process, can't be received from real client
if (!TempValue)
@@ -49,13 +49,13 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
AddonRealSize = TempValue; // temp value because ZLIB only excepts uLongf
- CurrentPosition = Source->rpos(); // get the position of the pointer in the structure
+ CurrentPosition = source->rpos(); // get the position of the pointer in the structure
AddOnPacked.resize(AddonRealSize); // resize target for zlib action
- if (!uncompress(const_cast<uint8*>(AddOnPacked.contents()), &AddonRealSize, const_cast<uint8*>((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK)
+ if (!uncompress(AddOnPacked.contents(), &AddonRealSize, source->contents() + CurrentPosition, source->size() - CurrentPosition)!= Z_OK)
{
- Target->Initialize(SMSG_ADDON_INFO);
+ target->Initialize(SMSG_ADDON_INFO);
uint32 addonsCount;
AddOnPacked >> addonsCount; // addons count?
@@ -81,14 +81,14 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
sLog->outDebug(LOG_FILTER_NETWORKIO, "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);
+ *target << uint8(state);
uint8 unk1 = (enabled ? 1 : 0);
- *Target << uint8(unk1);
+ *target << uint8(unk1);
if (unk1)
{
uint8 unk = (crc != 0x4c1c776d); // If addon is Standard addon CRC
- *Target << uint8(unk);
+ *target << uint8(unk);
if (unk)
{
unsigned char tdata[256] =
@@ -110,18 +110,18 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
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
};
- Target->append(tdata, sizeof(tdata));
+ target->append(tdata, sizeof(tdata));
}
- *Target << uint32(0);
+ *target << uint32(0);
}
uint8 unk3 = (enabled ? 0 : 1);
- *Target << uint8(unk3);
+ *target << uint8(unk3);
if (unk3)
{
// String, 256 (null terminated?)
- *Target << uint8(0);
+ *target << uint8(0);
}
}
@@ -129,7 +129,7 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target)
AddOnPacked >> unk4;
uint32 count = 0;
- *Target << uint32(count);
+ *target << uint32(count);
if (AddOnPacked.rpos() != AddOnPacked.size())
sLog->outDebug(LOG_FILTER_NETWORKIO, "packet under read!");
diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp
index 71c5da9af2b..9955879d37a 100644
--- a/src/server/game/Handlers/BattleGroundHandler.cpp
+++ b/src/server/game/Handlers/BattleGroundHandler.cpp
@@ -154,7 +154,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData)
}
// check Deserter debuff
- if (!_player->CanJoinToBattleground())
+ if (!_player->CanJoinToBattleground(bg))
{
WorldPacket data;
sBattlegroundMgr->BuildStatusFailedPacket(&data, bg, _player, 0, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS);
@@ -498,7 +498,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData)
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())
+ if (!_player->CanJoinToBattleground(bg))
{
//send bg command result to show nice message
WorldPacket data2;
@@ -540,6 +540,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData)
sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, _player, queueSlot, STATUS_IN_PROGRESS, _player->GetBattlegroundQueueJoinTime(bgTypeId), bg->GetElapsedTime(), 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
@@ -551,6 +552,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData)
_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()
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index 1105886813a..eef049fdb66 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -600,7 +600,7 @@ void WorldSession::HandleAddonMessagechatOpcode(WorldPacket& recvData)
return;
// Weird way to log stuff...
- sScriptMgr->OnPlayerChat(sender, CHAT_MSG_ADDON, LANG_ADDON, message);
+ sScriptMgr->OnPlayerChat(sender, uint32(CHAT_MSG_ADDON), uint32(LANG_ADDON), message);
}
// Disabled addon channel?
@@ -616,7 +616,7 @@ void WorldSession::HandleAddonMessagechatOpcode(WorldPacket& recvData)
return;
WorldPacket data;
- ChatHandler::FillMessageData(&data, this, type, LANG_ADDON, "", 0, message.c_str(), NULL);
+ ChatHandler::FillMessageData(&data, this, type, uint32(LANG_ADDON), "", 0, message.c_str(), NULL);
group->BroadcastAddonMessagePacket(&data, prefix, false);
break;
}
@@ -649,7 +649,7 @@ void WorldSession::HandleAddonMessagechatOpcode(WorldPacket& recvData)
break;
WorldPacket data;
- ChatHandler::FillMessageData(&data, this, type, LANG_ADDON, "", 0, message.c_str(), NULL, prefix.c_str());
+ ChatHandler::FillMessageData(&data, this, type, uint32(LANG_ADDON), "", 0, message.c_str(), NULL, prefix.c_str());
group->BroadcastAddonMessagePacket(&data, prefix, true, -1, group->GetMemberGroup(sender->GetGUID()));
break;
}
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 617270ac119..4887bfee38b 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -816,8 +816,6 @@ void WorldSession::SendListInventory(uint64 vendorGuid)
if (vendorItem->ExtendedCost == 0)
continue; // there's no price defined for currencies, only extendedcost is used
- uint32 precision = (currencyTemplate->Flags & CURRENCY_FLAG_HIGH_PRECISION) ? CURRENCY_PRECISION : 1;
-
++count;
itemsData << uint32(slot + 1); // client expects counting to start at 1
itemsData << uint32(0); // max durability
@@ -838,7 +836,7 @@ void WorldSession::SendListInventory(uint64 vendorGuid)
itemsData << uint32(0); // displayId
// if (!unk "enabler") data << uint32(something);
itemsData << int32(-1);
- itemsData << uint32(vendorItem->maxcount * precision);
+ itemsData << uint32(vendorItem->maxcount);
}
// else error
}
diff --git a/src/server/game/Handlers/LFGHandler.cpp b/src/server/game/Handlers/LFGHandler.cpp
index ef8b39a926d..c2ff0d4b8e0 100644
--- a/src/server/game/Handlers/LFGHandler.cpp
+++ b/src/server/game/Handlers/LFGHandler.cpp
@@ -23,10 +23,10 @@
#include "WorldPacket.h"
#include "WorldSession.h"
-void BuildPlayerLockDungeonBlock(WorldPacket& data, LfgLockMap const& lock)
+void BuildPlayerLockDungeonBlock(WorldPacket& data, lfg::LfgLockMap const& lock)
{
data << uint32(lock.size()); // Size of lock dungeons
- for (LfgLockMap::const_iterator it = lock.begin(); it != lock.end(); ++it)
+ for (lfg::LfgLockMap::const_iterator it = lock.begin(); it != lock.end(); ++it)
{
data << uint32(it->first); // Dungeon entry (id + type)
data << uint32(it->second); // Lock status
@@ -35,10 +35,10 @@ void BuildPlayerLockDungeonBlock(WorldPacket& data, LfgLockMap const& lock)
}
}
-void BuildPartyLockDungeonBlock(WorldPacket& data, const LfgLockPartyMap& lockMap)
+void BuildPartyLockDungeonBlock(WorldPacket& data, lfg::LfgLockPartyMap const& lockMap)
{
data << uint8(lockMap.size());
- for (LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
+ for (lfg::LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
{
data << uint64(it->first); // Player guid
BuildPlayerLockDungeonBlock(data, it->second);
@@ -47,7 +47,7 @@ void BuildPartyLockDungeonBlock(WorldPacket& data, const LfgLockPartyMap& lockMa
void WorldSession::HandleLfgJoinOpcode(WorldPacket& recvData)
{
- if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER) ||
+ if (!sLFGMgr->isOptionEnabled(lfg::LFG_OPTION_ENABLE_DUNGEON_FINDER | lfg::LFG_OPTION_ENABLE_RAID_BROWSER) ||
(GetPlayer()->GetGroup() && GetPlayer()->GetGroup()->GetLeaderGUID() != GetPlayer()->GetGUID() &&
(GetPlayer()->GetGroup()->GetMembersCount() == MAXGROUPSIZE || !GetPlayer()->GetGroup()->isLFGGroup())))
{
@@ -68,7 +68,7 @@ void WorldSession::HandleLfgJoinOpcode(WorldPacket& recvData)
return;
}
- LfgDungeonSet newDungeons;
+ lfg::LfgDungeonSet newDungeons;
for (int8 i = 0; i < numDungeons; ++i)
{
uint32 dungeon;
@@ -169,21 +169,12 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recvData*
GetPlayerInfo().c_str());
// Get Random dungeons that can be done at a certain level and expansion
- LfgDungeonSet randomDungeons;
uint8 level = GetPlayer()->getLevel();
- uint8 expansion = GetPlayer()->GetSession()->Expansion();
-
- LFGDungeonContainer& LfgDungeons = sLFGMgr->GetLFGDungeonMap();
- for (LFGDungeonContainer::const_iterator itr = LfgDungeons.begin(); itr != LfgDungeons.end(); ++itr)
- {
- LFGDungeonData const& dungeon = itr->second;
- if ((dungeon.type == LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id)))
- && dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel)
- randomDungeons.insert(dungeon.Entry());
- }
+ lfg::LfgDungeonSet const& randomDungeons =
+ sLFGMgr->GetRandomAndSeasonalDungeons(level, GetPlayer()->GetSession()->Expansion());
// Get player locked Dungeons
- LfgLockMap const& lock = sLFGMgr->GetLockedDungeons(guid);
+ lfg::LfgLockMap const& lock = sLFGMgr->GetLockedDungeons(guid);
uint32 rsize = uint32(randomDungeons.size());
uint32 lsize = uint32(lock.size());
@@ -191,10 +182,10 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recvData*
WorldPacket data(SMSG_LFG_PLAYER_INFO, 1 + rsize * (4 + 1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4) + 4 + lsize * (1 + 4 + 4 + 4 + 4 + 1 + 4 + 4 + 4));
data << uint8(randomDungeons.size()); // Random Dungeon count
- for (LfgDungeonSet::const_iterator it = randomDungeons.begin(); it != randomDungeons.end(); ++it)
+ for (lfg::LfgDungeonSet::const_iterator it = randomDungeons.begin(); it != randomDungeons.end(); ++it)
{
data << uint32(*it); // Dungeon Entry (id + type)
- LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level);
+ lfg::LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level);
Quest const* quest = NULL;
bool done = false;
if (reward)
@@ -252,7 +243,7 @@ void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recvData*
return;
// Get the locked dungeons of the other party members
- LfgLockPartyMap lockMap;
+ lfg::LfgLockPartyMap lockMap;
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* plrg = itr->getSource();
@@ -267,7 +258,7 @@ void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recvData*
}
uint32 size = 0;
- for (LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
+ for (lfg::LfgLockPartyMap::const_iterator it = lockMap.begin(); it != lockMap.end(); ++it)
size += 8 + 4 + uint32(it->second.size()) * (4 + 4 + 4 + 4);
sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_PARTY_INFO %s", GetPlayerInfo().c_str());
@@ -299,7 +290,7 @@ void WorldSession::HandleLfgGetStatus(WorldPacket& /*recvData*/)
sLog->outDebug(LOG_FILTER_LFG, "CMSG_LFG_GET_STATUS %s", GetPlayerInfo().c_str());
uint64 guid = GetPlayer()->GetGUID();
- LfgUpdateData updateData = sLFGMgr->GetLfgStatus(guid);
+ lfg::LfgUpdateData updateData = sLFGMgr->GetLfgStatus(guid);
if (GetPlayer()->GetGroup())
{
@@ -315,19 +306,19 @@ void WorldSession::HandleLfgGetStatus(WorldPacket& /*recvData*/)
}
}
-void WorldSession::SendLfgUpdatePlayer(LfgUpdateData const& updateData)
+void WorldSession::SendLfgUpdatePlayer(lfg::LfgUpdateData const& updateData)
{
bool queued = false;
uint8 size = uint8(updateData.dungeons.size());
switch (updateData.updateType)
{
- case LFG_UPDATETYPE_JOIN_QUEUE:
- case LFG_UPDATETYPE_ADDED_TO_QUEUE:
+ case lfg::LFG_UPDATETYPE_JOIN_QUEUE:
+ case lfg::LFG_UPDATETYPE_ADDED_TO_QUEUE:
queued = true;
break;
- case LFG_UPDATETYPE_UPDATE_STATUS:
- queued = updateData.state == LFG_STATE_QUEUED;
+ case lfg::LFG_UPDATETYPE_UPDATE_STATUS:
+ queued = updateData.state == lfg::LFG_STATE_QUEUED;
break;
default:
break;
@@ -345,14 +336,14 @@ void WorldSession::SendLfgUpdatePlayer(LfgUpdateData const& updateData)
data << uint8(0); // unk - Always 0
data << uint8(size);
- for (LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
+ for (lfg::LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
data << uint32(*it);
data << updateData.comment;
}
SendPacket(&data);
}
-void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData)
+void WorldSession::SendLfgUpdateParty(const lfg::LfgUpdateData& updateData)
{
bool join = false;
bool queued = false;
@@ -360,15 +351,15 @@ void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData)
switch (updateData.updateType)
{
- case LFG_UPDATETYPE_ADDED_TO_QUEUE: // Rolecheck Success
+ case lfg::LFG_UPDATETYPE_ADDED_TO_QUEUE: // Rolecheck Success
queued = true;
// no break on purpose
- case LFG_UPDATETYPE_PROPOSAL_BEGIN:
+ case lfg::LFG_UPDATETYPE_PROPOSAL_BEGIN:
join = true;
break;
- case LFG_UPDATETYPE_UPDATE_STATUS:
- join = updateData.state != LFG_STATE_ROLECHECK && updateData.state != LFG_STATE_NONE;
- queued = updateData.state == LFG_STATE_QUEUED;
+ case lfg::LFG_UPDATETYPE_UPDATE_STATUS:
+ join = updateData.state != lfg::LFG_STATE_ROLECHECK && updateData.state != lfg::LFG_STATE_NONE;
+ queued = updateData.state == lfg::LFG_STATE_QUEUED;
break;
default:
break;
@@ -389,7 +380,7 @@ void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData)
data << uint8(0); // unk - Always 0
data << uint8(size);
- for (LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
+ for (lfg::LfgDungeonSet::const_iterator it = updateData.dungeons.begin(); it != updateData.dungeons.end(); ++it)
data << uint32(*it);
data << updateData.comment;
}
@@ -408,9 +399,9 @@ void WorldSession::SendLfgRoleChosen(uint64 guid, uint8 roles)
SendPacket(&data);
}
-void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck& roleCheck)
+void WorldSession::SendLfgRoleCheckUpdate(lfg::LfgRoleCheck const& roleCheck)
{
- LfgDungeonSet dungeons;
+ lfg::LfgDungeonSet dungeons;
if (roleCheck.rDungeonId)
dungeons.insert(roleCheck.rDungeonId);
else
@@ -420,16 +411,11 @@ void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck& roleCheck)
WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + roleCheck.roles.size() * (8 + 1 + 4 + 1));
data << uint32(roleCheck.state); // Check result
- data << uint8(roleCheck.state == LFG_ROLECHECK_INITIALITING);
+ data << uint8(roleCheck.state == lfg::LFG_ROLECHECK_INITIALITING);
data << uint8(dungeons.size()); // Number of dungeons
if (!dungeons.empty())
- {
- for (LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it)
- {
- LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(*it);
- data << uint32(dungeon ? dungeon->Entry() : 0); // Dungeon
- }
- }
+ for (lfg::LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it)
+ data << uint32(sLFGMgr->GetLFGDungeonEntry(*it)); // Dungeon
data << uint8(roleCheck.roles.size()); // Players in group
if (!roleCheck.roles.empty())
@@ -443,7 +429,7 @@ void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck& roleCheck)
Player* player = ObjectAccessor::FindPlayer(guid);
data << uint8(player ? player->getLevel() : 0); // Level
- for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it)
+ for (lfg::LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it)
{
if (it->first == roleCheck.leader)
continue;
@@ -460,10 +446,10 @@ void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck& roleCheck)
SendPacket(&data);
}
-void WorldSession::SendLfgJoinResult(LfgJoinResultData const& joinData)
+void WorldSession::SendLfgJoinResult(lfg::LfgJoinResultData const& joinData)
{
uint32 size = 0;
- for (LfgLockPartyMap::const_iterator it = joinData.lockmap.begin(); it != joinData.lockmap.end(); ++it)
+ for (lfg::LfgLockPartyMap::const_iterator it = joinData.lockmap.begin(); it != joinData.lockmap.end(); ++it)
size += 8 + 4 + uint32(it->second.size()) * (4 + 4 + 4 + 4);
sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_JOIN_RESULT %s checkResult: %u checkValue: %u",
@@ -477,7 +463,7 @@ void WorldSession::SendLfgJoinResult(LfgJoinResultData const& joinData)
SendPacket(&data);
}
-void WorldSession::SendLfgQueueStatus(LfgQueueStatusData const& queueData)
+void WorldSession::SendLfgQueueStatus(lfg::LfgQueueStatusData const& queueData)
{
sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_QUEUE_STATUS %s dungeon: %u, waitTime: %d, "
"avgWaitTime: %d, waitTimeTanks: %d, waitTimeHealer: %d, waitTimeDps: %d, "
@@ -500,7 +486,7 @@ void WorldSession::SendLfgQueueStatus(LfgQueueStatusData const& queueData)
SendPacket(&data);
}
-void WorldSession::SendLfgPlayerReward(LfgPlayerRewardData const& rewardData)
+void WorldSession::SendLfgPlayerReward(lfg::LfgPlayerRewardData const& rewardData)
{
if (!rewardData.rdungeonEntry || !rewardData.sdungeonEntry || !rewardData.quest)
return;
@@ -534,43 +520,43 @@ void WorldSession::SendLfgPlayerReward(LfgPlayerRewardData const& rewardData)
SendPacket(&data);
}
-void WorldSession::SendLfgBootProposalUpdate(LfgPlayerBoot const& boot)
+void WorldSession::SendLfgBootProposalUpdate(lfg::LfgPlayerBoot const& boot)
{
uint64 guid = GetPlayer()->GetGUID();
- LfgAnswer playerVote = boot.votes.find(guid)->second;
+ lfg::LfgAnswer playerVote = boot.votes.find(guid)->second;
uint8 votesNum = 0;
uint8 agreeNum = 0;
uint32 secsleft = uint8((boot.cancelTime - time(NULL)) / 1000);
- for (LfgAnswerContainer::const_iterator it = boot.votes.begin(); it != boot.votes.end(); ++it)
+ for (lfg::LfgAnswerContainer::const_iterator it = boot.votes.begin(); it != boot.votes.end(); ++it)
{
- if (it->second != LFG_ANSWER_PENDING)
+ if (it->second != lfg::LFG_ANSWER_PENDING)
{
++votesNum;
- if (it->second == LFG_ANSWER_AGREE)
+ if (it->second == lfg::LFG_ANSWER_AGREE)
++agreeNum;
}
}
sLog->outDebug(LOG_FILTER_LFG, "SMSG_LFG_BOOT_PROPOSAL_UPDATE %s inProgress: %u - "
"didVote: %u - agree: %u - victim: %u votes: %u - agrees: %u - left: %u - "
"needed: %u - reason %s",
- GetPlayerInfo().c_str(), uint8(boot.inProgress), uint8(playerVote != LFG_ANSWER_PENDING),
- uint8(playerVote == LFG_ANSWER_AGREE), GUID_LOPART(boot.victim), votesNum, agreeNum,
- secsleft, LFG_GROUP_KICK_VOTES_NEEDED, boot.reason.c_str());
- WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + boot.reason.length());
+ GetPlayerInfo().c_str(), uint8(boot.inProgress), uint8(playerVote != lfg::LFG_ANSWER_PENDING),
+ uint8(playerVote == lfg::LFG_ANSWER_AGREE), GUID_LOPART(boot.victim), votesNum, agreeNum,
+ secsleft, lfg::LFG_GROUP_KICK_VOTES_NEEDED, boot.reason.c_str());
+ WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + boot.reason.length());
data << uint8(boot.inProgress); // Vote in progress
- data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote
- data << uint8(playerVote == LFG_ANSWER_AGREE); // Agree
+ data << uint8(playerVote != lfg::LFG_ANSWER_PENDING); // Did Vote
+ data << uint8(playerVote == lfg::LFG_ANSWER_AGREE); // Agree
data << uint8(0); // Unknown 4.2.2
data << uint64(boot.victim); // Victim GUID
data << uint32(votesNum); // Total Votes
data << uint32(agreeNum); // Agree Count
data << uint32(secsleft); // Time Left
- data << uint32(LFG_GROUP_KICK_VOTES_NEEDED); // Needed Votes
+ data << uint32(lfg::LFG_GROUP_KICK_VOTES_NEEDED); // Needed Votes
data << boot.reason.c_str(); // Kick reason
SendPacket(&data);
}
-void WorldSession::SendLfgUpdateProposal(LfgProposal const& proposal)
+void WorldSession::SendLfgUpdateProposal(lfg::LfgProposal const& proposal)
{
uint64 guid = GetPlayer()->GetGUID();
uint64 gguid = proposal.players.find(guid)->second.group;
@@ -583,13 +569,12 @@ void WorldSession::SendLfgUpdateProposal(LfgProposal const& proposal)
// show random dungeon if player selected random dungeon and it's not lfg group
if (!silent)
{
- LfgDungeonSet const& playerDungeons = sLFGMgr->GetSelectedDungeons(guid);
+ lfg::LfgDungeonSet const& playerDungeons = sLFGMgr->GetSelectedDungeons(guid);
if (playerDungeons.find(proposal.dungeonId) == playerDungeons.end())
dungeonEntry = (*playerDungeons.begin());
}
- if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonEntry))
- dungeonEntry = dungeon->Entry();
+ dungeonEntry = sLFGMgr->GetLFGDungeonEntry(dungeonEntry);
WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + proposal.players.size() * (4 + 1 + 1 + 1 + 1 +1));
data << uint32(dungeonEntry); // Dungeon
@@ -599,9 +584,9 @@ void WorldSession::SendLfgUpdateProposal(LfgProposal const& proposal)
data << uint8(silent); // Show proposal window
data << uint8(proposal.players.size()); // Group size
- for (LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
+ for (lfg::LfgProposalPlayerContainer::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it)
{
- LfgProposalPlayer const& player = it->second;
+ lfg::LfgProposalPlayer const& player = it->second;
data << uint32(player.role); // Role
data << uint8(it->first == guid); // Self player
if (!player.group) // Player not it a group
@@ -614,8 +599,8 @@ void WorldSession::SendLfgUpdateProposal(LfgProposal const& proposal)
data << uint8(player.group == proposal.group); // In dungeon (silent)
data << uint8(player.group == gguid); // Same Group than player
}
- data << uint8(player.accept != LFG_ANSWER_PENDING);// Answered
- data << uint8(player.accept == LFG_ANSWER_AGREE); // Accepted
+ data << uint8(player.accept != lfg::LFG_ANSWER_PENDING);// Answered
+ data << uint8(player.accept == lfg::LFG_ANSWER_AGREE); // Accepted
}
SendPacket(&data);
}
diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp
index c21d16d3600..48f11c596d2 100644
--- a/src/server/game/Handlers/MailHandler.cpp
+++ b/src/server/game/Handlers/MailHandler.cpp
@@ -570,13 +570,18 @@ void WorldSession::HandleMailTakeMoney(WorldPacket& recvData)
return;
}
- player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
+ if (!player->ModifyMoney(m->money, false))
+ {
+ player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_TOO_MUCH_GOLD);
+ return;
+ }
- player->ModifyMoney(money);
m->money = 0;
m->state = MAIL_STATE_CHANGED;
player->m_mailsUpdated = true;
+ player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
+
// save money and mail to prevent cheating
SQLTransaction trans = CharacterDatabase.BeginTransaction();
player->SaveGoldToDB(trans);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 579b4f38a5c..e08a7a30699 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -396,7 +396,7 @@ void WorldSession::HandleLogoutRequestOpcode(WorldPacket& /*recvData*/)
//instant logout in taverns/cities or on taxi or for admins, gm's, mod's if its enabled in worldserver.conf
if (GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight() ||
- GetSecurity() >= AccountTypes(sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT)))
+ HasPermission(RBAC_PERM_INSTANT_LOGOUT))
{
WorldPacket data(SMSG_LOGOUT_RESPONSE, 1+4);
data << uint32(reason);
@@ -975,7 +975,7 @@ void WorldSession::HandleUpdateAccountData(WorldPacket& recvData)
dest.resize(decompressedSize);
uLongf realSize = decompressedSize;
- if (uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recvData.contents() + recvData.rpos()), recvData.size() - recvData.rpos()) != Z_OK)
+ if (uncompress(dest.contents(), &realSize, recvData.contents() + recvData.rpos(), recvData.size() - recvData.rpos()) != Z_OK)
{
recvData.rfinish(); // unnneded warning spam in this case
sLog->outError(LOG_FILTER_NETWORKIO, "UAD: Failed to decompress account data");
@@ -1016,7 +1016,7 @@ void WorldSession::HandleRequestAccountData(WorldPacket& recvData)
ByteBuffer dest;
dest.resize(destSize);
- if (size && compress(const_cast<uint8*>(dest.contents()), &destSize, (uint8*)adata->Data.c_str(), size) != Z_OK)
+ if (size && compress(dest.contents(), &destSize, (uint8 const*)adata->Data.c_str(), size) != Z_OK)
{
sLog->outDebug(LOG_FILTER_NETWORKIO, "RAD: Failed to compress account data");
return;
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 3d2e84c57e8..b2971abaeba 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -126,7 +126,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
{
// short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
- flight->Initialize(*GetPlayer());
+ flight->Initialize(GetPlayer());
return;
}
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 340b5a3ddc1..f7f540a7be5 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -203,13 +203,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
if (!owner->IsValidAttackTarget(TargetUnit))
return;
- // Not let attack through obstructions
- if (sWorld->getBoolConfig(CONFIG_PET_LOS))
- {
- if (!pet->IsWithinLOSInMap(TargetUnit))
- return;
- }
-
pet->ClearUnitState(UNIT_STATE_FOLLOW);
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp
index 4463613a97b..2da2735a3f9 100644
--- a/src/server/game/Handlers/TicketHandler.cpp
+++ b/src/server/game/Handlers/TicketHandler.cpp
@@ -69,7 +69,7 @@ void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData)
dest.resize(decompressedSize);
uLongf realSize = decompressedSize;
- if (uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recvData.contents() + pos), recvData.size() - pos) == Z_OK)
+ if (uncompress(dest.contents(), &realSize, recvData.contents() + pos, recvData.size() - pos) == Z_OK)
{
dest >> chatLog;
ticket->SetChatLog(times, chatLog);
diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp
index 4545a8df353..b25f0a301a9 100644
--- a/src/server/game/Handlers/TradeHandler.cpp
+++ b/src/server/game/Handlers/TradeHandler.cpp
@@ -353,6 +353,20 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
return;
}
+ if (_player->GetMoney() >= uint64(MAX_MONEY_AMOUNT) - his_trade->GetMoney())
+ {
+ _player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
+ my_trade->SetAccepted(false, true);
+ return;
+ }
+
+ if (trader->GetMoney() >= uint64(MAX_MONEY_AMOUNT) - my_trade->GetMoney())
+ {
+ trader->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
+ his_trade->SetAccepted(false, true);
+ return;
+ }
+
// not accept if some items now can't be trade (cheating)
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
{
@@ -363,6 +377,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
+
if (item->IsBindedNotWith(trader))
{
SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE);
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 0a66c781c8f..94aea40f511 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -26,6 +26,7 @@
#include "SpellInfo.h"
#include "Group.h"
#include "Player.h"
+#include "Containers.h"
static Rates const qualityToRate[MAX_ITEM_QUALITY] =
{
@@ -51,10 +52,26 @@ LootStore LootTemplates_Reference("reference_loot_template", "reference
LootStore LootTemplates_Skinning("skinning_loot_template", "creature skinning id", true);
LootStore LootTemplates_Spell("spell_loot_template", "spell id (random item creating)", false);
+struct NotMatchingLootMode : public std::unary_function<LootStoreItem*, bool>
+{
+ explicit NotMatchingLootMode(uint16 lootMode) : _lootMode(lootMode) { }
+
+ bool operator()(LootStoreItem* item) const
+ {
+ return !(item->lootmode & _lootMode);
+ }
+
+private:
+ uint16 _lootMode;
+};
+
class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
{
public:
- void AddEntry(LootStoreItem& item); // Adds an entry to the group (at loading stage)
+ LootGroup() { }
+ ~LootGroup();
+
+ void AddEntry(LootStoreItem* item); // Adds an entry to the group (at loading stage)
bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry
bool HasQuestDropForPlayer(Player const* player) const;
// The same for active quests of the player
@@ -72,7 +89,11 @@ class LootTemplate::LootGroup // A set of loot def
LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB
LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance
- LootStoreItem const* Roll() const; // Rolls an item from the group, returns NULL if all miss their chances
+ LootStoreItem const* Roll(uint16 lootMode) const; // Rolls an item from the group, returns NULL if all miss their chances
+
+ // This class must never be copied - storing pointers
+ LootGroup(LootGroup const&);
+ LootGroup& operator=(LootGroup const&);
};
//Remove all data and free all memory
@@ -126,10 +147,13 @@ uint32 LootStore::LoadLootTable()
continue; // error already printed to log/console.
}
- LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, lootmode, group, mincountOrRef, maxcount);
+ LootStoreItem* storeitem = new LootStoreItem(item, chanceOrQuestChance, lootmode, group, mincountOrRef, maxcount);
- if (!storeitem.IsValid(*this, entry)) // Validity checks
+ if (!storeitem->IsValid(*this, entry)) // Validity checks
+ {
+ delete storeitem;
continue;
+ }
// Looking for the template of the entry
// often entries are put together
@@ -139,7 +163,7 @@ uint32 LootStore::LoadLootTable()
tab = m_LootTemplates.find(entry);
if (tab == m_LootTemplates.end())
{
- std::pair< LootTemplateMap::iterator, bool > pr = m_LootTemplates.insert(LootTemplateMap::value_type(entry, new LootTemplate));
+ std::pair< LootTemplateMap::iterator, bool > pr = m_LootTemplates.insert(LootTemplateMap::value_type(entry, new LootTemplate()));
tab = pr.first;
}
}
@@ -182,7 +206,7 @@ void LootStore::ResetConditions()
for (LootTemplateMap::iterator itr = m_LootTemplates.begin(); itr != m_LootTemplates.end(); ++itr)
{
ConditionList empty;
- (*itr).second->CopyConditions(empty);
+ itr->second->CopyConditions(empty);
}
}
@@ -327,7 +351,6 @@ LootItem::LootItem(LootStoreItem const& li)
needs_quest = li.needs_quest;
- count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only
randomSuffix = GenerateEnchSuffixFactor(itemid);
randomPropertyId = Item::GenerateItemRandomPropertyId(itemid);
is_looted = 0;
@@ -376,26 +399,30 @@ void LootItem::AddAllowedLooter(const Player* player)
//
// Inserts the item into the loot (called by LootTemplate processors)
-void Loot::AddItem(LootStoreItem const & item)
+void Loot::AddItem(LootStoreItem const& item)
{
- if (item.needs_quest) // Quest drop
- {
- if (quest_items.size() < MAX_NR_QUEST_ITEMS)
- quest_items.push_back(LootItem(item));
- }
- else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop
+ ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid);
+ if (!proto)
+ return;
+
+ uint32 count = urand(item.mincountOrRef, item.maxcount);
+ uint32 stacks = count / proto->GetMaxStackSize() + (count % proto->GetMaxStackSize() ? 1 : 0);
+
+ std::vector<LootItem>& lootItems = item.needs_quest ? quest_items : items;
+ uint32 limit = item.needs_quest ? MAX_NR_QUEST_ITEMS : MAX_NR_LOOT_ITEMS;
+
+ for (uint32 i = 0; i < stacks && lootItems.size() < limit; ++i)
{
- items.push_back(LootItem(item));
+ LootItem generatedLoot(item);
+ generatedLoot.count = std::min(count, proto->GetMaxStackSize());
+ lootItems.push_back(generatedLoot);
+ count -= proto->GetMaxStackSize();
// non-conditional one-player only items are counted here,
// free for all items are counted in FillFFALoot(),
// non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
- if (item.conditions.empty())
- {
- ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid);
- if (!proto || (proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT) == 0)
- ++unlootedCount;
- }
+ if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT))
+ ++unlootedCount;
}
}
@@ -1028,34 +1055,56 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
// --------- LootTemplate::LootGroup ---------
//
+LootTemplate::LootGroup::~LootGroup()
+{
+ while (!ExplicitlyChanced.empty())
+ {
+ delete ExplicitlyChanced.back();
+ ExplicitlyChanced.pop_back();
+ }
+
+ while (!EqualChanced.empty())
+ {
+ delete EqualChanced.back();
+ EqualChanced.pop_back();
+ }
+}
+
// Adds an entry to the group (at loading stage)
-void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
+void LootTemplate::LootGroup::AddEntry(LootStoreItem* item)
{
- if (item.chance != 0)
+ if (item->chance != 0)
ExplicitlyChanced.push_back(item);
else
EqualChanced.push_back(item);
}
// Rolls an item from the group, returns NULL if all miss their chances
-LootStoreItem const* LootTemplate::LootGroup::Roll() const
+LootStoreItem const* LootTemplate::LootGroup::Roll(uint16 lootMode) const
{
- if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked
+ LootStoreItemList possibleLoot = ExplicitlyChanced;
+ possibleLoot.remove_if(NotMatchingLootMode(lootMode));
+
+ if (!possibleLoot.empty()) // First explicitly chanced entries are checked
{
- float Roll = (float)rand_chance();
+ float roll = (float)rand_chance();
- for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i) // check each explicitly chanced entry in the template and modify its chance based on quality.
+ for (LootStoreItemList::const_iterator itr = possibleLoot.begin(); itr != possibleLoot.end(); ++itr) // check each explicitly chanced entry in the template and modify its chance based on quality.
{
- if (ExplicitlyChanced[i].chance >= 100.0f)
- return &ExplicitlyChanced[i];
+ LootStoreItem* item = *itr;
+ if (item->chance >= 100.0f)
+ return item;
- Roll -= ExplicitlyChanced[i].chance;
- if (Roll < 0)
- return &ExplicitlyChanced[i];
+ roll -= item->chance;
+ if (roll < 0)
+ return item;
}
}
- if (!EqualChanced.empty()) // If nothing selected yet - an item is taken from equal-chanced part
- return &EqualChanced[irand(0, EqualChanced.size()-1)];
+
+ possibleLoot = EqualChanced;
+ possibleLoot.remove_if(NotMatchingLootMode(lootMode));
+ if (!possibleLoot.empty()) // If nothing selected yet - an item is taken from equal-chanced part
+ return Trinity::Containers::SelectRandomContainerElement(possibleLoot);
return NULL; // Empty drop from the group
}
@@ -1063,12 +1112,14 @@ LootStoreItem const* LootTemplate::LootGroup::Roll() const
// True if group includes at least 1 quest drop entry
bool LootTemplate::LootGroup::HasQuestDrop() const
{
- for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- if (i->needs_quest)
+ for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
+ if ((*i)->needs_quest)
return true;
- for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
- if (i->needs_quest)
+
+ for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
+ if ((*i)->needs_quest)
return true;
+
return false;
}
@@ -1076,111 +1127,30 @@ bool LootTemplate::LootGroup::HasQuestDrop() const
bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const* player) const
{
for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- if (player->HasQuestForItem(i->itemid))
+ if (player->HasQuestForItem((*i)->itemid))
return true;
+
for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
- if (player->HasQuestForItem(i->itemid))
+ if (player->HasQuestForItem((*i)->itemid))
return true;
+
return false;
}
void LootTemplate::LootGroup::CopyConditions(ConditionList /*conditions*/)
{
for (LootStoreItemList::iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- {
- i->conditions.clear();
- }
+ (*i)->conditions.clear();
+
for (LootStoreItemList::iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
- {
- i->conditions.clear();
- }
+ (*i)->conditions.clear();
}
// Rolls an item from the group (if any takes its chance) and adds the item to the loot
void LootTemplate::LootGroup::Process(Loot& loot, uint16 lootMode) const
{
- // build up list of possible drops
- LootStoreItemList EqualPossibleDrops = EqualChanced;
- LootStoreItemList ExplicitPossibleDrops = ExplicitlyChanced;
-
- uint8 uiAttemptCount = 0;
- const uint8 uiMaxAttempts = ExplicitlyChanced.size() + EqualChanced.size();
-
- while (!ExplicitPossibleDrops.empty() || !EqualPossibleDrops.empty())
- {
- if (uiAttemptCount == uiMaxAttempts) // already tried rolling too many times, just abort
- return;
-
- LootStoreItem* item = NULL;
-
- // begin rolling (normally called via Roll())
- LootStoreItemList::iterator itr;
- uint8 itemSource = 0;
- if (!ExplicitPossibleDrops.empty()) // First explicitly chanced entries are checked
- {
- itemSource = 1;
- float Roll = (float)rand_chance();
- // check each explicitly chanced entry in the template and modify its chance based on quality
- for (itr = ExplicitPossibleDrops.begin(); itr != ExplicitPossibleDrops.end(); itr = ExplicitPossibleDrops.erase(itr))
- {
- if (itr->chance >= 100.0f)
- {
- item = &*itr;
- break;
- }
-
- Roll -= itr->chance;
- if (Roll < 0)
- {
- item = &*itr;
- break;
- }
- }
- }
- if (item == NULL && !EqualPossibleDrops.empty()) // If nothing selected yet - an item is taken from equal-chanced part
- {
- itemSource = 2;
- itr = EqualPossibleDrops.begin();
- std::advance(itr, irand(0, EqualPossibleDrops.size()-1));
- item = &*itr;
- }
- // finish rolling
-
- ++uiAttemptCount;
-
- if (item != NULL && item->lootmode & lootMode) // only add this item if roll succeeds and the mode matches
- {
- bool duplicate = false;
- if (ItemTemplate const* _proto = sObjectMgr->GetItemTemplate(item->itemid))
- {
- uint8 _item_counter = 0;
- for (LootItemList::const_iterator _item = loot.items.begin(); _item != loot.items.end(); ++_item)
- if (_item->itemid == item->itemid) // search through the items that have already dropped
- {
- ++_item_counter;
- if (_proto->InventoryType == 0 && _item_counter == 3) // Non-equippable items are limited to 3 drops
- duplicate = true;
- else if (_proto->InventoryType != 0 && _item_counter == 1) // Equippable item are limited to 1 drop
- duplicate = true;
- }
- }
- if (duplicate) // if item->itemid is a duplicate, remove it
- switch (itemSource)
- {
- case 1: // item came from ExplicitPossibleDrops
- ExplicitPossibleDrops.erase(itr);
- break;
- case 2: // item came from EqualPossibleDrops
- EqualPossibleDrops.erase(itr);
- break;
- }
- else // otherwise, add the item and exit the function
- {
- loot.AddItem(*item);
- return;
- }
- }
- }
+ if (LootStoreItem const* item = Roll(lootMode))
+ loot.AddItem(*item);
}
// Overall chance for the group without equal chanced items
@@ -1189,8 +1159,8 @@ float LootTemplate::LootGroup::RawTotalChance() const
float result = 0;
for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
- if (!i->needs_quest)
- result += i->chance;
+ if (!(*i)->needs_quest)
+ result += (*i)->chance;
return result;
}
@@ -1210,37 +1180,35 @@ void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint
{
float chance = RawTotalChance();
if (chance > 101.0f) // TODO: replace with 100% when DBs will be ready
- {
sLog->outError(LOG_FILTER_SQL, "Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance);
- }
if (chance >= 100.0f && !EqualChanced.empty())
- {
sLog->outError(LOG_FILTER_SQL, "Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)", lootstore.GetName(), id, group_id, chance);
- }
}
void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& /*store*/, LootIdSet* ref_set) const
{
- for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
+ for (LootStoreItemList::const_iterator ieItr = ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
{
- if (ieItr->mincountOrRef < 0)
+ LootStoreItem* item = *ieItr;
+ if (item->mincountOrRef < 0)
{
- if (!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
- LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
+ if (!LootTemplates_Reference.GetLootFor(-item->mincountOrRef))
+ LootTemplates_Reference.ReportNotExistedId(-item->mincountOrRef);
else if (ref_set)
- ref_set->erase(-ieItr->mincountOrRef);
+ ref_set->erase(-item->mincountOrRef);
}
}
- for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
+ for (LootStoreItemList::const_iterator ieItr = EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
{
- if (ieItr->mincountOrRef < 0)
+ LootStoreItem* item = *ieItr;
+ if (item->mincountOrRef < 0)
{
- if (!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
- LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
+ if (!LootTemplates_Reference.GetLootFor(-item->mincountOrRef))
+ LootTemplates_Reference.ReportNotExistedId(-item->mincountOrRef);
else if (ref_set)
- ref_set->erase(-ieItr->mincountOrRef);
+ ref_set->erase(-item->mincountOrRef);
}
}
}
@@ -1249,14 +1217,30 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& /*store*/, Lo
// --------- LootTemplate ---------
//
+LootTemplate::~LootTemplate()
+{
+ while (!Entries.empty())
+ {
+ delete Entries.back();
+ Entries.pop_back();
+ }
+
+ for (size_t i = 0; i < Groups.size(); ++i)
+ delete Groups[i];
+ Groups.clear();
+}
+
// Adds an entry to the group (at loading stage)
-void LootTemplate::AddEntry(LootStoreItem& item)
+void LootTemplate::AddEntry(LootStoreItem* item)
{
- if (item.group > 0 && item.mincountOrRef > 0) // Group
+ if (item->group > 0 && item->mincountOrRef > 0) // Group
{
- if (item.group >= Groups.size())
- Groups.resize(item.group); // Adds new group the the loot template if needed
- Groups[item.group-1].AddEntry(item); // Adds new entry to the group
+ if (item->group >= Groups.size())
+ Groups.resize(item->group, NULL); // Adds new group the the loot template if needed
+ if (!Groups[item->group - 1])
+ Groups[item->group - 1] = new LootGroup();
+
+ Groups[item->group-1]->AddEntry(item); // Adds new entry to the group
}
else // Non-grouped entries and references are stored together
Entries.push_back(item);
@@ -1265,10 +1249,11 @@ void LootTemplate::AddEntry(LootStoreItem& item)
void LootTemplate::CopyConditions(ConditionList conditions)
{
for (LootStoreItemList::iterator i = Entries.begin(); i != Entries.end(); ++i)
- i->conditions.clear();
+ (*i)->conditions.clear();
for (LootGroups::iterator i = Groups.begin(); i != Groups.end(); ++i)
- i->CopyConditions(conditions);
+ if (LootGroup* group = *i)
+ group->CopyConditions(conditions);
}
void LootTemplate::CopyConditions(LootItem* li) const
@@ -1276,10 +1261,11 @@ void LootTemplate::CopyConditions(LootItem* li) const
// Copies the conditions list from a template item to a LootItem
for (LootStoreItemList::const_iterator _iter = Entries.begin(); _iter != Entries.end(); ++_iter)
{
- if (_iter->itemid != li->itemid)
+ LootStoreItem* item = *_iter;
+ if (item->itemid != li->itemid)
continue;
- li->conditions = _iter->conditions;
+ li->conditions = item->conditions;
break;
}
}
@@ -1292,54 +1278,42 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId
if (groupId > Groups.size())
return; // Error message already printed at loading stage
- Groups[groupId-1].Process(loot, lootMode);
+ if (!Groups[groupId - 1])
+ return;
+
+ Groups[groupId-1]->Process(loot, lootMode);
return;
}
// Rolling non-grouped items
for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i)
{
- if (i->lootmode &~ lootMode) // Do not add if mode mismatch
+ LootStoreItem* item = *i;
+ if (item->lootmode &~ lootMode) // Do not add if mode mismatch
continue;
- if (!i->Roll(rate))
- continue; // Bad luck for the entry
+ if (!item->Roll(rate))
+ continue; // Bad luck for the entry
- if (ItemTemplate const* _proto = sObjectMgr->GetItemTemplate(i->itemid))
+ if (item->mincountOrRef < 0) // References processing
{
- uint8 _item_counter = 0;
- LootItemList::const_iterator _item = loot.items.begin();
- for (; _item != loot.items.end(); ++_item)
- if (_item->itemid == i->itemid) // search through the items that have already dropped
- {
- ++_item_counter;
- if (_proto->InventoryType == 0 && _item_counter == 3) // Non-equippable items are limited to 3 drops
- continue;
- else if (_proto->InventoryType != 0 && _item_counter == 1) // Equippable item are limited to 1 drop
- continue;
- }
- if (_item != loot.items.end())
- continue;
- }
-
- if (i->mincountOrRef < 0) // References processing
- {
- LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef);
+ LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-item->mincountOrRef);
if (!Referenced)
- continue; // Error message already printed at loading stage
+ continue; // Error message already printed at loading stage
- uint32 maxcount = uint32(float(i->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
- for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
- Referenced->Process(loot, rate, lootMode, i->group);
+ uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT));
+ for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator
+ Referenced->Process(loot, rate, lootMode, item->group);
}
- else // Plain entries (not a reference, not grouped)
- loot.AddItem(*i); // Chance is already checked, just add
+ else // Plain entries (not a reference, not grouped)
+ loot.AddItem(*item); // Chance is already checked, just add
}
// Now processing groups
for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i)
- i->Process(loot, lootMode);
+ if (LootGroup* group = *i)
+ group->Process(loot, lootMode);
}
// True if template includes at least 1 quest drop entry
@@ -1349,27 +1323,33 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con
{
if (groupId > Groups.size())
return false; // Error message [should be] already printed at loading stage
- return Groups[groupId-1].HasQuestDrop();
+
+ if (!Groups[groupId - 1])
+ return false;
+
+ return Groups[groupId-1]->HasQuestDrop();
}
for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i)
{
- if (i->mincountOrRef < 0) // References
+ LootStoreItem* item = *i;
+ if (item->mincountOrRef < 0) // References
{
- LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
+ LootTemplateMap::const_iterator Referenced = store.find(-item->mincountOrRef);
if (Referenced == store.end())
continue; // Error message [should be] already printed at loading stage
- if (Referenced->second->HasQuestDrop(store, i->group))
+ if (Referenced->second->HasQuestDrop(store, item->group))
return true;
}
- else if (i->needs_quest)
+ else if (item->needs_quest)
return true; // quest drop found
}
// Now processing groups
for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i)
- if (i->HasQuestDrop())
- return true;
+ if (LootGroup* group = *i)
+ if (group->HasQuestDrop())
+ return true;
return false;
}
@@ -1381,28 +1361,34 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
{
if (groupId > Groups.size())
return false; // Error message already printed at loading stage
- return Groups[groupId-1].HasQuestDropForPlayer(player);
+
+ if (!Groups[groupId - 1])
+ return false;
+
+ return Groups[groupId-1]->HasQuestDropForPlayer(player);
}
// Checking non-grouped entries
for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i)
{
- if (i->mincountOrRef < 0) // References processing
+ LootStoreItem* item = *i;
+ if (item->mincountOrRef < 0) // References processing
{
- LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
+ LootTemplateMap::const_iterator Referenced = store.find(-item->mincountOrRef);
if (Referenced == store.end())
continue; // Error message already printed at loading stage
- if (Referenced->second->HasQuestDropForPlayer(store, player, i->group))
+ if (Referenced->second->HasQuestDropForPlayer(store, player, item->group))
return true;
}
- else if (player->HasQuestForItem(i->itemid))
+ else if (player->HasQuestForItem(item->itemid))
return true; // active quest drop found
}
// Now checking groups
for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i)
- if (i->HasQuestDropForPlayer(player))
- return true;
+ if (LootGroup* group = *i)
+ if (group->HasQuestDropForPlayer(player))
+ return true;
return false;
}
@@ -1411,8 +1397,9 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
{
// Checking group chances
- for (uint32 i=0; i < Groups.size(); ++i)
- Groups[i].Verify(lootstore, id, i+1);
+ for (uint32 i = 0; i < Groups.size(); ++i)
+ if (Groups[i])
+ Groups[i]->Verify(lootstore, id, i + 1);
// TODO: References validity checks
}
@@ -1421,18 +1408,21 @@ void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_se
{
for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
{
- if (ieItr->mincountOrRef < 0)
+ LootStoreItem* item = *ieItr;
+ if (item->mincountOrRef < 0)
{
- if (!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
- LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
+ if (!LootTemplates_Reference.GetLootFor(-item->mincountOrRef))
+ LootTemplates_Reference.ReportNotExistedId(-item->mincountOrRef);
else if (ref_set)
- ref_set->erase(-ieItr->mincountOrRef);
+ ref_set->erase(-item->mincountOrRef);
}
}
for (LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr)
- grItr->CheckLootRefs(store, ref_set);
+ if (LootGroup* group = *grItr)
+ group->CheckLootRefs(store, ref_set);
}
+
bool LootTemplate::addConditionItem(Condition* cond)
{
if (!cond || !cond->isLoaded())//should never happen, checked at loading
@@ -1440,41 +1430,48 @@ bool LootTemplate::addConditionItem(Condition* cond)
sLog->outError(LOG_FILTER_LOOT, "LootTemplate::addConditionItem: condition is null");
return false;
}
+
if (!Entries.empty())
{
for (LootStoreItemList::iterator i = Entries.begin(); i != Entries.end(); ++i)
{
- if (i->itemid == uint32(cond->SourceEntry))
+ if ((*i)->itemid == uint32(cond->SourceEntry))
{
- i->conditions.push_back(cond);
+ (*i)->conditions.push_back(cond);
return true;
}
}
}
+
if (!Groups.empty())
{
for (LootGroups::iterator groupItr = Groups.begin(); groupItr != Groups.end(); ++groupItr)
{
- LootStoreItemList* itemList = (*groupItr).GetExplicitlyChancedItemList();
+ LootGroup* group = *groupItr;
+ if (!group)
+ continue;
+
+ LootStoreItemList* itemList = group->GetExplicitlyChancedItemList();
if (!itemList->empty())
{
for (LootStoreItemList::iterator i = itemList->begin(); i != itemList->end(); ++i)
{
- if ((*i).itemid == uint32(cond->SourceEntry))
+ if ((*i)->itemid == uint32(cond->SourceEntry))
{
- (*i).conditions.push_back(cond);
+ (*i)->conditions.push_back(cond);
return true;
}
}
}
- itemList = (*groupItr).GetEqualChancedItemList();
+
+ itemList = group->GetEqualChancedItemList();
if (!itemList->empty())
{
for (LootStoreItemList::iterator i = itemList->begin(); i != itemList->end(); ++i)
{
- if ((*i).itemid == uint32(cond->SourceEntry))
+ if ((*i)->itemid == uint32(cond->SourceEntry))
{
- (*i).conditions.push_back(cond);
+ (*i)->conditions.push_back(cond);
return true;
}
}
@@ -1487,10 +1484,9 @@ bool LootTemplate::addConditionItem(Condition* cond)
bool LootTemplate::isReference(uint32 id)
{
for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
- {
- if (ieItr->itemid == id && ieItr->mincountOrRef < 0)
+ if ((*ieItr)->itemid == id && (*ieItr)->mincountOrRef < 0)
return true;
- }
+
return false;//not found or not reference
}
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index e51a4607ce1..7474fc87a71 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -27,6 +27,7 @@
#include <map>
#include <vector>
+#include <list>
enum RollType
{
@@ -173,7 +174,7 @@ class LootTemplate;
typedef std::vector<QuestItem> QuestItemList;
typedef std::vector<LootItem> LootItemList;
typedef std::map<uint32, QuestItemList*> QuestItemMap;
-typedef std::vector<LootStoreItem> LootStoreItemList;
+typedef std::list<LootStoreItem*> LootStoreItemList;
typedef UNORDERED_MAP<uint32, LootTemplate*> LootTemplateMap;
typedef std::set<uint32> LootIdSet;
@@ -217,11 +218,14 @@ class LootStore
class LootTemplate
{
class LootGroup; // A set of loot definitions for items (refs are not allowed inside)
- typedef std::vector<LootGroup> LootGroups;
+ typedef std::vector<LootGroup*> LootGroups;
public:
+ LootTemplate() { }
+ ~LootTemplate();
+
// Adds an entry to the group (at loading stage)
- void AddEntry(LootStoreItem& item);
+ void AddEntry(LootStoreItem* item);
// Rolls for every item in the template and adds the rolled items the the loot
void Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId = 0) const;
void CopyConditions(ConditionList conditions);
@@ -241,6 +245,10 @@ class LootTemplate
private:
LootStoreItemList Entries; // not grouped only
LootGroups Groups; // groups have own (optimised) processing, grouped entries go there
+
+ // Objects of this class must never be copied, we are storing pointers in container
+ LootTemplate(LootTemplate const&);
+ LootTemplate& operator=(LootTemplate const&);
};
//=====================================================
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 9c3b7d5e5b5..30ed39932ae 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -18,6 +18,7 @@
#include "Map.h"
#include "Battleground.h"
+#include "MMapFactory.h"
#include "CellImpl.h"
#include "DynamicTree.h"
#include "GridNotifiers.h"
@@ -25,7 +26,6 @@
#include "GridStates.h"
#include "Group.h"
#include "InstanceScript.h"
-#include "LFGMgr.h"
#include "MapInstanced.h"
#include "MapManager.h"
#include "ObjectAccessor.h"
@@ -43,7 +43,7 @@ union u_map_magic
};
u_map_magic MapMagic = { {'M','A','P','S'} };
-u_map_magic MapVersionMagic = { {'v','1','.','2'} };
+u_map_magic MapVersionMagic = { {'v','1','.','3'} };
u_map_magic MapAreaMagic = { {'A','R','E','A'} };
u_map_magic MapHeightMagic = { {'M','H','G','T'} };
u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
@@ -71,6 +71,8 @@ Map::~Map()
if (!m_scriptSchedule.empty())
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
bool Map::ExistMap(uint32 mapid, int gx, int gy)
@@ -119,6 +121,16 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)
return true;
}
+void Map::LoadMMap(int gx, int gy)
+{
+ bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy);
+
+ if (mmapLoadResult)
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ else
+ sLog->outInfo(LOG_FILTER_MAPS, "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+}
+
void Map::LoadVMap(int gx, int gy)
{
// x and y are swapped !!
@@ -167,18 +179,16 @@ void Map::LoadMap(int gx, int gy, bool reload)
}
// map file name
- char *tmp=NULL;
- int len = sWorld->GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
+ char* tmp = NULL;
+ int len = sWorld->GetDataPath().length() + strlen("maps/%03u%02u%02u.map") + 1;
tmp = new char[len];
- snprintf(tmp, len, (char *)(sWorld->GetDataPath()+"maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
+ snprintf(tmp, len, (char *)(sWorld->GetDataPath() + "maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
sLog->outInfo(LOG_FILTER_MAPS, "Loading map %s", tmp);
// loading data
GridMaps[gx][gy] = new GridMap();
if (!GridMaps[gx][gy]->loadData(tmp))
- {
sLog->outError(LOG_FILTER_MAPS, "Error loading map file: \n %s\n", tmp);
- }
- delete [] tmp;
+ delete[] tmp;
sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy);
}
@@ -186,8 +196,12 @@ void Map::LoadMap(int gx, int gy, bool reload)
void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
+ // Only load the data for the base map
if (i_InstanceId == 0)
- LoadVMap(gx, gy); // Only load the data for the base map
+ {
+ LoadVMap(gx, gy);
+ LoadMMap(gx, gy);
+ }
}
void Map::InitStateMachine()
@@ -998,8 +1012,8 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
GridMaps[gx][gy]->unloadData();
delete GridMaps[gx][gy];
}
- // x and y are swapped
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy);
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy);
}
else
((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy));
@@ -1071,7 +1085,7 @@ GridMap::~GridMap()
unloadData();
}
-bool GridMap::loadData(char *filename)
+bool GridMap::loadData(char* filename)
{
// Unload old data if exist
unloadData();
@@ -1905,10 +1919,10 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
{
- Vector3 startPos = Vector3(x1, y1, z1);
- Vector3 dstPos = Vector3(x2, y2, z2);
+ G3D::Vector3 startPos(x1, y1, z1);
+ G3D::Vector3 dstPos(x2, y2, z2);
- Vector3 resultPos;
+ G3D::Vector3 resultPos;
bool result = _dynamicTree.getObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist);
rx = resultPos.x;
@@ -2470,13 +2484,6 @@ bool InstanceMap::AddPlayerToMap(Player* player)
ASSERT(playerBind->save == mapSave);
}
}
-
- if (group && group->isLFGGroup())
- if (uint32 dungeonId = sLFGMgr->GetDungeon(group->GetGUID(), true))
- if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId))
- if (LFGDungeonData const* randomDungeon = sLFGMgr->GetLFGDungeon(*(sLFGMgr->GetSelectedDungeons(player->GetGUID()).begin())))
- if (uint32(dungeon->map) == GetId() && dungeon->difficulty == GetDifficulty() && randomDungeon->type == LFG_TYPE_RANDOM)
- player->CastSpell(player, LFG_SPELL_LUCK_OF_THE_DRAW, true);
}
// for normal instances cancel the reset schedule when the
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 5bb40a45f7e..e66e0869686 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -76,6 +76,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -480,6 +482,7 @@ class Map : public GridRefManager<NGridType>
void LoadMapAndVMap(int gx, int gy);
void LoadVMap(int gx, int gy);
void LoadMap(int gx, int gy, bool reload = false);
+ void LoadMMap(int gx, int gy);
GridMap* GetGrid(float x, float y);
void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
index 27a1e0cc432..c2079c22e76 100644
--- a/src/server/game/Maps/MapInstanced.cpp
+++ b/src/server/game/Maps/MapInstanced.cpp
@@ -21,6 +21,7 @@
#include "MapManager.h"
#include "Battleground.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "InstanceSaveMgr.h"
#include "World.h"
#include "Group.h"
@@ -261,6 +262,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD))
{
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId());
// in that case, unload grids of the base map, too
// so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
Map::UnloadAll();
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index 77acb5e0218..03884ae003e 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -86,7 +86,40 @@ enum TrinityStrings
LANG_CONNECTED_PLAYERS = 60,
LANG_ACCOUNT_ADDON = 61,
LANG_IMPROPER_VALUE = 62,
- // Room for more level 0 63-99 not used
+ LANG_RBAC_WRONG_PARAMETER_ID = 63,
+ LANG_RBAC_WRONG_PARAMETER_REALM = 64,
+ LANG_RBAC_GROUP_IN_LIST = 65,
+ LANG_RBAC_GROUP_NOT_IN_LIST = 66,
+ LANG_RBAC_GROUP_ADDED = 67,
+ LANG_RBAC_GROUP_REMOVED = 68,
+ LANG_RBAC_GROUP_LIST_HEADER = 69,
+ LANG_RBAC_LIST_EMPTY = 70,
+ LANG_RBAC_LIST_ELEMENT = 71,
+ LANG_RBAC_ROLE_GRANTED_IN_LIST = 72,
+ LANG_RBAC_ROLE_GRANTED_IN_DENIED_LIST = 73,
+ LANG_RBAC_ROLE_GRANTED = 74,
+ LANG_RBAC_ROLE_DENIED_IN_LIST = 75,
+ LANG_RBAC_ROLE_DENIED_IN_GRANTED_LIST = 76,
+ LANG_RBAC_ROLE_DENIED = 77,
+ LANG_RBAC_ROLE_REVOKED = 78,
+ LANG_RBAC_ROLE_REVOKED_NOT_IN_LIST = 79,
+ LANG_RBAC_ROLE_LIST_HEADER_GRANTED = 80,
+ LANG_RBAC_ROLE_LIST_HEADER_DENIED = 81,
+ LANG_RBAC_PERM_GRANTED_IN_LIST = 82,
+ LANG_RBAC_PERM_GRANTED_IN_DENIED_LIST = 83,
+ LANG_RBAC_PERM_GRANTED = 84,
+ LANG_RBAC_PERM_DENIED_IN_LIST = 85,
+ LANG_RBAC_PERM_DENIED_IN_GRANTED_LIST = 86,
+ LANG_RBAC_PERM_DENIED = 87,
+ LANG_RBAC_PERM_REVOKED = 88,
+ LANG_RBAC_PERM_REVOKED_NOT_IN_LIST = 89,
+ LANG_RBAC_PERM_LIST_HEADER_GRANTED = 90,
+ LANG_RBAC_PERM_LIST_HEADER_DENIED = 91,
+ LANG_RBAC_PERM_LIST_GLOBAL = 92,
+ LANG_RBAC_LIST_GROUPS_HEADER = 93,
+ LANG_RBAC_LIST_ROLES_HEADER = 94,
+ LANG_RBAC_LIST_PERMISSIONS_HEADER = 95,
+ // Room for more level 0 96-99 not used
// level 1 chat
LANG_GLOBAL_NOTIFY = 100,
@@ -530,6 +563,7 @@ enum TrinityStrings
LANG_PINFO_BAN = 453,
LANG_PINFO_MAP_ONLINE = 714,
LANG_PINFO_MAP_OFFLINE = 716,
+ LANG_PINFO_GUILD_INFO = 749,
LANG_YOU_SET_EXPLORE_ALL = 551,
LANG_YOU_SET_EXPLORE_NOTHING = 552,
@@ -750,7 +784,32 @@ enum TrinityStrings
LANG_COMMAND_CREATURESTORAGE_NOTFOUND = 818,
LANG_CHANNEL_CITY = 819,
- // Room for in-game strings 820-999 not used
+
+ LANG_NPCINFO_GOSSIP = 820,
+ LANG_NPCINFO_QUESTGIVER = 821,
+ LANG_NPCINFO_TRAINER_CLASS = 822,
+ LANG_NPCINFO_TRAINER_PROFESSION = 823,
+ LANG_NPCINFO_VENDOR_AMMO = 824,
+ LANG_NPCINFO_VENDOR_FOOD = 825,
+ LANG_NPCINFO_VENDOR_POISON = 826,
+ LANG_NPCINFO_VENDOR_REAGENT = 827,
+ LANG_NPCINFO_REPAIR = 828,
+ LANG_NPCINFO_FLIGHTMASTER = 829,
+ LANG_NPCINFO_SPIRITHEALER = 830,
+ LANG_NPCINFO_SPIRITGUIDE = 831,
+ LANG_NPCINFO_INNKEEPER = 832,
+ LANG_NPCINFO_BANKER = 833,
+ LANG_NPCINFO_PETITIONER = 834,
+ LANG_NPCINFO_TABARDDESIGNER = 835,
+ LANG_NPCINFO_BATTLEMASTER = 836,
+ LANG_NPCINFO_AUCTIONEER = 837,
+ LANG_NPCINFO_STABLEMASTER = 838,
+ LANG_NPCINFO_GUILD_BANKER = 839,
+ LANG_NPCINFO_SPELLCLICK = 840,
+ LANG_NPCINFO_MAILBOX = 841,
+ LANG_NPCINFO_PLAYER_VEHICLE = 842,
+
+ // Room for in-game strings 843-999 not used
// Level 4 (CLI only commands)
LANG_COMMAND_EXIT = 1000,
@@ -830,7 +889,13 @@ enum TrinityStrings
LANG_MOVEGENS_EFFECT = 1142,
LANG_MOVEFLAGS_GET = 1143,
LANG_MOVEFLAGS_SET = 1144,
- // Room for more level 3 1144-1199 not used
+ LANG_GROUP_ALREADY_IN_GROUP = 1145,
+ LANG_GROUP_PLAYER_JOINED = 1146,
+ LANG_GROUP_NOT_IN_GROUP = 1147,
+ LANG_GROUP_FULL = 1148,
+ LANG_GROUP_TYPE = 1149,
+ LANG_GROUP_PLAYER_NAME_GUID = 1150,
+ // Room for more level 3 1151-1199 not used
// Debug commands
LANG_CINEMATIC_NOT_EXIST = 1200,
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 6ff47f40ed4..3a061910faa 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -19,6 +19,7 @@
#ifndef TRINITY_SHAREDDEFINES_H
#define TRINITY_SHAREDDEFINES_H
+#include "DetourNavMesh.h"
#include "Define.h"
#include <cassert>
@@ -35,7 +36,6 @@ enum SpellEffIndex
#define EFFECT_FIRST_FOUND 254
#define EFFECT_ALL 255
-
// loot modes for creatures and gameobjects, bitmask!
enum LootModes
{
@@ -2834,7 +2834,7 @@ enum CreatureTypeFlags2
CREATURE_TYPEFLAGS_2_UNK5 = 0x00000010,
CREATURE_TYPEFLAGS_2_UNK6 = 0x00000020,
CREATURE_TYPEFLAGS_2_UNK7 = 0x00000040,
- CREATURE_TYPEFLAGS_2_UNK8 = 0x00000080,
+ CREATURE_TYPEFLAGS_2_UNK8 = 0x00000080
};
enum CreatureEliteType
@@ -2884,7 +2884,7 @@ enum HolidayIds
HOLIDAY_RATED_BG_25_VS_25 = 443,
HOLIDAY_ANNIVERSARY_7_YEARS = 467,
HOLIDAY_DARKMOON_FAIRE_TEROKKAR = 479,
- HOLIDAY_ANNIVERSARY_8_YEARS = 484,
+ HOLIDAY_ANNIVERSARY_8_YEARS = 484
};
// values based at QuestInfo.dbc
@@ -3149,7 +3149,7 @@ enum SkillType
SKILL_PET_SHALE_SPIDER = 817,
SKILL_PET_BEETLE = 818,
SKILL_ALL_GUILD_PERKS = 821,
- SKILL_PET_HYDRA = 824,
+ SKILL_PET_HYDRA = 824
};
#define MAX_SKILL_TYPE 825
@@ -3324,7 +3324,7 @@ enum ChatMsg
CHAT_MSG_ACHIEVEMENT = 0x30,
CHAT_MSG_GUILD_ACHIEVEMENT = 0x31,
CHAT_MSG_ARENA_POINTS = 0x32,
- CHAT_MSG_PARTY_LEADER = 0x33,
+ CHAT_MSG_PARTY_LEADER = 0x33
};
#define MAX_CHAT_MSG_TYPE 0x34
@@ -3342,14 +3342,14 @@ enum ChatLinkColors
// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask
enum PetDiet
{
- PET_DIET_MEAT = 1,
- PET_DIET_FISH = 2,
- PET_DIET_CHEESE = 3,
- PET_DIET_BREAD = 4,
- PET_DIET_FUNGAS = 5,
- PET_DIET_FRUIT = 6,
- PET_DIET_RAW_MEAT = 7,
- PET_DIET_RAW_FISH = 8
+ PET_DIET_MEAT = 1,
+ PET_DIET_FISH = 2,
+ PET_DIET_CHEESE = 3,
+ PET_DIET_BREAD = 4,
+ PET_DIET_FUNGAS = 5,
+ PET_DIET_FRUIT = 6,
+ PET_DIET_RAW_MEAT = 7,
+ PET_DIET_RAW_FISH = 8
};
#define MAX_PET_DIET 9
@@ -3874,4 +3874,33 @@ enum PartyResult
ERR_PARTY_LFG_TELEPORT_IN_COMBAT = 30
};
+const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
#endif
diff --git a/src/server/game/Movement/FollowerRefManager.h b/src/server/game/Movement/FollowerRefManager.h
index 2bb31d27227..92904f8e4af 100644
--- a/src/server/game/Movement/FollowerRefManager.h
+++ b/src/server/game/Movement/FollowerRefManager.h
@@ -29,4 +29,3 @@ class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase
};
#endif
-
diff --git a/src/server/game/Movement/FollowerReference.cpp b/src/server/game/Movement/FollowerReference.cpp
index 2ce23e214d1..30797bbaaca 100644
--- a/src/server/game/Movement/FollowerReference.cpp
+++ b/src/server/game/Movement/FollowerReference.cpp
@@ -34,4 +34,3 @@ void FollowerReference::sourceObjectDestroyLink()
{
getSource()->stopFollowing();
}
-
diff --git a/src/server/game/Movement/FollowerReference.h b/src/server/game/Movement/FollowerReference.h
index 9e373d2527e..43ad7e7fa58 100644
--- a/src/server/game/Movement/FollowerReference.h
+++ b/src/server/game/Movement/FollowerReference.h
@@ -32,4 +32,3 @@ class FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
void sourceObjectDestroyLink();
};
#endif
-
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 277a5721c3d..c9ca7772186 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -87,7 +87,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
ASSERT(!empty());
_cleanFlag |= MMCF_UPDATE;
- if (!top()->Update(*_owner, diff))
+ if (!top()->Update(_owner, diff))
{
_cleanFlag &= ~MMCF_UPDATE;
MovementExpired();
@@ -111,7 +111,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
else if (needInitTop())
InitTop();
else if (_cleanFlag & MMCF_RESET)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
_cleanFlag &= ~MMCF_RESET;
}
@@ -132,7 +132,7 @@ void MotionMaster::DirectClean(bool reset)
if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedClean()
@@ -163,7 +163,7 @@ void MotionMaster::DirectExpire(bool reset)
else if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedExpire()
@@ -199,19 +199,19 @@ void MotionMaster::MoveTargetedHome()
{
Clear(false);
- if (_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ if (_owner->GetTypeId() == TYPEID_UNIT && !_owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted home", _owner->GetEntry(), _owner->GetGUIDLow());
Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE);
}
- else if (_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Pet or controlled creature (Entry: %u GUID: %u) targeting home", _owner->GetEntry(), _owner->GetGUIDLow());
- Unit* target = ((Creature*)_owner)->GetCharmerOrOwner();
+ Unit* target = _owner->ToCreature()->GetCharmerOrOwner();
if (target)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
+ Mutate(new FollowMovementGenerator<Creature>(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
}
}
else
@@ -248,7 +248,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Player>(*target, dist, angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
else
{
@@ -256,7 +256,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Creature>(*target, dist, angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
}
@@ -272,7 +272,7 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) follow to %s (GUID: %u)", _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Player>(*target, dist, angle), slot);
+ Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
}
else
{
@@ -280,22 +280,22 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target, dist, angle), slot);
+ Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
}
}
-void MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
}
@@ -306,7 +306,7 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x, y, z);
init.SetAnimation(Movement::ToGround);
init.Launch();
@@ -320,7 +320,7 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x, y, z);
init.SetAnimation(Movement::ToFly);
init.Launch();
@@ -340,7 +340,7 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa
_owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x, y, z);
init.SetParabolic(max_height, 0);
init.SetOrientationFixed(true);
@@ -370,8 +370,8 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee
float moveTimeHalf = speedZ / Movement::gravity;
float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x, y, z);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z, false);
init.SetParabolic(max_height, 0);
init.SetVelocity(speedXY);
init.Launch();
@@ -399,14 +399,14 @@ void MotionMaster::MoveFall(uint32 id /*=0*/)
_owner->m_movementInfo.SetFallTime(0);
}
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false);
init.SetFall();
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
-void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
+void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, bool generatePath)
{
if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
@@ -414,16 +414,28 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
}
+void MotionMaster::MoveCharge(PathGenerator path, float speed, uint32 id)
+{
+ Vector3 dest = path.GetActualEndPosition();
+
+ MoveCharge(dest.x, dest.y, dest.z, speed, id);
+
+ Movement::MoveSplineInit init(_owner);
+ init.MovebyPath(path.GetPath());
+ init.SetVelocity(speed);
+ init.Launch();
+}
+
void MotionMaster::MoveSeekAssistance(float x, float y, float z)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
@@ -546,7 +558,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
else
{
_needInit[slot] = false;
- m->Initialize(*_owner);
+ m->Initialize(_owner);
}
}
@@ -614,7 +626,7 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
void MotionMaster::InitTop()
{
- top()->Initialize(*_owner);
+ top()->Initialize(_owner);
_needInit[_top] = false;
}
@@ -622,7 +634,7 @@ void MotionMaster::DirectDelete(_Ty curr)
{
if (isStatic(curr))
return;
- curr->Finalize(*_owner);
+ curr->Finalize(_owner);
delete curr;
}
@@ -639,9 +651,9 @@ void MotionMaster::DelayedDelete(_Ty curr)
bool MotionMaster::GetDestination(float &x, float &y, float &z)
{
if (_owner->movespline->Finalized())
- return false;
+ return false;
- const G3D::Vector3& dest = _owner->movespline->FinalDestination();
+ G3D::Vector3 const& dest = _owner->movespline->FinalDestination();
x = dest.x;
y = dest.y;
z = dest.z;
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index f6f58afef22..4b6075aac10 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -26,6 +26,7 @@
class MovementGenerator;
class Unit;
+class PathGenerator;
// Creature Entry ID used for waypoints show, visible only for GMs
#define VISUAL_WAYPOINT 1
@@ -154,13 +155,14 @@ class MotionMaster //: private std::stack<MovementGenerator *>
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, const Position &pos)
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ); }
- void MovePoint(uint32 id, float x, float y, float z);
+ void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
- void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
+ void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, bool generatePath = false);
+ void MoveCharge(PathGenerator path, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP)
@@ -199,4 +201,3 @@ class MotionMaster //: private std::stack<MovementGenerator *>
uint8 _cleanFlag;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index 1c1c38bc72f..39394a75513 100644..100755
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -33,41 +33,47 @@ class MovementGenerator
public:
virtual ~MovementGenerator();
- virtual void Initialize(Unit &) = 0;
- virtual void Finalize(Unit &) = 0;
+ virtual void Initialize(Unit*) = 0;
+ virtual void Finalize(Unit*) = 0;
- virtual void Reset(Unit &) = 0;
+ virtual void Reset(Unit*) = 0;
- virtual bool Update(Unit &, uint32 time_diff) = 0;
+ virtual bool Update(Unit*, uint32 time_diff) = 0;
virtual MovementGeneratorType GetMovementGeneratorType() = 0;
virtual void unitSpeedChanged() { }
+
+ // used by Evade code for select point to evade with expected restart default movement
+ virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};
template<class T, class D>
class MovementGeneratorMedium : public MovementGenerator
{
public:
- void Initialize(Unit &u)
+ void Initialize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoInitialize(*((T*)&u));
+ (static_cast<D*>(this))->DoInitialize(static_cast<T*>(u));
}
- void Finalize(Unit &u)
+
+ void Finalize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoFinalize(*((T*)&u));
+ (static_cast<D*>(this))->DoFinalize(static_cast<T*>(u));
}
- void Reset(Unit &u)
+
+ void Reset(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoReset(*((T*)&u));
+ (static_cast<D*>(this))->DoReset(static_cast<T*>(u));
}
- bool Update(Unit &u, uint32 time_diff)
+
+ bool Update(Unit* u, uint32 time_diff)
{
//u->AssertIsType<T>();
- return (static_cast<D*>(this))->DoUpdate(*((T*)&u), time_diff);
+ return (static_cast<D*>(this))->DoUpdate(static_cast<T*>(u), time_diff);
}
};
@@ -88,4 +94,3 @@ typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGenerato
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
#endif
-
diff --git a/src/server/game/Movement/MovementGeneratorImpl.h b/src/server/game/Movement/MovementGeneratorImpl.h
index 2618cadc14f..b77db7b5b9d 100644
--- a/src/server/game/Movement/MovementGeneratorImpl.h
+++ b/src/server/game/Movement/MovementGeneratorImpl.h
@@ -28,4 +28,3 @@ MovementGeneratorFactory<MOVEMENT_GEN>::Create(void * /*data*/) const
return (new MOVEMENT_GEN());
}
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
index 482c16997a0..72537f0898c 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -19,6 +19,7 @@
#include "Creature.h"
#include "MapManager.h"
#include "ConfusedMovementGenerator.h"
+#include "PathGenerator.h"
#include "VMapFactory.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -30,100 +31,44 @@
#endif
template<class T>
-void ConfusedMovementGenerator<T>::DoInitialize(T &unit)
+void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
{
- unit.StopMoving();
- float const wander_distance = 4;
- float x = unit.GetPositionX();
- float y = unit.GetPositionY();
- float z = unit.GetPositionZ();
+ unit->AddUnitState(UNIT_STATE_CONFUSED);
+ unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->GetPosition(i_x, i_y, i_z);
- Map const* map = unit.GetBaseMap();
+ if (!unit->isAlive() || unit->IsStopped())
+ return;
- i_nextMove = 1;
-
- bool is_water_ok, is_land_ok;
- _InitSpecific(unit, is_water_ok, is_land_ok);
-
- for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
- {
- float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
- float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
-
- // prevent invalid coordinates generation
- Trinity::NormalizeMapCoord(wanderX);
- Trinity::NormalizeMapCoord(wanderY);
-
- if (unit.IsWithinLOS(wanderX, wanderY, z))
- {
- bool is_water = map->IsInWater(wanderX, wanderY, z);
-
- if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
- {
- //! Cannot use coordinates outside our InhabitType. Use the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
- }
- else
- {
- //! Trying to access path outside line of sight. Skip this by using the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
-
- unit.UpdateAllowedPositionZ(wanderX, wanderY, z);
-
- //! Positions are fine - apply them to this waypoint
- i_waypoints[idx][0] = wanderX;
- i_waypoints[idx][1] = wanderY;
- i_waypoints[idx][2] = z;
- }
-
- unit.StopMoving();
- unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
-}
-
-template<>
-void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = creature.canSwim();
- is_land_ok = creature.canWalk();
-}
-
-template<>
-void ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = true;
- is_land_ok = true;
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
}
template<class T>
-void ConfusedMovementGenerator<T>::DoReset(T &unit)
+void ConfusedMovementGenerator<T>::DoReset(T* unit)
{
- i_nextMove = 1;
i_nextMoveTime.Reset(0);
- unit.StopMoving();
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+
+ if (!unit->isAlive() || unit->IsStopped())
+ return;
+
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
}
template<class T>
-bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
+bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, uint32 diff)
{
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return true;
if (i_nextMoveTime.Passed())
{
// currently moving, update location
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
- if (unit.movespline->Finalized())
- {
- i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
- i_nextMoveTime.Reset(urand(500, 1200)); // Guessed
- }
+ if (unit->movespline->Finalized())
+ i_nextMoveTime.Reset(urand(800, 1500));
}
else
{
@@ -132,14 +77,25 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
if (i_nextMoveTime.Passed())
{
// start moving
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+
+ float dest = 4.0f * (float)rand_norm() - 2.0f;
+
+ Position pos;
+ pos.Relocate(i_x, i_y, i_z);
+ unit->MovePositionToFirstCollision(pos, dest, 0.0f);
+
+ PathGenerator path(unit);
+ path.SetPathLengthLimit(30.0f);
+ bool result = path.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ if (!result || (path.GetPathType() & PATHFIND_NOPATH))
+ {
+ i_nextMoveTime.Reset(100);
+ return true;
+ }
- ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
- float x = i_waypoints[i_nextMove][0];
- float y = i_waypoints[i_nextMove][1];
- float z = i_waypoints[i_nextMove][2];
Movement::MoveSplineInit init(unit);
- init.MoveTo(x, y, z);
+ init.MovebyPath(path.GetPath());
init.SetWalk(true);
init.Launch();
}
@@ -149,25 +105,25 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
}
template<>
-void ConfusedMovementGenerator<Player>::DoFinalize(Player &unit)
+void ConfusedMovementGenerator<Player>::DoFinalize(Player* unit)
{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->StopMoving();
}
template<>
-void ConfusedMovementGenerator<Creature>::DoFinalize(Creature &unit)
+void ConfusedMovementGenerator<Creature>::DoFinalize(Creature* unit)
{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
- if (unit.getVictim())
- unit.SetTarget(unit.getVictim()->GetGUID());
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ if (unit->getVictim())
+ unit->SetTarget(unit->getVictim()->GetGUID());
}
-template void ConfusedMovementGenerator<Player>::DoInitialize(Player &player);
-template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature &creature);
-template void ConfusedMovementGenerator<Player>::DoReset(Player &player);
-template void ConfusedMovementGenerator<Creature>::DoReset(Creature &creature);
-template bool ConfusedMovementGenerator<Player>::DoUpdate(Player &player, uint32 diff);
-template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff);
-
+template void ConfusedMovementGenerator<Player>::DoInitialize(Player*);
+template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*);
+template void ConfusedMovementGenerator<Player>::DoReset(Player*);
+template void ConfusedMovementGenerator<Creature>::DoReset(Creature*);
+template bool ConfusedMovementGenerator<Player>::DoUpdate(Player*, uint32 diff);
+template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature*, uint32 diff);
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
index 20e84f3a97f..da29b8aa12e 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
@@ -22,25 +22,20 @@
#include "MovementGenerator.h"
#include "Timer.h"
-#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
-
template<class T>
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
public:
explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
- void _InitSpecific(T &, bool &, bool &);
TimeTracker i_nextMoveTime;
- float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
- uint32 i_nextMove;
+ float i_x, i_y, i_z;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
index d9050e2b76a..216fffbfee1 100755..100644
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -20,6 +20,7 @@
#include "CreatureAI.h"
#include "MapManager.h"
#include "FleeingMovementGenerator.h"
+#include "PathGenerator.h"
#include "ObjectAccessor.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -29,384 +30,163 @@
#define MAX_QUIET_DISTANCE 43.0f
template<class T>
-void FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
+void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
{
- if (!&owner)
+ if (!owner)
return;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
- if (!_setMoveData(owner))
- return;
+ owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
float x, y, z;
- if (!_getPoint(owner, x, y, z))
- return;
+ _getPoint(owner, x, y, z);
- owner.AddUnitState(UNIT_STATE_FLEEING_MOVE);
+ PathGenerator path(owner);
+ path.SetPathLengthLimit(30.0f);
+ bool result = path.CalculatePath(x, y, z);
+ if (!result || (path.GetPathType() & PATHFIND_NOPATH))
+ {
+ i_nextCheckTime.Reset(100);
+ return;
+ }
Movement::MoveSplineInit init(owner);
- init.MoveTo(x, y, z);
+ init.MovebyPath(path.GetPath());
init.SetWalk(false);
- init.Launch();
+ int32 traveltime = init.Launch();
+ i_nextCheckTime.Reset(traveltime + urand(800, 1500));
}
template<class T>
-bool FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
+void FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z)
{
- if (!&owner)
- return false;
-
- x = owner.GetPositionX();
- y = owner.GetPositionY();
- z = owner.GetPositionZ();
-
- float temp_x, temp_y, angle;
- const Map* _map = owner.GetBaseMap();
- // primitive path-finding
- for (uint8 i = 0; i < 18; ++i)
+ float dist_from_caster, angle_to_caster;
+ if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
- if (i_only_forward && i > 2)
- break;
-
- float distance = 5.0f;
-
- switch (i)
- {
- case 0:
- angle = i_cur_angle;
- break;
- case 1:
- angle = i_cur_angle;
- distance /= 2;
- break;
- case 2:
- angle = i_cur_angle;
- distance /= 4;
- break;
- case 3:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- break;
- case 4:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- break;
- case 5:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 6:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 7:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- break;
- case 8:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- break;
- case 9:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 10:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 11:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 12:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 13:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 14:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 15:
- angle = i_cur_angle + static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 16:
- angle = i_cur_angle - static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 17:
- angle = i_cur_angle + static_cast<float>(M_PI);
- distance /= 2;
- break;
- default:
- angle = 0.0f;
- distance = 0.0f;
- break;
- }
-
- temp_x = x + distance * std::cos(angle);
- temp_y = y + distance * std::sin(angle);
- Trinity::NormalizeMapCoord(temp_x);
- Trinity::NormalizeMapCoord(temp_y);
- if (owner.IsWithinLOS(temp_x, temp_y, z))
- {
- bool is_water_now = _map->IsInWater(x, y, z);
-
- if (is_water_now && _map->IsInWater(temp_x, temp_y, z))
- {
- x = temp_x;
- y = temp_y;
- return true;
- }
- float new_z = _map->GetHeight(owner.GetPhaseMask(), temp_x, temp_y, z, true);
-
- if (new_z <= INVALID_HEIGHT)
- continue;
-
- bool is_water_next = _map->IsInWater(temp_x, temp_y, new_z);
-
- if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
- continue;
-
- if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
- {
- float new_z_left = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f * std::cos(angle+static_cast<float>(M_PI/2)), temp_y + 1.0f * std::sin(angle+static_cast<float>(M_PI/2)), z, true);
- float new_z_right = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f * std::cos(angle-static_cast<float>(M_PI/2)), temp_y + 1.0f * std::sin(angle-static_cast<float>(M_PI/2)), z, true);
- if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
- {
- x = temp_x;
- y = temp_y;
- z = new_z;
- return true;
- }
- }
- }
- }
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500, 1000));
- return false;
-}
-
-template<class T>
-bool FleeingMovementGenerator<T>::_setMoveData(T &owner)
-{
- float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
-
- if (i_to_distance_from_caster > 0.0f)
- {
- if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
- // if we reach lower distance
- (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
- // if we can't be close
- (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
- // if we reach bigger distance
- (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
- (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
- // if we leave 'quiet zone'
- {
- // we are very far or too close, stopping
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500, 1000));
- return false;
- }
+ dist_from_caster = fright->GetDistance(owner);
+ if (dist_from_caster > 0.2f)
+ angle_to_caster = fright->GetAngle(owner);
else
- {
- // now we are running, continue
- i_last_distance_from_caster = cur_dist_xyz;
- return true;
- }
- }
-
- float cur_dist;
- float angle_to_caster;
-
- if (Unit* fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- cur_dist = fright->GetDistance(&owner);
- if (cur_dist < cur_dist_xyz)
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- angle_to_caster = fright->GetAngle(&owner);
- }
- else
- {
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
- }
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
}
else
{
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
+ dist_from_caster = 0.0f;
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
}
- // if we too close may use 'path-finding' else just stop
- i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
-
- //get angle and 'distance from caster' to run
- float angle;
-
- if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
+ float dist, angle;
+ if (dist_from_caster < MIN_QUIET_DISTANCE)
{
- angle = (float)rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * static_cast<float>(M_PI/3) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE;
- i_only_forward = true;
+ dist = frand(0.4f, 1.3f)*(MIN_QUIET_DISTANCE - dist_from_caster);
+ angle = angle_to_caster + frand(-static_cast<float>(M_PI)/8, static_cast<float>(M_PI)/8);
}
- else if (cur_dist < MIN_QUIET_DISTANCE)
+ else if (dist_from_caster > MAX_QUIET_DISTANCE)
{
- angle = static_cast<float>(M_PI/6) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = cur_dist*2/3 + (float)rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
+ dist = frand(0.4f, 1.0f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = -angle_to_caster + frand(-static_cast<float>(M_PI)/4, static_cast<float>(M_PI)/4);
}
- else if (cur_dist > MAX_QUIET_DISTANCE)
+ else // we are inside quiet range
{
- angle = (float)rand_norm()*static_cast<float>(M_PI/3) + static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ dist = frand(0.6f, 1.2f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = frand(0, 2*static_cast<float>(M_PI));
}
- else
- {
- angle = (float)rand_norm()*static_cast<float>(M_PI);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
- }
-
- int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
- i_cur_angle = sign*angle + angle_to_caster;
- // current distance
- i_last_distance_from_caster = cur_dist;
-
- return true;
+ Position pos;
+ owner->GetFirstCollisionPosition(pos, dist, angle);
+ x = pos.m_positionX;
+ y = pos.m_positionY;
+ z = pos.m_positionZ;
}
template<class T>
-void FleeingMovementGenerator<T>::DoInitialize(T &owner)
+void FleeingMovementGenerator<T>::DoInitialize(T* owner)
{
- if (!&owner)
+ if (!owner)
return;
- owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
-
- _Init(owner);
-
- if (Unit* fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- }
- else
- {
- i_caster_x = owner.GetPositionX();
- i_caster_y = owner.GetPositionY();
- i_caster_z = owner.GetPositionZ();
- }
-
- i_only_forward = true;
- i_cur_angle = 0.0f;
- i_last_distance_from_caster = 0.0f;
- i_to_distance_from_caster = 0.0f;
+ owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
_setTargetLocation(owner);
}
template<>
-void FleeingMovementGenerator<Creature>::_Init(Creature &owner)
+void FleeingMovementGenerator<Player>::DoFinalize(Player* owner)
{
- if (!&owner)
- return;
-
- //owner.SetTargetGuid(ObjectGuid());
- is_water_ok = owner.canSwim();
- is_land_ok = owner.canWalk();
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
+ owner->StopMoving();
}
template<>
-void FleeingMovementGenerator<Player>::_Init(Player &)
+void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner)
{
- is_water_ok = true;
- is_land_ok = true;
-}
-
-template<>
-void FleeingMovementGenerator<Player>::DoFinalize(Player &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- owner.StopMoving();
-}
-
-template<>
-void FleeingMovementGenerator<Creature>::DoFinalize(Creature &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (owner.getVictim())
- owner.SetTarget(owner.getVictim()->GetGUID());
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (owner->getVictim())
+ owner->SetTarget(owner->getVictim()->GetGUID());
}
template<class T>
-void FleeingMovementGenerator<T>::DoReset(T &owner)
+void FleeingMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
-bool FleeingMovementGenerator<T>::DoUpdate(T &owner, uint32 time_diff)
+bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
- if (!&owner || !owner.isAlive())
+ if (!owner || !owner->isAlive())
return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
i_nextCheckTime.Update(time_diff);
- if (i_nextCheckTime.Passed() && owner.movespline->Finalized())
+ if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
_setTargetLocation(owner);
return true;
}
-template void FleeingMovementGenerator<Player>::DoInitialize(Player &);
-template void FleeingMovementGenerator<Creature>::DoInitialize(Creature &);
-template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
-template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
-template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
-template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
-template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
-template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
-template void FleeingMovementGenerator<Player>::DoReset(Player &);
-template void FleeingMovementGenerator<Creature>::DoReset(Creature &);
-template bool FleeingMovementGenerator<Player>::DoUpdate(Player &, uint32);
-template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature &, uint32);
-
-void TimedFleeingMovementGenerator::Finalize(Unit &owner)
+template void FleeingMovementGenerator<Player>::DoInitialize(Player*);
+template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*);
+template void FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
+template void FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
+template void FleeingMovementGenerator<Player>::DoReset(Player*);
+template void FleeingMovementGenerator<Creature>::DoReset(Creature*);
+template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32);
+template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
+
+void TimedFleeingMovementGenerator::Finalize(Unit* owner)
{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (Unit* victim = owner.getVictim())
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (Unit* victim = owner->getVictim())
{
- if (owner.isAlive())
+ if (owner->isAlive())
{
- owner.AttackStop();
- owner.ToCreature()->AI()->AttackStart(victim);
+ owner->AttackStop();
+ owner->ToCreature()->AI()->AttackStart(victim);
}
}
}
-bool TimedFleeingMovementGenerator::Update(Unit & owner, uint32 time_diff)
+bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff)
{
- if (!owner.isAlive())
+ if (!owner->isAlive())
return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
@@ -418,4 +198,3 @@ bool TimedFleeingMovementGenerator::Update(Unit & owner, uint32 time_diff)
// This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
}
-
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
index 8ad165c8206..33a7c705564 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
@@ -27,29 +27,17 @@ class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovem
public:
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
private:
- void _setTargetLocation(T &owner);
- bool _getPoint(T &owner, float &x, float &y, float &z);
- bool _setMoveData(T &owner);
- void _Init(T &);
+ void _setTargetLocation(T*);
+ void _getPoint(T*, float &x, float &y, float &z);
- bool is_water_ok :1;
- bool is_land_ok :1;
- bool i_only_forward:1;
-
- float i_caster_x;
- float i_caster_y;
- float i_caster_z;
- float i_last_distance_from_caster;
- float i_to_distance_from_caster;
- float i_cur_angle;
uint64 i_frightGUID;
TimeTracker i_nextCheckTime;
};
@@ -62,12 +50,11 @@ class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
i_totalFleeTime(time) {}
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
- bool Update(Unit &, uint32);
- void Finalize(Unit &);
+ bool Update(Unit*, uint32);
+ void Finalize(Unit*);
private:
TimeTracker i_totalFleeTime;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index 4f0a620c303..a94ef5d4f87 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -23,49 +23,50 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void HomeMovementGenerator<Creature>::DoInitialize(Creature & owner)
+void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
_setTargetLocation(owner);
}
-void HomeMovementGenerator<Creature>::DoReset(Creature &)
+void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
+{
+ if (arrived)
+ {
+ owner->ClearUnitState(UNIT_STATE_EVADE);
+ owner->SetWalk(true);
+ owner->LoadCreaturesAddon(true);
+ owner->AI()->JustReachedHome();
+ }
+}
+
+void HomeMovementGenerator<Creature>::DoReset(Creature*)
{
}
-void HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
{
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return;
Movement::MoveSplineInit init(owner);
float x, y, z, o;
// at apply we can select more nice return points base at current movegen
- //if (owner.GetMotionMaster()->empty() || !owner.GetMotionMaster()->top()->GetResetPosition(owner, x, y, z))
- //{
- owner.GetHomePosition(x, y, z, o);
- init.SetFacing(o);
- //}
+ if (owner->GetMotionMaster()->empty() || !owner->GetMotionMaster()->top()->GetResetPosition(owner, x, y, z))
+ {
+ owner->GetHomePosition(x, y, z, o);
+ init.SetFacing(o);
+ }
init.MoveTo(x, y, z);
init.SetWalk(false);
init.Launch();
arrived = false;
- owner.ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE));
-}
-bool HomeMovementGenerator<Creature>::DoUpdate(Creature &owner, const uint32 /*time_diff*/)
-{
- arrived = owner.movespline->Finalized();
- return !arrived;
+ owner->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE));
}
-void HomeMovementGenerator<Creature>::DoFinalize(Creature& owner)
+bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, const uint32 /*time_diff*/)
{
- if (arrived)
- {
- owner.ClearUnitState(UNIT_STATE_EVADE);
- owner.SetWalk(true);
- owner.LoadCreaturesAddon(true);
- owner.AI()->JustReachedHome();
- }
+ arrived = owner->movespline->Finalized();
+ return !arrived;
}
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
index c93241b82db..3d6c6ab18c9 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
@@ -34,15 +34,14 @@ class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature
HomeMovementGenerator() : arrived(false) {}
~HomeMovementGenerator() {}
- void DoInitialize(Creature &);
- void DoFinalize(Creature &);
- void DoReset(Creature &);
- bool DoUpdate(Creature &, const uint32);
+ void DoInitialize(Creature*);
+ void DoFinalize(Creature*);
+ void DoReset(Creature*);
+ bool DoUpdate(Creature*, const uint32);
MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
private:
- void _setTargetLocation(Creature &);
+ void _setTargetLocation(Creature*);
bool arrived;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
index c952ad9ba94..81442570940 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
@@ -24,33 +24,33 @@ IdleMovementGenerator si_idleMovement;
// StopMoving is needed to make unit stop if its last movement generator expires
// But it should not be sent otherwise there are many redundent packets
-void IdleMovementGenerator::Initialize(Unit &owner)
+void IdleMovementGenerator::Initialize(Unit* owner)
{
Reset(owner);
}
-void IdleMovementGenerator::Reset(Unit& owner)
+void IdleMovementGenerator::Reset(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
}
-void RotateMovementGenerator::Initialize(Unit& owner)
+void RotateMovementGenerator::Initialize(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
- if (owner.getVictim())
- owner.SetInFront(owner.getVictim());
+ if (owner->getVictim())
+ owner->SetInFront(owner->getVictim());
- owner.AddUnitState(UNIT_STATE_ROTATING);
+ owner->AddUnitState(UNIT_STATE_ROTATING);
- owner.AttackStop();
+ owner->AttackStop();
}
-bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
+bool RotateMovementGenerator::Update(Unit* owner, uint32 diff)
{
- float angle = owner.GetOrientation();
+ float angle = owner->GetOrientation();
if (m_direction == ROTATE_DIRECTION_LEFT)
{
angle += (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
@@ -61,8 +61,8 @@ bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
angle -= (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
while (angle < 0) angle += static_cast<float>(M_PI * 2);
}
- owner.SetOrientation(angle);
- owner.SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
+ owner->SetOrientation(angle);
+ owner->SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
if (m_duration > diff)
m_duration -= diff;
@@ -72,24 +72,24 @@ bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
return true;
}
-void RotateMovementGenerator::Finalize(Unit &unit)
+void RotateMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROTATING);
- if (unit.GetTypeId() == TYPEID_UNIT)
- unit.ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
+ unit->ClearUnitState(UNIT_STATE_ROTATING);
+ if (unit->GetTypeId() == TYPEID_UNIT)
+ unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
}
-void DistractMovementGenerator::Initialize(Unit& owner)
+void DistractMovementGenerator::Initialize(Unit* owner)
{
- owner.AddUnitState(UNIT_STATE_DISTRACTED);
+ owner->AddUnitState(UNIT_STATE_DISTRACTED);
}
-void DistractMovementGenerator::Finalize(Unit& owner)
+void DistractMovementGenerator::Finalize(Unit* owner)
{
- owner.ClearUnitState(UNIT_STATE_DISTRACTED);
+ owner->ClearUnitState(UNIT_STATE_DISTRACTED);
}
-bool DistractMovementGenerator::Update(Unit& /*owner*/, uint32 time_diff)
+bool DistractMovementGenerator::Update(Unit* /*owner*/, uint32 time_diff)
{
if (time_diff > m_timer)
return false;
@@ -98,9 +98,8 @@ bool DistractMovementGenerator::Update(Unit& /*owner*/, uint32 time_diff)
return true;
}
-void AssistanceDistractMovementGenerator::Finalize(Unit &unit)
+void AssistanceDistractMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_DISTRACTED);
- unit.ToCreature()->SetReactState(REACT_AGGRESSIVE);
+ unit->ClearUnitState(UNIT_STATE_DISTRACTED);
+ unit->ToCreature()->SetReactState(REACT_AGGRESSIVE);
}
-
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
index bbcc2413cae..0043891db2c 100644..100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
@@ -25,10 +25,10 @@ class IdleMovementGenerator : public MovementGenerator
{
public:
- void Initialize(Unit &);
- void Finalize(Unit &) { }
- void Reset(Unit &);
- bool Update(Unit &, uint32) { return true; }
+ void Initialize(Unit*);
+ void Finalize(Unit*) { }
+ void Reset(Unit*);
+ bool Update(Unit*, uint32) { return true; }
MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
};
@@ -39,10 +39,10 @@ class RotateMovementGenerator : public MovementGenerator
public:
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, uint32 time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
private:
@@ -55,10 +55,10 @@ class DistractMovementGenerator : public MovementGenerator
public:
explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, uint32 time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
private:
@@ -72,8 +72,7 @@ class AssistanceDistractMovementGenerator : public DistractMovementGenerator
DistractMovementGenerator(timer) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
- void Finalize(Unit& unit);
+ void Finalize(Unit*);
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 68f62d28899..1f5503948c8 100644..100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -27,111 +27,116 @@
//----- Point Movement Generator
template<class T>
-void PointMovementGenerator<T>::DoInitialize(T &unit)
+void PointMovementGenerator<T>::DoInitialize(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
+
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+
+ if (id == EVENT_CHARGE)
+ return;
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f)
init.SetVelocity(speed);
init.Launch();
}
template<class T>
-bool PointMovementGenerator<T>::DoUpdate(T &unit, uint32 /*diff*/)
+bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
{
- if (!&unit)
+ if (!unit)
return false;
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- unit.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- unit.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
- if (i_recalculateSpeed && !unit.movespline->Finalized())
+ if (id != EVENT_CHARGE && i_recalculateSpeed && !unit->movespline->Finalized())
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
init.SetVelocity(speed);
init.Launch();
}
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
template<class T>
-void PointMovementGenerator<T>::DoFinalize(T &unit)
+void PointMovementGenerator<T>::DoFinalize(T* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ if (unit->HasUnitState(UNIT_STATE_CHARGING))
+ unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
- if (unit.movespline->Finalized())
+ if (unit->movespline->Finalized())
MovementInform(unit);
}
template<class T>
-void PointMovementGenerator<T>::DoReset(T &unit)
+void PointMovementGenerator<T>::DoReset(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template<class T>
-void PointMovementGenerator<T>::MovementInform(T & /*unit*/)
+void PointMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
-template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit)
{
- if (unit.AI())
- unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+ if (unit->AI())
+ unit->AI()->MovementInform(POINT_MOTION_TYPE, id);
}
-template void PointMovementGenerator<Player>::DoInitialize(Player&);
-template void PointMovementGenerator<Creature>::DoInitialize(Creature&);
-template void PointMovementGenerator<Player>::DoFinalize(Player&);
-template void PointMovementGenerator<Creature>::DoFinalize(Creature&);
-template void PointMovementGenerator<Player>::DoReset(Player&);
-template void PointMovementGenerator<Creature>::DoReset(Creature&);
-template bool PointMovementGenerator<Player>::DoUpdate(Player &, uint32);
-template bool PointMovementGenerator<Creature>::DoUpdate(Creature&, uint32);
+template void PointMovementGenerator<Player>::DoInitialize(Player*);
+template void PointMovementGenerator<Creature>::DoInitialize(Creature*);
+template void PointMovementGenerator<Player>::DoFinalize(Player*);
+template void PointMovementGenerator<Creature>::DoFinalize(Creature*);
+template void PointMovementGenerator<Player>::DoReset(Player*);
+template void PointMovementGenerator<Creature>::DoReset(Creature*);
+template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32);
+template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
-void AssistanceMovementGenerator::Finalize(Unit &unit)
+void AssistanceMovementGenerator::Finalize(Unit* unit)
{
- unit.ToCreature()->SetNoCallAssistance(false);
- unit.ToCreature()->CallAssistance();
- if (unit.isAlive())
- unit.GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
+ unit->ToCreature()->SetNoCallAssistance(false);
+ unit->ToCreature()->CallAssistance();
+ if (unit->isAlive())
+ unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
}
-bool EffectMovementGenerator::Update(Unit &unit, uint32)
+bool EffectMovementGenerator::Update(Unit* unit, uint32)
{
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
-void EffectMovementGenerator::Finalize(Unit &unit)
+void EffectMovementGenerator::Finalize(Unit* unit)
{
- if (unit.GetTypeId() != TYPEID_UNIT)
+ if (unit->GetTypeId() != TYPEID_UNIT)
return;
- if (((Creature&)unit).AI())
- ((Creature&)unit).AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
// Need restore previous movement since we have no proper states system
- //if (unit.isAlive() && !unit.HasUnitState(UNIT_STATE_CONFUSED|UNIT_STATE_FLEEING))
- //{
- // if (Unit* victim = unit.getVictim())
- // unit.GetMotionMaster()->MoveChase(victim);
- // else
- // unit.GetMotionMaster()->Initialize();
- //}
+ if (unit->isAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
+ {
+ if (Unit* victim = unit->getVictim())
+ unit->GetMotionMaster()->MoveChase(victim);
+ else
+ unit->GetMotionMaster()->Initialize();
+ }
+
+ if (unit->ToCreature()->AI())
+ unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
}
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
index f9a51d0c5f3..421736ca4ec 100644
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
@@ -26,25 +26,26 @@ template<class T>
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
{
public:
- PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f) : id(_id),
- i_x(_x), i_y(_y), i_z(_z), speed(_speed) {}
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z, bool _generatePath, float _speed = 0.0f) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), speed(_speed), m_generatePath(_generatePath), i_recalculateSpeed(false) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, uint32);
- void MovementInform(T &);
+ void MovementInform(T*);
void unitSpeedChanged() { i_recalculateSpeed = true; }
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
- bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
+ void GetDestination(float& x, float& y, float& z) const { x = i_x; y = i_y; z = i_z; }
private:
uint32 id;
float i_x, i_y, i_z;
float speed;
+ bool m_generatePath;
bool i_recalculateSpeed;
};
@@ -52,10 +53,10 @@ class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
- PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+ PointMovementGenerator<Creature>(0, _x, _y, _z, true) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
- void Finalize(Unit &);
+ void Finalize(Unit*);
};
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
@@ -63,14 +64,13 @@ class EffectMovementGenerator : public MovementGenerator
{
public:
explicit EffectMovementGenerator(uint32 Id) : m_Id(Id) {}
- void Initialize(Unit &) {}
- void Finalize(Unit &unit);
- void Reset(Unit &) {}
- bool Update(Unit &u, uint32);
+ void Initialize(Unit*) {}
+ void Finalize(Unit*);
+ void Reset(Unit*) {}
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; }
private:
uint32 m_Id;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 94819e87213..723b0748494 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -33,16 +33,16 @@
#endif
template<>
-void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
+void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ;
- creature.GetHomePosition(respX, respY, respZ, respO);
- Map const* map = creature.GetBaseMap();
+ creature->GetHomePosition(respX, respY, respZ, respO);
+ Map const* map = creature->GetBaseMap();
// For 2D/3D system selection
//bool is_land_ok = creature.CanWalk(); // not used?
//bool is_water_ok = creature.CanSwim(); // not used?
- bool is_air_ok = creature.CanFly();
+ bool is_air_ok = creature->CanFly();
const float angle = float(rand_norm()) * static_cast<float>(M_PI*2.0f);
const float range = float(rand_norm()) * wander_distance;
@@ -77,17 +77,17 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
// The fastest way to get an accurate result 90% of the time.
// Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
if (fabs(destZ - respZ) > travelDistZ) // Map check
{
// Vmap Horizontal or above
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ - 2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ - 2.0f, true);
if (fabs(destZ - respZ) > travelDistZ)
{
// Vmap Higher
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
// let's forget this bad coords where a z cannot be find and retry at next tick
if (fabs(destZ - respZ) > travelDistZ)
@@ -101,7 +101,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
else
i_nextMoveTime.Reset(urand(500, 10000));
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(destX, destY, destZ);
@@ -109,47 +109,47 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(destX, destY, destZ);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(destX, destY, destZ);
}
template<>
-void RandomMovementGenerator<Creature>::DoInitialize(Creature &creature)
+void RandomMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
- if (!creature.isAlive())
+ if (!creature->isAlive())
return;
if (!wander_distance)
- wander_distance = creature.GetRespawnRadius();
+ wander_distance = creature->GetRespawnRadius();
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
_setRandomLocation(creature);
}
template<>
-void RandomMovementGenerator<Creature>::DoReset(Creature &creature)
+void RandomMovementGenerator<Creature>::DoReset(Creature* creature)
{
DoInitialize(creature);
}
template<>
-void RandomMovementGenerator<Creature>::DoFinalize(Creature &creature)
+void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
template<>
-bool RandomMovementGenerator<Creature>::DoUpdate(Creature &creature, const uint32 diff)
+bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff)
{
- if (creature.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
{
i_nextMoveTime.Reset(0); // Expire the timer
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- if (creature.movespline->Finalized())
+ if (creature->movespline->Finalized())
{
i_nextMoveTime.Update(diff);
if (i_nextMoveTime.Passed())
@@ -159,14 +159,14 @@ bool RandomMovementGenerator<Creature>::DoUpdate(Creature &creature, const uint3
}
template<>
-bool RandomMovementGenerator<Creature>::GetResetPosition(Creature &creature, float& x, float& y, float& z)
+bool RandomMovementGenerator<Creature>::GetResetPos(Creature* creature, float& x, float& y, float& z)
{
float radius;
- creature.GetRespawnPosition(x, y, z, NULL, &radius);
+ creature->GetRespawnPosition(x, y, z, NULL, &radius);
// use current if in range
- if (creature.IsWithinDist2d(x, y, radius))
- creature.GetPosition(x, y, z);
+ if (creature->IsWithinDist2d(x, y, radius))
+ creature->GetPosition(x, y, z);
return true;
}
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
index 3e74753bc91..a6159e995fe 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
@@ -27,12 +27,12 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen
public:
RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) {}
- void _setRandomLocation(T &);
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, const uint32);
- bool GetResetPosition(T&, float& x, float& y, float& z);
+ void _setRandomLocation(T*);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, const uint32);
+ bool GetResetPos(T*, float& x, float& y, float& z);
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
private:
TimeTrackerSmall i_nextMoveTime;
@@ -41,4 +41,3 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen
float wander_distance;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index c1a4c5f70a3..abb4ac9964b 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -26,89 +26,87 @@
#include "MoveSpline.h"
#include "Player.h"
-#include <cmath>
-
template<class T, typename D>
-void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T &owner)
+void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool updateDestination)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return;
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
+ return;
+
+ if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature()))
return;
float x, y, z;
- //! Following block of code deleted by MrSmite in issue 4891
- //! Code kept for learning and diagnostical purposes
-//
-// if (i_offset && i_target->IsWithinDistInMap(&owner, 2*i_offset))
-// {
-// if (!owner.movespline->Finalized())
-// return;
-//
-// owner.GetPosition(x, y, z);
-// }
-// else
- if (!i_offset)
- {
- if (i_target->IsWithinMeleeRange(&owner))
- return;
- // to nearest random contact position
- i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f);
- }
- else
+ if (updateDestination || !i_path)
{
- float dist;
- float size;
-
- // Pets need special handling.
- // We need to subtract GetObjectSize() because it gets added back further down the chain
- // and that makes pets too far away. Subtracting it allows pets to properly
- // be (GetCombatReach() + i_offset) away.
- // Only applies when i_target is pet's owner otherwise pets and mobs end up
- // doing a "dance" while fighting
- if (owner.isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
+ if (!i_offset)
{
- dist = i_target->GetCombatReach();
- size = i_target->GetCombatReach() - i_target->GetObjectSize();
+ // to nearest contact position
+ i_target->GetContactPoint(owner, x, y, z);
}
else
{
- dist = i_offset + 1.0f;
- size = owner.GetObjectSize();
+ float dist;
+ float size;
+
+ // Pets need special handling.
+ // We need to subtract GetObjectSize() because it gets added back further down the chain
+ // and that makes pets too far away. Subtracting it allows pets to properly
+ // be (GetCombatReach() + i_offset) away.
+ // Only applies when i_target is pet's owner otherwise pets and mobs end up
+ // doing a "dance" while fighting
+ if (owner->isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ dist = i_target->GetCombatReach();
+ size = i_target->GetCombatReach() - i_target->GetObjectSize();
+ }
+ else
+ {
+ dist = i_offset + 1.0f;
+ size = owner->GetObjectSize();
+ }
+
+ if (i_target->IsWithinDistInMap(owner, dist))
+ return;
+
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x, y, z, size, i_offset, i_angle);
}
+ }
+ else
+ {
+ // the destination has not changed, we just need to refresh the path (usually speed change)
+ G3D::Vector3 end = i_path->GetEndPosition();
+ x = end.x;
+ y = end.y;
+ z = end.z;
+ }
- if (i_target->IsWithinDistInMap(&owner, dist))
- return;
+ if (!i_path)
+ i_path = new PathGenerator(owner);
- // to at i_offset distance from target and i_angle from target facing
- i_target->GetClosePoint(x, y, z, size, i_offset, i_angle);
- }
+ // allow pets to use shortcut if no path found when following their master
+ bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->isPet()
+ && owner->HasUnitState(UNIT_STATE_FOLLOW));
- /*
- We MUST not check the distance difference and avoid setting the new location for smaller distances.
- By that we risk having far too many GetContactPoint() calls freezing the whole system.
- In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
- some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
- If the distance to the target it too large to ignore,
- but the distance to the new contact point is short enough to be ignored,
- we will calculate a new contact point each update loop, but will never move to it.
- The system will freeze.
- ralf
-
- //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
- float bothObjectSize = i_target->GetObjectBoundingRadius() + owner.GetObjectBoundingRadius() + CONTACT_DISTANCE;
- if ( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x, y, z) < bothObjectSize )
- return;
- */
+ bool result = i_path->CalculatePath(x, y, z, forceDest);
+ if (!result || (i_path->GetPathType() & PATHFIND_NOPATH))
+ {
+ // Cant reach target
+ i_recalculateTravel = true;
+ return;
+ }
D::_addUnitStateMove(owner);
i_targetReached = false;
i_recalculateTravel = false;
+ owner->AddUnitState(UNIT_STATE_CHASE);
Movement::MoveSplineInit init(owner);
- init.MoveTo(x, y, z);
+ init.MovebyPath(i_path->GetPath());
init.SetWalk(((D*)this)->EnableWalking());
// Using the same condition for facing target as the one that is used for SetInFront on movement end
// - applies to ChaseMovementGenerator mostly
@@ -118,52 +116,26 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T &owner)
init.Launch();
}
-template<>
-void TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/)
-{
- // nothing to do for Player
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/)
-{
- // nothing to do for Player
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance)
-{
- i_offset = fDistance;
- i_recalculateTravel = true;
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance)
-{
- i_offset = fDistance;
- i_recalculateTravel = true;
-}
-
template<class T, typename D>
-bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T &owner, uint32 time_diff)
+bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return false;
- if (!owner.isAlive())
- return true;
+ if (!owner || !owner->isAlive())
+ return false;
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
{
D::_clearUnitStateMove(owner);
return true;
}
// prevent movement while casting spells with cast time or channel time
- if (owner.HasUnitState(UNIT_STATE_CASTING))
+ if (owner->HasUnitState(UNIT_STATE_CASTING))
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
return true;
}
@@ -174,22 +146,29 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T &owner, uint32 time_diff)
return true;
}
+ bool targetMoved = false;
i_recheckDistance.Update(time_diff);
if (i_recheckDistance.Passed())
{
- i_recheckDistance.Reset(50);
+ i_recheckDistance.Reset(100);
//More distance let have better performance, less distance let have more sensitive reaction at target move.
- float allowed_dist = i_target->GetObjectSize() + owner.GetObjectSize() + MELEE_RANGE - 0.5f;
- float dist = (owner.movespline->FinalDestination() - G3D::Vector3(i_target->GetPositionX(), i_target->GetPositionY(), i_target->GetPositionZ())).squaredLength();
- if (dist >= allowed_dist * allowed_dist)
- _setTargetLocation(owner);
+ float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+ G3D::Vector3 dest = owner->movespline->FinalDestination();
+
+ if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly())
+ targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist);
+ else
+ targetMoved = !i_target->IsWithinDist2d(dest.x, dest.y, allowed_dist);
}
- if (owner.movespline->Finalized())
+ if (i_recalculateTravel || targetMoved)
+ _setTargetLocation(owner, targetMoved);
+
+ if (owner->movespline->Finalized())
{
static_cast<D*>(this)->MovementInform(owner);
- if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget()))
- owner.SetInFront(i_target.getTarget());
+ if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
+ owner->SetInFront(i_target.getTarget());
if (!i_targetReached)
{
@@ -197,60 +176,56 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T &owner, uint32 time_diff)
static_cast<D*>(this)->_reachTarget(owner);
}
}
- else
- {
- if (i_recalculateTravel)
- _setTargetLocation(owner);
- }
+
return true;
}
//-----------------------------------------------//
template<class T>
-void ChaseMovementGenerator<T>::_reachTarget(T &owner)
+void ChaseMovementGenerator<T>::_reachTarget(T* owner)
{
- if (owner.IsWithinMeleeRange(this->i_target.getTarget()))
- owner.Attack(this->i_target.getTarget(), true);
+ if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
+ owner->Attack(this->i_target.getTarget(),true);
}
template<>
-void ChaseMovementGenerator<Player>::DoInitialize(Player &owner)
+void ChaseMovementGenerator<Player>::DoInitialize(Player* owner)
{
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
- _setTargetLocation(owner);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ _setTargetLocation(owner, true);
}
template<>
-void ChaseMovementGenerator<Creature>::DoInitialize(Creature &owner)
+void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
- owner.SetWalk(false);
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
- _setTargetLocation(owner);
+ owner->SetWalk(false);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ _setTargetLocation(owner, true);
}
template<class T>
-void ChaseMovementGenerator<T>::DoFinalize(T &owner)
+void ChaseMovementGenerator<T>::DoFinalize(T* owner)
{
- owner.ClearUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
}
template<class T>
-void ChaseMovementGenerator<T>::DoReset(T &owner)
+void ChaseMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
-void ChaseMovementGenerator<T>::MovementInform(T & /*unit*/)
+void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
-void ChaseMovementGenerator<Creature>::MovementInform(Creature &unit)
+void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+ if (unit->AI())
+ unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
@@ -267,85 +242,86 @@ bool FollowMovementGenerator<Player>::EnableWalking() const
}
template<>
-void FollowMovementGenerator<Player>::_updateSpeed(Player &/*u*/)
+void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/)
{
// nothing to do for Player
}
template<>
-void FollowMovementGenerator<Creature>::_updateSpeed(Creature &u)
+void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
{
// pet only sync speed with owner
- if (!((Creature&)u).isPet() || !i_target.isValid() || i_target->GetGUID() != u.GetOwnerGUID())
+ /// Make sure we are not in the process of a map change (IsInWorld)
+ if (!owner->isPet() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
return;
- u.UpdateSpeed(MOVE_RUN, true);
- u.UpdateSpeed(MOVE_WALK, true);
- u.UpdateSpeed(MOVE_SWIM, true);
+ owner->UpdateSpeed(MOVE_RUN, true);
+ owner->UpdateSpeed(MOVE_WALK, true);
+ owner->UpdateSpeed(MOVE_SWIM, true);
}
template<>
-void FollowMovementGenerator<Player>::DoInitialize(Player &owner)
+void FollowMovementGenerator<Player>::DoInitialize(Player* owner)
{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
- _setTargetLocation(owner);
+ _setTargetLocation(owner, true);
}
template<>
-void FollowMovementGenerator<Creature>::DoInitialize(Creature &owner)
+void FollowMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
- _setTargetLocation(owner);
+ _setTargetLocation(owner, true);
}
template<class T>
-void FollowMovementGenerator<T>::DoFinalize(T &owner)
+void FollowMovementGenerator<T>::DoFinalize(T* owner)
{
- owner.ClearUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
}
template<class T>
-void FollowMovementGenerator<T>::DoReset(T &owner)
+void FollowMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
-void FollowMovementGenerator<T>::MovementInform(T & /*unit*/)
+void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
-void FollowMovementGenerator<Creature>::MovementInform(Creature &unit)
+void FollowMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+ if (unit->AI())
+ unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
-template void TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template void TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template bool TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::DoUpdate(Player &, uint32);
-template bool TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::DoUpdate(Player &, uint32);
-template bool TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::DoUpdate(Creature &, uint32);
-template bool TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::DoUpdate(Creature &, uint32);
-
-template void ChaseMovementGenerator<Player>::_reachTarget(Player &);
-template void ChaseMovementGenerator<Creature>::_reachTarget(Creature &);
-template void ChaseMovementGenerator<Player>::DoFinalize(Player &);
-template void ChaseMovementGenerator<Creature>::DoFinalize(Creature &);
-template void ChaseMovementGenerator<Player>::DoReset(Player &);
-template void ChaseMovementGenerator<Creature>::DoReset(Creature &);
-template void ChaseMovementGenerator<Player>::MovementInform(Player &unit);
-
-template void FollowMovementGenerator<Player>::DoFinalize(Player &);
-template void FollowMovementGenerator<Creature>::DoFinalize(Creature &);
-template void FollowMovementGenerator<Player>::DoReset(Player &);
-template void FollowMovementGenerator<Creature>::DoReset(Creature &);
-template void FollowMovementGenerator<Player>::MovementInform(Player &unit);
+template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool);
+template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool);
+template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool);
+template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool);
+template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32);
+template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32);
+template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
+template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
+
+template void ChaseMovementGenerator<Player>::_reachTarget(Player*);
+template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*);
+template void ChaseMovementGenerator<Player>::DoFinalize(Player*);
+template void ChaseMovementGenerator<Creature>::DoFinalize(Creature*);
+template void ChaseMovementGenerator<Player>::DoReset(Player*);
+template void ChaseMovementGenerator<Creature>::DoReset(Creature*);
+template void ChaseMovementGenerator<Player>::MovementInform(Player*);
+
+template void FollowMovementGenerator<Player>::DoFinalize(Player*);
+template void FollowMovementGenerator<Creature>::DoFinalize(Creature*);
+template void FollowMovementGenerator<Player>::DoReset(Player*);
+template void FollowMovementGenerator<Creature>::DoReset(Creature*);
+template void FollowMovementGenerator<Player>::MovementInform(Player*);
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
index 4105668838d..3edeb348d54 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
@@ -23,11 +23,12 @@
#include "FollowerReference.h"
#include "Timer.h"
#include "Unit.h"
+#include "PathGenerator.h"
class TargetedMovementGeneratorBase
{
public:
- TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); }
+ TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); }
void stopFollowing() { }
protected:
FollowerReference i_target;
@@ -37,24 +38,24 @@ template<class T, typename D>
class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
{
protected:
- TargetedMovementGeneratorMedium(Unit &target, float offset, float angle) :
- TargetedMovementGeneratorBase(target), i_recheckDistance(0),
- i_offset(offset), i_angle(angle),
+ TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) :
+ TargetedMovementGeneratorBase(target), i_path(NULL),
+ i_recheckDistance(0), i_offset(offset), i_angle(angle),
i_recalculateTravel(false), i_targetReached(false)
{
}
- ~TargetedMovementGeneratorMedium() {}
+ ~TargetedMovementGeneratorMedium() { delete i_path; }
public:
- bool DoUpdate(T &, uint32);
+ bool DoUpdate(T*, uint32);
Unit* GetTarget() const { return i_target.getTarget(); }
- void unitSpeedChanged() { i_recalculateTravel=true; }
- void UpdateFinalDistance(float fDistance);
-
+ void unitSpeedChanged() { i_recalculateTravel = true; }
+ bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; }
protected:
- void _setTargetLocation(T &);
+ void _setTargetLocation(T* owner, bool updateDestination);
+ PathGenerator* i_path;
TimeTrackerSmall i_recheckDistance;
float i_offset;
float i_angle;
@@ -66,50 +67,50 @@ template<class T>
class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >
{
public:
- ChaseMovementGenerator(Unit &target)
+ ChaseMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
- ChaseMovementGenerator(Unit &target, float offset, float angle)
+ ChaseMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
~ChaseMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- void MovementInform(T &);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_CHASE_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); }
bool EnableWalking() const { return false;}
- bool _lostTarget(T &u) const { return u.getVictim() != this->GetTarget(); }
- void _reachTarget(T &);
+ bool _lostTarget(T* u) const { return u->getVictim() != this->GetTarget(); }
+ void _reachTarget(T*);
};
template<class T>
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
{
public:
- FollowMovementGenerator(Unit &target)
+ FollowMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){}
- FollowMovementGenerator(Unit &target, float offset, float angle)
+ FollowMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
~FollowMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- void MovementInform(T &);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
bool EnableWalking() const;
- bool _lostTarget(T &) const { return false; }
- void _reachTarget(T &) {}
+ bool _lostTarget(T*) const { return false; }
+ void _reachTarget(T*) {}
private:
- void _updateSpeed(T &u);
+ void _updateSpeed(T* owner);
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index 2add3439575..626039ed2d5 100644..100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -31,64 +31,64 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void WaypointMovementGenerator<Creature>::LoadPath(Creature &creature)
+void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
{
if (!path_id)
- path_id = creature.GetWaypointPath();
+ path_id = creature->GetWaypointPath();
i_path = sWaypointMgr->GetPath(path_id);
if (!i_path)
{
// No movement found for entry
- sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature.GetName().c_str(), creature.GetEntry(), creature.GetGUIDLow(), path_id);
+ sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUIDLow(), path_id);
return;
}
StartMoveNow(creature);
}
-void WaypointMovementGenerator<Creature>::DoInitialize(Creature &creature)
+void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
LoadPath(creature);
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
-void WaypointMovementGenerator<Creature>::DoFinalize(Creature &creature)
+void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
-void WaypointMovementGenerator<Creature>::DoReset(Creature &creature)
+void WaypointMovementGenerator<Creature>::DoReset(Creature* creature)
{
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
StartMoveNow(creature);
}
-void WaypointMovementGenerator<Creature>::OnArrived(Creature& creature)
+void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
{
if (!i_path || i_path->empty())
return;
if (m_isArrivalDone)
return;
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
m_isArrivalDone = true;
if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
{
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature.GetGUID());
- creature.GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, &creature, NULL);
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID());
+ creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL);
}
// Inform script
MovementInform(creature);
- creature.UpdateWaypointID(i_currentNode);
+ creature->UpdateWaypointID(i_currentNode);
Stop(i_path->at(i_currentNode)->delay);
}
-bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
+bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
{
if (!i_path || i_path->empty())
return false;
@@ -99,8 +99,8 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
{
if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
{
- creature.SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature.GetOrientation());
- creature.GetMotionMaster()->Initialize();
+ creature->SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature->GetOrientation());
+ creature->GetMotionMaster()->Initialize();
return false;
}
@@ -111,7 +111,7 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
m_isArrivalDone = false;
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(node->x, node->y, node->z);
@@ -124,19 +124,19 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
return true;
}
-bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff)
+bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff)
{
// Waypoint movement can be switched on/off
// This is quite handy for escort quests and other stuff
- if (creature.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
{
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
// prevent a crash at empty waypoint path.
@@ -150,9 +150,9 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 di
}
else
{
- if (creature.IsStopped())
+ if (creature->IsStopped())
Stop(STOP_TIME_FOR_PLAYER);
- else if (creature.movespline->Finalized())
+ else if (creature->movespline->Finalized())
{
OnArrived(creature);
return StartMove(creature);
@@ -161,13 +161,13 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 di
return true;
}
-void WaypointMovementGenerator<Creature>::MovementInform(Creature &creature)
+void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
{
- if (creature.AI())
- creature.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+ if (creature->AI())
+ creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
}
-bool WaypointMovementGenerator<Creature>::GetResetPosition(Creature&, float& x, float& y, float& z)
+bool WaypointMovementGenerator<Creature>::GetResetPos(Creature*, float& x, float& y, float& z)
{
// prevent a crash at empty waypoint path.
if (!i_path || i_path->empty())
@@ -196,37 +196,37 @@ uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
return i_path->size();
}
-void FlightPathMovementGenerator::DoInitialize(Player &player)
+void FlightPathMovementGenerator::DoInitialize(Player* player)
{
- DoReset(player);
+ Reset(player);
InitEndGridInfo();
}
-void FlightPathMovementGenerator::DoFinalize(Player& player)
+void FlightPathMovementGenerator::DoFinalize(Player* player)
{
// remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
- player.ClearUnitState(UNIT_STATE_IN_FLIGHT);
+ player->ClearUnitState(UNIT_STATE_IN_FLIGHT);
- player.Dismount();
- player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->Dismount();
+ player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
- if (player.m_taxi.empty())
+ if (player->m_taxi.empty())
{
- player.getHostileRefManager().setOnlineOfflineState(true);
+ player->getHostileRefManager().setOnlineOfflineState(true);
// update z position to ground and orientation for landing point
// this prevent cheating with landing point at lags
// when client side flight end early in comparison server side
- player.StopMoving();
+ player->StopMoving();
}
}
#define PLAYER_FLIGHT_SPEED 32.0f
-void FlightPathMovementGenerator::DoReset(Player & player)
+void FlightPathMovementGenerator::DoReset(Player* player)
{
- player.getHostileRefManager().setOnlineOfflineState(false);
- player.AddUnitState(UNIT_STATE_IN_FLIGHT);
- player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->getHostileRefManager().setOnlineOfflineState(false);
+ player->AddUnitState(UNIT_STATE_IN_FLIGHT);
+ player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
Movement::MoveSplineInit init(player);
uint32 end = GetPathAtMapEnd();
@@ -243,9 +243,9 @@ void FlightPathMovementGenerator::DoReset(Player & player)
init.Launch();
}
-bool FlightPathMovementGenerator::DoUpdate(Player &player, uint32 /*diff*/)
+bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/)
{
- uint32 pointId = (uint32)player.movespline->currentPathIdx();
+ uint32 pointId = (uint32)player->movespline->currentPathIdx();
if (pointId > i_currentNode)
{
bool departureEvent = true;
@@ -281,16 +281,16 @@ void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
}
}
-void FlightPathMovementGenerator::DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure)
+void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure)
{
if (uint32 eventid = departure ? node.departureEventID : node.arrivalEventID)
{
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player.GetName().c_str());
- player.GetMap()->ScriptsStart(sEventScripts, eventid, &player, &player);
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player->GetName().c_str());
+ player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player);
}
}
-bool FlightPathMovementGenerator::GetResetPosition(Player&, float& x, float& y, float& z)
+bool FlightPathMovementGenerator::GetResetPos(Player*, float& x, float& y, float& z)
{
const TaxiPathNodeEntry& node = (*i_path)[i_currentNode];
x = node.x; y = node.y; z = node.z;
@@ -322,331 +322,3 @@ void FlightPathMovementGenerator::PreloadEndGrid()
else
sLog->outInfo(LOG_FILTER_GENERAL, "Unable to determine map to preload flightmaster grid");
}
-
-
-//
-// Unique1's ASTAR Pathfinding Code... For future use & reference...
-//
-
-#ifdef __PATHFINDING__
-
-int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
-
-int ShortenASTARRoute(short int *pathlist, int number)
-{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
- short int temppathlist[MAX_PATHLIST_NODES];
- int count = 0;
- // int count2 = 0;
- int temp, temp2;
- int link;
- int upto = 0;
-
- for (temp = number; temp >= 0; temp--)
- {
- qboolean shortened = qfalse;
-
- for (temp2 = 0; temp2 < temp; temp2++)
- {
- for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
- {
- if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
- continue;
-
- //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
- // continue;
-
- //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
- // continue;
-
- if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
- { // Found a shorter route...
- //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
- {
- temppathlist[count] = pathlist[temp2];
- temp = temp2;
- ++count;
- shortened = qtrue;
- }
- }
- }
- }
-
- if (!shortened)
- {
- temppathlist[count] = pathlist[temp];
- ++count;
- }
- }
-
- upto = count;
-
- for (temp = 0; temp < count; temp++)
- {
- pathlist[temp] = temppathlist[upto];
- --upto;
- }
-
- G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
- return count;
-}
-
-/*
-===========================================================================
-CreatePathAStar
-This function uses the A* pathfinding algorithm to determine the
-shortest path between any two nodes.
-It's fairly complex, so I'm not really going to explain it much.
-Look up A* and binary heaps for more info.
-pathlist stores the ideal path between the nodes, in reverse order,
-and the return value is the number of nodes in that path
-===========================================================================
-*/
-int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
-{
- //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
- //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
- short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
- float gcost[MAX_NODES];
- int fcost[MAX_NODES];
- char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
- short int parent[MAX_NODES];
-
- short int numOpen = 0;
- short int atNode, temp, newnode=-1;
- qboolean found = qfalse;
- int count = -1;
- float gc;
- int i, u, v, m;
- vec3_t vec;
-
- //clear out all the arrays
- memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
- memset(fcost, 0, sizeof(int)*MAX_NODES);
- memset(list, 0, sizeof(char)*MAX_NODES);
- memset(parent, 0, sizeof(short int)*MAX_NODES);
- memset(gcost, -1, sizeof(float)*MAX_NODES);
-
- //make sure we have valid data before calculating everything
- if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
- return -1;
-
- openlist[1] = from; //add the starting node to the open list
- ++numOpen;
- gcost[from] = 0; //its f and g costs are obviously 0
- fcost[from] = 0;
-
- while (1)
- {
- if (numOpen != 0) //if there are still items in the open list
- {
- //pop the top item off of the list
- atNode = openlist[1];
- list[atNode] = 2; //put the node on the closed list so we don't check it again
- --numOpen;
-
- openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
- v = 1;
-
- //this while loop reorders the list so that the new lowest fcost is at the top again
- while (1)
- {
- u = v;
- if ((2*u+1) < numOpen) //if both children exist
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
- v = 2*u+1;
- }
- else
- {
- if ((2*u) < numOpen) //if only one child exists
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- }
- }
-
- if (u != v) //if they're out of order, swap this item with its parent
- {
- temp = openlist[u];
- openlist[u] = openlist[v];
- openlist[v] = temp;
- }
- else
- break;
- }
-
- for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node
- {
- newnode = nodes[atNode].links[i].targetNode;
-
- //if this path is blocked, skip it
- if (nodes[atNode].links[i].flags & PATH_BLOCKED)
- continue;
- //if this path is blocked, skip it
- if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
- continue;
- //skip any unreachable nodes
- if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
- continue;
- if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
- continue;
-
- if (list[newnode] == 2) //if this node is on the closed list, skip it
- continue;
-
- if (list[newnode] != 1) //if this node is not already on the open list
- {
- openlist[++numOpen] = newnode; //add the new node to the open list
- list[newnode] = 1;
- parent[newnode] = atNode; //record the node's parent
-
- if (newnode == to) //if we've found the goal, don't keep computing paths!
- break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
-
- //store it's f cost value
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //this loop re-orders the heap so that the lowest fcost is at the top
- m = numOpen;
- while (m != 1) //while this item isn't at the top of the heap already
- {
- //if it has a lower fcost than its parent
- if (fcost[openlist[m]] <= fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- }
- else //if this node is already on the open list
- {
- gc = gcost[atNode];
- VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
- gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
-
- if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
- {
- parent[newnode] = atNode; //set the new parent for this node
- gcost[newnode] = gc; //and the new g cost
-
- for (i = 1; i < numOpen; ++i) //loop through all the items on the open list
- {
- if (openlist[i] == newnode) //find this node in the list
- {
- //calculate the new fcost and store it
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //reorder the list again, with the lowest fcost item on top
- m = i;
- while (m != 1)
- {
- //if the item has a lower fcost than it's parent
- if (fcost[openlist[m]] < fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- break; //exit the 'for' loop because we already changed this node
- } //if
- } //for
- } //if (gc < gcost[newnode])
- } //if (list[newnode] != 1) --> else
- } //for (loop through links)
- } //if (numOpen != 0)
- else
- {
- found = qfalse; //there is no path between these nodes
- break;
- }
-
- if (list[to] == 1) //if the destination node is on the open list, we're done
- {
- found = qtrue;
- break;
- }
- } //while (1)
-
- if (found == qtrue) //if we found a path
- {
- //G_Printf("%s - path found!n", bot->client->pers.netname);
- count = 0;
-
- temp = to; //start at the end point
- while (temp != from) //travel along the path (backwards) until we reach the starting point
- {
- pathlist[count++] = temp; //add the node to the pathlist and increment the count
- temp = parent[temp]; //move to the parent of this node to continue the path
- }
-
- pathlist[count++] = from; //add the beginning node to the end of the pathlist
-
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- }
- else
- {
- //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
- count = CreateDumbRoute(from, to, pathlist);
-
- if (count > 0)
- {
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- return count;
- }
- }
-
- return count; //return the number of nodes in the path, -1 if not found
-}
-
-/*
-===========================================================================
-GetFCost
-Utility function used by A* pathfinding to calculate the
-cost to move between nodes towards a goal. Using the A*
-algorithm F = G + H, G here is the distance along the node
-paths the bot must travel, and H is the straight-line distance
-to the goal node.
-Returned as an int because more precision is unnecessary and it
-will slightly speed up heap access
-===========================================================================
-*/
-int GetFCost(int to, int num, int parentNum, float *gcost)
-{
- float gc = 0;
- float hc = 0;
- vec3_t v;
-
- if (gcost[num] == -1)
- {
- if (parentNum != -1)
- {
- gc = gcost[parentNum];
- VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
- gc += VectorLength(v);
- }
- gcost[num] = gc;
- }
- else
- gc = gcost[num];
-
- VectorSubtract(nodes[to].origin, nodes[num].origin, v);
- hc = VectorLength(v);
-
- return (int)(gc + hc);
-}
-#endif //__PATHFINDING__
-
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
index 319a5c66a03..72650570e12 100644..100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -65,19 +65,19 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
: i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) {}
~WaypointMovementGenerator() { i_path = NULL; }
- void DoInitialize(Creature &);
- void DoFinalize(Creature &);
- void DoReset(Creature &);
- bool DoUpdate(Creature &, uint32 diff);
+ void DoInitialize(Creature*);
+ void DoFinalize(Creature*);
+ void DoReset(Creature*);
+ bool DoUpdate(Creature*, uint32 diff);
- void MovementInform(Creature &);
+ void MovementInform(Creature*);
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
// now path movement implmementation
- void LoadPath(Creature &c);
+ void LoadPath(Creature*);
- bool GetResetPosition(Creature&, float& x, float& y, float& z);
+ bool GetResetPos(Creature*, float& x, float& y, float& z);
private:
@@ -91,10 +91,10 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
return i_nextMoveTime.Passed();
}
- void OnArrived(Creature&);
- bool StartMove(Creature&);
+ void OnArrived(Creature*);
+ bool StartMove(Creature*);
- void StartMoveNow(Creature& creature)
+ void StartMoveNow(Creature* creature)
{
i_nextMoveTime.Reset(0);
StartMove(creature);
@@ -118,10 +118,10 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
i_path = &pathnodes;
i_currentNode = startNode;
}
- void DoInitialize(Player &);
- void DoReset(Player &);
- void DoFinalize(Player &);
- bool DoUpdate(Player &, uint32);
+ void DoInitialize(Player*);
+ void DoReset(Player*);
+ void DoFinalize(Player*);
+ bool DoUpdate(Player*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
TaxiPathNodeList const& GetPath() { return *i_path; }
@@ -129,9 +129,9 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
bool HasArrived() const { return (i_currentNode >= i_path->size()); }
void SetCurrentNodeAfterTeleport();
void SkipCurrentNode() { ++i_currentNode; }
- void DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure);
+ void DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure);
- bool GetResetPosition(Player&, float& x, float& y, float& z);
+ bool GetResetPos(Player*, float& x, float& y, float& z);
void InitEndGridInfo();
void PreloadEndGrid();
@@ -143,4 +143,3 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
uint32 _preloadTargetNode; //! node index where preloading starts
};
#endif
-
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
new file mode 100644
index 00000000000..dbda4aa2411
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -0,0 +1,803 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathGenerator.h"
+#include "Map.h"
+#include "Creature.h"
+#include "MMapFactory.h"
+#include "MMapManager.h"
+#include "Log.h"
+
+#include "DetourCommon.h"
+#include "DetourNavMeshQuery.h"
+
+////////////////// PathGenerator //////////////////
+PathGenerator::PathGenerator(const Unit* owner) :
+ _polyLength(0), _type(PATHFIND_BLANK),
+ _useStraightPath(false), _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH),
+ _endPosition(Vector3::zero()), _sourceUnit(owner), _navMesh(NULL), _navMeshQuery(NULL)
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow());
+
+ uint32 mapId = _sourceUnit->GetMapId();
+ if (MMAP::MMapFactory::IsPathfindingEnabled(mapId))
+ {
+ MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager();
+ _navMesh = mmap->GetNavMesh(mapId);
+ _navMeshQuery = mmap->GetNavMeshQuery(mapId, _sourceUnit->GetInstanceId());
+ }
+
+ CreateFilter();
+}
+
+PathGenerator::~PathGenerator()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow());
+}
+
+bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest)
+{
+ float x, y, z;
+ _sourceUnit->GetPosition(x, y, z);
+
+ if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z))
+ return false;
+
+ Vector3 dest(destX, destY, destZ);
+ SetEndPosition(dest);
+
+ Vector3 start(x, y, z);
+ SetStartPosition(start);
+
+ _forceDestination = forceDest;
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow());
+
+ // make sure navMesh works - we can run on map w/o mmap
+ // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
+ if (!_navMesh || !_navMeshQuery || _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) ||
+ !HaveTile(start) || !HaveTile(dest))
+ {
+ BuildShortcut();
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return true;
+ }
+
+ UpdateFilter();
+
+ BuildPolyPath(start, dest);
+ return true;
+}
+
+dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* point, float* distance) const
+{
+ if (!polyPath || !polyPathSize)
+ return INVALID_POLYREF;
+
+ dtPolyRef nearestPoly = INVALID_POLYREF;
+ float minDist2d = FLT_MAX;
+ float minDist3d = 0.0f;
+
+ for (uint32 i = 0; i < polyPathSize; ++i)
+ {
+ float closestPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint))
+ continue;
+
+ float d = dtVdist2DSqr(point, closestPoint);
+ if (d < minDist2d)
+ {
+ minDist2d = d;
+ nearestPoly = polyPath[i];
+ minDist3d = dtVdistSqr(point, closestPoint);
+ }
+
+ if (minDist2d < 1.0f) // shortcut out - close enough for us
+ break;
+ }
+
+ if (distance)
+ *distance = dtSqrt(minDist3d);
+
+ return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
+}
+
+dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) const
+{
+ // first we check the current path
+ // if the current path doesn't contain the current poly,
+ // we need to use the expensive navMesh.findNearestPoly
+ dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance);
+ if (polyRef != INVALID_POLYREF)
+ return polyRef;
+
+ // we don't have it in our old path
+ // try to get it by findNearestPoly()
+ // first try with low search box
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area
+ float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f};
+ dtStatus result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ // still nothing ..
+ // try with bigger search box
+ extents[1] = 200.0f;
+ result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ return INVALID_POLYREF;
+}
+
+void PathGenerator::BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos)
+{
+ // *** getting start/end poly logic ***
+
+ float distToStartPoly, distToEndPoly;
+ float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x};
+ float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x};
+
+ dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
+ dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
+
+ // we have a hole in our mesh
+ // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
+ // its up to caller how he will use this info
+ if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n");
+ BuildShortcut();
+ bool path = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly();
+
+ bool waterPath = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->canSwim();
+ if (waterPath)
+ {
+ // Check both start and end points, if they're both in water, then we can *safely* let the creature move
+ for (uint32 i = 0; i < _pathPoints.size(); ++i)
+ {
+ ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL);
+ // One of the points is not in the water, cancel movement.
+ if (status == LIQUID_MAP_NO_WATER)
+ {
+ waterPath = false;
+ break;
+ }
+ }
+ }
+
+ _type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH;
+ return;
+ }
+
+ // we may need a better number here
+ bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);
+ if (farFromPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly);
+
+ bool buildShotrcut = false;
+ if (_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* owner = (Creature*)_sourceUnit;
+
+ Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos;
+ if (_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z))
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: underWater case\n");
+ if (owner->canSwim())
+ buildShotrcut = true;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: flying case\n");
+ if (owner->CanFly())
+ buildShotrcut = true;
+ }
+ }
+
+ if (buildShotrcut)
+ {
+ BuildShortcut();
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return;
+ }
+ else
+ {
+ float closestPoint[VERTEX_SIZE];
+ // we may want to use closestPointOnPolyBoundary instead
+ if (DT_SUCCESS == _navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint))
+ {
+ dtVcopy(endPoint, closestPoint);
+ SetActualEndPosition(Vector3(endPoint[2], endPoint[0], endPoint[1]));
+ }
+
+ _type = PATHFIND_INCOMPLETE;
+ }
+ }
+
+ // *** poly path generating logic ***
+
+ // start and end are on same polygon
+ // just need to move in straight line
+ if (startPoly == endPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == endPoly)\n");
+
+ BuildShortcut();
+
+ _pathPolyRefs[0] = startPoly;
+ _polyLength = 1;
+
+ _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL;
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: path type %d\n", _type);
+ return;
+ }
+
+ // look for startPoly/endPoly in current path
+ // TODO: we can merge it with getPathPolyByPosition() loop
+ bool startPolyFound = false;
+ bool endPolyFound = false;
+ uint32 pathStartIndex = 0;
+ uint32 pathEndIndex = 0;
+
+ if (_polyLength)
+ {
+ for (; pathStartIndex < _polyLength; ++pathStartIndex)
+ {
+ // here to carch few bugs
+ ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
+
+ if (_pathPolyRefs[pathStartIndex] == startPoly)
+ {
+ startPolyFound = true;
+ break;
+ }
+ }
+
+ for (pathEndIndex = _polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex)
+ if (_pathPolyRefs[pathEndIndex] == endPoly)
+ {
+ endPolyFound = true;
+ break;
+ }
+ }
+
+ if (startPolyFound && endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n");
+
+ // we moved along the path and the target did not move out of our old poly-path
+ // our path is a simple subpath case, we have all the data we need
+ // just "cut" it out
+
+ _polyLength = pathEndIndex - pathStartIndex + 1;
+ memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
+ }
+ else if (startPolyFound && !endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n");
+
+ // we are moving on the old path but target moved out
+ // so we have atleast part of poly-path ready
+
+ _polyLength -= pathStartIndex;
+
+ // try to adjust the suffix of the path instead of recalculating entire length
+ // at given interval the target cannot get too far from its last location
+ // thus we have less poly to cover
+ // sub-path of optimal path is optimal
+
+ // take ~80% of the original length
+ // TODO : play with the values here
+ uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
+ memmove(_pathPolyRefs, _pathPolyRefs+pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
+
+ dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
+
+ // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
+ float suffixEndPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
+ // try to recover by using prev polyref
+ --prefixPolyLength;
+ suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // suffixStartPoly is still invalid, error state
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // generate suffix
+ uint32 suffixPolyLength = 0;
+ dtStatus dtResult = _navMeshQuery->findPath(
+ suffixStartPoly, // start polygon
+ endPoly, // end polygon
+ suffixEndPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs + prefixPolyLength - 1, // [out] path
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path
+
+ if (!suffixPolyLength || dtResult != DT_SUCCESS)
+ {
+ // this is probably an error state, but we'll leave it
+ // and hopefully recover on the next Update
+ // we still need to copy our preffix
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength);
+
+ // new path = prefix + suffix - overlap
+ _polyLength = prefixPolyLength + suffixPolyLength - 1;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n");
+
+ // either we have no path at all -> first run
+ // or something went really wrong -> we aren't moving along the path to the target
+ // just generate new path
+
+ // free and invalidate old path data
+ Clear();
+
+ dtStatus dtResult = _navMeshQuery->findPath(
+ startPoly, // start polygon
+ endPoly, // end polygon
+ startPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs, // [out] path
+ (int*)&_polyLength,
+ MAX_PATH_LENGTH); // max number of polygons in output path
+
+ if (!_polyLength || dtResult != DT_SUCCESS)
+ {
+ // only happens if we passed bad data to findPath(), or navmesh is messed up
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // by now we know what type of path we can get
+ if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
+ _type = PATHFIND_NORMAL;
+ else
+ _type = PATHFIND_INCOMPLETE;
+
+ // generate the point-path out of our up-to-date poly-path
+ BuildPointPath(startPoint, endPoint);
+}
+
+void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoint)
+{
+ float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE];
+ uint32 pointCount = 0;
+ dtStatus dtResult = DT_FAILURE;
+ if (_useStraightPath)
+ {
+ dtResult = _navMeshQuery->findStraightPath(
+ startPoint, // start position
+ endPoint, // end position
+ _pathPolyRefs, // current path
+ _polyLength, // lenth of current path
+ pathPoints, // [out] path corner points
+ NULL, // [out] flags
+ NULL, // [out] shortened path
+ (int*)&pointCount,
+ _pointPathLimit); // maximum number of points/polygons to use
+ }
+ else
+ {
+ dtResult = FindSmoothPath(
+ startPoint, // start position
+ endPoint, // end position
+ _pathPolyRefs, // current path
+ _polyLength, // length of current path
+ pathPoints, // [out] path corner points
+ (int*)&pointCount,
+ _pointPathLimit); // maximum number of points
+ }
+
+ if (pointCount < 2 || dtResult != DT_SUCCESS)
+ {
+ // only happens if pass bad data to findStraightPath or navmesh is broken
+ // single point paths can be generated here
+ // TODO : check the exact cases
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount);
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ else if (pointCount == _pointPathLimit)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d\n", pointCount, _pointPathLimit);
+ BuildShortcut();
+ _type = PATHFIND_SHORT;
+ return;
+ }
+
+ _pathPoints.resize(pointCount);
+ for (uint32 i = 0; i < pointCount; ++i)
+ _pathPoints[i] = Vector3(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1]);
+
+ NormalizePath();
+
+ // first point is always our current location - we need the next one
+ SetActualEndPosition(_pathPoints[pointCount-1]);
+
+ // force the given destination, if needed
+ if (_forceDestination &&
+ (!(_type & PATHFIND_NORMAL) || !InRange(GetEndPosition(), GetActualEndPosition(), 1.0f, 1.0f)))
+ {
+ // we may want to keep partial subpath
+ if (Dist3DSqr(GetActualEndPosition(), GetEndPosition()) < 0.3f * Dist3DSqr(GetStartPosition(), GetEndPosition()))
+ {
+ SetActualEndPosition(GetEndPosition());
+ _pathPoints[_pathPoints.size()-1] = GetEndPosition();
+ }
+ else
+ {
+ SetActualEndPosition(GetEndPosition());
+ BuildShortcut();
+ }
+
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", _type, pointCount, _polyLength);
+}
+
+void PathGenerator::NormalizePath()
+{
+ for (uint32 i = 0; i < _pathPoints.size(); ++i)
+ _sourceUnit->UpdateAllowedPositionZ(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z);
+}
+
+void PathGenerator::BuildShortcut()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildShortcut :: making shortcut\n");
+
+ Clear();
+
+ // make two point path, our curr pos is the start, and dest is the end
+ _pathPoints.resize(2);
+
+ // set start and a default next position
+ _pathPoints[0] = GetStartPosition();
+ _pathPoints[1] = GetActualEndPosition();
+
+ NormalizePath();
+
+ _type = PATHFIND_SHORTCUT;
+}
+
+void PathGenerator::CreateFilter()
+{
+ uint16 includeFlags = 0;
+ uint16 excludeFlags = 0;
+
+ if (_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* creature = (Creature*)_sourceUnit;
+ if (creature->canWalk())
+ includeFlags |= NAV_GROUND; // walk
+
+ // creatures don't take environmental damage
+ if (creature->canSwim())
+ includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim
+ }
+ else // assume Player
+ {
+ // perfect support not possible, just stay 'safe'
+ includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA | NAV_SLIME);
+ }
+
+ _filter.setIncludeFlags(includeFlags);
+ _filter.setExcludeFlags(excludeFlags);
+
+ UpdateFilter();
+}
+
+void PathGenerator::UpdateFilter()
+{
+ // allow creatures to cheat and use different movement types if they are moved
+ // forcefully into terrain they can't normally move in
+ if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
+ {
+ uint16 includedFlags = _filter.getIncludeFlags();
+ includedFlags |= GetNavTerrain(_sourceUnit->GetPositionX(),
+ _sourceUnit->GetPositionY(),
+ _sourceUnit->GetPositionZ());
+
+ _filter.setIncludeFlags(includedFlags);
+ }
+}
+
+NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z)
+{
+ LiquidData data;
+ _sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
+
+ switch (data.type_flags)
+ {
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ return NAV_WATER;
+ case MAP_LIQUID_TYPE_MAGMA:
+ return NAV_MAGMA;
+ case MAP_LIQUID_TYPE_SLIME:
+ return NAV_SLIME;
+ default:
+ return NAV_GROUND;
+ }
+}
+
+bool PathGenerator::HaveTile(const Vector3& p) const
+{
+ int tx = -1, ty = -1;
+ float point[VERTEX_SIZE] = {p.y, p.z, p.x};
+
+ _navMesh->calcTileLoc(point, &tx, &ty);
+
+ /// Workaround
+ /// For some reason, often the tx and ty variables wont get a valid value
+ /// Use this check to prevent getting negative tile coords and crashing on getTileAt
+ if (tx < 0 || ty < 0)
+ return false;
+
+ return (_navMesh->getTileAt(tx, ty) != NULL);
+}
+
+uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited)
+{
+ int32 furthestPath = -1;
+ int32 furthestVisited = -1;
+
+ // Find furthest common polygon.
+ for (int32 i = npath-1; i >= 0; --i)
+ {
+ bool found = false;
+ for (int32 j = nvisited-1; j >= 0; --j)
+ {
+ if (path[i] == visited[j])
+ {
+ furthestPath = i;
+ furthestVisited = j;
+ found = true;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ // If no intersection found just return current path.
+ if (furthestPath == -1 || furthestVisited == -1)
+ return npath;
+
+ // Concatenate paths.
+
+ // Adjust beginning of the buffer to include the visited.
+ uint32 req = nvisited - furthestVisited;
+ uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
+ uint32 size = npath > orig ? npath - orig : 0;
+ if (req + size > maxPath)
+ size = maxPath-req;
+
+ if (size)
+ memmove(path + req, path + orig, size * sizeof(dtPolyRef));
+
+ // Store visited
+ for (uint32 i = 0; i < req; ++i)
+ path[i] = visited[(nvisited - 1) - i];
+
+ return req+size;
+}
+
+bool PathGenerator::GetSteerTarget(float const* startPos, float const* endPos,
+ float minTargetDist, dtPolyRef const* path, uint32 pathSize,
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef)
+{
+ // Find steer target.
+ static const uint32 MAX_STEER_POINTS = 3;
+ float steerPath[MAX_STEER_POINTS*VERTEX_SIZE];
+ unsigned char steerPathFlags[MAX_STEER_POINTS];
+ dtPolyRef steerPathPolys[MAX_STEER_POINTS];
+ uint32 nsteerPath = 0;
+ dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
+ steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
+ if (!nsteerPath || DT_SUCCESS != dtResult)
+ return false;
+
+ // Find vertex far enough to steer to.
+ uint32 ns = 0;
+ while (ns < nsteerPath)
+ {
+ // Stop at Off-Mesh link or when point is further than slop away.
+ if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
+ !InRangeYZX(&steerPath[ns*VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
+ break;
+ ns++;
+ }
+ // Failed to find good point to steer to.
+ if (ns >= nsteerPath)
+ return false;
+
+ dtVcopy(steerPos, &steerPath[ns*VERTEX_SIZE]);
+ steerPos[1] = startPos[1]; // keep Z value
+ steerPosFlag = steerPathFlags[ns];
+ steerPosRef = steerPathPolys[ns];
+
+ return true;
+}
+
+dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPos,
+ dtPolyRef const* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize)
+{
+ *smoothPathSize = 0;
+ uint32 nsmoothPath = 0;
+
+ dtPolyRef polys[MAX_PATH_LENGTH];
+ memcpy(polys, polyPath, sizeof(dtPolyRef)*polyPathSize);
+ uint32 npolys = polyPathSize;
+
+ float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))
+ return DT_FAILURE;
+
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPolyBoundary(polys[npolys-1], endPos, targetPos))
+ return DT_FAILURE;
+
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+
+ // Move towards target a small advancement at a time until target reached or
+ // when ran out of memory to store the path.
+ while (npolys && nsmoothPath < maxSmoothPathSize)
+ {
+ // Find location to steer towards.
+ float steerPos[VERTEX_SIZE];
+ unsigned char steerPosFlag;
+ dtPolyRef steerPosRef = INVALID_POLYREF;
+
+ if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
+ break;
+
+ bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END);
+ bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);
+
+ // Find movement delta.
+ float delta[VERTEX_SIZE];
+ dtVsub(delta, steerPos, iterPos);
+ float len = dtSqrt(dtVdot(delta,delta));
+ // If the steer target is end of path or off-mesh link, do not move past the location.
+ if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
+ len = 1.0f;
+ else
+ len = SMOOTH_PATH_STEP_SIZE / len;
+
+ float moveTgt[VERTEX_SIZE];
+ dtVmad(moveTgt, iterPos, delta, len);
+
+ // Move
+ float result[VERTEX_SIZE];
+ const static uint32 MAX_VISIT_POLY = 16;
+ dtPolyRef visited[MAX_VISIT_POLY];
+
+ uint32 nvisited = 0;
+ _navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY);
+ npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
+
+ _navMeshQuery->getPolyHeight(polys[0], result, &result[1]);
+ result[1] += 0.5f;
+ dtVcopy(iterPos, result);
+
+ // Handle end of path and off-mesh links when close enough.
+ if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Reached end of path.
+ dtVcopy(iterPos, targetPos);
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ break;
+ }
+ else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Advance the path up to and over the off-mesh connection.
+ dtPolyRef prevRef = INVALID_POLYREF;
+ dtPolyRef polyRef = polys[0];
+ uint32 npos = 0;
+ while (npos < npolys && polyRef != steerPosRef)
+ {
+ prevRef = polyRef;
+ polyRef = polys[npos];
+ npos++;
+ }
+
+ for (uint32 i = npos; i < npolys; ++i)
+ polys[i-npos] = polys[i];
+
+ npolys -= npos;
+
+ // Handle the connection.
+ float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE];
+ if (DT_SUCCESS == _navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos))
+ {
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], startPos);
+ nsmoothPath++;
+ }
+ // Move position at the other side of the off-mesh link.
+ dtVcopy(iterPos, endPos);
+ _navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]);
+ iterPos[1] += 0.5f;
+ }
+ }
+
+ // Store results.
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ }
+
+ *smoothPathSize = nsmoothPath;
+
+ // this is most likely a loop
+ return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
+}
+
+bool PathGenerator::InRangeYZX(const float* v1, const float* v2, float r, float h) const
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1]; // elevation
+ const float dz = v2[2] - v1[2];
+ return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
+}
+
+bool PathGenerator::InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const
+{
+ Vector3 d = p1 - p2;
+ return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
+}
+
+float PathGenerator::Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const
+{
+ return (p1 - p2).squaredLength();
+}
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
new file mode 100644
index 00000000000..a20f900b584
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PATH_GENERATOR_H
+#define _PATH_GENERATOR_H
+
+#include "SharedDefines.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "MoveSplineInitArgs.h"
+
+using Movement::Vector3;
+using Movement::PointsArray;
+
+class Unit;
+
+// 74*4.0f=296y number_of_points*interval = max_path_len
+// this is way more than actual evade range
+// I think we can safely cut those down even more
+#define MAX_PATH_LENGTH 74
+#define MAX_POINT_PATH_LENGTH 74
+
+#define SMOOTH_PATH_STEP_SIZE 4.0f
+#define SMOOTH_PATH_SLOP 0.3f
+
+#define VERTEX_SIZE 3
+#define INVALID_POLYREF 0
+
+enum PathType
+{
+ PATHFIND_BLANK = 0x00, // path not built yet
+ PATHFIND_NORMAL = 0x01, // normal path
+ PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior)
+ PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target
+ PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one
+ PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps
+ PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
+};
+
+class PathGenerator
+{
+ public:
+ PathGenerator(Unit const* owner);
+ ~PathGenerator();
+
+ // Calculate the path from owner to given destination
+ // return: true if new path was calculated, false otherwise (no change needed)
+ bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
+
+ // option setters - use optional
+ void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; };
+ void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); };
+
+ // result getters
+ Vector3 const& GetStartPosition() const { return _startPosition; }
+ Vector3 const& GetEndPosition() const { return _endPosition; }
+ Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
+
+ PointsArray& GetPath() { return _pathPoints; }
+ PathType GetPathType() const { return _type; }
+
+ private:
+
+ dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
+ uint32 _polyLength; // number of polygons in the path
+
+ PointsArray _pathPoints; // our actual (x,y,z) path to the target
+ PathType _type; // tells what kind of path this is
+
+ bool _useStraightPath; // type of path will be generated
+ bool _forceDestination; // when set, we will always arrive at given point
+ uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
+
+ Vector3 _startPosition; // {x, y, z} of current location
+ Vector3 _endPosition; // {x, y, z} of the destination
+ Vector3 _actualEndPosition;// {x, y, z} of the closest possible point to given destination
+
+ Unit const* const _sourceUnit; // the unit that is moving
+ dtNavMesh const* _navMesh; // the nav mesh
+ dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
+
+ dtQueryFilter _filter; // use single filter for all movements, update it when needed
+
+ void SetStartPosition(Vector3 Point) { _startPosition = Point; }
+ void SetEndPosition(Vector3 Point) { _actualEndPosition = Point; _endPosition = Point; }
+ void SetActualEndPosition(Vector3 Point) { _actualEndPosition = Point; }
+ void NormalizePath();
+
+ void Clear()
+ {
+ _polyLength = 0;
+ _pathPoints.clear();
+ }
+
+ bool InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const;
+ float Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const;
+ bool InRangeYZX(float const* v1, float const* v2, float r, float h) const;
+
+ dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const;
+ dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const;
+ bool HaveTile(Vector3 const& p) const;
+
+ void BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos);
+ void BuildPointPath(float const* startPoint, float const* endPoint);
+ void BuildShortcut();
+
+ NavTerrain GetNavTerrain(float x, float y, float z);
+ void CreateFilter();
+ void UpdateFilter();
+
+ // smooth path aux functions
+ uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited);
+ bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos,
+ unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
+ dtStatus FindSmoothPath(float const* startPos, float const* endPos,
+ dtPolyRef const* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
+};
+
+#endif
diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h
index 8e51e2678dd..b18166ea615 100644
--- a/src/server/game/Movement/Spline/MoveSpline.h
+++ b/src/server/game/Movement/Spline/MoveSpline.h
@@ -78,18 +78,17 @@ namespace Movement
UpdateResult _updateState(int32& ms_time_diff);
int32 next_timestamp() const { return spline.length(point_Idx+1); }
int32 segment_time_elapsed() const { return next_timestamp()-time_passed; }
- int32 Duration() const { return spline.length(); }
int32 timeElapsed() const { return Duration() - time_passed; }
int32 timePassed() const { return time_passed; }
public:
+ int32 Duration() const { return spline.length(); }
const MySpline& _Spline() const { return spline; }
int32 _currentSplineIdx() const { return point_Idx; }
void _Finalize();
void _Interrupt() { splineflags.done = true;}
public:
-
void Initialize(const MoveSplineInitArgs&);
bool Initialized() const { return !spline.empty(); }
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp
index 61178f3f9ad..249f25a6353 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.cpp
+++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp
@@ -58,36 +58,36 @@ namespace Movement
return MOVE_RUN;
}
- void MoveSplineInit::Launch()
+ int32 MoveSplineInit::Launch()
{
- MoveSpline& move_spline = *unit.movespline;
+ MoveSpline& move_spline = *unit->movespline;
- Location real_position(unit.GetPositionX(), unit.GetPositionY(), unit.GetPositionZMinusOffset(), unit.GetOrientation());
+ Location real_position(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZMinusOffset(), unit->GetOrientation());
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- if (unit.GetTransGUID())
+ if (unit->GetTransGUID())
{
- real_position.x = unit.GetTransOffsetX();
- real_position.y = unit.GetTransOffsetY();
- real_position.z = unit.GetTransOffsetZ();
- real_position.orientation = unit.GetTransOffsetO();
+ real_position.x = unit->GetTransOffsetX();
+ real_position.y = unit->GetTransOffsetY();
+ real_position.z = unit->GetTransOffsetZ();
+ real_position.orientation = unit->GetTransOffsetO();
}
// there is a big chance that current position is unknown if current state is not finalized, need compute it
// this also allows calculate spline position and update map position in much greater intervals
// Don't compute for transport movement if the unit is in a motion between two transports
- if (!move_spline.Finalized() && move_spline.onTransport == (unit.GetTransGUID() != 0))
+ if (!move_spline.Finalized() && move_spline.onTransport == (unit->GetTransGUID() != 0))
real_position = move_spline.ComputePosition();
// should i do the things that user should do? - no.
if (args.path.empty())
- return;
+ return 0;
// correct first vertex
args.path[0] = real_position;
args.initialOrientation = real_position.orientation;
- move_spline.onTransport = (unit.GetTransGUID() != 0);
+ move_spline.onTransport = (unit->GetTransGUID() != 0);
- uint32 moveFlags = unit.m_movementInfo.GetMovementFlags();
+ uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
if (args.flags.walkmode)
moveFlags |= MOVEMENTFLAG_WALKING;
else
@@ -96,33 +96,35 @@ namespace Movement
moveFlags |= MOVEMENTFLAG_FORWARD;
if (!args.HasVelocity)
- args.velocity = unit.GetSpeed(SelectSpeedType(moveFlags));
+ args.velocity = unit->GetSpeed(SelectSpeedType(moveFlags));
- if (!args.Validate(&unit))
- return;
+ if (!args.Validate(unit))
+ return 0;
if (moveFlags & MOVEMENTFLAG_ROOT)
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
- unit.m_movementInfo.SetMovementFlags(moveFlags);
+ unit->m_movementInfo.SetMovementFlags(moveFlags);
move_spline.Initialize(args);
WorldPacket data(SMSG_MONSTER_MOVE, 64);
- data.append(unit.GetPackGUID());
- if (unit.GetTransGUID())
+ data.append(unit->GetPackGUID());
+ if (unit->GetTransGUID())
{
data.SetOpcode(SMSG_MONSTER_MOVE_TRANSPORT);
- data.appendPackGUID(unit.GetTransGUID());
- data << int8(unit.GetTransSeat());
+ data.appendPackGUID(unit->GetTransGUID());
+ data << int8(unit->GetTransSeat());
}
PacketBuilder::WriteMonsterMove(move_spline, data);
- unit.SendMessageToSet(&data, true);
+ unit->SendMessageToSet(&data, true);
+
+ return move_spline.Duration();
}
void MoveSplineInit::Stop()
{
- MoveSpline& move_spline = *unit.movespline;
+ MoveSpline& move_spline = *unit->movespline;
// No need to stop if we are not moving
if (move_spline.Finalized())
@@ -130,30 +132,30 @@ namespace Movement
Location loc = move_spline.ComputePosition();
args.flags = MoveSplineFlag::Done;
- unit.m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FORWARD);
+ unit->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FORWARD);
move_spline.Initialize(args);
WorldPacket data(SMSG_MONSTER_MOVE, 64);
- data.append(unit.GetPackGUID());
- if (unit.GetTransGUID())
+ data.append(unit->GetPackGUID());
+ if (unit->GetTransGUID())
{
data.SetOpcode(SMSG_MONSTER_MOVE_TRANSPORT);
- data.appendPackGUID(unit.GetTransGUID());
- data << int8(unit.GetTransSeat());
+ data.appendPackGUID(unit->GetTransGUID());
+ data << int8(unit->GetTransSeat());
}
PacketBuilder::WriteStopMovement(loc, args.splineId, data);
- unit.SendMessageToSet(&data, true);
+ unit->SendMessageToSet(&data, true);
}
- MoveSplineInit::MoveSplineInit(Unit& m) : unit(m)
+ MoveSplineInit::MoveSplineInit(Unit* m) : unit(m)
{
args.splineId = splineIdGen.NewId();
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- args.TransformForTransport = unit.GetTransGUID();
+ args.TransformForTransport = unit->GetTransGUID();
// mix existing state into new
- args.flags.walkmode = unit.m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
- args.flags.flying = unit.m_movementInfo.HasMovementFlag(MovementFlags(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY));
+ args.flags.walkmode = unit->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
+ args.flags.flying = unit->m_movementInfo.HasMovementFlag(MovementFlags(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY));
args.flags.smoothGroundPath = true; // enabled by default, CatmullRom mode or client config "pathSmoothing" will disable this
}
@@ -167,9 +169,9 @@ namespace Movement
{
if (args.TransformForTransport)
{
- if (Unit* vehicle = unit.GetVehicleBase())
+ if (Unit* vehicle = unit->GetVehicleBase())
angle -= vehicle->GetOrientation();
- else if (Transport* transport = unit.GetTransport())
+ else if (Transport* transport = unit->GetTransport())
angle -= transport->GetOrientation();
}
@@ -177,8 +179,19 @@ namespace Movement
args.flags.EnableFacingAngle();
}
- void MoveSplineInit::MoveTo(Vector3 const& dest)
+ void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination)
{
+ if (generatePath)
+ {
+ PathGenerator path(unit);
+ bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination);
+ if (result && path.GetPathType() & ~PATHFIND_NOPATH)
+ {
+ MovebyPath(path.GetPath());
+ return;
+ }
+ }
+
args.path_Idx_offset = 0;
args.path.resize(2);
TransportPathTransform transform(unit, args.TransformForTransport);
@@ -188,14 +201,14 @@ namespace Movement
void MoveSplineInit::SetFall()
{
args.flags.EnableFalling();
- args.flags.fallingSlow = unit.HasUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
+ args.flags.fallingSlow = unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
}
Vector3 TransportPathTransform::operator()(Vector3 input)
{
if (_transformForTransport)
{
- if (TransportBase* transport = _owner.GetDirectTransport())
+ if (TransportBase* transport = _owner->GetDirectTransport())
{
float unused = 0.0f; // need reference
transport->CalculatePassengerOffset(input.x, input.y, input.z, unused);
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h
index 74e5cdfa9f4..79e0f085d44 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.h
+++ b/src/server/game/Movement/Spline/MoveSplineInit.h
@@ -20,6 +20,7 @@
#define TRINITYSERVER_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
+#include "PathGenerator.h"
class Unit;
@@ -37,12 +38,12 @@ namespace Movement
class TransportPathTransform
{
public:
- TransportPathTransform(Unit& owner, bool transformForTransport)
+ TransportPathTransform(Unit* owner, bool transformForTransport)
: _owner(owner), _transformForTransport(transformForTransport) { }
Vector3 operator()(Vector3 input);
private:
- Unit& _owner;
+ Unit* _owner;
bool _transformForTransport;
};
@@ -52,11 +53,11 @@ namespace Movement
{
public:
- explicit MoveSplineInit(Unit& m);
+ explicit MoveSplineInit(Unit* m);
/* Final pass of initialization that launches spline movement.
*/
- void Launch();
+ int32 Launch();
/* Final pass of initialization that stops movement.
*/
@@ -87,10 +88,10 @@ namespace Movement
*/
void MovebyPath(const PointsArray& path, int32 pointId = 0);
- /* Initializes simple A to B mition, A is current unit's position, B is destination
+ /* Initializes simple A to B motion, A is current unit's position, B is destination
*/
- void MoveTo(const Vector3& destination);
- void MoveTo(float x, float y, float z);
+ void MoveTo(const Vector3& destination, bool generatePath = true, bool forceDestination = false);
+ void MoveTo(float x, float y, float z, bool generatePath = true, bool forceDestination = false);
/* Sets Id of fisrt point of the path. When N-th path point will be done ILisener will notify that pointId + N done
* Needed for waypoint movement where path splitten into parts
@@ -145,7 +146,7 @@ namespace Movement
protected:
MoveSplineInitArgs args;
- Unit& unit;
+ Unit* unit;
};
inline void MoveSplineInit::SetFly() { args.flags.flying = true; }
@@ -165,9 +166,9 @@ namespace Movement
std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport));
}
- inline void MoveSplineInit::MoveTo(float x, float y, float z)
+ inline void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination)
{
- MoveTo(G3D::Vector3(x, y, z));
+ MoveTo(G3D::Vector3(x, y, z), generatePath, forceDestination);
}
inline void MoveSplineInit::SetParabolic(float amplitude, float time_shift)
diff --git a/src/server/game/Movement/Spline/MovementPacketBuilder.cpp b/src/server/game/Movement/Spline/MovementPacketBuilder.cpp
index f2dc8897532..eb3d45bb0ec 100644
--- a/src/server/game/Movement/Spline/MovementPacketBuilder.cpp
+++ b/src/server/game/Movement/Spline/MovementPacketBuilder.cpp
@@ -192,7 +192,7 @@ namespace Movement
{
if (!moveSpline.Finalized())
{
- MoveSplineFlag splineFlags = moveSpline.splineflags;
+ MoveSplineFlag const& splineFlags = moveSpline.splineflags;
if ((splineFlags & MoveSplineFlag::Parabolic) && moveSpline.effect_start_time < moveSpline.Duration())
data << moveSpline.vertical_acceleration; // added in 3.1
diff --git a/src/server/game/Movement/Waypoints/Path.h b/src/server/game/Movement/Waypoints/Path.h
index 3f78a640384..39f05184cf6 100644
--- a/src/server/game/Movement/Waypoints/Path.h
+++ b/src/server/game/Movement/Waypoints/Path.h
@@ -20,10 +20,12 @@
#define TRINITYCORE_PATH_H
#include "Common.h"
-#include <vector>
+#include <deque>
-struct SimplePathNode
+struct PathNode
{
+ PathNode(): x(0.0f), y(0.0f), z(0.0f) { }
+ PathNode(float _x, float _y, float _z): x(_x), y(_y), z(_z) { }
float x, y, z;
};
template<typename PathElem, typename PathNode = PathElem>
@@ -36,6 +38,20 @@ class Path
void resize(unsigned int sz) { i_nodes.resize(sz); }
void clear() { i_nodes.clear(); }
void erase(uint32 idx) { i_nodes.erase(i_nodes.begin()+idx); }
+ void crop(unsigned int start, unsigned int end)
+ {
+ while(start && !i_nodes.empty())
+ {
+ i_nodes.pop_front();
+ --start;
+ }
+
+ while(end && !i_nodes.empty())
+ {
+ i_nodes.pop_back();
+ --end;
+ }
+ }
float GetTotalLength(uint32 start, uint32 end) const
{
@@ -76,10 +92,9 @@ class Path
void set(size_t idx, PathElem elem) { i_nodes[idx] = elem; }
protected:
- std::vector<PathElem> i_nodes;
+ std::deque<PathElem> i_nodes;
};
-typedef Path<SimplePathNode> SimplePath;
+typedef Path<PathNode> SimplePath;
#endif
-
diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp
index 937a2ddef72..6fba8308077 100644
--- a/src/server/game/Movement/Waypoints/WaypointManager.cpp
+++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp
@@ -49,7 +49,6 @@ void WaypointMgr::Load()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 waypoints. DB table `waypoint_data` is empty!");
-
return;
}
@@ -87,7 +86,6 @@ void WaypointMgr::Load()
while (result->NextRow());
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u waypoints in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-
}
void WaypointMgr::ReloadPath(uint32 id)
diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h
index 07f855380f9..b17714f1a51 100644
--- a/src/server/game/Movement/Waypoints/WaypointManager.h
+++ b/src/server/game/Movement/Waypoints/WaypointManager.h
@@ -68,4 +68,3 @@ class WaypointMgr
#define sWaypointMgr ACE_Singleton<WaypointMgr, ACE_Null_Mutex>::instance()
#endif
-
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 8cbc4b62424..245ca7fd41d 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -66,9 +66,11 @@ void AddSC_list_commandscript();
void AddSC_lookup_commandscript();
void AddSC_message_commandscript();
void AddSC_misc_commandscript();
+void AddSC_mmaps_commandscript();
void AddSC_modify_commandscript();
void AddSC_npc_commandscript();
void AddSC_quest_commandscript();
+void AddSC_rbac_commandscript();
void AddSC_reload_commandscript();
void AddSC_reset_commandscript();
void AddSC_server_commandscript();
@@ -695,9 +697,11 @@ void AddCommandScripts()
AddSC_list_commandscript();
AddSC_message_commandscript();
AddSC_misc_commandscript();
+ AddSC_mmaps_commandscript();
AddSC_modify_commandscript();
AddSC_npc_commandscript();
AddSC_quest_commandscript();
+ AddSC_rbac_commandscript();
AddSC_reload_commandscript();
AddSC_reset_commandscript();
AddSC_server_commandscript();
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index bdbfa46ba5d..5a973395c68 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -580,6 +580,8 @@ void ScriptMgr::OnPlayerEnterMap(Map* map, Player* player)
ASSERT(map);
ASSERT(player);
+ FOREACH_SCRIPT(PlayerScript)->OnMapChanged(player);
+
SCR_MAP_BGN(WorldMapScript, map, itr, end, entry, IsWorldMap);
itr->second->OnPlayerEnter(map, player);
SCR_MAP_END;
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 92b5e8299e1..e9d040200e4 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -753,6 +753,9 @@ class PlayerScript : public UnitScript
// Called when a player switches to a new zone
virtual void OnUpdateZone(Player* /*player*/, uint32 /*newZone*/, uint32 /*newArea*/) { }
+
+ // Called when a player changes to a new map (after moving to new map)
+ virtual void OnMapChanged(Player* /*player*/) { }
};
class GuildScript : public ScriptObject
diff --git a/src/server/game/Server/Protocol/PacketLog.cpp b/src/server/game/Server/Protocol/PacketLog.cpp
index 649f4130c06..fe8b6804020 100644
--- a/src/server/game/Server/Protocol/PacketLog.cpp
+++ b/src/server/game/Server/Protocol/PacketLog.cpp
@@ -55,7 +55,7 @@ void PacketLog::LogPacket(WorldPacket const& packet, Direction direction)
data << uint8(direction);
for (uint32 i = 0; i < packet.size(); i++)
- data << const_cast<WorldPacket&>(packet)[i];
+ data << packet[i];
fwrite(data.contents(), 1, data.size(), _file);
fflush(_file);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index bb108b0d1fa..d12128b11c1 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -22,6 +22,7 @@
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include <zlib.h>
+#include "Config.h"
#include "Common.h"
#include "DatabaseEnv.h"
#include "Log.h"
@@ -119,7 +120,8 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
_filterAddonMessages(false),
recruiterId(recruiter),
isRecruiter(isARecruiter),
- timeLastWhoCommand(0)
+ timeLastWhoCommand(0),
+ _RBACData(NULL)
{
if (sock)
{
@@ -160,8 +162,8 @@ WorldSession::~WorldSession()
m_Socket = NULL;
}
- if (_warden)
- delete _warden;
+ delete _warden;
+ delete _RBACData;
///- empty incoming packet queue
WorldPacket* packet = NULL;
@@ -862,7 +864,7 @@ void WorldSession::ReadAddonsInfo(WorldPacket &data)
ByteBuffer addonInfo;
addonInfo.resize(size);
- if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
+ if (uncompress(addonInfo.contents(), &uSize, data.contents() + pos, data.size() - pos) == Z_OK)
{
uint32 addonsCount;
addonInfo >> addonsCount; // addons count
@@ -1152,3 +1154,24 @@ void WorldSession::InitWarden(BigNumber* k, std::string const& os)
// _warden->Init(this, k);
}
}
+
+void WorldSession::LoadPermissions()
+{
+ uint32 id = GetAccountId();
+ std::string name;
+ int32 realmId = ConfigMgr::GetIntDefault("RealmID", 0);
+ AccountMgr::GetName(id, name);
+
+ _RBACData = new RBACData(id, name, realmId);
+ _RBACData->LoadFromDB();
+}
+
+RBACData* WorldSession::GetRBACData()
+{
+ return _RBACData;
+}
+
+bool WorldSession::HasPermission(uint32 permission)
+{
+ return _RBACData->HasPermission(permission);
+}
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index d2ef4c4eea5..419de3a466e 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -24,6 +24,7 @@
#define __WORLDSESSION_H
#include "Common.h"
+#include "AccountMgr.h"
#include "SharedDefines.h"
#include "AddonMgr.h"
#include "DatabaseEnv.h"
@@ -49,15 +50,18 @@ struct AreaTableEntry;
struct AuctionEntry;
struct DeclinedName;
struct ItemTemplate;
+struct MovementInfo;
+
+namespace lfg
+{
struct LfgJoinResultData;
-struct LfgLockStatus;
struct LfgPlayerBoot;
struct LfgProposal;
struct LfgQueueStatusData;
struct LfgPlayerRewardData;
struct LfgRoleCheck;
struct LfgUpdateData;
-struct MovementInfo;
+}
enum AccountDataType
{
@@ -218,6 +222,10 @@ class WorldSession
void SendAuthResponse(uint8 code, bool queued, uint32 queuePos = 0);
void SendClientCacheVersion(uint32 version);
+ RBACData* GetRBACData();
+ bool HasPermission(uint32 permissionId);
+ void LoadPermissions();
+
AccountTypes GetSecurity() const { return _security; }
uint32 GetAccountId() const { return _accountId; }
Player* GetPlayer() const { return _player; }
@@ -811,16 +819,16 @@ class WorldSession
void HandleLfrLeaveOpcode(WorldPacket& recvData);
void HandleLfgGetStatus(WorldPacket& recvData);
- void SendLfgUpdatePlayer(LfgUpdateData const& updateData);
- void SendLfgUpdateParty(LfgUpdateData const& updateData);
+ void SendLfgUpdatePlayer(lfg::LfgUpdateData const& updateData);
+ void SendLfgUpdateParty(lfg::LfgUpdateData const& updateData);
void SendLfgRoleChosen(uint64 guid, uint8 roles);
- void SendLfgRoleCheckUpdate(LfgRoleCheck const& pRoleCheck);
+ void SendLfgRoleCheckUpdate(lfg::LfgRoleCheck const& pRoleCheck);
void SendLfgLfrList(bool update);
- void SendLfgJoinResult(LfgJoinResultData const& joinData);
- void SendLfgQueueStatus(LfgQueueStatusData const& queueData);
- void SendLfgPlayerReward(LfgPlayerRewardData const& lfgPlayerRewardData);
- void SendLfgBootProposalUpdate(LfgPlayerBoot const& boot);
- void SendLfgUpdateProposal(LfgProposal const& proposal);
+ void SendLfgJoinResult(lfg::LfgJoinResultData const& joinData);
+ void SendLfgQueueStatus(lfg::LfgQueueStatusData const& queueData);
+ void SendLfgPlayerReward(lfg::LfgPlayerRewardData const& lfgPlayerRewardData);
+ void SendLfgBootProposalUpdate(lfg::LfgPlayerBoot const& boot);
+ void SendLfgUpdateProposal(lfg::LfgProposal const& proposal);
void SendLfgDisabled();
void SendLfgOfferContinue(uint32 dungeonEntry);
void SendLfgTeleportError(uint8 err);
@@ -1009,6 +1017,7 @@ class WorldSession
ACE_Based::LockedQueue<WorldPacket*, ACE_Thread_Mutex> _recvQueue;
time_t timeLastWhoCommand;
z_stream_s* _compressionStream;
+ RBACData* _RBACData;
};
#endif
/// @}
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 24406cdbdab..295432dc956 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -844,6 +844,8 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
}
// Get the account information from the realmd database
+ // 0 1 2 3 4 5 6 7 8
+ // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
stmt->setString(0, account);
@@ -860,15 +862,11 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
Field* fields = result->Fetch();
- uint8 expansion = fields[6].GetUInt8();
+ uint8 expansion = fields[4].GetUInt8();
uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION);
if (expansion > world_expansion)
expansion = world_expansion;
- sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: (s, v) check s: %s v: %s",
- fields[5].GetCString(),
- fields[4].GetCString());
-
///- Re-check ip locking (same check as in realmd).
if (fields[3].GetUInt8() == 1) // if ip is locked
{
@@ -884,13 +882,13 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
k.SetHexStr(fields[1].GetCString());
- int64 mutetime = fields[7].GetInt64();
+ int64 mutetime = fields[5].GetInt64();
//! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now.
if (mutetime < 0)
{
mutetime = time(NULL) + llabs(mutetime);
- PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN);
stmt->setInt64(0, mutetime);
stmt->setUInt32(1, id);
@@ -898,12 +896,12 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
LoginDatabase.Execute(stmt);
}
- locale = LocaleConstant (fields[8].GetUInt8());
+ locale = LocaleConstant (fields[6].GetUInt8());
if (locale >= TOTAL_LOCALES)
locale = LOCALE_enUS;
- uint32 recruiter = fields[9].GetUInt32();
- std::string os = fields[10].GetString();
+ uint32 recruiter = fields[7].GetUInt32();
+ std::string os = fields[8].GetString();
// Must be done before WorldSession is created
if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED) && os != "Win" && os != "OSX")
@@ -1006,6 +1004,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
m_Session->LoadGlobalAccountData();
m_Session->LoadTutorialsData();
m_Session->ReadAddonsInfo(addonsData);
+ m_Session->LoadPermissions();
// Initialize Warden system only if it is enabled by config
if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
diff --git a/src/server/game/Skills/SkillDiscovery.cpp b/src/server/game/Skills/SkillDiscovery.cpp
index 9fc426323d1..e6ba28d5f1c 100644
--- a/src/server/game/Skills/SkillDiscovery.cpp
+++ b/src/server/game/Skills/SkillDiscovery.cpp
@@ -56,7 +56,6 @@ void LoadSkillDiscoveryTable()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
-
return;
}
@@ -154,7 +153,6 @@ void LoadSkillDiscoveryTable()
}
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u skill discovery definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-
}
uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
diff --git a/src/server/game/Skills/SkillExtraItems.cpp b/src/server/game/Skills/SkillExtraItems.cpp
index 170cf0e57a2..5c3b69e48d2 100644
--- a/src/server/game/Skills/SkillExtraItems.cpp
+++ b/src/server/game/Skills/SkillExtraItems.cpp
@@ -61,7 +61,6 @@ void LoadSkillExtraItemTable()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty.");
-
return;
}
@@ -75,28 +74,28 @@ void LoadSkillExtraItemTable()
if (!sSpellMgr->GetSpellInfo(spellId))
{
- sLog->outError(LOG_FILTER_GENERAL, "Skill specialization %u has non-existent spell id in `skill_extra_item_template`!", spellId);
+ sLog->outError(LOG_FILTER_SQL, "Skill specialization %u has non-existent spell id in `skill_extra_item_template`!", spellId);
continue;
}
uint32 requiredSpecialization = fields[1].GetUInt32();
if (!sSpellMgr->GetSpellInfo(requiredSpecialization))
{
- sLog->outError(LOG_FILTER_GENERAL, "Skill specialization %u have not existed required specialization spell id %u in `skill_extra_item_template`!", spellId, requiredSpecialization);
+ sLog->outError(LOG_FILTER_SQL, "Skill specialization %u have not existed required specialization spell id %u in `skill_extra_item_template`!", spellId, requiredSpecialization);
continue;
}
float additionalCreateChance = fields[2].GetFloat();
if (additionalCreateChance <= 0.0f)
{
- sLog->outError(LOG_FILTER_GENERAL, "Skill specialization %u has too low additional create chance in `skill_extra_item_template`!", spellId);
+ sLog->outError(LOG_FILTER_SQL, "Skill specialization %u has too low additional create chance in `skill_extra_item_template`!", spellId);
continue;
}
uint8 additionalMaxNum = fields[3].GetUInt8();
if (!additionalMaxNum)
{
- sLog->outError(LOG_FILTER_GENERAL, "Skill specialization %u has 0 max number of extra items in `skill_extra_item_template`!", spellId);
+ sLog->outError(LOG_FILTER_SQL, "Skill specialization %u has 0 max number of extra items in `skill_extra_item_template`!", spellId);
continue;
}
@@ -111,7 +110,6 @@ void LoadSkillExtraItemTable()
while (result->NextRow());
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u spell specialization definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-
}
bool canCreateExtraItems(Player* player, uint32 spellId, float &additionalChance, uint8 &additionalMax)
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 186cdb8c552..5f93551a099 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -504,8 +504,6 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
}
}
- float DoneActualBenefit = 0.0f;
-
// custom amount calculations go here
switch (GetAuraType())
{
@@ -538,176 +536,8 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
}
break;
case SPELL_AURA_SCHOOL_ABSORB:
- m_canBeRecalculated = false;
- if (!caster)
- break;
- switch (GetSpellInfo()->SpellFamilyName)
- {
- case SPELLFAMILY_MAGE:
- // Ice Barrier
- if (GetSpellInfo()->SpellFamilyFlags[1] & 0x1 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8)
- {
- // +87% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.87f;
- }
- // Mage Ward
- else if (GetSpellInfo()->SpellFamilyFlags[0] & 0x8 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- }
- break;
- case SPELLFAMILY_WARLOCK:
- // Shadow Ward
- if (m_spellInfo->SpellFamilyFlags[2] & 0x80000000)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- }
- break;
- case SPELLFAMILY_PRIEST:
- // Power Word: Shield
- if (GetSpellInfo()->SpellFamilyFlags[0] & 0x1)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseHealingBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo());
-
- amount += int32(DoneActualBenefit);
-
- // Twin Disciplines
- if (AuraEffect const* pAurEff = caster->GetAuraEffect(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, SPELLFAMILY_PRIEST, 2292, 0))
- AddPct(amount, pAurEff->GetAmount());
-
- // Reuse variable, not sure if this code below can be moved before Twin Disciplines
- DoneActualBenefit = float(amount);
- DoneActualBenefit *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
- amount = int32(DoneActualBenefit);
-
- return amount;
- }
- break;
- default:
- break;
- }
- break;
case SPELL_AURA_MANA_SHIELD:
m_canBeRecalculated = false;
- if (!caster)
- break;
- // Mana Shield
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_MAGE && GetSpellInfo()->SpellFamilyFlags[0] & 0x8000 && m_spellInfo->SpellFamilyFlags[2] & 0x8)
- {
- // +80.7% from +spd bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.807f;
- }
- break;
- case SPELL_AURA_DUMMY:
- if (!caster)
- break;
- // Earth Shield
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[1] & 0x400)
- {
- amount = caster->SpellHealingBonusDone(GetBase()->GetUnitOwner(), GetSpellInfo(), amount, SPELL_DIRECT_DAMAGE);
- amount = GetBase()->GetUnitOwner()->SpellHealingBonusTaken(caster, GetSpellInfo(), amount, SPELL_DIRECT_DAMAGE);
- }
- break;
- case SPELL_AURA_PERIODIC_DAMAGE:
- if (!caster)
- break;
- // Rip
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[0] & 0x00800000)
- {
- m_canBeRecalculated = false;
- // 0.01*$AP*cp
- if (caster->GetTypeId() != TYPEID_PLAYER)
- break;
-
- uint8 cp = caster->ToPlayer()->GetComboPoints();
-
- // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
- if (AuraEffect const* aurEff = caster->GetAuraEffect(34241, EFFECT_0))
- amount += cp * aurEff->GetAmount();
- // Idol of Worship. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
- else if (AuraEffect const* aurEff = caster->GetAuraEffect(60774, EFFECT_0))
- amount += cp * aurEff->GetAmount();
-
- amount += uint32(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), cp));
- }
- // Rend
- else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_WARRIOR && GetSpellInfo()->SpellFamilyFlags[0] & 0x20)
- {
- m_canBeRecalculated = false;
- // ${0.25 * 6 * (($MWB + $mwb) / 2 + $AP / 14 * $MWS)} bonus per tick
- float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
- int32 mws = caster->GetAttackTime(BASE_ATTACK);
- float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
- float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
- float mwb = ((mwb_min + mwb_max) / 2 + ap * mws / 14000) * 0.25f * 6.0f;
- amount += int32(caster->ApplyEffectModifiers(m_spellInfo, m_effIndex, mwb));
- }
- break;
- case SPELL_AURA_PERIODIC_ENERGIZE:
- switch (m_spellInfo->Id)
- {
- case 57669: // Replenishment (0.2% from max)
- amount = CalculatePct(GetBase()->GetUnitOwner()->GetMaxPower(POWER_MANA), amount);
- break;
- case 61782: // Infinite Replenishment
- amount = GetBase()->GetUnitOwner()->GetMaxPower(POWER_MANA) * 0.0025f;
- break;
- case 29166: // Innervate
- ApplyPct(amount, float(GetBase()->GetUnitOwner()->GetCreatePowers(POWER_MANA)) / GetTotalTicks());
- break;
- case 48391: // Owlkin Frenzy
- ApplyPct(amount, GetBase()->GetUnitOwner()->GetCreatePowers(POWER_MANA));
- break;
- default:
- break;
- }
- break;
- case SPELL_AURA_PERIODIC_HEAL:
- if (!caster)
- break;
- // Lightwell Renew
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags[2] & 0x4000)
- {
- if (caster->GetTypeId() == TYPEID_PLAYER)
- // Bonus from Glyph of Lightwell
- if (AuraEffect* modHealing = caster->GetAuraEffect(55673, 0))
- AddPct(amount, modHealing->GetAmount());
- }
- break;
- case SPELL_AURA_MOD_THREAT:
- {
- uint8 level_diff = 0;
- float multiplier = 0.0f;
- switch (GetId())
- {
- // Arcane Shroud
- case 26400:
- level_diff = GetBase()->GetUnitOwner()->getLevel() - 60;
- multiplier = 2;
- break;
- // The Eye of Diminution
- case 28862:
- level_diff = GetBase()->GetUnitOwner()->getLevel() - 60;
- multiplier = 1;
- break;
- }
- if (level_diff > 0)
- amount += int32(multiplier * level_diff);
- break;
- }
- case SPELL_AURA_MOD_INCREASE_HEALTH:
- // Vampiric Blood
- if (GetId() == 55233)
- amount = GetBase()->GetUnitOwner()->CountPctFromMaxHealth(amount);
- break;
- case SPELL_AURA_MOD_INCREASE_SPEED:
- // Dash - do not set speed if not in cat form
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && GetSpellInfo()->SpellFamilyFlags[2] & 0x00000008)
- amount = GetBase()->GetUnitOwner()->GetShapeshiftForm() == FORM_CAT ? amount : 0;
break;
case SPELL_AURA_MOUNTED:
if (MountCapabilityEntry const* mountCapability = GetBase()->GetUnitOwner()->GetMountCapability(uint32(GetMiscValueB())))
@@ -751,13 +581,8 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
default:
break;
}
- if (DoneActualBenefit != 0.0f)
- {
- DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo());
- amount += (int32)DoneActualBenefit;
- }
- GetBase()->CallScriptEffectCalcAmountHandlers(const_cast<AuraEffect const*>(this), amount, m_canBeRecalculated);
+ GetBase()->CallScriptEffectCalcAmountHandlers(this, amount, m_canBeRecalculated);
amount *= GetBase()->GetStackAmount();
return amount;
}
@@ -791,7 +616,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool resetPeriodicTimer /*= tru
break;
}
- GetBase()->CallScriptEffectCalcPeriodicHandlers(const_cast<AuraEffect const*>(this), m_isPeriodic, m_amplitude);
+ GetBase()->CallScriptEffectCalcPeriodicHandlers(this, m_isPeriodic, m_amplitude);
if (!m_isPeriodic)
return;
@@ -845,32 +670,6 @@ void AuraEffect::CalculateSpellMod()
{
switch (GetAuraType())
{
- case SPELL_AURA_DUMMY:
- switch (GetSpellInfo()->SpellFamilyName)
- {
- case SPELLFAMILY_DRUID:
- switch (GetId())
- {
- case 34246: // Idol of the Emerald Queen
- case 60779: // Idol of Lush Moss
- {
- if (!m_spellmod)
- {
- m_spellmod = new SpellModifier(GetBase());
- m_spellmod->op = SPELLMOD_DOT;
- m_spellmod->type = SPELLMOD_FLAT;
- m_spellmod->spellId = GetId();
- m_spellmod->mask[1] = 0x0010;
- }
- m_spellmod->value = GetAmount()/7;
- }
- break;
- }
- break;
- default:
- break;
- }
- break;
case SPELL_AURA_ADD_FLAT_MODIFIER:
case SPELL_AURA_ADD_PCT_MODIFIER:
if (!m_spellmod)
@@ -879,7 +678,7 @@ void AuraEffect::CalculateSpellMod()
m_spellmod->op = SpellModOp(GetMiscValue());
ASSERT(m_spellmod->op < MAX_SPELLMOD);
- m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types
+ m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types
m_spellmod->spellId = GetId();
m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask;
m_spellmod->charges = GetBase()->GetCharges();
@@ -889,7 +688,7 @@ void AuraEffect::CalculateSpellMod()
default:
break;
}
- GetBase()->CallScriptEffectCalcSpellModHandlers(const_cast<AuraEffect const*>(this), m_spellmod);
+ GetBase()->CallScriptEffectCalcSpellModHandlers(this, m_spellmod);
}
void AuraEffect::ChangeAmount(int32 newAmount, bool mark, bool onStackOrReapply)
@@ -948,15 +747,15 @@ void AuraEffect::HandleEffect(AuraApplication * aurApp, uint8 mode, bool apply)
// call scripts helping/replacing effect handlers
bool prevented = false;
if (apply)
- prevented = GetBase()->CallScriptEffectApplyHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), (AuraEffectHandleModes)mode);
+ prevented = GetBase()->CallScriptEffectApplyHandlers(this, aurApp, (AuraEffectHandleModes)mode);
else
- prevented = GetBase()->CallScriptEffectRemoveHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), (AuraEffectHandleModes)mode);
+ prevented = GetBase()->CallScriptEffectRemoveHandlers(this, aurApp, (AuraEffectHandleModes)mode);
// check if script events have removed the aura or if default effect prevention was requested
if ((apply && aurApp->GetRemoveMode()) || prevented)
return;
- (*this.*AuraEffectHandler [GetAuraType()])(const_cast<AuraApplication const*>(aurApp), mode, apply);
+ (*this.*AuraEffectHandler[GetAuraType()])(aurApp, mode, apply);
// check if script events have removed the aura or if default effect prevention was requested
if (apply && aurApp->GetRemoveMode())
@@ -964,9 +763,9 @@ void AuraEffect::HandleEffect(AuraApplication * aurApp, uint8 mode, bool apply)
// call scripts triggering additional events after apply/remove
if (apply)
- GetBase()->CallScriptAfterEffectApplyHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), (AuraEffectHandleModes)mode);
+ GetBase()->CallScriptAfterEffectApplyHandlers(this, aurApp, (AuraEffectHandleModes)mode);
else
- GetBase()->CallScriptAfterEffectRemoveHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), (AuraEffectHandleModes)mode);
+ GetBase()->CallScriptAfterEffectRemoveHandlers(this, aurApp, (AuraEffectHandleModes)mode);
}
void AuraEffect::HandleEffect(Unit* target, uint8 mode, bool apply)
@@ -1270,7 +1069,7 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const
void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
- bool prevented = GetBase()->CallScriptEffectProcHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), eventInfo);
+ bool prevented = GetBase()->CallScriptEffectProcHandlers(this, aurApp, eventInfo);
if (prevented)
return;
@@ -1295,7 +1094,7 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
break;
}
- GetBase()->CallScriptAfterEffectProcHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), eventInfo);
+ GetBase()->CallScriptAfterEffectProcHandlers(this, aurApp, eventInfo);
}
void AuraEffect::CleanupTriggeredSpells(Unit* target)
@@ -2649,7 +2448,10 @@ void AuraEffect::HandleAuraTrackCreatures(AuraApplication const* aurApp, uint8 m
if (target->GetTypeId() != TYPEID_PLAYER)
return;
- target->SetUInt32Value(PLAYER_TRACK_CREATURES, (apply) ? ((uint32)1)<<(GetMiscValue()-1) : 0);
+ if (apply)
+ target->SetFlag(PLAYER_TRACK_CREATURES, uint32(1) << (GetMiscValue() - 1));
+ else
+ target->RemoveFlag(PLAYER_TRACK_CREATURES, uint32(1) << (GetMiscValue() - 1));
}
void AuraEffect::HandleAuraTrackResources(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2662,7 +2464,10 @@ void AuraEffect::HandleAuraTrackResources(AuraApplication const* aurApp, uint8 m
if (target->GetTypeId() != TYPEID_PLAYER)
return;
- target->SetUInt32Value(PLAYER_TRACK_RESOURCES, (apply) ? ((uint32)1)<<(GetMiscValue()-1): 0);
+ if (apply)
+ target->SetFlag(PLAYER_TRACK_RESOURCES, uint32(1) << (GetMiscValue() - 1));
+ else
+ target->RemoveFlag(PLAYER_TRACK_RESOURCES, uint32(1) << (GetMiscValue() - 1));
}
void AuraEffect::HandleAuraTrackStealthed(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2774,6 +2579,8 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo
if (apply)
{
uint32 creatureEntry = GetMiscValue();
+ uint32 displayId = 0;
+ uint32 vehicleId = 0;
// Festive Holiday Mount
if (target->HasAura(62061))
@@ -2784,30 +2591,28 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo
creatureEntry = 15665;
}
- CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(creatureEntry);
- if (!ci)
+ if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(creatureEntry))
{
- sLog->outError(LOG_FILTER_SQL, "AuraMounted: `creature_template`='%u' not found in database (only need its modelid)", GetMiscValue());
- return;
- }
+ uint32 team = 0;
+ if (target->GetTypeId() == TYPEID_PLAYER)
+ team = target->ToPlayer()->GetTeam();
- uint32 team = 0;
- if (target->GetTypeId() == TYPEID_PLAYER)
- team = target->ToPlayer()->GetTeam();
+ displayId = ObjectMgr::ChooseDisplayId(team, ci);
+ sObjectMgr->GetCreatureModelRandomGender(&displayId);
- uint32 displayID = ObjectMgr::ChooseDisplayId(team, ci);
- sObjectMgr->GetCreatureModelRandomGender(&displayID);
+ vehicleId = ci->VehicleId;
- //some spell has one aura of mount and one of vehicle
- for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- if (GetSpellInfo()->Effects[i].Effect == SPELL_EFFECT_SUMMON
- && GetSpellInfo()->Effects[i].MiscValue == GetMiscValue())
- displayID = 0;
+ //some spell has one aura of mount and one of vehicle
+ for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (GetSpellInfo()->Effects[i].Effect == SPELL_EFFECT_SUMMON
+ && GetSpellInfo()->Effects[i].MiscValue == GetMiscValue())
+ displayId = 0;
+ }
- target->Mount(displayID, ci->VehicleId, GetMiscValue());
+ target->Mount(displayId, vehicleId, creatureEntry);
// cast speed aura
- if (MountCapabilityEntry const* mountCapability = target->GetMountCapability(uint32(GetMiscValueB())))
+ if (MountCapabilityEntry const* mountCapability = sMountCapabilityStore.LookupEntry(GetAmount()))
target->CastSpell(target, mountCapability->SpeedModSpell, true);
}
else
@@ -3624,22 +3429,19 @@ void AuraEffect::HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint
Unit* target = aurApp->GetTarget();
- target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, GetMiscValue(), apply);
+ target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, GetMiscValue(), apply);
// when removing flag aura, handle flag drop
- if (!apply && target->GetTypeId() == TYPEID_PLAYER
- && (GetSpellInfo()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION))
+ Player* player = target->ToPlayer();
+ if (!apply && player && (GetSpellInfo()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION))
{
- if (target->GetTypeId() == TYPEID_PLAYER)
+ if (player->InBattleground())
{
- if (target->ToPlayer()->InBattleground())
- {
- if (Battleground* bg = target->ToPlayer()->GetBattleground())
- bg->EventPlayerDroppedFlag(target->ToPlayer());
- }
- else
- sOutdoorPvPMgr->HandleDropFlag((Player*)target, GetSpellInfo()->Id);
+ if (Battleground* bg = player->GetBattleground())
+ bg->EventPlayerDroppedFlag(player);
}
+ else
+ sOutdoorPvPMgr->HandleDropFlag(player, GetSpellInfo()->Id);
}
}
@@ -4916,10 +4718,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
if (Aura* newAura = target->AddAura(71564, target))
newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount);
break;
- case 59628: // Tricks of the Trade
- if (caster && caster->GetMisdirectionTarget())
- target->SetReducedThreatPercent(100, caster->GetMisdirectionTarget()->GetGUID());
- break;
}
}
// AT REMOVE
@@ -5014,20 +4812,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
if (GetId() == 61777)
target->CastSpell(target, GetAmount(), true);
break;
- case SPELLFAMILY_ROGUE:
- // Tricks of the trade
- switch (GetId())
- {
- case 59628: //Tricks of the trade buff on rogue (6sec duration)
- target->SetReducedThreatPercent(0, 0);
- break;
- case 57934: //Tricks of the trade buff on rogue (30sec duration)
- if (aurApp->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE || !caster->GetMisdirectionTarget())
- target->SetReducedThreatPercent(0, 0);
- else
- target->SetReducedThreatPercent(0, caster->GetMisdirectionTarget()->GetGUID());
- break;
- }
default:
break;
}
@@ -5298,10 +5082,6 @@ void AuraEffect::HandleAuraEmpathy(AuraApplication const* aurApp, uint8 mode, bo
return;
Unit* target = aurApp->GetTarget();
-
- if (target->GetTypeId() != TYPEID_UNIT)
- return;
-
if (!apply)
{
// do not remove unit flag if there are more than this auraEffect of that kind on unit on unit
@@ -5309,8 +5089,7 @@ void AuraEffect::HandleAuraEmpathy(AuraApplication const* aurApp, uint8 mode, bo
return;
}
- CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(target->GetEntry());
- if (ci && ci->type == CREATURE_TYPE_BEAST)
+ if (target->GetCreatureType() == CREATURE_TYPE_BEAST)
target->ApplyModUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO, apply);
}
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index d578b3507c6..4abca023dd3 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -1388,17 +1388,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
// mods at aura apply or remove
switch (GetSpellInfo()->SpellFamilyName)
{
- case SPELLFAMILY_GENERIC:
- switch (GetId())
- {
- case 50720: // Vigilance
- if (apply)
- target->CastSpell(caster, 59665, true, 0, 0, caster->GetGUID());
- else
- target->SetReducedThreatPercent(0, 0);
- break;
- }
- break;
case SPELLFAMILY_DRUID:
// Enrage
if ((GetSpellInfo()->SpellFamilyFlags[0] & 0x80000) && GetSpellInfo()->SpellIconID == 961)
@@ -1781,14 +1770,14 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event
void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
- CallScriptProcHandlers(const_cast<AuraApplication const*>(aurApp), eventInfo);
+ CallScriptProcHandlers(aurApp, eventInfo);
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (aurApp->HasEffect(i))
// OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc()
GetEffect(i)->HandleProc(aurApp, eventInfo);
- CallScriptAfterProcHandlers(const_cast<AuraApplication const*>(aurApp), eventInfo);
+ CallScriptAfterProcHandlers(aurApp, eventInfo);
// Remove aura if we've used last charge to proc
if (IsUsingCharges() && !GetCharges())
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 2619ef0eeb6..ba570ad73a4 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -491,7 +491,7 @@ SpellValue::SpellValue(SpellInfo const* proto)
Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, uint64 originalCasterGUID, bool skipCheck) :
m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)),
m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster)
-, m_spellValue(new SpellValue(m_spellInfo))
+, m_spellValue(new SpellValue(m_spellInfo)), m_preGeneratedPath(PathGenerator(m_caster))
{
m_customError = SPELL_CUSTOM_ERROR_NONE;
m_skipCheck = skipCheck;
@@ -611,6 +611,7 @@ Spell::~Spell()
if (m_caster && m_caster->GetTypeId() == TYPEID_PLAYER)
ASSERT(m_caster->ToPlayer()->m_spellModTakingSpell != this);
+
delete m_spellValue;
CheckEffectExecuteData();
@@ -1342,11 +1343,6 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
}
}
- // todo: move to scripts, but we must call it before resize list by MaxAffectedTargets
- // Intimidating Shout
- if (m_spellInfo->Id == 5246 && effIndex != EFFECT_0)
- unitTargets.remove(m_targets.GetUnitTarget());
-
// Other special target selection goes here
if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
Trinity::Containers::RandomResizeList(unitTargets, maxTargets);
@@ -5189,12 +5185,30 @@ SpellCastResult Spell::CheckCast(bool strict)
if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953))
m_caster->RemoveMovementImpairingAuras();
}
+
if (m_caster->HasUnitState(UNIT_STATE_ROOT))
return SPELL_FAILED_ROOTED;
+
+ Unit* target = m_targets.GetUnitTarget();
+
+ if (!target)
+ return SPELL_FAILED_DONT_REPORT;
+
if (m_caster->GetTypeId() == TYPEID_PLAYER)
- if (Unit* target = m_targets.GetUnitTarget())
- if (!target->isAlive())
- return SPELL_FAILED_BAD_TARGETS;
+ if (!target->isAlive())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Position pos;
+ target->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ target->GetFirstCollisionPosition(pos, CONTACT_DISTANCE, target->GetRelativeAngle(m_caster));
+
+ m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f);
+ bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize());
+ if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)
+ return SPELL_FAILED_OUT_OF_RANGE;
+ else if (!result)
+ return SPELL_FAILED_NOPATH;
+
break;
}
case SPELL_EFFECT_SKINNING:
@@ -6520,7 +6534,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const
break;
}
- if (IsTriggered() || m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS)
+ if (IsTriggered() || m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS))
return true;
// todo: shit below shouldn't be here, but it's temporary
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index c7415d4c9d3..27474645d77 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -23,6 +23,7 @@
#include "SharedDefines.h"
#include "ObjectMgr.h"
#include "SpellInfo.h"
+#include "PathGenerator.h"
class Unit;
class Player;
@@ -669,6 +670,7 @@ class Spell
bool m_skipCheck;
uint8 m_auraScaleMask;
+ PathGenerator m_preGeneratedPath;
ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS];
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index e86ac6ef97f..bb730b94002 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -63,6 +63,7 @@
#include "GameObjectAI.h"
#include "AccountMgr.h"
#include "InstanceScript.h"
+#include "PathGenerator.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "ReputationMgr.h"
@@ -756,12 +757,6 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, spell->Id, true);
return;
}
- // Righteous Defense
- case 31980:
- {
- m_caster->CastSpell(unitTarget, 31790, true);
- return;
- }
// Cloak of Shadows
case 35729:
{
@@ -1012,32 +1007,8 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/)
return;
// Pre effects
- uint8 uiMaxSafeLevel = 0;
switch (m_spellInfo->Id)
{
- case 48129: // Scroll of Recall
- uiMaxSafeLevel = 40;
- case 60320: // Scroll of Recall II
- if (!uiMaxSafeLevel)
- uiMaxSafeLevel = 70;
- case 60321: // Scroll of Recal III
- if (!uiMaxSafeLevel)
- uiMaxSafeLevel = 80;
-
- if (unitTarget->getLevel() > uiMaxSafeLevel)
- {
- unitTarget->AddAura(60444, unitTarget); //Apply Lost! Aura
-
- // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335
- uint32 spellId = 60323;
- if (m_caster->ToPlayer()->GetTeam() == HORDE)
- spellId += 5;
-
- spellId += urand(0, 7);
- m_caster->CastSpell(m_caster, spellId, true);
- return;
- }
- break;
case 66550: // teleports outside (Isle of Conquest)
if (Player* target = unitTarget->ToPlayer())
{
@@ -1061,7 +1032,7 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/)
// If not exist data for dest location - return
if (!m_targets.HasDst())
{
- sLog->outError(LOG_FILTER_SPELLS_AURAS, "Spell::EffectTeleportUnits - does not have destination for spell ID %u\n", m_spellInfo->Id);
+ sLog->outError(LOG_FILTER_SPELLS_AURAS, "Spell::EffectTeleportUnits - does not have destination for spellId %u.", m_spellInfo->Id);
return;
}
@@ -1432,10 +1403,6 @@ void Spell::EffectHealPct(SpellEffIndex /*effIndex*/)
if (!m_originalCaster)
return;
- // Rune Tap - Party
- if (m_spellInfo->Id == 59754 && unitTarget == m_caster)
- return;
-
uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, unitTarget->CountPctFromMaxHealth(damage), HEAL);
heal = unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL);
@@ -3020,24 +2987,6 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex)
switch (m_spellInfo->SpellFamilyName)
{
- case SPELLFAMILY_GENERIC:
- {
- switch (m_spellInfo->Id)
- {
- case 69055: // Saber Lash
- case 70814: // Saber Lash
- {
- uint32 count = 0;
- for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
- if (ihit->effectMask & (1 << effIndex))
- ++count;
-
- totalDamagePercentMod /= count;
- break;
- }
- }
- break;
- }
case SPELLFAMILY_WARRIOR:
{
// Devastate (player ones)
@@ -3440,23 +3389,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
return;
unitTarget->RemoveAurasDueToSpell(m_spellInfo->Effects[effIndex].CalcValue());
break;
- // PX-238 Winter Wondervolt TRAP
- case 26275:
- {
- uint32 spells[4] = { 26272, 26157, 26273, 26274 };
-
- // check presence
- for (uint8 j = 0; j < 4; ++j)
- if (unitTarget->HasAuraEffect(spells[j], 0))
- return;
-
- // select spell
- uint32 iTmpSpellId = spells[urand(0, 3)];
-
- // cast
- unitTarget->CastSpell(unitTarget, iTmpSpellId, true);
- return;
- }
// Bending Shinbone
case 8856:
{
@@ -3502,14 +3434,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, 22682, true);
return;
}
- // Piccolo of the Flaming Fire
- case 17512:
- {
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE);
- return;
- }
// Decimate
case 28374:
case 54426:
@@ -3652,17 +3576,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
m_caster->MonsterTextEmote(buf, 0);
break;
}
- // Vigilance
- case 50725:
- {
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // Remove Taunt cooldown
- unitTarget->ToPlayer()->RemoveSpellCooldown(355, true);
-
- return;
- }
// Death Knight Initiate Visual
case 51519:
{
@@ -3821,17 +3734,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
}
return;
}
- case 63845: // Create Lance
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if (m_caster->ToPlayer()->GetTeam() == ALLIANCE)
- m_caster->CastSpell(m_caster, 63914, true);
- else
- m_caster->CastSpell(m_caster, 63919, true);
- return;
- }
case 59317: // Teleporting
{
@@ -4025,46 +3927,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
return;
}
}
- case SPELLFAMILY_POTION:
- {
- switch (m_spellInfo->Id)
- {
- // Netherbloom
- case 28702:
- {
- if (!unitTarget)
- return;
- // 25% chance of casting a random buff
- if (roll_chance_i(75))
- return;
-
- // triggered spells are 28703 to 28707
- // Note: some sources say, that there was the possibility of
- // receiving a debuff. However, this seems to be removed by a patch.
- const uint32 spellid = 28703;
-
- // don't overwrite an existing aura
- for (uint8 i = 0; i < 5; ++i)
- if (unitTarget->HasAura(spellid + i))
- return;
- unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
- break;
- }
-
- // Nightmare Vine
- case 28720:
- {
- if (!unitTarget)
- return;
- // 25% chance of casting Nightmare Pollen
- if (roll_chance_i(75))
- return;
- unitTarget->CastSpell(unitTarget, 28721, true);
- break;
- }
- }
- break;
- }
case SPELLFAMILY_DEATHKNIGHT:
{
// Pestilence
@@ -4085,19 +3947,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
}
break;
}
- case SPELLFAMILY_WARRIOR:
- {
- // Shattering Throw
- if (m_spellInfo->SpellFamilyFlags[1] & 0x00400000)
- {
- if (!unitTarget)
- return;
- // remove shields, will still display immune to damage part
- unitTarget->RemoveAurasWithMechanic(1<<MECHANIC_IMMUNE_SHIELD, AURA_REMOVE_BY_ENEMY_SPELL);
- return;
- }
- break;
- }
}
// normal DB scripted effect
@@ -4580,45 +4429,12 @@ void Spell::EffectResurrect(SpellEffIndex effIndex)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
- if (!unitTarget)
- return;
- if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
- if (unitTarget->isAlive())
- return;
- if (!unitTarget->IsInWorld())
+ if (unitTarget->isAlive() || !unitTarget->IsInWorld())
return;
- switch (m_spellInfo->Id)
- {
- // Defibrillate (Goblin Jumper Cables) have 33% chance on success
- case 8342:
- if (roll_chance_i(67))
- {
- m_caster->CastSpell(m_caster, 8338, true, m_CastItem);
- return;
- }
- break;
- // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
- case 22999:
- if (roll_chance_i(50))
- {
- m_caster->CastSpell(m_caster, 23055, true, m_CastItem);
- return;
- }
- break;
- // Defibrillate (Gnomish Army Knife) have 67% chance on success_list
- case 54732:
- if (roll_chance_i(33))
- {
- return;
- }
- break;
- default:
- break;
- }
-
Player* target = unitTarget->ToPlayer();
if (target->IsRessurectRequested()) // already have one active request
@@ -4810,25 +4626,24 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/)
void Spell::EffectCharge(SpellEffIndex /*effIndex*/)
{
+ if (!unitTarget)
+ return;
+
if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
{
- if (!unitTarget)
- return;
-
- float angle = unitTarget->GetRelativeAngle(m_caster);
- Position pos;
-
- unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
- unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), angle);
-
- m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ + unitTarget->GetObjectSize());
+ if (m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH)
+ {
+ Position pos;
+ unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), unitTarget->GetRelativeAngle(m_caster));
+ m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ }
+ else
+ m_caster->GetMotionMaster()->MoveCharge(m_preGeneratedPath);
}
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
{
- if (!unitTarget)
- return;
-
// not all charge effects used in negative spells
if (!m_spellInfo->IsPositive() && m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->Attack(unitTarget, true);
@@ -4864,26 +4679,10 @@ void Spell::EffectKnockBack(SpellEffIndex effIndex)
if (creatureTarget->isWorldBoss() || creatureTarget->IsDungeonBoss())
return;
- // Spells with SPELL_EFFECT_KNOCK_BACK(like Thunderstorm) can't knoback target if target has ROOT/STUN
+ // Spells with SPELL_EFFECT_KNOCK_BACK (like Thunderstorm) can't knockback target if target has ROOT/STUN
if (unitTarget->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
- // Typhoon
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x01000000)
- {
- // Glyph of Typhoon
- if (m_caster->HasAura(62135))
- return;
- }
-
- // Thunderstorm
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[1] & 0x00002000)
- {
- // Glyph of Thunderstorm
- if (m_caster->HasAura(62132))
- return;
- }
-
// Instantly interrupt non melee spells being casted
if (unitTarget->IsNonMeleeSpellCasted(true))
unitTarget->InterruptNonMeleeSpells(true);
@@ -5687,7 +5486,7 @@ void Spell::EffectRedirectThreat(SpellEffIndex /*effIndex*/)
return;
if (unitTarget)
- m_caster->SetReducedThreatPercent((uint32)damage, unitTarget->GetGUID());
+ m_caster->SetRedirectThreat(unitTarget->GetGUID(), uint32(damage));
}
void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/)
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index e85d0e2bfe6..aadc30e3921 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -386,7 +386,7 @@ bool SpellEffectInfo::IsAura() const
bool SpellEffectInfo::IsAura(AuraType aura) const
{
- return IsAura() && AuraType(ApplyAuraName) == uint32(aura);
+ return IsAura() && ApplyAuraName == uint32(aura);
}
bool SpellEffectInfo::IsTargetingArea() const
@@ -1007,7 +1007,7 @@ bool SpellInfo::IsLootCrafting() const
return (Effects[0].Effect == SPELL_EFFECT_CREATE_RANDOM_ITEM ||
// different random cards from Inscription (121==Virtuoso Inking Set category) r without explicit item
(Effects[0].Effect == SPELL_EFFECT_CREATE_ITEM_2 &&
- (TotemCategory[0] != 0 || Effects[0].ItemType == 0)));
+ ((TotemCategory[0] != 0 || (Totem[0] != 0 && SpellIconID == 1)) || Effects[0].ItemType == 0)));
}
bool SpellInfo::IsQuestTame() const
@@ -2095,6 +2095,9 @@ SpellSpecificType SpellInfo::GetSpellSpecific() const
case SPELL_AURA_AOE_CHARM:
return SPELL_SPECIFIC_CHARM;
case SPELL_AURA_TRACK_CREATURES:
+ /// @workaround For non-stacking tracking spells (We need generic solution)
+ if (Id == 30645) // Gas Cloud Tracking
+ return SPELL_SPECIFIC_NORMAL;
case SPELL_AURA_TRACK_RESOURCES:
case SPELL_AURA_TRACK_STEALTHED:
return SPELL_SPECIFIC_TRACKER;
@@ -2244,9 +2247,17 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c
return 0;
}
}
- SpellSchools school = GetFirstSchoolInMask(schoolMask);
- // Flat mod from caster auras by spell school
- powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school);
+
+ // Flat mod from caster auras by spell school and power type
+ Unit::AuraEffectList const& auras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL);
+ for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
+ {
+ if (!((*i)->GetMiscValue() & schoolMask))
+ continue;
+ if (!((*i)->GetMiscValueB() & (1 << PowerType)))
+ continue;
+ powerCost += (*i)->GetAmount();
+ }
// Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
if (AttributesEx4 & SPELL_ATTR4_SPELL_VS_EXTEND_COST)
powerCost += caster->GetAttackTime(OFF_ATTACK) / 100;
@@ -2257,8 +2268,16 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c
if (Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)
powerCost = int32(powerCost / (1.117f * SpellLevel / caster->getLevel() -0.1327f));
- // PCT mod from user auras by school
- powerCost = int32(powerCost * (1.0f + caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + school)));
+ // PCT mod from user auras by spell school and power type
+ Unit::AuraEffectList const& aurasPct = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT);
+ for (Unit::AuraEffectList::const_iterator i = aurasPct.begin(); i != aurasPct.end(); ++i)
+ {
+ if (!((*i)->GetMiscValue() & schoolMask))
+ continue;
+ if (!((*i)->GetMiscValueB() & (1 << PowerType)))
+ continue;
+ powerCost += CalculatePct(powerCost, (*i)->GetAmount());
+ }
if (powerCost < 0)
powerCost = 0;
return powerCost;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 4ce2604810c..b648a14fce5 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3382,7 +3382,7 @@ void SpellMgr::LoadSpellInfoCorrections()
break;
case 69055: // Saber Lash (Lord Marrowgar)
case 70814: // Saber Lash (Lord Marrowgar)
- spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_5_YARDS); // 5yd
+ spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_8_YARDS); // 8yd
break;
case 69075: // Bone Storm (Lord Marrowgar)
case 70834: // Bone Storm (Lord Marrowgar)
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 89ed223545f..6229a30a5f0 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -612,7 +612,7 @@ SpellValue const* SpellScript::GetSpellValue()
bool AuraScript::_Validate(SpellInfo const* entry)
{
for (std::list<CheckAreaTargetHandler>::iterator itr = DoCheckAreaTarget.begin(); itr != DoCheckAreaTarget.end(); ++itr)
- if (!entry->HasAreaAuraEffect())
+ if (!entry->HasAreaAuraEffect() && !entry->HasEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA))
sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have area aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
for (std::list<AuraDispelHandler>::iterator itr = OnDispel.begin(); itr != OnDispel.end(); ++itr)
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index f030b3dc6f2..55b4d3f2e22 100644
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -41,7 +41,7 @@ GmTicket::GmTicket(Player* player, WorldPacket& recvData) : _createTime(time(NUL
_playerGuid = player->GetGUID();
uint32 mapId;
- recvData >> mapId; // Map is sent as UInt32!
+ recvData >> mapId; // Map is sent as UInt32!
_mapId = mapId;
recvData >> _posX;
diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.cpp b/src/server/game/Tools/CharacterDatabaseCleaner.cpp
index 379b6fc6225..29d96cc0cfe 100644
--- a/src/server/game/Tools/CharacterDatabaseCleaner.cpp
+++ b/src/server/game/Tools/CharacterDatabaseCleaner.cpp
@@ -23,7 +23,6 @@
#include "Database/DatabaseEnv.h"
#include "SpellMgr.h"
#include "DBCStores.h"
-#include "AchievementMgr.h"
void CharacterDatabaseCleaner::CleanDatabase()
{
diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp
index a99688c8844..af695c482cf 100644
--- a/src/server/game/Warden/Warden.cpp
+++ b/src/server/game/Warden/Warden.cpp
@@ -219,7 +219,7 @@ std::string Warden::Penalty(WardenCheck* check /*= NULL*/)
void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData)
{
- _warden->DecryptData(const_cast<uint8*>(recvData.contents()), recvData.size());
+ _warden->DecryptData(recvData.contents(), recvData.size());
uint8 opcode;
recvData >> opcode;
sLog->outDebug(LOG_FILTER_WARDEN, "Got packet, opcode %02X, size %u", opcode, uint32(recvData.size()));
diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp
index c6542c68360..ec271192450 100644
--- a/src/server/game/Warden/WardenCheckMgr.cpp
+++ b/src/server/game/Warden/WardenCheckMgr.cpp
@@ -44,7 +44,6 @@ void WardenCheckMgr::LoadWardenChecks()
if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
{
sLog->outInfo(LOG_FILTER_WARDEN, ">> Warden disabled, loading checks skipped.");
-
return;
}
@@ -53,7 +52,6 @@ void WardenCheckMgr::LoadWardenChecks()
if (!result)
{
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Warden checks. DB table `warden_checks` is empty!");
-
return;
}
@@ -145,8 +143,7 @@ void WardenCheckMgr::LoadWardenChecks()
}
while (result->NextRow());
- sLog->outInfo(LOG_FILTER_WARDEN, ">> Loaded %u warden checks.", count);
-
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u warden checks.", count);
}
void WardenCheckMgr::LoadWardenOverrides()
@@ -155,7 +152,6 @@ void WardenCheckMgr::LoadWardenOverrides()
if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
{
sLog->outInfo(LOG_FILTER_WARDEN, ">> Warden disabled, loading check overrides skipped.");
-
return;
}
@@ -165,7 +161,6 @@ void WardenCheckMgr::LoadWardenOverrides()
if (!result)
{
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Warden action overrides. DB table `warden_action` is empty!");
-
return;
}
@@ -194,8 +189,7 @@ void WardenCheckMgr::LoadWardenOverrides()
}
while (result->NextRow());
- sLog->outInfo(LOG_FILTER_WARDEN, ">> Loaded %u warden action overrides.", count);
-
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u warden action overrides.", count);
}
WardenCheck* WardenCheckMgr::GetWardenDataById(uint16 Id)
diff --git a/src/server/game/Warden/WardenMac.cpp b/src/server/game/Warden/WardenMac.cpp
index 7c2979e0dc6..fa84cd216b6 100644
--- a/src/server/game/Warden/WardenMac.cpp
+++ b/src/server/game/Warden/WardenMac.cpp
@@ -206,7 +206,7 @@ void WardenMac::RequestData()
buff.hexlike();
// Encrypt with warden RC4 key.
- EncryptData(const_cast<uint8*>(buff.contents()), buff.size());
+ EncryptData(buff.contents(), buff.size());
WorldPacket pkt(SMSG_WARDEN_DATA, buff.size());
pkt.append(buff);
diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp
index 4da05eded0c..bf423459222 100644
--- a/src/server/game/Warden/WardenWin.cpp
+++ b/src/server/game/Warden/WardenWin.cpp
@@ -310,7 +310,7 @@ void WardenWin::RequestData()
buff.hexlike();
// Encrypt with warden RC4 key
- EncryptData(const_cast<uint8*>(buff.contents()), buff.size());
+ EncryptData(buff.contents(), buff.size());
WorldPacket pkt(SMSG_WARDEN_DATA, buff.size());
pkt.append(buff);
diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp
index 8bfa74d07e5..5eff13725b9 100644
--- a/src/server/game/Weather/WeatherMgr.cpp
+++ b/src/server/game/Weather/WeatherMgr.cpp
@@ -98,7 +98,6 @@ void LoadWeatherData()
if (!result)
{
sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
-
return;
}
@@ -142,7 +141,6 @@ void LoadWeatherData()
while (result->NextRow());
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u weather definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-
}
void SendFineWeatherUpdateToPlayer(Player* player)
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 4b027e9ebbf..4a1d1566af1 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -21,6 +21,7 @@
*/
#include "Common.h"
+#include "Memory.h"
#include "DatabaseEnv.h"
#include "Config.h"
#include "SystemConfig.h"
@@ -55,6 +56,7 @@
#include "TemporarySummon.h"
#include "WaypointMovementGenerator.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "GameEventMgr.h"
#include "PoolMgr.h"
#include "GridNotifiersImpl.h"
@@ -137,6 +139,7 @@ World::~World()
delete command;
VMAP::VMapFactory::clear();
+ MMAP::MMapFactory::clear();
//TODO free addSessQueue
}
@@ -266,7 +269,7 @@ void World::AddSession_(WorldSession* s)
if (decrease_session)
--Sessions;
- if (pLimit > 0 && Sessions >= pLimit && AccountMgr::IsPlayerAccount(s->GetSecurity()) && !HasRecentlyDisconnected(s))
+ if (pLimit > 0 && Sessions >= pLimit && !s->HasPermission(RBAC_PERM_SKIP_QUEUE) && !HasRecentlyDisconnected(s))
{
AddQueuedPlayer(s);
UpdateMaxSessionCounters();
@@ -584,7 +587,6 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_TICKET_LEVEL_REQ] = ConfigMgr::GetIntDefault("LevelReq.Ticket", 1);
m_int_configs[CONFIG_AUCTION_LEVEL_REQ] = ConfigMgr::GetIntDefault("LevelReq.Auction", 1);
m_int_configs[CONFIG_MAIL_LEVEL_REQ] = ConfigMgr::GetIntDefault("LevelReq.Mail", 1);
- m_bool_configs[CONFIG_ALLOW_PLAYER_COMMANDS] = ConfigMgr::GetBoolDefault("AllowPlayerCommands", 1);
m_bool_configs[CONFIG_PRESERVE_CUSTOM_CHANNELS] = ConfigMgr::GetBoolDefault("PreserveCustomChannels", false);
m_int_configs[CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION] = ConfigMgr::GetIntDefault("PreserveCustomChannelDuration", 14);
m_bool_configs[CONFIG_GRID_UNLOAD] = ConfigMgr::GetBoolDefault("GridUnload", true);
@@ -773,7 +775,7 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_START_PLAYER_MONEY] = ConfigMgr::GetIntDefault("StartPlayerMoney", 0);
if (int32(m_int_configs[CONFIG_START_PLAYER_MONEY]) < 0)
{
- sLog->outError(LOG_FILTER_SERVER_LOADING, "StartPlayerMoney (%i) must be in range 0.." UI64FMTD ". Set to %u.", m_int_configs[CONFIG_START_PLAYER_MONEY], MAX_MONEY_AMOUNT, 0);
+ sLog->outError(LOG_FILTER_SERVER_LOADING, "StartPlayerMoney (%i) must be in range 0.." UI64FMTD ". Set to %u.", m_int_configs[CONFIG_START_PLAYER_MONEY], uint64(MAX_MONEY_AMOUNT), 0);
m_int_configs[CONFIG_START_PLAYER_MONEY] = 0;
}
else if (m_int_configs[CONFIG_START_PLAYER_MONEY] > 0x7FFFFFFF-1) // TODO: (See MAX_MONEY_AMOUNT)
@@ -1078,8 +1080,6 @@ void World::LoadConfigSettings(bool reload)
sLog->outError(LOG_FILTER_SERVER_LOADING, "ClientCacheVersion can't be negative %d, ignored.", clientCacheId);
}
- m_int_configs[CONFIG_INSTANT_LOGOUT] = ConfigMgr::GetIntDefault("InstantLogout", SEC_MODERATOR);
-
m_int_configs[CONFIG_GUILD_NEWS_LOG_COUNT] = ConfigMgr::GetIntDefault("Guild.NewsLogRecordsCount", GUILD_NEWSLOG_MAX_RECORDS);
if (m_int_configs[CONFIG_GUILD_NEWS_LOG_COUNT] > GUILD_NEWSLOG_MAX_RECORDS)
m_int_configs[CONFIG_GUILD_NEWS_LOG_COUNT] = GUILD_NEWSLOG_MAX_RECORDS;
@@ -1143,6 +1143,15 @@ void World::LoadConfigSettings(bool reload)
if (dataPath.at(dataPath.length()-1) != '/' && dataPath.at(dataPath.length()-1) != '\\')
dataPath.push_back('/');
+#if PLATFORM == PLATFORM_UNIX || PLATFORM == PLATFORM_APPLE
+ if (dataPath[0] == '~')
+ {
+ const char* home = getenv("HOME");
+ if (home)
+ dataPath.replace(0, 1, home);
+ }
+#endif
+
if (reload)
{
if (dataPath != m_dataPath)
@@ -1154,22 +1163,23 @@ void World::LoadConfigSettings(bool reload)
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Using DataDir %s", m_dataPath.c_str());
}
+ m_bool_configs[CONFIG_ENABLE_MMAPS] = ConfigMgr::GetBoolDefault("mmap.enablePathFinding", false);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "WORLD: MMap data directory is: %smmaps", m_dataPath.c_str());
+
m_bool_configs[CONFIG_VMAP_INDOOR_CHECK] = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", 0);
bool enableIndoor = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", true);
bool enableLOS = ConfigMgr::GetBoolDefault("vmap.enableLOS", true);
bool enableHeight = ConfigMgr::GetBoolDefault("vmap.enableHeight", true);
- bool enablePetLOS = ConfigMgr::GetBoolDefault("vmap.petLOS", true);
if (!enableHeight)
sLog->outError(LOG_FILTER_SERVER_LOADING, "VMap height checking disabled! Creatures movements and other various things WILL be broken! Expect no support.");
VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS);
VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight);
- sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight: %i, getHeight: %i, indoorCheck: %i PetLOS: %i", enableLOS, enableHeight, enableIndoor, enablePetLOS);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight: %i, getHeight: %i, indoorCheck: %i", enableLOS, enableHeight, enableIndoor);
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap data directory is: %svmaps", m_dataPath.c_str());
m_int_configs[CONFIG_MAX_WHO] = ConfigMgr::GetIntDefault("MaxWhoListReturns", 49);
- m_bool_configs[CONFIG_PET_LOS] = ConfigMgr::GetBoolDefault("vmap.petLOS", true);
m_bool_configs[CONFIG_START_ALL_SPELLS] = ConfigMgr::GetBoolDefault("PlayerStart.AllSpells", false);
if (m_bool_configs[CONFIG_START_ALL_SPELLS])
sLog->outWarn(LOG_FILTER_SERVER_LOADING, "PlayerStart.AllSpells enabled - may not function as intended!");
@@ -1245,6 +1255,7 @@ void World::LoadConfigSettings(bool reload)
// misc
m_bool_configs[CONFIG_PDUMP_NO_PATHS] = ConfigMgr::GetBoolDefault("PlayerDump.DisallowPaths", true);
m_bool_configs[CONFIG_PDUMP_NO_OVERWRITE] = ConfigMgr::GetBoolDefault("PlayerDump.DisallowOverwrite", true);
+ m_bool_configs[CONFIG_UI_QUESTLEVELS_IN_DIALOGS] = ConfigMgr::GetBoolDefault("UI.ShowQuestLevelsInDialogs", false);
// call ScriptMgr if we're reloading the configuration
m_bool_configs[CONFIG_WINTERGRASP_ENABLE] = ConfigMgr::GetBoolDefault("Wintergrasp.Enable", false);
@@ -1270,6 +1281,9 @@ void World::SetInitialWorldSettings()
///- Initialize the random number generator
srand((unsigned int)time(NULL));
+ ///- Initialize detour memory management
+ dtAllocSetCustom(dtCustomAlloc, dtCustomFree);
+
///- Initialize config settings
LoadConfigSettings();
@@ -1365,6 +1379,8 @@ void World::SetInitialWorldSettings()
sObjectMgr->SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Localization strings loaded in %u ms", GetMSTimeDiffToNow(oldMSTime));
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Account Roles and Permissions...");
+ sAccountMgr->LoadRBAC();
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Page Texts...");
sObjectMgr->LoadPageTexts();
@@ -1414,8 +1430,8 @@ void World::SetInitialWorldSettings()
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Item Random Enchantments Table...");
LoadRandomEnchantmentsTable();
- sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Disables");
- DisableMgr::LoadDisables(); // must be before loading quests and items
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Disables"); // must be before loading quests and items
+ DisableMgr::LoadDisables();
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Items..."); // must be after LoadRandomEnchantmentsTable and LoadPageTexts
sObjectMgr->LoadItemTemplates();
@@ -1531,6 +1547,9 @@ void World::SetInitialWorldSettings()
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Graveyard-zone links...");
sObjectMgr->LoadGraveyardZones();
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Graveyard Orientations...");
+ sObjectMgr->LoadGraveyardOrientations();
+
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading spell pet auras...");
sSpellMgr->LoadSpellPetAuras();
@@ -1598,6 +1617,7 @@ void World::SetInitialWorldSettings()
///- Load dynamic data tables from the database
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Item Auctions...");
sAuctionMgr->LoadAuctionItems();
+
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Auctions...");
sAuctionMgr->LoadAuctions();
@@ -1607,6 +1627,7 @@ void World::SetInitialWorldSettings()
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Guild rewards...");
sGuildMgr->LoadGuildRewards();
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Guilds...");
sGuildMgr->LoadGuilds();
sGuildFinderMgr->LoadFromDB();
@@ -2948,8 +2969,6 @@ void World::ResetMonthlyQuests()
if (itr->second->GetPlayer())
itr->second->GetPlayer()->ResetMonthlyQuestStatus();
- time_t mostRecentQuestTime = 0;
-
// generate time
time_t curTime = time(NULL);
tm localTm = *localtime(&curTime);
@@ -2976,14 +2995,8 @@ void World::ResetMonthlyQuests()
time_t nextMonthResetTime = mktime(&localTm);
- // last reset time before current moment
- time_t resetTime = (curTime < nextMonthResetTime) ? nextMonthResetTime - MONTH : nextMonthResetTime;
-
- // need reset (if we have quest time before last reset time (not processed by some reason)
- if (mostRecentQuestTime && mostRecentQuestTime <= resetTime)
- m_NextMonthlyQuestReset = mostRecentQuestTime;
- else // plan next reset time
- m_NextMonthlyQuestReset = (curTime >= nextMonthResetTime) ? nextMonthResetTime + MONTH : nextMonthResetTime;
+ // plan next reset time
+ m_NextMonthlyQuestReset = (curTime >= nextMonthResetTime) ? nextMonthResetTime + MONTH : nextMonthResetTime;
sWorld->setWorldState(WS_MONTHLY_QUEST_RESET_TIME, uint64(m_NextMonthlyQuestReset));
}
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index cca1ac39728..6c86057eefb 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -145,7 +145,6 @@ enum WorldBoolConfigs
CONFIG_ARENA_LOG_EXTENDED_INFO,
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
CONFIG_VMAP_INDOOR_CHECK,
- CONFIG_PET_LOS,
CONFIG_START_ALL_SPELLS,
CONFIG_START_ALL_EXPLORED,
CONFIG_START_ALL_REP,
@@ -171,8 +170,10 @@ enum WorldBoolConfigs
CONFIG_QUEST_IGNORE_AUTO_ACCEPT,
CONFIG_QUEST_IGNORE_AUTO_COMPLETE,
CONFIG_WARDEN_ENABLED,
+ CONFIG_ENABLE_MMAPS,
CONFIG_WINTERGRASP_ENABLE,
CONFIG_GUILD_LEVELING_ENABLED,
+ CONFIG_UI_QUESTLEVELS_IN_DIALOGS, // Should we add quest levels to the title in the NPC dialogs?
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 5ccd244ef7f..a32a42b8172 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -45,6 +45,8 @@ message("")
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/scripts/Commands/CMakeLists.txt b/src/server/scripts/Commands/CMakeLists.txt
index 97e9b35e6cd..83e97b2c80d 100644
--- a/src/server/scripts/Commands/CMakeLists.txt
+++ b/src/server/scripts/Commands/CMakeLists.txt
@@ -35,6 +35,7 @@ set(scripts_STAT_SRCS
Commands/cs_modify.cpp
Commands/cs_npc.cpp
Commands/cs_quest.cpp
+ Commands/cs_rbac.cpp
Commands/cs_reload.cpp
Commands/cs_reset.cpp
Commands/cs_tele.cpp
@@ -42,6 +43,7 @@ set(scripts_STAT_SRCS
Commands/cs_server.cpp
Commands/cs_titles.cpp
Commands/cs_wp.cpp
+ Commands/cs_mmaps.cpp
# Commands/cs_pdump.cpp
# Commands/cs_channel.cpp
# Commands/cs_pet.cpp
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 3a20a03bb4a..4dc44bbfc58 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -106,7 +106,7 @@ public:
if (!accountName || !password)
return false;
- AccountOpResult result = AccountMgr::CreateAccount(std::string(accountName), std::string(password));
+ AccountOpResult result = sAccountMgr->CreateAccount(std::string(accountName), std::string(password));
switch (result)
{
case AOR_OK:
@@ -503,36 +503,8 @@ public:
return false;
}
- // If gmRealmID is -1, delete all values for the account id, else, insert values for the specific realmID
- PreparedStatement* stmt;
-
- if (gmRealmID == -1)
- {
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS);
-
- stmt->setUInt32(0, targetAccountId);
- }
- else
- {
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM);
-
- stmt->setUInt32(0, targetAccountId);
- stmt->setUInt32(1, realmID);
- }
-
- LoginDatabase.Execute(stmt);
-
- if (gm != 0)
- {
- stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_ACCESS);
-
- stmt->setUInt32(0, targetAccountId);
- stmt->setUInt8(1, uint8(gm));
- stmt->setInt32(2, gmRealmID);
-
- LoginDatabase.Execute(stmt);
- }
-
+ RBACData* rbac = isAccountNameGiven ? NULL : handler->getSelectedPlayer()->GetSession()->GetRBACData();
+ sAccountMgr->UpdateAccountAccess(rbac, targetAccountId, uint8(gm), gmRealmID);
handler->PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm);
return true;
diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp
index 37e282cac8e..34738777c85 100644
--- a/src/server/scripts/Commands/cs_disable.cpp
+++ b/src/server/scripts/Commands/cs_disable.cpp
@@ -48,6 +48,7 @@ public:
{ "achievement_criteria", SEC_ADMINISTRATOR, true, &HandleRemoveDisableAchievementCriteriaCommand, "", NULL },
{ "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleRemoveDisableOutdoorPvPCommand, "", NULL },
{ "vmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableVmapCommand, "", NULL },
+ { "mmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableMMapCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
static ChatCommand addDisableCommandTable[] =
@@ -59,6 +60,7 @@ public:
{ "achievement_criteria", SEC_ADMINISTRATOR, true, &HandleAddDisableAchievementCriteriaCommand, "", NULL },
{ "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleAddDisableOutdoorPvPCommand, "", NULL },
{ "vmap", SEC_ADMINISTRATOR, true, &HandleAddDisableVmapCommand, "", NULL },
+ { "mmap", SEC_ADMINISTRATOR, true, &HandleAddDisableMMapCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
static ChatCommand disableCommandTable[] =
@@ -172,6 +174,17 @@ public:
disableTypeStr = "vmap";
break;
}
+ case DISABLE_TYPE_MMAP:
+ {
+ if (!sMapStore.LookupEntry(entry))
+ {
+ handler->PSendSysMessage(LANG_COMMAND_NOMAPFOUND);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+ disableTypeStr = "mmap";
+ break;
+ }
default:
break;
}
@@ -256,6 +269,14 @@ public:
return HandleAddDisables(handler, args, DISABLE_TYPE_VMAP);
}
+ static bool HandleAddDisableMMapCommand(ChatHandler* handler, char const* args)
+ {
+ if (!*args)
+ return false;
+
+ return HandleAddDisables(handler, args, DISABLE_TYPE_MMAP);
+ }
+
static bool HandleRemoveDisables(ChatHandler* handler, char const* args, uint8 disableType)
{
char* entryStr = strtok((char*)args, " ");
@@ -289,6 +310,9 @@ public:
case DISABLE_TYPE_VMAP:
disableTypeStr = "vmap";
break;
+ case DISABLE_TYPE_MMAP:
+ disableTypeStr = "mmap";
+ break;
}
PreparedStatement* stmt = NULL;
@@ -367,6 +391,14 @@ public:
return HandleRemoveDisables(handler, args, DISABLE_TYPE_VMAP);
}
+
+ static bool HandleRemoveDisableMMapCommand(ChatHandler* handler, char const* args)
+ {
+ if (!*args)
+ return false;
+
+ return HandleRemoveDisables(handler, args, DISABLE_TYPE_MMAP);
+ }
};
void AddSC_disable_commandscript()
diff --git a/src/server/scripts/Commands/cs_lfg.cpp b/src/server/scripts/Commands/cs_lfg.cpp
index 7f39a8fc024..aa9d9308bcd 100644
--- a/src/server/scripts/Commands/cs_lfg.cpp
+++ b/src/server/scripts/Commands/cs_lfg.cpp
@@ -28,12 +28,12 @@ void GetPlayerInfo(ChatHandler* handler, Player* player)
return;
uint64 guid = player->GetGUID();
- LfgDungeonSet dungeons = sLFGMgr->GetSelectedDungeons(guid);
+ lfg::LfgDungeonSet dungeons = sLFGMgr->GetSelectedDungeons(guid);
- std::string const& state = sLFGMgr->GetStateString(sLFGMgr->GetState(guid));
+ std::string const& state = lfg::GetStateString(sLFGMgr->GetState(guid));
handler->PSendSysMessage(LANG_LFG_PLAYER_INFO, player->GetName().c_str(),
- state.c_str(), uint8(dungeons.size()), sLFGMgr->ConcatenateDungeons(dungeons).c_str(),
- sLFGMgr->GetRolesString(sLFGMgr->GetRoles(guid)).c_str(), sLFGMgr->GetComment(guid).c_str());
+ state.c_str(), uint8(dungeons.size()), lfg::ConcatenateDungeons(dungeons).c_str(),
+ lfg::GetRolesString(sLFGMgr->GetRoles(guid)).c_str(), sLFGMgr->GetComment(guid).c_str());
}
class lfg_commandscript : public CommandScript
@@ -87,7 +87,7 @@ public:
}
uint64 guid = grp->GetGUID();
- std::string const& state = sLFGMgr->GetStateString(sLFGMgr->GetState(guid));
+ std::string const& state = lfg::GetStateString(sLFGMgr->GetState(guid));
handler->PSendSysMessage(LANG_LFG_GROUP_INFO, grp->isLFGGroup(),
state.c_str(), sLFGMgr->GetDungeon(guid));
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index a1098cff5d3..3acfe9c14ba 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -33,6 +33,8 @@
#include "ace/INET_Addr.h"
#include "Player.h"
#include "Pet.h"
+#include "LFG.h"
+#include "GroupMgr.h"
class misc_commandscript : public CommandScript
{
@@ -46,6 +48,8 @@ public:
{ "leader", SEC_ADMINISTRATOR, false, &HandleGroupLeaderCommand, "", NULL },
{ "disband", SEC_ADMINISTRATOR, false, &HandleGroupDisbandCommand, "", NULL },
{ "remove", SEC_ADMINISTRATOR, false, &HandleGroupRemoveCommand, "", NULL },
+ { "join", SEC_ADMINISTRATOR, false, &HandleGroupJoinCommand, "", NULL },
+ { "list", SEC_ADMINISTRATOR, false, &HandleGroupListCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
static ChatCommand petCommandTable[] =
@@ -1545,6 +1549,8 @@ public:
std::string userName = handler->GetTrinityString(LANG_ERROR);
std::string eMail = handler->GetTrinityString(LANG_ERROR);
+ std::string muteReason = "";
+ std::string muteBy = "";
std::string lastIp = handler->GetTrinityString(LANG_ERROR);
uint32 security = 0;
std::string lastLogin = handler->GetTrinityString(LANG_ERROR);
@@ -1561,6 +1567,8 @@ public:
security = fields[1].GetUInt8();
eMail = fields[2].GetString();
muteTime = fields[5].GetUInt64();
+ muteReason = fields[6].GetString();
+ muteBy = fields[7].GetString();
if (eMail.empty())
eMail = "-";
@@ -1622,7 +1630,7 @@ public:
}
if (muteTime > 0)
- handler->PSendSysMessage(LANG_PINFO_MUTE, secsToTimeString(muteTime - time(NULL), true).c_str());
+ handler->PSendSysMessage(LANG_PINFO_MUTE, secsToTimeString(muteTime - time(NULL), true).c_str(), muteBy.c_str(), muteReason.c_str());
if (banTime >= 0)
handler->PSendSysMessage(LANG_PINFO_BAN, banTime > 0 ? secsToTimeString(banTime - time(NULL), true).c_str() : "permanently", bannedby.c_str(), banreason.c_str());
@@ -1734,6 +1742,23 @@ public:
else
handler->PSendSysMessage(LANG_PINFO_MAP_OFFLINE, map->name, areaName.c_str());
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED);
+ stmt->setUInt32(0, GUID_LOPART(targetGuid));
+
+ result = CharacterDatabase.Query(stmt);
+ if (result)
+ {
+ Field* fields = result->Fetch();
+
+ uint32 guildId = fields[0].GetUInt32();
+ std::string guildName = fields[1].GetString();
+ std::string guildRank = fields[2].GetString();
+ std::string note = fields[3].GetString();
+ std::string officeNote = fields[4].GetString();
+
+ handler->PSendSysMessage(LANG_PINFO_GUILD_INFO, guildName.c_str(), guildId, guildRank.c_str(), note.c_str(), officeNote.c_str());
+ }
+
return true;
}
@@ -1803,6 +1828,11 @@ public:
return false;
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
+ std::string muteBy = "";
+ if (handler->GetSession())
+ muteBy = handler->GetSession()->GetPlayerName();
+ else
+ muteBy = "Console";
if (target)
{
@@ -1810,7 +1840,7 @@ public:
int64 muteTime = time(NULL) + notSpeakTime * MINUTE;
target->GetSession()->m_muteTime = muteTime;
stmt->setInt64(0, muteTime);
- ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notSpeakTime, muteReasonStr.c_str());
+ ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notSpeakTime, muteBy.c_str(), muteReasonStr.c_str());
}
else
{
@@ -1819,7 +1849,9 @@ public:
stmt->setInt64(0, muteTime);
}
- stmt->setUInt32(1, accountId);
+ stmt->setString(1, muteReasonStr.c_str());
+ stmt->setString(2, muteBy.c_str());
+ stmt->setUInt32(3, accountId);
LoginDatabase.Execute(stmt);
std::string nameLink = handler->playerLink(targetName);
@@ -1862,7 +1894,9 @@ public:
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
stmt->setInt64(0, 0);
- stmt->setUInt32(1, accountId);
+ stmt->setString(1, "");
+ stmt->setString(2, "");
+ stmt->setUInt32(3, accountId);
LoginDatabase.Execute(stmt);
if (target)
@@ -2723,6 +2757,132 @@ public:
return true;
}
+ static bool HandleGroupJoinCommand(ChatHandler* handler, char const* args)
+ {
+ if (!*args)
+ return false;
+
+ Player* playerSource = NULL;
+ Player* playerTarget = NULL;
+ Group* groupSource = NULL;
+ Group* groupTarget = NULL;
+ uint64 guidSource = 0;
+ uint64 guidTarget = 0;
+ char* nameplgrStr = strtok((char*)args, " ");
+ char* nameplStr = strtok(NULL, " ");
+
+ if (handler->GetPlayerGroupAndGUIDByName(nameplgrStr, playerSource, groupSource, guidSource, true))
+ {
+ if (groupSource)
+ {
+ if (handler->GetPlayerGroupAndGUIDByName(nameplStr, playerTarget, groupTarget, guidTarget, true))
+ {
+ if (!groupTarget && playerTarget->GetGroup() != groupSource)
+ {
+ if (!groupSource->IsFull())
+ {
+ groupSource->AddMember(playerTarget);
+ groupSource->BroadcastGroupUpdate();
+ handler->PSendSysMessage(LANG_GROUP_PLAYER_JOINED, playerTarget->GetName().c_str(), playerSource->GetName().c_str());
+ return true;
+ }
+ else
+ {
+ // group is full
+ handler->PSendSysMessage(LANG_GROUP_FULL);
+ return true;
+ }
+ }
+ else
+ {
+ // group is full or target player already in a group
+ handler->PSendSysMessage(LANG_GROUP_ALREADY_IN_GROUP, playerTarget->GetName().c_str());
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // specified source player is not in a group
+ handler->PSendSysMessage(LANG_GROUP_NOT_IN_GROUP, playerSource->GetName().c_str());
+ return true;
+ }
+ }
+
+ return true;
+ }
+
+ static bool HandleGroupListCommand(ChatHandler* handler, char const* args)
+ {
+ Player* playerTarget;
+ uint64 guidTarget;
+ std::string nameTarget;
+
+ uint32 parseGUID = MAKE_NEW_GUID(atol((char*)args), 0, HIGHGUID_PLAYER);
+
+ if (sObjectMgr->GetPlayerNameByGUID(parseGUID, nameTarget))
+ {
+ playerTarget = sObjectMgr->GetPlayerByLowGUID(parseGUID);
+ guidTarget = parseGUID;
+ }
+ else if (!handler->extractPlayerTarget((char*)args, &playerTarget, &guidTarget, &nameTarget))
+ return false;
+
+ Group* groupTarget = NULL;
+ if (playerTarget)
+ groupTarget = playerTarget->GetGroup();
+
+ if (!groupTarget)
+ {
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GROUP_MEMBER);
+ stmt->setUInt32(0, guidTarget);
+ PreparedQueryResult resultGroup = CharacterDatabase.Query(stmt);
+ if (resultGroup)
+ groupTarget = sGroupMgr->GetGroupByDbStoreId((*resultGroup)[0].GetUInt32());
+ }
+
+ if (groupTarget)
+ {
+ handler->PSendSysMessage(LANG_GROUP_TYPE, (groupTarget->isRaidGroup() ? "raid" : "party"));
+ Group::MemberSlotList const& members = groupTarget->GetMemberSlots();
+ for (Group::MemberSlotList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Group::MemberSlot const& slot = *itr;
+
+ std::string flags;
+ if (slot.flags & MEMBER_FLAG_ASSISTANT)
+ flags = "Assistant";
+
+ if (slot.flags & MEMBER_FLAG_MAINTANK)
+ {
+ if (!flags.empty())
+ flags.append(", ");
+ flags.append("MainTank");
+ }
+
+ if (slot.flags & MEMBER_FLAG_MAINASSIST)
+ {
+ if (!flags.empty())
+ flags.append(", ");
+ flags.append("MainAssist");
+ }
+
+ if (flags.empty())
+ flags = "None";
+
+ Player* p = ObjectAccessor::FindPlayer((*itr).guid);
+ const char* onlineState = (p && p->IsInWorld()) ? "online" : "offline";
+
+ handler->PSendSysMessage(LANG_GROUP_PLAYER_NAME_GUID, slot.name.c_str(), onlineState,
+ GUID_LOPART(slot.guid), flags.c_str(), lfg::GetRolesString(slot.roles).c_str());
+ }
+ }
+ else
+ handler->PSendSysMessage(LANG_GROUP_NOT_IN_GROUP, nameTarget.c_str());
+
+ return true;
+ }
+
static bool HandlePlayAllCommand(ChatHandler* handler, char const* args)
{
if (!*args)
diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp
new file mode 100644
index 00000000000..97861133983
--- /dev/null
+++ b/src/server/scripts/Commands/cs_mmaps.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+* @file cs_mmaps.cpp
+* @brief .mmap related commands
+*
+* This file contains the CommandScripts for all
+* mmap sub-commands
+*/
+
+#include "ScriptMgr.h"
+#include "Chat.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "PointMovementGenerator.h"
+#include "PathGenerator.h"
+#include "MMapFactory.h"
+#include "Map.h"
+#include "TargetedMovementGenerator.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+class mmaps_commandscript : public CommandScript
+{
+public:
+ mmaps_commandscript() : CommandScript("mmaps_commandscript") { }
+
+ ChatCommand* GetCommands() const
+ {
+ static ChatCommand mmapCommandTable[] =
+ {
+ { "path", SEC_ADMINISTRATOR, false, &HandleMmapPathCommand, "", NULL },
+ { "loc", SEC_ADMINISTRATOR, false, &HandleMmapLocCommand, "", NULL },
+ { "loadedtiles", SEC_ADMINISTRATOR, false, &HandleMmapLoadedTilesCommand, "", NULL },
+ { "stats", SEC_ADMINISTRATOR, false, &HandleMmapStatsCommand, "", NULL },
+ { "testarea", SEC_ADMINISTRATOR, false, &HandleMmapTestArea, "", NULL },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "mmap", SEC_ADMINISTRATOR, true, NULL, "", mmapCommandTable },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+ return commandTable;
+ }
+
+ static bool HandleMmapPathCommand(ChatHandler* handler, char const* args)
+ {
+ if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ handler->PSendSysMessage("mmap path:");
+
+ // units
+ Player* player = handler->GetSession()->GetPlayer();
+ Unit* target = handler->getSelectedUnit();
+ if (!player || !target)
+ {
+ handler->PSendSysMessage("Invalid target/source selection.");
+ return true;
+ }
+
+ char* para = strtok((char*)args, " ");
+
+ bool useStraightPath = false;
+ if (para && strcmp(para, "true") == 0)
+ useStraightPath = true;
+
+ // unit locations
+ float x, y, z;
+ player->GetPosition(x, y, z);
+
+ // path
+ PathGenerator path(target);
+ path.SetUseStraightPath(useStraightPath);
+ bool result = path.CalculatePath(x, y, z);
+
+ PointsArray pointPath = path.GetPath();
+ handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str());
+ handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : "SmoothPath");
+ handler->PSendSysMessage("Result: %s - Length: "SIZEFMTD" - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType());
+
+ Vector3 start = path.GetStartPosition();
+ Vector3 end = path.GetEndPosition();
+ Vector3 actualEnd = path.GetActualEndPosition();
+
+ handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z);
+ handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z);
+ handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z);
+
+ if (!player->isGameMaster())
+ handler->PSendSysMessage("Enable GM mode to see the path points.");
+
+ for (uint32 i = 0; i < pointPath.size(); ++i)
+ player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000);
+
+ return true;
+ }
+
+ static bool HandleMmapLocCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ handler->PSendSysMessage("mmap tileloc:");
+
+ // grid tile location
+ Player* player = handler->GetSession()->GetPlayer();
+
+ int32 gx = 32 - player->GetPositionX() / SIZE_OF_GRIDS;
+ int32 gy = 32 - player->GetPositionY() / SIZE_OF_GRIDS;
+
+ handler->PSendSysMessage("%03u%02i%02i.mmtile", player->GetMapId(), gy, gx);
+ handler->PSendSysMessage("gridloc [%i,%i]", gx, gy);
+
+ // calculate navmesh tile location
+ dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
+ dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId());
+ if (!navmesh || !navmeshquery)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ float const* min = navmesh->getParams()->orig;
+ float x, y, z;
+ player->GetPosition(x, y, z);
+ float location[VERTEX_SIZE] = {y, z, x};
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f};
+
+ int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS);
+ int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS);
+
+ handler->PSendSysMessage("Calc [%02i,%02i]", tilex, tiley);
+
+ // navmesh poly -> navmesh tile location
+ dtQueryFilter filter = dtQueryFilter();
+ dtPolyRef polyRef = INVALID_POLYREF;
+ navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, NULL);
+
+ if (polyRef == INVALID_POLYREF)
+ handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)");
+ else
+ {
+ dtMeshTile const* tile;
+ dtPoly const* poly;
+ navmesh->getTileAndPolyByRef(polyRef, &tile, &poly);
+ if (tile)
+ handler->PSendSysMessage("Dt [%02i,%02i]", tile->header->x, tile->header->y);
+ else
+ handler->PSendSysMessage("Dt [??,??] (no tile loaded)");
+ }
+
+ return true;
+ }
+
+ static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId();
+ dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid);
+ dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId());
+ if (!navmesh || !navmeshquery)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ handler->PSendSysMessage("mmap loadedtiles:");
+
+ for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
+ {
+ dtMeshTile const* tile = navmesh->getTile(i);
+ if (!tile || !tile->header)
+ continue;
+
+ handler->PSendSysMessage("[%02i,%02i]", tile->header->x, tile->header->y);
+ }
+
+ return true;
+ }
+
+ static bool HandleMmapStatsCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ uint32 mapId = handler->GetSession()->GetPlayer()->GetMapId();
+ handler->PSendSysMessage("mmap stats:");
+ handler->PSendSysMessage(" global mmap pathfinding is %sabled", MMAP::MMapFactory::IsPathfindingEnabled(mapId) ? "en" : "dis");
+
+ MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager();
+ handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
+
+ dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
+ if (!navmesh)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ uint32 tileCount = 0;
+ uint32 nodeCount = 0;
+ uint32 polyCount = 0;
+ uint32 vertCount = 0;
+ uint32 triCount = 0;
+ uint32 triVertCount = 0;
+ uint32 dataSize = 0;
+ for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
+ {
+ dtMeshTile const* tile = navmesh->getTile(i);
+ if (!tile || !tile->header)
+ continue;
+
+ tileCount++;
+ nodeCount += tile->header->bvNodeCount;
+ polyCount += tile->header->polyCount;
+ vertCount += tile->header->vertCount;
+ triCount += tile->header->detailTriCount;
+ triVertCount += tile->header->detailVertCount;
+ dataSize += tile->dataSize;
+ }
+
+ handler->PSendSysMessage("Navmesh stats:");
+ handler->PSendSysMessage(" %u tiles loaded", tileCount);
+ handler->PSendSysMessage(" %u BVTree nodes", nodeCount);
+ handler->PSendSysMessage(" %u polygons (%u vertices)", polyCount, vertCount);
+ handler->PSendSysMessage(" %u triangles (%u vertices)", triCount, triVertCount);
+ handler->PSendSysMessage(" %.2f MB of data (not including pointers)", ((float)dataSize / sizeof(unsigned char)) / 1048576);
+
+ return true;
+ }
+
+ static bool HandleMmapTestArea(ChatHandler* handler, char const* /*args*/)
+ {
+ float radius = 40.0f;
+ WorldObject* object = handler->GetSession()->GetPlayer();
+
+ CellCoord pair(Trinity::ComputeCellCoord(object->GetPositionX(), object->GetPositionY()));
+ Cell cell(pair);
+ cell.SetNoCreate();
+
+ std::list<Creature*> creatureList;
+
+ Trinity::AnyUnitInObjectRangeCheck go_check(object, radius);
+ Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck> go_search(object, creatureList, go_check);
+ TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck>, GridTypeMapContainer> go_visit(go_search);
+
+ // Get Creatures
+ cell.Visit(pair, go_visit, *(object->GetMap()), *object, radius);
+
+ if (!creatureList.empty())
+ {
+ handler->PSendSysMessage("Found "SIZEFMTD" Creatures.", creatureList.size());
+
+ uint32 paths = 0;
+ uint32 uStartTime = getMSTime();
+
+ float gx, gy, gz;
+ object->GetPosition(gx, gy, gz);
+ for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr)
+ {
+ PathGenerator path(*itr);
+ path.CalculatePath(gx, gy, gz);
+ ++paths;
+ }
+
+ uint32 uPathLoadTime = getMSTimeDiff(uStartTime, getMSTime());
+ handler->PSendSysMessage("Generated %i paths in %i ms", paths, uPathLoadTime);
+ }
+ else
+ handler->PSendSysMessage("No creatures in %f yard range.", radius);
+
+ return true;
+ }
+};
+
+void AddSC_mmaps_commandscript()
+{
+ new mmaps_commandscript();
+}
diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp
index 907ddd2f6f2..466cd318b68 100644
--- a/src/server/scripts/Commands/cs_modify.cpp
+++ b/src/server/scripts/Commands/cs_modify.cpp
@@ -1048,9 +1048,12 @@ public:
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, handler->GetNameLink().c_str(), uint32(moneyToAdd));
if (moneyToAdd >= MAX_MONEY_AMOUNT)
- target->SetMoney(MAX_MONEY_AMOUNT);
- else
- target->ModifyMoney(moneyToAdd);
+ moneyToAdd = MAX_MONEY_AMOUNT;
+
+ if (targetMoney >= uint64(MAX_MONEY_AMOUNT) - moneyToAdd)
+ moneyToAdd -= targetMoney;
+
+ target->ModifyMoney(moneyToAdd);
}
sLog->outDebug(LOG_FILTER_GENERAL, handler->GetTrinityString(LANG_NEW_MONEY), uint32(targetMoney), int32(moneyToAdd), uint32(target->GetMoney()));
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 0217aea1149..6653439b703 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -33,6 +33,42 @@ EndScriptData */
#include "Player.h"
#include "Pet.h"
+struct NpcFlagText
+{
+ uint32 flag;
+ int32 text;
+};
+
+#define NPCFLAG_COUNT 24
+
+const NpcFlagText npcFlagTexts[NPCFLAG_COUNT] =
+{
+ { UNIT_NPC_FLAG_AUCTIONEER, LANG_NPCINFO_AUCTIONEER },
+ { UNIT_NPC_FLAG_BANKER, LANG_NPCINFO_BANKER },
+ { UNIT_NPC_FLAG_BATTLEMASTER, LANG_NPCINFO_BATTLEMASTER },
+ { UNIT_NPC_FLAG_FLIGHTMASTER, LANG_NPCINFO_FLIGHTMASTER },
+ { UNIT_NPC_FLAG_GOSSIP, LANG_NPCINFO_GOSSIP },
+ { UNIT_NPC_FLAG_GUILD_BANKER, LANG_NPCINFO_GUILD_BANKER },
+ { UNIT_NPC_FLAG_INNKEEPER, LANG_NPCINFO_INNKEEPER },
+ { UNIT_NPC_FLAG_PETITIONER, LANG_NPCINFO_PETITIONER },
+ { UNIT_NPC_FLAG_PLAYER_VEHICLE, LANG_NPCINFO_PLAYER_VEHICLE },
+ { UNIT_NPC_FLAG_QUESTGIVER, LANG_NPCINFO_QUESTGIVER },
+ { UNIT_NPC_FLAG_REPAIR, LANG_NPCINFO_REPAIR },
+ { UNIT_NPC_FLAG_SPELLCLICK, LANG_NPCINFO_SPELLCLICK },
+ { UNIT_NPC_FLAG_SPIRITGUIDE, LANG_NPCINFO_SPIRITGUIDE },
+ { UNIT_NPC_FLAG_SPIRITHEALER, LANG_NPCINFO_SPIRITHEALER },
+ { UNIT_NPC_FLAG_STABLEMASTER, LANG_NPCINFO_STABLEMASTER },
+ { UNIT_NPC_FLAG_TABARDDESIGNER, LANG_NPCINFO_TABARDDESIGNER },
+ { UNIT_NPC_FLAG_TRAINER, LANG_NPCINFO_TRAINER },
+ { UNIT_NPC_FLAG_TRAINER_CLASS, LANG_NPCINFO_TRAINER_CLASS },
+ { UNIT_NPC_FLAG_TRAINER_PROFESSION, LANG_NPCINFO_TRAINER_PROFESSION },
+ { UNIT_NPC_FLAG_VENDOR, LANG_NPCINFO_VENDOR },
+ { UNIT_NPC_FLAG_VENDOR_AMMO, LANG_NPCINFO_VENDOR_AMMO },
+ { UNIT_NPC_FLAG_VENDOR_FOOD, LANG_NPCINFO_VENDOR_FOOD },
+ { UNIT_NPC_FLAG_VENDOR_POISON, LANG_NPCINFO_VENDOR_POISON },
+ { UNIT_NPC_FLAG_VENDOR_REAGENT, LANG_NPCINFO_VENDOR_REAGENT }
+};
+
class npc_commandscript : public CommandScript
{
public:
@@ -623,11 +659,9 @@ public:
handler->PSendSysMessage(LANG_NPCINFO_POSITION, float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str());
- if (npcflags & UNIT_NPC_FLAG_VENDOR)
- handler->SendSysMessage(LANG_NPCINFO_VENDOR);
-
- if (npcflags & UNIT_NPC_FLAG_TRAINER)
- handler->SendSysMessage(LANG_NPCINFO_TRAINER);
+ for (uint8 i = 0; i < NPCFLAG_COUNT; i++)
+ if (npcflags & npcFlagTexts[i].flag)
+ handler->PSendSysMessage(npcFlagTexts[i].text, npcFlagTexts[i].flag);
return true;
}
diff --git a/src/server/scripts/Commands/cs_rbac.cpp b/src/server/scripts/Commands/cs_rbac.cpp
new file mode 100644
index 00000000000..092aabb0045
--- /dev/null
+++ b/src/server/scripts/Commands/cs_rbac.cpp
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* ScriptData
+Name: rbac_commandscript
+%Complete: 100
+Comment: All role based access control related commands (including account related)
+Category: commandscripts
+EndScriptData */
+
+#include "RBAC.h"
+#include "Config.h"
+#include "Chat.h"
+#include "Language.h"
+#include "Player.h"
+#include "ScriptMgr.h"
+
+struct RBACCommandData
+{
+ RBACCommandData(): id(0), realmId(0), rbac(NULL), needDelete(false) { }
+ uint32 id;
+ int32 realmId;
+ RBACData* rbac;
+ bool needDelete;
+};
+
+class rbac_commandscript : public CommandScript
+{
+public:
+ rbac_commandscript() : CommandScript("rbac_commandscript") { }
+
+ ChatCommand* GetCommands() const
+ {
+ static ChatCommand rbacGroupsCommandTable[] =
+ {
+ { "add", SEC_ADMINISTRATOR, true, &HandleRBACGroupAddCommand, "", NULL },
+ { "remove", SEC_ADMINISTRATOR, true, &HandleRBACGroupRemoveCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, true, &HandleRBACGroupListCommand, "", NULL },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ static ChatCommand rbacRolesCommandTable[] =
+ {
+ { "grant", SEC_ADMINISTRATOR, true, &HandleRBACRoleGrantCommand, "", NULL },
+ { "deny", SEC_ADMINISTRATOR, true, &HandleRBACRoleDenyCommand, "", NULL },
+ { "revoke", SEC_ADMINISTRATOR, true, &HandleRBACRoleRevokeCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, true, &HandleRBACRoleListCommand, "", NULL },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ static ChatCommand rbacPermsCommandTable[] =
+ {
+ { "grant", SEC_ADMINISTRATOR, true, &HandleRBACPermGrantCommand, "", NULL },
+ { "deny", SEC_ADMINISTRATOR, true, &HandleRBACPermDenyCommand, "", NULL },
+ { "revoke", SEC_ADMINISTRATOR, true, &HandleRBACPermRevokeCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, true, &HandleRBACPermListCommand, "", NULL },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ static ChatCommand rbacListCommandTable[] =
+ {
+ { "groups", SEC_ADMINISTRATOR, true, &HandleRBACListGroupsCommand, "", NULL },
+ { "roles", SEC_ADMINISTRATOR, true, &HandleRBACListRolesCommand, "", NULL },
+ { "permissions", SEC_ADMINISTRATOR, true, &HandleRBACListPermissionsCommand, "", NULL },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ static ChatCommand rbacAccountCommandTable[] =
+ {
+ { "group", SEC_ADMINISTRATOR, true, NULL, "", rbacGroupsCommandTable },
+ { "role", SEC_ADMINISTRATOR, true, NULL, "", rbacRolesCommandTable },
+ { "permission", SEC_ADMINISTRATOR, true, NULL, "", rbacPermsCommandTable },
+ { "", SEC_ADMINISTRATOR, true, &HandleRBACAccountPermissionCommand, "", NULL },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ static ChatCommand rbacCommandTable[] =
+ {
+ { "account", SEC_ADMINISTRATOR, true, NULL, "", rbacAccountCommandTable },
+ { "list", SEC_ADMINISTRATOR, true, NULL, "", rbacListCommandTable },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "rbac", SEC_ADMINISTRATOR, true, NULL, "", rbacCommandTable },
+ { NULL, SEC_ADMINISTRATOR, false, NULL, "", NULL }
+ };
+
+ return commandTable;
+ }
+
+ static RBACCommandData* ReadParams(ChatHandler* handler, char const* args, bool checkParams = true)
+ {
+ if (!args)
+ return NULL;
+
+ char* param1 = strtok((char*)args, " ");
+ char* param2 = strtok(NULL, " ");
+ char* param3 = strtok(NULL, " ");
+
+ int32 realmId = -1;
+ uint32 accountId = 0;
+ std::string accountName;
+ uint32 id = 0;
+ RBACCommandData* data = NULL;
+ RBACData* rdata = NULL;
+ bool useSelectedPlayer = false;
+
+ if (checkParams)
+ {
+ if (!param3)
+ {
+ if (param2)
+ realmId = atoi(param2);
+
+ if (param1)
+ id = atoi(param1);
+
+ useSelectedPlayer = true;
+ }
+ else
+ {
+ id = atoi(param2);
+ realmId = atoi(param3);
+ }
+
+ if (!id)
+ {
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, id);
+ handler->SetSentErrorMessage(true);
+ return NULL;
+ }
+
+ if (realmId < -1 || !realmId)
+ {
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_REALM, realmId);
+ handler->SetSentErrorMessage(true);
+ return NULL;
+ }
+ }
+ else if (!param1)
+ useSelectedPlayer = true;
+
+ if (useSelectedPlayer)
+ {
+ Player* player = handler->getSelectedPlayer();
+ if (!player)
+ return NULL;
+
+ rdata = player->GetSession()->GetRBACData();
+ accountId = rdata->GetId();
+ AccountMgr::GetName(accountId, accountName);
+ }
+ else
+ {
+ accountName = param1;
+
+ if (AccountMgr::normalizeString(accountName))
+ accountId = AccountMgr::GetId(accountName);
+
+ if (!accountId)
+ {
+ handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
+ handler->SetSentErrorMessage(true);
+ return NULL;
+ }
+ }
+
+ if (checkParams && handler->HasLowerSecurityAccount(NULL, accountId, true))
+ return NULL;
+
+ data = new RBACCommandData();
+
+ if (!rdata)
+ {
+ data->rbac = new RBACData(accountId, accountName, ConfigMgr::GetIntDefault("RealmID", 0));
+ data->rbac->LoadFromDB();
+ data->needDelete = true;
+ }
+ else
+ data->rbac = rdata;
+
+ data->id = id;
+ data->realmId = realmId;
+ return data;
+ }
+
+ static bool HandleRBACGroupAddCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->AddGroup(command->id, command->realmId);
+ RBACGroup const* group = sAccountMgr->GetRBACGroup(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_ADD_ALREADY_ADDED:
+ handler->PSendSysMessage(LANG_RBAC_GROUP_IN_LIST, command->id, group->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_GROUP_ADDED, command->id, group->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACGroupRemoveCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->RemoveGroup(command->id, command->realmId);
+ RBACGroup const* group = sAccountMgr->GetRBACGroup(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_REVOKE_NOT_IN_LIST:
+ handler->PSendSysMessage(LANG_RBAC_GROUP_NOT_IN_LIST, command->id, group->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_GROUP_REMOVED, command->id, group->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACGroupListCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args, false);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage(LANG_RBAC_GROUP_LIST_HEADER, command->rbac->GetId(), command->rbac->GetName().c_str());
+ RBACGroupContainer const& groups = command->rbac->GetGroups();
+ if (groups.empty())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (RBACGroupContainer::const_iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ RBACGroup const* group = sAccountMgr->GetRBACGroup(*it);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, group->GetId(), group->GetName().c_str());
+ }
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACRoleGrantCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->GrantRole(command->id, command->realmId);
+ RBACRole const* role = sAccountMgr->GetRBACRole(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_ADD_ALREADY_ADDED:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_GRANTED_IN_LIST, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_IN_DENIED_LIST:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_GRANTED_IN_DENIED_LIST, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_GRANTED, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACRoleDenyCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->DenyRole(command->id, command->realmId);
+ RBACRole const* role = sAccountMgr->GetRBACRole(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_ADD_ALREADY_ADDED:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_DENIED_IN_LIST, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_IN_GRANTED_LIST:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_DENIED_IN_GRANTED_LIST, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_DENIED, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACRoleRevokeCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->RevokeRole(command->id, command->realmId);
+ RBACRole const* role = sAccountMgr->GetRBACRole(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_REVOKE_NOT_IN_LIST:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_REVOKED_NOT_IN_LIST, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_ROLE_REVOKED, command->id, role->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACRoleListCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args, false);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage(LANG_RBAC_ROLE_LIST_HEADER_GRANTED, command->rbac->GetId(), command->rbac->GetName().c_str());
+ RBACGroupContainer const& granted = command->rbac->GetGrantedRoles();
+ if (granted.empty())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (RBACRoleContainer::const_iterator it = granted.begin(); it != granted.end(); ++it)
+ {
+ RBACRole const* role = sAccountMgr->GetRBACRole(*it);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, role->GetId(), role->GetName().c_str());
+ }
+ }
+
+ handler->PSendSysMessage(LANG_RBAC_ROLE_LIST_HEADER_DENIED, command->rbac->GetId(), command->rbac->GetName().c_str());
+ RBACGroupContainer const& denied = command->rbac->GetDeniedRoles();
+ if (denied.empty())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (RBACRoleContainer::const_iterator it = denied.begin(); it != denied.end(); ++it)
+ {
+ RBACRole const* role = sAccountMgr->GetRBACRole(*it);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, role->GetId(), role->GetName().c_str());
+ }
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACPermGrantCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->GrantPermission(command->id, command->realmId);
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_ADD_ALREADY_ADDED:
+ handler->PSendSysMessage(LANG_RBAC_PERM_GRANTED_IN_LIST, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_IN_DENIED_LIST:
+ handler->PSendSysMessage(LANG_RBAC_PERM_GRANTED_IN_DENIED_LIST, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_PERM_GRANTED, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACPermDenyCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->DenyPermission(command->id, command->realmId);
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_ADD_ALREADY_ADDED:
+ handler->PSendSysMessage(LANG_RBAC_PERM_DENIED_IN_LIST, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_IN_GRANTED_LIST:
+ handler->PSendSysMessage(LANG_RBAC_PERM_DENIED_IN_GRANTED_LIST, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_PERM_DENIED, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACPermRevokeCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ RBACCommandResult result = command->rbac->RevokePermission(command->id, command->realmId);
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(command->id);
+
+ switch (result)
+ {
+ case RBAC_CANT_REVOKE_NOT_IN_LIST:
+ handler->PSendSysMessage(LANG_RBAC_PERM_REVOKED_NOT_IN_LIST, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_OK:
+ handler->PSendSysMessage(LANG_RBAC_PERM_REVOKED, command->id, permission->GetName().c_str(),
+ command->realmId, command->rbac->GetId(), command->rbac->GetName().c_str());
+ break;
+ case RBAC_ID_DOES_NOT_EXISTS:
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, command->id);
+ break;
+ default:
+ break;
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACPermListCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args, false);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage(LANG_RBAC_PERM_LIST_HEADER_GRANTED, command->rbac->GetId(), command->rbac->GetName().c_str());
+ RBACPermissionContainer const& granted = command->rbac->GetGrantedPermissions();
+ if (!granted.any())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (uint32 i = 0; i < RBAC_PERM_MAX; ++i)
+ if (granted.test(i))
+ {
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(i);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str());
+ }
+ }
+
+ handler->PSendSysMessage(LANG_RBAC_PERM_LIST_HEADER_DENIED, command->rbac->GetId(), command->rbac->GetName().c_str());
+ RBACPermissionContainer const& denied = command->rbac->GetDeniedPermissions();
+ if (!denied.any())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (uint32 i = 0; i < RBAC_PERM_MAX; ++i)
+ if (denied.test(i))
+ {
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(i);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str());
+ }
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACAccountPermissionCommand(ChatHandler* handler, char const* args)
+ {
+ RBACCommandData* command = ReadParams(handler, args, false);
+
+ if (!command)
+ {
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage(LANG_RBAC_PERM_LIST_GLOBAL, command->rbac->GetId(), command->rbac->GetName().c_str());
+ RBACPermissionContainer const& permissions = command->rbac->GetPermissions();
+ if (!permissions.any())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (uint32 i = 0; i < RBAC_PERM_MAX; ++i)
+ if (permissions.test(i))
+ {
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(i);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str());
+ }
+ }
+
+ if (command->needDelete)
+ delete command;
+
+ return true;
+ }
+
+ static bool HandleRBACListGroupsCommand(ChatHandler* handler, char const* args)
+ {
+ uint32 id = 0;
+ if (char* param1 = strtok((char*)args, " "))
+ id = atoi(param1);
+
+ if (!id)
+ {
+ RBACGroupsContainer const& groups = sAccountMgr->GetRBACGroupList();
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_GROUPS_HEADER));
+ for (RBACGroupsContainer::const_iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ RBACGroup const* group = it->second;
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, group->GetId(), group->GetName().c_str());
+ }
+ }
+ else
+ {
+ RBACGroup const* group = sAccountMgr->GetRBACGroup(id);
+ if (!group)
+ {
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, id);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_GROUPS_HEADER));
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, group->GetId(), group->GetName().c_str());
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_ROLES_HEADER));
+ RBACRoleContainer const& roles = group->GetRoles();
+ if (roles.empty())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (RBACRoleContainer::const_iterator it = roles.begin(); it != roles.end(); ++it)
+ {
+ RBACRole const* role = sAccountMgr->GetRBACRole(*it);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, role->GetId(), role->GetName().c_str());
+ }
+ }
+ }
+
+ return true;
+ }
+
+ static bool HandleRBACListRolesCommand(ChatHandler* handler, char const* args)
+ {
+ uint32 id = 0;
+ if (char* param1 = strtok((char*)args, " "))
+ id = atoi(param1);
+
+ if (!id)
+ {
+ RBACRolesContainer const& roles = sAccountMgr->GetRBACRoleList();
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_ROLES_HEADER));
+ for (RBACRolesContainer::const_iterator it = roles.begin(); it != roles.end(); ++it)
+ {
+ RBACRole const* role = it->second;
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, role->GetId(), role->GetName().c_str());
+ }
+ }
+ else
+ {
+ RBACRole const* role = sAccountMgr->GetRBACRole(id);
+ if (!role)
+ {
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, id);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_ROLES_HEADER));
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, role->GetId(), role->GetName().c_str());
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_PERMISSIONS_HEADER));
+ RBACPermissionContainer const& permissions = role->GetPermissions();
+ if (!permissions.any())
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_EMPTY));
+ else
+ {
+ for (uint32 i = 0; i < RBAC_PERM_MAX; ++i)
+ if (permissions.test(i))
+ {
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(i);
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str());
+ }
+ }
+ }
+
+ return true;
+ }
+
+ static bool HandleRBACListPermissionsCommand(ChatHandler* handler, char const* args)
+ {
+ uint32 id = 0;
+ if (char* param1 = strtok((char*)args, " "))
+ id = atoi(param1);
+
+ if (!id)
+ {
+ RBACPermissionsContainer const& permissions = sAccountMgr->GetRBACPermissionList();
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_PERMISSIONS_HEADER));
+ for (RBACPermissionsContainer::const_iterator it = permissions.begin(); it != permissions.end(); ++it)
+ {
+ RBACPermission const* permission = it->second;
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str());
+ }
+ }
+ else
+ {
+ RBACPermission const* permission = sAccountMgr->GetRBACPermission(id);
+ if (!permission)
+ {
+ handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, id);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+
+ handler->PSendSysMessage("%s", handler->GetTrinityString(LANG_RBAC_LIST_PERMISSIONS_HEADER));
+ handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str());
+ }
+
+ return true;
+ }
+};
+
+void AddSC_rbac_commandscript()
+{
+ new rbac_commandscript();
+}
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index f5f998bbf0d..52721f9866d 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -452,54 +452,53 @@ public:
cInfo->dynamicflags = fields[33].GetUInt32();
cInfo->family = fields[34].GetUInt8();
cInfo->trainer_type = fields[35].GetUInt8();
- cInfo->trainer_spell = fields[36].GetUInt32();
- cInfo->trainer_class = fields[37].GetUInt8();
- cInfo->trainer_race = fields[38].GetUInt8();
- cInfo->minrangedmg = fields[39].GetFloat();
- cInfo->maxrangedmg = fields[40].GetFloat();
- cInfo->rangedattackpower = fields[41].GetUInt16();
- cInfo->type = fields[42].GetUInt8();
- cInfo->type_flags = fields[43].GetUInt32();
- cInfo->lootid = fields[44].GetUInt32();
- cInfo->pickpocketLootId = fields[45].GetUInt32();
- cInfo->SkinLootId = fields[46].GetUInt32();
+ cInfo->trainer_class = fields[36].GetUInt8();
+ cInfo->trainer_race = fields[37].GetUInt8();
+ cInfo->minrangedmg = fields[38].GetFloat();
+ cInfo->maxrangedmg = fields[39].GetFloat();
+ cInfo->rangedattackpower = fields[40].GetUInt16();
+ cInfo->type = fields[41].GetUInt8();
+ cInfo->type_flags = fields[42].GetUInt32();
+ cInfo->lootid = fields[43].GetUInt32();
+ cInfo->pickpocketLootId = fields[44].GetUInt32();
+ cInfo->SkinLootId = fields[45].GetUInt32();
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
- cInfo->resistance[i] = fields[47 + i -1].GetUInt16();
-
- cInfo->spells[0] = fields[53].GetUInt32();
- cInfo->spells[1] = fields[54].GetUInt32();
- cInfo->spells[2] = fields[55].GetUInt32();
- cInfo->spells[3] = fields[56].GetUInt32();
- cInfo->spells[4] = fields[57].GetUInt32();
- cInfo->spells[5] = fields[58].GetUInt32();
- cInfo->spells[6] = fields[59].GetUInt32();
- cInfo->spells[7] = fields[60].GetUInt32();
- cInfo->PetSpellDataId = fields[61].GetUInt32();
- cInfo->VehicleId = fields[62].GetUInt32();
- cInfo->mingold = fields[63].GetUInt32();
- cInfo->maxgold = fields[64].GetUInt32();
- cInfo->AIName = fields[65].GetString();
- cInfo->MovementType = fields[66].GetUInt8();
- cInfo->InhabitType = fields[67].GetUInt8();
- cInfo->HoverHeight = fields[68].GetFloat();
- cInfo->ModHealth = fields[69].GetFloat();
- cInfo->ModMana = fields[70].GetFloat();
- cInfo->ModManaExtra = fields[71].GetFloat();
- cInfo->ModArmor = fields[72].GetFloat();
- cInfo->RacialLeader = fields[73].GetBool();
- cInfo->questItems[0] = fields[74].GetUInt32();
- cInfo->questItems[1] = fields[75].GetUInt32();
- cInfo->questItems[2] = fields[76].GetUInt32();
- cInfo->questItems[3] = fields[77].GetUInt32();
- cInfo->questItems[4] = fields[78].GetUInt32();
- cInfo->questItems[5] = fields[79].GetUInt32();
- cInfo->movementId = fields[80].GetUInt32();
- cInfo->RegenHealth = fields[81].GetBool();
- cInfo->equipmentId = fields[82].GetUInt32();
- cInfo->MechanicImmuneMask = fields[83].GetUInt32();
- cInfo->flags_extra = fields[84].GetUInt32();
- cInfo->ScriptID = sObjectMgr->GetScriptId(fields[85].GetCString());
+ cInfo->resistance[i] = fields[46 + i -1].GetUInt16();
+
+ cInfo->spells[0] = fields[52].GetUInt32();
+ cInfo->spells[1] = fields[53].GetUInt32();
+ cInfo->spells[2] = fields[54].GetUInt32();
+ cInfo->spells[3] = fields[55].GetUInt32();
+ cInfo->spells[4] = fields[56].GetUInt32();
+ cInfo->spells[5] = fields[57].GetUInt32();
+ cInfo->spells[6] = fields[58].GetUInt32();
+ cInfo->spells[7] = fields[59].GetUInt32();
+ cInfo->PetSpellDataId = fields[60].GetUInt32();
+ cInfo->VehicleId = fields[61].GetUInt32();
+ cInfo->mingold = fields[62].GetUInt32();
+ cInfo->maxgold = fields[63].GetUInt32();
+ cInfo->AIName = fields[64].GetString();
+ cInfo->MovementType = fields[65].GetUInt8();
+ cInfo->InhabitType = fields[66].GetUInt8();
+ cInfo->HoverHeight = fields[67].GetFloat();
+ cInfo->ModHealth = fields[68].GetFloat();
+ cInfo->ModMana = fields[69].GetFloat();
+ cInfo->ModManaExtra = fields[70].GetFloat();
+ cInfo->ModArmor = fields[71].GetFloat();
+ cInfo->RacialLeader = fields[72].GetBool();
+ cInfo->questItems[0] = fields[73].GetUInt32();
+ cInfo->questItems[1] = fields[74].GetUInt32();
+ cInfo->questItems[2] = fields[75].GetUInt32();
+ cInfo->questItems[3] = fields[76].GetUInt32();
+ cInfo->questItems[4] = fields[77].GetUInt32();
+ cInfo->questItems[5] = fields[78].GetUInt32();
+ cInfo->movementId = fields[79].GetUInt32();
+ cInfo->RegenHealth = fields[80].GetBool();
+ cInfo->equipmentId = fields[81].GetUInt32();
+ cInfo->MechanicImmuneMask = fields[82].GetUInt32();
+ cInfo->flags_extra = fields[83].GetUInt32();
+ cInfo->ScriptID = sObjectMgr->GetScriptId(fields[84].GetCString());
sObjectMgr->CheckCreatureTemplate(cInfo);
}
diff --git a/src/server/scripts/EasternKingdoms/BaradinHold/boss_alizabal.cpp b/src/server/scripts/EasternKingdoms/BaradinHold/boss_alizabal.cpp
index d7441d6311b..3c6369003ec 100644
--- a/src/server/scripts/EasternKingdoms/BaradinHold/boss_alizabal.cpp
+++ b/src/server/scripts/EasternKingdoms/BaradinHold/boss_alizabal.cpp
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
@@ -16,6 +16,11 @@
*/
#include "baradin_hold.h"
+#include "InstanceScript.h"
+#include "ScriptMgr.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "ScriptedCreature.h"
enum Texts
{
@@ -74,7 +79,7 @@ class boss_alizabal : public CreatureScript
{
public:
boss_alizabal() : CreatureScript("boss_alizabal") { }
-
+
struct boss_alizabalAI : public BossAI
{
boss_alizabalAI(Creature* creature) : BossAI(creature, DATA_ALIZABAL)
@@ -137,7 +142,7 @@ class boss_alizabal : public CreatureScript
}
}
- void MovementInform(uint32 type, uint32 pointId)
+ void MovementInform(uint32 /*type*/, uint32 pointId)
{
switch (pointId)
{
@@ -248,7 +253,7 @@ class boss_alizabal : public CreatureScript
break;
}
}
-
+
DoMeleeAttackIfReady();
}
};
diff --git a/src/server/scripts/EasternKingdoms/BaradinHold/instance_baradin_hold.cpp b/src/server/scripts/EasternKingdoms/BaradinHold/instance_baradin_hold.cpp
index 9bbfebecf2c..0e2b7107548 100644
--- a/src/server/scripts/EasternKingdoms/BaradinHold/instance_baradin_hold.cpp
+++ b/src/server/scripts/EasternKingdoms/BaradinHold/instance_baradin_hold.cpp
@@ -15,7 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include"baradin_hold.h"
+#include "baradin_hold.h"
+#include "InstanceScript.h"
+#include "ScriptMgr.h"
DoorData const doorData[] =
{
@@ -94,7 +96,8 @@ public:
default:
break;
}
- return NULL;
+
+ return 0;
}
void OnGameObjectRemove(GameObject* go)
diff --git a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp
index 0aeba151385..fa1ab5528ca 100644
--- a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp
@@ -101,10 +101,13 @@ public:
if (KnockBack_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_KNOCKBACK);
- //Drop 50% aggro
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(me->getVictim(), -50);
+ if (Unit* target = me->getVictim())
+ {
+ DoCast(target, SPELL_KNOCKBACK);
+ // Drop 50% aggro
+ if (DoGetThreat(target))
+ DoModifyThreatPercent(target, -50);
+ }
KnockBack_Timer = urand(15000, 30000);
} else KnockBack_Timer -= diff;
diff --git a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_firemaw.cpp b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_firemaw.cpp
index 7ca74f4ed4f..4c36d78667b 100644
--- a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_firemaw.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_firemaw.cpp
@@ -68,24 +68,26 @@ public:
//ShadowFlame_Timer
if (ShadowFlame_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_SHADOWFLAME);
+ DoCastVictim(SPELL_SHADOWFLAME);
ShadowFlame_Timer = urand(15000, 18000);
} else ShadowFlame_Timer -= diff;
//WingBuffet_Timer
if (WingBuffet_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_WINGBUFFET);
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(me->getVictim(), -75);
-
+ if (Unit* target = me->getVictim())
+ {
+ DoCast(target, SPELL_WINGBUFFET);
+ if (DoGetThreat(target))
+ DoModifyThreatPercent(target, -75);
+ }
WingBuffet_Timer = 25000;
} else WingBuffet_Timer -= diff;
//FlameBuffet_Timer
if (FlameBuffet_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_FLAMEBUFFET);
+ DoCastVictim(SPELL_FLAMEBUFFET);
FlameBuffet_Timer = 5000;
} else FlameBuffet_Timer -= diff;
diff --git a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_flamegor.cpp b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_flamegor.cpp
index a1659330a6f..57215e3f43d 100644
--- a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_flamegor.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_flamegor.cpp
@@ -76,17 +76,19 @@ public:
//ShadowFlame_Timer
if (ShadowFlame_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_SHADOWFLAME);
+ DoCastVictim(SPELL_SHADOWFLAME);
ShadowFlame_Timer = urand(15000, 22000);
} else ShadowFlame_Timer -= diff;
//WingBuffet_Timer
if (WingBuffet_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_WINGBUFFET);
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(me->getVictim(), -75);
-
+ if (Unit* target = me->getVictim())
+ {
+ DoCast(target, SPELL_WINGBUFFET);
+ if (DoGetThreat(target))
+ DoModifyThreatPercent(target, -75);
+ }
WingBuffet_Timer = 25000;
} else WingBuffet_Timer -= diff;
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp
index 6985ad2c4ad..f508266434b 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp
@@ -160,7 +160,7 @@ public:
## npc_koltira_deathweaver
######*/
-enum eKoltira
+enum Koltira
{
SAY_BREAKOUT1 = 0,
SAY_BREAKOUT2 = 1,
@@ -180,10 +180,9 @@ enum eKoltira
NPC_CRIMSON_ACOLYTE = 29007,
NPC_HIGH_INQUISITOR_VALROTH = 29001,
- NPC_KOLTIRA_ALT = 28447,
//not sure about this id
- //NPC_DEATH_KNIGHT_MOUNT = 29201,
+ //NPC_DEATH_KNIGHT_MOUNT = 29201,
MODEL_DEATH_KNIGHT_MOUNT = 25278
};
@@ -198,17 +197,12 @@ public:
{
creature->SetStandState(UNIT_STAND_STATE_STAND);
- if (npc_escortAI* pEscortAI = CAST_AI(npc_koltira_deathweaver::npc_koltira_deathweaverAI, creature->AI()))
- pEscortAI->Start(false, false, player->GetGUID());
+ if (npc_escortAI* escortAI = CAST_AI(npc_koltira_deathweaver::npc_koltira_deathweaverAI, creature->AI()))
+ escortAI->Start(false, false, player->GetGUID());
}
return true;
}
- CreatureAI* GetAI(Creature* creature) const
- {
- return new npc_koltira_deathweaverAI(creature);
- }
-
struct npc_koltira_deathweaverAI : public npc_escortAI
{
npc_koltira_deathweaverAI(Creature* creature) : npc_escortAI(creature)
@@ -216,20 +210,17 @@ public:
me->SetReactState(REACT_DEFENSIVE);
}
- uint32 m_uiWave;
- uint32 m_uiWave_Timer;
- uint64 m_uiValrothGUID;
-
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
{
- m_uiWave = 0;
- m_uiWave_Timer = 3000;
- m_uiValrothGUID = 0;
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ wave = 0;
+ waveTimer = 3000;
+ valrothGUID = 0;
me->LoadEquipment(0, true);
- me->RemoveAura(SPELL_ANTI_MAGIC_ZONE);
+ me->RemoveAurasDueToSpell(SPELL_ANTI_MAGIC_ZONE);
+ me->RemoveAurasDueToSpell(SPELL_KOLTIRA_TRANSFORM);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
}
@@ -239,22 +230,21 @@ public:
{
case 0:
Talk(SAY_BREAKOUT1);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
break;
case 1:
me->SetStandState(UNIT_STAND_STATE_KNEEL);
break;
case 2:
me->SetStandState(UNIT_STAND_STATE_STAND);
- //me->UpdateEntry(NPC_KOLTIRA_ALT); //unclear if we must update or not
DoCast(me, SPELL_KOLTIRA_TRANSFORM);
me->LoadEquipment(me->GetEquipmentId());
break;
case 3:
SetEscortPaused(true);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->SetStandState(UNIT_STAND_STATE_KNEEL);
Talk(SAY_BREAKOUT2);
- DoCast(me, SPELL_ANTI_MAGIC_ZONE); // cast again that makes bubble up
+ DoCast(me, SPELL_ANTI_MAGIC_ZONE);
break;
case 4:
SetRun(true);
@@ -274,9 +264,8 @@ public:
summoned->AI()->AttackStart(player);
if (summoned->GetEntry() == NPC_HIGH_INQUISITOR_VALROTH)
- m_uiValrothGUID = summoned->GetGUID();
+ valrothGUID = summoned->GetGUID();
- summoned->AddThreat(me, 0.0f);
summoned->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
}
@@ -292,51 +281,51 @@ public:
if (HasEscortState(STATE_ESCORT_PAUSED))
{
- if (m_uiWave_Timer <= uiDiff)
+ if (waveTimer <= uiDiff)
{
- switch (m_uiWave)
+ switch (wave)
{
case 0:
Talk(SAY_BREAKOUT3);
SummonAcolyte(3);
- m_uiWave_Timer = 20000;
+ waveTimer = 20000;
break;
case 1:
Talk(SAY_BREAKOUT4);
SummonAcolyte(3);
- m_uiWave_Timer = 20000;
+ waveTimer = 20000;
break;
case 2:
Talk(SAY_BREAKOUT5);
SummonAcolyte(4);
- m_uiWave_Timer = 20000;
+ waveTimer = 20000;
break;
case 3:
Talk(SAY_BREAKOUT6);
me->SummonCreature(NPC_HIGH_INQUISITOR_VALROTH, 1642.329f, -6045.818f, 127.583f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
- m_uiWave_Timer = 1000;
+ waveTimer = 1000;
break;
case 4:
{
- Creature* temp = Unit::GetCreature(*me, m_uiValrothGUID);
+ Creature* temp = Unit::GetCreature(*me, valrothGUID);
if (!temp || !temp->isAlive())
{
Talk(SAY_BREAKOUT8);
- m_uiWave_Timer = 5000;
+ waveTimer = 5000;
}
else
{
- m_uiWave_Timer = 2500;
- return; //return, we don't want m_uiWave to increment now
+ waveTimer = 2500;
+ return;
}
break;
}
case 5:
Talk(SAY_BREAKOUT9);
me->RemoveAurasDueToSpell(SPELL_ANTI_MAGIC_ZONE);
- // i do not know why the armor will also be removed
- m_uiWave_Timer = 2500;
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ waveTimer = 2500;
break;
case 6:
Talk(SAY_BREAKOUT10);
@@ -344,14 +333,24 @@ public:
break;
}
- ++m_uiWave;
+ ++wave;
}
else
- m_uiWave_Timer -= uiDiff;
+ waveTimer -= uiDiff;
}
}
+
+ private:
+ uint8 wave;
+ uint32 waveTimer;
+ uint64 valrothGUID;
+
};
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_koltira_deathweaverAI(creature);
+ }
};
//Scarlet courier
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
index 58e130ce644..19660cec4af 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
@@ -66,8 +66,8 @@ enum eMisc
Position const PosMove[2] =
{
- { 299.4884f, 92.76137f, 105.6335f },
- { 314.8673f, 90.30210f, 101.6459f }
+ { 299.4884f, 92.76137f, 105.6335f, 0.0f },
+ { 314.8673f, 90.30210f, 101.6459f, 0.0f }
};
class boss_kirtonos_the_herald : public CreatureScript
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
index c186ada72b7..73b52c4185f 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
@@ -345,10 +345,12 @@ public:
return new boss_alythessAI (creature);
};
- struct boss_alythessAI : public Scripted_NoMovementAI
+ struct boss_alythessAI : public ScriptedAI
{
- boss_alythessAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ boss_alythessAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
+
instance = creature->GetInstanceScript();
IntroStepCounter = 10;
}
@@ -418,9 +420,7 @@ public:
void AttackStart(Unit* who)
{
if (!me->isInCombat())
- {
- Scripted_NoMovementAI::AttackStart(who);
- }
+ ScriptedAI::AttackStart(who);
}
void MoveInLineOfSight(Unit* who)
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp
index a8755c16418..742c80bea39 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp
@@ -780,8 +780,9 @@ public:
if (AgonyCurseTimer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (!target) target = me->getVictim();
+ Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1);
+ if (!target)
+ target = me->getVictim();
DoCast(target, SPELL_AGONY_CURSE);
AgonyCurseTimer = 20000;
} else AgonyCurseTimer -= diff;
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
index 7718b3682f8..3d9cb69cbae 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
@@ -392,11 +392,13 @@ public:
return new mob_kiljaeden_controllerAI (creature);
}
- struct mob_kiljaeden_controllerAI : public Scripted_NoMovementAI
+ struct mob_kiljaeden_controllerAI : public ScriptedAI
{
- mob_kiljaeden_controllerAI(Creature* creature) : Scripted_NoMovementAI(creature), summons(me)
+ mob_kiljaeden_controllerAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
instance = creature->GetInstanceScript();
+
+ SetCombatMovement(false);
}
InstanceScript* instance;
@@ -492,11 +494,13 @@ public:
return new boss_kiljaedenAI (creature);
}
- struct boss_kiljaedenAI : public Scripted_NoMovementAI
+ struct boss_kiljaedenAI : public ScriptedAI
{
- boss_kiljaedenAI(Creature* creature) : Scripted_NoMovementAI(creature), summons(me)
+ boss_kiljaedenAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
instance = creature->GetInstanceScript();
+
+ SetCombatMovement(false);
}
InstanceScript* instance;
@@ -520,7 +524,7 @@ public:
void InitializeAI()
{
- Scripted_NoMovementAI::InitializeAI();
+ // Scripted_NoMovementAI::InitializeAI();
}
void Reset()
@@ -615,7 +619,8 @@ public:
void EnterEvadeMode()
{
- Scripted_NoMovementAI::EnterEvadeMode();
+ ScriptedAI::EnterEvadeMode();
+
summons.DespawnAll();
// Reset the controller
@@ -999,9 +1004,12 @@ public:
return new mob_felfire_portalAI (creature);
}
- struct mob_felfire_portalAI : public Scripted_NoMovementAI
+ struct mob_felfire_portalAI : public ScriptedAI
{
- mob_felfire_portalAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ mob_felfire_portalAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
uint32 uiSpawnFiendTimer;
@@ -1100,9 +1108,12 @@ public:
return new mob_armageddonAI (creature);
}
- struct mob_armageddonAI : public Scripted_NoMovementAI
+ struct mob_armageddonAI : public ScriptedAI
{
- mob_armageddonAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ mob_armageddonAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
uint8 spell;
uint32 uiTimer;
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp
index 5eb79258005..b45e55e0605 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp
@@ -213,10 +213,11 @@ public:
return new boss_muruAI (creature);
}
- struct boss_muruAI : public Scripted_NoMovementAI
+ struct boss_muruAI : public ScriptedAI
{
- boss_muruAI(Creature* creature) : Scripted_NoMovementAI(creature), Summons(me)
+ boss_muruAI(Creature* creature) : ScriptedAI(creature), Summons(creature)
{
+ SetCombatMovement(false);
instance = creature->GetInstanceScript();
}
@@ -377,10 +378,11 @@ public:
return new npc_muru_portalAI (creature);
}
- struct npc_muru_portalAI : public Scripted_NoMovementAI
+ struct npc_muru_portalAI : public ScriptedAI
{
- npc_muru_portalAI(Creature* creature) : Scripted_NoMovementAI(creature), Summons(me)
+ npc_muru_portalAI(Creature* creature) : ScriptedAI(creature), Summons(creature)
{
+ SetCombatMovement(false);
instance = creature->GetInstanceScript();
}
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
index 1b2512ccf63..f1cb7b4116a 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
@@ -201,8 +201,11 @@ class boss_akilzon : public CreatureScript
//dealdamege
for (std::list<Unit*>::const_iterator i = tempUnitMap.begin(); i != tempUnitMap.end(); ++i)
{
- if (!Cloud->IsWithinDist(*i, 6, false))
- Cloud->CastCustomSpell(*i, 43137, &bp0, NULL, NULL, true, 0, 0, me->GetGUID());
+ if (Unit* target = (*i))
+ {
+ if (!Cloud->IsWithinDist(target, 6, false))
+ Cloud->CastCustomSpell(target, 43137, &bp0, NULL, NULL, true, 0, 0, me->GetGUID());
+ }
}
// visual
float x, y, z;
diff --git a/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp b/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp
index 432768a51de..33cb5f649d7 100644
--- a/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp
+++ b/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp
@@ -551,9 +551,12 @@ public:
return new npc_infused_crystalAI (creature);
}
- struct npc_infused_crystalAI : public Scripted_NoMovementAI
+ struct npc_infused_crystalAI : public ScriptedAI
{
- npc_infused_crystalAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ npc_infused_crystalAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
uint32 EndTimer;
uint32 WaveTimer;
diff --git a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp
index ee22b766154..c8bc82be245 100644
--- a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp
+++ b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp
@@ -254,9 +254,12 @@ public:
return new npc_andorhal_towerAI (creature);
}
- struct npc_andorhal_towerAI : public Scripted_NoMovementAI
+ struct npc_andorhal_towerAI : public ScriptedAI
{
- npc_andorhal_towerAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ npc_andorhal_towerAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
void MoveInLineOfSight(Unit* who)
{
diff --git a/src/server/scripts/Kalimdor/CMakeLists.txt b/src/server/scripts/Kalimdor/CMakeLists.txt
index 44f48a1c35e..6f145d3e9a4 100644
--- a/src/server/scripts/Kalimdor/CMakeLists.txt
+++ b/src/server/scripts/Kalimdor/CMakeLists.txt
@@ -38,11 +38,11 @@ set(scripts_STAT_SRCS
Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal.h
Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp
Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp
- Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite.cpp
- Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm.cpp
+ Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp
+ Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp
Kalimdor/CavernsOfTime/CullingOfStratholme/boss_meathook.cpp
Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp
- Kalimdor/CavernsOfTime/CullingOfStratholme/boss_epoch.cpp
+ Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp
Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
Kalimdor/CavernsOfTime/CullingOfStratholme/instance_culling_of_stratholme.cpp
Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp
index 36f6f94b324..c28d70e2634 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp
@@ -1369,8 +1369,7 @@ public:
forcemove = false;
if (forcemove)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
me->Attack(target, false);
}
if (MoveTimer <= diff)
@@ -1409,11 +1408,11 @@ public:
return new alliance_riflemanAI(creature);
}
- struct alliance_riflemanAI : public Scripted_NoMovementAI
+ struct alliance_riflemanAI : public ScriptedAI
{
- alliance_riflemanAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ alliance_riflemanAI(Creature* creature) : ScriptedAI(creature)
{
- Reset();
+ SetCombatMovement(false);
}
uint32 ExplodeTimer;
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_epoch.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp
index fad5736e18f..fad5736e18f 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_epoch.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_chrono_lord_epoch.cpp
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp
index 8c2861db299..8c2861db299 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_infinite_corruptor.cpp
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp
index d7d9beaedd4..d7d9beaedd4 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_salramm_the_fleshcrafter.cpp
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/boss_chrono_lord_deja.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/boss_chrono_lord_deja.cpp
index ea372621026..7ac8d4e783b 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/boss_chrono_lord_deja.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/boss_chrono_lord_deja.cpp
@@ -124,8 +124,8 @@ public:
//Arcane Discharge
if (ArcaneDischarge_Timer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- DoCast(target, SPELL_ARCANE_DISCHARGE);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ DoCast(target, SPELL_ARCANE_DISCHARGE);
ArcaneDischarge_Timer = 20000+rand()%10000;
} else ArcaneDischarge_Timer -= diff;
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/instance_dark_portal.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/instance_dark_portal.cpp
index 9c17ed10d9c..78b655bb73b 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/instance_dark_portal.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/DarkPortal/instance_dark_portal.cpp
@@ -122,7 +122,7 @@ public:
bool IsEncounterInProgress() const
{
- if (const_cast<instance_dark_portal_InstanceMapScript*>(this)->GetData(TYPE_MEDIVH) == IN_PROGRESS)
+ if (GetData(TYPE_MEDIVH) == IN_PROGRESS)
return true;
return false;
diff --git a/src/server/scripts/Kalimdor/Firelands/boss_alysrazor.cpp b/src/server/scripts/Kalimdor/Firelands/boss_alysrazor.cpp
index 1dc70c5e505..b7816242afc 100644
--- a/src/server/scripts/Kalimdor/Firelands/boss_alysrazor.cpp
+++ b/src/server/scripts/Kalimdor/Firelands/boss_alysrazor.cpp
@@ -298,7 +298,7 @@ class npc_blazing_monstrosity : public CreatureScript
passenger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
// Hack to relocate vehicle on vehicle so exiting players are not moved under map
- Movement::MoveSplineInit init(*passenger);
+ Movement::MoveSplineInit init(passenger);
init.DisableTransportPathTransformations();
init.MoveTo(0.6654003f, 0.0f, 1.9815f);
init.SetFacing(0.0f);
diff --git a/src/server/scripts/Kalimdor/Firelands/firelands.h b/src/server/scripts/Kalimdor/Firelands/firelands.h
index 330158d6c94..cf12892b2c9 100644
--- a/src/server/scripts/Kalimdor/Firelands/firelands.h
+++ b/src/server/scripts/Kalimdor/Firelands/firelands.h
@@ -19,7 +19,7 @@
#define FIRELANDS_H_
#include "Map.h"
-#include "Creature.h"
+#include "CreatureAI.h"
#define FirelandsScriptName "instance_firelands"
diff --git a/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp b/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp
index 1ea0ee0a343..2169320621b 100644
--- a/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp
+++ b/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp
@@ -86,8 +86,8 @@ public:
return true;
}
protected:
- InstanceScript* _instance;
Unit* _owner;
+ InstanceScript* _instance;
};
class boss_earthrager_ptah : public CreatureScript
@@ -262,8 +262,8 @@ public:
}
protected:
- bool _hasDispersed;
uint8 _summonDeaths;
+ bool _hasDispersed;
};
CreatureAI* GetAI(Creature* creature) const
diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp
index 285d893fe41..c69a696827e 100644
--- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp
+++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp
@@ -197,11 +197,12 @@ class npc_buru_egg : public CreatureScript
public:
npc_buru_egg() : CreatureScript("npc_buru_egg") { }
- struct npc_buru_eggAI : public Scripted_NoMovementAI
+ struct npc_buru_eggAI : public ScriptedAI
{
- npc_buru_eggAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_buru_eggAI(Creature* creature) : ScriptedAI(creature)
{
_instance = me->GetInstanceScript();
+ SetCombatMovement(false);
}
void EnterCombat(Unit* attacker)
diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp
index 2a9ea4ba4fc..a16e9cfa89d 100644
--- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp
+++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp
@@ -274,10 +274,11 @@ public:
for (uint8 i = 0; i < 10; ++i)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- Creature* Summoned = me->SummonCreature(15621, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000);
- if (Summoned && target)
- Summoned->AI()->AttackStart(target);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ if (Creature* Summoned = me->SummonCreature(15621, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000))
+ Summoned->AI()->AttackStart(target);
+ }
}
}
diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp
index 3da1fc1dc5c..b5fbd644592 100644
--- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp
+++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp
@@ -156,13 +156,15 @@ public:
return new eye_of_cthunAI (creature);
}
- struct eye_of_cthunAI : public Scripted_NoMovementAI
+ struct eye_of_cthunAI : public ScriptedAI
{
- eye_of_cthunAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ eye_of_cthunAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
if (!instance)
sLog->outError(LOG_FILTER_TSCR, "No Instance eye_of_cthunAI");
+
+ SetCombatMovement(false);
}
InstanceScript* instance;
@@ -460,9 +462,9 @@ public:
return new cthunAI (creature);
}
- struct cthunAI : public Scripted_NoMovementAI
+ struct cthunAI : public ScriptedAI
{
- cthunAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ cthunAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
@@ -916,15 +918,17 @@ public:
return new eye_tentacleAI (creature);
}
- struct eye_tentacleAI : public Scripted_NoMovementAI
+ struct eye_tentacleAI : public ScriptedAI
{
- eye_tentacleAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ eye_tentacleAI(Creature* creature) : ScriptedAI(creature)
{
if (Creature* pPortal = me->SummonCreature(MOB_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{
pPortal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID();
}
+
+ SetCombatMovement(false);
}
uint32 MindflayTimer;
@@ -989,9 +993,9 @@ public:
return new claw_tentacleAI (creature);
}
- struct claw_tentacleAI : public Scripted_NoMovementAI
+ struct claw_tentacleAI : public ScriptedAI
{
- claw_tentacleAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ claw_tentacleAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
@@ -1099,9 +1103,9 @@ public:
return new giant_claw_tentacleAI (creature);
}
- struct giant_claw_tentacleAI : public Scripted_NoMovementAI
+ struct giant_claw_tentacleAI : public ScriptedAI
{
- giant_claw_tentacleAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ giant_claw_tentacleAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
@@ -1218,9 +1222,9 @@ public:
return new giant_eye_tentacleAI (creature);
}
- struct giant_eye_tentacleAI : public Scripted_NoMovementAI
+ struct giant_eye_tentacleAI : public ScriptedAI
{
- giant_eye_tentacleAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ giant_eye_tentacleAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
@@ -1282,9 +1286,9 @@ public:
return new flesh_tentacleAI (creature);
}
- struct flesh_tentacleAI : public Scripted_NoMovementAI
+ struct flesh_tentacleAI : public ScriptedAI
{
- flesh_tentacleAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ flesh_tentacleAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
}
diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_fankriss.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_fankriss.cpp
index 80fdc111911..52d59efd0df 100644
--- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_fankriss.cpp
+++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_fankriss.cpp
@@ -137,9 +137,7 @@ public:
{
if (SpawnHatchlings_Timer <= diff)
{
- Unit* target = NULL;
- target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target && target->GetTypeId() == TYPEID_PLAYER)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
{
DoCast(target, SPELL_ROOT);
diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp
index 0ecf5e51c1f..f4c0eed481e 100644
--- a/src/server/scripts/Kalimdor/zone_desolace.cpp
+++ b/src/server/scripts/Kalimdor/zone_desolace.cpp
@@ -37,7 +37,6 @@ EndContentData */
enum DyingKodo
{
- // signed for 9999
SAY_SMEED_HOME = 0,
QUEST_KODO = 5561,
@@ -49,7 +48,7 @@ enum DyingKodo
NPC_TAMED_KODO = 11627,
SPELL_KODO_KOMBO_ITEM = 18153,
- SPELL_KODO_KOMBO_PLAYER_BUFF = 18172, //spells here have unclear function, but using them at least for visual parts and checks
+ SPELL_KODO_KOMBO_PLAYER_BUFF = 18172,
SPELL_KODO_KOMBO_DESPAWN_BUFF = 18377,
SPELL_KODO_KOMBO_GOSSIP = 18362
@@ -64,110 +63,55 @@ public:
{
if (player->HasAura(SPELL_KODO_KOMBO_PLAYER_BUFF) && creature->HasAura(SPELL_KODO_KOMBO_DESPAWN_BUFF))
{
- //the expected quest objective
- player->TalkedToCreature(creature->GetEntry(), creature->GetGUID());
-
+ player->TalkedToCreature(creature->GetEntry(), 0);
player->RemoveAurasDueToSpell(SPELL_KODO_KOMBO_PLAYER_BUFF);
- creature->GetMotionMaster()->MoveIdle();
}
player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
- bool EffectDummyCreature(Unit* pCaster, uint32 spellId, uint32 effIndex, Creature* creatureTarget)
- {
- //always check spellid and effectindex
- if (spellId == SPELL_KODO_KOMBO_ITEM && effIndex == 0)
- {
- //no effect if player/creature already have aura from spells
- if (pCaster->HasAura(SPELL_KODO_KOMBO_PLAYER_BUFF) || creatureTarget->HasAura(SPELL_KODO_KOMBO_DESPAWN_BUFF))
- return true;
-
- if (creatureTarget->GetEntry() == NPC_AGED_KODO ||
- creatureTarget->GetEntry() == NPC_DYING_KODO ||
- creatureTarget->GetEntry() == NPC_ANCIENT_KODO)
- {
- pCaster->CastSpell(pCaster, SPELL_KODO_KOMBO_PLAYER_BUFF, true);
-
- creatureTarget->UpdateEntry(NPC_TAMED_KODO);
- creatureTarget->CastSpell(creatureTarget, SPELL_KODO_KOMBO_DESPAWN_BUFF, false);
-
- if (creatureTarget->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
- creatureTarget->GetMotionMaster()->MoveIdle();
-
- creatureTarget->GetMotionMaster()->MoveFollow(pCaster, PET_FOLLOW_DIST, creatureTarget->GetFollowAngle());
- }
-
- //always return true when we are handling this spell and effect
- return true;
- }
- return false;
- }
-
- CreatureAI* GetAI(Creature* creature) const
- {
- return new npc_aged_dying_ancient_kodoAI(creature);
- }
-
struct npc_aged_dying_ancient_kodoAI : public ScriptedAI
{
- npc_aged_dying_ancient_kodoAI(Creature* creature) : ScriptedAI(creature) { Reset(); }
-
- uint32 DespawnTimer;
+ npc_aged_dying_ancient_kodoAI(Creature* creature) : ScriptedAI(creature) {}
- void Reset()
+ void MoveInLineOfSight(Unit* who)
{
- DespawnTimer = 0;
+ if (who->GetEntry() == NPC_SMEED && me->IsWithinDistInMap(who, 10.0f) && !me->HasAura(SPELL_KODO_KOMBO_GOSSIP))
+ {
+ me->GetMotionMaster()->Clear();
+ DoCast(me, SPELL_KODO_KOMBO_GOSSIP, true);
+ if (Creature* smeed = who->ToCreature())
+ smeed->AI()->Talk(SAY_SMEED_HOME);
+ }
}
- void MoveInLineOfSight(Unit* who)
+ void SpellHit(Unit* caster, SpellInfo const* spell)
{
- if (who->GetEntry() == NPC_SMEED)
+ if (spell->Id == SPELL_KODO_KOMBO_ITEM)
{
- if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
- return;
-
- if (me->IsWithinDistInMap(who, 10.0f))
+ if (!(caster->HasAura(SPELL_KODO_KOMBO_PLAYER_BUFF) || me->HasAura(SPELL_KODO_KOMBO_DESPAWN_BUFF))
+ && (me->GetEntry() == NPC_AGED_KODO || me->GetEntry() == NPC_DYING_KODO || me->GetEntry() == NPC_ANCIENT_KODO))
{
- if (Creature* talker = who->ToCreature())
- talker->AI()->Talk(SAY_SMEED_HOME);
+ caster->CastSpell(caster, SPELL_KODO_KOMBO_PLAYER_BUFF, true);
+ DoCast(me, SPELL_KODO_KOMBO_DESPAWN_BUFF, true);
- //spell have no implemented effect (dummy), so useful to notify spellHit
- DoCast(me, SPELL_KODO_KOMBO_GOSSIP, true);
+ me->UpdateEntry(NPC_TAMED_KODO);
+ me->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, me->GetFollowAngle());
}
}
- }
-
- void SpellHit(Unit* /*pCaster*/, SpellInfo const* pSpell)
- {
- if (pSpell->Id == SPELL_KODO_KOMBO_GOSSIP)
+ else if (spell->Id == SPELL_KODO_KOMBO_GOSSIP)
{
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- DespawnTimer = 60000;
+ me->DespawnOrUnsummon(60000);
}
}
-
- void UpdateAI(const uint32 diff)
- {
- //timer should always be == 0 unless we already updated entry of creature. Then not expect this updated to ever be in combat.
- if (DespawnTimer && DespawnTimer <= diff)
- {
- if (!me->getVictim() && me->isAlive())
- {
- Reset();
- me->setDeathState(JUST_DIED);
- me->Respawn();
- return;
- }
- } else DespawnTimer -= diff;
-
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
- }
};
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_aged_dying_ancient_kodoAI(creature);
+ }
};
diff --git a/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp b/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp
index 1139122f73b..52887928d0a 100644
--- a/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp
+++ b/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp
@@ -81,8 +81,7 @@ public:
if (BerserkerChargeTimer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_BERSERKER_CHARGE);
BerserkerChargeTimer = 25000;
} else BerserkerChargeTimer -= diff;
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
index d484e2a4279..bb08b48ab4a 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
@@ -182,9 +182,9 @@ class mob_amanitar_mushrooms : public CreatureScript
public:
mob_amanitar_mushrooms() : CreatureScript("mob_amanitar_mushrooms") { }
- struct mob_amanitar_mushroomsAI : public Scripted_NoMovementAI
+ struct mob_amanitar_mushroomsAI : public ScriptedAI
{
- mob_amanitar_mushroomsAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ mob_amanitar_mushroomsAI(Creature* creature) : ScriptedAI(creature) {}
EventMap events;
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
index 7893fb7c984..2f731017646 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
@@ -254,9 +254,9 @@ class mob_nadox_eggs : public CreatureScript
public:
mob_nadox_eggs() : CreatureScript("mob_nadox_eggs") { }
- struct mob_nadox_eggsAI : public Scripted_NoMovementAI
+ struct mob_nadox_eggsAI : public ScriptedAI
{
- mob_nadox_eggsAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_nadox_eggsAI(Creature* creature) : ScriptedAI(creature)
{
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
creature->UpdateAllStats();
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_herald_volazj.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_herald_volazj.cpp
index 7f6709b34fa..38c409ee706 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_herald_volazj.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_herald_volazj.cpp
@@ -322,5 +322,5 @@ public:
void AddSC_boss_volazj()
{
- new boss_volazj;
+ new boss_volazj();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
index c36bcf9708f..732216420f9 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
@@ -523,15 +523,17 @@ class npc_jedogas_aufseher_trigger : public CreatureScript
public:
npc_jedogas_aufseher_trigger() : CreatureScript("npc_jedogas_aufseher_trigger") { }
- struct npc_jedogas_aufseher_triggerAI : public Scripted_NoMovementAI
+ struct npc_jedogas_aufseher_triggerAI : public ScriptedAI
{
- npc_jedogas_aufseher_triggerAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_jedogas_aufseher_triggerAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
bRemoved = false;
bRemoved2 = false;
bCasted = false;
bCasted2 = false;
+
+ SetCombatMovement(false);
}
InstanceScript* instance;
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
index 8f77cc7ec6f..53d0ddd6c19 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
@@ -424,7 +424,7 @@ public:
void AddSC_boss_taldaram()
{
- new boss_taldaram;
- new mob_taldaram_flamesphere;
- new prince_taldaram_sphere;
+ new boss_taldaram();
+ new mob_taldaram_flamesphere();
+ new prince_taldaram_sphere();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp
index db4959ae670..26a4aeeca01 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp
@@ -336,5 +336,5 @@ public:
void AddSC_instance_ahnkahet()
{
- new instance_ahnkahet;
+ new instance_ahnkahet();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
index 1442ff265f4..06a640be6e4 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
@@ -360,5 +360,5 @@ public:
void AddSC_boss_anub_arak()
{
- new boss_anub_arak;
+ new boss_anub_arak();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp
index 6c707a8388f..e7decdc10fd 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp
@@ -198,5 +198,5 @@ public:
void AddSC_boss_hadronox()
{
- new boss_hadronox;
+ new boss_hadronox();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp
index 7873cadd096..875aafe9826 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp
@@ -213,5 +213,5 @@ public:
void AddSC_instance_azjol_nerub()
{
- new instance_azjol_nerub;
+ new instance_azjol_nerub();
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp
index f5864fe7b8f..3021e628063 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp
@@ -1480,10 +1480,11 @@ public:
return new mob_twilight_eggsAI(creature);
}
- struct mob_twilight_eggsAI : public Scripted_NoMovementAI
+ struct mob_twilight_eggsAI : public ScriptedAI
{
- mob_twilight_eggsAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_twilight_eggsAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
instance = creature->GetInstanceScript();
}
@@ -1602,11 +1603,11 @@ public:
return new npc_twilight_fissureAI(creature);
}
- struct npc_twilight_fissureAI : public Scripted_NoMovementAI
+ struct npc_twilight_fissureAI : public ScriptedAI
{
- npc_twilight_fissureAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_twilight_fissureAI(Creature* creature) : ScriptedAI(creature)
{
- Reset();
+ SetCombatMovement(false);
}
uint32 VoidBlast_Timer;
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp
index 205a0b10d69..70c1141b6e6 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp
@@ -61,9 +61,7 @@ enum Phases
{
PHASE_ALL = 0,
PHASE_INTRO = 1,
- PHASE_COMBAT = 2,
-
- PHASE_INTRO_MASK = 1 << PHASE_INTRO,
+ PHASE_COMBAT = 2
};
class boss_baltharus_the_warborn : public CreatureScript
@@ -166,15 +164,16 @@ class boss_baltharus_the_warborn : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if (!UpdateVictim() && !(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ bool introPhase = events.IsInPhase(PHASE_INTRO);
+ if (!UpdateVictim() && !introPhase)
return;
- if (!(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ if (!introPhase)
me->SetHealth(instance->GetData(DATA_BALTHARUS_SHARED_HEALTH));
events.Update(diff);
- if (me->HasUnitState(UNIT_STATE_CASTING) && !(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ if (me->HasUnitState(UNIT_STATE_CASTING) && !introPhase)
return;
while (uint32 eventId = events.ExecuteEvent())
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 13e4e5b51d5..d84091e3cc7 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -164,12 +164,7 @@ enum Phases
PHASE_INTRO = 1,
PHASE_ONE = 2,
PHASE_TWO = 3,
- PHASE_THREE = 4,
-
- PHASE_INTRO_MASK = 1 << PHASE_INTRO,
- PHASE_ONE_MASK = 1 << PHASE_ONE,
- PHASE_TWO_MASK = 1 << PHASE_TWO,
- PHASE_THREE_MASK = 1 << PHASE_THREE
+ PHASE_THREE = 4
};
enum Misc
@@ -324,7 +319,7 @@ class boss_halion : public CreatureScript
void EnterEvadeMode()
{
// Phase 1: We always can evade. Phase 2 & 3: We can evade if and only if the controller tells us to.
- if ((events.GetPhaseMask() & PHASE_ONE_MASK) || _canEvade)
+ if (events.IsInPhase(PHASE_ONE) || _canEvade)
generic_halionAI::EnterEvadeMode();
}
@@ -368,7 +363,7 @@ class boss_halion : public CreatureScript
void DamageTaken(Unit* attacker, uint32& damage)
{
- if (me->HealthBelowPctDamaged(75, damage) && (events.GetPhaseMask() & PHASE_ONE_MASK))
+ if (me->HealthBelowPctDamaged(75, damage) && events.IsInPhase(PHASE_ONE))
{
events.SetPhase(PHASE_TWO);
Talk(SAY_PHASE_TWO);
@@ -382,7 +377,7 @@ class boss_halion : public CreatureScript
return;
}
- if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ if (events.IsInPhase(PHASE_THREE))
{
// Don't consider copied damage.
if (!me->InSamePhase(attacker))
@@ -395,7 +390,7 @@ class boss_halion : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if (events.GetPhaseMask() & PHASE_TWO_MASK)
+ if (events.IsInPhase(PHASE_TWO))
return;
generic_halionAI::UpdateAI(diff);
@@ -527,7 +522,7 @@ class boss_twilight_halion : public CreatureScript
void DamageTaken(Unit* attacker, uint32& damage)
{
- if (me->HealthBelowPctDamaged(50, damage) && (events.GetPhaseMask() & PHASE_TWO_MASK))
+ if (me->HealthBelowPctDamaged(50, damage) && events.IsInPhase(PHASE_TWO))
{
events.SetPhase(PHASE_THREE);
me->CastStop();
@@ -536,7 +531,7 @@ class boss_twilight_halion : public CreatureScript
return;
}
- if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ if (events.IsInPhase(PHASE_THREE))
{
// Don't consider copied damage.
if (!me->InSamePhase(attacker))
@@ -692,7 +687,7 @@ class npc_halion_controller : public CreatureScript
// The isInCombat() check is needed because that check should be false when Halion is
// not engaged, while it would return true without as UpdateVictim() checks for
// combat state.
- if (!(_events.GetPhaseMask() & PHASE_INTRO_MASK) && me->isInCombat() && !UpdateVictim())
+ if (!(_events.IsInPhase(PHASE_INTRO)) && me->isInCombat() && !UpdateVictim())
{
EnterEvadeMode();
return;
@@ -977,11 +972,13 @@ class npc_meteor_strike_initial : public CreatureScript
public:
npc_meteor_strike_initial() : CreatureScript("npc_meteor_strike_initial") { }
- struct npc_meteor_strike_initialAI : public Scripted_NoMovementAI
+ struct npc_meteor_strike_initialAI : public ScriptedAI
{
- npc_meteor_strike_initialAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ npc_meteor_strike_initialAI(Creature* creature) : ScriptedAI(creature),
_instance(creature->GetInstanceScript())
- { }
+ {
+ SetCombatMovement(false);
+ }
void DoAction(int32 const action)
{
@@ -1049,13 +1046,15 @@ class npc_meteor_strike : public CreatureScript
public:
npc_meteor_strike() : CreatureScript("npc_meteor_strike") { }
- struct npc_meteor_strikeAI : public Scripted_NoMovementAI
+ struct npc_meteor_strikeAI : public ScriptedAI
{
- npc_meteor_strikeAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ npc_meteor_strikeAI(Creature* creature) : ScriptedAI(creature),
_instance(creature->GetInstanceScript())
{
_range = 5.0f;
_spawnCount = 0;
+
+ SetCombatMovement(false);
}
void DoAction(int32 const action)
@@ -1118,11 +1117,13 @@ class npc_combustion_consumption : public CreatureScript
public:
npc_combustion_consumption() : CreatureScript("npc_combustion_consumption") { }
- struct npc_combustion_consumptionAI : public Scripted_NoMovementAI
+ struct npc_combustion_consumptionAI : public ScriptedAI
{
- npc_combustion_consumptionAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ npc_combustion_consumptionAI(Creature* creature) : ScriptedAI(creature),
_instance(creature->GetInstanceScript()), _summonerGuid(0)
{
+ SetCombatMovement(false);
+
switch (me->GetEntry())
{
case NPC_COMBUSTION:
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
index e44f5fba1b8..17b804ca31e 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
@@ -154,9 +154,7 @@ enum Phases
{
// Anub'arak
PHASE_MELEE = 1,
- PHASE_SUBMERGED = 2,
-
- PHASE_MASK_MELEE = 1 << PHASE_MELEE
+ PHASE_SUBMERGED = 2
};
class boss_anubarak_trial : public CreatureScript
@@ -403,7 +401,7 @@ class boss_anubarak_trial : public CreatureScript
}
- if (HealthBelowPct(30) && events.GetPhaseMask() & PHASE_MASK_MELEE && !_reachedPhase3)
+ if (HealthBelowPct(30) && events.IsInPhase(PHASE_MELEE) && !_reachedPhase3)
{
_reachedPhase3 = true;
DoCastAOE(SPELL_LEECHING_SWARM);
@@ -411,7 +409,7 @@ class boss_anubarak_trial : public CreatureScript
Talk(SAY_LEECHING_SWARM);
}
- if (events.GetPhaseMask() & PHASE_MASK_MELEE)
+ if (events.IsInPhase(PHASE_MELEE))
DoMeleeAttackIfReady();
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
index 82cff5ff01f..a44938e0f82 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
@@ -18,8 +18,9 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
-#include "trial_of_the_crusader.h"
#include "SpellScript.h"
+#include "Player.h"
+#include "trial_of_the_crusader.h"
enum Yells
{
@@ -69,7 +70,6 @@ enum BossSpells
SPELL_FEL_INFERNO = 67047,
SPELL_FEL_STREAK = 66494,
SPELL_LORD_HITTIN = 66326, // special effect preventing more specific spells be cast on the same player within 10 seconds
- SPELL_MISTRESS_KISS_DEBUFF = 66334,
SPELL_MISTRESS_KISS_DAMAGE_SILENCE = 66359
};
@@ -223,10 +223,11 @@ class mob_legion_flame : public CreatureScript
public:
mob_legion_flame() : CreatureScript("mob_legion_flame") { }
- struct mob_legion_flameAI : public Scripted_NoMovementAI
+ struct mob_legion_flameAI : public ScriptedAI
{
- mob_legion_flameAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_legion_flameAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
_instance = creature->GetInstanceScript();
}
@@ -258,10 +259,11 @@ class mob_infernal_volcano : public CreatureScript
public:
mob_infernal_volcano() : CreatureScript("mob_infernal_volcano") { }
- struct mob_infernal_volcanoAI : public Scripted_NoMovementAI
+ struct mob_infernal_volcanoAI : public ScriptedAI
{
- mob_infernal_volcanoAI(Creature* creature) : Scripted_NoMovementAI(creature), _summons(me)
+ mob_infernal_volcanoAI(Creature* creature) : ScriptedAI(creature), _summons(me)
{
+ SetCombatMovement(false);
}
void Reset()
@@ -533,6 +535,21 @@ class spell_mistress_kiss : public SpellScriptLoader
}
};
+class MistressKissTargetSelector
+{
+ public:
+ MistressKissTargetSelector() { }
+
+ bool operator()(WorldObject* unit) const
+ {
+ if (unit->GetTypeId() == TYPEID_PLAYER)
+ if (unit->ToPlayer()->getPowerType() == POWER_MANA)
+ return false;
+
+ return true;
+ }
+};
+
class spell_mistress_kiss_area : public SpellScriptLoader
{
public:
@@ -542,44 +559,27 @@ class spell_mistress_kiss_area : public SpellScriptLoader
{
PrepareSpellScript(spell_mistress_kiss_area_SpellScript)
- bool Load()
+ void FilterTargets(std::list<WorldObject*>& targets)
{
- if (GetCaster())
- if (sSpellMgr->GetSpellIdForDifficulty(SPELL_MISTRESS_KISS_DEBUFF, GetCaster()))
- return true;
- return false;
- }
+ // get a list of players with mana
+ targets.remove_if(MistressKissTargetSelector());
+ if (targets.empty())
+ return;
- void HandleScript(SpellEffIndex /*effIndex*/)
- {
- Unit* caster = GetCaster();
- Unit* target = GetHitUnit();
- if (caster && target)
- caster->CastSpell(target, SPELL_MISTRESS_KISS_DEBUFF, true);
+ WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
+ targets.clear();
+ targets.push_back(target);
}
- void FilterTargets(std::list<WorldObject*>& targets)
+ void HandleScript(SpellEffIndex /*effIndex*/)
{
- // get a list of players with mana
- std::list<WorldObject*> _targets;
- for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
- if ((*itr)->ToUnit()->getPowerType() == POWER_MANA)
- _targets.push_back(*itr);
-
- // pick a random target and kiss him
- if (WorldObject* _target = Trinity::Containers::SelectRandomContainerElement(_targets))
- {
- // correctly fill "targets" for the visual effect
- targets.clear();
- targets.push_back(_target);
- if (Unit* caster = GetCaster())
- caster->CastSpell(_target->ToUnit(), SPELL_MISTRESS_KISS_DEBUFF, true);
- }
+ GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), true);
}
void Register()
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mistress_kiss_area_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnEffectHitTarget += SpellEffectFn(spell_mistress_kiss_area_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
index 03a305356c4..95f59903141 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp
@@ -146,10 +146,7 @@ enum Phases
{
PHASE_MOBILE = 1,
PHASE_STATIONARY = 2,
- PHASE_SUBMERGED = 3,
-
- PHASE_MASK_MOBILE = 1 << PHASE_MOBILE,
- PHASE_MASK_STATIONARY = 1 << PHASE_STATIONARY
+ PHASE_SUBMERGED = 3
};
class boss_gormok : public CreatureScript
@@ -624,9 +621,9 @@ struct boss_jormungarAI : public BossAI
return;
}
}
- if (events.GetPhaseMask() & PHASE_MASK_MOBILE)
+ if (events.IsInPhase(PHASE_MOBILE))
DoMeleeAttackIfReady();
- if (events.GetPhaseMask() & PHASE_MASK_STATIONARY)
+ if (events.IsInPhase(PHASE_STATIONARY))
DoSpellAttackIfReady(SpitSpell);
}
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp
index 9b6f4a6a0da..2509c3d0d59 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp
@@ -654,11 +654,11 @@ class mob_bullet_controller : public CreatureScript
public:
mob_bullet_controller() : CreatureScript("mob_bullet_controller") { }
- struct mob_bullet_controllerAI : public Scripted_NoMovementAI
+ struct mob_bullet_controllerAI : public ScriptedAI
{
- mob_bullet_controllerAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_bullet_controllerAI(Creature* creature) : ScriptedAI(creature)
{
- Reset();
+ SetCombatMovement(false);
}
void Reset()
diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp
index 261eb854aa3..5d261fd3804 100644
--- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp
@@ -118,7 +118,7 @@ class boss_bronjahm : public CreatureScript
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/)
{
- if (events.GetPhaseMask() & (1 << PHASE_1) && !HealthAbovePct(30))
+ if (events.IsInPhase(PHASE_1) && !HealthAbovePct(30))
{
events.SetPhase(PHASE_2);
DoCast(me, SPELL_TELEPORT);
diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp
index 160d45f5140..cc25e450340 100644
--- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp
@@ -52,11 +52,7 @@ enum Phases
{
PHASE_ONE = 1,
PHASE_TWO = 2,
- PHASE_THREE = 3,
-
- PHASE_ONE_MASK = 1 << PHASE_ONE,
- PHASE_TWO_MASK = 1 << PHASE_TWO,
- PHASE_THREE_MASK = 1 << PHASE_THREE,
+ PHASE_THREE = 3
};
enum MiscData
@@ -136,7 +132,7 @@ enum Events
void DamageTaken(Unit* /*attacker*/, uint32& /*uiDamage*/)
{
- if (events.GetPhaseMask() & PHASE_ONE_MASK && !HealthAbovePct(66))
+ if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(66))
{
events.SetPhase(PHASE_TWO);
Talk(SAY_PHASE2);
@@ -146,7 +142,7 @@ enum Events
return;
}
- if (events.GetPhaseMask() & PHASE_TWO_MASK && !HealthAbovePct(33))
+ if (events.IsInPhase(PHASE_TWO) && !HealthAbovePct(33))
{
events.SetPhase(PHASE_THREE);
Talk(SAY_PHASE3);
@@ -162,12 +158,12 @@ enum Events
if (type != EFFECT_MOTION_TYPE || id != POINT_FORGE)
return;
- if (events.GetPhaseMask() & PHASE_TWO_MASK)
+ if (events.IsInPhase(PHASE_TWO))
{
DoCast(me, SPELL_FORGE_BLADE);
SetEquipmentSlots(false, EQUIP_ID_SWORD);
}
- if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ if (events.IsInPhase(PHASE_THREE))
{
me->RemoveAurasDueToSpell(SPELL_FORGE_BLADE_HELPER);
DoCast(me, SPELL_FORGE_MACE);
@@ -226,15 +222,15 @@ enum Events
break;
case EVENT_JUMP:
me->AttackStop();
- if (events.GetPhaseMask() & PHASE_TWO_MASK)
+ if (events.IsInPhase(PHASE_TWO))
me->GetMotionMaster()->MoveJump(northForgePos.GetPositionX(), northForgePos.GetPositionY(), northForgePos.GetPositionZ(), 25.0f, 15.0f);
- else if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ else if (events.IsInPhase(PHASE_THREE))
me->GetMotionMaster()->MoveJump(southForgePos.GetPositionX(), southForgePos.GetPositionY(), southForgePos.GetPositionZ(), 25.0f, 15.0f);
break;
case EVENT_RESUME_ATTACK:
- if (events.GetPhaseMask() & PHASE_TWO_MASK)
+ if (events.IsInPhase(PHASE_THREE))
events.ScheduleEvent(EVENT_CHILLING_WAVE, 5000, 0, PHASE_TWO);
- else if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ else if (events.IsInPhase(PHASE_THREE))
events.ScheduleEvent(EVENT_DEEP_FREEZE, 10000, 0, PHASE_THREE);
AttackStart(me->getVictim());
break;
diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp
index 783cc266509..165cd8d647a 100644
--- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp
@@ -94,7 +94,7 @@ enum Phases
PHASE_NONE = 0,
PHASE_INTRO = 1,
PHASE_COMBAT = 2,
- PHASE_OUTRO = 3,
+ PHASE_OUTRO = 3
};
enum Actions
@@ -168,7 +168,7 @@ class boss_tyrannus : public CreatureScript
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
- if (victim && me->Attack(victim, true) && !(events.GetPhaseMask() & (1 << PHASE_INTRO)))
+ if (victim && me->Attack(victim, true) && !events.IsInPhase(PHASE_INTRO))
me->GetMotionMaster()->MoveChase(victim);
}
@@ -217,7 +217,7 @@ class boss_tyrannus : public CreatureScript
void UpdateAI(const uint32 diff)
{
- if (!UpdateVictim() && !(events.GetPhaseMask() & (1 << PHASE_INTRO)))
+ if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO))
return;
events.Update(diff);
@@ -337,7 +337,7 @@ class boss_rimefang : public CreatureScript
void UpdateAI(const uint32 diff)
{
- if (!UpdateVictim() && !(_events.GetPhaseMask() & (1 << PHASE_COMBAT)))
+ if (!UpdateVictim() && !_events.IsInPhase(PHASE_COMBAT))
return;
_events.Update(diff);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
index c8caa3976e4..ab0c44aa6d0 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
@@ -46,6 +46,7 @@ enum Spells
SPELL_FRENZIED_BLOODTHIRST_VISUAL = 71949,
SPELL_VAMPIRIC_BITE = 71726,
SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_PLR = 70879,
+ SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL = 70872,
SPELL_FRENZIED_BLOODTHIRST = 70877,
SPELL_UNCONTROLLABLE_FRENZY = 70923,
SPELL_PRESENCE_OF_THE_DARKFALLEN = 71952,
@@ -698,6 +699,42 @@ class spell_blood_queen_bloodbolt : public SpellScriptLoader
}
};
+// 70871 - Essence of the Blood Queen
+class spell_blood_queen_essence_of_the_blood_queen : public SpellScriptLoader
+{
+ public:
+ spell_blood_queen_essence_of_the_blood_queen() : SpellScriptLoader("spell_blood_queen_essence_of_the_blood_queen") { }
+
+ class spell_blood_queen_essence_of_the_blood_queen_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_blood_queen_essence_of_the_blood_queen_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL))
+ return false;
+ return true;
+ }
+
+ void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ int32 heal = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount());
+ GetTarget()->CastCustomSpell(SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), TRIGGERED_FULL_MASK, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_blood_queen_essence_of_the_blood_queen_AuraScript::OnProc, EFFECT_1, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_blood_queen_essence_of_the_blood_queen_AuraScript();
+ }
+};
+
class spell_blood_queen_pact_of_the_darkfallen : public SpellScriptLoader
{
public:
@@ -849,6 +886,7 @@ void AddSC_boss_blood_queen_lana_thel()
new spell_blood_queen_vampiric_bite();
new spell_blood_queen_frenzied_bloodthirst();
new spell_blood_queen_bloodbolt();
+ new spell_blood_queen_essence_of_the_blood_queen();
new spell_blood_queen_pact_of_the_darkfallen();
new spell_blood_queen_pact_of_the_darkfallen_dmg();
new spell_blood_queen_pact_of_the_darkfallen_dmg_target();
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp
index ce1166663f3..c92f10e8b95 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp
@@ -185,9 +185,7 @@ enum Phases
{
PHASE_INTRO_A = 1,
PHASE_INTRO_H = 2,
- PHASE_COMBAT = 3,
-
- PHASE_INTRO_MASK = (1 << PHASE_INTRO_A) | (1 << PHASE_INTRO_H),
+ PHASE_COMBAT = 3
};
enum Actions
@@ -415,9 +413,16 @@ class boss_deathbringer_saurfang : public CreatureScript
}
}
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
+ {
+ if (spell->Id == SPELL_BLOOD_LINK_POWER)
+ if (Aura* bloodPower = me->GetAura(SPELL_BLOOD_POWER))
+ bloodPower->RecalculateAmountOfEffects();
+ }
+
void UpdateAI(uint32 const diff)
{
- if (!UpdateVictim() && !(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ if (!UpdateVictim() && !(events.IsInPhase(PHASE_INTRO_A) || events.IsInPhase(PHASE_INTRO_H)))
return;
events.Update(diff);
@@ -605,7 +610,7 @@ class npc_high_overlord_saurfang_icc : public CreatureScript
case ACTION_START_EVENT:
{
// Prevent crashes
- if (_events.GetPhaseMask() & PHASE_INTRO_MASK)
+ if (_events.IsInPhase(PHASE_INTRO_A) || _events.IsInPhase(PHASE_INTRO_H))
return;
GetCreatureListWithEntryInGrid(_guardList, me, NPC_SE_KOR_KRON_REAVER, 20.0f);
@@ -814,7 +819,7 @@ class npc_muradin_bronzebeard_icc : public CreatureScript
case ACTION_START_EVENT:
{
// Prevent crashes
- if (_events.GetPhaseMask() & PHASE_INTRO_MASK)
+ if (_events.IsInPhase(PHASE_INTRO_A) || _events.IsInPhase(PHASE_INTRO_H))
return;
_events.SetPhase(PHASE_INTRO_A);
@@ -1004,8 +1009,6 @@ class spell_deathbringer_blood_link : public SpellScriptLoader
void HandleDummy(SpellEffIndex /*effIndex*/)
{
GetHitUnit()->CastCustomSpell(SPELL_BLOOD_LINK_POWER, SPELLVALUE_BASE_POINT0, GetEffectValue(), GetHitUnit(), true);
- if (Aura* bloodPower = GetHitUnit()->GetAura(SPELL_BLOOD_POWER))
- bloodPower->RecalculateAmountOfEffects();
PreventHitDefaultEffect(EFFECT_0);
}
@@ -1093,13 +1096,6 @@ class spell_deathbringer_blood_power : public SpellScriptLoader
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_deathbringer_blood_power_AuraScript::RecalculateHook, EFFECT_0, SPELL_AURA_MOD_SCALE);
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_deathbringer_blood_power_AuraScript::RecalculateHook, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
}
-
- bool Load()
- {
- if (GetUnitOwner()->getPowerType() != POWER_ENERGY)
- return false;
- return true;
- }
};
SpellScript* GetSpellScript() const
@@ -1251,7 +1247,6 @@ class spell_deathbringer_blood_nova_targeting : public SpellScriptLoader
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_deathbringer_blood_nova_targeting_SpellScript::FilterTargetsInitial, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_deathbringer_blood_nova_targeting_SpellScript::FilterTargetsSubsequent, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
OnEffectHitTarget += SpellEffectFn(spell_deathbringer_blood_nova_targeting_SpellScript::HandleForceCast, EFFECT_0, SPELL_EFFECT_FORCE_CAST);
- OnEffectHitTarget += SpellEffectFn(spell_deathbringer_blood_nova_targeting_SpellScript::HandleForceCast, EFFECT_0, SPELL_EFFECT_FORCE_CAST);
}
WorldObject* target;
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
index 2b7c9c62f6b..8854a8d1f1a 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
@@ -160,10 +160,7 @@ enum Phases
PHASE_ALL = 0,
PHASE_INTRO = 1,
PHASE_ONE = 2,
- PHASE_TWO = 3,
-
- PHASE_INTRO_MASK = 1 << PHASE_INTRO,
- PHASE_ONE_MASK = 1 << PHASE_ONE,
+ PHASE_TWO = 3
};
enum DeprogrammingData
@@ -259,7 +256,7 @@ class boss_lady_deathwhisper : public CreatureScript
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
- if (victim && me->Attack(victim, true) && !(events.GetPhaseMask() & PHASE_ONE_MASK))
+ if (victim && me->Attack(victim, true) && !events.IsInPhase(PHASE_ONE))
me->GetMotionMaster()->MoveChase(victim);
}
@@ -358,7 +355,7 @@ class boss_lady_deathwhisper : public CreatureScript
void DamageTaken(Unit* /*damageDealer*/, uint32& damage)
{
// phase transition
- if (events.GetPhaseMask() & PHASE_ONE_MASK && damage > (uint32)me->GetPower(POWER_MANA))
+ if (events.IsInPhase(PHASE_ONE) && damage > uint32(me->GetPower(POWER_MANA)))
{
Talk(SAY_PHASE_2);
Talk(EMOTE_PHASE_2);
@@ -406,12 +403,12 @@ class boss_lady_deathwhisper : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if ((!UpdateVictim() && !(events.GetPhaseMask() & PHASE_INTRO_MASK)) || !CheckInRoom())
+ if ((!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) || !CheckInRoom())
return;
events.Update(diff);
- if (me->HasUnitState(UNIT_STATE_CASTING) && !(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ if (me->HasUnitState(UNIT_STATE_CASTING) && !events.IsInPhase(PHASE_INTRO))
return;
while (uint32 eventId = events.ExecuteEvent())
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
index 4c18821d2f7..d7b8408b7c4 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
@@ -20,8 +20,9 @@
#include "ScriptedCreature.h"
#include "SpellAuras.h"
#include "MapManager.h"
-#include "icecrown_citadel.h"
+#include "MoveSplineInit.h"
#include "Player.h"
+#include "icecrown_citadel.h"
enum ScriptTexts
{
@@ -53,7 +54,7 @@ enum Spells
SPELL_COLDFLAME_SUMMON = 69147,
};
-uint32 const boneSpikeSummonId[3] = {69062, 72669, 72670};
+uint32 const BoneSpikeSummonId[3] = {69062, 72669, 72670};
enum Events
{
@@ -78,7 +79,46 @@ enum MovementPoints
POINT_TARGET_COLDFLAME = 36672631,
};
-#define DATA_COLDFLAME_GUID 0
+enum MiscInfo
+{
+ DATA_COLDFLAME_GUID = 0,
+
+ // Manual marking for targets hit by Bone Slice as no aura exists for this purpose
+ // These units are the tanks in this encounter
+ // and should be immune to Bone Spike Graveyard
+ DATA_SPIKE_IMMUNE = 1,
+ //DATA_SPIKE_IMMUNE_1, = 2, // Reserved & used
+ //DATA_SPIKE_IMMUNE_2, = 3, // Reserved & used
+
+ ACTION_CLEAR_SPIKE_IMMUNITIES = 1,
+
+ MAX_BONE_SPIKE_IMMUNE = 3,
+};
+
+class BoneSpikeTargetSelector : public std::unary_function<Unit*, bool>
+{
+ public:
+ BoneSpikeTargetSelector(UnitAI* ai) : _ai(ai) { }
+
+ bool operator()(Unit* unit) const
+ {
+ if (unit->GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ if (unit->HasAura(SPELL_IMPALED))
+ return false;
+
+ // Check if it is one of the tanks soaking Bone Slice
+ for (uint32 i = 0; i < MAX_BONE_SPIKE_IMMUNE; ++i)
+ if (unit->GetGUID() == _ai->GetGUID(DATA_SPIKE_IMMUNE + i))
+ return false;
+
+ return true;
+ }
+
+ private:
+ UnitAI* _ai;
+};
class boss_lord_marrowgar : public CreatureScript
{
@@ -103,11 +143,12 @@ class boss_lord_marrowgar : public CreatureScript
me->RemoveAurasDueToSpell(SPELL_BONE_STORM);
me->RemoveAurasDueToSpell(SPELL_BERSERK);
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
- events.ScheduleEvent(EVENT_BONE_SPIKE_GRAVEYARD, urand(10000, 15000), EVENT_GROUP_SPECIAL);
+ events.ScheduleEvent(EVENT_BONE_SPIKE_GRAVEYARD, 15000, EVENT_GROUP_SPECIAL);
events.ScheduleEvent(EVENT_COLDFLAME, 5000, EVENT_GROUP_SPECIAL);
events.ScheduleEvent(EVENT_WARN_BONE_STORM, urand(45000, 50000));
events.ScheduleEvent(EVENT_ENRAGE, 600000);
_boneSlice = false;
+ _boneSpikeImmune.clear();
}
void EnterCombat(Unit* /*who*/)
@@ -199,18 +240,18 @@ class boss_lord_marrowgar : public CreatureScript
if (!unit)
unit = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
if (unit)
- me->GetMotionMaster()->MovePoint(POINT_TARGET_BONESTORM_PLAYER, unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ());
+ me->GetMotionMaster()->MovePoint(POINT_TARGET_BONESTORM_PLAYER, *unit);
break;
}
case EVENT_BONE_STORM_END:
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
- DoStartMovement(me->getVictim());
+ me->GetMotionMaster()->MoveChase(me->getVictim());
me->SetSpeed(MOVE_RUN, _baseSpeed, true);
events.CancelEvent(EVENT_BONE_STORM_MOVE);
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
if (!IsHeroic())
- events.RescheduleEvent(EVENT_BONE_SPIKE_GRAVEYARD, urand(15000, 20000), EVENT_GROUP_SPECIAL);
+ events.RescheduleEvent(EVENT_BONE_SPIKE_GRAVEYARD, 15000, EVENT_GROUP_SPECIAL);
break;
case EVENT_ENABLE_BONE_SLICE:
_boneSlice = true;
@@ -239,7 +280,7 @@ class boss_lord_marrowgar : public CreatureScript
return;
// lock movement
- DoStartNoMovement(me->getVictim());
+ me->GetMotionMaster()->MoveIdle();
}
Position const* GetLastColdflamePosition() const
@@ -247,23 +288,51 @@ class boss_lord_marrowgar : public CreatureScript
return &_coldflameLastPos;
}
- uint64 GetGUID(int32 type/* = 0 */) const
+ uint64 GetGUID(int32 type /*= 0 */) const
{
- if (type == DATA_COLDFLAME_GUID)
- return _coldflameTarget;
+ switch (type)
+ {
+ case DATA_COLDFLAME_GUID:
+ return _coldflameTarget;
+ case DATA_SPIKE_IMMUNE + 0:
+ case DATA_SPIKE_IMMUNE + 1:
+ case DATA_SPIKE_IMMUNE + 2:
+ {
+ uint32 index = uint32(type - DATA_SPIKE_IMMUNE);
+ if (index < _boneSpikeImmune.size())
+ return _boneSpikeImmune[index];
+
+ break;
+ }
+ }
+
return 0LL;
}
- void SetGUID(uint64 guid, int32 type/* = 0 */)
+ void SetGUID(uint64 guid, int32 type /*= 0 */)
{
- if (type != DATA_COLDFLAME_GUID)
+ switch (type)
+ {
+ case DATA_COLDFLAME_GUID:
+ _coldflameTarget = guid;
+ break;
+ case DATA_SPIKE_IMMUNE:
+ _boneSpikeImmune.push_back(guid);
+ break;
+ }
+ }
+
+ void DoAction(int32 const action)
+ {
+ if (action != ACTION_CLEAR_SPIKE_IMMUNITIES)
return;
- _coldflameTarget = guid;
+ _boneSpikeImmune.clear();
}
private:
Position _coldflameLastPos;
+ std::vector<uint64> _boneSpikeImmune;
uint64 _coldflameTarget;
uint32 _boneStormDuration;
float _baseSpeed;
@@ -295,18 +364,17 @@ class npc_coldflame : public CreatureScript
if (owner->GetTypeId() != TYPEID_UNIT)
return;
- Creature* creOwner = owner->ToCreature();
Position pos;
- // random target case
+ if (MarrowgarAI* marrowgarAI = CAST_AI(MarrowgarAI, owner->GetAI()))
+ pos.Relocate(marrowgarAI->GetLastColdflamePosition());
+ else
+ pos.Relocate(owner);
+
if (owner->HasAura(SPELL_BONE_STORM))
{
- if (MarrowgarAI* marrowgarAI = CAST_AI(MarrowgarAI, creOwner->AI()))
- {
- Position const* ownerPos = marrowgarAI->GetLastColdflamePosition();
- float ang = me->GetAngle(ownerPos) - static_cast<float>(M_PI);
- me->SetOrientation(ang);
- owner->GetNearPosition(pos, 2.5f, 0.0f);
- }
+ float ang = Position::NormalizeOrientation(pos.GetAngle(me));
+ me->SetOrientation(ang);
+ owner->GetNearPoint2D(pos.m_positionX, pos.m_positionY, 5.0f - owner->GetObjectSize(), ang);
}
else
{
@@ -317,12 +385,14 @@ class npc_coldflame : public CreatureScript
return;
}
- me->SetOrientation(owner->GetAngle(target));
- owner->GetNearPosition(pos, owner->GetObjectSize() / 2.0f, 0.0f);
+ float ang = Position::NormalizeOrientation(pos.GetAngle(target));
+ me->SetOrientation(ang);
+ owner->GetNearPoint2D(pos.m_positionX, pos.m_positionY, 15.0f - owner->GetObjectSize(), ang);
}
me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), me->GetPositionZ(), me->GetOrientation());
- _events.ScheduleEvent(EVENT_COLDFLAME_TRIGGER, 450);
+ DoCast(SPELL_COLDFLAME_SUMMON);
+ _events.ScheduleEvent(EVENT_COLDFLAME_TRIGGER, 500);
}
void UpdateAI(uint32 const diff)
@@ -332,10 +402,10 @@ class npc_coldflame : public CreatureScript
if (_events.ExecuteEvent() == EVENT_COLDFLAME_TRIGGER)
{
Position newPos;
- me->GetNearPosition(newPos, 5.5f, 0.0f);
+ me->GetNearPosition(newPos, 5.0f, 0.0f);
me->NearTeleportTo(newPos.GetPositionX(), newPos.GetPositionY(), me->GetPositionZ(), me->GetOrientation());
DoCast(SPELL_COLDFLAME_SUMMON);
- _events.ScheduleEvent(EVENT_COLDFLAME_TRIGGER, 450);
+ _events.ScheduleEvent(EVENT_COLDFLAME_TRIGGER, 500);
}
}
@@ -354,11 +424,13 @@ class npc_bone_spike : public CreatureScript
public:
npc_bone_spike() : CreatureScript("npc_bone_spike") { }
- struct npc_bone_spikeAI : public Scripted_NoMovementAI
+ struct npc_bone_spikeAI : public ScriptedAI
{
- npc_bone_spikeAI(Creature* creature) : Scripted_NoMovementAI(creature), _hasTrappedUnit(false)
+ npc_bone_spikeAI(Creature* creature) : ScriptedAI(creature), _hasTrappedUnit(false)
{
ASSERT(creature->GetVehicleKit());
+
+ SetCombatMovement(false);
}
void JustDied(Unit* /*killer*/)
@@ -384,6 +456,24 @@ class npc_bone_spike : public CreatureScript
_hasTrappedUnit = true;
}
+ void PassengerBoarded(Unit* passenger, int8 /*seat*/, bool apply)
+ {
+ if (!apply)
+ return;
+
+ /// @HACK - Change passenger offset to the one taken directly from sniffs
+ /// Remove this when proper calculations are implemented.
+ /// This fixes healing spiked people
+ Movement::MoveSplineInit init(passenger);
+ init.DisableTransportPathTransformations();
+ init.MoveTo(-0.02206125f, -0.02132235f, 5.514783f, false);
+ init.Launch();
+
+ /// @WORKAROUND - Clear ON VEHICLE state to allow healing (Invalid target errors)
+ /// Current rule for applying this state is questionable (seatFlags & VEHICLE_SEAT_FLAG_ALLOW_TURNING ???)
+ passenger->ClearUnitState(UNIT_STATE_ONVEHICLE);
+ }
+
void UpdateAI(uint32 const diff)
{
if (!_hasTrappedUnit)
@@ -486,16 +576,24 @@ class spell_marrowgar_coldflame_damage : public SpellScriptLoader
{
PrepareAuraScript(spell_marrowgar_coldflame_damage_AuraScript);
- void OnPeriodic(AuraEffect const* /*aurEff*/)
+ bool CanBeAppliedOn(Unit* target)
{
- if (DynamicObject* owner = GetDynobjOwner())
- if (GetTarget()->GetExactDist2d(owner) >= owner->GetRadius() || GetTarget()->HasAura(SPELL_IMPALED))
- PreventDefaultAction();
+ if (target->HasAura(SPELL_IMPALED))
+ return false;
+
+ if (target->GetExactDist2d(GetOwner()) > GetSpellInfo()->Effects[EFFECT_0].CalcRadius())
+ return false;
+
+ if (Aura* aur = target->GetAura(GetId()))
+ if (aur->GetOwner() != GetOwner())
+ return false;
+
+ return true;
}
void Register()
{
- OnEffectPeriodic += AuraEffectPeriodicFn(spell_marrowgar_coldflame_damage_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_marrowgar_coldflame_damage_AuraScript::CanBeAppliedOn);
}
};
@@ -514,9 +612,23 @@ class spell_marrowgar_bone_spike_graveyard : public SpellScriptLoader
{
PrepareSpellScript(spell_marrowgar_bone_spike_graveyard_SpellScript);
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ for (uint32 i = 0; i < 3; ++i)
+ if (!sSpellMgr->GetSpellInfo(BoneSpikeSummonId[i]))
+ return false;
+
+ return true;
+ }
+
+ bool Load()
+ {
+ return GetCaster()->GetTypeId() == TYPEID_UNIT && GetCaster()->IsAIEnabled;
+ }
+
SpellCastResult CheckCast()
{
- return GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, 0.0f, true, -SPELL_IMPALED) ? SPELL_CAST_OK : SPELL_FAILED_NO_VALID_TARGETS;
+ return GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, BoneSpikeTargetSelector(GetCaster()->GetAI())) ? SPELL_CAST_OK : SPELL_FAILED_NO_VALID_TARGETS;
}
void HandleSpikes(SpellEffIndex effIndex)
@@ -524,22 +636,22 @@ class spell_marrowgar_bone_spike_graveyard : public SpellScriptLoader
PreventHitDefaultEffect(effIndex);
if (Creature* marrowgar = GetCaster()->ToCreature())
{
- bool didHit = false;
CreatureAI* marrowgarAI = marrowgar->AI();
uint8 boneSpikeCount = uint8(GetCaster()->GetMap()->GetSpawnMode() & 1 ? 3 : 1);
- for (uint8 i = 0; i < boneSpikeCount; ++i)
- {
- // select any unit but not the tank
- Unit* target = marrowgarAI->SelectTarget(SELECT_TARGET_RANDOM, 1, 150.0f, true, -SPELL_IMPALED);
- if (!target)
- break;
- didHit = true;
- target->CastCustomSpell(boneSpikeSummonId[i], SPELLVALUE_BASE_POINT0, 0, target, true);
+ std::list<Unit*> targets;
+ marrowgarAI->SelectTargetList(targets, BoneSpikeTargetSelector(marrowgarAI), boneSpikeCount, SELECT_TARGET_RANDOM);
+ if (targets.empty())
+ return;
+
+ uint32 i = 0;
+ for (std::list<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr, ++i)
+ {
+ Unit* target = *itr;
+ target->CastCustomSpell(BoneSpikeSummonId[i], SPELLVALUE_BASE_POINT0, 0, target, true);
}
- if (didHit)
- marrowgarAI->Talk(SAY_BONESPIKE);
+ marrowgarAI->Talk(SAY_BONESPIKE);
}
}
@@ -582,6 +694,58 @@ class spell_marrowgar_bone_storm : public SpellScriptLoader
}
};
+class spell_marrowgar_bone_slice : public SpellScriptLoader
+{
+ public:
+ spell_marrowgar_bone_slice() : SpellScriptLoader("spell_marrowgar_bone_slice") { }
+
+ class spell_marrowgar_bone_slice_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_marrowgar_bone_slice_SpellScript);
+
+ bool Load()
+ {
+ _targetCount = 0;
+ return true;
+ }
+
+ void ClearSpikeImmunities()
+ {
+ GetCaster()->GetAI()->DoAction(ACTION_CLEAR_SPIKE_IMMUNITIES);
+ }
+
+ void CountTargets(std::list<WorldObject*>& targets)
+ {
+ _targetCount = std::min<uint32>(targets.size(), GetSpellInfo()->MaxAffectedTargets);
+ }
+
+ void SplitDamage()
+ {
+ // Mark the unit as hit, even if the spell missed or was dodged/parried
+ GetCaster()->GetAI()->SetGUID(GetHitUnit()->GetGUID(), DATA_SPIKE_IMMUNE);
+
+ if (!_targetCount)
+ return; // This spell can miss all targets
+
+ SetHitDamage(GetHitDamage() / _targetCount);
+ }
+
+ void Register()
+ {
+ BeforeCast += SpellCastFn(spell_marrowgar_bone_slice_SpellScript::ClearSpikeImmunities);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_marrowgar_bone_slice_SpellScript::CountTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
+ OnHit += SpellHitFn(spell_marrowgar_bone_slice_SpellScript::SplitDamage);
+ }
+
+ uint32 _targetCount;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_marrowgar_bone_slice_SpellScript();
+ }
+};
+
void AddSC_boss_lord_marrowgar()
{
new boss_lord_marrowgar();
@@ -592,4 +756,5 @@ void AddSC_boss_lord_marrowgar()
new spell_marrowgar_coldflame_damage();
new spell_marrowgar_bone_spike_graveyard();
new spell_marrowgar_bone_storm();
+ new spell_marrowgar_bone_slice();
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
index 05bf7b9794a..103c72e947d 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
@@ -145,10 +145,7 @@ enum Phases
PHASE_ROTFACE = 2,
PHASE_COMBAT_1 = 4,
PHASE_COMBAT_2 = 5,
- PHASE_COMBAT_3 = 6,
-
- PHASE_MASK_COMBAT = (1 << PHASE_COMBAT_1) | (1 << PHASE_COMBAT_2) | (1 << PHASE_COMBAT_3),
- PHASE_MASK_NOT_SELF = (1 << PHASE_FESTERGUT) | (1 << PHASE_ROTFACE)
+ PHASE_COMBAT_3 = 6
};
enum Points
@@ -233,7 +230,7 @@ class boss_professor_putricide : public CreatureScript
void Reset()
{
- if (!(events.GetPhaseMask() & PHASE_MASK_NOT_SELF))
+ if (!(events.IsInPhase(PHASE_ROTFACE) || events.IsInPhase(PHASE_FESTERGUT)))
instance->SetBossState(DATA_PROFESSOR_PUTRICIDE, NOT_STARTED);
instance->SetData(DATA_NAUSEA_ACHIEVEMENT, uint32(true));
@@ -252,7 +249,7 @@ class boss_professor_putricide : public CreatureScript
void EnterCombat(Unit* who)
{
- if (events.GetPhaseMask() & PHASE_MASK_NOT_SELF)
+ if (events.IsInPhase(PHASE_ROTFACE) || events.IsInPhase(PHASE_FESTERGUT))
return;
if (!instance->CheckRequiredBosses(DATA_PROFESSOR_PUTRICIDE, who->ToPlayer()))
@@ -282,7 +279,7 @@ class boss_professor_putricide : public CreatureScript
{
_JustReachedHome();
me->SetWalk(false);
- if (events.GetPhaseMask() & PHASE_MASK_COMBAT)
+ if (events.IsInPhase(PHASE_COMBAT_1) || events.IsInPhase(PHASE_COMBAT_2) || events.IsInPhase(PHASE_COMBAT_3))
instance->SetBossState(DATA_PROFESSOR_PUTRICIDE, FAIL);
}
@@ -568,7 +565,7 @@ class boss_professor_putricide : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if ((!(events.GetPhaseMask() & PHASE_MASK_NOT_SELF) && !UpdateVictim()) || !CheckInRoom())
+ if ((!(events.IsInPhase(PHASE_ROTFACE) || events.IsInPhase(PHASE_FESTERGUT)) && !UpdateVictim()) || !CheckInRoom())
return;
events.Update(diff);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
index 45c5302bfe8..1c6ed848158 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
@@ -576,11 +576,12 @@ class npc_ice_tomb : public CreatureScript
public:
npc_ice_tomb() : CreatureScript("npc_ice_tomb") { }
- struct npc_ice_tombAI : public Scripted_NoMovementAI
+ struct npc_ice_tombAI : public ScriptedAI
{
- npc_ice_tombAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_ice_tombAI(Creature* creature) : ScriptedAI(creature)
{
_trappedPlayerGUID = 0;
+ SetCombatMovement(false);
}
void Reset()
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
index 962e12a2461..7ced791f44c 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -145,6 +145,7 @@ enum Spells
SPELL_RESTORE_SOUL = 72595,
SPELL_RESTORE_SOULS = 73650, // Heroic
SPELL_DARK_HUNGER = 69383, // Passive proc healing
+ SPELL_DARK_HUNGER_HEAL = 69384,
SPELL_DESTROY_SOUL = 74086, // Used when Terenas Menethil dies
SPELL_SOUL_RIP = 69397, // Deals increasing damage
SPELL_SOUL_RIP_DAMAGE = 69398,
@@ -278,20 +279,10 @@ enum Phases
PHASE_THREE = 4,
PHASE_TRANSITION = 5,
PHASE_FROSTMOURNE = 6, // only set on heroic mode when all players are sent into frostmourne
- PHASE_OUTRO = 7,
-
- PHASE_MASK_INTRO = 1 << PHASE_INTRO,
- PHASE_MASK_ONE = 1 << PHASE_ONE,
- PHASE_MASK_TWO = 1 << PHASE_TWO,
- PHASE_MASK_THREE = 1 << PHASE_THREE,
- PHASE_MASK_TRANSITION = 1 << PHASE_TRANSITION,
- PHASE_MASK_NO_CAST_CHECK = (1 << PHASE_TRANSITION) | (1 << PHASE_FROSTMOURNE) | (1 << PHASE_OUTRO),
- PHASE_MASK_FROSTMOURNE = 1 << PHASE_FROSTMOURNE,
- PHASE_MASK_OUTRO = 1 << PHASE_OUTRO,
- PHASE_MASK_NO_VICTIM = (1 << PHASE_INTRO) | (1 << PHASE_OUTRO) | (1 << PHASE_FROSTMOURNE),
+ PHASE_OUTRO = 7
};
-#define PHASE_TWO_THREE (events.GetPhaseMask() & PHASE_MASK_TWO ? PHASE_TWO : PHASE_THREE)
+#define PHASE_TWO_THREE (events.IsInPhase(PHASE_TWO) ? PHASE_TWO : PHASE_THREE)
Position const CenterPosition = {503.6282f, -2124.655f, 840.8569f, 0.0f};
Position const TirionIntro = {489.2970f, -2124.840f, 840.8569f, 0.0f};
@@ -569,7 +560,7 @@ class boss_the_lich_king : public CreatureScript
void KilledUnit(Unit* victim)
{
- if (victim->GetTypeId() == TYPEID_PLAYER && !me->IsInEvadeMode() && !(events.GetPhaseMask() & PHASE_MASK_OUTRO))
+ if (victim->GetTypeId() == TYPEID_PLAYER && !me->IsInEvadeMode() && !events.IsInPhase(PHASE_OUTRO))
Talk(SAY_LK_KILL);
}
@@ -649,7 +640,7 @@ class boss_the_lich_king : public CreatureScript
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/)
{
- if (events.GetPhaseMask() & PHASE_MASK_ONE && !HealthAbovePct(70))
+ if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(70))
{
events.SetPhase(PHASE_TRANSITION);
me->SetReactState(REACT_PASSIVE);
@@ -658,7 +649,7 @@ class boss_the_lich_king : public CreatureScript
return;
}
- if (events.GetPhaseMask() & PHASE_MASK_TWO && !HealthAbovePct(40))
+ if (events.IsInPhase(PHASE_TWO) && !HealthAbovePct(40))
{
events.SetPhase(PHASE_TRANSITION);
me->SetReactState(REACT_PASSIVE);
@@ -667,7 +658,7 @@ class boss_the_lich_king : public CreatureScript
return;
}
- if (events.GetPhaseMask() & PHASE_MASK_THREE && !HealthAbovePct(10))
+ if (events.IsInPhase(PHASE_THREE) && !HealthAbovePct(10))
{
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
@@ -738,7 +729,7 @@ class boss_the_lich_king : public CreatureScript
summon->SetReactState(REACT_PASSIVE);
summon->SetSpeed(MOVE_FLIGHT, 0.5f);
summon->GetMotionMaster()->MoveRandom(10.0f);
- if (!(events.GetPhaseMask() & PHASE_MASK_FROSTMOURNE))
+ if (!events.IsInPhase(PHASE_FROSTMOURNE))
summon->m_Events.AddEvent(new VileSpiritActivateEvent(summon), summon->m_Events.CalculateTime(15000));
return;
}
@@ -853,14 +844,14 @@ class boss_the_lich_king : public CreatureScript
void UpdateAI(uint32 const diff)
{
// check phase first to prevent updating victim and entering evade mode when not wanted
- if (!(events.GetPhaseMask() & PHASE_MASK_NO_VICTIM))
+ if (!(events.IsInPhase(PHASE_OUTRO) || events.IsInPhase(PHASE_INTRO) || events.IsInPhase(PHASE_FROSTMOURNE)))
if (!UpdateVictim())
return;
events.Update(diff);
// during Remorseless Winter phases The Lich King is channeling a spell, but we must continue casting other spells
- if (me->HasUnitState(UNIT_STATE_CASTING) && !(events.GetPhaseMask() & PHASE_MASK_NO_CAST_CHECK))
+ if (me->HasUnitState(UNIT_STATE_CASTING) && !(events.IsInPhase(PHASE_TRANSITION) || events.IsInPhase(PHASE_OUTRO) || events.IsInPhase(PHASE_FROSTMOURNE)))
return;
while (uint32 eventId = events.ExecuteEvent())
@@ -916,7 +907,7 @@ class boss_the_lich_king : public CreatureScript
break;
case EVENT_INFEST:
DoCast(me, SPELL_INFEST);
- events.ScheduleEvent(EVENT_INFEST, urand(21000, 24000), 0, (events.GetPhaseMask() & PHASE_MASK_ONE) ? PHASE_ONE : PHASE_TWO);
+ events.ScheduleEvent(EVENT_INFEST, urand(21000, 24000), 0, events.IsInPhase(PHASE_ONE) ? PHASE_ONE : PHASE_TWO);
break;
case EVENT_NECROTIC_PLAGUE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, NecroticPlagueTargetCheck(me, NECROTIC_PLAGUE_LK, NECROTIC_PLAGUE_PLR)))
@@ -985,7 +976,7 @@ class boss_the_lich_king : public CreatureScript
break;
case EVENT_START_ATTACK:
me->SetReactState(REACT_AGGRESSIVE);
- if (events.GetPhaseMask() & PHASE_MASK_FROSTMOURNE)
+ if (events.IsInPhase(PHASE_FROSTMOURNE))
events.SetPhase(PHASE_THREE);
break;
case EVENT_VILE_SPIRITS:
@@ -1233,7 +1224,7 @@ class npc_tirion_fordring_tft : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if (!UpdateVictim() && !(_events.GetPhaseMask() & (PHASE_MASK_INTRO | PHASE_MASK_OUTRO)))
+ if (!UpdateVictim() && !(_events.IsInPhase(PHASE_OUTRO) || _events.IsInPhase(PHASE_INTRO)))
return;
_events.Update(diff);
@@ -2827,8 +2818,6 @@ class spell_the_lich_king_vile_spirit_damage_target_search : public SpellScriptL
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_the_lich_king_vile_spirit_damage_target_search_SpellScript::CheckTargetCount, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
-
- Unit* _target;
};
SpellScript* GetSpellScript() const
@@ -2989,6 +2978,41 @@ class spell_the_lich_king_restore_soul : public SpellScriptLoader
}
};
+class spell_the_lich_king_dark_hunger : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_dark_hunger() : SpellScriptLoader("spell_the_lich_king_dark_hunger") { }
+
+ class spell_the_lich_king_dark_hunger_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_dark_hunger_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DARK_HUNGER_HEAL))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ int32 heal = int32(eventInfo.GetDamageInfo()->GetDamage() / 2);
+ GetTarget()->CastCustomSpell(SPELL_DARK_HUNGER_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_the_lich_king_dark_hunger_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_dark_hunger_AuraScript();
+ }
+};
+
class spell_the_lich_king_in_frostmourne_room : public SpellScriptLoader
{
public:
@@ -3234,6 +3258,7 @@ void AddSC_boss_the_lich_king()
new spell_the_lich_king_lights_favor();
new spell_the_lich_king_soul_rip();
new spell_the_lich_king_restore_soul();
+ new spell_the_lich_king_dark_hunger();
new spell_the_lich_king_in_frostmourne_room();
new spell_the_lich_king_summon_spirit_bomb();
new spell_the_lich_king_trigger_vile_spirit();
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
index 12868094b2e..5621f9b7a50 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
@@ -664,10 +664,11 @@ class npc_frost_freeze_trap : public CreatureScript
public:
npc_frost_freeze_trap() : CreatureScript("npc_frost_freeze_trap") { }
- struct npc_frost_freeze_trapAI: public Scripted_NoMovementAI
+ struct npc_frost_freeze_trapAI: public ScriptedAI
{
- npc_frost_freeze_trapAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_frost_freeze_trapAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
}
void DoAction(int32 const action)
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
index 536c2af2bd8..99d3bfe59b0 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
@@ -125,11 +125,11 @@ public:
return new npc_grobbulus_poison_cloudAI(creature);
}
- struct npc_grobbulus_poison_cloudAI : public Scripted_NoMovementAI
+ struct npc_grobbulus_poison_cloudAI : public ScriptedAI
{
- npc_grobbulus_poison_cloudAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_grobbulus_poison_cloudAI(Creature* creature) : ScriptedAI(creature)
{
- Reset();
+ SetCombatMovement(false);
}
uint32 Cloud_Timer;
diff --git a/src/server/scripts/Northrend/Nexus/Nexus/boss_anomalus.cpp b/src/server/scripts/Northrend/Nexus/Nexus/boss_anomalus.cpp
index 955c6b801af..fd5841a7727 100644
--- a/src/server/scripts/Northrend/Nexus/Nexus/boss_anomalus.cpp
+++ b/src/server/scripts/Northrend/Nexus/Nexus/boss_anomalus.cpp
@@ -191,11 +191,12 @@ class mob_chaotic_rift : public CreatureScript
public:
mob_chaotic_rift() : CreatureScript("mob_chaotic_rift") { }
- struct mob_chaotic_riftAI : public Scripted_NoMovementAI
+ struct mob_chaotic_riftAI : public ScriptedAI
{
- mob_chaotic_riftAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_chaotic_riftAI(Creature* creature) : ScriptedAI(creature)
{
instance = me->GetInstanceScript();
+ SetCombatMovement(false);
}
InstanceScript* instance;
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
index 6f017884063..dc0ffcd2c63 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
@@ -198,8 +198,7 @@ public:
summoned->CastSpell(summoned, DUNGEON_MODE(SPELL_SPARK_VISUAL_TRIGGER, H_SPELL_SPARK_VISUAL_TRIGGER), true);
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
summoned->SetInCombatWith(target);
summoned->GetMotionMaster()->Clear();
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp
index 7fbf5f3ee79..8accb3182ce 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp
@@ -117,9 +117,7 @@ public:
{
if (PartingSorrowTimer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
-
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_PARTING_SORROW);
PartingSorrowTimer = urand(30000, 40000);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
index 1f96848fa0a..e5ab119e3dc 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
@@ -176,10 +176,7 @@ enum EncounterPhases
{
PHASE_NORMAL = 0,
PHASE_ROLE_PLAY = 1,
- PHASE_BIG_BANG = 2,
-
- PHASE_MASK_NO_UPDATE = (1 << PHASE_ROLE_PLAY) | (1 << PHASE_BIG_BANG),
- PHASE_MASK_NO_CAST_CHECK = 1 << PHASE_ROLE_PLAY,
+ PHASE_BIG_BANG = 2
};
enum AchievmentInfo
@@ -343,7 +340,7 @@ class boss_algalon_the_observer : public CreatureScript
DoCast(me, SPELL_RIDE_THE_LIGHTNING, true);
me->GetMotionMaster()->MovePoint(POINT_ALGALON_LAND, AlgalonLandPos);
me->SetHomePosition(AlgalonLandPos);
- Movement::MoveSplineInit init(*me);
+ Movement::MoveSplineInit init(me);
init.MoveTo(AlgalonLandPos.GetPositionX(), AlgalonLandPos.GetPositionY(), AlgalonLandPos.GetPositionZ());
init.SetOrientationFixed(true);
init.Launch();
@@ -542,12 +539,12 @@ class boss_algalon_the_observer : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if ((!(events.GetPhaseMask() & PHASE_MASK_NO_UPDATE) && !UpdateVictim()) || !CheckInRoom())
+ if ((!(events.IsInPhase(PHASE_ROLE_PLAY) || events.IsInPhase(PHASE_BIG_BANG)) && !UpdateVictim()) || !CheckInRoom())
return;
events.Update(diff);
- if (!(events.GetPhaseMask() & PHASE_MASK_NO_CAST_CHECK))
+ if (!events.IsInPhase(PHASE_ROLE_PLAY))
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
@@ -772,7 +769,7 @@ class npc_living_constellation : public CreatureScript
void UpdateAI(uint32 const diff)
{
- if (!(_events.GetPhaseMask() & PHASE_MASK_NO_UPDATE) && !UpdateVictim())
+ if (!(_events.IsInPhase(PHASE_ROLE_PLAY) || _events.IsInPhase(PHASE_BIG_BANG)) && !UpdateVictim())
return;
_events.Update(diff);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
index 4db3b58c53f..0a873bc5792 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp
@@ -216,10 +216,12 @@ class npc_iron_roots : public CreatureScript
public:
npc_iron_roots() : CreatureScript("npc_iron_roots") { }
- struct npc_iron_rootsAI : public Scripted_NoMovementAI
+ struct npc_iron_rootsAI : public ScriptedAI
{
- npc_iron_rootsAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_iron_rootsAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
+
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);
me->ApplySpellImmune(0, IMMUNITY_ID, 49560, true); // Death Grip
me->setFaction(14);
@@ -1337,10 +1339,11 @@ class npc_sun_beam : public CreatureScript
public:
npc_sun_beam() : CreatureScript("npc_sun_beam") { }
- struct npc_sun_beamAI : public Scripted_NoMovementAI
+ struct npc_sun_beamAI : public ScriptedAI
{
- npc_sun_beamAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_sun_beamAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
me->SetReactState(REACT_PASSIVE);
DoCastAOE(SPELL_FREYA_UNSTABLE_ENERGY_VISUAL, true);
DoCast(SPELL_FREYA_UNSTABLE_ENERGY);
@@ -1358,10 +1361,11 @@ class npc_healthy_spore : public CreatureScript
public:
npc_healthy_spore() : CreatureScript("npc_healthy_spore") { }
- struct npc_healthy_sporeAI : public Scripted_NoMovementAI
+ struct npc_healthy_sporeAI : public ScriptedAI
{
- npc_healthy_sporeAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_healthy_sporeAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_PASSIVE);
DoCast(me, SPELL_HEALTHY_SPORE_VISUAL);
@@ -1397,10 +1401,12 @@ class npc_eonars_gift : public CreatureScript
public:
npc_eonars_gift() : CreatureScript("npc_eonars_gift") { }
- struct npc_eonars_giftAI : public Scripted_NoMovementAI
+ struct npc_eonars_giftAI : public ScriptedAI
{
- npc_eonars_giftAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_eonars_giftAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
+
lifeBindersGiftTimer = 12000;
DoCast(me, SPELL_GROW);
DoCast(me, SPELL_PHEROMONES, true);
@@ -1435,10 +1441,12 @@ class npc_nature_bomb : public CreatureScript
public:
npc_nature_bomb() : CreatureScript("npc_nature_bomb") { }
- struct npc_nature_bombAI : public Scripted_NoMovementAI
+ struct npc_nature_bombAI : public ScriptedAI
{
- npc_nature_bombAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_nature_bombAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
+
bombTimer = urand(8000, 10000);
DoCast(SPELL_OBJECT_BOMB);
}
@@ -1475,10 +1483,12 @@ class npc_unstable_sun_beam : public CreatureScript
public:
npc_unstable_sun_beam() : CreatureScript("npc_unstable_sun_beam") { }
- struct npc_unstable_sun_beamAI : public Scripted_NoMovementAI
+ struct npc_unstable_sun_beamAI : public ScriptedAI
{
- npc_unstable_sun_beamAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_unstable_sun_beamAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
+
despawnTimer = urand(7000, 12000);
instance = me->GetInstanceScript();
DoCast(me, SPELL_PHOTOSYNTHESIS);
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
index 79edede01df..b51c6994dd5 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
@@ -739,10 +739,11 @@ class npc_mole_machine_trigger : public CreatureScript
public:
npc_mole_machine_trigger() : CreatureScript("npc_mole_machine_trigger") { }
- struct npc_mole_machine_triggerAI : public Scripted_NoMovementAI
+ struct npc_mole_machine_triggerAI : public ScriptedAI
{
- npc_mole_machine_triggerAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_mole_machine_triggerAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED);
}
@@ -818,10 +819,11 @@ class npc_devouring_flame : public CreatureScript
public:
npc_devouring_flame() : CreatureScript("npc_devouring_flame") { }
- struct npc_devouring_flameAI : public Scripted_NoMovementAI
+ struct npc_devouring_flameAI : public ScriptedAI
{
- npc_devouring_flameAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_devouring_flameAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED);
}
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
index 43d19d78f8a..1ae6a35403b 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
@@ -451,11 +451,12 @@ class mob_xt002_heart : public CreatureScript
public:
mob_xt002_heart() : CreatureScript("mob_xt002_heart") { }
- struct mob_xt002_heartAI : public Scripted_NoMovementAI
+ struct mob_xt002_heartAI : public ScriptedAI
{
- mob_xt002_heartAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ mob_xt002_heartAI(Creature* creature) : ScriptedAI(creature),
_instance(creature->GetInstanceScript())
{
+ SetCombatMovement(false);
}
void UpdateAI(uint32 const /*diff*/) { }
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
index f579fb2b93c..dbf2058546f 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
@@ -154,7 +154,7 @@ public:
Talk(YELL_DEAD_1);
}
- if (events.GetPhaseMask() & (1 << PHASE_EVENT))
+ if (events.IsInPhase(PHASE_EVENT))
damage = 0;
}
@@ -206,7 +206,7 @@ public:
void UpdateAI(const uint32 diff)
{
- if (!UpdateVictim() && !(events.GetPhaseMask() & (1 << PHASE_EVENT)))
+ if (!UpdateVictim() && !events.IsInPhase(PHASE_EVENT))
return;
events.Update(diff);
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp
index 5a38d163da3..1442dddb120 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp
@@ -459,11 +459,13 @@ public:
return new npc_ritual_channelerAI(creature);
}
- struct npc_ritual_channelerAI : public Scripted_NoMovementAI
+ struct npc_ritual_channelerAI : public ScriptedAI
{
- npc_ritual_channelerAI(Creature* creature) :Scripted_NoMovementAI(creature)
+ npc_ritual_channelerAI(Creature* creature) :ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
+
+ SetCombatMovement(false);
}
InstanceScript* instance;
@@ -554,7 +556,7 @@ class spell_paralyze_pinnacle : public SpellScriptLoader
void FilterTargets(std::list<WorldObject*>& unitList)
{
- unitList.remove_if (RitualTargetCheck(GetCaster()));
+ unitList.remove_if(RitualTargetCheck(GetCaster()));
}
void Register()
diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp
index 613b8ebf8dc..688e7d818df 100644
--- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp
+++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp
@@ -234,9 +234,9 @@ class mob_frozen_orb_stalker : public CreatureScript
public:
mob_frozen_orb_stalker() : CreatureScript("mob_frozen_orb_stalker") { }
- struct mob_frozen_orb_stalkerAI : public Scripted_NoMovementAI
+ struct mob_frozen_orb_stalkerAI : public ScriptedAI
{
- mob_frozen_orb_stalkerAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_frozen_orb_stalkerAI(Creature* creature) : ScriptedAI(creature)
{
creature->SetVisible(false);
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_DISABLE_MOVE);
@@ -244,6 +244,8 @@ class mob_frozen_orb_stalker : public CreatureScript
instance = creature->GetInstanceScript();
spawned = false;
+
+ SetCombatMovement(false);
}
void UpdateAI(const uint32 /*diff*/)
diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp
index 8e7507bce61..760156dc1c3 100644
--- a/src/server/scripts/Northrend/zone_borean_tundra.cpp
+++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp
@@ -2207,9 +2207,9 @@ class npc_warmage_coldarra : public CreatureScript
public:
npc_warmage_coldarra() : CreatureScript("npc_warmage_coldarra") { }
- struct npc_warmage_coldarraAI : public Scripted_NoMovementAI
+ struct npc_warmage_coldarraAI : public ScriptedAI
{
- npc_warmage_coldarraAI(Creature* creature) : Scripted_NoMovementAI(creature){}
+ npc_warmage_coldarraAI(Creature* creature) : ScriptedAI(creature) {}
uint32 m_uiTimer; //Timer until recast
diff --git a/src/server/scripts/Northrend/zone_crystalsong_forest.cpp b/src/server/scripts/Northrend/zone_crystalsong_forest.cpp
index d12b5176b15..963778dd802 100644
--- a/src/server/scripts/Northrend/zone_crystalsong_forest.cpp
+++ b/src/server/scripts/Northrend/zone_crystalsong_forest.cpp
@@ -49,9 +49,12 @@ class npc_warmage_violetstand : public CreatureScript
public:
npc_warmage_violetstand() : CreatureScript("npc_warmage_violetstand") { }
- struct npc_warmage_violetstandAI : public Scripted_NoMovementAI
+ struct npc_warmage_violetstandAI : public ScriptedAI
{
- npc_warmage_violetstandAI(Creature* creature) : Scripted_NoMovementAI(creature){}
+ npc_warmage_violetstandAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
uint64 uiTargetGUID;
diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp
index 6a104e28a1f..fad88d84d4f 100644
--- a/src/server/scripts/Northrend/zone_dalaran.cpp
+++ b/src/server/scripts/Northrend/zone_dalaran.cpp
@@ -55,9 +55,9 @@ class npc_mageguard_dalaran : public CreatureScript
public:
npc_mageguard_dalaran() : CreatureScript("npc_mageguard_dalaran") { }
- struct npc_mageguard_dalaranAI : public Scripted_NoMovementAI
+ struct npc_mageguard_dalaranAI : public ScriptedAI
{
- npc_mageguard_dalaranAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_mageguard_dalaranAI(Creature* creature) : ScriptedAI(creature)
{
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_NORMAL, true);
diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp
index 098fa80a3d3..70210844394 100644
--- a/src/server/scripts/Northrend/zone_icecrown.cpp
+++ b/src/server/scripts/Northrend/zone_icecrown.cpp
@@ -264,9 +264,12 @@ class npc_guardian_pavilion : public CreatureScript
public:
npc_guardian_pavilion() : CreatureScript("npc_guardian_pavilion") { }
- struct npc_guardian_pavilionAI : public Scripted_NoMovementAI
+ struct npc_guardian_pavilionAI : public ScriptedAI
{
- npc_guardian_pavilionAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ npc_guardian_pavilionAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
void MoveInLineOfSight(Unit* who)
{
@@ -370,9 +373,12 @@ class npc_tournament_training_dummy : public CreatureScript
public:
npc_tournament_training_dummy(): CreatureScript("npc_tournament_training_dummy"){}
- struct npc_tournament_training_dummyAI : Scripted_NoMovementAI
+ struct npc_tournament_training_dummyAI : ScriptedAI
{
- npc_tournament_training_dummyAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ npc_tournament_training_dummyAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
EventMap events;
bool isVulnerable;
@@ -584,13 +590,15 @@ class npc_blessed_banner : public CreatureScript
public:
npc_blessed_banner() : CreatureScript("npc_blessed_banner") { }
- struct npc_blessed_bannerAI : public Scripted_NoMovementAI
+ struct npc_blessed_bannerAI : public ScriptedAI
{
- npc_blessed_bannerAI(Creature* creature) : Scripted_NoMovementAI(creature), Summons(me)
+ npc_blessed_bannerAI(Creature* creature) : ScriptedAI(creature), Summons(me)
{
HalofSpawned = false;
PhaseCount = 0;
Summons.DespawnAll();
+
+ SetCombatMovement(false);
}
EventMap events;
diff --git a/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp b/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp
index 3a56a0bfed2..53d51e1fd6c 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp
@@ -187,9 +187,7 @@ public:
void RevertThreatOnTarget(uint64 guid)
{
- Unit* unit = NULL;
- unit = Unit::GetUnit(*me, guid);
- if (unit)
+ if (Unit* unit = Unit::GetUnit(*me, guid))
{
if (DoGetThreat(unit))
DoModifyThreatPercent(unit, -100);
@@ -279,8 +277,7 @@ public:
{
if (Phase1)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target && target->isAlive())
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
Phase1 = false;
@@ -308,7 +305,8 @@ public:
AcidGeyserTimer = 1000;
PhaseChangeTimer = 30000;
}
- } else // Encounter is a loop pretty much. Phase 1 -> Phase 2 -> Phase 1 -> Phase 2 till death or enrage
+ }
+ else // Encounter is a loop pretty much. Phase 1 -> Phase 2 -> Phase 1 -> Phase 2 till death or enrage
{
if (TargetGUID)
RevertThreatOnTarget(TargetGUID);
diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
index b8003761cc7..a91d25fa984 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
@@ -422,8 +422,7 @@ public:
Glaive->InterruptNonMeleeSpells(true);
DoCast(me, SPELL_FLAME_ENRAGE, true);
DoResetThreat();
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target && target->isAlive())
+ if (SelectTarget(SELECT_TARGET_RANDOM, 0))
{
me->AddThreat(me->getVictim(), 5000000.0f);
AttackStart(me->getVictim());
diff --git a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
index 6bc6633e49e..2fac33760e2 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
@@ -272,9 +272,12 @@ public:
return new npc_volcanoAI (creature);
}
- struct npc_volcanoAI : public Scripted_NoMovementAI
+ struct npc_volcanoAI : public ScriptedAI
{
- npc_volcanoAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ npc_volcanoAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
void Reset()
{
diff --git a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp
index 6c28b058cf7..2dc0517dc49 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp
@@ -468,8 +468,7 @@ public:
if (CrushingShadowsTimer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target && target->isAlive())
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_CRUSHING_SHADOWS);
CrushingShadowsTimer = urand(10, 26) * 1000;
} else CrushingShadowsTimer -= diff;
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp
index d9ad35f9552..5037f1c4964 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp
@@ -227,9 +227,7 @@ public:
//Only if not incombat check if the event is started
if (!me->isInCombat() && instance && instance->GetData(DATA_KARATHRESSEVENT))
{
- Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER));
-
- if (target)
+ if (Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER)))
{
AttackStart(target);
GetAdvisors();
@@ -360,12 +358,8 @@ public:
{
if (instance)
{
- Creature* Karathress = NULL;
- Karathress = (Unit::GetCreature((*me), instance->GetData64(DATA_KARATHRESS)));
-
- if (Karathress)
- if (!me->isAlive() && Karathress)
- CAST_AI(boss_fathomlord_karathress::boss_fathomlord_karathressAI, Karathress->AI())->EventSharkkisDeath();
+ if (Creature* Karathress = (Unit::GetCreature((*me), instance->GetData64(DATA_KARATHRESS))))
+ CAST_AI(boss_fathomlord_karathress::boss_fathomlord_karathressAI, Karathress->AI())->EventSharkkisDeath();
}
}
@@ -383,12 +377,8 @@ public:
//Only if not incombat check if the event is started
if (!me->isInCombat() && instance && instance->GetData(DATA_KARATHRESSEVENT))
{
- Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER));
-
- if (target)
- {
+ if (Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER)))
AttackStart(target);
- }
}
//Return since we have no target
@@ -445,12 +435,13 @@ public:
pet_id = CREATURE_FATHOM_SPOREBAT;
}
//DoCast(me, spell_id, true);
- Creature* Pet = DoSpawnCreature(pet_id, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (Pet && target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
- Pet->AI()->AttackStart(target);
- SummonedPet = Pet->GetGUID();
+ if (Creature* Pet = DoSpawnCreature(pet_id, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000))
+ {
+ Pet->AI()->AttackStart(target);
+ SummonedPet = Pet->GetGUID();
+ }
}
} else Pet_Timer -= diff;
@@ -500,12 +491,8 @@ public:
{
if (instance)
{
- Creature* Karathress = NULL;
- Karathress = (Unit::GetCreature((*me), instance->GetData64(DATA_KARATHRESS)));
-
- if (Karathress)
- if (!me->isAlive() && Karathress)
- CAST_AI(boss_fathomlord_karathress::boss_fathomlord_karathressAI, Karathress->AI())->EventTidalvessDeath();
+ if (Creature* Karathress = Unit::GetCreature((*me), instance->GetData64(DATA_KARATHRESS)))
+ CAST_AI(boss_fathomlord_karathress::boss_fathomlord_karathressAI, Karathress->AI())->EventTidalvessDeath();
}
}
@@ -524,12 +511,8 @@ public:
//Only if not incombat check if the event is started
if (!me->isInCombat() && instance && instance->GetData(DATA_KARATHRESSEVENT))
{
- Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER));
-
- if (target)
- {
+ if (Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER)))
AttackStart(target);
- }
}
//Return since we have no target
@@ -627,12 +610,8 @@ public:
{
if (instance)
{
- Creature* Karathress = NULL;
- Karathress = (Unit::GetCreature((*me), instance->GetData64(DATA_KARATHRESS)));
-
- if (Karathress)
- if (!me->isAlive() && Karathress)
- CAST_AI(boss_fathomlord_karathress::boss_fathomlord_karathressAI, Karathress->AI())->EventCaribdisDeath();
+ if (Creature* Karathress = Unit::GetCreature((*me), instance->GetData64(DATA_KARATHRESS)))
+ CAST_AI(boss_fathomlord_karathress::boss_fathomlord_karathressAI, Karathress->AI())->EventCaribdisDeath();
}
}
@@ -650,12 +629,8 @@ public:
//Only if not incombat check if the event is started
if (!me->isInCombat() && instance && instance->GetData(DATA_KARATHRESSEVENT))
{
- Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER));
-
- if (target)
- {
+ if (Unit* target = Unit::GetUnit(*me, instance->GetData64(DATA_KARATHRESSEVENT_STARTER)))
AttackStart(target);
- }
}
//Return since we have no target
@@ -697,11 +672,8 @@ public:
Cyclone->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
Cyclone->setFaction(me->getFaction());
Cyclone->CastSpell(Cyclone, SPELL_CYCLONE_CYCLONE, true);
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target)
- {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
Cyclone->AI()->AttackStart(target);
- }
}
} else Cyclone_Timer -= diff;
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_hydross_the_unstable.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_hydross_the_unstable.cpp
index b31292c4b00..ec85fe7293d 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_hydross_the_unstable.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_hydross_the_unstable.cpp
@@ -272,8 +272,7 @@ public:
//VileSludge_Timer
if (VileSludge_Timer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_VILE_SLUDGE);
VileSludge_Timer = 15000;
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp
index 788de7753ff..c9d0ab08e1c 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp
@@ -78,10 +78,11 @@ public:
return new boss_the_lurker_belowAI (creature);
}
- struct boss_the_lurker_belowAI : public Scripted_NoMovementAI
+ struct boss_the_lurker_belowAI : public ScriptedAI
{
- boss_the_lurker_belowAI(Creature* creature) : Scripted_NoMovementAI(creature), Summons(me)
+ boss_the_lurker_belowAI(Creature* creature) : ScriptedAI(creature), Summons(me)
{
+ SetCombatMovement(false);
instance = creature->GetInstanceScript();
}
@@ -152,11 +153,10 @@ public:
Summons.DespawnAll();
}
- void EnterCombat(Unit* who)
+ void EnterCombat(Unit* /*who*/)
{
if (instance)
instance->SetData(DATA_THELURKERBELOWEVENT, IN_PROGRESS);
- Scripted_NoMovementAI::EnterCombat(who);
}
void MoveInLineOfSight(Unit* who)
@@ -368,10 +368,11 @@ public:
return new mob_coilfang_ambusherAI (creature);
}
- struct mob_coilfang_ambusherAI : public Scripted_NoMovementAI
+ struct mob_coilfang_ambusherAI : public ScriptedAI
{
- mob_coilfang_ambusherAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ mob_coilfang_ambusherAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
}
uint32 MultiShotTimer;
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp
index 9476bb28f8e..06ea83881c8 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp
@@ -184,10 +184,9 @@ public:
for (uint8 i = 0; i < 10; ++i)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- Creature* Murloc = me->SummonCreature(NPC_TIDEWALKER_LURKER, MurlocCords[i][0], MurlocCords[i][1], MurlocCords[i][2], MurlocCords[i][3], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
- if (target && Murloc)
- Murloc->AI()->AttackStart(target);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ if (Creature* Murloc = me->SummonCreature(NPC_TIDEWALKER_LURKER, MurlocCords[i][0], MurlocCords[i][1], MurlocCords[i][2], MurlocCords[i][3], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000))
+ Murloc->AI()->AttackStart(target);
}
Talk(EMOTE_EARTHQUAKE);
Earthquake = false;
diff --git a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp
index 83596c46936..e2b2425dcfc 100644
--- a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp
+++ b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp
@@ -519,8 +519,7 @@ public:
//GreaterPolymorph_Timer
if (GreaterPolymorph_Timer <= diff)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_GREATER_POLYMORPH);
GreaterPolymorph_Timer = urand(15000, 20000);
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
index 01ca1636c6b..9ec6691839b 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
@@ -245,11 +245,13 @@ class boss_high_astromancer_solarian : public CreatureScript
}
else
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (!me->HasInArc(2.5f, target))
- target = me->getVictim();
- if (target)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ if (!me->HasInArc(2.5f, target))
+ target = me->getVictim();
+
DoCast(target, SPELL_ARCANE_MISSILES);
+ }
}
ArcaneMissiles_Timer = 3000;
}
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp
index 07d563cd762..c4111f59d65 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp
@@ -1404,9 +1404,12 @@ class mob_kael_flamestrike : public CreatureScript
: CreatureScript("mob_kael_flamestrike")
{
}
- struct mob_kael_flamestrikeAI : public Scripted_NoMovementAI
+ struct mob_kael_flamestrikeAI : public ScriptedAI
{
- mob_kael_flamestrikeAI(Creature* creature) : Scripted_NoMovementAI(creature) {}
+ mob_kael_flamestrikeAI(Creature* creature) : ScriptedAI(creature)
+ {
+ SetCombatMovement(false);
+ }
uint32 Timer;
bool Casting;
diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp
index 0a016f0923c..a1bfc0a090a 100644
--- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp
+++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp
@@ -661,6 +661,7 @@ class npc_karynaku : public CreatureScript
/*####
# npc_overlord_morghor
+# this whole script is wrong and needs a rewrite.even the illidan npc used is the wrong one.npc id 23467 may be the correct one
####*/
enum eOverlordData
{
@@ -766,7 +767,7 @@ public:
Player* player = Unit::GetPlayer(*me, PlayerGUID);
Creature* Illi = Creature::GetCreature(*me, IllidanGUID);
- if (!player || !Illi)
+ if (!player)
{
EnterEvadeMode();
return 0;
@@ -794,14 +795,21 @@ public:
return 2000;
break;
case 5:
- Illi->SetVisible(true);
- Illi->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ if (Illi)
+ {
+ Illi->SetVisible(true);
+ Illi->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ Illi->SetDisplayId(21526);
+ }
return 350;
break;
case 6:
- Illi->CastSpell(Illi, SPELL_ONE, true);
- Illi->SetTarget(me->GetGUID());
- me->SetTarget(IllidanGUID);
+ if (Illi)
+ {
+ Illi->CastSpell(Illi, SPELL_ONE, true);
+ Illi->SetTarget(me->GetGUID());
+ me->SetTarget(IllidanGUID);
+ }
return 2000;
break;
case 7:
@@ -810,10 +818,15 @@ public:
break;
case 8:
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 8);
- return 9000;
+ return 2500;
+ break;
+ case 9:
+ // missing text "Lord Illidan, this is the Dragonmaw that I, and others, have told you about. He will lead us to victory!"
+ return 5000;
break;
case 10:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_1);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_1);
return 5000;
break;
case 11:
@@ -821,42 +834,53 @@ public:
return 6000;
break;
case 12:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_2);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_2);
return 5500;
break;
case 13:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_3);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_3);
return 4000;
break;
case 14:
- Illi->SetTarget(PlayerGUID);
+ if (Illi)
+ Illi->SetTarget(PlayerGUID);
return 1500;
break;
case 15:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_4);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_4);
return 1500;
break;
case 16:
- Illi->CastSpell(player, SPELL_TWO, true);
+ if (Illi)
+ Illi->CastSpell(player, SPELL_TWO, true);
player->RemoveAurasDueToSpell(SPELL_THREE);
player->RemoveAurasDueToSpell(SPELL_FOUR);
return 5000;
break;
case 17:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_5);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_5);
return 5000;
break;
case 18:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_6);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_6);
return 5000;
break;
case 19:
- Illi->AI()->Talk(LORD_ILLIDAN_SAY_7);
+ if (Illi)
+ Illi->AI()->Talk(LORD_ILLIDAN_SAY_7);
return 5000;
break;
case 20:
- Illi->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
- Illi->SetDisableGravity(true);
+ if (Illi)
+ {
+ Illi->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
+ Illi->SetDisableGravity(true);
+ }
return 500;
break;
case 21:
@@ -864,8 +888,11 @@ public:
return 500;
break;
case 22:
- Illi->SetVisible(false);
- Illi->setDeathState(JUST_DIED);
+ if (Illi)
+ {
+ Illi->SetVisible(false);
+ Illi->setDeathState(JUST_DIED);
+ }
return 1000;
break;
case 23:
@@ -886,7 +913,7 @@ public:
break;
case 27:
{
- Unit* Yarzill = me->FindNearestCreature(C_YARZILL, 50);
+ Unit* Yarzill = me->FindNearestCreature(C_YARZILL, 50.0f);
if (Yarzill)
Yarzill->SetTarget(PlayerGUID);
return 500;
@@ -921,9 +948,11 @@ public:
}
break;
case 32:
- me->GetMotionMaster()->MovePoint(0, -5085.77f, 577.231f, 86.6719f); return 5000;
+ me->GetMotionMaster()->MovePoint(0, -5085.77f, 577.231f, 86.6719f);
+ return 5000;
break;
case 33:
+ me->SetTarget(0);
Reset();
return 100;
break;
@@ -940,7 +969,7 @@ public:
if (ConversationTimer <= diff)
{
- if (Event && IllidanGUID && PlayerGUID)
+ if (Event && PlayerGUID)
ConversationTimer = NextStep(++Step);
} else ConversationTimer -= diff;
}
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index 5b43a46cb1f..f5a6bb61120 100644
--- a/src/server/scripts/Spells/spell_dk.cpp
+++ b/src/server/scripts/Spells/spell_dk.cpp
@@ -31,12 +31,14 @@ enum DeathKnightSpells
SPELL_DK_ANTI_MAGIC_SHELL_TALENT = 51052,
SPELL_DK_BLACK_ICE_R1 = 49140,
SPELL_DK_BLOOD_BOIL_TRIGGERED = 65658,
+ SPELL_DK_BLOOD_GORGED_HEAL = 50454,
SPELL_DK_CORPSE_EXPLOSION_TRIGGERED = 43999,
SPELL_DK_CORPSE_EXPLOSION_VISUAL = 51270,
SPELL_DK_DEATH_COIL_DAMAGE = 47632,
SPELL_DK_DEATH_COIL_HEAL = 47633,
SPELL_DK_DEATH_STRIKE_HEAL = 45470,
SPELL_DK_GHOUL_EXPLODE = 47496,
+ SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE = 58625,
SPELL_DK_RUNIC_POWER_ENERGIZE = 49088,
SPELL_DK_SCOURGE_STRIKE_TRIGGERED = 70890,
SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189,
@@ -251,6 +253,58 @@ class spell_dk_blood_boil : public SpellScriptLoader
}
};
+// 50453 - Bloodworms Health Leech
+class spell_dk_blood_gorged : public SpellScriptLoader
+{
+ public:
+ spell_dk_blood_gorged() : SpellScriptLoader("spell_dk_blood_gorged") { }
+
+ class spell_dk_blood_gorged_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_blood_gorged_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DK_BLOOD_GORGED_HEAL))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ _procTarget = GetTarget()->GetOwner();
+ return _procTarget;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ int32 bp = int32(eventInfo.GetDamageInfo()->GetDamage() * 1.5f);
+ GetTarget()->CastCustomSpell(SPELL_DK_BLOOD_GORGED_HEAL, SPELLVALUE_BASE_POINT0, bp, _procTarget, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_dk_blood_gorged_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_dk_blood_gorged_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+
+ private:
+ Unit* _procTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dk_blood_gorged_AuraScript();
+ }
+};
+
// 49158 - Corpse Explosion (51325, 51326, 51327, 51328)
class spell_dk_corpse_explosion : public SpellScriptLoader
{
@@ -584,6 +638,55 @@ class spell_dk_ghoul_explode : public SpellScriptLoader
}
};
+// 48792 - Icebound Fortitude
+class spell_dk_icebound_fortitude : public SpellScriptLoader
+{
+ public:
+ spell_dk_icebound_fortitude() : SpellScriptLoader("spell_dk_icebound_fortitude") { }
+
+ class spell_dk_icebound_fortitude_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_icebound_fortitude_AuraScript);
+
+ bool Load()
+ {
+ Unit* caster = GetCaster();
+ return caster && caster->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ int32 value = amount;
+ uint32 defValue = uint32(caster->ToPlayer()->GetSkillValue(SKILL_DEFENSE) + caster->ToPlayer()->GetRatingBonusValue(CR_DEFENSE_SKILL));
+
+ if (defValue > 400)
+ value -= int32((defValue - 400) * 0.15);
+
+ // Glyph of Icebound Fortitude
+ if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE, EFFECT_0))
+ {
+ int32 valMax = -aurEff->GetAmount();
+ if (value > valMax)
+ value = valMax;
+ }
+ amount = value;
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dk_icebound_fortitude_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dk_icebound_fortitude_AuraScript();
+ }
+};
+
// 50365, 50371 - Improved Blood Presence
class spell_dk_improved_blood_presence : public SpellScriptLoader
{
@@ -677,6 +780,33 @@ class spell_dk_improved_unholy_presence : public SpellScriptLoader
}
};
+// 59754 Rune Tap - Party
+class spell_dk_rune_tap_party : public SpellScriptLoader
+{
+ public:
+ spell_dk_rune_tap_party() : SpellScriptLoader("spell_dk_rune_tap_party") { }
+
+ class spell_dk_rune_tap_party_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_dk_rune_tap_party_SpellScript);
+
+ void CheckTargets(std::list<WorldObject*>& targets)
+ {
+ targets.remove(GetCaster());
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_dk_rune_tap_party_SpellScript::CheckTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_PARTY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_dk_rune_tap_party_SpellScript();
+ }
+};
+
// 55090 - Scourge Strike (55265, 55270, 55271)
class spell_dk_scourge_strike : public SpellScriptLoader
{
@@ -784,6 +914,33 @@ class spell_dk_spell_deflection : public SpellScriptLoader
}
};
+// 55233 - Vampiric Blood
+class spell_dk_vampiric_blood : public SpellScriptLoader
+{
+ public:
+ spell_dk_vampiric_blood() : SpellScriptLoader("spell_dk_vampiric_blood") { }
+
+ class spell_dk_vampiric_blood_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_vampiric_blood_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ amount = GetUnitOwner()->CountPctFromMaxHealth(amount);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dk_vampiric_blood_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_INCREASE_HEALTH);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dk_vampiric_blood_AuraScript();
+ }
+};
+
// 52284 - Will of the Necropolis
class spell_dk_will_of_the_necropolis : public SpellScriptLoader
{
@@ -854,6 +1011,7 @@ void AddSC_deathknight_spell_scripts()
new spell_dk_anti_magic_shell_self();
new spell_dk_anti_magic_zone();
new spell_dk_blood_boil();
+ new spell_dk_blood_gorged();
new spell_dk_corpse_explosion();
new spell_dk_death_coil();
new spell_dk_death_gate();
@@ -861,9 +1019,12 @@ void AddSC_deathknight_spell_scripts()
new spell_dk_death_pact();
new spell_dk_death_strike();
new spell_dk_ghoul_explode();
+ new spell_dk_icebound_fortitude();
new spell_dk_improved_blood_presence();
new spell_dk_improved_unholy_presence();
+ new spell_dk_rune_tap_party();
new spell_dk_scourge_strike();
new spell_dk_spell_deflection();
+ new spell_dk_vampiric_blood();
new spell_dk_will_of_the_necropolis();
}
diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp
index b5b4937a14c..8fd44544fb8 100644
--- a/src/server/scripts/Spells/spell_druid.cpp
+++ b/src/server/scripts/Spells/spell_druid.cpp
@@ -39,10 +39,15 @@ enum DruidSpells
SPELL_DRUID_SOLAR_ECLIPSE = 48517,
SPELL_DRUID_LUNAR_ECLIPSE = 48518,
SPELL_DRUID_ENRAGE_MOD_DAMAGE = 51185,
+ SPELL_DRUID_GLYPH_OF_TYPHOON = 62135,
+ SPELL_DRUID_IDOL_OF_FERAL_SHADOWS = 34241,
+ SPELL_DRUID_IDOL_OF_WORSHIP = 60774,
SPELL_DRUID_INCREASED_MOONFIRE_DURATION = 38414,
SPELL_DRUID_KING_OF_THE_JUNGLE = 48492,
SPELL_DRUID_LIFEBLOOM_ENERGIZE = 64372,
SPELL_DRUID_LIFEBLOOM_FINAL_HEAL = 33778,
+ SPELL_DRUID_LIVING_SEED_HEAL = 48503,
+ SPELL_DRUID_LIVING_SEED_PROC = 48504,
SPELL_DRUID_NATURES_SPLENDOR = 57865,
SPELL_DRUID_SURVIVAL_INSTINCTS = 50322,
SPELL_DRUID_SAVAGE_ROAR = 62071,
@@ -160,6 +165,35 @@ class spell_dru_eclipse_energize : public SpellScriptLoader
}
};
+// -1850 - Dash
+class spell_dru_dash : public SpellScriptLoader
+{
+ public:
+ spell_dru_dash() : SpellScriptLoader("spell_dru_dash") { }
+
+ class spell_dru_dash_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_dash_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ // do not set speed if not in cat form
+ if (GetUnitOwner()->GetShapeshiftForm() != FORM_CAT)
+ amount = 0;
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_dash_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_INCREASE_SPEED);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_dash_AuraScript();
+ }
+};
+
// -5229 - Enrage
class spell_dru_enrage : public SpellScriptLoader
{
@@ -240,6 +274,69 @@ class spell_dru_glyph_of_starfire : public SpellScriptLoader
}
};
+// 34246 - Idol of the Emerald Queen
+// 60779 - Idol of Lush Moss
+class spell_dru_idol_lifebloom : public SpellScriptLoader
+{
+ public:
+ spell_dru_idol_lifebloom() : SpellScriptLoader("spell_dru_idol_lifebloom") { }
+
+ class spell_dru_idol_lifebloom_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_idol_lifebloom_AuraScript);
+
+ void HandleEffectCalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod)
+ {
+ if (!spellMod)
+ {
+ spellMod = new SpellModifier(GetAura());
+ spellMod->op = SPELLMOD_DOT;
+ spellMod->type = SPELLMOD_FLAT;
+ spellMod->spellId = GetId();
+ spellMod->mask = GetSpellInfo()->Effects[aurEff->GetEffIndex()].SpellClassMask;
+ }
+ spellMod->value = aurEff->GetAmount() / 7;
+ }
+
+ void Register()
+ {
+ DoEffectCalcSpellMod += AuraEffectCalcSpellModFn(spell_dru_idol_lifebloom_AuraScript::HandleEffectCalcSpellMod, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_idol_lifebloom_AuraScript();
+ }
+};
+
+// 29166 - Innervate
+class spell_dru_innervate : public SpellScriptLoader
+{
+ public:
+ spell_dru_innervate() : SpellScriptLoader("spell_dru_innervate") { }
+
+ class spell_dru_innervate_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_innervate_AuraScript);
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ amount = CalculatePct(int32(GetUnitOwner()->GetCreatePowers(POWER_MANA) / aurEff->GetTotalTicks()), amount);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_innervate_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_ENERGIZE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_innervate_AuraScript();
+ }
+};
+
// -5570 - Insect Swarm
class spell_dru_insect_swarm : public SpellScriptLoader
{
@@ -351,6 +448,77 @@ class spell_dru_lifebloom : public SpellScriptLoader
}
};
+// -48496 - Living Seed
+class spell_dru_living_seed : public SpellScriptLoader
+{
+ public:
+ spell_dru_living_seed() : SpellScriptLoader("spell_dru_living_seed") { }
+
+ class spell_dru_living_seed_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_living_seed_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_LIVING_SEED_PROC))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ int32 amount = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount());
+ GetTarget()->CastCustomSpell(SPELL_DRUID_LIVING_SEED_PROC, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_dru_living_seed_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_living_seed_AuraScript();
+ }
+};
+
+// 48504 - Living Seed (Proc)
+class spell_dru_living_seed_proc : public SpellScriptLoader
+{
+ public:
+ spell_dru_living_seed_proc() : SpellScriptLoader("spell_dru_living_seed_proc") { }
+
+ class spell_dru_living_seed_proc_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_living_seed_proc_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_LIVING_SEED_HEAL))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastCustomSpell(SPELL_DRUID_LIVING_SEED_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_dru_living_seed_proc_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_living_seed_proc_AuraScript();
+ }
+};
+
// 69366 - Moonkin Form passive
class spell_dru_moonkin_form_passive : public SpellScriptLoader
{
@@ -395,6 +563,33 @@ class spell_dru_moonkin_form_passive : public SpellScriptLoader
}
};
+// 48391 - Owlkin Frenzy
+class spell_dru_owlkin_frenzy : public SpellScriptLoader
+{
+ public:
+ spell_dru_owlkin_frenzy() : SpellScriptLoader("spell_dru_owlkin_frenzy") { }
+
+ class spell_dru_owlkin_frenzy_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_owlkin_frenzy_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ amount = CalculatePct(GetUnitOwner()->GetCreatePowers(POWER_MANA), amount);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_owlkin_frenzy_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_PERIODIC_ENERGIZE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_owlkin_frenzy_AuraScript();
+ }
+};
+
// -16972 - Predatory Strikes
class spell_dru_predatory_strikes : public SpellScriptLoader
{
@@ -468,6 +663,54 @@ class spell_dru_primal_tenacity : public SpellScriptLoader
}
};
+// -1079 - Rip
+class spell_dru_rip : public SpellScriptLoader
+{
+ public:
+ spell_dru_rip() : SpellScriptLoader("spell_dru_rip") { }
+
+ class spell_dru_rip_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_rip_AuraScript);
+
+ bool Load()
+ {
+ Unit* caster = GetCaster();
+ return caster && caster->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+
+ if (Unit* caster = GetCaster())
+ {
+ // 0.01 * $AP * cp
+ uint8 cp = caster->ToPlayer()->GetComboPoints();
+
+ // Idol of Feral Shadows. Can't be handled as SpellMod due its dependency from CPs
+ if (AuraEffect const* idol = caster->GetAuraEffect(SPELL_DRUID_IDOL_OF_FERAL_SHADOWS, EFFECT_0))
+ amount += cp * idol->GetAmount();
+ // Idol of Worship. Can't be handled as SpellMod due its dependency from CPs
+ else if (AuraEffect const* idol = caster->GetAuraEffect(SPELL_DRUID_IDOL_OF_WORSHIP, EFFECT_0))
+ amount += cp * idol->GetAmount();
+
+ amount += int32(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), cp));
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_rip_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_rip_AuraScript();
+ }
+};
+
// 62606 - Savage Defense
class spell_dru_savage_defense : public SpellScriptLoader
{
@@ -777,6 +1020,35 @@ class spell_dru_tiger_s_fury : public SpellScriptLoader
}
};
+// -61391 - Typhoon
+class spell_dru_typhoon : public SpellScriptLoader
+{
+ public:
+ spell_dru_typhoon() : SpellScriptLoader("spell_dru_typhoon") { }
+
+ class spell_dru_typhoon_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_dru_typhoon_SpellScript);
+
+ void HandleKnockBack(SpellEffIndex effIndex)
+ {
+ // Glyph of Typhoon
+ if (GetCaster()->HasAura(SPELL_DRUID_GLYPH_OF_TYPHOON))
+ PreventHitDefaultEffect(effIndex);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_dru_typhoon_SpellScript::HandleKnockBack, EFFECT_0, SPELL_EFFECT_KNOCK_BACK);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_dru_typhoon_SpellScript();
+ }
+};
+
// 70691 - Item T10 Restoration 4P Bonus
class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader
{
@@ -834,14 +1106,21 @@ class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader
void AddSC_druid_spell_scripts()
{
+ new spell_dru_dash();
new spell_dru_eclipse_energize();
new spell_dru_enrage();
new spell_dru_glyph_of_starfire();
+ new spell_dru_idol_lifebloom();
+ new spell_dru_innervate();
new spell_dru_insect_swarm();
new spell_dru_lifebloom();
+ new spell_dru_living_seed();
+ new spell_dru_living_seed_proc();
new spell_dru_moonkin_form_passive();
+ new spell_dru_owlkin_frenzy();
new spell_dru_predatory_strikes();
new spell_dru_primal_tenacity();
+ new spell_dru_rip();
new spell_dru_savage_defense();
new spell_dru_savage_roar();
new spell_dru_starfall_aoe();
@@ -849,5 +1128,6 @@ void AddSC_druid_spell_scripts()
new spell_dru_survival_instincts();
new spell_dru_swift_flight_passive();
new spell_dru_tiger_s_fury();
+ new spell_dru_typhoon();
new spell_dru_t10_restoration_4p_bonus();
}
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 54cb346a033..74334f192f4 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -72,6 +72,97 @@ class spell_gen_absorb0_hitlimit1 : public SpellScriptLoader
}
};
+// 28764 - Adaptive Warding (Frostfire Regalia Set)
+enum AdaptiveWarding
+{
+ SPELL_GEN_ADAPTIVE_WARDING_FIRE = 28765,
+ SPELL_GEN_ADAPTIVE_WARDING_NATURE = 28768,
+ SPELL_GEN_ADAPTIVE_WARDING_FROST = 28766,
+ SPELL_GEN_ADAPTIVE_WARDING_SHADOW = 28769,
+ SPELL_GEN_ADAPTIVE_WARDING_ARCANE = 28770
+};
+
+class spell_gen_adaptive_warding : public SpellScriptLoader
+{
+ public:
+ spell_gen_adaptive_warding() : SpellScriptLoader("spell_gen_adaptive_warding") { }
+
+ class spell_gen_adaptive_warding_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_gen_adaptive_warding_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_FIRE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_NATURE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_FROST) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_SHADOW) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_ARCANE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetDamageInfo()->GetSpellInfo()) // eventInfo.GetSpellInfo()
+ return false;
+
+ // find Mage Armor
+ if (!GetTarget()->GetAuraEffect(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000, 0x0, 0x0))
+ return false;
+
+ switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_NORMAL:
+ case SPELL_SCHOOL_HOLY:
+ return false;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ uint32 spellId = 0;
+ switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_FIRE:
+ spellId = SPELL_GEN_ADAPTIVE_WARDING_FIRE;
+ break;
+ case SPELL_SCHOOL_NATURE:
+ spellId = SPELL_GEN_ADAPTIVE_WARDING_NATURE;
+ break;
+ case SPELL_SCHOOL_FROST:
+ spellId = SPELL_GEN_ADAPTIVE_WARDING_FROST;
+ break;
+ case SPELL_SCHOOL_SHADOW:
+ spellId = SPELL_GEN_ADAPTIVE_WARDING_SHADOW;
+ break;
+ case SPELL_SCHOOL_ARCANE:
+ spellId = SPELL_GEN_ADAPTIVE_WARDING_ARCANE;
+ break;
+ default:
+ return;
+ }
+ GetTarget()->CastSpell(GetTarget(), spellId, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_gen_adaptive_warding_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_gen_adaptive_warding_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_gen_adaptive_warding_AuraScript();
+ }
+};
+
// 41337 Aura of Anger
class spell_gen_aura_of_anger : public SpellScriptLoader
{
@@ -226,6 +317,245 @@ class spell_gen_cannibalize : public SpellScriptLoader
}
};
+// 63845 - Create Lance
+enum CreateLanceSpells
+{
+ SPELL_CREATE_LANCE_ALLIANCE = 63914,
+ SPELL_CREATE_LANCE_HORDE = 63919
+};
+
+class spell_gen_create_lance : public SpellScriptLoader
+{
+ public:
+ spell_gen_create_lance() : SpellScriptLoader("spell_gen_create_lance") { }
+
+ class spell_gen_create_lance_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_create_lance_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_CREATE_LANCE_ALLIANCE) || !sSpellMgr->GetSpellInfo(SPELL_CREATE_LANCE_HORDE))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Player* target = GetHitPlayer())
+ {
+ if (target->GetTeam() == ALLIANCE)
+ GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_ALLIANCE, true);
+ else
+ GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_HORDE, true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_create_lance_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_create_lance_SpellScript();
+ }
+};
+
+// 28702 - Netherbloom
+enum Netherbloom
+{
+ SPELL_NETHERBLOOM_POLLEN_1 = 28703
+};
+
+class spell_gen_netherbloom : public SpellScriptLoader
+{
+ public:
+ spell_gen_netherbloom() : SpellScriptLoader("spell_gen_netherbloom") { }
+
+ class spell_gen_netherbloom_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_netherbloom_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ for (uint8 i = 0; i < 5; ++i)
+ if (!sSpellMgr->GetSpellInfo(SPELL_NETHERBLOOM_POLLEN_1 + i))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Unit* target = GetHitUnit())
+ {
+ // 25% chance of casting a random buff
+ if (roll_chance_i(75))
+ return;
+
+ // triggered spells are 28703 to 28707
+ // Note: some sources say, that there was the possibility of
+ // receiving a debuff. However, this seems to be removed by a patch.
+
+ // don't overwrite an existing aura
+ for (uint8 i = 0; i < 5; ++i)
+ if (target->HasAura(SPELL_NETHERBLOOM_POLLEN_1 + i))
+ return;
+
+ target->CastSpell(target, SPELL_NETHERBLOOM_POLLEN_1 + urand(0, 4), true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_netherbloom_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_netherbloom_SpellScript();
+ }
+};
+
+// 28720 - Nightmare Vine
+enum NightmareVine
+{
+ SPELL_NIGHTMARE_POLLEN = 28721
+};
+
+class spell_gen_nightmare_vine : public SpellScriptLoader
+{
+ public:
+ spell_gen_nightmare_vine() : SpellScriptLoader("spell_gen_nightmare_vine") { }
+
+ class spell_gen_nightmare_vine_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_nightmare_vine_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_NIGHTMARE_POLLEN))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Unit* target = GetHitUnit())
+ {
+ // 25% chance of casting Nightmare Pollen
+ if (roll_chance_i(25))
+ target->CastSpell(target, SPELL_NIGHTMARE_POLLEN, true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_nightmare_vine_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_nightmare_vine_SpellScript();
+ }
+};
+
+// 27539 - Obsidian Armor
+enum ObsidianArmor
+{
+ SPELL_GEN_OBSIDIAN_ARMOR_HOLY = 27536,
+ SPELL_GEN_OBSIDIAN_ARMOR_FIRE = 27533,
+ SPELL_GEN_OBSIDIAN_ARMOR_NATURE = 27538,
+ SPELL_GEN_OBSIDIAN_ARMOR_FROST = 27534,
+ SPELL_GEN_OBSIDIAN_ARMOR_SHADOW = 27535,
+ SPELL_GEN_OBSIDIAN_ARMOR_ARCANE = 27540
+};
+
+class spell_gen_obsidian_armor : public SpellScriptLoader
+{
+ public:
+ spell_gen_obsidian_armor() : SpellScriptLoader("spell_gen_obsidian_armor") { }
+
+ class spell_gen_obsidian_armor_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_gen_obsidian_armor_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_HOLY) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_FIRE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_NATURE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_FROST) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_SHADOW) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_ARCANE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetDamageInfo()->GetSpellInfo()) // eventInfo.GetSpellInfo()
+ return false;
+
+ if (GetFirstSchoolInMask(eventInfo.GetSchoolMask()) == SPELL_SCHOOL_NORMAL)
+ return false;
+
+ return true;
+ }
+
+ void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ uint32 spellId = 0;
+ switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask()))
+ {
+ case SPELL_SCHOOL_HOLY:
+ spellId = SPELL_GEN_OBSIDIAN_ARMOR_HOLY;
+ break;
+ case SPELL_SCHOOL_FIRE:
+ spellId = SPELL_GEN_OBSIDIAN_ARMOR_FIRE;
+ break;
+ case SPELL_SCHOOL_NATURE:
+ spellId = SPELL_GEN_OBSIDIAN_ARMOR_NATURE;
+ break;
+ case SPELL_SCHOOL_FROST:
+ spellId = SPELL_GEN_OBSIDIAN_ARMOR_FROST;
+ break;
+ case SPELL_SCHOOL_SHADOW:
+ spellId = SPELL_GEN_OBSIDIAN_ARMOR_SHADOW;
+ break;
+ case SPELL_SCHOOL_ARCANE:
+ spellId = SPELL_GEN_OBSIDIAN_ARMOR_ARCANE;
+ break;
+ default:
+ return;
+ }
+ GetTarget()->CastSpell(GetTarget(), spellId, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_gen_obsidian_armor_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_gen_obsidian_armor_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_gen_obsidian_armor_AuraScript();
+ }
+};
+
// 45472 Parachute
enum ParachuteSpells
{
@@ -1322,68 +1652,6 @@ class spell_gen_oracle_wolvar_reputation : public SpellScriptLoader
}
};
-class spell_gen_luck_of_the_draw : public SpellScriptLoader
-{
- public:
- spell_gen_luck_of_the_draw() : SpellScriptLoader("spell_gen_luck_of_the_draw") { }
-
- class spell_gen_luck_of_the_draw_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_gen_luck_of_the_draw_AuraScript);
-
- bool Load()
- {
- return GetUnitOwner()->GetTypeId() == TYPEID_PLAYER;
- }
-
- // cheap hax to make it have update calls
- void CalcPeriodic(AuraEffect const* /*effect*/, bool& isPeriodic, int32& amplitude)
- {
- isPeriodic = true;
- amplitude = 5 * IN_MILLISECONDS;
- }
-
- void Update(AuraEffect* /*effect*/)
- {
- if (Player* owner = GetUnitOwner()->ToPlayer())
- {
- const LfgDungeonSet dungeons = sLFGMgr->GetSelectedDungeons(owner->GetGUID());
- LfgDungeonSet::const_iterator itr = dungeons.begin();
-
- if (itr == dungeons.end())
- {
- Remove(AURA_REMOVE_BY_DEFAULT);
- return;
- }
-
-
- LFGDungeonData const* randomDungeon = sLFGMgr->GetLFGDungeon(*itr);
- if (Group* group = owner->GetGroup())
- if (Map const* map = owner->GetMap())
- if (group->isLFGGroup())
- if (uint32 dungeonId = sLFGMgr->GetDungeon(group->GetGUID(), true))
- if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId))
- if (uint32(dungeon->map) == map->GetId() && dungeon->difficulty == map->GetDifficulty())
- if (randomDungeon && randomDungeon->type == LFG_TYPE_RANDOM)
- return; // in correct dungeon
-
- Remove(AURA_REMOVE_BY_DEFAULT);
- }
- }
-
- void Register()
- {
- DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_gen_luck_of_the_draw_AuraScript::CalcPeriodic, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
- OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_luck_of_the_draw_AuraScript::Update, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
- }
- };
-
- AuraScript* GetAuraScript() const
- {
- return new spell_gen_luck_of_the_draw_AuraScript();
- }
-};
-
enum DummyTrigger
{
SPELL_PERSISTANT_SHIELD_TRIGGERED = 26470,
@@ -3163,13 +3431,172 @@ class spell_gen_replenishment : public SpellScriptLoader
}
};
+enum RunningWildMountIds
+{
+ RUNNING_WILD_MODEL_MALE = 29422,
+ RUNNING_WILD_MODEL_FEMALE = 29423,
+ SPELL_ALTERED_FORM = 97709,
+};
+
+class spell_gen_running_wild : public SpellScriptLoader
+{
+ public:
+ spell_gen_running_wild() : SpellScriptLoader("spell_gen_running_wild") { }
+
+ class spell_gen_running_wild_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_gen_running_wild_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sCreatureDisplayInfoStore.LookupEntry(RUNNING_WILD_MODEL_MALE))
+ return false;
+ if (!sCreatureDisplayInfoStore.LookupEntry(RUNNING_WILD_MODEL_FEMALE))
+ return false;
+ return true;
+ }
+
+ void HandleMount(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* target = GetTarget();
+ PreventDefaultAction();
+
+ target->Mount(target->getGender() == GENDER_FEMALE ? RUNNING_WILD_MODEL_FEMALE : RUNNING_WILD_MODEL_MALE, 0, 0);
+
+ // cast speed aura
+ if (MountCapabilityEntry const* mountCapability = sMountCapabilityStore.LookupEntry(aurEff->GetAmount()))
+ target->CastSpell(target, mountCapability->SpeedModSpell, TRIGGERED_FULL_MASK);
+ }
+
+ void Register()
+ {
+ OnEffectApply += AuraEffectApplyFn(spell_gen_running_wild_AuraScript::HandleMount, EFFECT_1, SPELL_AURA_MOUNTED, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ class spell_gen_running_wild_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_running_wild_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ALTERED_FORM))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ // Definitely not a good thing, but currently the only way to do something at cast start
+ // Should be replaced as soon as possible with a new hook: BeforeCastStart
+ GetCaster()->CastSpell(GetCaster(), SPELL_ALTERED_FORM, TRIGGERED_FULL_MASK);
+ return false;
+ }
+
+ void Register()
+ {
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_gen_running_wild_AuraScript();
+ }
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_running_wild_SpellScript();
+ }
+};
+
+class spell_gen_two_forms : public SpellScriptLoader
+{
+ public:
+ spell_gen_two_forms() : SpellScriptLoader("spell_gen_two_forms") { }
+
+ class spell_gen_two_forms_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_two_forms_SpellScript);
+
+ SpellCastResult CheckCast()
+ {
+ if (GetCaster()->isInCombat())
+ {
+ SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_CANT_TRANSFORM);
+ return SPELL_FAILED_CUSTOM_ERROR;
+ }
+
+ // Player cannot transform to human form if he is forced to be worgen for some reason (Darkflight)
+ if (GetCaster()->GetAuraEffectsByType(SPELL_AURA_WORGEN_ALTERED_FORM).size() > 1)
+ {
+ SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_CANT_TRANSFORM);
+ return SPELL_FAILED_CUSTOM_ERROR;
+ }
+
+ return SPELL_CAST_OK;
+ }
+
+ void HandleTransform(SpellEffIndex effIndex)
+ {
+ Unit* target = GetHitUnit();
+ PreventHitDefaultEffect(effIndex);
+ if (target->HasAuraType(SPELL_AURA_WORGEN_ALTERED_FORM))
+ target->RemoveAurasByType(SPELL_AURA_WORGEN_ALTERED_FORM);
+ else // Basepoints 1 for this aura control whether to trigger transform transition animation or not.
+ target->CastCustomSpell(SPELL_ALTERED_FORM, SPELLVALUE_BASE_POINT0, 1, target, TRIGGERED_FULL_MASK);
+ }
+
+ void Register()
+ {
+ OnCheckCast += SpellCheckCastFn(spell_gen_two_forms_SpellScript::CheckCast);
+ OnEffectHitTarget += SpellEffectFn(spell_gen_two_forms_SpellScript::HandleTransform, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_two_forms_SpellScript();
+ }
+};
+
+class spell_gen_darkflight : public SpellScriptLoader
+{
+ public:
+ spell_gen_darkflight() : SpellScriptLoader("spell_gen_darkflight") { }
+
+ class spell_gen_darkflight_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_darkflight_SpellScript);
+
+ void TriggerTransform()
+ {
+ GetCaster()->CastSpell(GetCaster(), SPELL_ALTERED_FORM, TRIGGERED_FULL_MASK);
+ }
+
+ void Register()
+ {
+ AfterCast += SpellCastFn(spell_gen_darkflight_SpellScript::TriggerTransform);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_darkflight_SpellScript();
+ }
+};
+
void AddSC_generic_spell_scripts()
{
new spell_gen_absorb0_hitlimit1();
+ new spell_gen_adaptive_warding();
new spell_gen_aura_of_anger();
new spell_gen_av_drekthar_presence();
new spell_gen_burn_brutallus();
new spell_gen_cannibalize();
+ new spell_gen_create_lance();
+ new spell_gen_netherbloom();
+ new spell_gen_nightmare_vine();
+ new spell_gen_obsidian_armor();
new spell_gen_parachute();
new spell_gen_pet_summoned();
new spell_gen_remove_flight_auras();
@@ -3192,7 +3619,6 @@ void AddSC_generic_spell_scripts()
new spell_gen_launch();
new spell_gen_vehicle_scaling();
new spell_gen_oracle_wolvar_reputation();
- new spell_gen_luck_of_the_draw();
new spell_gen_dummy_trigger();
new spell_gen_spirit_healer_res();
new spell_gen_gadgetzan_transporter_backfire();
@@ -3243,4 +3669,7 @@ void AddSC_generic_spell_scripts()
new spell_gen_increase_stats_buff("spell_mage_arcane_brilliance");
new spell_gen_increase_stats_buff("spell_mage_dalaran_brilliance");
new spell_gen_replenishment();
+ new spell_gen_running_wild();
+ new spell_gen_two_forms();
+ new spell_gen_darkflight();
}
diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp
index d883b4d7da7..dbfc2b44501 100644
--- a/src/server/scripts/Spells/spell_holiday.cpp
+++ b/src/server/scripts/Spells/spell_holiday.cpp
@@ -303,27 +303,10 @@ class spell_winter_veil_mistletoe : public SpellScriptLoader
void HandleScript(SpellEffIndex /*effIndex*/)
{
- Unit* caster = GetCaster();
-
if (Player* target = GetHitPlayer())
{
- uint32 spellId = 0;
- switch (urand(0, 2))
- {
- case 0:
- spellId = SPELL_CREATE_MISTLETOE;
- break;
- case 1:
- spellId = SPELL_CREATE_HOLLY;
- break;
- case 2:
- spellId = SPELL_CREATE_SNOWFLAKES;
- break;
- default:
- return;
- }
-
- caster->CastSpell(target, spellId, true);
+ uint32 spellId = RAND(SPELL_CREATE_HOLLY, SPELL_CREATE_MISTLETOE, SPELL_CREATE_SNOWFLAKES);
+ GetCaster()->CastSpell(target, spellId, true);
}
}
@@ -339,6 +322,71 @@ class spell_winter_veil_mistletoe : public SpellScriptLoader
}
};
+// 26275 - PX-238 Winter Wondervolt TRAP
+enum PX238WinterWondervolt
+{
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1 = 26157,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2 = 26272,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3 = 26273,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4 = 26274
+};
+
+class spell_winter_veil_px_238_winter_wondervolt : public SpellScriptLoader
+{
+ public:
+ spell_winter_veil_px_238_winter_wondervolt() : SpellScriptLoader("spell_winter_veil_px_238_winter_wondervolt") { }
+
+ class spell_winter_veil_px_238_winter_wondervolt_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_winter_veil_px_238_winter_wondervolt_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ uint32 const spells[4] =
+ {
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4
+ };
+
+ if (Unit* target = GetHitUnit())
+ {
+ for (uint8 i = 0; i < 4; ++i)
+ if (target->HasAura(spells[i]))
+ return;
+
+ GetCaster()->CastSpell(target, spells[urand(0, 3)], true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_winter_veil_px_238_winter_wondervolt_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+
+ private:
+
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_winter_veil_px_238_winter_wondervolt_SpellScript();
+ }
+};
+
void AddSC_holiday_spell_scripts()
{
// Love is in the Air
@@ -349,4 +397,5 @@ void AddSC_holiday_spell_scripts()
new spell_hallow_end_tricky_treat();
// Winter Veil
new spell_winter_veil_mistletoe();
+ new spell_winter_veil_px_238_winter_wondervolt();
}
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index a9b21807899..891a7daaa1f 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -33,12 +33,15 @@
enum HunterSpells
{
SPELL_HUNTER_ASPECT_OF_THE_BEAST_PET = 61669,
+ SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE = 34075,
SPELL_HUNTER_BESTIAL_WRATH = 19574,
SPELL_HUNTER_CHIMERA_SHOT_SERPENT = 53353,
SPELL_HUNTER_CHIMERA_SHOT_VIPER = 53358,
SPELL_HUNTER_CHIMERA_SHOT_SCORPID = 53359,
+ SPELL_HUNTER_GLYPH_OF_ASPECT_OF_THE_VIPER = 56851,
SPELL_HUNTER_INVIGORATION_TRIGGERED = 53398,
SPELL_HUNTER_MASTERS_CALL_TRIGGERED = 62305,
+ SPELL_HUNTER_MISDIRECTION_PROC = 35079,
SPELL_HUNTER_PET_LAST_STAND_TRIGGERED = 53479,
SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX = 55709,
SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED = 54114,
@@ -99,6 +102,50 @@ class spell_hun_aspect_of_the_beast : public SpellScriptLoader
}
};
+// 34074 - Aspect of the Viper
+class spell_hun_ascpect_of_the_viper : public SpellScriptLoader
+{
+ public:
+ spell_hun_ascpect_of_the_viper() : SpellScriptLoader("spell_hun_ascpect_of_the_viper") { }
+
+ class spell_hun_ascpect_of_the_viper_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_hun_ascpect_of_the_viper_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_GLYPH_OF_ASPECT_OF_THE_VIPER))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+
+ uint32 maxMana = GetTarget()->GetMaxPower(POWER_MANA);
+ int32 mana = CalculatePct(maxMana, GetTarget()->GetAttackTime(RANGED_ATTACK) / 1000.0f);
+
+ if (AuraEffect const* glyph = GetTarget()->GetAuraEffect(SPELL_HUNTER_GLYPH_OF_ASPECT_OF_THE_VIPER, EFFECT_0))
+ AddPct(mana, glyph->GetAmount());
+
+ GetTarget()->CastCustomSpell(SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_hun_ascpect_of_the_viper_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_OBS_MOD_POWER);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_hun_ascpect_of_the_viper_AuraScript();
+ }
+};
+
// 53209 - Chimera Shot
class spell_hun_chimera_shot : public SpellScriptLoader
{
@@ -358,16 +405,35 @@ class spell_hun_misdirection : public SpellScriptLoader
{
PrepareAuraScript(spell_hun_misdirection_AuraScript);
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_MISDIRECTION_PROC))
+ return false;
+ return true;
+ }
+
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- if (Unit* caster = GetCaster())
- if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT)
- caster->SetReducedThreatPercent(0, 0);
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT)
+ GetTarget()->ResetRedirectThreat();
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ return GetTarget()->GetRedirectThreatTarget();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_MISDIRECTION_PROC, true, NULL, aurEff);
}
void Register()
{
AfterEffectRemove += AuraEffectRemoveFn(spell_hun_misdirection_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ DoCheckProc += AuraCheckProcFn(spell_hun_misdirection_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_hun_misdirection_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY);
}
};
@@ -377,7 +443,7 @@ class spell_hun_misdirection : public SpellScriptLoader
}
};
-// 35079 - Misdirection proc
+// 35079 - Misdirection (Proc)
class spell_hun_misdirection_proc : public SpellScriptLoader
{
public:
@@ -389,8 +455,7 @@ class spell_hun_misdirection_proc : public SpellScriptLoader
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- if (GetCaster())
- GetCaster()->SetReducedThreatPercent(0, 0);
+ GetTarget()->ResetRedirectThreat();
}
void Register()
@@ -740,6 +805,7 @@ class spell_hun_target_only_pet_and_owner : public SpellScriptLoader
void AddSC_hunter_spell_scripts()
{
new spell_hun_aspect_of_the_beast();
+ new spell_hun_ascpect_of_the_viper();
new spell_hun_chimera_shot();
new spell_hun_disengage();
new spell_hun_invigoration();
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index b4e06cb6b48..b8e17f4ecca 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -73,6 +73,150 @@ class spell_item_trigger_spell : public SpellScriptLoader
}
};
+// 26400 - Arcane Shroud
+class spell_item_arcane_shroud : public SpellScriptLoader
+{
+ public:
+ spell_item_arcane_shroud() : SpellScriptLoader("spell_item_arcane_shroud") { }
+
+ class spell_item_arcane_shroud_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_arcane_shroud_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ int32 diff = GetUnitOwner()->getLevel() - 60;
+ if (diff > 0)
+ amount += 2 * diff;
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_arcane_shroud_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_arcane_shroud_AuraScript();
+ }
+};
+
+// 64411 - Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings)
+enum BlessingOfAncientKings
+{
+ SPELL_PROTECTION_OF_ANCIENT_KINGS = 64413
+};
+
+class spell_item_blessing_of_ancient_kings : public SpellScriptLoader
+{
+ public:
+ spell_item_blessing_of_ancient_kings() : SpellScriptLoader("spell_item_blessing_of_ancient_kings") { }
+
+ class spell_item_blessing_of_ancient_kings_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_blessing_of_ancient_kings_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PROTECTION_OF_ANCIENT_KINGS))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetProcTarget();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ int32 absorb = int32(CalculatePct(eventInfo.GetHealInfo()->GetHeal(), 15.0f));
+ if (AuraEffect* protEff = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PROTECTION_OF_ANCIENT_KINGS, 0, eventInfo.GetActor()->GetGUID()))
+ {
+ // The shield can grow to a maximum size of 20,000 damage absorbtion
+ protEff->SetAmount(std::min<int32>(protEff->GetAmount() + absorb, 20000));
+
+ // Refresh and return to prevent replacing the aura
+ aurEff->GetBase()->RefreshDuration();
+ }
+ else
+ GetTarget()->CastCustomSpell(SPELL_PROTECTION_OF_ANCIENT_KINGS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_item_blessing_of_ancient_kings_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_item_blessing_of_ancient_kings_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_blessing_of_ancient_kings_AuraScript();
+ }
+};
+
+// 8342 - Defibrillate (Goblin Jumper Cables) have 33% chance on success
+// 22999 - Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
+// 54732 - Defibrillate (Gnomish Army Knife) have 67% chance on success
+enum Defibrillate
+{
+ SPELL_GOBLIN_JUMPER_CABLES_FAIL = 8338,
+ SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL = 23055
+};
+
+class spell_item_defibrillate : public SpellScriptLoader
+{
+ public:
+ spell_item_defibrillate(char const* name, uint8 chance, uint32 failSpell = 0) : SpellScriptLoader(name), _chance(chance), _failSpell(failSpell) { }
+
+ class spell_item_defibrillate_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_defibrillate_SpellScript);
+
+ public:
+ spell_item_defibrillate_SpellScript(uint8 chance, uint32 failSpell) : SpellScript(), _chance(chance), _failSpell(failSpell) { }
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (_failSpell && !sSpellMgr->GetSpellInfo(_failSpell))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ if (roll_chance_i(_chance))
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (_failSpell)
+ GetCaster()->CastSpell(GetCaster(), _failSpell, true, GetCastItem());
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_defibrillate_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_RESURRECT);
+ }
+
+ private:
+ uint8 _chance;
+ uint32 _failSpell;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_item_defibrillate_SpellScript(_chance, _failSpell);
+ }
+
+ private:
+ uint8 _chance;
+ uint32 _failSpell;
+};
+
// http://www.wowhead.com/item=6522 Deviate Fish
// 8063 Deviate Fish
enum DeviateFishSpells
@@ -357,6 +501,47 @@ class spell_item_mingos_fortune_generator : public SpellScriptLoader
}
};
+// 71875, 71877 - Item - Black Bruise: Necrotic Touch Proc
+enum NecroticTouch
+{
+ SPELL_ITEM_NECROTIC_TOUCH_PROC = 71879
+};
+
+class spell_item_necrotic_touch : public SpellScriptLoader
+{
+ public:
+ spell_item_necrotic_touch() : SpellScriptLoader("spell_item_necrotic_touch") { }
+
+ class spell_item_necrotic_touch_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_necrotic_touch_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ITEM_NECROTIC_TOUCH_PROC))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ int32 bp = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount());
+ GetTarget()->CastCustomSpell(SPELL_ITEM_NECROTIC_TOUCH_PROC, SPELLVALUE_BASE_POINT0, bp, GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_item_necrotic_touch_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_necrotic_touch_AuraScript();
+ }
+};
+
// http://www.wowhead.com/item=10720 Gnomish Net-o-Matic Projector
// 13120 Net-o-Matic
enum NetOMaticSpells
@@ -464,6 +649,35 @@ class spell_item_noggenfogger_elixir : public SpellScriptLoader
}
};
+// 17512 - Piccolo of the Flaming Fire
+class spell_item_piccolo_of_the_flaming_fire : public SpellScriptLoader
+{
+ public:
+ spell_item_piccolo_of_the_flaming_fire() : SpellScriptLoader("spell_item_piccolo_of_the_flaming_fire") { }
+
+ class spell_item_piccolo_of_the_flaming_fire_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_piccolo_of_the_flaming_fire_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (Player* target = GetHitPlayer())
+ target->HandleEmoteCommand(EMOTE_STATE_DANCE);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_piccolo_of_the_flaming_fire_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_item_piccolo_of_the_flaming_fire_SpellScript();
+ }
+};
+
// http://www.wowhead.com/item=6657 Savory Deviate Delight
// 8213 Savory Deviate Delight
enum SavoryDeviateDelight
@@ -522,6 +736,262 @@ class spell_item_savory_deviate_delight : public SpellScriptLoader
}
};
+// 48129 - Scroll of Recall
+// 60320 - Scroll of Recall II
+// 60321 - Scroll of Recall III
+enum ScrollOfRecall
+{
+ SPELL_SCROLL_OF_RECALL_I = 48129,
+ SPELL_SCROLL_OF_RECALL_II = 60320,
+ SPELL_SCROLL_OF_RECALL_III = 60321,
+ SPELL_LOST = 60444,
+ SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1 = 60323,
+ SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1 = 60328,
+};
+
+class spell_item_scroll_of_recall : public SpellScriptLoader
+{
+ public:
+ spell_item_scroll_of_recall() : SpellScriptLoader("spell_item_scroll_of_recall") { }
+
+ class spell_item_scroll_of_recall_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_scroll_of_recall_SpellScript);
+
+ bool Load()
+ {
+ return GetCaster()->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ Unit* caster = GetCaster();
+ uint8 maxSafeLevel = 0;
+ switch (GetSpellInfo()->Id)
+ {
+ case SPELL_SCROLL_OF_RECALL_I: // Scroll of Recall
+ maxSafeLevel = 40;
+ break;
+ case SPELL_SCROLL_OF_RECALL_II: // Scroll of Recall II
+ maxSafeLevel = 70;
+ break;
+ case SPELL_SCROLL_OF_RECALL_III: // Scroll of Recal III
+ maxSafeLevel = 80;
+ break;
+ default:
+ break;
+ }
+
+ if (caster->getLevel() > maxSafeLevel)
+ {
+ caster->CastSpell(caster, SPELL_LOST, true);
+
+ // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335
+ uint32 spellId = SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1;
+ if (GetCaster()->ToPlayer()->GetTeam() == HORDE)
+ spellId = SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1;
+
+ GetCaster()->CastSpell(GetCaster(), spellId + urand(0, 7), true);
+
+ PreventHitDefaultEffect(effIndex);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_scroll_of_recall_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_item_scroll_of_recall_SpellScript();
+ }
+};
+
+// 71169 - Shadow's Fate (Shadowmourne questline)
+enum ShadowsFate
+{
+ SPELL_SOUL_FEAST = 71203,
+ QUEST_A_FEAST_OF_SOULS = 24547
+};
+
+class spell_item_shadows_fate : public SpellScriptLoader
+{
+ public:
+ spell_item_shadows_fate() : SpellScriptLoader("spell_item_shadows_fate") { }
+
+ class spell_item_shadows_fate_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_shadows_fate_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_FEAST))
+ return false;
+ if (!sObjectMgr->GetQuestTemplate(QUEST_A_FEAST_OF_SOULS))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ _procTarget = GetCaster();
+ return _procTarget && _procTarget->GetTypeId() == TYPEID_PLAYER && _procTarget->ToPlayer()->GetQuestStatus(QUEST_A_FEAST_OF_SOULS) == QUEST_STATUS_INCOMPLETE;
+ }
+
+ void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(_procTarget, SPELL_SOUL_FEAST, true);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_item_shadows_fate_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_item_shadows_fate_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+
+ private:
+ Unit* _procTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_shadows_fate_AuraScript();
+ }
+};
+
+enum Shadowmourne
+{
+ SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE = 71904,
+ SPELL_SHADOWMOURNE_SOUL_FRAGMENT = 71905,
+ SPELL_SHADOWMOURNE_VISUAL_LOW = 72521,
+ SPELL_SHADOWMOURNE_VISUAL_HIGH = 72523,
+ SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF = 73422,
+};
+
+// 71903 - Item - Shadowmourne Legendary
+class spell_item_shadowmourne : public SpellScriptLoader
+{
+ public:
+ spell_item_shadowmourne() : SpellScriptLoader("spell_item_shadowmourne") { }
+
+ class spell_item_shadowmourne_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_shadowmourne_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_SOUL_FRAGMENT))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (GetTarget()->HasAura(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) // cant collect shards while under effect of Chaos Bane buff
+ return false;
+ return eventInfo.GetProcTarget() && eventInfo.GetProcTarget()->isAlive();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(GetTarget(), SPELL_SHADOWMOURNE_SOUL_FRAGMENT, true, NULL, aurEff);
+
+ // this can't be handled in AuraScript of SoulFragments because we need to know victim
+ if (Aura* soulFragments = GetTarget()->GetAura(SPELL_SHADOWMOURNE_SOUL_FRAGMENT))
+ {
+ if (soulFragments->GetStackAmount() >= 10)
+ {
+ GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE, true, NULL, aurEff);
+ soulFragments->Remove();
+ }
+ }
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_item_shadowmourne_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_item_shadowmourne_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_shadowmourne_AuraScript();
+ }
+};
+
+// 71905 - Soul Fragment
+class spell_item_shadowmourne_soul_fragment : public SpellScriptLoader
+{
+ public:
+ spell_item_shadowmourne_soul_fragment() : SpellScriptLoader("spell_item_shadowmourne_soul_fragment") { }
+
+ class spell_item_shadowmourne_soul_fragment_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_shadowmourne_soul_fragment_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_LOW) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_HIGH) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF))
+ return false;
+ return true;
+ }
+
+ void OnStackChange(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* target = GetTarget();
+ switch (GetStackAmount())
+ {
+ case 1:
+ target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_LOW, true);
+ break;
+ case 6:
+ target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW);
+ target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_HIGH, true);
+ break;
+ case 10:
+ target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH);
+ target->CastSpell(target, SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF, true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* target = GetTarget();
+ target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW);
+ target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH);
+ }
+
+ void Register()
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_item_shadowmourne_soul_fragment_AuraScript::OnStackChange, EFFECT_0, SPELL_AURA_MOD_STAT, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAL | AURA_EFFECT_HANDLE_REAPPLY));
+ AfterEffectRemove += AuraEffectRemoveFn(spell_item_shadowmourne_soul_fragment_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_shadowmourne_soul_fragment_AuraScript();
+ }
+};
+
// http://www.wowhead.com/item=7734 Six Demon Bag
// 14537 Six Demon Bag
enum SixDemonBagSpells
@@ -593,6 +1063,35 @@ class spell_item_six_demon_bag : public SpellScriptLoader
}
};
+// 28862 - The Eye of Diminution
+class spell_item_the_eye_of_diminution : public SpellScriptLoader
+{
+ public:
+ spell_item_the_eye_of_diminution() : SpellScriptLoader("spell_item_the_eye_of_diminution") { }
+
+ class spell_item_the_eye_of_diminution_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_the_eye_of_diminution_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ int32 diff = GetUnitOwner()->getLevel() - 60;
+ if (diff > 0)
+ amount += diff;
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_the_eye_of_diminution_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_the_eye_of_diminution_AuraScript();
+ }
+};
+
// http://www.wowhead.com/item=44012 Underbelly Elixir
// 59640 Underbelly Elixir
enum UnderbellyElixirSpells
@@ -646,70 +1145,6 @@ class spell_item_underbelly_elixir : public SpellScriptLoader
}
};
-enum eShadowmourneVisuals
-{
- SPELL_SHADOWMOURNE_VISUAL_LOW = 72521,
- SPELL_SHADOWMOURNE_VISUAL_HIGH = 72523,
- SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF = 73422,
-};
-
-class spell_item_shadowmourne : public SpellScriptLoader
-{
-public:
- spell_item_shadowmourne() : SpellScriptLoader("spell_item_shadowmourne") { }
-
- class spell_item_shadowmourne_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_item_shadowmourne_AuraScript);
-
- bool Validate(SpellInfo const* /*spellEntry*/)
- {
- if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_LOW) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_HIGH) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF))
- return false;
- return true;
- }
-
- void OnStackChange(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- Unit* target = GetTarget();
- switch (GetStackAmount())
- {
- case 1:
- target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_LOW, true);
- break;
- case 6:
- target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW);
- target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_HIGH, true);
- break;
- case 10:
- target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH);
- target->CastSpell(target, SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF, true);
- break;
- default:
- break;
- }
- }
-
- void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- Unit* target = GetTarget();
- target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW);
- target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH);
- }
-
- void Register()
- {
- AfterEffectApply += AuraEffectApplyFn(spell_item_shadowmourne_AuraScript::OnStackChange, EFFECT_0, SPELL_AURA_MOD_STAT, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAL | AURA_EFFECT_HANDLE_REAPPLY));
- AfterEffectRemove += AuraEffectRemoveFn(spell_item_shadowmourne_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL);
- }
- };
-
- AuraScript* GetAuraScript() const
- {
- return new spell_item_shadowmourne_AuraScript();
- }
-};
-
enum AirRifleSpells
{
SPELL_AIR_RIFLE_HOLD_VISUAL = 65582,
@@ -2030,17 +2465,28 @@ void AddSC_item_spell_scripts()
// 23075 Mithril Mechanical Dragonling
new spell_item_trigger_spell("spell_item_mithril_mechanical_dragonling", SPELL_MITHRIL_MECHANICAL_DRAGONLING);
+ new spell_item_arcane_shroud();
+ new spell_item_blessing_of_ancient_kings();
+ new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL);
+ new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL);
+ new spell_item_defibrillate("spell_item_gnomish_army_knife", 33);
new spell_item_deviate_fish();
new spell_item_flask_of_the_north();
new spell_item_gnomish_death_ray();
new spell_item_make_a_wish();
new spell_item_mingos_fortune_generator();
+ new spell_item_necrotic_touch();
new spell_item_net_o_matic();
new spell_item_noggenfogger_elixir();
+ new spell_item_piccolo_of_the_flaming_fire();
new spell_item_savory_deviate_delight();
+ new spell_item_scroll_of_recall();
+ new spell_item_shadows_fate();
+ new spell_item_shadowmourne();
+ new spell_item_shadowmourne_soul_fragment();
new spell_item_six_demon_bag();
+ new spell_item_the_eye_of_diminution();
new spell_item_underbelly_elixir();
- new spell_item_shadowmourne();
new spell_item_red_rider_air_rifle();
new spell_item_create_heart_candy();
diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp
index 8c33117bb96..cd3673c17de 100644
--- a/src/server/scripts/Spells/spell_mage.cpp
+++ b/src/server/scripts/Spells/spell_mage.cpp
@@ -25,45 +25,47 @@
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
+#include "Pet.h"
enum MageSpells
{
+ SPELL_MAGE_BURNOUT = 29077,
+ SPELL_MAGE_COLD_SNAP = 11958,
+ SPELL_MAGE_FOCUS_MAGIC_PROC = 54648,
+ SPELL_MAGE_FROST_WARDING_R1 = 11189,
+ SPELL_MAGE_FROST_WARDING_TRIGGERED = 57776,
+ SPELL_MAGE_INCANTERS_ABSORBTION_R1 = 44394,
+ SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED = 44413,
+ SPELL_MAGE_IGNITE = 12654,
+ SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE = 29077,
+ SPELL_MAGE_SQUIRREL_FORM = 32813,
+ SPELL_MAGE_GIRAFFE_FORM = 32816,
+ SPELL_MAGE_SERPENT_FORM = 32817,
+ SPELL_MAGE_DRAGONHAWK_FORM = 32818,
+ SPELL_MAGE_WORGEN_FORM = 32819,
+ SPELL_MAGE_SHEEP_FORM = 32820,
+ SPELL_MAGE_GLYPH_OF_ETERNAL_WATER = 70937,
+ SPELL_MAGE_SUMMON_WATER_ELEMENTAL_PERMANENT = 70908,
+ SPELL_MAGE_SUMMON_WATER_ELEMENTAL_TEMPORARY = 70907,
+ SPELL_MAGE_GLYPH_OF_BLAST_WAVE = 62126,
+
SPELL_MAGE_FLAMESTRIKE = 2120,
SPELL_MAGE_CHILLED_R1 = 12484,
SPELL_MAGE_CHILLED_R2 = 12485,
- SPELL_MAGE_COLD_SNAP = 11958,
-
SPELL_MAGE_CONE_OF_COLD_AURA_R1 = 11190,
SPELL_MAGE_CONE_OF_COLD_AURA_R2 = 12489,
SPELL_MAGE_CONE_OF_COLD_TRIGGER_R1 = 83301,
SPELL_MAGE_CONE_OF_COLD_TRIGGER_R2 = 83302,
- SPELL_MAGE_FROST_WARDING_R1 = 28332,
- SPELL_MAGE_FROST_WARDING_TRIGGERED = 57776,
-
SPELL_MAGE_SHATTERED_BARRIER_R1 = 44745,
SPELL_MAGE_SHATTERED_BARRIER_R2 = 54787,
SPELL_MAGE_SHATTERED_BARRIER_FREEZE_R1 = 55080,
SPELL_MAGE_SHATTERED_BARRIER_FREEZE_R2 = 83073,
- SPELL_MAGE_INCANTER_S_ABSORPTION_TRIGGERED = 44413,
- SPELL_MAGE_INCANTER_S_ABSORPTION_KNOCKBACK = 86261,
-
- SPELL_MAGE_SQUIRREL_FORM = 32813,
- SPELL_MAGE_GIRAFFE_FORM = 32816,
- SPELL_MAGE_SERPENT_FORM = 32817,
- SPELL_MAGE_DRAGONHAWK_FORM = 32818,
- SPELL_MAGE_WORGEN_FORM = 32819,
- SPELL_MAGE_SHEEP_FORM = 32820,
-
SPELL_MAGE_IMPROVED_MANA_GEM_TRIGGERED = 83098,
- SPELL_MAGE_GLYPH_OF_ETERNAL_WATER = 70937,
- SPELL_MAGE_SUMMON_WATER_ELEMENTAL_PERMANENT = 70908,
- SPELL_MAGE_SUMMON_WATER_ELEMENTAL_TEMPORARY = 70907,
-
SPELL_MAGE_FINGERS_OF_FROST = 44544
};
@@ -76,6 +78,31 @@ enum MageIcons
ICON_MAGE_IMPROVED_MANA_GEM = 1036
};
+// Incanter's Absorbtion
+class spell_mage_incanters_absorbtion_base_AuraScript : public AuraScript
+{
+ public:
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_R1))
+ return false;
+ return true;
+ }
+
+ void Trigger(AuraEffect* aurEff, DamageInfo& /*dmgInfo*/, uint32& absorbAmount)
+ {
+ Unit* target = GetTarget();
+
+ if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_INCANTERS_ABSORBTION_R1, EFFECT_0))
+ {
+ int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount());
+ target->CastCustomSpell(target, SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
+ }
+ }
+};
+
// 11113 - Blast Wave
class spell_mage_blast_wave : public SpellScriptLoader
{
@@ -130,6 +157,51 @@ class spell_mage_blast_wave : public SpellScriptLoader
}
};
+// -44449 - Burnout
+class spell_mage_burnout : public SpellScriptLoader
+{
+ public:
+ spell_mage_burnout() : SpellScriptLoader("spell_mage_burnout") { }
+
+ class spell_mage_burnout_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_mage_burnout_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_BURNOUT))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetDamageInfo()->GetSpellInfo(); // eventInfo.GetSpellInfo()
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ int32 mana = int32(eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask()));
+ mana = CalculatePct(mana, aurEff->GetAmount());
+
+ GetTarget()->CastCustomSpell(SPELL_MAGE_BURNOUT, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_mage_burnout_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_mage_burnout_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_mage_burnout_AuraScript();
+ }
+};
+
// 42208 - Blizzard
/// Updated 4.3.4
class spell_mage_blizzard : public SpellScriptLoader
@@ -327,29 +399,47 @@ class spell_mage_conjure_refreshment : public SpellScriptLoader
}
};
-// -6143, -543 - Frost Warding
-class spell_mage_frost_warding_trigger : public SpellScriptLoader
+// -543 - Fire Ward
+// -6143 - Frost Ward
+class spell_mage_fire_frost_ward : public SpellScriptLoader
{
public:
- spell_mage_frost_warding_trigger() : SpellScriptLoader("spell_mage_frost_warding_trigger") { }
+ spell_mage_fire_frost_ward() : SpellScriptLoader("spell_mage_fire_frost_ward") { }
- class spell_mage_frost_warding_trigger_AuraScript : public AuraScript
+ class spell_mage_fire_frost_ward_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript
{
- PrepareAuraScript(spell_mage_frost_warding_trigger_AuraScript);
+ PrepareAuraScript(spell_mage_fire_frost_ward_AuraScript);
bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_TRIGGERED) || !sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_R1))
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_TRIGGERED))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_R1))
return false;
return true;
}
- void Absorb(AuraEffect* aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount)
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+ }
+ }
+
+ void Absorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
{
Unit* target = GetTarget();
if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_FROST_WARDING_R1, EFFECT_0))
{
- int32 chance = talentAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue();
+ int32 chance = talentAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // SPELL_EFFECT_DUMMY with NO_TARGET
if (roll_chance_i(chance))
{
@@ -364,13 +454,66 @@ class spell_mage_frost_warding_trigger : public SpellScriptLoader
void Register()
{
- OnEffectAbsorb += AuraEffectAbsorbFn(spell_mage_frost_warding_trigger_AuraScript::Absorb, EFFECT_0);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_fire_frost_ward_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ OnEffectAbsorb += AuraEffectAbsorbFn(spell_mage_fire_frost_ward_AuraScript::Absorb, EFFECT_0);
+ AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_fire_frost_ward_AuraScript::Trigger, EFFECT_0);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_mage_fire_frost_ward_AuraScript();
+ }
+};
+
+// 54646 - Focus Magic
+class spell_mage_focus_magic : public SpellScriptLoader
+{
+ public:
+ spell_mage_focus_magic() : SpellScriptLoader("spell_mage_focus_magic") { }
+
+ class spell_mage_focus_magic_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_mage_focus_magic_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FOCUS_MAGIC_PROC))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ _procTarget = GetCaster();
+ return _procTarget && _procTarget->isAlive();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(_procTarget, SPELL_MAGE_FOCUS_MAGIC_PROC, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_mage_focus_magic_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_mage_focus_magic_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
}
+
+ private:
+ Unit* _procTarget;
};
AuraScript* GetAuraScript() const
{
- return new spell_mage_frost_warding_trigger_AuraScript();
+ return new spell_mage_focus_magic_AuraScript();
}
};
@@ -483,77 +626,170 @@ class spell_mage_ice_barrier : public SpellScriptLoader
}
};
-// 1463 - Mana Shield
-/// Updated 4.3.4
-class spell_mage_mana_shield : public SpellScriptLoader
+// -11119 - Ignite
+class spell_mage_ignite : public SpellScriptLoader
{
public:
- spell_mage_mana_shield() : SpellScriptLoader("spell_mage_mana_shield") { }
+ spell_mage_ignite() : SpellScriptLoader("spell_mage_ignite") { }
- class spell_mage_mana_shield_AuraScript : public AuraScript
+ class spell_mage_ignite_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_mage_ignite_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_IGNITE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetProcTarget();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ SpellInfo const* igniteDot = sSpellMgr->GetSpellInfo(SPELL_MAGE_IGNITE);
+ int32 pct = 8 * GetSpellInfo()->GetRank();
+
+ int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks());
+ amount += eventInfo.GetProcTarget()->GetRemainingPeriodicAmount(eventInfo.GetActor()->GetGUID(), SPELL_MAGE_IGNITE, SPELL_AURA_PERIODIC_DAMAGE);
+ GetTarget()->CastCustomSpell(SPELL_MAGE_IGNITE, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_mage_ignite_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_mage_ignite_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_mage_ignite_AuraScript();
+ }
+};
+
+// 543 - Mage Ward
+/// Updated 4.3.4
+class spell_mage_mage_ward : public SpellScriptLoader
+{
+ public:
+ spell_mage_mage_ward() : SpellScriptLoader("spell_mage_mage_ward") { }
+
+ class spell_mage_mage_ward_AuraScript : public AuraScript
{
- PrepareAuraScript(spell_mage_mana_shield_AuraScript);
+ PrepareAuraScript(spell_mage_mage_ward_AuraScript);
void HandleAbsorb(AuraEffect* /*aurEff*/, DamageInfo & /*dmgInfo*/, uint32 & absorbAmount)
{
if (AuraEffect* aurEff = GetTarget()->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_GENERIC, ICON_MAGE_INCANTER_S_ABSORPTION, EFFECT_0))
{
int32 bp = CalculatePct(absorbAmount, aurEff->GetAmount());
- GetTarget()->CastCustomSpell(GetTarget(), SPELL_MAGE_INCANTER_S_ABSORPTION_TRIGGERED, &bp, NULL, NULL, true);
+ GetTarget()->CastCustomSpell(GetTarget(), SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true);
}
}
- void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL)
- GetTarget()->CastSpell(GetTarget(), SPELL_MAGE_INCANTER_S_ABSORPTION_KNOCKBACK, true);
- }
-
void Register()
{
- AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_mana_shield_AuraScript::HandleAbsorb, EFFECT_0);
- AfterEffectRemove += AuraEffectRemoveFn(spell_mage_mana_shield_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_MANA_SHIELD, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_mage_ward_AuraScript::HandleAbsorb, EFFECT_0);
}
};
AuraScript* GetAuraScript() const
{
- return new spell_mage_mana_shield_AuraScript();
+ return new spell_mage_mage_ward_AuraScript();
}
};
-// 543 - Mage Ward
+// 1463 - Mana Shield
/// Updated 4.3.4
-class spell_mage_mage_ward : public SpellScriptLoader
+class spell_mage_mana_shield : public SpellScriptLoader
{
- public:
- spell_mage_mage_ward() : SpellScriptLoader("spell_mage_mage_ward") { }
+ public:
+ spell_mage_mana_shield() : SpellScriptLoader("spell_mage_mana_shield") { }
- class spell_mage_mage_ward_AuraScript : public AuraScript
+ class spell_mage_mana_shield_AuraScript : public AuraScript
{
- PrepareAuraScript(spell_mage_mage_ward_AuraScript);
+ PrepareAuraScript(spell_mage_mana_shield_AuraScript);
void HandleAbsorb(AuraEffect* /*aurEff*/, DamageInfo & /*dmgInfo*/, uint32 & absorbAmount)
{
if (AuraEffect* aurEff = GetTarget()->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_GENERIC, ICON_MAGE_INCANTER_S_ABSORPTION, EFFECT_0))
{
int32 bp = CalculatePct(absorbAmount, aurEff->GetAmount());
- GetTarget()->CastCustomSpell(GetTarget(), SPELL_MAGE_INCANTER_S_ABSORPTION_TRIGGERED, &bp, NULL, NULL, true);
+ GetTarget()->CastCustomSpell(GetTarget(), SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true);
}
}
+ void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL)
+ GetTarget()->CastSpell(GetTarget(), SPELL_MAGE_INCANTERS_ABSORBTION_R1, true);
+ }
+
void Register()
{
- AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_mage_ward_AuraScript::HandleAbsorb, EFFECT_0);
+ AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_mana_shield_AuraScript::HandleAbsorb, EFFECT_0);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_mage_mana_shield_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_MANA_SHIELD, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const
{
- return new spell_mage_mage_ward_AuraScript();
+ return new spell_mage_mana_shield_AuraScript();
}
};
+// -29074 - Master of Elements
+class spell_mage_master_of_elements : public SpellScriptLoader
+{
+ public:
+ spell_mage_master_of_elements() : SpellScriptLoader("spell_mage_master_of_elements") { }
+
+ class spell_mage_master_of_elements_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_mage_master_of_elements_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetDamageInfo()->GetSpellInfo(); // eventInfo.GetSpellInfo()
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ int32 mana = int32(eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask()));
+ mana = CalculatePct(mana, aurEff->GetAmount());
+
+ if (mana > 0)
+ GetTarget()->CastCustomSpell(SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_mage_master_of_elements_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_mage_master_of_elements_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_mage_master_of_elements_AuraScript();
+ }
+};
+
enum SilvermoonPolymorph
{
NPC_AUROSALIA = 18744
@@ -669,6 +905,14 @@ class spell_mage_summon_water_elemental : public SpellScriptLoader
void HandleDummy(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
+
+ if (Player* player = caster->ToPlayer())
+ if (Guardian* elemental = player->GetGuardianPet())
+ // Check if the pet we are going to unsummon is the mage's water elemental
+ if (elemental->GetEntry() == uint32(sSpellMgr->GetSpellInfo(SPELL_MAGE_SUMMON_WATER_ELEMENTAL_TEMPORARY)->Effects[EFFECT_0].MiscValue) ||
+ elemental->GetEntry() == uint32(sSpellMgr->GetSpellInfo(SPELL_MAGE_SUMMON_WATER_ELEMENTAL_PERMANENT)->Effects[EFFECT_0].MiscValue))
+ elemental->UnSummon();
+
// Glyph of Eternal Water
if (caster->HasAura(SPELL_MAGE_GLYPH_OF_ETERNAL_WATER))
caster->CastSpell(caster, SPELL_MAGE_SUMMON_WATER_ELEMENTAL_PERMANENT, true);
@@ -748,15 +992,19 @@ void AddSC_mage_spell_scripts()
{
new spell_mage_blast_wave();
new spell_mage_blizzard();
+ new spell_mage_burnout();
new spell_mage_cold_snap();
new spell_mage_cone_of_cold();
new spell_mage_conjure_refreshment();
- new spell_mage_frost_warding_trigger();
+ new spell_mage_fire_frost_ward();
+ new spell_mage_focus_magic();
new spell_mage_frostbolt();
- new spell_mage_living_bomb();
new spell_mage_ice_barrier();
- new spell_mage_mana_shield();
+ new spell_mage_ignite();
+ new spell_mage_living_bomb();
new spell_mage_mage_ward();
+ new spell_mage_mana_shield();
+ new spell_mage_master_of_elements();
new spell_mage_polymorph_cast_visual();
new spell_mage_replenish_mana();
new spell_mage_summon_water_elemental();
diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp
index 47592630a11..ac9b861f584 100644
--- a/src/server/scripts/Spells/spell_paladin.cpp
+++ b/src/server/scripts/Spells/spell_paladin.cpp
@@ -31,6 +31,7 @@ enum PaladinSpells
{
SPELL_PALADIN_DIVINE_PLEA = 54428,
SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF = 67480,
+ SPELL_PALADIN_BLESSING_OF_SANCTUARY_ENERGIZE = 57319,
SPELL_PALADIN_HOLY_SHOCK_R1 = 20473,
SPELL_PALADIN_HOLY_SHOCK_R1_DAMAGE = 25912,
@@ -45,12 +46,25 @@ enum PaladinSpells
SPELL_PALADIN_DIVINE_STORM_DUMMY = 54171,
SPELL_PALADIN_DIVINE_STORM_HEAL = 54172,
+ SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE = 25997,
+
SPELL_PALADIN_FORBEARANCE = 25771,
SPELL_PALADIN_AVENGING_WRATH_MARKER = 61987,
SPELL_PALADIN_IMMUNE_SHIELD_MARKER = 61988,
SPELL_PALADIN_HAND_OF_SACRIFICE = 6940,
- SPELL_PALADIN_DIVINE_SACRIFICE = 64205
+ SPELL_PALADIN_DIVINE_SACRIFICE = 64205,
+
+ SPELL_PALADIN_DIVINE_PURPOSE_PROC = 90174,
+
+ SPELL_PALADIN_GLYPH_OF_SALVATION = 63225,
+
+ SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT = 31790,
+
+ SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS = 25742,
+
+ SPELL_GENERIC_ARENA_DAMPENING = 74410,
+ SPELL_GENERIC_BATTLEGROUND_DAMPENING = 74411
};
// 31850 - Ardent Defender
@@ -185,8 +199,8 @@ class spell_pal_blessing_of_faith : public SpellScriptLoader
}
};
-// 20911 Blessing of Sanctuary
-// 25899 Greater Blessing of Sanctuary
+// 20911 - Blessing of Sanctuary
+// 25899 - Greater Blessing of Sanctuary
class spell_pal_blessing_of_sanctuary : public SpellScriptLoader
{
public:
@@ -200,6 +214,8 @@ class spell_pal_blessing_of_sanctuary : public SpellScriptLoader
{
if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF))
return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_BLESSING_OF_SANCTUARY_ENERGIZE))
+ return false;
return true;
}
@@ -216,10 +232,23 @@ class spell_pal_blessing_of_sanctuary : public SpellScriptLoader
target->RemoveAura(SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF, GetCasterGUID());
}
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ return GetTarget()->getPowerType() == POWER_MANA;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(GetTarget(), SPELL_PALADIN_BLESSING_OF_SANCTUARY_ENERGIZE, true, NULL, aurEff);
+ }
+
void Register()
{
AfterEffectApply += AuraEffectApplyFn(spell_pal_blessing_of_sanctuary_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
AfterEffectRemove += AuraEffectRemoveFn(spell_pal_blessing_of_sanctuary_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ DoCheckProc += AuraCheckProcFn(spell_pal_blessing_of_sanctuary_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_pal_blessing_of_sanctuary_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
}
};
@@ -410,6 +439,43 @@ class spell_pal_exorcism_and_holy_wrath_damage : public SpellScriptLoader
}
};
+// -9799 - Eye for an Eye
+class spell_pal_eye_for_an_eye : public SpellScriptLoader
+{
+ public:
+ spell_pal_eye_for_an_eye() : SpellScriptLoader("spell_pal_eye_for_an_eye") { }
+
+ class spell_pal_eye_for_an_eye_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pal_eye_for_an_eye_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE))
+ return false;
+ return true;
+ }
+
+ void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ // return damage % to attacker but < 50% own total health
+ int32 damage = int32(std::min(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()), GetTarget()->GetMaxHealth() / 2));
+ GetTarget()->CastCustomSpell(SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_pal_eye_for_an_eye_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pal_eye_for_an_eye_AuraScript();
+ }
+};
+
// 63521 - Guarded by The Light
class spell_pal_guarded_by_the_light : public SpellScriptLoader
{
@@ -490,6 +556,39 @@ class spell_pal_hand_of_sacrifice : public SpellScriptLoader
}
};
+// 1038 - Hand of Salvation
+class spell_pal_hand_of_salvation : public SpellScriptLoader
+{
+ public:
+ spell_pal_hand_of_salvation() : SpellScriptLoader("spell_pal_hand_of_salvation") { }
+
+ class spell_pal_hand_of_salvation_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pal_hand_of_salvation_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ // Glyph of Salvation
+ if (caster->GetGUID() == GetUnitOwner()->GetGUID())
+ if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_PALADIN_GLYPH_OF_SALVATION, EFFECT_0))
+ amount -= aurEff->GetAmount();
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pal_hand_of_salvation_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pal_hand_of_salvation_AuraScript();
+ }
+};
+
// -20473 - Holy Shock
class spell_pal_holy_shock : public SpellScriptLoader
{
@@ -656,6 +755,13 @@ class spell_pal_righteous_defense : public SpellScriptLoader
{
PrepareSpellScript(spell_pal_righteous_defense_SpellScript);
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT))
+ return false;
+ return true;
+ }
+
SpellCastResult CheckCast()
{
Unit* caster = GetCaster();
@@ -673,9 +779,27 @@ class spell_pal_righteous_defense : public SpellScriptLoader
return SPELL_CAST_OK;
}
+ void HandleTriggerSpellLaunch(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ }
+
+ void HandleTriggerSpellHit(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (Unit* target = GetHitUnit())
+ GetCaster()->CastSpell(target, SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT, true);
+ }
+
void Register()
{
OnCheckCast += SpellCheckCastFn(spell_pal_righteous_defense_SpellScript::CheckCast);
+ //! WORKAROUND
+ //! target select will be executed in hitphase of effect 0
+ //! so we must handle trigger spell also in hit phase (default execution in launch phase)
+ //! see issue #3718
+ OnEffectLaunchTarget += SpellEffectFn(spell_pal_righteous_defense_SpellScript::HandleTriggerSpellLaunch, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL);
+ OnEffectHitTarget += SpellEffectFn(spell_pal_righteous_defense_SpellScript::HandleTriggerSpellHit, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL);
}
};
@@ -719,18 +843,139 @@ class spell_pal_sacred_shield : public SpellScriptLoader
}
};
+// 85256 - Templar's Verdict
+/// Updated 4.3.4
+class spell_pal_templar_s_verdict : public SpellScriptLoader
+{
+ public:
+ spell_pal_templar_s_verdict() : SpellScriptLoader("spell_pal_templar_s_verdict") { }
+
+ class spell_pal_templar_s_verdict_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_pal_templar_s_verdict_SpellScript);
+
+ bool Validate (SpellInfo const* /*spellEntry*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_DIVINE_PURPOSE_PROC))
+ return false;
+
+ return true;
+ }
+
+ bool Load()
+ {
+ if (GetCaster()->GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ if (GetCaster()->ToPlayer()->getClass() != CLASS_PALADIN)
+ return false;
+
+ return true;
+ }
+
+ void ChangeDamage(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+ int32 damage = GetHitDamage();
+
+ if (caster->HasAura(SPELL_PALADIN_DIVINE_PURPOSE_PROC))
+ damage *= 7.5; // 7.5*30% = 225%
+ else
+ {
+ switch (caster->GetPower(POWER_HOLY_POWER))
+ {
+ case 0: // 1 Holy Power
+ damage = damage;
+ break;
+ case 1: // 2 Holy Power
+ damage *= 3; // 3*30 = 90%
+ break;
+ case 2: // 3 Holy Power
+ damage *= 7.5; // 7.5*30% = 225%
+ break;
+ }
+ }
+
+ SetHitDamage(damage);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_pal_templar_s_verdict_SpellScript::ChangeDamage, EFFECT_0, SPELL_EFFECT_WEAPON_PERCENT_DAMAGE);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_pal_templar_s_verdict_SpellScript();
+ }
+};
+
+// 20154, 21084 - Seal of Righteousness - melee proc dummy (addition ${$MWS*(0.022*$AP+0.044*$SPH)} damage)
+class spell_pal_seal_of_righteousness : public SpellScriptLoader
+{
+ public:
+ spell_pal_seal_of_righteousness() : SpellScriptLoader("spell_pal_seal_of_righteousness") { }
+
+ class spell_pal_seal_of_righteousness_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pal_seal_of_righteousness_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetProcTarget();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ float ap = GetTarget()->GetTotalAttackPowerValue(BASE_ATTACK);
+ int32 holy = GetTarget()->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_HOLY);
+ holy += eventInfo.GetProcTarget()->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_HOLY);
+ int32 bp = int32((ap * 0.022f + 0.044f * holy) * GetTarget()->GetAttackTime(BASE_ATTACK) / 1000);
+ GetTarget()->CastCustomSpell(SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_pal_seal_of_righteousness_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_righteousness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pal_seal_of_righteousness_AuraScript();
+ }
+};
+
+
void AddSC_paladin_spell_scripts()
{
//new spell_pal_ardent_defender();
new spell_pal_blessing_of_faith();
new spell_pal_blessing_of_sanctuary();
new spell_pal_divine_sacrifice();
+ new spell_pal_divine_storm();
+ new spell_pal_divine_storm_dummy();
new spell_pal_exorcism_and_holy_wrath_damage();
+ new spell_pal_eye_for_an_eye();
new spell_pal_guarded_by_the_light();
new spell_pal_hand_of_sacrifice();
+ new spell_pal_hand_of_salvation();
new spell_pal_holy_shock();
new spell_pal_judgement_of_command();
new spell_pal_lay_on_hands();
new spell_pal_righteous_defense();
new spell_pal_sacred_shield();
+ new spell_pal_templar_s_verdict();
+ new spell_pal_seal_of_righteousness();
}
diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp
index b47eff816a3..c2bfc913893 100644
--- a/src/server/scripts/Spells/spell_priest.cpp
+++ b/src/server/scripts/Spells/spell_priest.cpp
@@ -29,29 +29,122 @@
enum PriestSpells
{
+ SPELL_PRIEST_DIVINE_AEGIS = 47753,
SPELL_PRIEST_EMPOWERED_RENEW = 63544,
+ SPELL_PRIEST_GLYPH_OF_LIGHTWELL = 55673,
+ SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL = 56161,
SPELL_PRIEST_GLYPH_OF_SHADOW = 107906,
SPELL_PRIEST_GUARDIAN_SPIRIT_HEAL = 48153,
SPELL_PRIEST_LEAP_OF_FAITH = 73325,
- SPELL_PRIEST_LEAP_OF_FAITH_TRIGGERED = 92572,
- SPELL_PRIEST_LEAP_OF_FAITH_EFFECT_TRIGGER = 92833,
SPELL_PRIEST_LEAP_OF_FAITH_EFFECT = 92832,
+ SPELL_PRIEST_LEAP_OF_FAITH_EFFECT_TRIGGER = 92833,
+ SPELL_PRIEST_LEAP_OF_FAITH_TRIGGERED = 92572,
+ SPELL_PRIEST_MANA_LEECH_PROC = 34650,
SPELL_PRIEST_PENANCE_R1 = 47540,
SPELL_PRIEST_PENANCE_R1_DAMAGE = 47758,
SPELL_PRIEST_PENANCE_R1_HEAL = 47757,
- SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED = 33619,
SPELL_PRIEST_REFLECTIVE_SHIELD_R1 = 33201,
- SPELL_PRIEST_SHADOW_WORD_DEATH = 32409,
+ SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED = 33619,
SPELL_PRIEST_SHADOWFORM_VISUAL_WITHOUT_GLYPH = 107903,
SPELL_PRIEST_SHADOWFORM_VISUAL_WITH_GLYPH = 107904,
+ SPELL_PRIEST_SHADOW_WORD_DEATH = 32409,
SPELL_PRIEST_T9_HEALING_2P = 67201,
- SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL = 64085
+ SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL = 64085,
};
enum PriestSpellIcons
{
+ PRIEST_ICON_ID_BORROWED_TIME = 2899,
PRIEST_ICON_ID_EMPOWERED_RENEW_TALENT = 3021,
- PRIEST_ICON_ID_PAIN_AND_SUFFERING = 2874
+ PRIEST_ICON_ID_PAIN_AND_SUFFERING = 2874,
+};
+
+// -47509 - Divine Aegis
+class spell_pri_divine_aegis : public SpellScriptLoader
+{
+ public:
+ spell_pri_divine_aegis() : SpellScriptLoader("spell_pri_divine_aegis") { }
+
+ class spell_pri_divine_aegis_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_divine_aegis_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_DIVINE_AEGIS))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetProcTarget();
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ int32 absorb = CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount());
+
+ // Multiple effects stack, so let's try to find this aura.
+ if (AuraEffect const* aegis = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PRIEST_DIVINE_AEGIS, EFFECT_0))
+ absorb += aegis->GetAmount();
+
+ absorb = std::min(absorb, eventInfo.GetProcTarget()->getLevel() * 125);
+
+ GetTarget()->CastCustomSpell(SPELL_PRIEST_DIVINE_AEGIS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_pri_divine_aegis_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_pri_divine_aegis_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pri_divine_aegis_AuraScript();
+ }
+};
+
+// 55680 - Glyph of Prayer of Healing
+class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader
+{
+ public:
+ spell_pri_glyph_of_prayer_of_healing() : SpellScriptLoader("spell_pri_glyph_of_prayer_of_healing") { }
+
+ class spell_pri_glyph_of_prayer_of_healing_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_glyph_of_prayer_of_healing_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL);
+ int32 heal = int32(CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks());
+ GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPH_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectProc += AuraEffectProcFn(spell_pri_glyph_of_prayer_of_healing_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pri_glyph_of_prayer_of_healing_AuraScript();
+ }
};
// 47788 - Guardian Spirit
@@ -151,6 +244,38 @@ class spell_pri_leap_of_faith_effect_trigger : public SpellScriptLoader
}
};
+// -7001 - Lightwell Renew
+class spell_pri_lightwell_renew : public SpellScriptLoader
+{
+ public:
+ spell_pri_lightwell_renew() : SpellScriptLoader("spell_pri_lightwell_renew") { }
+
+ class spell_pri_lightwell_renew_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_lightwell_renew_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ // Bonus from Glyph of Lightwell
+ if (AuraEffect* modHealing = caster->GetAuraEffect(SPELL_PRIEST_GLYPH_OF_LIGHTWELL, EFFECT_0))
+ AddPct(amount, modHealing->GetAmount());
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_lightwell_renew_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pri_lightwell_renew_AuraScript();
+ }
+};
+
// 8129 - Mana Burn
class spell_pri_mana_burn : public SpellScriptLoader
{
@@ -179,6 +304,57 @@ class spell_pri_mana_burn : public SpellScriptLoader
}
};
+// 28305 - Mana Leech (Passive) (Priest Pet Aura)
+class spell_pri_mana_leech : public SpellScriptLoader
+{
+ public:
+ spell_pri_mana_leech() : SpellScriptLoader("spell_pri_mana_leech") { }
+
+ class spell_pri_mana_leech_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_mana_leech_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_MANA_LEECH_PROC))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ _procTarget = GetTarget()->GetOwner();
+ return _procTarget;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(_procTarget, SPELL_PRIEST_MANA_LEECH_PROC, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_pri_mana_leech_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_pri_mana_leech_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+
+ private:
+ Unit* _procTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pri_mana_leech_AuraScript();
+ }
+};
+
// 49821 - Mind Sear
class spell_pri_mind_sear : public SpellScriptLoader
{
@@ -307,6 +483,82 @@ class spell_pri_penance : public SpellScriptLoader
}
};
+// -17 - Power Word: Shield
+class spell_pri_power_word_shield : public SpellScriptLoader
+{
+ public:
+ spell_pri_power_word_shield() : SpellScriptLoader("spell_pri_power_word_shield") { }
+
+ class spell_pri_power_word_shield_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_power_word_shield_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_R1))
+ return false;
+ return true;
+ }
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
+
+ // Borrowed Time
+ if (AuraEffect const* borrowedTime = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, PRIEST_ICON_ID_BORROWED_TIME, EFFECT_1))
+ bonus += CalculatePct(1.0f, borrowedTime->GetAmount());
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+
+ // Improved PW: Shield: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :)
+ // Improved PW: Shield is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage
+ bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus);
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+
+ // Twin Disciplines
+ if (AuraEffect const* twinDisciplines = caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PRIEST, 0x400000, 0, 0, GetCasterGUID()))
+ AddPct(amount, twinDisciplines->GetAmount());
+
+ // Focused Power
+ amount *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
+ }
+ }
+
+ void ReflectDamage(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
+ {
+ Unit* target = GetTarget();
+ if (dmgInfo.GetAttacker() == target)
+ return;
+
+ if (Unit* caster = GetCaster())
+ if (AuraEffect* talentAurEff = caster->GetAuraEffectOfRankedSpell(SPELL_PRIEST_REFLECTIVE_SHIELD_R1, EFFECT_0))
+ {
+ int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount());
+ target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_power_word_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ AfterEffectAbsorb += AuraEffectAbsorbFn(spell_pri_power_word_shield_AuraScript::ReflectDamage, EFFECT_0);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pri_power_word_shield_AuraScript();
+ }
+};
+
// 33110 - Prayer of Mending Heal
class spell_pri_prayer_of_mending_heal : public SpellScriptLoader
{
@@ -545,12 +797,17 @@ class spell_pri_vampiric_touch : public SpellScriptLoader
void AddSC_priest_spell_scripts()
{
+ new spell_pri_divine_aegis();
+ new spell_pri_glyph_of_prayer_of_healing();
new spell_pri_guardian_spirit();
new spell_pri_leap_of_faith_effect_trigger();
+ new spell_pri_lightwell_renew();
new spell_pri_mana_burn();
+ new spell_pri_mana_leech();
new spell_pri_mind_sear();
new spell_pri_pain_and_suffering_proc();
new spell_pri_penance();
+ new spell_pri_power_word_shield();
new spell_pri_prayer_of_mending_heal();
new spell_pri_reflective_shield_trigger();
new spell_pri_renew();
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index 14d1c1a2f98..b19f5e2cbed 100644
--- a/src/server/scripts/Spells/spell_rogue.cpp
+++ b/src/server/scripts/Spells/spell_rogue.cpp
@@ -28,10 +28,13 @@
enum RogueSpells
{
+ SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK = 22482,
SPELL_ROGUE_CHEAT_DEATH_COOLDOWN = 31231,
SPELL_ROGUE_GLYPH_OF_PREPARATION = 56819,
SPELL_ROGUE_PREY_ON_THE_WEAK = 58670,
- SPELL_ROGUE_SHIV_TRIGGERED = 5940
+ SPELL_ROGUE_SHIV_TRIGGERED = 5940,
+ SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST = 57933,
+ SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC = 59628,
};
enum RogueSpellIcons
@@ -39,6 +42,61 @@ enum RogueSpellIcons
ICON_ROGUE_IMPROVED_RECUPERATE = 4819
};
+// 13877, 33735, (check 51211, 65956) - Blade Flurry
+class spell_rog_blade_flurry : public SpellScriptLoader
+{
+ public:
+ spell_rog_blade_flurry() : SpellScriptLoader("spell_rog_blade_flurry") { }
+
+ class spell_rog_blade_flurry_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_rog_blade_flurry_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ _procTarget = eventInfo.GetActor()->SelectNearbyTarget(eventInfo.GetProcTarget());
+ return _procTarget;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ if (eventInfo.GetDamageInfo())
+ {
+ int32 damage = eventInfo.GetDamageInfo()->GetDamage();
+ GetTarget()->CastCustomSpell(SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK, SPELLVALUE_BASE_POINT0, damage, _procTarget, true, NULL, aurEff);
+ }
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_rog_blade_flurry_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_rog_blade_flurry_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_MOD_MELEE_HASTE);
+ }
+
+ private:
+ Unit* _procTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_rog_blade_flurry_AuraScript();
+ }
+};
+
// 31228 - Cheat Death
class spell_rog_cheat_death : public SpellScriptLoader
{
@@ -404,6 +462,58 @@ class spell_rog_recuperate : public SpellScriptLoader
}
};
+// -1943 - Rupture
+class spell_rog_rupture : public SpellScriptLoader
+{
+ public:
+ spell_rog_rupture() : SpellScriptLoader("spell_rog_rupture") { }
+
+ class spell_rog_rupture_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_rog_rupture_AuraScript);
+
+ bool Load()
+ {
+ Unit* caster = GetCaster();
+ return caster && caster->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ canBeRecalculated = false;
+
+ float const attackpowerPerCombo[6] =
+ {
+ 0.0f,
+ 0.015f, // 1 point: ${($m1 + $b1*1 + 0.015 * $AP) * 4} damage over 8 secs
+ 0.024f, // 2 points: ${($m1 + $b1*2 + 0.024 * $AP) * 5} damage over 10 secs
+ 0.03f, // 3 points: ${($m1 + $b1*3 + 0.03 * $AP) * 6} damage over 12 secs
+ 0.03428571f, // 4 points: ${($m1 + $b1*4 + 0.03428571 * $AP) * 7} damage over 14 secs
+ 0.0375f // 5 points: ${($m1 + $b1*5 + 0.0375 * $AP) * 8} damage over 16 secs
+ };
+
+ uint8 cp = caster->ToPlayer()->GetComboPoints();
+ if (cp > 5)
+ cp = 5;
+
+ amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * attackpowerPerCombo[cp]);
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_rog_rupture_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_rog_rupture_AuraScript();
+ }
+};
+
// 5938 - Shiv
class spell_rog_shiv : public SpellScriptLoader
{
@@ -445,13 +555,108 @@ class spell_rog_shiv : public SpellScriptLoader
}
};
+// 57934 - Tricks of the Trade
+class spell_rog_tricks_of_the_trade : public SpellScriptLoader
+{
+ public:
+ spell_rog_tricks_of_the_trade() : SpellScriptLoader("spell_rog_tricks_of_the_trade") { }
+
+ class spell_rog_tricks_of_the_trade_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_rog_tricks_of_the_trade_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _redirectTarget = NULL;
+ return true;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT)
+ GetTarget()->ResetRedirectThreat();
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ _redirectTarget = GetTarget()->GetRedirectThreatTarget();
+ return _redirectTarget;
+ }
+
+ void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+
+ Unit* target = GetTarget();
+ target->CastSpell(_redirectTarget, SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST, true);
+ target->CastSpell(target, SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC, true);
+ Remove(AURA_REMOVE_BY_DEFAULT); // maybe handle by proc charges
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_rog_tricks_of_the_trade_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ DoCheckProc += AuraCheckProcFn(spell_rog_tricks_of_the_trade_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_rog_tricks_of_the_trade_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY);
+ }
+
+ private:
+ Unit* _redirectTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_rog_tricks_of_the_trade_AuraScript();
+ }
+};
+
+// 59628 - Tricks of the Trade (Proc)
+class spell_rog_tricks_of_the_trade_proc : public SpellScriptLoader
+{
+ public:
+ spell_rog_tricks_of_the_trade_proc() : SpellScriptLoader("spell_rog_tricks_of_the_trade_proc") { }
+
+ class spell_rog_tricks_of_the_trade_proc_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_rog_tricks_of_the_trade_proc_AuraScript);
+
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ GetTarget()->ResetRedirectThreat();
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_rog_tricks_of_the_trade_proc_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_rog_tricks_of_the_trade_proc_AuraScript();
+ }
+};
+
void AddSC_rogue_spell_scripts()
{
+ new spell_rog_blade_flurry();
new spell_rog_cheat_death();
new spell_rog_deadly_poison();
new spell_rog_nerves_of_steel();
new spell_rog_preparation();
new spell_rog_prey_on_the_weak();
new spell_rog_recuperate();
+ new spell_rog_rupture();
new spell_rog_shiv();
+ new spell_rog_tricks_of_the_trade();
+ new spell_rog_tricks_of_the_trade_proc();
}
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index e2e3f5a52e7..045227a8f99 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -34,9 +34,14 @@ enum ShamanSpells
SPELL_MAGE_TEMPORAL_DISPLACEMENT = 80354,
SPELL_SHAMAN_ANCESTRAL_AWAKENING_PROC = 52752,
SPELL_SHAMAN_BIND_SIGHT = 6277,
+ SPELL_SHAMAN_EARTH_SHIELD_HEAL = 379,
SPELL_SHAMAN_EXHAUSTION = 57723,
SPELL_SHAMAN_FIRE_NOVA_TRIGGERED_R1 = 8349,
SPELL_SHAMAN_FLAME_SHOCK = 8050,
+ SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD = 63279,
+ SPELL_SHAMAN_GLYPH_OF_HEALING_STREAM_TOTEM = 55456,
+ SPELL_SHAMAN_GLYPH_OF_MANA_TIDE = 55441,
+ SPELL_SHAMAN_GLYPH_OF_THUNDERSTORM = 62132,
SPELL_SHAMAN_LAVA_FLOWS_R1 = 51480,
SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 65264,
SPELL_SHAMAN_SATED = 57724,
@@ -193,6 +198,68 @@ class spell_sha_chain_heal : public SpellScriptLoader
}
};
+// -974 - Earth Shield
+class spell_sha_earth_shield : public SpellScriptLoader
+{
+ public:
+ spell_sha_earth_shield() : SpellScriptLoader("spell_sha_earth_shield") { }
+
+ class spell_sha_earth_shield_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_sha_earth_shield_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_EARTH_SHIELD_HEAL))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD))
+ return false;
+ return true;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool & /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ amount = caster->SpellHealingBonusDone(GetUnitOwner(), GetSpellInfo(), amount, HEAL);
+ amount = GetUnitOwner()->SpellHealingBonusTaken(caster, GetSpellInfo(), amount, HEAL);
+
+ // Glyph of Earth Shield
+ //! WORKAROUND
+ //! this glyph is a proc
+ if (AuraEffect* glyph = caster->GetAuraEffect(SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD, EFFECT_0))
+ AddPct(amount, glyph->GetAmount());
+ }
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+
+ //! HACK due to currenct proc system implementation
+ if (Player* player = GetTarget()->ToPlayer())
+ if (player->HasSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL))
+ return;
+
+ GetTarget()->CastCustomSpell(SPELL_SHAMAN_EARTH_SHIELD_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), GetTarget(), true, NULL, aurEff, GetCasterGUID());
+
+ if (Player* player = GetTarget()->ToPlayer())
+ player->AddSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, time(NULL) + 3);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_sha_earth_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_DUMMY);
+ OnEffectProc += AuraEffectProcFn(spell_sha_earth_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_sha_earth_shield_AuraScript();
+ }
+};
+
// 6474 - Earthbind Totem - Fix Talent:Earthen Power, Earth's Grasp
/// Updated 4.3.4
class spell_sha_earthbind_totem : public SpellScriptLoader
@@ -549,11 +616,41 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader
}
};
+// -51490 - Thunderstorm
+class spell_sha_thunderstorm : public SpellScriptLoader
+{
+ public:
+ spell_sha_thunderstorm() : SpellScriptLoader("spell_sha_thunderstorm") { }
+
+ class spell_sha_thunderstorm_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_sha_thunderstorm_SpellScript);
+
+ void HandleKnockBack(SpellEffIndex effIndex)
+ {
+ // Glyph of Thunderstorm
+ if (GetCaster()->HasAura(SPELL_SHAMAN_GLYPH_OF_THUNDERSTORM))
+ PreventHitDefaultEffect(effIndex);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_sha_thunderstorm_SpellScript::HandleKnockBack, EFFECT_2, SPELL_EFFECT_KNOCK_BACK);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_sha_thunderstorm_SpellScript();
+ }
+};
+
void AddSC_shaman_spell_scripts()
{
new spell_sha_ancestral_awakening_proc();
new spell_sha_bloodlust();
new spell_sha_chain_heal();
+ new spell_sha_earth_shield();
new spell_sha_earthbind_totem();
new spell_sha_earthen_power();
new spell_sha_fire_nova();
@@ -562,4 +659,5 @@ void AddSC_shaman_spell_scripts()
new spell_sha_heroism();
new spell_sha_lava_lash();
new spell_sha_mana_tide_totem();
+ new spell_sha_thunderstorm();
}
diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp
index 21dea0b726b..92d8e940a22 100644
--- a/src/server/scripts/Spells/spell_warlock.cpp
+++ b/src/server/scripts/Spells/spell_warlock.cpp
@@ -25,29 +25,41 @@
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
+#include "SpellAuras.h"
enum WarlockSpells
{
SPELL_WARLOCK_BANE_OF_DOOM_EFFECT = 18662,
+ SPELL_WARLOCK_CURSE_OF_DOOM_EFFECT = 18662,
+ SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST = 62388,
SPELL_WARLOCK_DEMONIC_CIRCLE_SUMMON = 48018,
SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT = 48020,
- SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST = 62388,
- SPELL_WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS = 54435,
- SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER = 54443,
SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD = 54508,
SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER = 54509,
SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP = 54444,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS = 54435,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER = 54443,
+ SPELL_WARLOCK_DEMON_SOUL_IMP = 79459,
+ SPELL_WARLOCK_DEMON_SOUL_FELHUNTER = 79460,
+ SPELL_WARLOCK_DEMON_SOUL_FELGUARD = 79452,
+ SPELL_WARLOCK_DEMON_SOUL_SUCCUBUS = 79453,
+ SPELL_WARLOCK_DEMON_SOUL_VOIDWALKER = 79454,
+ SPELL_WARLOCK_FEL_SYNERGY_HEAL = 54181,
+ SPELL_WARLOCK_GLYPH_OF_SIPHON_LIFE = 63106,
+ SPELL_WARLOCK_HAUNT = 48181,
+ SPELL_WARLOCK_HAUNT_HEAL = 48210,
+ SPELL_WARLOCK_IMMOLATE = 348,
SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R1 = 18692,
SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R2 = 18693,
- SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R1 = 18703,
- SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R2 = 18704,
SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1 = 60955,
SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2 = 60956,
- SPELL_WARLOCK_HAUNT = 48181,
- SPELL_WARLOCK_HAUNT_HEAL = 48210,
+ SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R1 = 18703,
+ SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R2 = 18704,
SPELL_WARLOCK_LIFE_TAP_ENERGIZE = 31818,
SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2 = 32553,
+ SPELL_WARLOCK_SIPHON_LIFE_HEAL = 63106,
SPELL_WARLOCK_SOULSHATTER = 32835,
+ SPELL_WARLOCK_UNSTABLE_AFFLICTION = 30108,
SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117
};
@@ -112,6 +124,41 @@ class spell_warl_banish : public SpellScriptLoader
}
};
+// 17962 - Conflagrate - Updated to 4.3.4
+class spell_warl_conflagrate : public SpellScriptLoader
+{
+ public:
+ spell_warl_conflagrate() : SpellScriptLoader("spell_warl_conflagrate") { }
+
+ class spell_warl_conflagrate_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warl_conflagrate_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMMOLATE))
+ return false;
+ return true;
+ }
+
+ void HandleHit(SpellEffIndex /*effIndex*/)
+ {
+ if (AuraEffect const* aurEff = GetHitUnit()->GetAuraEffect(SPELL_WARLOCK_IMMOLATE, EFFECT_2, GetCaster()->GetGUID()))
+ SetHitDamage(CalculatePct(aurEff->GetAmount(), GetSpellInfo()->Effects[EFFECT_1].CalcValue(GetCaster())));
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warl_conflagrate_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warl_conflagrate_SpellScript();
+ }
+};
+
// 6201 - Create Healthstone
class spell_warl_create_healthstone : public SpellScriptLoader
{
@@ -332,6 +379,73 @@ class spell_warl_demonic_circle_teleport : public SpellScriptLoader
}
};
+// 77801 - Demon Soul - Updated to 4.3.4
+class spell_warl_demon_soul : public SpellScriptLoader
+{
+ public:
+ spell_warl_demon_soul() : SpellScriptLoader("spell_warl_demon_soul") { }
+
+ class spell_warl_demon_soul_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warl_demon_soul_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMON_SOUL_IMP))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMON_SOUL_FELHUNTER))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMON_SOUL_FELGUARD))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMON_SOUL_SUCCUBUS))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMON_SOUL_VOIDWALKER))
+ return false;
+ return true;
+ }
+
+ void OnHitTarget(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+ if (Creature* targetCreature = GetHitCreature())
+ {
+ if (targetCreature->isPet())
+ {
+ CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(targetCreature->GetEntry());
+ switch (ci->family)
+ {
+ case CREATURE_FAMILY_SUCCUBUS:
+ caster->CastSpell(caster, SPELL_WARLOCK_DEMON_SOUL_SUCCUBUS);
+ break;
+ case CREATURE_FAMILY_VOIDWALKER:
+ caster->CastSpell(caster, SPELL_WARLOCK_DEMON_SOUL_VOIDWALKER);
+ break;
+ case CREATURE_FAMILY_FELGUARD:
+ caster->CastSpell(caster, SPELL_WARLOCK_DEMON_SOUL_FELGUARD);
+ break;
+ case CREATURE_FAMILY_FELHUNTER:
+ caster->CastSpell(caster, SPELL_WARLOCK_DEMON_SOUL_FELHUNTER);
+ break;
+ case CREATURE_FAMILY_IMP:
+ caster->CastSpell(caster, SPELL_WARLOCK_DEMON_SOUL_IMP);
+ break;
+ }
+ }
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warl_demon_soul_SpellScript::OnHitTarget, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warl_demon_soul_SpellScript;
+ }
+};
+
// 47193 - Demonic Empowerment
/// Updated 4.3.4
class spell_warl_demonic_empowerment : public SpellScriptLoader
@@ -426,6 +540,86 @@ class spell_warl_everlasting_affliction : public SpellScriptLoader
}
};
+// 77799 - Fel Flame - Updated to 4.3.4
+class spell_warl_fel_flame : public SpellScriptLoader
+{
+ public:
+ spell_warl_fel_flame() : SpellScriptLoader("spell_warl_fel_flame") { }
+
+ class spell_warl_fel_flame_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warl_fel_flame_SpellScript);
+
+ void OnHitTarget(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+ Unit* target = GetHitUnit();
+ Aura* aura = target->GetAura(SPELL_WARLOCK_UNSTABLE_AFFLICTION, caster->GetGUID());
+ if (!aura)
+ aura = target->GetAura(SPELL_WARLOCK_IMMOLATE, caster->GetGUID());
+
+ if (!aura)
+ return;
+
+ int32 newDuration = aura->GetDuration() + GetSpellInfo()->Effects[EFFECT_1].CalcValue() * 1000;
+ aura->SetDuration(std::min(newDuration, aura->GetMaxDuration()));
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warl_fel_flame_SpellScript::OnHitTarget, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warl_fel_flame_SpellScript;
+ }
+};
+
+// -47230 - Fel Synergy
+class spell_warl_fel_synergy : public SpellScriptLoader
+{
+ public:
+ spell_warl_fel_synergy() : SpellScriptLoader("spell_warl_fel_synergy") { }
+
+ class spell_warl_fel_synergy_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_fel_synergy_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_FEL_SYNERGY_HEAL))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return GetTarget()->GetGuardianPet() && eventInfo.GetDamageInfo()->GetDamage();
+ }
+
+ void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ int32 heal = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount());
+ GetTarget()->CastCustomSpell(SPELL_WARLOCK_FEL_SYNERGY_HEAL, SPELLVALUE_BASE_POINT0, heal, (Unit*)NULL, true, NULL, aurEff); // TARGET_UNIT_PET
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_warl_fel_synergy_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_warl_fel_synergy_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warl_fel_synergy_AuraScript();
+ }
+};
+
// 48181 - Haunt
/// Updated 4.3.4
class spell_warl_haunt : public SpellScriptLoader
@@ -601,6 +795,34 @@ class spell_warl_life_tap : public SpellScriptLoader
}
};
+// 18541 - Ritual of Doom Effect
+class spell_warl_ritual_of_doom_effect : public SpellScriptLoader
+{
+ public:
+ spell_warl_ritual_of_doom_effect() : SpellScriptLoader("spell_warl_ritual_of_doom_effect") { }
+
+ class spell_warl_ritual_of_doom_effect_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warl_ritual_of_doom_effect_SpellScript);
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+ caster->CastSpell(caster, GetEffectValue(), true);
+ }
+
+ void Register()
+ {
+ OnEffectHit += SpellEffectFn(spell_warl_ritual_of_doom_effect_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warl_ritual_of_doom_effect_SpellScript();
+ }
+};
+
// 27285 - Seed of Corruption
/// Updated 4.3.4
class spell_warl_seed_of_corruption : public SpellScriptLoader
@@ -630,6 +852,92 @@ class spell_warl_seed_of_corruption : public SpellScriptLoader
}
};
+// -7235 - Shadow Ward
+class spell_warl_shadow_ward : public SpellScriptLoader
+{
+ public:
+ spell_warl_shadow_ward() : SpellScriptLoader("spell_warl_shadow_ward") { }
+
+ class spell_warl_shadow_ward_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_shadow_ward_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warl_shadow_ward_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warl_shadow_ward_AuraScript();
+ }
+};
+
+// 63108 - Siphon Life
+class spell_warl_siphon_life : public SpellScriptLoader
+{
+ public:
+ spell_warl_siphon_life() : SpellScriptLoader("spell_warl_siphon_life") { }
+
+ class spell_warl_siphon_life_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_siphon_life_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SIPHON_LIFE_HEAL))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_GLYPH_OF_SIPHON_LIFE))
+ return false;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ return eventInfo.GetDamageInfo()->GetDamage();
+ }
+
+ void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+
+ int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()));
+ // Glyph of Siphon Life
+ if (AuraEffect const* glyph = GetTarget()->GetAuraEffect(SPELL_WARLOCK_GLYPH_OF_SIPHON_LIFE, EFFECT_0))
+ AddPct(amount, glyph->GetAmount());
+
+ GetTarget()->CastCustomSpell(SPELL_WARLOCK_SIPHON_LIFE_HEAL, SPELLVALUE_BASE_POINT0, amount, GetTarget(), true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_warl_siphon_life_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_warl_siphon_life_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warl_siphon_life_AuraScript();
+ }
+};
+
// 29858 - Soulshatter
/// Updated 4.3.4
class spell_warl_soulshatter : public SpellScriptLoader
@@ -713,15 +1021,22 @@ void AddSC_warlock_spell_scripts()
{
new spell_warl_bane_of_doom();
new spell_warl_banish();
+ new spell_warl_conflagrate();
new spell_warl_create_healthstone();
new spell_warl_demonic_circle_summon();
new spell_warl_demonic_circle_teleport();
new spell_warl_demonic_empowerment();
+ new spell_warl_demon_soul();
new spell_warl_everlasting_affliction();
+ new spell_warl_fel_flame();
+ new spell_warl_fel_synergy();
new spell_warl_haunt();
new spell_warl_health_funnel();
new spell_warl_life_tap();
+ new spell_warl_ritual_of_doom_effect();
new spell_warl_seed_of_corruption();
+ new spell_warl_shadow_ward();
+ new spell_warl_siphon_life();
new spell_warl_soulshatter();
new spell_warl_unstable_affliction();
}
diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index 909e90beaad..9e2cb8aad71 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -35,10 +35,26 @@ enum WarriorSpells
SPELL_WARRIOR_DEEP_WOUNDS_RANK_2 = 12850,
SPELL_WARRIOR_DEEP_WOUNDS_RANK_3 = 12868,
SPELL_WARRIOR_DEEP_WOUNDS_RANK_PERIODIC = 12721,
+ SPELL_WARRIOR_EXECUTE = 20647,
+ SPELL_WARRIOR_GLYPH_OF_EXECUTION = 58367,
+ SPELL_WARRIOR_GLYPH_OF_VIGILANCE = 63326,
SPELL_WARRIOR_JUGGERNAUT_CRIT_BONUS_BUFF = 65156,
SPELL_WARRIOR_JUGGERNAUT_CRIT_BONUS_TALENT = 64976,
SPELL_WARRIOR_LAST_STAND_TRIGGERED = 12976,
- SPELL_WARRIOR_SLAM = 50782
+ SPELL_WARRIOR_SLAM = 50782,
+ SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK = 26654,
+ SPELL_WARRIOR_TAUNT = 355,
+ SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_1 = 46859,
+ SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_2 = 46860,
+ SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_1 = 64849,
+ SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_2 = 64850,
+ SPELL_WARRIOR_VIGILANCE_PROC = 50725,
+ SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT = 59665,
+
+ SPELL_PALADIN_BLESSING_OF_SANCTUARY = 20911,
+ SPELL_PALADIN_GREATER_BLESSING_OF_SANCTUARY = 25899,
+ SPELL_PRIEST_RENEWED_HOPE = 63944,
+ SPELL_GEN_DAMAGE_REDUCTION_AURA = 68066,
};
enum WarriorSpellIcons
@@ -286,6 +302,62 @@ class spell_warr_execute : public SpellScriptLoader
}
};
+// 59725 - Improved Spell Reflection
+class spell_warr_improved_spell_reflection : public SpellScriptLoader
+{
+ public:
+ spell_warr_improved_spell_reflection() : SpellScriptLoader("spell_warr_improved_spell_reflection") { }
+
+ class spell_warr_improved_spell_reflection_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_improved_spell_reflection_SpellScript);
+
+ void FilterTargets(std::list<WorldObject*>& unitList)
+ {
+ if (GetCaster())
+ unitList.remove(GetCaster());
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_improved_spell_reflection_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_PARTY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_improved_spell_reflection_SpellScript();
+ }
+};
+
+// 5246 - Intimidating Shout
+class spell_warr_intimidating_shout : public SpellScriptLoader
+{
+ public:
+ spell_warr_intimidating_shout() : SpellScriptLoader("spell_warr_intimidating_shout") { }
+
+ class spell_warr_intimidating_shout_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_intimidating_shout_SpellScript);
+
+ void FilterTargets(std::list<WorldObject*>& unitList)
+ {
+ unitList.remove(GetExplTargetWorldObject());
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_intimidating_shout_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_intimidating_shout_SpellScript::FilterTargets, EFFECT_2, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_intimidating_shout_SpellScript();
+ }
+};
+
/// Updated 4.3.4
class spell_warr_last_stand : public SpellScriptLoader
{
@@ -325,6 +397,121 @@ class spell_warr_last_stand : public SpellScriptLoader
}
};
+// 7384, 7887, 11584, 11585 - Overpower
+class spell_warr_overpower : public SpellScriptLoader
+{
+ public:
+ spell_warr_overpower() : SpellScriptLoader("spell_warr_overpower") { }
+
+ class spell_warr_overpower_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_overpower_SpellScript);
+
+ void HandleEffect(SpellEffIndex /*effIndex*/)
+ {
+ uint32 spellId = 0;
+ if (GetCaster()->HasAura(SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_1))
+ spellId = SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_1;
+ else if (GetCaster()->HasAura(SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_2))
+ spellId = SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_2;
+
+ if (!spellId)
+ return;
+
+ if (Player* target = GetHitPlayer())
+ if (target->HasUnitState(UNIT_STATE_CASTING))
+ target->CastSpell(target, spellId, true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warr_overpower_SpellScript::HandleEffect, EFFECT_0, SPELL_EFFECT_ANY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_overpower_SpellScript();
+ }
+};
+
+// -772 - Rend
+class spell_warr_rend : public SpellScriptLoader
+{
+ public:
+ spell_warr_rend() : SpellScriptLoader("spell_warr_rend") { }
+
+ class spell_warr_rend_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warr_rend_AuraScript);
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ canBeRecalculated = false;
+
+ // $0.2 * (($MWB + $mwb) / 2 + $AP / 14 * $MWS) bonus per tick
+ float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
+ int32 mws = caster->GetAttackTime(BASE_ATTACK);
+ float mwbMin = caster->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+ float mwbMax = caster->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
+ float mwb = ((mwbMin + mwbMax) / 2 + ap * mws / 14000) * 0.2f;
+ amount += int32(caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), mwb));
+
+ // "If used while your target is above 75% health, Rend does 35% more damage."
+ // as for 3.1.3 only ranks above 9 (wrong tooltip?)
+ if (GetSpellInfo()->GetRank() >= 9)
+ {
+ if (GetUnitOwner()->HasAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, GetSpellInfo(), caster))
+ AddPct(amount, GetSpellInfo()->Effects[EFFECT_2].CalcValue(caster));
+ }
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warr_rend_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warr_rend_AuraScript();
+ }
+};
+
+// 64380, 65941 - Shattering Throw
+class spell_warr_shattering_throw : public SpellScriptLoader
+{
+ public:
+ spell_warr_shattering_throw() : SpellScriptLoader("spell_warr_shattering_throw") { }
+
+ class spell_warr_shattering_throw_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_shattering_throw_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ // remove shields, will still display immune to damage part
+ if (Unit* target = GetHitUnit())
+ target->RemoveAurasWithMechanic(1 << MECHANIC_IMMUNE_SHIELD, AURA_REMOVE_BY_ENEMY_SPELL);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warr_shattering_throw_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_shattering_throw_SpellScript();
+ }
+};
+
/// Updated 4.3.4
class spell_warr_slam : public SpellScriptLoader
{
@@ -361,6 +548,182 @@ class spell_warr_slam : public SpellScriptLoader
}
};
+// 12328, 18765, 35429 - Sweeping Strikes
+class spell_warr_sweeping_strikes : public SpellScriptLoader
+{
+ public:
+ spell_warr_sweeping_strikes() : SpellScriptLoader("spell_warr_sweeping_strikes") { }
+
+ class spell_warr_sweeping_strikes_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warr_sweeping_strikes_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ _procTarget = eventInfo.GetActor()->SelectNearbyTarget(eventInfo.GetProcTarget());
+ return _procTarget;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(_procTarget, SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ DoCheckProc += AuraCheckProcFn(spell_warr_sweeping_strikes_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_warr_sweeping_strikes_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+
+ private:
+ Unit* _procTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warr_sweeping_strikes_AuraScript();
+ }
+};
+
+// 50720 - Vigilance
+class spell_warr_vigilance : public SpellScriptLoader
+{
+ public:
+ spell_warr_vigilance() : SpellScriptLoader("spell_warr_vigilance") { }
+
+ class spell_warr_vigilance_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warr_vigilance_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_GLYPH_OF_VIGILANCE))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_VIGILANCE_PROC))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_GEN_DAMAGE_REDUCTION_AURA))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _procTarget = NULL;
+ return true;
+ }
+
+ void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* target = GetTarget();
+ target->CastSpell(target, SPELL_GEN_DAMAGE_REDUCTION_AURA, true);
+
+ if (Unit* caster = GetCaster())
+ target->CastSpell(caster, SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT, true);
+ }
+
+ void HandleAfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ //! WORKAROUND
+ //! this glyph is a proc
+ if (Unit* caster = GetCaster())
+ {
+ if (AuraEffect const* glyph = caster->GetAuraEffect(SPELL_WARRIOR_GLYPH_OF_VIGILANCE, EFFECT_0))
+ GetTarget()->ModifyRedirectThreat(glyph->GetAmount());
+ }
+ }
+
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* target = GetTarget();
+ if (target->HasAura(SPELL_GEN_DAMAGE_REDUCTION_AURA) &&
+ !(target->HasAura(SPELL_PALADIN_BLESSING_OF_SANCTUARY) ||
+ target->HasAura(SPELL_PALADIN_GREATER_BLESSING_OF_SANCTUARY) ||
+ target->HasAura(SPELL_PRIEST_RENEWED_HOPE)))
+ {
+ target->RemoveAurasDueToSpell(SPELL_GEN_DAMAGE_REDUCTION_AURA);
+ }
+
+ target->ResetRedirectThreat();
+ }
+
+ bool CheckProc(ProcEventInfo& /*eventInfo*/)
+ {
+ _procTarget = GetCaster();
+ return _procTarget;
+ }
+
+ void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(_procTarget, SPELL_WARRIOR_VIGILANCE_PROC, true, NULL, aurEff);
+ }
+
+ void Register()
+ {
+ OnEffectApply += AuraEffectApplyFn(spell_warr_vigilance_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ AfterEffectApply += AuraEffectApplyFn(spell_warr_vigilance_AuraScript::HandleAfterApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ OnEffectRemove += AuraEffectRemoveFn(spell_warr_vigilance_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ DoCheckProc += AuraCheckProcFn(spell_warr_vigilance_AuraScript::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_warr_vigilance_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+
+ private:
+ Unit* _procTarget;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warr_vigilance_AuraScript();
+ }
+};
+
+// 50725 Vigilance
+class spell_warr_vigilance_trigger : public SpellScriptLoader
+{
+ public:
+ spell_warr_vigilance_trigger() : SpellScriptLoader("spell_warr_vigilance_trigger") { }
+
+ class spell_warr_vigilance_trigger_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_vigilance_trigger_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ // Remove Taunt cooldown
+ if (Player* target = GetHitPlayer())
+ target->RemoveSpellCooldown(SPELL_WARRIOR_TAUNT, true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warr_vigilance_trigger_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_vigilance_trigger_SpellScript();
+ }
+};
+
void AddSC_warrior_spell_scripts()
{
new spell_warr_bloodthirst();
@@ -369,6 +732,14 @@ void AddSC_warrior_spell_scripts()
new spell_warr_concussion_blow();
new spell_warr_deep_wounds();
new spell_warr_execute();
+ new spell_warr_improved_spell_reflection();
+ new spell_warr_intimidating_shout();
new spell_warr_last_stand();
+ new spell_warr_overpower();
+ new spell_warr_rend();
+ new spell_warr_shattering_throw();
new spell_warr_slam();
+ new spell_warr_sweeping_strikes();
+ new spell_warr_vigilance();
+ new spell_warr_vigilance_trigger();
}
diff --git a/src/server/scripts/World/achievement_scripts.cpp b/src/server/scripts/World/achievement_scripts.cpp
index 41a32185620..d0ffe5214db 100644
--- a/src/server/scripts/World/achievement_scripts.cpp
+++ b/src/server/scripts/World/achievement_scripts.cpp
@@ -315,6 +315,30 @@ class achievement_not_even_a_scratch : public AchievementCriteriaScript
}
};
+enum FlirtWithDisaster
+{
+ AURA_PERFUME_FOREVER = 70235,
+ AURA_PERFUME_ENCHANTRESS = 70234,
+ AURA_PERFUME_VICTORY = 70233,
+};
+
+class achievement_flirt_with_disaster_perf_check : public AchievementCriteriaScript
+{
+ public:
+ achievement_flirt_with_disaster_perf_check() : AchievementCriteriaScript("achievement_flirt_with_disaster_perf_check") { }
+
+ bool OnCheck(Player* player, Unit* /*target*/)
+ {
+ if (!player)
+ return false;
+
+ if (player->HasAura(AURA_PERFUME_FOREVER) || player->HasAura(AURA_PERFUME_ENCHANTRESS) || player->HasAura(AURA_PERFUME_VICTORY))
+ return true;
+
+ return false;
+ }
+};
+
void AddSC_achievement_scripts()
{
new achievement_resilient_victory();
@@ -333,4 +357,5 @@ void AddSC_achievement_scripts()
new achievement_bg_sa_defense_of_ancients();
new achievement_tilted();
new achievement_not_even_a_scratch();
+ new achievement_flirt_with_disaster_perf_check();
}
diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp
index e5f2c060109..777de92bc3b 100644
--- a/src/server/scripts/World/npcs_special.cpp
+++ b/src/server/scripts/World/npcs_special.cpp
@@ -1981,10 +1981,11 @@ class npc_training_dummy : public CreatureScript
public:
npc_training_dummy() : CreatureScript("npc_training_dummy") { }
- struct npc_training_dummyAI : Scripted_NoMovementAI
+ struct npc_training_dummyAI : ScriptedAI
{
- npc_training_dummyAI(Creature* creature) : Scripted_NoMovementAI(creature)
+ npc_training_dummyAI(Creature* creature) : ScriptedAI(creature)
{
+ SetCombatMovement(false);
entry = creature->GetEntry();
}
@@ -2015,12 +2016,6 @@ public:
damage = 0;
}
- void EnterCombat(Unit* /*who*/)
- {
- if (entry != NPC_ADVANCED_TARGET_DUMMY && entry != NPC_TARGET_DUMMY)
- return;
- }
-
void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index 86d0cbf613b..01ccf648b31 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -50,6 +50,7 @@ set(shared_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/utf8cpp
diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h
index 34b7c5083e3..fa02b1d369a 100644
--- a/src/server/shared/Database/DatabaseWorkerPool.h
+++ b/src/server/shared/Database/DatabaseWorkerPool.h
@@ -31,6 +31,9 @@
#include "QueryHolder.h"
#include "AdhocStatement.h"
+#define MIN_MYSQL_SERVER_VERSION 50100u
+#define MIN_MYSQL_CLIENT_VERSION 50100u
+
class PingOperation : public SQLOperation
{
//! Operation for idle delaythreads
@@ -53,6 +56,7 @@ class DatabaseWorkerPool
_connections.resize(IDX_SIZE);
WPFatal (mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
+ WPFatal (mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1");
}
~DatabaseWorkerPool()
@@ -73,6 +77,8 @@ class DatabaseWorkerPool
{
T* t = new T(_queue, _connectionInfo);
res &= t->Open();
+ if (res) // only check mysql version if connection is valid
+ WPFatal(mysql_get_server_version(t->GetHandle()) >= MIN_MYSQL_SERVER_VERSION, "TrinityCore does not support MySQL versions below 5.1");
_connections[IDX_ASYNC][i] = t;
++_connectionCount[IDX_ASYNC];
}
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index 64110c8a8ce..111ab111b02 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -101,6 +101,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_GUILD_MEMBER, "SELECT guildid, rank FROM guild_member WHERE guid = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED, "SELECT g.guildid, g.name, gr.rname, gm.pnote, gm.offnote "
+ "FROM guild g JOIN guild_member gm ON g.guildid = gm.guildid "
+ "JOIN guild_rank gr ON g.guildid = gr.guildid AND gm.rank = gr.rid WHERE gm.guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_SEL_CHARACTER_ACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_CRITERIAPROGRESS, "SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_EQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, item4, item5, item6, item7, item8, "
@@ -144,6 +147,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD, "UPDATE item_instance SET duration = ?, flags = ?, durability = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ITEM_INSTANCE, "DELETE FROM item_instance WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER, "DELETE FROM item_instance WHERE owner_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_GIFT_OWNER, "UPDATE character_gifts SET guid = ? WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GIFT, "DELETE FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_SYNCH);
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
index 74b56b1a437..67f794a9c01 100755
--- a/src/server/shared/Database/Implementation/CharacterDatabase.h
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
@@ -105,6 +105,7 @@ enum CharacterDatabaseStatements
CHAR_SEL_CHARACTER_SPELLCOOLDOWNS,
CHAR_SEL_CHARACTER_DECLINEDNAMES,
CHAR_SEL_GUILD_MEMBER,
+ CHAR_SEL_GUILD_MEMBER_EXTENDED,
CHAR_SEL_CHARACTER_ARENAINFO,
CHAR_SEL_CHARACTER_ACHIEVEMENTS,
CHAR_SEL_CHARACTER_CRITERIAPROGRESS,
@@ -144,6 +145,7 @@ enum CharacterDatabaseStatements
CHAR_UPD_ITEM_INSTANCE,
CHAR_UPD_ITEM_INSTANCE_ON_LOAD,
CHAR_DEL_ITEM_INSTANCE,
+ CHAR_DEL_ITEM_INSTANCE_BY_OWNER,
CHAR_UPD_GIFT_OWNER,
CHAR_DEL_GIFT,
CHAR_SEL_CHARACTER_GIFT_BY_ITEM,
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp
index 70d509af6fe..a23294a038c 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp
@@ -22,11 +22,11 @@ void LoginDatabaseConnection::DoPrepareStatements()
if (!m_reconnecting)
m_stmts.resize(MAX_LOGINDATABASE_STATEMENTS);
- PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity realmd', 'Failed login autoban')", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity realmd', 'Failed login autoban')", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_IP_BANNED_BY_IP, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) AND ip LIKE CONCAT('%%', ?, '%%') ORDER BY unbandate", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED, "SELECT bandate, unbandate FROM account_banned WHERE id = ? AND active = 1", CONNECTION_SYNCH);
@@ -42,12 +42,12 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, sessionkey, last_ip, locked, v, s, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_IP_NOT_BANNED, "DELETE FROM ip_banned WHERE ip = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_ACCOUNT_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?, 1)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED, "UPDATE account_banned SET active = 0 WHERE id = ? AND active != 0", CONNECTION_ASYNC);
@@ -55,14 +55,15 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_REALM_CHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, joindate) VALUES(?, ?, NOW())", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, joindate) VALUES(?, ?, NOW())", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "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", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_LOG, "INSERT INTO logs (time, realm, type, level, string) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET v = 0, s = 0, username = ?, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_PASSWORD, "UPDATE account SET v = 0, s = 0, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_UPD_MUTE_TIME, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_MUTE_TIME, "UPDATE account SET mutetime = ? , mutereason = ? , muteby = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = 1 WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC);
@@ -76,7 +77,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_GET_USERNAME_BY_ID, "SELECT username FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT 1 FROM account WHERE id = ? AND sha_pass_hash = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT 1 FROM account WHERE username = ? AND sha_pass_hash = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO, "SELECT a.username, a.last_ip, aa.gmlevel, a.expansion FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.id = ?", CONNECTION_SYNCH);
@@ -87,4 +88,18 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH);
+
+ PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_GROUPS, "SELECT groupId FROM rbac_account_groups WHERE accountId = ? AND (realmId = ? OR realmId = -1) GROUP BY groupId", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_GROUP, "INSERT INTO rbac_account_groups (accountId, groupId, realmId) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_GROUP, "DELETE FROM rbac_account_groups WHERE accountId = ? AND groupId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC);
+
+ PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_ROLES, "SELECT roleId, granted FROM rbac_account_roles WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY roleId, realmId", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_ROLE, "INSERT INTO rbac_account_roles (accountId, roleId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_ROLE, "DELETE FROM rbac_account_roles WHERE accountId = ? AND roleId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC);
+
+ PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION, "INSERT INTO rbac_account_permissions (accountId, permissionId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, "DELETE FROM rbac_account_permissions WHERE accountId = ? AND permissionId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC);
}
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h
index 798016d553d..939cc4b4790 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.h
+++ b/src/server/shared/Database/Implementation/LoginDatabase.h
@@ -83,6 +83,7 @@ enum LoginDatabaseStatements
LOGIN_UPD_USERNAME,
LOGIN_UPD_PASSWORD,
LOGIN_UPD_MUTE_TIME,
+ LOGIN_UPD_MUTE_TIME_LOGIN,
LOGIN_UPD_LAST_IP,
LOGIN_UPD_ACCOUNT_ONLINE,
LOGIN_UPD_UPTIME_PLAYERS,
@@ -108,6 +109,16 @@ enum LoginDatabaseStatements
LOGIN_SEL_REALMLIST_SECURITY_LEVEL,
LOGIN_DEL_ACCOUNT,
+ LOGIN_SEL_ACCOUNT_ACCESS_BY_ID,
+ LOGIN_SEL_RBAC_ACCOUNT_GROUPS,
+ LOGIN_INS_RBAC_ACCOUNT_GROUP,
+ LOGIN_DEL_RBAC_ACCOUNT_GROUP,
+ LOGIN_SEL_RBAC_ACCOUNT_ROLES,
+ LOGIN_INS_RBAC_ACCOUNT_ROLE,
+ LOGIN_DEL_RBAC_ACCOUNT_ROLE,
+ LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS,
+ LOGIN_INS_RBAC_ACCOUNT_PERMISSION,
+ LOGIN_DEL_RBAC_ACCOUNT_PERMISSION,
MAX_LOGINDATABASE_STATEMENTS
};
diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp
index 56e164b2ef1..185ac647e33 100644
--- a/src/server/shared/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/shared/Database/Implementation/WorldDatabase.cpp
@@ -60,7 +60,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID, "UPDATE waypoint_data SET wpguid = 0", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_POS, "SELECT id, point FROM waypoint_data WHERE (abs(position_x - ?) <= ?) and (abs(position_y - ?) <= ?) and (abs(position_z - ?) <= ?)", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID, "SELECT wpguid FROM waypoint_data WHERE id = ? and wpguid <> 0", CONNECTION_SYNCH);
- PrepareStatement(WOLRD_SEL_WAYPOINT_DATA_ACTION, "SELECT DISTINCT action FROM waypoint_data", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_ACTION, "SELECT DISTINCT action FROM waypoint_data", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPTS_MAX_ID, "SELECT MAX(guid) FROM waypoint_scripts", CONNECTION_SYNCH);
PrepareStatement(WORLD_INS_CREATURE_ADDON, "INSERT INTO creature_addon(guid, path_id) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_UPD_CREATURE_ADDON_PATH, "UPDATE creature_addon SET path_id = ? WHERE guid = ?", CONNECTION_ASYNC);
@@ -78,7 +78,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_INS_CREATURE_TRANSPORT, "INSERT INTO creature_transport (guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO) values (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_UPD_CREATURE_TRANSPORT_EMOTE, "UPDATE creature_transport SET emote = ? WHERE transport_entry = ? AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
- PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_IP2NATION_COUNTRY, "SELECT c.country FROM ip2nationCountries c, ip2nation i WHERE i.ip < ? AND c.code = i.country ORDER BY i.ip DESC LIMIT 0,1", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
diff --git a/src/server/shared/Database/Implementation/WorldDatabase.h b/src/server/shared/Database/Implementation/WorldDatabase.h
index 30cc45c535a..032baf29dd9 100644
--- a/src/server/shared/Database/Implementation/WorldDatabase.h
+++ b/src/server/shared/Database/Implementation/WorldDatabase.h
@@ -80,7 +80,7 @@ enum WorldDatabaseStatements
WORLD_SEL_WAYPOINT_DATA_MAX_POINT,
WORLD_SEL_WAYPOINT_DATA_BY_POS,
WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID,
- WOLRD_SEL_WAYPOINT_DATA_ACTION,
+ WORLD_SEL_WAYPOINT_DATA_ACTION,
WORLD_SEL_WAYPOINT_SCRIPTS_MAX_ID,
WORLD_UPD_CREATURE_ADDON_PATH,
WORLD_INS_CREATURE_ADDON,
diff --git a/src/server/shared/Debugging/Errors.h b/src/server/shared/Debugging/Errors.h
index 3d10740f149..10e94634e9a 100644
--- a/src/server/shared/Debugging/Errors.h
+++ b/src/server/shared/Debugging/Errors.h
@@ -24,7 +24,7 @@
#include <ace/Stack_Trace.h>
#include <ace/OS_NS_unistd.h>
-#define WPAssert(assertion) { if (!(assertion)) { ACE_Stack_Trace st; sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } }
+#define WPAssert(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } }
#define WPError(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); *((volatile int*)NULL) = 0; } }
#define WPWarning(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s WARNING:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); } }
#define WPFatal(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s FATAL ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); ACE_OS::sleep(10); *((volatile int*)NULL) = 0; } }
diff --git a/src/server/shared/Debugging/WheatyExceptionReport.cpp b/src/server/shared/Debugging/WheatyExceptionReport.cpp
index 96a115f8057..ea9ab096dcd 100644
--- a/src/server/shared/Debugging/WheatyExceptionReport.cpp
+++ b/src/server/shared/Debugging/WheatyExceptionReport.cpp
@@ -193,154 +193,125 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
{
// Windows NT product family.
case VER_PLATFORM_WIN32_NT:
+ {
+ #if WINVER < 0x0500
+ BYTE suiteMask = osvi.wReserved[0];
+ BYTE productType = osvi.wReserved[1];
+ #else
+ WORD suiteMask = osvi.wSuiteMask;
+ BYTE productType = osvi.wProductType;
+ #endif // WINVER < 0x0500
+
// Test for the specific product family.
if (osvi.dwMajorVersion == 6)
{
- #if WINVER < 0x0500
- if (osvi.wReserved[1] == VER_NT_WORKSTATION)
- #else
- if (osvi.wProductType == VER_NT_WORKSTATION)
- #endif // WINVER < 0x0500
+ if (productType == VER_NT_WORKSTATION)
{
- if (osvi.dwMinorVersion == 1)
+ if (osvi.dwMinorVersion == 2)
+ _tcsncat(szVersion, _T("Windows 8 "), cntMax);
+ else if (osvi.dwMinorVersion == 1)
_tcsncat(szVersion, _T("Windows 7 "), cntMax);
else
_tcsncat(szVersion, _T("Windows Vista "), cntMax);
}
+ else if (osvi.dwMinorVersion == 2)
+ _tcsncat(szVersion, _T("Windows Server 2012 "), cntMax);
else if (osvi.dwMinorVersion == 1)
_tcsncat(szVersion, _T("Windows Server 2008 R2 "), cntMax);
else
_tcsncat(szVersion, _T("Windows Server 2008 "), cntMax);
}
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
_tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
_tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
_tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
- if (osvi.dwMajorVersion <= 4)
+ else if (osvi.dwMajorVersion <= 4)
_tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
// Test for specific product on Windows NT 4.0 SP6 and later.
if (bOsVersionInfoEx)
{
// Test for the workstation type.
- #if WINVER < 0x0500
- if (osvi.wReserved[1] == VER_NT_WORKSTATION)
- #else
- if (osvi.wProductType == VER_NT_WORKSTATION)
- #endif // WINVER < 0x0500
- {
- if (osvi.dwMajorVersion == 4)
- _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_PERSONAL)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
- #endif // WINVER < 0x0500
+ if (productType == VER_NT_WORKSTATION)
+ {
+ if (osvi.dwMajorVersion == 4)
+ _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
+ else if (suiteMask & VER_SUITE_PERSONAL)
_tcsncat(szVersion, _T("Home Edition "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT)
- #endif // WINVER < 0x0500
+ else if (suiteMask & VER_SUITE_EMBEDDEDNT)
_tcsncat(szVersion, _T("Embedded "), cntMax);
- else
- _tcsncat(szVersion, _T("Professional "), cntMax);
- }
- // Test for the server type.
- #if WINVER < 0x0500
- else if (osvi.wReserved[1] == VER_NT_SERVER)
- #else
- else if (osvi.wProductType == VER_NT_SERVER)
- #endif // WINVER < 0x0500
- {
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ else
+ _tcsncat(szVersion, _T("Professional "), cntMax);
+ }
+ // Test for the server type.
+ else if (productType == VER_NT_SERVER)
{
- #if WINVER < 0x0500
- if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
- #else
- if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
- #endif // WINVER < 0x0500
+ if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ {
+ if (suiteMask & VER_SUITE_DATACENTER)
_tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
- #endif // WINVER < 0x0500
+ else if (suiteMask & VER_SUITE_ENTERPRISE)
_tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] == VER_SUITE_BLADE)
- #else
- else if (osvi.wSuiteMask == VER_SUITE_BLADE)
- #endif // WINVER < 0x0500
+ else if (suiteMask == VER_SUITE_BLADE)
_tcsncat(szVersion, _T("Web Edition "), cntMax);
- else
- _tcsncat(szVersion, _T("Standard Edition "), cntMax);
- }
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
- {
- #if WINVER < 0x0500
- if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
- #else
- if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
- #endif // WINVER < 0x0500
+ else
+ _tcsncat(szVersion, _T("Standard Edition "), cntMax);
+ }
+ else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ {
+ if (suiteMask & VER_SUITE_DATACENTER)
_tcsncat(szVersion, _T("Datacenter Server "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
- #endif // WINVER < 0x0500
+ else if (suiteMask & VER_SUITE_ENTERPRISE)
_tcsncat(szVersion, _T("Advanced Server "), cntMax);
- else
- _tcsncat(szVersion, _T("Server "), cntMax);
- }
- else // Windows NT 4.0
- {
- #if WINVER < 0x0500
- if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
- #else
- if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
- #endif // WINVER < 0x0500
+ else
+ _tcsncat(szVersion, _T("Server "), cntMax);
+ }
+ else // Windows NT 4.0
+ {
+ if (suiteMask & VER_SUITE_ENTERPRISE)
_tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
- else
- _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
+ else
+ _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
+ }
}
}
- }
- // Display service pack (if any) and build number.
- if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0)
- {
- HKEY hKey;
- LONG lRet;
- // Test for SP6 versus SP6a.
- lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
- if (lRet == ERROR_SUCCESS)
+ // Display service pack (if any) and build number.
+ if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0)
{
- _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
- osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
+ HKEY hKey;
+ LONG lRet;
+
+ // Test for SP6 versus SP6a.
+ lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
+ if (lRet == ERROR_SUCCESS)
+ {
+ _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ else // Windows NT 4.0 prior to SP6a
+ {
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ _tcsncat(szVersion, wszTmp, cntMax);
+ }
+ ::RegCloseKey(hKey);
}
- else // Windows NT 4.0 prior to SP6a
+ else // Windows NT 3.51 and earlier or Windows 2000 and later
{
- _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ if (!_tcslen(osvi.szCSDVersion))
+ _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
+ osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
+ else
+ _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
+ osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
_tcsncat(szVersion, wszTmp, cntMax);
}
- ::RegCloseKey(hKey);
- }
- else // Windows NT 3.51 and earlier or Windows 2000 and later
- {
- if (!_tcslen(osvi.szCSDVersion))
- _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
- osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- else
- _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
+ break;
}
- break;
default:
_stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
diff --git a/src/server/shared/Logging/Appender.h b/src/server/shared/Logging/Appender.h
index a8854a8abc6..08628948b90 100644
--- a/src/server/shared/Logging/Appender.h
+++ b/src/server/shared/Logging/Appender.h
@@ -123,6 +123,12 @@ struct LogMessage
std::string prefix;
std::string param1;
time_t mtime;
+
+ ///@ Returns size of the log message content in bytes
+ uint32 Size() const
+ {
+ return prefix.size() + text.size();
+ }
};
class Appender
diff --git a/src/server/shared/Logging/AppenderFile.cpp b/src/server/shared/Logging/AppenderFile.cpp
index 8189237bb4e..93d53bcc30d 100644
--- a/src/server/shared/Logging/AppenderFile.cpp
+++ b/src/server/shared/Logging/AppenderFile.cpp
@@ -18,58 +18,76 @@
#include "AppenderFile.h"
#include "Common.h"
-AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, const char* _filename, const char* _logDir, const char* _mode, AppenderFlags _flags)
- : Appender(id, name, APPENDER_FILE, level, _flags)
- , filename(_filename)
- , logDir(_logDir)
- , mode(_mode)
+AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, const char* _filename, const char* _logDir, const char* _mode, AppenderFlags _flags, uint64 fileSize):
+ Appender(id, name, APPENDER_FILE, level, _flags),
+ logfile(NULL),
+ filename(_filename),
+ logDir(_logDir),
+ mode(_mode),
+ maxFileSize(fileSize),
+ fileSize(0)
{
dynamicName = std::string::npos != filename.find("%s");
backup = _flags & APPENDER_FLAGS_MAKE_FILE_BACKUP;
- logfile = !dynamicName ? OpenFile(_filename, _mode, backup) : NULL;
+ logfile = !dynamicName ? OpenFile(_filename, _mode, mode == "w" && backup) : NULL;
}
AppenderFile::~AppenderFile()
{
- if (logfile)
- {
- fclose(logfile);
- logfile = NULL;
- }
+ CloseFile();
}
void AppenderFile::_write(LogMessage const& message)
{
+ bool exceedMaxSize = maxFileSize > 0 && (fileSize + message.Size()) > maxFileSize;
+
if (dynamicName)
{
char namebuf[TRINITY_PATH_MAX];
snprintf(namebuf, TRINITY_PATH_MAX, filename.c_str(), message.param1.c_str());
- logfile = OpenFile(namebuf, mode, backup);
+ logfile = OpenFile(namebuf, mode, backup || exceedMaxSize);
}
+ else if (exceedMaxSize)
+ logfile = OpenFile(filename, "w", true);
- if (logfile)
- {
- fprintf(logfile, "%s%s", message.prefix.c_str(), message.text.c_str());
- fflush(logfile);
+ if (!logfile)
+ return;
- if (dynamicName)
- {
- fclose(logfile);
- logfile = NULL;
- }
- }
+ fprintf(logfile, "%s%s", message.prefix.c_str(), message.text.c_str());
+ fflush(logfile);
+ fileSize += message.Size();
+
+ if (dynamicName)
+ CloseFile();
}
FILE* AppenderFile::OpenFile(std::string const &filename, std::string const &mode, bool backup)
{
- if (mode == "w" && backup)
+ std::string fullName(logDir + filename);
+ if (backup)
{
- std::string newName(filename);
+ CloseFile();
+ std::string newName(fullName);
newName.push_back('.');
newName.append(LogMessage::getTimeStr(time(NULL)));
- rename(filename.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore
+ rename(fullName.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore
}
- return fopen((logDir + filename).c_str(), mode.c_str());
+ if (FILE* ret = fopen(fullName.c_str(), mode.c_str()))
+ {
+ fileSize = ftell(ret);
+ return ret;
+ }
+
+ return NULL;
+}
+
+void AppenderFile::CloseFile()
+{
+ if (logfile)
+ {
+ fclose(logfile);
+ logfile = NULL;
+ }
}
diff --git a/src/server/shared/Logging/AppenderFile.h b/src/server/shared/Logging/AppenderFile.h
index a3fe285cc7d..c15974799e1 100644
--- a/src/server/shared/Logging/AppenderFile.h
+++ b/src/server/shared/Logging/AppenderFile.h
@@ -23,11 +23,12 @@
class AppenderFile: public Appender
{
public:
- AppenderFile(uint8 _id, std::string const& _name, LogLevel level, const char* filename, const char* logDir, const char* mode, AppenderFlags flags);
+ AppenderFile(uint8 _id, std::string const& _name, LogLevel level, const char* filename, const char* logDir, const char* mode, AppenderFlags flags, uint64 maxSize);
~AppenderFile();
FILE* OpenFile(std::string const& _name, std::string const& _mode, bool _backup);
private:
+ void CloseFile();
void _write(LogMessage const& message);
FILE* logfile;
std::string filename;
@@ -35,6 +36,8 @@ class AppenderFile: public Appender
std::string mode;
bool dynamicName;
bool backup;
+ uint64 maxFileSize;
+ uint64 fileSize;
};
#endif
diff --git a/src/server/shared/Logging/Log.cpp b/src/server/shared/Logging/Log.cpp
index 96c72b5eb74..920ce4ce570 100644
--- a/src/server/shared/Logging/Log.cpp
+++ b/src/server/shared/Logging/Log.cpp
@@ -89,8 +89,9 @@ void Log::CreateAppenderFromConfig(const char* name)
options = ConfigMgr::GetStringDefault(options.c_str(), "");
Tokenizer tokens(options, ',');
Tokenizer::const_iterator iter = tokens.begin();
+ uint8 size = tokens.size();
- if (tokens.size() < 2)
+ if (size < 2)
{
fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong configuration for appender %s. Config line: %s\n", name, options.c_str());
return;
@@ -98,16 +99,15 @@ void Log::CreateAppenderFromConfig(const char* name)
AppenderFlags flags = APPENDER_FLAGS_NONE;
AppenderType type = AppenderType(atoi(*iter));
- ++iter;
- LogLevel level = LogLevel(atoi(*iter));
+ LogLevel level = LogLevel(atoi(*(++iter)));
if (level > LOG_LEVEL_FATAL)
{
fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level %d for appender %s\n", level, name);
return;
}
- if (++iter != tokens.end())
- flags = AppenderFlags(atoi(*iter));
+ if (size > 2)
+ flags = AppenderFlags(atoi(*(++iter)));
switch (type)
{
@@ -115,8 +115,8 @@ void Log::CreateAppenderFromConfig(const char* name)
{
AppenderConsole* appender = new AppenderConsole(NextAppenderId(), name, level, flags);
appenders[appender->getId()] = appender;
- if (++iter != tokens.end())
- appender->InitColors(*iter);
+ if (size > 3)
+ appender->InitColors(*(++iter));
//fprintf(stdout, "Log::CreateAppenderFromConfig: Created Appender %s (%u), Type CONSOLE, Mask %u\n", appender->getName().c_str(), appender->getId(), appender->getLogLevel()); // DEBUG - RemoveMe
break;
}
@@ -125,16 +125,16 @@ void Log::CreateAppenderFromConfig(const char* name)
std::string filename;
std::string mode = "a";
- if (++iter == tokens.end())
+ if (size < 4)
{
fprintf(stderr, "Log::CreateAppenderFromConfig: Missing file name for appender %s\n", name);
return;
}
- filename = *iter;
+ filename = *(++iter);
- if (++iter != tokens.end())
- mode = *iter;
+ if (size > 4)
+ mode = *(++iter);
if (flags & APPENDER_FLAGS_USE_TIMESTAMP)
{
@@ -145,8 +145,12 @@ void Log::CreateAppenderFromConfig(const char* name)
filename += m_logsTimestamp;
}
+ uint64 maxFileSize = 0;
+ if (size > 5)
+ maxFileSize = atoi(*(++iter));
+
uint8 id = NextAppenderId();
- appenders[id] = new AppenderFile(id, name, level, filename.c_str(), m_logsDir.c_str(), mode.c_str(), flags);
+ appenders[id] = new AppenderFile(id, name, level, filename.c_str(), m_logsDir.c_str(), mode.c_str(), flags, maxFileSize);
//fprintf(stdout, "Log::CreateAppenderFromConfig: Created Appender %s (%u), Type FILE, Mask %u, File %s, Mode %s\n", name, id, level, filename.c_str(), mode.c_str()); // DEBUG - RemoveMe
break;
}
diff --git a/src/server/shared/Memory.h b/src/server/shared/Memory.h
new file mode 100644
index 00000000000..25533638915
--- /dev/null
+++ b/src/server/shared/Memory.h
@@ -0,0 +1,19 @@
+
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#include "DetourAlloc.h"
+
+// memory management
+inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/)
+{
+ return (void*)new unsigned char[size];
+}
+
+inline void dtCustomFree(void* ptr)
+{
+ delete [] (unsigned char*)ptr;
+}
+
+#endif \ No newline at end of file
diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h
index 68d86406faa..c0a5daedf25 100644
--- a/src/server/shared/Packets/ByteBuffer.h
+++ b/src/server/shared/Packets/ByteBuffer.h
@@ -523,6 +523,8 @@ class ByteBuffer
return *this;
}
+ uint8 * contents() { return &_storage[0]; }
+
const uint8 *contents() const { return &_storage[0]; }
size_t size() const { return _storage.size(); }
diff --git a/src/server/shared/Utilities/Util.cpp b/src/server/shared/Utilities/Util.cpp
index 8b6862fa760..32ff396e1c1 100644
--- a/src/server/shared/Utilities/Util.cpp
+++ b/src/server/shared/Utilities/Util.cpp
@@ -22,7 +22,6 @@
#include "SFMT.h"
#include "Errors.h" // for ASSERT
#include <ace/TSS_T.h>
-#include <ace/INET_Addr.h>
typedef ACE_TSS<SFMTRand> SFMTRandTSS;
static SFMTRandTSS sfmtRand;
@@ -239,6 +238,21 @@ bool IsIPAddress(char const* ipaddress)
return inet_addr(ipaddress) != INADDR_NONE;
}
+std::string GetAddressString(ACE_INET_Addr const& addr)
+{
+ char buf[ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16];
+ addr.addr_to_string(buf, ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16);
+ return buf;
+}
+
+bool IsIPAddrInNetwork(ACE_INET_Addr const& net, ACE_INET_Addr const& addr, ACE_INET_Addr const& subnetMask)
+{
+ uint32 mask = subnetMask.get_ip_address();
+ if ((net.get_ip_address() & mask) == (addr.get_ip_address() & mask))
+ return true;
+ return false;
+}
+
/// create PID file
uint32 CreatePIDFile(const std::string& filename)
{
diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h
index 5a85e103e2a..5d1ce862d21 100644
--- a/src/server/shared/Utilities/Util.h
+++ b/src/server/shared/Utilities/Util.h
@@ -25,6 +25,7 @@
#include <string>
#include <vector>
#include <list>
+#include <ace/INET_Addr.h>
// Searcher for map of structs
template<typename T, class S> struct Finder
@@ -343,6 +344,13 @@ void utf8printf(FILE* out, const char *str, ...);
void vutf8printf(FILE* out, const char *str, va_list* ap);
bool IsIPAddress(char const* ipaddress);
+
+/// Checks if address belongs to the a network with specified submask
+bool IsIPAddrInNetwork(ACE_INET_Addr const& net, ACE_INET_Addr const& addr, ACE_INET_Addr const& subnetMask);
+
+/// Transforms ACE_INET_Addr address into string format "dotted_ip:port"
+std::string GetAddressString(ACE_INET_Addr const& addr);
+
uint32 CreatePIDFile(const std::string& filename);
std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false);
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 81442a28da2..16b48287ead 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -39,6 +39,7 @@ endif()
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/gsoap
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/SFMT
@@ -162,6 +163,7 @@ target_link_libraries(worldserver
collision
g3dlib
gsoap
+ Detour
${JEMALLOC_LIBRARY}
${READLINE_LIBRARY}
${TERMCAP_LIBRARY}
diff --git a/src/server/worldserver/RemoteAccess/RASocket.cpp b/src/server/worldserver/RemoteAccess/RASocket.cpp
index e0f4e7f0de6..b939f267233 100644
--- a/src/server/worldserver/RemoteAccess/RASocket.cpp
+++ b/src/server/worldserver/RemoteAccess/RASocket.cpp
@@ -49,14 +49,14 @@ int RASocket::open(void *)
return -1;
}
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Incoming connection from %s", remote_addr.get_host_addr());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "Incoming connection from %s", remote_addr.get_host_addr());
return activate();
}
int RASocket::handle_close(ACE_HANDLE, ACE_Reactor_Mask)
{
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Closing connection");
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "Closing connection");
peer().close_reader();
wait();
destroy();
@@ -142,7 +142,7 @@ int RASocket::process_command(const std::string& command)
if (command.length() == 0)
return 0;
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Got command: %s", command.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "Received command: %s", command.c_str());
// handle quit, exit and logout commands to terminate connection
if (command == "quit" || command == "exit" || command == "logout") {
@@ -184,15 +184,13 @@ int RASocket::check_access_level(const std::string& user)
AccountMgr::normalizeString(safeUser);
-
-
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS);
stmt->setString(0, safeUser);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (!result)
{
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "User %s does not exist in database", user.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "User %s does not exist in database", user.c_str());
return -1;
}
@@ -200,12 +198,12 @@ int RASocket::check_access_level(const std::string& user)
if (fields[1].GetUInt8() < _minLevel)
{
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "User %s has no privilege to login", user.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "User %s has no privilege to login", user.c_str());
return -1;
}
else if (fields[2].GetInt32() != -1)
{
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "User %s has to be assigned on all realms (with RealmID = '-1')", user.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "User %s has to be assigned on all realms (with RealmID = '-1')", user.c_str());
return -1;
}
@@ -231,7 +229,7 @@ int RASocket::check_password(const std::string& user, const std::string& pass)
if (!result)
{
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Wrong password for user: %s", user.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "Wrong password for user: %s", user.c_str());
return -1;
}
@@ -254,7 +252,7 @@ int RASocket::authenticate()
if (recv_line(pass) == -1)
return -1;
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Login attempt for user: %s", user.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "Login attempt for user: %s", user.c_str());
if (check_access_level(user) == -1)
return -1;
@@ -262,7 +260,7 @@ int RASocket::authenticate()
if (check_password(user, pass) == -1)
return -1;
- sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "User login: %s", user.c_str());
+ sLog->outInfo(LOG_FILTER_REMOTECOMMAND, "User login: %s", user.c_str());
return 0;
}
@@ -388,7 +386,8 @@ void RASocket::zprint(void* callbackArg, const char * szText)
ACE_Message_Block* mb = new ACE_Message_Block(sz);
mb->copy(szText, sz);
- if (socket->putq(mb, const_cast<ACE_Time_Value*>(&ACE_Time_Value::zero)) == -1)
+ ACE_Time_Value tv = ACE_Time_Value::zero;
+ if (socket->putq(mb, &tv) == -1)
{
sLog->outDebug(LOG_FILTER_REMOTECOMMAND, "Failed to enqueue message, queue is full or closed. Error is %s", ACE_OS::strerror(errno));
mb->release();
diff --git a/src/server/worldserver/TCSoap/TCSoap.cpp b/src/server/worldserver/TCSoap/TCSoap.cpp
index 5d578a19124..7d460da4f83 100644
--- a/src/server/worldserver/TCSoap/TCSoap.cpp
+++ b/src/server/worldserver/TCSoap/TCSoap.cpp
@@ -78,33 +78,33 @@ int ns1__executeCommand(soap* soap, char* command, char** result)
// security check
if (!soap->userid || !soap->passwd)
{
- sLog->outDebug(LOG_FILTER_SOAP, "Client didn't provide login information");
+ sLog->outInfo(LOG_FILTER_SOAP, "Client didn't provide login information");
return 401;
}
uint32 accountId = AccountMgr::GetId(soap->userid);
if (!accountId)
{
- sLog->outDebug(LOG_FILTER_SOAP, "Client used invalid username '%s'", soap->userid);
+ sLog->outInfo(LOG_FILTER_SOAP, "Client used invalid username '%s'", soap->userid);
return 401;
}
if (!AccountMgr::CheckPassword(accountId, soap->passwd))
{
- sLog->outDebug(LOG_FILTER_SOAP, "Invalid password for account '%s'", soap->userid);
+ sLog->outInfo(LOG_FILTER_SOAP, "Invalid password for account '%s'", soap->userid);
return 401;
}
if (AccountMgr::GetSecurity(accountId) < SEC_ADMINISTRATOR)
{
- sLog->outDebug(LOG_FILTER_SOAP, "%s's gmlevel is too low", soap->userid);
+ sLog->outInfo(LOG_FILTER_SOAP, "%s's gmlevel is too low", soap->userid);
return 403;
}
if (!command || !*command)
- return soap_sender_fault(soap, "Command mustn't be empty", "The supplied command was an empty string");
+ return soap_sender_fault(soap, "Command can not be empty", "The supplied command was an empty string");
- sLog->outDebug(LOG_FILTER_SOAP, "Got command '%s'", command);
+ sLog->outInfo(LOG_FILTER_SOAP, "Received command '%s'", command);
SOAPCommand connection;
// commands are executed in the world thread. We have to wait for them to be completed
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index f241b4d1b23..35d37d97f79 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -270,6 +270,14 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
#
+# mmap.enablePathFinding
+# Description: Enable/Disable pathfinding using mmaps - experimental
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+mmap.enablePathFinding = 0
+
+#
# vmap.enableLOS
# vmap.enableHeight
# Description: VMmap support for line of sight and height calculation.
@@ -281,14 +289,6 @@ vmap.enableLOS = 1
vmap.enableHeight = 1
#
-# vmap.petLOS
-# Description: Check line of sight for pets, to avoid them attacking through walls.
-# Default: 1 - (Enabled, each pet attack will be checked for line of sight)
-# 0 - (Disabled, somewhat less CPU usage)
-
-vmap.petLOS = 1
-
-#
# vmap.enableIndoorCheck
# Description: VMap based indoor check to remove outdoor-only auras (mounts etc.).
# Default: 1 - (Enabled)
@@ -787,18 +787,6 @@ RecruitAFriend.MaxLevel = 80
RecruitAFriend.MaxDifference = 4
#
-# InstantLogout
-# Description: Required security level for instantly logging out everywhere.
-# Does not work while in combat, dueling or falling.
-# Default: 1 - (Enabled, Mods/GMs/Admins)
-# 0 - (Enabled, Everyone)
-# 2 - (Enabled, GMs/Admins)
-# 3 - (Enabled, Admins)
-# 4 - (Disabled)
-
-InstantLogout = 1
-
-#
# DisableWaterBreath
# Description: Required security level for water breathing.
# Default: 4 - (Disabled)
@@ -1542,14 +1530,6 @@ ChatLevelReq.Whisper = 1
ChatLevelReq.Say = 1
#
-# AllowPlayerCommands
-# Description: Allow players to use commands.
-# Default: 1 - (Enabled)
-# 0 - (Disabled)
-
-AllowPlayerCommands = 1
-
-#
# PreserveCustomChannels
# Description: Store custom chat channel settings like password, automatic ownership handout
# or ban list in the database. Needs to be enabled to save custom
@@ -2620,6 +2600,14 @@ PlayerDump.DisallowPaths = 1
PlayerDump.DisallowOverwrite = 1
#
+# UI.ShowQuestLevelsInDialogs
+# Description: Show quest levels next to quest titles in UI dialogs
+# Example: [13] Westfall Stew
+# Default: 0 (do not show)
+
+UI.ShowQuestLevelsInDialogs = 0
+
+#
###################################################################################################
###################################################################################################
@@ -2629,7 +2617,7 @@ PlayerDump.DisallowOverwrite = 1
# Appender config values: Given a appender "name"
# Appender.name
# Description: Defines 'where to log'
-# Format: Type,LogLevel,Flags,optional1,optional2
+# Format: Type,LogLevel,Flags,optional1,optional2,optional3
#
# Type
# 0 - (None)
@@ -2680,6 +2668,13 @@ PlayerDump.DisallowOverwrite = 1
# a - (Append)
# w - (Overwrite)
#
+# MaxFileSize: Maximum file size of the log file before creating a new log file
+# (read as optional3 if Type = File)
+# Size is measured in bytes expressed in a 64-bit unsigned integer.
+# Maximum value is 4294967295 (4 gb). Leave blank for no limit.
+# NOTE: Does not work with dynamic filenames.
+# Example: 536870912 (512 mb)
+#
Appender.Console=1,3,0
Appender.Server=2,2,0,Server.log,w
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 0386f27d3df..671de25dfd3 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -11,3 +11,5 @@
add_subdirectory(map_extractor)
add_subdirectory(vmap4_assembler)
add_subdirectory(vmap4_extractor)
+add_subdirectory(mmaps_generator)
+# add_subdirectory(mesh_extractor)
diff --git a/src/tools/map_extractor/CMakeLists.txt b/src/tools/map_extractor/CMakeLists.txt
index 0bd2e2dbd0b..64d0573147c 100644
--- a/src/tools/map_extractor/CMakeLists.txt
+++ b/src/tools/map_extractor/CMakeLists.txt
@@ -18,6 +18,8 @@ include_directories (
${CMAKE_CURRENT_SOURCE_DIR}/loadlib
)
+include_directories(${include_Dirs})
+
add_executable(mapextractor
${sources}
)
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index c5241cad933..7b591ddfc85 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -326,7 +326,7 @@ void ReadLiquidTypeTableDBC()
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
SFileCloseFile(dbcFile);
- printf("Done! (%u LiqTypes loaded)\n", liqTypeCount);
+ printf("Done! (%u LiqTypes loaded)\n", (uint32)liqTypeCount);
}
//
@@ -335,7 +335,7 @@ void ReadLiquidTypeTableDBC()
// Map file format data
static char const* MAP_MAGIC = "MAPS";
-static char const* MAP_VERSION_MAGIC = "v1.2";
+static char const* MAP_VERSION_MAGIC = "v1.3";
static char const* MAP_AREA_MAGIC = "AREA";
static char const* MAP_HEIGHT_MAGIC = "MHGT";
static char const* MAP_LIQUID_MAGIC = "MLIQ";
@@ -351,6 +351,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -884,9 +886,38 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.liquidMapSize += sizeof(float)*liquidHeader.width*liquidHeader.height;
}
+ // map hole info
+ uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
+
+ if (map.liquidMapOffset)
+ map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
+ else
+ map.holesOffset = map.heightMapOffset + map.heightMapSize;
+
+ memset(holes, 0, sizeof(holes));
+ bool hasHoles = false;
+
+ for (int i = 0; i < ADT_CELLS_PER_GRID; ++i)
+ {
+ for (int j = 0; j < ADT_CELLS_PER_GRID; ++j)
+ {
+ adt_MCNK * cell = adt.cells[i][j];
+ if (!cell)
+ continue;
+ holes[i][j] = cell->holes;
+ if (!hasHoles && cell->holes != 0)
+ hasHoles = true;
+ }
+ }
+
+ if (hasHoles)
+ map.holesSize = sizeof(holes);
+ else
+ map.holesSize = 0;
+
// Ok all data prepared - store it
- FILE *output=fopen(filename2, "wb");
- if(!output)
+ FILE* output = fopen(filename2, "wb");
+ if (!output)
{
printf("Can't create the output file '%s'\n", filename2);
return false;
@@ -934,6 +965,11 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
fwrite(&liquid_height[y + liquidHeader.offsetY][liquidHeader.offsetX], sizeof(float), liquidHeader.width, output);
}
}
+
+ // store hole data
+ if (hasHoles)
+ fwrite(holes, map.holesSize, 1, output);
+
fclose(output);
return true;
diff --git a/src/tools/map_extractor/adt.cpp b/src/tools/map_extractor/adt.cpp
index bb1e3bfcc45..2b7c19000cd 100644
--- a/src/tools/map_extractor/adt.cpp
+++ b/src/tools/map_extractor/adt.cpp
@@ -6,6 +6,13 @@
int holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
int holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
+u_map_fcc MHDRMagic = { {'R','D','H','M'} };
+u_map_fcc MCINMagic = { {'N','I','C','M'} };
+u_map_fcc MH2OMagic = { {'O','2','H','M'} };
+u_map_fcc MCNKMagic = { {'K','N','C','M'} };
+u_map_fcc MCVTMagic = { {'T','V','C','M'} };
+u_map_fcc MCLQMagic = { {'Q','L','C','M'} };
+
bool isHole(int holes, int i, int j)
{
int testi = i / 2;
@@ -74,7 +81,7 @@ bool ADT_file::prepareLoadedData()
bool adt_MHDR::prepareLoadedData()
{
- if (fcc != 'MHDR')
+ if (fcc != MHDRMagic.fcc)
return false;
if (size!=sizeof(adt_MHDR)-8)
@@ -93,7 +100,7 @@ bool adt_MHDR::prepareLoadedData()
bool adt_MCIN::prepareLoadedData()
{
- if (fcc != 'MCIN')
+ if (fcc != MCINMagic.fcc)
return false;
// Check cells data
@@ -107,7 +114,7 @@ bool adt_MCIN::prepareLoadedData()
bool adt_MH2O::prepareLoadedData()
{
- if (fcc != 'MH2O')
+ if (fcc != MH2OMagic.fcc)
return false;
// Check liquid data
@@ -119,7 +126,7 @@ bool adt_MH2O::prepareLoadedData()
bool adt_MCNK::prepareLoadedData()
{
- if (fcc != 'MCNK')
+ if (fcc != MCNKMagic.fcc)
return false;
// Check height map
@@ -134,7 +141,7 @@ bool adt_MCNK::prepareLoadedData()
bool adt_MCVT::prepareLoadedData()
{
- if (fcc != 'MCVT')
+ if (fcc != MCVTMagic.fcc)
return false;
if (size != sizeof(adt_MCVT)-8)
@@ -145,8 +152,8 @@ bool adt_MCVT::prepareLoadedData()
bool adt_MCLQ::prepareLoadedData()
{
- if (fcc != 'MCLQ')
+ if (fcc != MCLQMagic.fcc)
return false;
return true;
-} \ No newline at end of file
+}
diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp
index 0e2112b2f26..6a47f2d2a30 100644
--- a/src/tools/map_extractor/loadlib.cpp
+++ b/src/tools/map_extractor/loadlib.cpp
@@ -3,6 +3,8 @@
#include "loadlib.h"
#include <cstdio>
+u_map_fcc MverMagic = { {'R','E','V','M'} };
+
FileLoader::FileLoader()
{
data = 0;
@@ -48,7 +50,7 @@ bool FileLoader::prepareLoadedData()
{
// Check version
version = (file_MVER *) data;
- if (version->fcc != 'MVER')
+ if (version->fcc != MverMagic.fcc)
return false;
if (version->ver != FILE_FORMAT_VERSION)
return false;
diff --git a/src/tools/map_extractor/loadlib/loadlib.h b/src/tools/map_extractor/loadlib/loadlib.h
index 7a158ddfcf1..dd8a205a7b2 100644
--- a/src/tools/map_extractor/loadlib/loadlib.h
+++ b/src/tools/map_extractor/loadlib/loadlib.h
@@ -31,6 +31,12 @@ typedef uint8_t uint8;
#define FILE_FORMAT_VERSION 18
+union u_map_fcc
+{
+ char fcc_txt[4];
+ uint32 fcc;
+};
+
//
// File version chunk
//
diff --git a/src/tools/map_extractor/wdt.cpp b/src/tools/map_extractor/wdt.cpp
index 6c2fa337d4f..f9f1d8a163c 100644
--- a/src/tools/map_extractor/wdt.cpp
+++ b/src/tools/map_extractor/wdt.cpp
@@ -2,23 +2,27 @@
#include "wdt.h"
+u_map_fcc MWMOMagic = { {'O', 'M', 'W', 'M'} };
+u_map_fcc MPHDMagic = { {'D', 'H', 'P', 'M'} };
+u_map_fcc MAINMagic = { {'N', 'I', 'A', 'M'} };
+
bool wdt_MWMO::prepareLoadedData()
{
- if (fcc != 'MWMO')
+ if (fcc != MWMOMagic.fcc)
return false;
return true;
}
bool wdt_MPHD::prepareLoadedData()
{
- if (fcc != 'MPHD')
+ if (fcc != MPHDMagic.fcc)
return false;
return true;
}
bool wdt_MAIN::prepareLoadedData()
{
- if (fcc != 'MAIN')
+ if (fcc != MAINMagic.fcc)
return false;
return true;
}
@@ -59,4 +63,4 @@ bool WDT_file::prepareLoadedData()
if (!wmo->prepareLoadedData())
wmo = NULL; // optional as of cataclysm
return true;
-} \ No newline at end of file
+}
diff --git a/src/tools/mesh_extractor/ADT.cpp b/src/tools/mesh_extractor/ADT.cpp
new file mode 100644
index 00000000000..c2ac19d5be0
--- /dev/null
+++ b/src/tools/mesh_extractor/ADT.cpp
@@ -0,0 +1,53 @@
+#include "ADT.h"
+#include "DoodadHandler.h"
+#include "LiquidHandler.h"
+#include "WorldModelHandler.h"
+
+ADT::ADT( std::string file ) : ObjectData(NULL), Data(NULL), HasObjectData(false),
+ _DoodadHandler(NULL), _WorldModelHandler(NULL), _LiquidHandler(NULL)
+{
+ Data = new ChunkedData(file);
+ ObjectData = new ChunkedData(Utils::Replace(file, ".adt", "_obj0.adt"));
+ if (ObjectData->Stream)
+ HasObjectData = true;
+ else
+ ObjectData = NULL;
+}
+
+ADT::~ADT()
+{
+ delete ObjectData;
+ delete Data;
+
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ delete *itr;
+
+ MapChunks.clear();
+ delete _DoodadHandler;
+ delete _WorldModelHandler;
+ delete _LiquidHandler;
+}
+
+void ADT::Read()
+{
+ Header.Read(Data->GetChunkByName("MHDR")->GetStream());
+ MapChunks.reserve(16 * 16);
+
+ for (std::vector<Chunk*>::iterator itr = Data->Chunks.begin(); itr != Data->Chunks.end(); ++itr)
+ if ((*itr)->Name == "MCNK")
+ MapChunks.push_back(new MapChunk(this, *itr));
+
+ _LiquidHandler = new LiquidHandler(this);
+
+ // do this separate from map chunk initialization to access liquid data
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ (*itr)->GenerateTriangles();
+
+ _DoodadHandler = new DoodadHandler(this);
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ _DoodadHandler->ProcessMapChunk(*itr);
+
+ _WorldModelHandler = new WorldModelHandler(this);
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ _WorldModelHandler->ProcessMapChunk(*itr);
+}
diff --git a/src/tools/mesh_extractor/ADT.h b/src/tools/mesh_extractor/ADT.h
new file mode 100644
index 00000000000..133596eb024
--- /dev/null
+++ b/src/tools/mesh_extractor/ADT.h
@@ -0,0 +1,29 @@
+#ifndef ADT_H
+#define ADT_H
+#include "ChunkedData.h"
+#include "MapChunk.h"
+
+class DoodadHandler;
+class WorldModelHandler;
+class LiquidHandler;
+
+class ADT
+{
+public:
+ ADT(std::string file);
+ ~ADT();
+
+ void Read();
+
+ ChunkedData* ObjectData;
+ ChunkedData* Data;
+ std::vector<MapChunk*> MapChunks;
+ MHDR Header;
+ // Can we dispose of this?
+ bool HasObjectData;
+
+ DoodadHandler* _DoodadHandler;
+ WorldModelHandler* _WorldModelHandler;
+ LiquidHandler* _LiquidHandler;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/CMakeLists.txt b/src/tools/mesh_extractor/CMakeLists.txt
new file mode 100644
index 00000000000..9ed8472051d
--- /dev/null
+++ b/src/tools/mesh_extractor/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2005-2009 MaNGOS project <http://getmangos.com/>
+# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE meshExtract_Sources *.cpp *.h)
+
+set(include_Base
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${ACE_INCLUDE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+if( WIN32 )
+ set(include_Base
+ ${include_Base}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq/win
+ )
+endif()
+
+include_directories(${include_Base})
+
+add_executable(MeshExtractor ${meshExtract_Sources})
+
+target_link_libraries(MeshExtractor
+ g3dlib
+ mpq
+ Recast
+ Detour
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ ${ACE_LIBRARY}
+)
+
+if( UNIX )
+ install(TARGETS MeshExtractor DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS MeshExtractor DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mesh_extractor/Cache.h b/src/tools/mesh_extractor/Cache.h
new file mode 100644
index 00000000000..60e3d8434cf
--- /dev/null
+++ b/src/tools/mesh_extractor/Cache.h
@@ -0,0 +1,63 @@
+#ifndef CACHE_H
+#define CACHE_H
+#include <string>
+#include <map>
+#include "Define.h"
+#include <ace/Guard_T.h>
+#include <ace/Synch.h>
+
+class WorldModelRoot;
+class Model;
+
+template<class K, class T>
+class GenericCache
+{
+public:
+ GenericCache() {}
+
+ static const uint32 FlushLimit = 1000;
+
+ void Insert(K key, T* val)
+ {
+ ACE_GUARD(ACE_Thread_Mutex, g, mutex);
+
+ if (_items.size() > FlushLimit)
+ Clear();
+ _items[key] = val;
+ }
+
+ T* Get(K key)
+ {
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ typename std::map<K, T*>::iterator itr = _items.find(key);
+ if (itr != _items.end())
+ return itr->second;
+ return NULL;
+ }
+
+ void Clear()
+ {
+ for (typename std::map<K, T*>::iterator itr = _items.begin(); itr != _items.end(); ++itr)
+ delete itr->second;
+ _items.clear();
+ }
+private:
+ std::map<K, T*> _items;
+ ACE_Thread_Mutex mutex;
+};
+
+class CacheClass
+{
+public:
+ CacheClass() {}
+ GenericCache<std::string, Model> ModelCache;
+ GenericCache<std::string, WorldModelRoot> WorldModelCache;
+
+ void Clear()
+ {
+
+ }
+};
+
+extern CacheClass* Cache;
+#endif
diff --git a/src/tools/mesh_extractor/Chunk.cpp b/src/tools/mesh_extractor/Chunk.cpp
new file mode 100644
index 00000000000..4605ae0f0dd
--- /dev/null
+++ b/src/tools/mesh_extractor/Chunk.cpp
@@ -0,0 +1,31 @@
+#include "Chunk.h"
+#include "Utils.h"
+
+int32 Chunk::FindSubChunkOffset(std::string name)
+{
+ // Reverse the name
+ name = std::string(name.rbegin(), name.rend());
+ if (name.size() != 4)
+ return -1;
+
+ FILE* stream = GetStream();
+ uint32 matched = 0;
+ while (uint32(ftell(stream)) < Utils::Size(stream))
+ {
+ char b = 0;
+ if (fread(&b, sizeof(char), 1, stream) != 1 || b != name[matched])
+ matched = 0;
+ else
+ ++matched;
+
+ if (matched == 4)
+ return ftell(stream) - 4;
+ }
+ return -1;
+}
+
+FILE* Chunk::GetStream()
+{
+ fseek(Stream, Offset, SEEK_SET);
+ return Stream;
+}
diff --git a/src/tools/mesh_extractor/Chunk.h b/src/tools/mesh_extractor/Chunk.h
new file mode 100644
index 00000000000..f3681a9f576
--- /dev/null
+++ b/src/tools/mesh_extractor/Chunk.h
@@ -0,0 +1,20 @@
+#ifndef CHUNK_H
+#define CHUNK_H
+#include "Define.h"
+#include <string>
+class ChunkedData;
+
+class Chunk
+{
+public:
+ Chunk(const char* name, uint32 length, uint32 offset, FILE* stream) : Name(name), Length(length), Offset(offset), Stream(stream) {}
+
+ int32 FindSubChunkOffset(std::string name);
+ FILE* GetStream();
+ std::string Name;
+ uint32 Length;
+ uint32 Offset;
+ FILE* Stream;
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/ChunkedData.cpp b/src/tools/mesh_extractor/ChunkedData.cpp
new file mode 100644
index 00000000000..e0db12a6be7
--- /dev/null
+++ b/src/tools/mesh_extractor/ChunkedData.cpp
@@ -0,0 +1,74 @@
+#include "ChunkedData.h"
+#include "MPQManager.h"
+#include "Utils.h"
+
+#include <string>
+
+ChunkedData::ChunkedData( FILE* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) :
+Stream(stream)
+{
+ if (!Stream)
+ return;
+ Load(maxLength, chunksHint);
+}
+
+ChunkedData::ChunkedData( std::string file, uint32 chunksHint /*= 300*/ )
+{
+ Stream = MPQHandler->GetFile(file);
+ if (!Stream)
+ return;
+ Load(0, chunksHint);
+}
+
+void ChunkedData::Load( uint32 maxLength, uint32 chunksHint )
+{
+ if (!maxLength)
+ maxLength = Utils::Size(Stream);
+ Chunks.reserve(chunksHint);
+ uint32 baseOffset = ftell(Stream);
+ uint32 calcOffset = 0;
+ while ((calcOffset + baseOffset) < Utils::Size(Stream) && (calcOffset < maxLength))
+ {
+ char nameBytes[5];
+ uint32 read = fread(&nameBytes, sizeof(char), 4, Stream);
+ nameBytes[read] = '\0';
+ std::string name = std::string(nameBytes);
+ // Utils::Reverse(nameBytes);
+ name = std::string(name.rbegin(), name.rend());
+ uint32 length;
+ if (fread(&length, sizeof(uint32), 1, Stream) != 1)
+ continue;
+ calcOffset += 8;
+ Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, Stream));
+ calcOffset += length;
+ // save an extra seek at the end
+ if ((calcOffset + baseOffset) < Utils::Size(Stream) && calcOffset < maxLength)
+ fseek(Stream, length, SEEK_CUR);
+ }
+}
+
+int ChunkedData::GetFirstIndex( std::string name )
+{
+ for (uint32 i = 0; i < Chunks.size(); ++i)
+ if (Chunks[i]->Name == name)
+ return i;
+ return -1;
+}
+
+Chunk* ChunkedData::GetChunkByName( std::string name )
+{
+ for (uint32 i = 0; i < Chunks.size(); ++i)
+ if (Chunks[i]->Name == name)
+ return Chunks[i];
+ return NULL;
+}
+
+ChunkedData::~ChunkedData()
+{
+ for (std::vector<Chunk*>::iterator itr = Chunks.begin(); itr != Chunks.end(); ++itr)
+ delete *itr;
+
+ Chunks.clear();
+ if (Stream)
+ fclose(Stream);
+}
diff --git a/src/tools/mesh_extractor/ChunkedData.h b/src/tools/mesh_extractor/ChunkedData.h
new file mode 100644
index 00000000000..e23648c845e
--- /dev/null
+++ b/src/tools/mesh_extractor/ChunkedData.h
@@ -0,0 +1,21 @@
+#ifndef CHNK_H
+#define CHNK_H
+
+#include <vector>
+#include "Chunk.h"
+
+class ChunkedData
+{
+public:
+ ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300);
+ ChunkedData(std::string file, uint32 chunksHint = 300);
+ ~ChunkedData();
+
+ int GetFirstIndex(std::string name);
+ Chunk* GetChunkByName(std::string name);
+
+ void Load(uint32 maxLength, uint32 chunksHint);
+ std::vector<Chunk*> Chunks;
+ FILE* Stream;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/Constants.h b/src/tools/mesh_extractor/Constants.h
new file mode 100644
index 00000000000..02e2d25559f
--- /dev/null
+++ b/src/tools/mesh_extractor/Constants.h
@@ -0,0 +1,57 @@
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+class Constants
+{
+public:
+ enum TriangleType
+ {
+ TRIANGLE_TYPE_UNKNOWN,
+ TRIANGLE_TYPE_TERRAIN,
+ TRIANGLE_TYPE_WATER,
+ TRIANGLE_TYPE_DOODAD,
+ TRIANGLE_TYPE_WMO
+ };
+
+ enum PolyArea
+ {
+ POLY_AREA_TERRAIN = 1,
+ POLY_AREA_WATER = 2,
+ POLY_AREA_ROAD = 3,
+ POLY_AREA_DANGER = 4,
+ };
+
+ enum PolyFlag
+ {
+ POLY_FLAG_WALK = 1,
+ POLY_FLAG_SWIM = 2,
+ POLY_FLAG_FLIGHTMASTER = 4
+ };
+
+ enum ExtractFlags
+ {
+ EXTRACT_FLAG_DBC = 0x01,
+ EXTRACT_FLAG_MAPS = 0x02,
+ EXTRACT_FLAG_VMAPS = 0x04,
+ EXTRACT_FLAG_GOB_MODELS = 0x08,
+ EXTRACT_FLAG_MMAPS = 0x10,
+ EXTRACT_FLAG_TEST = 0x20,
+ EXTRACT_FLAG_ALLOWED = EXTRACT_FLAG_DBC | EXTRACT_FLAG_MAPS | EXTRACT_FLAG_VMAPS | EXTRACT_FLAG_GOB_MODELS | EXTRACT_FLAG_MMAPS | EXTRACT_FLAG_TEST
+ };
+
+ static const float TileSize;
+ static const float MaxXY;
+ static const float ChunkSize;
+ static const float UnitSize;
+ static const float Origin[];
+ static const float PI;
+ static const float MaxStandableHeight;
+ static bool ToWoWCoords;
+ static const char* VMAPMagic;
+ static const float BaseUnitDim;
+ static const int VertexPerMap;
+ static const int VertexPerTile;
+ static const int TilesPerMap;
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp
new file mode 100644
index 00000000000..d6125bdd8e2
--- /dev/null
+++ b/src/tools/mesh_extractor/ContinentBuilder.cpp
@@ -0,0 +1,144 @@
+#include "ContinentBuilder.h"
+#include "TileBuilder.h"
+#include "WDT.h"
+#include "Utils.h"
+#include "DetourNavMesh.h"
+#include "Cache.h"
+#include "ace/Task.h"
+#include "Recast.h"
+
+class BuilderThread : public ACE_Task_Base
+{
+private:
+ int X, Y, MapId;
+ std::string Continent;
+ bool debug;
+ dtNavMeshParams Params;
+ ContinentBuilder* cBuilder;
+public:
+ BuilderThread(ContinentBuilder* _cBuilder, bool deb, dtNavMeshParams& params) : debug(deb), Params(params), cBuilder(_cBuilder), Free(true) {}
+ void SetData(int x, int y, int map, std::string cont) { X = x; Y = y; MapId = map; Continent = cont; }
+
+ int svc()
+ {
+ Free = false;
+ printf("[%02i,%02i] Building tile\n", X, Y);
+ TileBuilder builder(cBuilder, Continent, X, Y, MapId);
+ char buff[100];
+ sprintf(buff, "mmaps/%03u%02u%02u.mmtile", MapId, Y, X);
+ FILE* f = fopen(buff, "r");
+ if (f) // Check if file already exists.
+ {
+ printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y);
+ fclose(f);
+ Free = true;
+ return 0;
+ }
+ uint8* nav = builder.Build(debug, Params);
+ if (nav)
+ {
+ f = fopen(buff, "wb");
+ if (!f)
+ {
+ printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
+ return 0;
+ }
+ MmapTileHeader header;
+ header.size = builder.DataSize;
+ fwrite(&header, sizeof(MmapTileHeader), 1, f);
+ fwrite(nav, sizeof(unsigned char), builder.DataSize, f);
+ fclose(f);
+ }
+ dtFree(nav);
+ printf("[%02u,%02u] Tile Built!\n", X, Y);
+ Free = true;
+ return 0;
+ }
+
+ bool Free;
+};
+
+void ContinentBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+{
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * Constants::TileSize;
+ bmax[2] = (32 - int(tileY)) * Constants::TileSize;
+ bmin[0] = bmax[0] - Constants::TileSize;
+ bmin[2] = bmax[2] - Constants::TileSize;
+}
+
+void ContinentBuilder::CalculateTileBounds()
+{
+ for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
+ {
+ tileXMax = std::max(itr->X, tileXMax);
+ tileXMin = std::min(itr->X, tileXMin);
+
+ tileYMax = std::max(itr->Y, tileYMax);
+ tileYMin = std::min(itr->Y, tileYMin);
+ }
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+}
+
+void ContinentBuilder::Build(bool debug)
+{
+ char buff[50];
+ sprintf(buff, "mmaps/%03u.mmap", MapId);
+ FILE* mmap = fopen(buff, "wb");
+ if (!mmap)
+ {
+ printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
+ return;
+ }
+
+ CalculateTileBounds();
+
+ dtNavMeshParams params;
+ params.maxPolys = 1 << STATIC_POLY_BITS;
+ params.maxTiles = TileMap->TileTable.size();
+ rcVcopy(params.orig, bmin);
+ params.tileHeight = Constants::TileSize;
+ params.tileWidth = Constants::TileSize;
+ fwrite(&params, sizeof(dtNavMeshParams), 1, mmap);
+ fclose(mmap);
+ std::vector<BuilderThread*> Threads;
+ for (uint32 i = 0; i < NumberOfThreads; ++i)
+ Threads.push_back(new BuilderThread(this, debug, params));
+ printf("Map %s ( %i ) has %u tiles. Building them with %i threads\n", Continent.c_str(), MapId, uint32(TileMap->TileTable.size()), NumberOfThreads);
+ for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
+ {
+ bool next = false;
+ while (!next)
+ {
+ for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th)
+ {
+ if ((*_th)->Free)
+ {
+ (*_th)->SetData(itr->X, itr->Y, MapId, Continent);
+ (*_th)->activate();
+ next = true;
+ break;
+ }
+ }
+ // Wait for 20 seconds
+ ACE_OS::sleep(ACE_Time_Value (0, 20000));
+ }
+ }
+ Cache->Clear();
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+}
diff --git a/src/tools/mesh_extractor/ContinentBuilder.h b/src/tools/mesh_extractor/ContinentBuilder.h
new file mode 100644
index 00000000000..b36ca125b9e
--- /dev/null
+++ b/src/tools/mesh_extractor/ContinentBuilder.h
@@ -0,0 +1,30 @@
+#ifndef CONT_BUILDER_H
+#define CONT_BUILDER_H
+#include <string>
+#include "WDT.h"
+#include "Define.h"
+
+class ContinentBuilder
+{
+public:
+ ContinentBuilder(std::string continent, uint32 mapId, WDT* wdt, uint32 tn) :
+ Continent(continent), TileMap(wdt), MapId(mapId),
+ NumberOfThreads(tn), tileXMin(64), tileYMin(64), tileXMax(0), tileYMax(0)
+ {}
+
+ void Build(bool debug);
+ void getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax);
+ void CalculateTileBounds();
+ float bmin[3];
+ float bmax[3];
+private:
+ std::string Continent;
+ WDT* TileMap;
+ uint32 MapId;
+ uint32 NumberOfThreads;
+ int tileXMin;
+ int tileYMin;
+ int tileXMax;
+ int tileYMax;
+};
+#endif
diff --git a/src/tools/mesh_extractor/DBC.cpp b/src/tools/mesh_extractor/DBC.cpp
new file mode 100644
index 00000000000..9a55ff6d7ed
--- /dev/null
+++ b/src/tools/mesh_extractor/DBC.cpp
@@ -0,0 +1,70 @@
+#include <cstdio>
+#include "DBC.h"
+#include "Define.h"
+
+DBC::DBC( FILE* stream ) : StringBlock(NULL), StringBlockSize(0), IsFaulty(true)
+{
+ char magic[5];
+ uint32 count = 0;
+ count += fread(&magic, sizeof(char), 4, stream);
+ magic[4] = '\0';
+ count += fread(&RecordCount, sizeof(uint32), 1, stream);
+ Records.reserve(RecordCount);
+ count += fread(&Fields, sizeof(uint32), 1, stream);
+ count += fread(&RecordSize, sizeof(uint32), 1, stream);
+ count += fread(&StringBlockSize, sizeof(uint32), 1, stream);
+ if (count != 8)
+ printf("DBC::DBC: Failed to read some data expected 8, read %u\n", count);
+
+ for (int i = 0; i < RecordCount; i++)
+ {
+ Record* rec = new Record(this);
+ Records.push_back(rec);
+ int size = 0;
+ for (int f = 0; f < Fields; f++)
+ {
+ if (size + 4 > RecordSize)
+ {
+ IsFaulty = true;
+ break;
+ }
+ uint32 tmp;
+ if (fread(&tmp, sizeof(uint32), 1, stream) != 1)
+ printf("DBC::DBC: Failed to read some data expected 1, read 0\n");
+ rec->Values.push_back(tmp);
+ size += 4;
+ }
+ }
+ StringBlock = new uint8[StringBlockSize];
+ count = fread(StringBlock, sizeof(uint8), StringBlockSize, stream);
+ if (count != StringBlockSize)
+ printf("DBC::DBC: Failed to read some data expected %u, read %u\n", StringBlockSize, count);
+}
+
+std::string DBC::GetStringByOffset( int offset )
+{
+ int len = 0;
+ for (uint32 i = offset; i < StringBlockSize; i++)
+ {
+ if (!StringBlock[i])
+ {
+ len = (i - offset);
+ break;
+ }
+ }
+ char* d = new char[len+1];
+ strcpy(d, (const char*)(StringBlock + offset));
+ d[len] = '\0';
+ std::string val = std::string(d);
+ delete d;
+ return val;
+}
+
+Record* DBC::GetRecordById( int id )
+{
+ // we assume Id is index 0
+ for (std::vector<Record*>::iterator itr = Records.begin(); itr != Records.end(); ++itr)
+ if ((*itr)->Values[0] == id)
+ return *itr;
+ return NULL;
+}
diff --git a/src/tools/mesh_extractor/DBC.h b/src/tools/mesh_extractor/DBC.h
new file mode 100644
index 00000000000..5ed57754e73
--- /dev/null
+++ b/src/tools/mesh_extractor/DBC.h
@@ -0,0 +1,53 @@
+#ifndef DBC_H
+#define DBC_H
+#include <vector>
+#include <string>
+#include "Define.h"
+
+class Record;
+
+class DBC
+{
+public:
+ DBC(FILE* stream);
+
+ std::string GetStringByOffset(int offset);
+
+ Record* GetRecordById(int id);
+
+ std::string Name;
+ std::vector<Record*> Records;
+ int RecordCount;
+ int Fields;
+ int RecordSize;
+ uint8* StringBlock;
+ uint32 StringBlockSize;
+ bool IsFaulty;
+};
+
+class Record
+{
+public:
+ Record(DBC* dbc) : Source(dbc) {}
+
+ DBC* Source;
+ std::vector<int> Values;
+
+ int operator[](int index)
+ {
+ return Values[index];
+ }
+
+ template <typename T>
+ T GetValue(int index)
+ {
+ return *(T*)(&Values[index]);
+ }
+
+ std::string GetString(int index)
+ {
+ return Source->GetStringByOffset(Values[index]);
+ }
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/DoodadHandler.cpp b/src/tools/mesh_extractor/DoodadHandler.cpp
new file mode 100644
index 00000000000..56c2a7986f8
--- /dev/null
+++ b/src/tools/mesh_extractor/DoodadHandler.cpp
@@ -0,0 +1,109 @@
+#include "DoodadHandler.h"
+#include "Chunk.h"
+#include "Cache.h"
+#include "Model.h"
+#include "G3D/Matrix4.h"
+
+DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
+{
+ if (!adt->HasObjectData)
+ return;
+ Chunk* mddf = adt->ObjectData->GetChunkByName("MDDF");
+ if (mddf)
+ ReadDoodadDefinitions(mddf);
+
+ Chunk* mmid = adt->ObjectData->GetChunkByName("MMID");
+ Chunk* mmdx = adt->ObjectData->GetChunkByName("MMDX");
+ if (mmid && mmdx)
+ ReadDoodadPaths(mmid, mmdx);
+}
+
+void DoodadHandler::ProcessInternal( ChunkedData* subChunks )
+{
+ if (!IsSane())
+ return;
+ Chunk* doodadReferencesChunk = subChunks->GetChunkByName("MCRD");
+ if (!doodadReferencesChunk)
+ return;
+ FILE* stream = doodadReferencesChunk->GetStream();
+ uint32 refCount = doodadReferencesChunk->Length / 4;
+ for (uint32 i = 0; i < refCount; i++)
+ {
+ int32 index;
+ if (int count = fread(&index, sizeof(int32), 1, stream) != 1)
+ printf("DoodadHandler::ProcessInternal: Failed to read some data expected 1, read %d\n", count);
+ if (index < 0 || uint32(index) >= _definitions->size())
+ continue;
+ DoodadDefinition doodad = (*_definitions)[index];
+ if (_drawn.find(doodad.UniqueId) != _drawn.end())
+ continue;
+ _drawn.insert(doodad.UniqueId);
+ if (doodad.MmidIndex >= _paths->size())
+ continue;
+
+ std::string path = (*_paths)[doodad.MmidIndex];
+ Model* model = Cache->ModelCache.Get(path);
+ if (!model)
+ {
+ model = new Model(path);
+ Cache->ModelCache.Insert(path, model);
+ }
+ if (!model->IsCollidable)
+ continue;
+
+ Vertices.reserve(refCount * model->Vertices.size() * 0.2);
+ Triangles.reserve(refCount * model->Triangles.size() * 0.2);
+
+ InsertModelGeometry(doodad, model);
+ }
+}
+
+void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk )
+{
+ int32 count = chunk->Length / 36;
+ _definitions = new std::vector<DoodadDefinition>;
+ _definitions->reserve(count);
+ FILE* stream = chunk->GetStream();
+ for (int i = 0; i < count; i++)
+ {
+ DoodadDefinition def;
+ def.Read(stream);
+ _definitions->push_back(def);
+ }
+}
+
+void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data )
+{
+ int paths = id->Length / 4;
+ _paths = new std::vector<std::string>();
+ _paths->reserve(paths);
+ for (int i = 0; i < paths; i++)
+ {
+ FILE* idStream = id->GetStream();
+ fseek(idStream, i * 4, SEEK_CUR);
+ uint32 offset;
+ if (fread(&offset, sizeof(uint32), 1, idStream) != 1)
+ printf("DoodadHandler::ReadDoodadPaths: Failed to read some data expected 1, read 0\n");
+ FILE* dataStream = data->GetStream();
+ fseek(dataStream, offset + data->Offset, SEEK_SET);
+ _paths->push_back(Utils::ReadString(dataStream));
+ }
+}
+
+void DoodadHandler::InsertModelGeometry(const DoodadDefinition& def, Model* model)
+{
+ G3D::Matrix4 transformation = Utils::GetTransformation(def);
+ uint32 vertOffset = Vertices.size();
+
+ for (std::vector<Vector3>::iterator itr = model->Vertices.begin(); itr != model->Vertices.end(); ++itr)
+ Vertices.push_back(Utils::VectorTransform(*itr, transformation));
+
+ for (std::vector<Triangle<uint16> >::iterator itr = model->Triangles.begin(); itr != model->Triangles.end(); ++itr)
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_DOODAD, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
+}
+
+DoodadHandler::~DoodadHandler()
+{
+ delete _definitions;
+ delete _paths;
+}
diff --git a/src/tools/mesh_extractor/DoodadHandler.h b/src/tools/mesh_extractor/DoodadHandler.h
new file mode 100644
index 00000000000..96aecbcce27
--- /dev/null
+++ b/src/tools/mesh_extractor/DoodadHandler.h
@@ -0,0 +1,57 @@
+#ifndef DOOADHNDL_H
+#define DOOADHNDL_H
+#include "ObjectDataHandler.h"
+#include "Utils.h"
+#include "Chunk.h"
+#include "Model.h"
+#include <set>
+#include <vector>
+
+class DoodadDefinition : public IDefinition
+{
+public:
+ uint32 MmidIndex;
+ uint32 UniqueId;
+ uint16 DecimalScale;
+ uint16 Flags;
+
+ virtual float Scale() const { return DecimalScale / 1024.0f; }
+
+ void Read(FILE* stream)
+ {
+ int count = 0;
+
+ count += fread(&MmidIndex, sizeof(uint32), 1, stream);
+ count += fread(&UniqueId, sizeof(uint32), 1, stream);
+ Position = Vector3::Read(stream);
+ Rotation = Vector3::Read(stream);
+ count += fread(&DecimalScale, sizeof(uint16), 1, stream);
+ count += fread(&Flags, sizeof(uint16), 1, stream);
+ if (count != 4)
+ printf("DoodadDefinition::Read: Failed to read some data expected 4, read %d\n", count);
+ }
+};
+
+class DoodadHandler : public ObjectDataHandler
+{
+public:
+ DoodadHandler(ADT* adt);
+ ~DoodadHandler();
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool IsSane() { return _definitions && _paths; }
+
+
+protected:
+ void ProcessInternal(ChunkedData* chunk);
+
+private:
+ void ReadDoodadDefinitions(Chunk* chunk);
+ void ReadDoodadPaths(Chunk* id, Chunk* data);
+ void InsertModelGeometry(const DoodadDefinition& def, Model* model);
+ std::set<uint32> _drawn;
+ std::vector<DoodadDefinition>* _definitions;
+ std::vector<std::string>* _paths;
+};
+#endif
diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp
new file mode 100644
index 00000000000..2fc470e8e9f
--- /dev/null
+++ b/src/tools/mesh_extractor/Geometry.cpp
@@ -0,0 +1,123 @@
+#include "Geometry.h"
+#include "Constants.h"
+#include "ADT.h"
+#include "WorldModelHandler.h"
+#include "DoodadHandler.h"
+
+Geometry::Geometry() : Transform(false)
+{
+ Vertices.reserve(10000);
+ Triangles.reserve(10000);
+}
+
+void Geometry::CalculateBoundingBox( float*& min, float*& max )
+{
+ min = new float[3];
+ max = new float[3];
+
+ for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
+ {
+ if (itr->x > max[0])
+ max[0] = itr->x;
+ if (itr->x < min[0])
+ min[0] = itr->x;
+
+ if (itr->y > max[1])
+ max[1] = itr->y;
+ if (itr->y < min[1])
+ min[1] = itr->y;
+
+ if (itr->z > max[2])
+ max[2] = itr->z;
+ if (itr->z < min[2])
+ min[2] = itr->z;
+ }
+}
+
+void Geometry::CalculateMinMaxHeight( float& min, float& max )
+{
+ min = 0.0f;
+ max = 0.0f;
+
+ for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
+ {
+ if (Transform)
+ {
+ if (itr->y < min)
+ min = itr->y;
+ if (itr->y > max)
+ max = itr->y;
+ }
+ else
+ {
+ if (itr->z < min)
+ min = itr->z;
+ if (itr->z > max)
+ max = itr->z;
+ }
+ }
+}
+
+void Geometry::AddData( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris )
+{
+ uint32 vertOffset = Vertices.size();
+ for (std::vector<Vector3>::iterator itr = verts.begin(); itr != verts.end(); ++itr)
+ Vertices.push_back(Transform ? Utils::ToRecast(*itr) : *itr);
+
+ for (std::vector<Triangle<uint32> >::iterator itr = tris.begin(); itr != tris.end(); ++itr)
+ Triangles.push_back(Triangle<uint32>(itr->Type, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
+}
+
+void Geometry::GetRawData( float*& verts, int*& tris, uint8*& areas )
+{
+ verts = new float[Vertices.size() * 3];
+ for (uint32 i = 0; i < Vertices.size(); ++i)
+ {
+ Vector3& vert = Vertices[i];
+ verts[(i * 3) + 0] = vert.x;
+ verts[(i * 3) + 1] = vert.y;
+ verts[(i * 3) + 2] = vert.z;
+ }
+
+ tris = new int[Triangles.size() * 3];
+ for (uint32 i = 0; i < Triangles.size(); ++i)
+ {
+ Triangle<uint32>& tri = Triangles[i];
+ tris[(i * 3) + 0] = (int)tri.V0;
+ tris[(i * 3) + 1] = (int)tri.V1;
+ tris[(i * 3) + 2] = (int)tri.V2;
+ }
+
+ areas = new uint8[Triangles.size()];
+ for (uint32 i = 0; i < Triangles.size(); i++)
+ {
+ switch (Triangles[i].Type)
+ {
+ case Constants::TRIANGLE_TYPE_WATER:
+ areas[i] = Constants::POLY_AREA_WATER;
+ break;
+ default:
+ areas[i] = Constants::POLY_AREA_TERRAIN;
+ break;
+ }
+ }
+}
+
+void Geometry::AddAdt( ADT* adt )
+{
+ for (std::vector<MapChunk*>::iterator itr = adt->MapChunks.begin(); itr != adt->MapChunks.end(); ++itr)
+ {
+ std::vector<Triangle<uint32> > tmp;
+ tmp.reserve((*itr)->Triangles.size());
+ for (std::vector<Triangle<uint8> >::iterator itr2 = (*itr)->Triangles.begin(); itr2 != (*itr)->Triangles.end(); ++itr2)
+ tmp.push_back(Triangle<uint32>(itr2->Type, itr2->V0, itr2->V1, itr2->V2));
+ AddData((*itr)->Vertices, tmp);
+ }
+
+ if (!adt->_DoodadHandler->Triangles.empty())
+ AddData(adt->_DoodadHandler->Vertices, adt->_DoodadHandler->Triangles);
+
+ if (!adt->_WorldModelHandler->Triangles.empty())
+ AddData(adt->_WorldModelHandler->Vertices, adt->_WorldModelHandler->Triangles);
+}
+
diff --git a/src/tools/mesh_extractor/Geometry.h b/src/tools/mesh_extractor/Geometry.h
new file mode 100644
index 00000000000..e445234dd12
--- /dev/null
+++ b/src/tools/mesh_extractor/Geometry.h
@@ -0,0 +1,23 @@
+#ifndef GEOMETRY_H
+#define GEOMETRY_H
+#include <vector>
+
+#include "Utils.h"
+
+class ADT;
+class Geometry
+{
+public:
+ Geometry();
+
+ void CalculateBoundingBox(float*& min, float*& max);
+ void CalculateMinMaxHeight(float& min, float& max);
+ void AddData(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris);
+ void AddAdt(ADT* adt);
+ void GetRawData(float*& verts, int*& tris, uint8*& areas);
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool Transform;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/LiquidHandler.cpp b/src/tools/mesh_extractor/LiquidHandler.cpp
new file mode 100644
index 00000000000..285ea1a5b74
--- /dev/null
+++ b/src/tools/mesh_extractor/LiquidHandler.cpp
@@ -0,0 +1,102 @@
+#include "LiquidHandler.h"
+#include "Utils.h"
+
+LiquidHandler::LiquidHandler( ADT* adt ) : Source(adt)
+{
+ HandleNewLiquid();
+}
+
+void LiquidHandler::HandleNewLiquid()
+{
+ Chunk* chunk = Source->Data->GetChunkByName("MH2O");
+ if (!chunk)
+ return;
+
+ Vertices.reserve(1000);
+ Triangles.reserve(1000);
+
+ FILE* stream = chunk->GetStream();
+ H2OHeader header[256];
+ MCNKData.reserve(256);
+ for (int i = 0; i < 256; i++)
+ header[i] = H2OHeader::Read(stream);
+
+ for (int i = 0; i < 256; i++)
+ {
+ H2OHeader h = header[i];
+ if (h.LayerCount == 0)
+ {
+ // Need to fill in missing data with dummies.
+ MCNKData.push_back(MCNKLiquidData(NULL, H2ORenderMask()));
+ continue;
+ }
+ fseek(stream, chunk->Offset + h.OffsetInformation, SEEK_SET);
+ H2OInformation information = H2OInformation::Read(stream);
+
+ float** heights = new float*[9];
+ for (int j = 0; j < 9; ++i)
+ {
+ heights[j] = new float[9];
+ memset(heights[j], 0, sizeof(float) * 9);
+ }
+
+ H2ORenderMask renderMask;
+ if (information.LiquidType != 2)
+ {
+ fseek(stream, chunk->Offset + h.OffsetRender, SEEK_SET);
+ renderMask = H2ORenderMask::Read(stream);
+ if ((Utils::IsAllZero(renderMask.Mask, 8) || (information.Width == 8 && information.Height == 8)) && information.OffsetMask2)
+ {
+ fseek(stream, chunk->Offset + information.OffsetMask2, SEEK_SET);
+ uint32 size = ceil(information.Width * information.Height / 8.0f);
+ uint8* altMask = new uint8[size];
+ if (fread(altMask, sizeof(uint8), size, stream) == size)
+ for (uint32 mi = 0; mi < size; mi++)
+ renderMask.Mask[mi + information.OffsetY] |= altMask[mi];
+ delete[] altMask;
+ }
+ fseek(stream, chunk->Offset + information.OffsetHeightmap, SEEK_SET);
+
+ for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++)
+ for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++)
+ if (fread(&heights[x][y], sizeof(float), 1, stream) != 1)
+ return;
+ }
+ else
+ {
+ // Fill with ocean data
+ for (uint32 i = 0; i < 8; ++i)
+ renderMask.Mask[i] = 0xFF;
+
+ for (uint32 y = 0; y < 9; ++y)
+ for (uint32 x = 0; x < 9; ++x)
+ heights[x][y] = information.HeightLevel1;
+ }
+
+ MCNKData.push_back(MCNKLiquidData(heights, renderMask));
+
+ for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++)
+ {
+ for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++)
+ {
+ if (!renderMask.ShouldRender(x, y))
+ continue;
+
+ MapChunk* mapChunk = Source->MapChunks[i];
+ Vector3 location = mapChunk->Header.Position;
+ location.y = location.y - (x * Constants::UnitSize);
+ location.x = location.x - (y * Constants::UnitSize);
+ location.z = heights[x][y];
+
+ uint32 vertOffset = Vertices.size();
+ Vertices.push_back(location);
+ Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y, location.z));
+ Vertices.push_back(Vector3(location.x, location.y - Constants::UnitSize, location.z));
+ Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y - Constants::UnitSize, location.z));
+
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset+2, vertOffset + 1));
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
+ }
+ }
+ }
+}
diff --git a/src/tools/mesh_extractor/LiquidHandler.h b/src/tools/mesh_extractor/LiquidHandler.h
new file mode 100644
index 00000000000..41e128ba20b
--- /dev/null
+++ b/src/tools/mesh_extractor/LiquidHandler.h
@@ -0,0 +1,21 @@
+#ifndef LIQUID_H
+#define LIQUID_H
+#include "ADT.h"
+#include "Utils.h"
+#include "Define.h"
+
+#include <vector>
+
+class LiquidHandler
+{
+public:
+ LiquidHandler(ADT* adt);
+
+ ADT* Source;
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ std::vector<MCNKLiquidData> MCNKData;
+private:
+ void HandleNewLiquid();
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/MPQ.cpp b/src/tools/mesh_extractor/MPQ.cpp
new file mode 100644
index 00000000000..18a9eb0f0e3
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQ.cpp
@@ -0,0 +1,118 @@
+#include "MPQ.h"
+#include "MPQManager.h"
+#include <deque>
+#include <cstdio>
+
+MPQArchive::MPQArchive(const char* filename)
+{
+ int result = libmpq__archive_open(&mpq_a, filename, -1);
+ printf("Opening %s\n", filename);
+ if (result)
+ {
+ switch (result)
+ {
+ case LIBMPQ_ERROR_OPEN :
+ printf("Error opening archive '%s': Does file really exist?\n", filename);
+ break;
+ case LIBMPQ_ERROR_FORMAT : /* bad file format */
+ printf("Error opening archive '%s': Bad file format\n", filename);
+ break;
+ case LIBMPQ_ERROR_SEEK : /* seeking in file failed */
+ printf("Error opening archive '%s': Seeking in file failed\n", filename);
+ break;
+ case LIBMPQ_ERROR_READ : /* Read error in archive */
+ printf("Error opening archive '%s': Read error in archive\n", filename);
+ break;
+ case LIBMPQ_ERROR_MALLOC : /* maybe not enough memory? :) */
+ printf("Error opening archive '%s': Maybe not enough memory\n", filename);
+ break;
+ default:
+ printf("Error opening archive '%s': Unknown error\n", filename);
+ break;
+ }
+ }
+ GetFileListTo(Files);
+}
+
+void MPQArchive::close()
+{
+ libmpq__archive_close(mpq_a);
+}
+
+MPQFile::MPQFile(const char* filename):
+eof(false), buffer(0), pointer(0), size(0)
+{
+ for (std::deque<MPQArchive*>::iterator i = MPQHandler->Archives.begin(); i != MPQHandler->Archives.end();++i)
+ {
+ mpq_archive* mpq_a = (*i)->mpq_a;
+
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, filename, &filenum))
+ continue;
+ libmpq__off_t transferred;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ // HACK: in patch.mpq some files don't want to open and give 1 for filesize
+ if (size<=1) {
+ // printf("warning: file %s has size %d; cannot Read.\n", filename, size);
+ eof = true;
+ buffer = 0;
+ return;
+ }
+ buffer = new char[size];
+
+ //libmpq_file_getdata
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+ /*libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);*/
+ return;
+
+ }
+ eof = true;
+ buffer = 0;
+}
+
+size_t MPQFile::Read(void* dest, size_t bytes)
+{
+ if (eof)
+ return 0;
+
+ size_t rpos = pointer + bytes;
+ if (rpos > size_t(size)) {
+ bytes = size - pointer;
+ eof = true;
+ }
+
+ memcpy(dest, &(buffer[pointer]), bytes);
+
+ pointer = rpos;
+
+ return bytes;
+}
+
+void MPQFile::seek(int offset)
+{
+ pointer = offset;
+ eof = (pointer >= size);
+}
+
+void MPQFile::seekRelative(int offset)
+{
+ pointer += offset;
+ eof = (pointer >= size);
+}
+
+void MPQFile::close()
+{
+ if (buffer)
+ delete[] buffer;
+ buffer = 0;
+ eof = true;
+}
+
+FILE* MPQFile::GetFileStream()
+{
+ FILE* file = tmpfile();
+ fwrite(buffer, sizeof(char), size, file);
+ fseek(file, 0, SEEK_SET);
+ return file;
+}
diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h
new file mode 100644
index 00000000000..2f8b082f526
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQ.h
@@ -0,0 +1,88 @@
+#ifndef MPQ_H
+#define MPQ_H
+
+#include "libmpq/mpq.h"
+#include "Define.h"
+#include <string>
+#include <ctype.h>
+#include <vector>
+#include <iostream>
+#include <deque>
+
+class MPQArchive
+{
+
+public:
+ mpq_archive_s *mpq_a;
+
+ std::vector<std::string> Files;
+
+ MPQArchive(const char* filename);
+ void close();
+
+ void GetFileListTo(std::vector<std::string>& filelist) {
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return;
+ libmpq__off_t size, transferred;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ char *buffer = new char[size];
+
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+
+ char seps[] = "\n";
+ char* token;
+
+ token = strtok( buffer, seps );
+ uint32 counter = 0;
+ while ((token != NULL) && (counter < size)) {
+ //cout << token << endl;
+ token[strlen(token) - 1] = 0;
+ std::string s = token;
+ filelist.push_back(s);
+ counter += strlen(token) + 2;
+ token = strtok(NULL, seps);
+ }
+
+ delete[] buffer;
+ }
+};
+
+class MPQFile
+{
+ //MPQHANDLE handle;
+ bool eof;
+ char *buffer;
+ libmpq__off_t pointer,size;
+
+ // disable copying
+ MPQFile(const MPQFile& /*f*/) {}
+ void operator=(const MPQFile& /*f*/) {}
+
+public:
+ MPQFile(const char* filename); // filenames are not case sensitive
+ ~MPQFile() { close(); }
+ size_t Read(void* dest, size_t bytes);
+ FILE* GetFileStream();
+ size_t getSize() { return size; }
+ size_t getPos() { return pointer; }
+ char* getBuffer() { return buffer; }
+ char* getPointer() { return buffer + pointer; }
+ bool isEof() { return eof; }
+ void seek(int offset);
+ void seekRelative(int offset);
+ void close();
+};
+
+inline void flipcc(char *fcc)
+{
+ char t;
+ t=fcc[0];
+ fcc[0]=fcc[3];
+ fcc[3]=t;
+ t=fcc[1];
+ fcc[1]=fcc[2];
+ fcc[2]=t;
+}
+
+#endif
diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp
new file mode 100644
index 00000000000..91b9c121c89
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQManager.cpp
@@ -0,0 +1,108 @@
+#include "MPQManager.h"
+#include "MPQ.h"
+#include "DBC.h"
+#include "Utils.h"
+#include <ace/Guard_T.h>
+
+char const* MPQManager::Files[] = {
+ "common.MPQ",
+ "common-2.MPQ",
+ "expansion.MPQ",
+ "lichking.MPQ",
+ "patch.MPQ",
+ "patch-2.MPQ",
+ "patch-3.MPQ"
+};
+
+char const* MPQManager::Languages[] = { "enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" };
+
+void MPQManager::Initialize()
+{
+ InitializeDBC();
+ uint32 size = sizeof(Files) / sizeof(char*);
+ for (uint32 i = 0; i < size; ++i)
+ {
+ MPQArchive* arc = new MPQArchive(std::string("Data/" + std::string(Files[i])).c_str());
+ Archives.push_front(arc);
+ printf("Opened %s\n", Files[i]);
+ }
+}
+
+void MPQManager::InitializeDBC()
+{
+ BaseLocale = -1;
+ std::string fileName;
+ uint32 size = sizeof(Languages) / sizeof(char*);
+ MPQArchive* _baseLocale = NULL;
+ for (uint32 i = 0; i < size; ++i)
+ {
+ std::string _fileName = "Data/" + std::string(Languages[i]) + "/locale-" + std::string(Languages[i]) + ".MPQ";
+ FILE* file = fopen(_fileName.c_str(), "rb");
+ if (file)
+ {
+ if (BaseLocale == -1)
+ {
+ BaseLocale = i;
+ _baseLocale = new MPQArchive(_fileName.c_str());
+ fileName = _fileName;
+ LocaleFiles[i] = _baseLocale;
+ }
+ else
+ LocaleFiles[i] = new MPQArchive(_fileName.c_str());
+
+ AvailableLocales.insert(i);
+ printf("Detected locale: %s\n", Languages[i]);
+ }
+ }
+ Archives.push_front(_baseLocale);
+ if (BaseLocale == -1)
+ {
+ printf("No locale data detected\n");
+ ASSERT(false);
+ }
+ else
+ printf("Using default locale: %s\n", Languages[BaseLocale]);
+}
+
+FILE* MPQManager::GetFile( std::string path )
+{
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ MPQFile file(path.c_str());
+ if (file.isEof())
+ return NULL;
+ return file.GetFileStream();
+}
+
+DBC* MPQManager::GetDBC( std::string name )
+{
+ std::string path = "DBFilesClient\\" + name + ".dbc";
+ return new DBC(GetFile(path));
+}
+
+FILE* MPQManager::GetFileFrom( std::string path, MPQArchive* file )
+{
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ mpq_archive* mpq_a = file->mpq_a;
+
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, path.c_str(), &filenum))
+ return NULL;
+
+ libmpq__off_t transferred;
+ libmpq__off_t size = 0;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ // HACK: in patch.mpq some files don't want to open and give 1 for filesize
+ if (size <= 1)
+ return NULL;
+
+ uint8* buffer = new uint8[size];
+
+ //libmpq_file_getdata
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+
+ // Pack the return into a FILE stream
+ FILE* ret = tmpfile();
+ fwrite(buffer, sizeof(uint8), size, ret);
+ return ret;
+}
diff --git a/src/tools/mesh_extractor/MPQManager.h b/src/tools/mesh_extractor/MPQManager.h
new file mode 100644
index 00000000000..2f49ad258a5
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQManager.h
@@ -0,0 +1,36 @@
+#ifndef MPQ_MANAGER_H
+#define MPQ_MANAGER_H
+
+#include "MPQ.h"
+#include <ace/Synch.h>
+#include <set>
+#include <map>
+
+class DBC;
+class MPQManager
+{
+public:
+ MPQManager() {}
+ ~MPQManager() {}
+
+ void Initialize();
+ FILE* GetFile(std::string path);
+ FILE* GetFileFrom(std::string path, MPQArchive* file);
+ DBC* GetDBC(std::string name);
+ std::vector<std::string> GetAllFiles(std::string extension);
+
+ std::deque<MPQArchive*> Archives;
+ int32 BaseLocale;
+ std::set<uint32> AvailableLocales;
+ std::map<uint32, MPQArchive*> LocaleFiles;
+
+ static char const* Files[];
+ static char const* Languages[];
+protected:
+ void InitializeDBC();
+private:
+ ACE_Thread_Mutex mutex;
+};
+
+extern MPQManager* MPQHandler;
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/MapChunk.cpp b/src/tools/mesh_extractor/MapChunk.cpp
new file mode 100644
index 00000000000..8fe40773d43
--- /dev/null
+++ b/src/tools/mesh_extractor/MapChunk.cpp
@@ -0,0 +1,74 @@
+#include "MapChunk.h"
+#include "ADT.h"
+#include "LiquidHandler.h"
+
+MapChunk::MapChunk( ADT* _adt, Chunk* chunk ) : Adt(_adt), Source(chunk)
+{
+ FILE* stream = chunk->GetStream();
+ Header.Read(stream);
+ fseek(stream, chunk->Offset, SEEK_SET);
+ Index = Header.IndexX + Header.IndexY * 16;
+ GenerateVertices(stream);
+}
+
+void MapChunk::GenerateTriangles()
+{
+ Triangles.reserve(256);
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ if (HasHole(Header.Holes, x / 2, y / 2))
+ continue;
+
+ uint32 topLeft = (17 * y) + x;
+ uint32 topRight = (17 * y) + x + 1;
+ uint32 bottomLeft = (17 * (y + 1)) + x;
+ uint32 bottomRight = (17 * (y + 1)) + x + 1;
+ uint32 center = (17 * y) + 9 + x;
+
+ Constants::TriangleType triangleType = Constants::TRIANGLE_TYPE_TERRAIN;
+ if (Adt->_LiquidHandler && !Adt->_LiquidHandler->MCNKData.empty())
+ {
+ MCNKLiquidData& data = Adt->_LiquidHandler->MCNKData[Index];
+ float maxHeight = std::max(
+ std::max(
+ std::max(std::max(Vertices[topLeft].z, Vertices[topRight].z), Vertices[bottomLeft].z),
+ Vertices[bottomRight].z), Vertices[center].z);
+ if (data.IsWater(x, y, maxHeight))
+ triangleType = Constants::TRIANGLE_TYPE_WATER;
+ }
+
+ Triangles.push_back(Triangle<uint8>(triangleType, topRight, topLeft, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, topLeft, bottomLeft, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, bottomLeft, bottomRight, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, bottomRight, topRight, center));
+ }
+ }
+}
+
+void MapChunk::GenerateVertices( FILE* stream )
+{
+ fseek(stream, Header.OffsetMCVT, SEEK_CUR);
+ Vertices.reserve(125);
+
+ for (int j = 0; j < 17; j++)
+ {
+ int values = j % 2 ? 8 : 9;
+ for (int i = 0; i < values; i++)
+ {
+ float tmp;
+ if (fread(&tmp, sizeof(float), 1, stream) != 1)
+ printf("MapChunk::GenerateVertices: Failed to read some data expected 1, read 0\n");
+ Vector3 vert(Header.Position.x - (j * (Constants::UnitSize * 0.5f)), Header.Position.y - (i * Constants::UnitSize), Header.Position.z + tmp);
+ if (values == 8)
+ vert.y -= Constants::UnitSize * 0.5f;
+ Vertices.push_back(vert);
+ }
+ }
+}
+
+bool MapChunk::HasHole( uint32 map, int x, int y )
+{
+ return (map & 0x0000FFFF) & ((1 << x) << (y << 2));
+}
diff --git a/src/tools/mesh_extractor/MapChunk.h b/src/tools/mesh_extractor/MapChunk.h
new file mode 100644
index 00000000000..e7d835ae0e3
--- /dev/null
+++ b/src/tools/mesh_extractor/MapChunk.h
@@ -0,0 +1,24 @@
+#ifndef MAPCHUNK_H
+#define MAPCHUNK_H
+#include "Chunk.h"
+#include "Utils.h"
+#include "Constants.h"
+#include <vector>
+class ADT;
+
+class MapChunk
+{
+public:
+ MapChunk(ADT* _adt, Chunk* chunk);
+
+ void GenerateTriangles();
+ void GenerateVertices(FILE* stream);
+ static bool HasHole(uint32 map, int x, int y);
+ ADT* Adt;
+ Chunk* Source;
+ MapChunkHeader Header;
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint8> > Triangles;
+ int32 Index;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp
new file mode 100644
index 00000000000..e06f44c7125
--- /dev/null
+++ b/src/tools/mesh_extractor/MeshExtractor.cpp
@@ -0,0 +1,428 @@
+#include "MPQManager.h"
+#include "WDT.h"
+#include "ContinentBuilder.h"
+#include "Cache.h"
+#include "DBC.h"
+#include "Constants.h"
+#include "Model.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+#include <set>
+
+MPQManager* MPQHandler;
+CacheClass* Cache;
+
+void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads, bool debug)
+{
+ DBC* dbc = MPQHandler->GetDBC("Map");
+ for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr)
+ {
+ uint32 mapId = (*itr)->Values[0];
+
+ // Skip this map if a list of specific maps was provided and this one is not contained in it.
+ if (!mapIds.empty() && mapIds.find(mapId) == mapIds.end())
+ continue;
+
+ std::string name = (*itr)->GetString(1);
+ WDT wdt("World\\maps\\" + name + "\\" + name + ".wdt");
+ if (!wdt.IsValid || wdt.IsGlobalModel)
+ continue;
+ printf("Building %s MapId %u\n", name.c_str(), mapId);
+ ContinentBuilder builder(name, mapId, &wdt, threads);
+ builder.Build(debug);
+ }
+}
+
+void ExtractDBCs()
+{
+ printf("Extracting DBCs\n");
+ // Create the filesystem structure
+ std::string baseDBCPath = "dbc/";
+ Utils::CreateDir(baseDBCPath);
+
+ // Populate list of DBC files
+ std::set<std::string> DBCFiles;
+ for (std::vector<std::string>::iterator itr = MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.begin(); itr != MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.end(); ++itr)
+ if (itr->rfind(".dbc") == itr->length() - strlen(".dbc"))
+ DBCFiles.insert(*itr);
+
+ // Iterate over all available locales
+ for (std::set<uint32>::iterator itr = MPQHandler->AvailableLocales.begin(); itr != MPQHandler->AvailableLocales.end(); ++itr)
+ {
+ printf("Extracting DBCs for locale %s\n", MPQManager::Languages[*itr]);
+ std::string path = baseDBCPath;
+ if (*itr != uint32(MPQHandler->BaseLocale))
+ {
+ path += std::string(MPQManager::Languages[*itr]) + "/";
+ Utils::CreateDir(path);
+ }
+
+ std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt";
+ // Extract the component file
+ Utils::SaveToDisk(MPQHandler->GetFile(component), path + component);
+ // Extract the DBC files for the given locale
+ for (std::set<std::string>::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2)
+ Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + strlen("DBFilesClient\\")));
+ }
+ printf("DBC extraction finished!\n");
+}
+
+void ExtractGameobjectModels()
+{
+ Constants::ToWoWCoords = true;
+ printf("Extracting GameObject models\n");
+
+ std::string baseBuildingsPath = "Buildings/";
+ std::string baseVmapsPath = "vmaps/";
+ Utils::CreateDir(baseVmapsPath);
+ Utils::CreateDir(baseBuildingsPath);
+
+ FILE* modelList = fopen((baseVmapsPath + "GameObjectModels.list").c_str(), "wb");
+ if (!modelList)
+ {
+ printf("Could not create file vmaps/GameObjectModels.list, please make sure that you have the write permissions in the folder\n");
+ return;
+ }
+
+ DBC* dbc = MPQHandler->GetDBC("GameObjectDisplayInfo");
+ for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr)
+ {
+ std::string path = (*itr)->GetString(1);
+ std::string fileName = Utils::GetPlainName(path.c_str());
+ std::string extension = Utils::GetExtension(fileName);
+ // Convert the extension to lowercase
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (extension == "mdx" || extension == "m2")
+ {
+ fileName = Utils::FixModelPath(fileName);
+ Model model(path);
+
+ if (model.IsBad)
+ continue;
+
+ FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb");
+ if (!output)
+ {
+ printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str());
+ continue;
+ }
+ // Placeholder for 0 values
+ int Nop[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ fwrite(Constants::VMAPMagic, 8, 1, output);
+ uint32 numVerts = model.Header.CountBoundingVertices;
+ fwrite(&numVerts, sizeof(uint32), 1, output);
+ uint32 numGroups = 1;
+ fwrite(&numGroups, sizeof(uint32), 1, output);
+ fwrite(Nop, 4 * 3 , 1, output); // rootwmoid, flags, groupid
+ fwrite(Nop, sizeof(float), 3 * 2, output);//bbox, only needed for WMO currently
+ fwrite(Nop, 4, 1, output);// liquidflags
+ fwrite("GRP ", 4, 1, output);
+
+ uint32 branches = 1;
+ uint32 wsize = sizeof(branches) + sizeof(uint32) * branches;
+ fwrite(&wsize, sizeof(uint32), 1, output);
+ fwrite(&branches, sizeof(branches), 1, output);
+ uint32 numTris = model.Header.CountBoundingTriangles;
+ fwrite(&numTris, sizeof(uint32), 1, output);
+ fwrite("INDX", 4, 1, output);
+ wsize = sizeof(uint32) + sizeof(unsigned short) * numTris;
+ fwrite(&wsize, sizeof(int), 1, output);
+ fwrite(&numTris, sizeof(uint32), 1, output);
+ uint16* indices = new uint16[numTris];
+
+ if (numTris > 0)
+ {
+ uint32 i = 0;
+ for (std::vector<Triangle<uint16> >::iterator itr2 = model.Triangles.begin(); itr2 != model.Triangles.end(); ++itr2, ++i)
+ {
+ indices[i * 3 + 0] = itr2->V0;
+ indices[i * 3 + 1] = itr2->V1;
+ indices[i * 3 + 2] = itr2->V2;
+ }
+ fwrite(indices, sizeof(uint16), numTris, output);
+ }
+
+
+ fwrite("VERT", 4, 1, output);
+ wsize = sizeof(int) + sizeof(float) * 3 * numVerts;
+ fwrite(&wsize, sizeof(int), 1, output);
+ fwrite(&numVerts, sizeof(int), 1, output);
+ float* vertices = new float[numVerts*3];
+
+ if (numVerts > 0)
+ {
+ uint32 i = 0;
+ for (std::vector<Vector3>::iterator itr2 = model.Vertices.begin(); itr2 != model.Vertices.end(); ++itr2, ++i)
+ {
+ vertices[i * 3 + 0] = itr2->x;
+ vertices[i * 3 + 1] = itr2->y;
+ vertices[i * 3 + 2] = itr2->z;
+ }
+
+ fwrite(vertices, sizeof(float), numVerts * 3, output);
+ }
+
+ fclose(output);
+ delete[] indices;
+ delete[] vertices;
+
+ uint32 displayId = (*itr)->Values[0];
+ uint32 pathLength = fileName.size();
+ fwrite(&displayId, sizeof(uint32), 1, modelList);
+ fwrite(&pathLength, sizeof(uint32), 1, modelList);
+ fwrite(fileName.c_str(), sizeof(char), pathLength, modelList);
+ }
+ else if (extension == "wmo")
+ {
+ WorldModelRoot model(path);
+
+ FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb");
+ if (!output)
+ {
+ printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str());
+ continue;
+ }
+
+ fwrite(Constants::VMAPMagic, 1, 8, output);
+ uint32 numVertices = 0;
+ fwrite(&numVertices, sizeof(uint32), 1, output); // will be filled later
+ fwrite(&model.Header.CountGroups, sizeof(uint32), 1, output);
+ fwrite(&model.Header.WmoId, sizeof(uint32), 1, output);
+
+ for (std::vector<WorldModelGroup>::iterator itr2 = model.Groups.begin(); itr2 != model.Groups.end(); ++itr2)
+ {
+ fwrite(&itr2->Header.Flags, sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.WmoId, sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.BoundingBox[0], sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.BoundingBox[1], sizeof(uint32), 1, output);
+ uint32 LiquidFlags = itr2->HasLiquidData ? 1 : 0;
+ fwrite(&LiquidFlags, sizeof(uint32), 1, output);
+
+ fwrite("GRP ", sizeof(char), 4, output);
+ uint32 k = 0;
+ uint32 mobaBatch = itr2->MOBALength / 12;
+ uint32* MobaEx = new uint32[mobaBatch*4];
+
+ for(uint32 i = 8; i < itr2->MOBALength; i += 12)
+ MobaEx[k++] = itr2->MOBA[i];
+
+ int mobaSizeGrp = mobaBatch * 4 + 4;
+ fwrite(&mobaSizeGrp, 4, 1, output);
+ fwrite(&mobaBatch, 4, 1, output);
+ fwrite(MobaEx, 4, k, output);
+ delete[] MobaEx;
+
+ // Note: still not finished
+ }
+
+ fclose(output);
+ }
+ }
+
+ fclose(modelList);
+ printf("GameObject models extraction finished!");
+ Constants::ToWoWCoords = false;
+}
+
+bool HandleArgs(int argc, char** argv, uint32& threads, std::set<uint32>& mapList, bool& debugOutput, uint32& extractFlags)
+{
+ char* param = NULL;
+ extractFlags = 0;
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ threads = atoi(param);
+ printf("Using %i threads\n", threads);
+ }
+ else if (strcmp(argv[i], "--maps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* copy = strdup(param);
+ char* token = strtok(copy, ",");
+ while (token)
+ {
+ mapList.insert(atoi(token));
+ token = strtok(NULL, ",");
+ }
+
+ printf("Extracting only provided list of maps (%u).\n", uint32(mapList.size()));
+ }
+ else if (strcmp(argv[i], "--debug") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ debugOutput = atoi(param);
+ if (debugOutput)
+ printf("Output will contain debug information (.obj files)\n");
+ }
+ else if (strcmp(argv[i], "--extract") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ extractFlags = atoi(param);
+
+ if (!(extractFlags & Constants::EXTRACT_FLAG_ALLOWED)) // Tried to use an invalid flag
+ return false;
+
+ printf("Detected flags: \n");
+ printf("* Extract DBCs: %s\n", (extractFlags & Constants::EXTRACT_FLAG_DBC) ? "Yes" : "No");
+ printf("* Extract Maps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MAPS) ? "Yes" : "No");
+ printf("* Extract VMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_VMAPS) ? "Yes" : "No");
+ printf("* Extract GameObject Models: %s\n", (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ? "Yes" : "No");
+ printf("* Extract MMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MMAPS) ? "Yes" : "No");
+ }
+ }
+ return true;
+}
+
+void PrintUsage()
+{
+ printf("MeshExtractor help.\n");
+ printf("* Use \"--threads <number>\" to specify <number> threads, default to 4 (Only available when extracting MMaps)\n");
+ printf("* Use \"--maps a,b,c,d,e\" to extract only the maps specified (Do not use spaces)\n");
+ printf("* Use \"--debug 1\" to generate debug information of the tiles (Only available when extracting MMaps)\n");
+ printf("* Use \"--extract X\" to extract the data specified by the flag X (Note: You can combine the flags with the bitwise OR operator |). Available flags are: \n");
+ {
+ printf("- %u to extract DBCs\n", Constants::EXTRACT_FLAG_DBC);
+ printf("- %u to extract Maps (Not yet implemented)\n", Constants::EXTRACT_FLAG_MAPS);
+ printf("- %u to extract VMaps (Not yet implemented)\n", Constants::EXTRACT_FLAG_VMAPS);
+ printf("- %u to extract GameObject models (Not yet finished, you need to run VMapAssembler on the extracted files)\n", Constants::EXTRACT_FLAG_GOB_MODELS);
+ printf("- %u to extract MMaps (Not yet finished)\n", Constants::EXTRACT_FLAG_MMAPS);
+ }
+}
+
+void LoadTile(dtNavMesh*& navMesh, const char* tile)
+{
+ FILE* f = fopen(tile, "rb");
+ MmapTileHeader header;
+
+ if (fread(&header, sizeof(MmapTileHeader), 1, f) != 1)
+ return;
+
+ uint8* nav = new uint8[header.size];
+ if (fread(nav, header.size, 1, f) != 1)
+ return;
+
+ navMesh->addTile(nav, header.size, DT_TILE_FREE_DATA, 0, NULL);
+
+ fclose(f);
+}
+
+int main(int argc, char* argv[])
+{
+ if (!system("pause"))
+ {
+ printf("main: Error in system call to pause\n");
+ return -1;
+ }
+
+ uint32 threads = 4, extractFlags = 0;
+ std::set<uint32> mapIds;
+ bool debug = false;
+
+ if (!HandleArgs(argc, argv, threads, mapIds, debug, extractFlags))
+ {
+ PrintUsage();
+ return -1;
+ }
+
+ Cache = new CacheClass();
+ MPQHandler = new MPQManager();
+ MPQHandler->Initialize();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_DBC)
+ ExtractDBCs();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_MMAPS)
+ ExtractMMaps(mapIds, threads, debug);
+
+ if (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS)
+ ExtractGameobjectModels();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_TEST)
+ {
+ float start[] = { 0.0f, 0.0f, 0.0f };
+ float end[] = { 0.0f, 0.0f, 0.0f };
+
+ //
+ float m_spos[3];
+ m_spos[0] = -1.0f * start[1];
+ m_spos[1] = start[2];
+ m_spos[2] = -1.0f * start[0];
+
+ //
+ float m_epos[3];
+ m_epos[0] = -1.0f * end[1];
+ m_epos[1] = end[2];
+ m_epos[2] = -1.0f * end[0];
+
+ //
+ dtQueryFilter m_filter;
+ m_filter.setIncludeFlags(0xffff) ;
+ m_filter.setExcludeFlags(0);
+
+ //
+ float m_polyPickExt[3];
+ m_polyPickExt[0] = 2;
+ m_polyPickExt[1] = 4;
+ m_polyPickExt[2] = 2;
+
+ //
+ dtPolyRef m_startRef;
+ dtPolyRef m_endRef;
+
+ FILE* mmap = fopen(".mmap", "rb");
+ dtNavMeshParams params;
+ int count = fread(&params, sizeof(dtNavMeshParams), 1, mmap);
+ fclose(mmap);
+ if (count != 1)
+ {
+ printf("main: Error reading from .mmap\n");
+ return 0;
+ }
+
+ dtNavMesh* navMesh = new dtNavMesh();
+ dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery();
+
+ navMesh->init(&params);
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+
+ navMeshQuery->init(navMesh, 2048);
+
+ float nearestPt[3];
+
+ navMeshQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, nearestPt);
+ navMeshQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, nearestPt);
+
+ if ( !m_startRef || !m_endRef )
+ {
+ std::cerr << "Could not find any nearby poly's (" << m_startRef << "," << m_endRef << ")" << std::endl;
+ return 0;
+ }
+
+ printf("Found!");
+ }
+
+ return 0;
+}
diff --git a/src/tools/mesh_extractor/Model.cpp b/src/tools/mesh_extractor/Model.cpp
new file mode 100644
index 00000000000..77b1adbeaa0
--- /dev/null
+++ b/src/tools/mesh_extractor/Model.cpp
@@ -0,0 +1,67 @@
+#include "Model.h"
+#include "MPQManager.h"
+#include "Utils.h"
+
+Model::Model( std::string path ) : IsCollidable(false), IsBad(false)
+{
+ Stream = MPQHandler->GetFile(Utils::FixModelPath(path));
+ if (!Stream)
+ {
+ IsBad = true;
+ return;
+ }
+ Header.Read(Stream);
+ if (Header.OffsetBoundingNormals > 0 && Header.OffsetBoundingVertices > 0 &&
+ Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f)
+ {
+ IsCollidable = true;
+ ReadVertices(Stream);
+ ReadBoundingNormals(Stream);
+ ReadBoundingTriangles(Stream);
+ }
+}
+
+Model::~Model()
+{
+ if (Stream)
+ fclose(Stream);
+}
+
+void Model::ReadVertices( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingVertices, SEEK_SET);
+ Vertices.reserve(Header.CountBoundingVertices);
+ for (uint32 i = 0; i < Header.CountBoundingVertices; ++i)
+ {
+ Vertices.push_back(Vector3::Read(stream));
+ if (Constants::ToWoWCoords)
+ Vertices[i] = Utils::ToWoWCoords(Vertices[i]);
+ }
+}
+
+void Model::ReadBoundingTriangles( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingTriangles, SEEK_SET);
+ Triangles.reserve(Header.CountBoundingTriangles / 3);
+ for (uint32 i = 0; i < Header.CountBoundingTriangles / 3; i++)
+ {
+ Triangle<uint16> tri;
+ tri.Type = Constants::TRIANGLE_TYPE_DOODAD;
+ int count = 0;
+ count += fread(&tri.V0, sizeof(uint16), 1, stream);
+ count += fread(&tri.V1, sizeof(uint16), 1, stream);
+ count += fread(&tri.V2, sizeof(uint16), 1, stream);
+ if (count != 3)
+ printf("Model::ReadBoundingTriangles: Error reading data, expected 3, read %d\n", count);
+ Triangles.push_back(tri);
+ }
+}
+
+void Model::ReadBoundingNormals( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingNormals, SEEK_SET);
+ Normals.reserve(Header.CountBoundingNormals);
+ for (uint32 i = 0; i < Header.CountBoundingNormals; i++)
+ Normals.push_back(Vector3::Read(stream));
+}
+
diff --git a/src/tools/mesh_extractor/Model.h b/src/tools/mesh_extractor/Model.h
new file mode 100644
index 00000000000..ea9331e7c30
--- /dev/null
+++ b/src/tools/mesh_extractor/Model.h
@@ -0,0 +1,23 @@
+#ifndef MODEL_H
+#define MODEL_H
+#include <vector>
+#include "Utils.h"
+
+class Model
+{
+public:
+ Model(std::string path);
+ ~Model();
+
+ void ReadVertices(FILE* stream);
+ void ReadBoundingTriangles(FILE* stream);
+ void ReadBoundingNormals(FILE* stream);
+ ModelHeader Header;
+ std::vector<Vector3> Vertices;
+ std::vector<Vector3> Normals;
+ std::vector<Triangle<uint16> > Triangles;
+ bool IsCollidable;
+ FILE* Stream;
+ bool IsBad;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/ObjectDataHandler.cpp b/src/tools/mesh_extractor/ObjectDataHandler.cpp
new file mode 100644
index 00000000000..789efc6d62c
--- /dev/null
+++ b/src/tools/mesh_extractor/ObjectDataHandler.cpp
@@ -0,0 +1,21 @@
+#include "ObjectDataHandler.h"
+#include "Chunk.h"
+#include "ADT.h"
+#include "ChunkedData.h"
+
+void ObjectDataHandler::ProcessMapChunk( MapChunk* chunk )
+{
+ if (!Source->HasObjectData)
+ return;
+ // fuck it blizzard, why is this crap necessary?
+ int32 firstIndex = Source->ObjectData->GetFirstIndex("MCNK");
+ if (firstIndex == -1)
+ return;
+ if (uint32(firstIndex + chunk->Index) > Source->ObjectData->Chunks.size())
+ return;
+ Chunk* ourChunk = Source->ObjectData->Chunks[firstIndex + chunk->Index];
+ if (ourChunk->Length == 0)
+ return;
+ ChunkedData* subChunks = new ChunkedData(ourChunk->GetStream(), ourChunk->Length, 2);
+ ProcessInternal(subChunks);
+}
diff --git a/src/tools/mesh_extractor/ObjectDataHandler.h b/src/tools/mesh_extractor/ObjectDataHandler.h
new file mode 100644
index 00000000000..75b4e45700c
--- /dev/null
+++ b/src/tools/mesh_extractor/ObjectDataHandler.h
@@ -0,0 +1,15 @@
+#ifndef ODATA_HNDL_H
+#define ODATA_HNDL_H
+#include "ADT.h"
+#include "MapChunk.h"
+
+class ObjectDataHandler
+{
+public:
+ ObjectDataHandler(ADT* _adt) : Source(_adt) {}
+
+ void ProcessMapChunk(MapChunk* chunk);
+ virtual void ProcessInternal(ChunkedData* data) = 0;
+ ADT* Source;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp
new file mode 100644
index 00000000000..9bb9b11619f
--- /dev/null
+++ b/src/tools/mesh_extractor/TileBuilder.cpp
@@ -0,0 +1,311 @@
+#include "ContinentBuilder.h"
+#include "TileBuilder.h"
+#include "Geometry.h"
+#include "Constants.h"
+#include "Utils.h"
+#include "Cache.h"
+#include "ADT.h"
+#include "WDT.h"
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "DetourNavMeshBuilder.h"
+
+#include <ace/Synch.h>
+
+TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId) :
+ World(world), X(x), Y(y), MapId(mapId), _Geometry(NULL), DataSize(0), cBuilder(_cBuilder)
+{
+ /*
+ Test, non-working values
+ // Cell Size = TileSize / TileVoxelSize
+ // 1800 = TileVoxelSize
+ Config.cs = Constants::TileSize / 1800;
+ // Cell Height
+ Config.ch = 0.4f;
+ // Min Region Area = 20^2
+ Config.minRegionArea = 20*20;
+ // Merge Region Area = 40^2
+ Config.mergeRegionArea = 40*40;
+ Config.tileSize = Constants::TileSize / 4;
+ Config.walkableSlopeAngle = 50.0f;
+ Config.detailSampleDist = 3.0f;
+ Config.detailSampleMaxError = 1.25f;
+ Config.walkableClimb = floorf(1.0f / Config.ch);
+ Config.walkableHeight = ceilf(1.652778f / Config.ch);
+ Config.walkableRadius = ceilf(0.2951389f / Config.cs);
+ Config.maxEdgeLen = Config.walkableRadius * 8;
+ Config.borderSize = Config.walkableRadius + 4;
+ Config.width = 1800 + Config.borderSize * 2;
+ Config.height = 1800 + Config.borderSize * 2;
+ Config.maxVertsPerPoly = 6;
+ Config.maxSimplificationError = 1.3f;
+ */
+
+ // All are in UNIT metrics!
+ memset(&Config, 0, sizeof(rcConfig));
+
+ Config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ Config.cs = Constants::BaseUnitDim;
+ Config.ch = Constants::BaseUnitDim;
+ Config.walkableSlopeAngle = 60.0f;
+ Config.tileSize = Constants::VertexPerTile;
+ Config.walkableRadius = 1;
+ Config.borderSize = Config.walkableRadius + 3;
+ Config.maxEdgeLen = Constants::VertexPerTile + 1; //anything bigger than tileSize
+ Config.walkableHeight = 3;
+ Config.walkableClimb = 2; // keep less than walkableHeight
+ Config.minRegionArea = rcSqr(60);
+ Config.mergeRegionArea = rcSqr(50);
+ Config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ Config.detailSampleDist = Config.cs * 64;
+ Config.detailSampleMaxError = Config.ch * 2;
+
+ Context = new rcContext;
+}
+
+void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshParams& /*navMeshParams*/ )
+{
+ bmin = new float[3];
+ bmax = new float[3];
+ bmin[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * X);
+ bmin[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * Y);
+ bmax[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * (X + 1));
+ bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1));
+}
+
+uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams)
+{
+ _Geometry = new Geometry();
+ _Geometry->Transform = true;
+ ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y));
+ adt->Read();
+ _Geometry->AddAdt(adt);
+ delete adt;
+
+ if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty())
+ return NULL;
+
+ // again, we load everything - wasteful but who cares
+ for (int ty = Y - 2; ty <= Y + 2; ty++)
+ {
+ for (int tx = X - 2; tx <= X + 2; tx++)
+ {
+ // don't load main tile again
+ if (tx == X && ty == Y)
+ continue;
+
+ ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty));
+ // If this condition is met, it means that this wdt does not contain the ADT
+ if (!_adt->Data->Stream)
+ {
+ delete _adt;
+ continue;
+ }
+ _adt->Read();
+ _Geometry->AddAdt(_adt);
+ delete _adt;
+ }
+ }
+
+ if (dbg)
+ {
+ char buff[100];
+ sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X);
+ FILE* debug = fopen(buff, "wb");
+ for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i)
+ fprintf(debug, "v %f %f %f\n", _Geometry->Vertices[i].x, _Geometry->Vertices[i].y, _Geometry->Vertices[i].z);
+ for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i)
+ fprintf(debug, "f %i %i %i\n", _Geometry->Triangles[i].V0 + 1, _Geometry->Triangles[i].V1 + 1, _Geometry->Triangles[i].V2 + 1);
+ fclose(debug);
+ }
+
+ uint32 numVerts = _Geometry->Vertices.size();
+ uint32 numTris = _Geometry->Triangles.size();
+ float* vertices;
+ int* triangles;
+ uint8* areas;
+ _Geometry->GetRawData(vertices, triangles, areas);
+ _Geometry->Vertices.clear();
+ _Geometry->Triangles.clear();
+
+
+ rcVcopy(Config.bmin, cBuilder->bmin);
+ rcVcopy(Config.bmax, cBuilder->bmax);
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height);
+
+ // Initialize per tile config.
+ rcConfig tileCfg = Config;
+ tileCfg.width = Config.tileSize + Config.borderSize * 2;
+ tileCfg.height = Config.tileSize + Config.borderSize * 2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[Constants::TilesPerMap * Constants::TilesPerMap];
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap];
+
+ int nmerge = 0;
+ for (int y = 0; y < Constants::TilesPerMap; ++y)
+ {
+ for (int x = 0; x < Constants::TilesPerMap; ++x)
+ {
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = Config.bmin[0] + float(x * Config.tileSize - Config.borderSize) * Config.cs;
+ tileCfg.bmin[2] = Config.bmin[2] + float(y * Config.tileSize - Config.borderSize) * Config.cs;
+ tileCfg.bmax[0] = Config.bmin[0] + float((x + 1) * Config.tileSize + Config.borderSize) * Config.cs;
+ tileCfg.bmax[2] = Config.bmin[2] + float((y + 1) * Config.tileSize + Config.borderSize) * Config.cs;
+
+
+ rcHeightfield* hf = rcAllocHeightfield();
+ rcCreateHeightfield(Context, *hf, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch);
+ rcClearUnwalkableTriangles(Context, tileCfg.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas);
+ rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb);
+
+ // Once all geometry is rasterized, we do initial pass of filtering to
+ // remove unwanted overhangs caused by the conservative rasterization
+ // as well as filter spans where the character cannot possibly stand.
+ rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf);
+ rcFilterLedgeSpans(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf);
+ rcFilterWalkableLowHeightSpans(Context, tileCfg.walkableHeight, *hf);
+
+ // Compact the heightfield so that it is faster to handle from now on.
+ // This will result in more cache coherent data as well as the neighbours
+ // between walkable cells will be calculated.
+ rcCompactHeightfield* chf = rcAllocCompactHeightfield();
+ rcBuildCompactHeightfield(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf, *chf);
+
+ rcFreeHeightField(hf);
+
+ // Erode the walkable area by agent radius.
+ rcErodeWalkableArea(Context, Config.walkableRadius, *chf);
+ // Prepare for region partitioning, by calculating distance field along the walkable surface.
+ rcBuildDistanceField(Context, *chf);
+ // Partition the walkable surface into simple regions without holes.
+ rcBuildRegions(Context, *chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea);
+
+ // Create contours.
+ rcContourSet* cset = rcAllocContourSet();
+ rcBuildContours(Context, *chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset);
+
+ // Build polygon navmesh from the contours.
+ rcPolyMesh* pmesh = rcAllocPolyMesh();
+ rcBuildPolyMesh(Context, *cset, tileCfg.maxVertsPerPoly, *pmesh);
+
+ // Build detail mesh.
+ rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail();
+ rcBuildPolyMeshDetail(Context, *pmesh, *chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *dmesh);
+
+ // Free memory
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+
+ pmmerge[nmerge] = pmesh;
+ dmmerge[nmerge] = dmesh;
+ ++nmerge;
+ }
+ }
+
+ rcPolyMesh* pmesh = rcAllocPolyMesh();
+ rcMergePolyMeshes(Context, pmmerge, nmerge, *pmesh);
+
+ rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail();
+ rcMergePolyMeshDetails(Context, dmmerge, nmerge, *dmesh);
+
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ printf("[%02i,%02i] Meshes merged!\n", X, Y);
+
+ // Remove padding from the polymesh data. (Remove this odditity)
+ for (int i = 0; i < pmesh->nverts; ++i)
+ {
+ unsigned short* v = &pmesh->verts[i * 3];
+ v[0] -= (unsigned short)Config.borderSize;
+ v[2] -= (unsigned short)Config.borderSize;
+ }
+
+ // Set flags according to area types (e.g. Swim for Water)
+ for (int i = 0; i < pmesh->npolys; i++)
+ {
+ if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN)
+ pmesh->flags[i] = Constants::POLY_FLAG_WALK;
+ else if (pmesh->areas[i] == Constants::POLY_AREA_WATER)
+ pmesh->flags[i] = Constants::POLY_FLAG_SWIM;
+ }
+
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ // PolyMesh data
+ params.verts = pmesh->verts;
+ params.vertCount = pmesh->nverts;
+ params.polys = pmesh->polys;
+ params.polyAreas = pmesh->areas;
+ params.polyFlags = pmesh->flags;
+ params.polyCount = pmesh->npolys;
+ params.nvp = pmesh->nvp;
+ // PolyMeshDetail data
+ params.detailMeshes = dmesh->meshes;
+ params.detailVerts = dmesh->verts;
+ params.detailVertsCount = dmesh->nverts;
+ params.detailTris = dmesh->tris;
+ params.detailTriCount = dmesh->ntris;
+ rcVcopy(params.bmin, pmesh->bmin);
+ rcVcopy(params.bmax, pmesh->bmax);
+ // General settings
+ params.ch = Config.ch;
+ params.cs = Config.cs;
+ params.walkableClimb = Constants::BaseUnitDim * Config.walkableClimb;
+ params.walkableHeight = Constants::BaseUnitDim * Config.walkableHeight;
+ params.walkableRadius = Constants::BaseUnitDim * Config.walkableRadius;
+ params.tileX = (((cBuilder->bmin[0] + cBuilder->bmax[0]) / 2) - navMeshParams.orig[0]) / Constants::TileSize;
+ params.tileY = (((cBuilder->bmin[2] + cBuilder->bmax[2]) / 2) - navMeshParams.orig[2]) / Constants::TileSize;
+
+ rcVcopy(params.bmin, cBuilder->bmin);
+ rcVcopy(params.bmax, cBuilder->bmax);
+
+ // Offmesh-connection settings
+ params.offMeshConCount = 0; // none for now
+
+ params.tileSize = Constants::VertexPerMap;
+
+ if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("[%02i,%02i] No polygons to build on tile, skipping.\n", X, Y);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ delete areas;
+ delete triangles;
+ delete vertices;
+ return NULL;
+ }
+
+ int navDataSize;
+ uint8* navData;
+ printf("[%02i,%02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, pmesh->nverts, pmesh->npolys, dmesh->ntris);
+ bool result = dtCreateNavMeshData(&params, &navData, &navDataSize);
+
+ // Free some memory
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ delete areas;
+ delete triangles;
+ delete vertices;
+
+ if (result)
+ {
+ printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize);
+ DataSize = navDataSize;
+ return navData;
+ }
+
+ return NULL;
+}
+
+TileBuilder::~TileBuilder()
+{
+ delete Context;
+ delete _Geometry;
+}
diff --git a/src/tools/mesh_extractor/TileBuilder.h b/src/tools/mesh_extractor/TileBuilder.h
new file mode 100644
index 00000000000..40c96f6ec42
--- /dev/null
+++ b/src/tools/mesh_extractor/TileBuilder.h
@@ -0,0 +1,30 @@
+#ifndef TILE_BUILD_H
+#define TILE_BUILD_H
+#include <string>
+#include "Recast.h"
+
+#include "Geometry.h"
+
+class ContinentBuilder;
+class WDT;
+
+class TileBuilder
+{
+public:
+ TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId);
+ ~TileBuilder();
+
+ void CalculateTileBounds(float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams);
+ uint8* Build(bool dbg, dtNavMeshParams& navMeshParams);
+
+ std::string World;
+ int X;
+ int Y;
+ int MapId;
+ rcConfig Config;
+ rcContext* Context;
+ Geometry* _Geometry;
+ uint32 DataSize;
+ ContinentBuilder* cBuilder;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp
new file mode 100644
index 00000000000..acb1ed38e27
--- /dev/null
+++ b/src/tools/mesh_extractor/Utils.cpp
@@ -0,0 +1,568 @@
+#include "Utils.h"
+#include "WorldModelHandler.h"
+#include "Constants.h"
+#include <cstring>
+#include "G3D/Matrix4.h"
+#include "G3D/Quat.h"
+
+#ifdef _WIN32
+ #include "direct.h"
+#else
+ #include <sys/stat.h>
+ #include <unistd.h>
+#endif
+
+const float Constants::TileSize = 533.0f + (1/3.0f);
+const float Constants::MaxXY = 32.0f * Constants::TileSize;
+const float Constants::ChunkSize = Constants::TileSize / 16.0f;
+const float Constants::UnitSize = Constants::ChunkSize / 8.0f;
+const float Constants::Origin[] = { -Constants::MaxXY, 0.0f, -Constants::MaxXY };
+const float Constants::PI = 3.1415926f;
+const float Constants::MaxStandableHeight = 1.5f;
+const char* Constants::VMAPMagic = "VMAP041";
+bool Constants::ToWoWCoords = false;
+const float Constants::BaseUnitDim = 0.533333f;
+const int Constants::VertexPerMap = (Constants::TileSize / Constants::BaseUnitDim) + 0.5f;
+const int Constants::VertexPerTile = 40;
+const int Constants::TilesPerMap = Constants::VertexPerMap / Constants::VertexPerTile;
+
+void Utils::CreateDir( const std::string& Path )
+{
+#ifdef _WIN32
+ _mkdir( Path.c_str());
+#else
+ mkdir( Path.c_str(), 0777 );
+#endif
+}
+
+void Utils::Reverse(char word[])
+{
+ int len = strlen(word);
+ for (int i = 0;i < len / 2; i++)
+ {
+ word[i] ^= word[len-i-1];
+ word[len-i-1] ^= word[i];
+ word[i] ^= word[len-i-1];
+ }
+}
+
+std::string Utils::ReadString( FILE* file )
+{
+ std::string ret;
+ int i = 0;
+ while (true)
+ {
+ char b;
+ if (fread(&b, sizeof(char), 1, file) != 1 || b == 0)
+ break;
+ ret[i++] = b;
+ }
+ return ret;
+}
+
+uint32 Utils::Size( FILE* file )
+{
+ // store the old position
+ uint32 offset = ftell(file);
+ // Get file size
+ fseek(file, 0, SEEK_END);
+ uint32 size = ftell(file);
+ // reset back to the old position
+ fseek(file, offset, SEEK_SET);
+ return size;
+}
+
+Vector3 Utils::ToRecast( Vector3 val )
+{
+ return Vector3(-val.y, val.z, -val.x);
+}
+
+std::string Utils::GetAdtPath( std::string world, int x, int y )
+{
+ return "World\\Maps\\" + world + "\\" + world + "_" + Utils::ToString(x) + "_" + Utils::ToString(y) + ".adt";
+}
+
+std::string Utils::FixModelPath( std::string path )
+{
+ return Utils::GetPathBase(path) + ".M2";
+}
+
+G3D::Matrix4 Utils::RotationX(float angle)
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[2][2] = _cos;
+ ret[2][3] = _sin;
+ ret[3][2] = -_sin;
+ ret[3][3] = _cos;
+ return ret;
+}
+
+G3D::Matrix4 Utils::GetTransformation(IDefinition def)
+{
+ G3D::Matrix4 translation;
+ if (def.Position.x == 0.0f && def.Position.y == 0.0f && def.Position.z == 0.0f)
+ translation = G3D::Matrix4::identity();
+ else
+ translation = G3D::Matrix4::translation(-(def.Position.z - Constants::MaxXY),
+ -(def.Position.x - Constants::MaxXY), def.Position.y);
+
+ G3D::Matrix4 rotation = RotationX(ToRadians(def.Rotation.z)) * RotationY(ToRadians(def.Rotation.x)) * RotationZ(ToRadians(def.Rotation.y + 180));
+ if (def.Scale() < 1.0f || def.Scale() > 1.0f)
+ return G3D::Matrix4::scale(def.Scale()) * rotation * translation;
+ return rotation * translation;
+}
+
+G3D::Matrix4 Utils::RotationY( float angle )
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[1][1] = _cos;
+ ret[1][3] = -_sin;
+ ret[3][1] = _sin;
+ ret[3][3] = _cos;
+ return ret;
+}
+
+G3D::Matrix4 Utils::RotationZ( float angle )
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[1][1] = _cos;
+ ret[1][2] = _sin;
+ ret[2][1] = -_sin;
+ ret[2][2] = _cos;
+ return ret;
+}
+
+float Utils::ToRadians( float degrees )
+{
+ return Constants::PI * degrees / 180.0f;
+}
+
+Vector3 Utils::VectorTransform( Vector3 vec, G3D::Matrix4 matrix )
+{
+ Vector3 ret;
+ ret.x = vec.x * matrix[1][1] + vec.y * matrix[2][1] + vec.z * matrix[3][1] + matrix[4][1];
+ ret.y = vec.x * matrix[1][2] + vec.y * matrix[2][2] + vec.z * matrix[3][2] + matrix[4][2];
+ ret.z = vec.x * matrix[1][3] + vec.y * matrix[2][3] + vec.z * matrix[3][3] + matrix[4][3];
+ return ret;
+}
+
+std::string Utils::GetPathBase( std::string path )
+{
+ size_t lastIndex = path.find_last_of(".");
+ if (lastIndex != std::string::npos)
+ return path.substr(0, lastIndex);
+ return path;
+}
+
+Vector3 Vector3::Read( FILE* file )
+{
+ Vector3 ret;
+ if (fread(&ret, sizeof(Vector3), 1, file) != 1)
+ printf("Vector3::Read: Failed to read some data expected 1, read 0\n");
+ return ret;
+}
+
+Vector3 Utils::GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int /*x*/, int /*y*/)
+{
+ if (Utils::Distance(height, 0.0f) > 0.5f)
+ basePosition.z = 0.0f;
+ return Utils::VectorTransform(basePosition + Vector3(basePosition.x * Constants::UnitSize, basePosition.y * Constants::UnitSize, height), transformation);
+}
+
+float Utils::Distance( float x, float y )
+{
+ return sqrt(x*x + y*y);
+}
+
+std::string Utils::Replace( std::string str, const std::string& oldStr, const std::string& newStr )
+{
+ size_t pos = 0;
+ while((pos = str.find(oldStr, pos)) != std::string::npos)
+ {
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+ return str;
+}
+
+G3D::Matrix4 Utils::GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root )
+{
+ G3D::Matrix4 rootTransformation = Utils::GetTransformation(root);
+ G3D::Matrix4 translation = G3D::Matrix4::translation(inst.Position.x, inst.Position.y, inst.Position.z);
+ G3D::Matrix4 scale = G3D::Matrix4::scale(inst.Scale);
+ G3D::Matrix4 rotation = Utils::RotationY(Constants::PI);
+ G3D::Quat quat(-inst.QuatY, inst.QuatZ, -inst.QuatX, inst.QuatW);
+ G3D::Matrix4 quatRotation = quat.toRotationMatrix();
+
+ return scale * rotation * quatRotation ** translation * rootTransformation;
+}
+
+void Utils::SaveToDisk( FILE* stream, std::string path )
+{
+ FILE* disk = fopen(path.c_str(), "wb");
+ if (!disk)
+ {
+ printf("SaveToDisk: Could not save file %s to disk, please verify that you have write permissions on that directory\n", path.c_str());
+ return;
+ }
+
+ uint32 size = Utils::Size(stream);
+ uint8* data = new uint8[size];
+ // Read the data to an array
+ if (fread(data, 1, size, stream) != 1)
+ {
+ printf("SaveToDisk: Error reading from Stream while trying to save file %s to disck.\n", path.c_str());
+ return;
+ }
+ // And write it in the file
+ fwrite(data, 1, size, disk);
+
+ // Close the filestream
+ fclose(disk);
+ // Free the used memory
+ delete data;
+}
+
+Vector3 Utils::ToWoWCoords( Vector3 vec )
+{
+ return Vector3(vec.x, -vec.z, vec.y);
+}
+
+std::string Utils::GetExtension( std::string path )
+{
+ std::string::size_type idx = path.rfind('.');
+ std::string extension = "";
+
+ if(idx != std::string::npos)
+ extension = path.substr(idx+1);
+ return extension;
+}
+
+void MapChunkHeader::Read(FILE* stream)
+{
+ int count = 0;
+
+ count += fread(&Flags, sizeof(uint32), 1, stream);
+ count += fread(&IndexX, sizeof(uint32), 1, stream);
+ count += fread(&IndexY, sizeof(uint32), 1, stream);
+ count += fread(&Layers, sizeof(uint32), 1, stream);
+ count += fread(&DoodadRefs, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCVT, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCNR, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCLY, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCRF, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCAL, sizeof(uint32), 1, stream);
+ count += fread(&SizeMCAL, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCSH, sizeof(uint32), 1, stream);
+ count += fread(&SizeMCSH, sizeof(uint32), 1, stream);
+ count += fread(&AreaId, sizeof(uint32), 1, stream);
+ count += fread(&MapObjectRefs, sizeof(uint32), 1, stream);
+ count += fread(&Holes, sizeof(uint32), 1, stream);
+ LowQualityTextureMap = new uint32[4];
+ count += fread(LowQualityTextureMap, sizeof(uint32), 4, stream);
+ count += fread(&PredTex, sizeof(uint32), 1, stream);
+ count += fread(&NumberEffectDoodad, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCSE, sizeof(uint32), 1, stream);
+ count += fread(&SoundEmitters, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCLQ, sizeof(uint32), 1, stream);
+ count += fread(&SizeMCLQ, sizeof(uint32), 1, stream);
+ Position = Vector3::Read(stream);
+ count += fread(&OffsetMCCV, sizeof(uint32), 1, stream);
+
+ if (count != 27)
+ printf("MapChunkHeader::Read: Failed to read some data expected 27, read %d\n", count);
+}
+
+void MHDR::Read(FILE* stream)
+{
+ int count = 0;
+
+ count += fread(&Flags, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCIN, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMTEX, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMMDX, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMMID, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMWMO, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMWID, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMDDF, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMODF, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMFBO, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMH2O, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMTFX, sizeof(uint32), 1, stream);
+
+ if (count != 12)
+ printf("MHDR::Read: Failed to read some data expected 12, read %d\n", count);
+}
+
+void ModelHeader::Read(FILE* stream)
+{
+ int count = 0;
+
+ count += fread(&Magic, sizeof(char), 4, stream);
+ Magic[4] = '\0'; // null-terminate it.
+ count += fread(&Version, sizeof(uint32), 1, stream);
+ count += fread(&LengthModelName, sizeof(uint32), 1, stream);
+ count += fread(&OffsetName, sizeof(uint32), 1, stream);
+ count += fread(&ModelFlags, sizeof(uint32), 1, stream);
+ count += fread(&CountGlobalSequences, sizeof(uint32), 1, stream);
+ count += fread(&OffsetGlobalSequences, sizeof(uint32), 1, stream);
+ count += fread(&CountAnimations, sizeof(uint32), 1, stream);
+ count += fread(&OffsetAnimations, sizeof(uint32), 1, stream);
+ count += fread(&CountAnimationLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetAnimationLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountBones, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBones, sizeof(uint32), 1, stream);
+ count += fread(&CountKeyBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetKeyBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountVertices, sizeof(uint32), 1, stream);
+ count += fread(&OffsetVertices, sizeof(uint32), 1, stream);
+ count += fread(&CountViews, sizeof(uint32), 1, stream);
+ count += fread(&CountColors, sizeof(uint32), 1, stream);
+ count += fread(&OffsetColors, sizeof(uint32), 1, stream);
+ count += fread(&CountTextures, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTextures, sizeof(uint32), 1, stream);
+ count += fread(&CountTransparency, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTransparency, sizeof(uint32), 1, stream);
+ count += fread(&CountUvAnimation, sizeof(uint32), 1, stream);
+ count += fread(&OffsetUvAnimation, sizeof(uint32), 1, stream);
+ count += fread(&CountTexReplace, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTexReplace, sizeof(uint32), 1, stream);
+ count += fread(&CountRenderFlags, sizeof(uint32), 1, stream);
+ count += fread(&OffsetRenderFlags, sizeof(uint32), 1, stream);
+ count += fread(&CountBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountTexLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTexLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountTexUnits, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTexUnits, sizeof(uint32), 1, stream);
+ count += fread(&CountTransLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTransLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountUvAnimLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetUvAnimLookup, sizeof(uint32), 1, stream);
+ VertexBox[0] = Vector3::Read(stream);
+ VertexBox[1] = Vector3::Read(stream);
+ count += fread(&VertexRadius, sizeof(float), 1, stream);
+ BoundingBox[0] = Vector3::Read(stream);
+ BoundingBox[1] = Vector3::Read(stream);
+ count += fread(&BoundingRadius, sizeof(float), 1, stream);
+ count += fread(&CountBoundingTriangles, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoundingTriangles, sizeof(uint32), 1, stream);
+ count += fread(&CountBoundingVertices, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoundingVertices, sizeof(uint32), 1, stream);
+ count += fread(&CountBoundingNormals, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoundingNormals, sizeof(uint32), 1, stream);
+
+ if (count != 51)
+ printf("ModelHeader::Read: Failed to read some data expected 51, read %d\n", count);
+
+}
+
+WorldModelHeader WorldModelHeader::Read(FILE* stream)
+{
+ WorldModelHeader ret;
+ int count = 0;
+
+ count += fread(&ret.CountMaterials, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountGroups, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountPortals, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountLights, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountModels, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountDoodads, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountSets, sizeof(uint32), 1, stream);
+ count += fread(&ret.AmbientColorUnk, sizeof(uint32), 1, stream);
+ count += fread(&ret.WmoId, sizeof(uint32), 1, stream);
+ ret.BoundingBox[0] = Vector3::Read(stream);
+ ret.BoundingBox[1] = Vector3::Read(stream);
+ count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
+
+ if (count != 10)
+ printf("WorldModelHeader::Read: Failed to read some data expected 10, read %d\n", count);
+
+ return ret;
+}
+
+DoodadInstance DoodadInstance::Read(FILE* stream)
+{
+ DoodadInstance ret;
+ int count = 0;
+
+ count += fread(&ret.FileOffset, sizeof(uint32), 1, stream);
+ ret.Position = Vector3::Read(stream);
+ count += fread(&ret.QuatW, sizeof(float), 1, stream);
+ count += fread(&ret.QuatX, sizeof(float), 1, stream);
+ count += fread(&ret.QuatY, sizeof(float), 1, stream);
+ count += fread(&ret.QuatZ, sizeof(float), 1, stream);
+ count += fread(&ret.Scale, sizeof(float), 1, stream);
+ count += fread(&ret.LightColor, sizeof(uint32), 1, stream);
+
+ if (count != 7)
+ printf("DoodadInstance::Read: Failed to read some data expected 7, read %d\n", count);
+
+ return ret;
+}
+
+DoodadSet DoodadSet::Read(FILE* stream)
+{
+ DoodadSet ret;
+ char name[21];
+ int count = 0;
+
+ count += fread(&name, sizeof(char), 20, stream);
+ name[20] = '\0';
+ ret.Name = name;
+ count += fread(&ret.FirstInstanceIndex, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountInstances, sizeof(uint32), 1, stream);
+ count += fread(&ret.UnknownZero, sizeof(uint32), 1, stream);
+
+ if (count != 23)
+ printf("DoodadSet::Read: Failed to read some data expected 23, read %d\n", count);
+
+ return ret;
+}
+
+LiquidHeader LiquidHeader::Read(FILE* stream)
+{
+ LiquidHeader ret;
+ int count = 0;
+ count += fread(&ret.CountXVertices, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountYVertices, sizeof(uint32), 1, stream);
+ count += fread(&ret.Width, sizeof(uint32), 1, stream);
+ count += fread(&ret.Height, sizeof(uint32), 1, stream);
+ ret.BaseLocation = Vector3::Read(stream);
+ count += fread(&ret.MaterialId, sizeof(uint16), 1, stream);
+
+ if (count != 5)
+ printf("LiquidHeader::Read: Failed to read some data expected 5, read %d\n", count);
+
+ return ret;
+}
+
+LiquidData LiquidData::Read(FILE* stream, LiquidHeader& header)
+{
+ LiquidData ret;
+ ret.HeightMap = new float*[header.CountXVertices];
+ for (uint32 i = 0; i < header.CountXVertices; ++i)
+ ret.HeightMap[i] = new float[header.CountYVertices];
+
+ ret.RenderFlags = new uint8*[header.Width];
+ for (uint32 i = 0; i < header.Width; ++i)
+ ret.RenderFlags[i] = new uint8[header.Height];
+
+ for (uint32 y = 0; y < header.CountYVertices; y++)
+ {
+ for (uint32 x = 0; x < header.CountXVertices; x++)
+ {
+ uint32 discard;
+ float tmp;
+ if (fread(&discard, sizeof(uint32), 1, stream) == 1 &&
+ fread(&tmp, sizeof(float), 1, stream) == 1)
+ {
+ ret.HeightMap[x][y] = tmp;
+ }
+ }
+ }
+
+ for (uint32 y = 0; y < header.Height; y++)
+ {
+ for (uint32 x = 0; x < header.Width; x++)
+ {
+ uint8 tmp = 0;
+ if (fread(&tmp, sizeof(uint8), 1, stream) == 1)
+ ret.RenderFlags[x][y] = tmp;
+ }
+ }
+
+ return ret;
+}
+
+H2ORenderMask H2ORenderMask::Read(FILE* stream)
+{
+ H2ORenderMask ret;
+ if (int count = fread(&ret.Mask, sizeof(uint8), 8, stream) != 8)
+ printf("H2OHeader::Read: Failed to read some data expected 8, read %d\n", count);
+ return ret;
+}
+
+bool MCNKLiquidData::IsWater(int x, int y, float height)
+{
+ if (!Heights)
+ return false;
+ if (!Mask.ShouldRender(x, y))
+ return false;
+ float diff = Heights[x][y] - height;
+ if (diff > Constants::MaxStandableHeight)
+ return true;
+ return false;
+}
+
+H2OHeader H2OHeader::Read(FILE* stream)
+{
+ H2OHeader ret;
+ int count = 0;
+ count += fread(&ret.OffsetInformation, sizeof(uint32), 1, stream);
+ count += fread(&ret.LayerCount, sizeof(uint32), 1, stream);
+ count += fread(&ret.OffsetRender, sizeof(uint32), 1, stream);
+
+ if (count != 3)
+ printf("H2OHeader::Read: Failed to read some data expected 3, read %d\n", count);
+
+ return ret;
+}
+
+H2OInformation H2OInformation::Read(FILE* stream)
+{
+ H2OInformation ret;
+ int count = 0;
+ count += fread(&ret.LiquidType, sizeof(uint16), 1, stream);
+ count += fread(&ret.Flags, sizeof(uint16), 1, stream);
+ count += fread(&ret.HeightLevel1, sizeof(float), 1, stream);
+ count += fread(&ret.HeightLevel2, sizeof(float), 1, stream);
+ count += fread(&ret.OffsetX, sizeof(uint8), 1, stream);
+ count += fread(&ret.OffsetY, sizeof(uint8), 1, stream);
+ count += fread(&ret.Width, sizeof(uint8), 1, stream);
+ count += fread(&ret.Height, sizeof(uint8), 1, stream);
+ count += fread(&ret.OffsetMask2, sizeof(uint32), 1, stream);
+ count += fread(&ret.OffsetHeightmap, sizeof(uint32), 1, stream);
+
+ if (count != 10)
+ printf("H2OInformation::Read: Failed to read some data expected 10, read %d\n", count);
+
+ return ret;
+}
+
+char* Utils::GetPlainName(const char* FileName)
+{
+ char* temp;
+
+ if((temp = (char*)strrchr(FileName, '\\')) != NULL)
+ FileName = temp + 1;
+ return (char*)FileName;
+}
+
+WMOGroupHeader WMOGroupHeader::Read( FILE* stream )
+{
+ WMOGroupHeader ret;
+ int count = 0;
+ count += fread(&ret.OffsetGroupName, sizeof(uint32), 1, stream);
+ count += fread(&ret.OffsetDescriptiveName, sizeof(uint32), 1, stream);
+ count += fread(&ret.Flags, sizeof(uint32), 1, stream);
+ ret.BoundingBox[0] = Vector3::Read(stream);
+ ret.BoundingBox[1] = Vector3::Read(stream);
+ count += fread(&ret.OffsetPortals, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountPortals, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountBatches, sizeof(uint16), 4, stream);
+ count += fread(&ret.Fogs, sizeof(uint8), 4, stream);
+ count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
+ count += fread(&ret.WmoId, sizeof(uint32), 1, stream);
+
+ if (count != 15)
+ printf("WMOGroupHeader::Read: Failed to read some data expected 15, read %d\n", count);
+
+ return ret;
+}
diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h
new file mode 100644
index 00000000000..64fb1bb35ba
--- /dev/null
+++ b/src/tools/mesh_extractor/Utils.h
@@ -0,0 +1,381 @@
+#ifndef UTILS_H
+#define UTILS_H
+#include <cstdio>
+#include <string>
+#include <sstream>
+
+#include "G3D/Matrix4.h"
+#include "DetourNavMesh.h"
+
+#include "Define.h"
+#include "Constants.h"
+
+#include <ace/Stack_Trace.h>
+
+struct WorldModelDefinition;
+class DoodadInstance;
+
+#define ASSERT(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } }
+
+struct Vector3
+{
+ Vector3() {}
+ Vector3(float X, float Y, float Z) : x(X), y(Y), z(Z) {}
+ float x;
+ float y;
+ float z;
+
+ Vector3 operator +(Vector3 const& other)
+ {
+ return Vector3(x + other.x, y + other.y, z + other.z);
+ }
+
+ static Vector3 Read(FILE* file);
+};
+
+struct TilePos
+{
+ TilePos(int x, int y) : X(x), Y(y) {}
+ int X;
+ int Y;
+};
+
+template<typename T>
+struct Triangle
+{
+ Triangle() {}
+ Triangle(Constants::TriangleType type, T v0, T v1, T v2) : V0(v0), V1(v1), V2(v2), Type(type) {}
+ T V0;
+ T V1;
+ T V2;
+ Constants::TriangleType Type;
+};
+
+class MapChunkHeader
+{
+public:
+ MapChunkHeader() {}
+ uint32 Flags;
+ uint32 IndexX;
+ uint32 IndexY;
+ uint32 Layers;
+ uint32 DoodadRefs;
+ uint32 OffsetMCVT;
+ uint32 OffsetMCNR;
+ uint32 OffsetMCLY;
+ uint32 OffsetMCRF;
+ uint32 OffsetMCAL;
+ uint32 SizeMCAL;
+ uint32 OffsetMCSH;
+ uint32 SizeMCSH;
+ uint32 AreaId;
+ uint32 MapObjectRefs;
+ uint32 Holes;
+ uint32* LowQualityTextureMap;
+ uint32 PredTex;
+ uint32 NumberEffectDoodad;
+ uint32 OffsetMCSE;
+ uint32 SoundEmitters;
+ uint32 OffsetMCLQ;
+ uint32 SizeMCLQ;
+ Vector3 Position;
+ uint32 OffsetMCCV;
+
+ void Read(FILE* stream);
+};
+
+class MHDR
+{
+public:
+ MHDR() {}
+ uint32 Flags;
+ uint32 OffsetMCIN;
+ uint32 OffsetMTEX;
+ uint32 OffsetMMDX;
+ uint32 OffsetMMID;
+ uint32 OffsetMWMO;
+ uint32 OffsetMWID;
+ uint32 OffsetMDDF;
+ uint32 OffsetMODF;
+ uint32 OffsetMFBO;
+ uint32 OffsetMH2O;
+ uint32 OffsetMTFX;
+
+ void Read(FILE* stream);
+};
+
+class ModelHeader
+{
+public:
+ char Magic[5];
+ uint32 Version;
+ uint32 LengthModelName;
+ uint32 OffsetName;
+ uint32 ModelFlags;
+ uint32 CountGlobalSequences;
+ uint32 OffsetGlobalSequences;
+ uint32 CountAnimations;
+ uint32 OffsetAnimations;
+ uint32 CountAnimationLookup;
+ uint32 OffsetAnimationLookup;
+ uint32 CountBones;
+ uint32 OffsetBones;
+ uint32 CountKeyBoneLookup;
+ uint32 OffsetKeyBoneLookup;
+ uint32 CountVertices;
+ uint32 OffsetVertices;
+ uint32 CountViews;
+ uint32 CountColors;
+ uint32 OffsetColors;
+ uint32 CountTextures;
+ uint32 OffsetTextures;
+ uint32 CountTransparency;
+ uint32 OffsetTransparency;
+ uint32 CountUvAnimation;
+ uint32 OffsetUvAnimation;
+ uint32 CountTexReplace;
+ uint32 OffsetTexReplace;
+ uint32 CountRenderFlags;
+ uint32 OffsetRenderFlags;
+ uint32 CountBoneLookup;
+ uint32 OffsetBoneLookup;
+ uint32 CountTexLookup;
+ uint32 OffsetTexLookup;
+ uint32 CountTexUnits;
+ uint32 OffsetTexUnits;
+ uint32 CountTransLookup;
+ uint32 OffsetTransLookup;
+ uint32 CountUvAnimLookup;
+ uint32 OffsetUvAnimLookup;
+ Vector3 VertexBox[2];
+ float VertexRadius;
+ Vector3 BoundingBox[2];
+ float BoundingRadius;
+ uint32 CountBoundingTriangles;
+ uint32 OffsetBoundingTriangles;
+ uint32 CountBoundingVertices;
+ uint32 OffsetBoundingVertices;
+ uint32 CountBoundingNormals;
+ uint32 OffsetBoundingNormals;
+
+ void Read(FILE* stream);
+};
+
+class WorldModelHeader
+{
+public:
+ WorldModelHeader() {}
+ uint32 CountMaterials;
+ uint32 CountGroups;
+ uint32 CountPortals;
+ uint32 CountLights;
+ uint32 CountModels;
+ uint32 CountDoodads;
+ uint32 CountSets;
+ uint32 AmbientColorUnk;
+ uint32 WmoId;
+ Vector3 BoundingBox[2];
+ uint32 LiquidTypeRelated;
+
+ static WorldModelHeader Read(FILE* stream);
+};
+
+class DoodadInstance
+{
+public:
+ DoodadInstance() {}
+ uint32 FileOffset;
+ std::string File;
+ Vector3 Position;
+ float QuatW;
+ float QuatX;
+ float QuatY;
+ float QuatZ;
+ float Scale;
+ uint32 LightColor;
+
+ static DoodadInstance Read(FILE* stream);
+};
+
+class DoodadSet
+{
+public:
+ DoodadSet() {}
+ std::string Name;
+ uint32 FirstInstanceIndex;
+ uint32 CountInstances;
+ uint32 UnknownZero;
+
+ static DoodadSet Read(FILE* stream);
+};
+
+class LiquidHeader
+{
+public:
+ LiquidHeader() {}
+ uint32 CountXVertices;
+ uint32 CountYVertices;
+ uint32 Width;
+ uint32 Height;
+ Vector3 BaseLocation;
+ uint16 MaterialId;
+
+ static LiquidHeader Read(FILE* stream);
+};
+
+class LiquidData
+{
+public:
+ LiquidData() {}
+ float** HeightMap;
+ uint8** RenderFlags;
+
+ bool ShouldRender(int x, int y)
+ {
+ return RenderFlags[x][y] != 0x0F;
+ }
+
+ static LiquidData Read(FILE* stream, LiquidHeader& header);
+};
+
+class H2ORenderMask
+{
+public:
+ H2ORenderMask() {}
+ uint8 Mask[8];
+
+ bool ShouldRender(int x, int y)
+ {
+ return (Mask[y] >> x & 1) != 0;
+ }
+
+ static H2ORenderMask Read(FILE* stream);
+};
+
+class MCNKLiquidData
+{
+public:
+ MCNKLiquidData() {}
+ MCNKLiquidData(float** heights, H2ORenderMask mask) : Heights(heights), Mask(mask) {}
+
+ float** Heights;
+ H2ORenderMask Mask;
+
+ bool IsWater(int x, int y, float height);
+};
+
+class H2OHeader
+{
+public:
+ H2OHeader() {}
+ uint32 OffsetInformation;
+ uint32 LayerCount;
+ uint32 OffsetRender;
+
+ static H2OHeader Read(FILE* stream);
+};
+
+class H2OInformation
+{
+public:
+ H2OInformation() {}
+ uint16 LiquidType;
+ uint16 Flags;
+ float HeightLevel1;
+ float HeightLevel2;
+ uint8 OffsetX;
+ uint8 OffsetY;
+ uint8 Width;
+ uint8 Height;
+ uint32 OffsetMask2;
+ uint32 OffsetHeightmap;
+
+ static H2OInformation Read(FILE* stream);
+};
+
+class WMOGroupHeader
+{
+public:
+ WMOGroupHeader() {}
+
+ uint32 OffsetGroupName;
+ uint32 OffsetDescriptiveName;
+ uint32 Flags;
+ Vector3 BoundingBox[2];
+ uint32 OffsetPortals;
+ uint32 CountPortals;
+ uint16 CountBatches[4];
+ uint8 Fogs[4];
+ uint32 LiquidTypeRelated;
+ uint32 WmoId;
+
+ static WMOGroupHeader Read(FILE* stream);
+};
+
+// Dummy class to act as an interface.
+class IDefinition
+{
+public:
+ Vector3 Position;
+ Vector3 Rotation;
+ virtual float Scale() const { return 1.0f; };
+};
+
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+class Utils
+{
+public:
+ static void Reverse(char word[]);
+ static std::string ReadString(FILE* file);
+ static uint32 Size(FILE* file);
+ static Vector3 ToRecast( Vector3 val );
+ static std::string GetAdtPath(std::string world, int x, int y);
+ static std::string FixModelPath(std::string path);
+ static G3D::Matrix4 GetTransformation(IDefinition def);
+ /// They say its better to declare template functions in the header files.
+ template <typename T>
+ static std::string ToString(T val)
+ {
+ std::stringstream ss;
+ ss << val;
+ return ss.str();
+ }
+ static G3D::Matrix4 RotationX(float angle);
+ static G3D::Matrix4 RotationY(float angle);
+ static G3D::Matrix4 RotationZ(float angle);
+ static float ToRadians(float degrees);
+ static Vector3 VectorTransform(Vector3 vec, G3D::Matrix4 matrix);
+ static std::string GetPathBase(std::string path);
+ static Vector3 GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y);
+ static float Distance(float x, float y);
+ template<typename T>
+ static bool IsAllZero(T* arr, uint32 size)
+ {
+ for (uint32 i = 0; i < size; ++i)
+ if (arr[i])
+ return false;
+ return true;
+ }
+ static std::string Replace( std::string str, const std::string& oldStr, const std::string& newStr );
+ static G3D::Matrix4 GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root );
+ static void CreateDir( const std::string& Path );
+ static void SaveToDisk(FILE* stream, std::string path);
+ static Vector3 ToWoWCoords( Vector3 vec );
+ static std::string GetExtension( std::string path );
+ static char* GetPlainName(const char* FileName);
+};
+#endif
diff --git a/src/tools/mesh_extractor/WDT.cpp b/src/tools/mesh_extractor/WDT.cpp
new file mode 100644
index 00000000000..70d140e79ed
--- /dev/null
+++ b/src/tools/mesh_extractor/WDT.cpp
@@ -0,0 +1,60 @@
+#include "WDT.h"
+#include "Chunk.h"
+#include "ChunkedData.h"
+#include "Utils.h"
+#include "WorldModelHandler.h"
+
+WDT::WDT(std::string file) : IsGlobalModel(false), IsValid(false)
+{
+ Data = new ChunkedData(file, 2);
+ ReadTileTable();
+ ReadGlobalModel();
+}
+
+void WDT::ReadGlobalModel()
+{
+ Chunk* fileChunk = Data->GetChunkByName("MWMO");
+ Chunk* defChunk = Data->GetChunkByName("MODF");
+ if (!fileChunk || !defChunk)
+ return;
+
+ IsGlobalModel = true;
+ ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream());
+ ModelFile = Utils::ReadString(fileChunk->GetStream());
+}
+
+void WDT::ReadTileTable()
+{
+ Chunk* chunk = Data->GetChunkByName("MAIN");
+ if (!chunk)
+ return;
+ IsValid = true;
+ FILE* stream = chunk->GetStream();
+ for (int y = 0; y < 64; ++y)
+ {
+ for (int x = 0; x < 64; ++x)
+ {
+ const uint32 hasTileFlag = 0x1;
+ uint32 flags;
+ uint32 discard;
+ int count = 0;
+ count += fread(&flags, sizeof(uint32), 1, stream);
+ count += fread(&discard, sizeof(uint32), 1, stream);
+
+ if (count != 2)
+ printf("WDT::ReadTileTable: Failed to read some data expected 2, read %d\n", count);
+
+ if (flags & hasTileFlag)
+ TileTable.push_back(TilePos(x, y));
+
+ }
+ }
+}
+
+bool WDT::HasTile( int x, int y )
+{
+ for (std::vector<TilePos>::iterator itr = TileTable.begin(); itr != TileTable.end(); ++itr)
+ if (itr->X == x && itr->Y == y)
+ return true;
+ return false;
+}
diff --git a/src/tools/mesh_extractor/WDT.h b/src/tools/mesh_extractor/WDT.h
new file mode 100644
index 00000000000..a12aa65218b
--- /dev/null
+++ b/src/tools/mesh_extractor/WDT.h
@@ -0,0 +1,27 @@
+#ifndef WDT_H
+#define WDT_H
+#include <string>
+#include <vector>
+
+#include "ChunkedData.h"
+#include "WorldModelHandler.h"
+#include "Utils.h"
+
+class WDT
+{
+public:
+ WDT(std::string file);
+
+ ChunkedData* Data;
+ std::vector<TilePos> TileTable;
+ bool IsGlobalModel;
+ bool IsValid;
+ std::string ModelFile;
+ WorldModelDefinition ModelDefinition;
+ bool HasTile(int x, int y);
+private:
+ void ReadGlobalModel();
+ void ReadTileTable();
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelGroup.cpp b/src/tools/mesh_extractor/WorldModelGroup.cpp
new file mode 100644
index 00000000000..21e1c1e63e1
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelGroup.cpp
@@ -0,0 +1,143 @@
+#include "WorldModelGroup.h"
+#include "ChunkedData.h"
+#include "Chunk.h"
+#include "Utils.h"
+
+WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false)
+{
+ Data = new ChunkedData(path);
+ if (!Data->Stream)
+ {
+ IsBad = true;
+ return;
+ }
+ Chunk* mainChunk = Data->GetChunkByName("MOGP");
+ int32 firstSub = mainChunk->FindSubChunkOffset("MOPY");
+ if (firstSub == -1)
+ return;
+
+ Name = Utils::GetPlainName(path.c_str());
+
+ FILE* stream = mainChunk->GetStream();
+ fseek(stream, firstSub, SEEK_SET);
+ SubData = new ChunkedData(stream, mainChunk->Length - firstSub);
+
+ ReadHeader();
+ ReadMaterials();
+ ReadTriangles();
+ ReadVertices();
+ ReadNormals();
+ ReadLiquid();
+ ReadBatches();
+}
+
+void WorldModelGroup::ReadNormals()
+{
+ Chunk* chunk = SubData->GetChunkByName("MONR");
+ if (!chunk)
+ return;
+
+ uint32 normalCount = chunk->Length / 12;
+ ASSERT(normalCount == Vertices.size() && "normalCount is different than the Vertices count");
+ Normals.reserve(normalCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < normalCount; i++)
+ Normals.push_back(Vector3::Read(stream));
+}
+
+void WorldModelGroup::ReadLiquid()
+{
+ Chunk* chunk = SubData->GetChunkByName("MLIQ");
+ if (!chunk)
+ return;
+
+ HasLiquidData = true;
+ FILE* stream = chunk->GetStream();
+ LiquidDataHeader = LiquidHeader::Read(stream);
+ LiquidDataGeometry = LiquidData::Read(stream, LiquidDataHeader);
+}
+
+void WorldModelGroup::ReadVertices()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOVT");
+ if (!chunk)
+ return;
+
+ uint32 verticeCount = chunk->Length / 12;
+ Vertices.reserve(verticeCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < verticeCount; i++)
+ Vertices.push_back(Vector3::Read(stream));
+}
+
+void WorldModelGroup::ReadTriangles()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOVI");
+ if (!chunk)
+ return;
+
+ uint32 triangleCount = chunk->Length / 6;
+ ASSERT(triangleCount == TriangleFlags.size() && "triangleCount != TriangleFlags.size()");
+ FILE* stream = chunk->GetStream();
+ Triangles.reserve(triangleCount);
+ for (uint32 i = 0; i < triangleCount; i++)
+ {
+ uint16 v0;
+ uint16 v1;
+ uint16 v2;
+ int count = 0;
+ count += fread(&v0, sizeof(uint16), 1, stream);
+ count += fread(&v1, sizeof(uint16), 1, stream);
+ count += fread(&v2, sizeof(uint16), 1, stream);
+ if (count != 3)
+ printf("WorldModelGroup::ReadMaterials: Error reading data, expected 3, read %d\n", count);
+
+ Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_WMO, v0, v1, v2));
+ }
+}
+
+void WorldModelGroup::ReadMaterials()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOPY");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ uint32 triangleCount = chunk->Length / 2;
+ TriangleFlags.reserve(triangleCount);
+ TriangleMaterials.reserve(triangleCount);
+ for (uint32 i = 0; i < triangleCount; i++)
+ {
+ uint8 tmp;
+ if (fread(&tmp, sizeof(uint8), 1, stream) != 1)
+ printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n");
+ TriangleFlags.push_back(tmp);
+ // Read again for material.
+ if (fread(&tmp, sizeof(uint8), 1, stream) != 1)
+ printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n");
+ TriangleMaterials.push_back(tmp);
+ }
+}
+
+void WorldModelGroup::ReadHeader()
+{
+ Chunk* chunk = Data->GetChunkByName("MOGP");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ Header = WMOGroupHeader::Read(stream);
+}
+
+void WorldModelGroup::ReadBatches()
+{
+ Chunk* chunk = Data->GetChunkByName("MOBA");
+ if (!chunk)
+ return;
+
+ MOBALength = chunk->Length / 2;
+ MOBA = new uint16[MOBALength];
+ uint32 count = (uint32)fread(MOBA, sizeof(uint16), MOBALength, chunk->GetStream());
+ if (count != MOBALength)
+ printf("WorldModelGroup::ReadBatches: Error reading data, expected %u, read %u\n", MOBALength, count);
+}
diff --git a/src/tools/mesh_extractor/WorldModelGroup.h b/src/tools/mesh_extractor/WorldModelGroup.h
new file mode 100644
index 00000000000..e4fe34cbc75
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelGroup.h
@@ -0,0 +1,38 @@
+#ifndef WMOGROUP_H
+#define WMOGROUP_H
+#include "ChunkedData.h"
+#include "Utils.h"
+
+class WorldModelGroup
+{
+public:
+ WorldModelGroup(std::string path, int groupIndex);
+ ChunkedData* Data;
+ ChunkedData* SubData;
+ int GroupIndex;
+ std::string Name;
+ WMOGroupHeader Header;
+
+ std::vector<uint8> TriangleFlags;
+ std::vector<uint8> TriangleMaterials;
+ std::vector<Triangle<uint16> > Triangles;
+ std::vector<Vector3> Vertices;
+ std::vector<Vector3> Normals;
+ // @ToDo: Research.
+ uint16* MOBA;
+ uint32 MOBALength;
+
+ bool HasLiquidData;
+ bool IsBad;
+ LiquidHeader LiquidDataHeader;
+ LiquidData LiquidDataGeometry;
+private:
+ void ReadNormals();
+ void ReadLiquid();
+ void ReadVertices();
+ void ReadTriangles();
+ void ReadMaterials();
+ void ReadHeader();
+ void ReadBatches();
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelHandler.cpp b/src/tools/mesh_extractor/WorldModelHandler.cpp
new file mode 100644
index 00000000000..ecfff4e97d4
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelHandler.cpp
@@ -0,0 +1,204 @@
+#include "WorldModelHandler.h"
+#include "WorldModelRoot.h"
+#include "Chunk.h"
+#include "Cache.h"
+#include "Model.h"
+#include "Define.h"
+#include "G3D/Matrix4.h"
+#include <cstdio>
+
+WorldModelDefinition WorldModelDefinition::Read( FILE* file )
+{
+ WorldModelDefinition ret;
+ int count = 0;
+ count += fread(&ret.MwidIndex, sizeof(uint32), 1, file);
+ count += fread(&ret.UniqueId, sizeof(uint32), 1, file);
+ ret.Position = Vector3::Read(file);
+ ret.Rotation = Vector3::Read(file);
+ ret.UpperExtents = Vector3::Read(file);
+ ret.LowerExtents = Vector3::Read(file);
+ count += fread(&ret.Flags, sizeof(uint16), 1, file);
+ count += fread(&ret.DoodadSet, sizeof(uint16), 1, file);
+ uint32 discard;
+ count += fread(&discard, sizeof(uint32), 1, file);
+
+ if (count != 5)
+ printf("WorldModelDefinition::Read: Error reading data, expected 5, read %d\n", count);
+ return ret;
+}
+
+
+WorldModelHandler::WorldModelHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
+{
+ if (!adt->HasObjectData)
+ return;
+ ReadModelPaths();
+ ReadDefinitions();
+}
+
+void WorldModelHandler::ProcessInternal( ChunkedData* subChunks )
+{
+ if (!IsSane())
+ return;
+ Chunk* wmoReferencesChunk = subChunks->GetChunkByName("MCRW");
+ if (!wmoReferencesChunk)
+ return;
+ FILE* stream = wmoReferencesChunk->GetStream();
+ uint32 refCount = wmoReferencesChunk->Length / 4;
+ for (uint32 i = 0; i < refCount; i++)
+ {
+ int32 index;
+ if (fread(&index, sizeof(int32), 1, stream) != 1)
+ printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n");
+
+ if (index < 0 || uint32(index) >= _definitions->size())
+ continue;
+
+ WorldModelDefinition wmo = (*_definitions)[index];
+
+ if (_drawn.find(wmo.UniqueId) != _drawn.end())
+ continue;
+ _drawn.insert(wmo.UniqueId);
+
+ if (wmo.MwidIndex >= _paths->size())
+ continue;
+
+ std::string path = (*_paths)[wmo.MwidIndex];
+ WorldModelRoot* model = Cache->WorldModelCache.Get(path);
+ if (!model)
+ {
+ model = new WorldModelRoot(path);
+ Cache->WorldModelCache.Insert(path, model);
+ }
+
+ Vertices.reserve(1000);
+ Triangles.reserve(1000);
+
+ InsertModelGeometry(Vertices, Triangles, wmo, model);
+ }
+}
+
+void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root )
+{
+ G3D::Matrix4 transformation = Utils::GetTransformation(def);
+ for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
+ {
+ uint32 vertOffset = verts.size();
+ for (std::vector<Vector3>::iterator itr2 = group->Vertices.begin(); itr2 != group->Vertices.end(); ++itr2)
+ verts.push_back(Utils::VectorTransform(*itr2, transformation));
+
+ for (uint32 i = 0; i < group->Triangles.size(); ++i)
+ {
+ // only include collidable tris
+ if ((group->TriangleFlags[i] & 0x04) != 0 && group->TriangleMaterials[i] != 0xFF)
+ continue;
+ Triangle<uint16> tri = group->Triangles[i];
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, tri.V0 + vertOffset, tri.V1 + vertOffset, tri.V2 + vertOffset));
+ }
+ }
+
+ if (def.DoodadSet < root->DoodadSets.size())
+ {
+ DoodadSet set = root->DoodadSets[def.DoodadSet];
+ std::vector<DoodadInstance> instances;
+ instances.reserve(set.CountInstances);
+ for (uint32 i = set.FirstInstanceIndex; i < (set.CountInstances + set.FirstInstanceIndex); i++)
+ {
+ if (i >= root->DoodadInstances.size())
+ break;
+ instances.push_back(root->DoodadInstances[i]);
+ }
+
+ for (std::vector<DoodadInstance>::iterator instance = instances.begin(); instance != instances.end(); ++instance)
+ {
+ Model* model = Cache->ModelCache.Get(instance->File);
+ if (!model)
+ {
+ model = new Model(instance->File);
+ Cache->ModelCache.Insert(instance->File, model);
+ }
+
+ if (!model->IsCollidable)
+ continue;
+ G3D::Matrix4 doodadTransformation = Utils::GetWmoDoodadTransformation(*instance, def);
+ int vertOffset = verts.size();
+ for (std::vector<Vector3>::iterator itr2 = model->Vertices.begin(); itr2 != model->Vertices.end(); ++itr2)
+ verts.push_back(Utils::VectorTransform(*itr2, doodadTransformation));
+ for (std::vector<Triangle<uint16> >::iterator itr2 = model->Triangles.begin(); itr2 != model->Triangles.end(); ++itr2)
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, itr2->V0 + vertOffset, itr2->V1 + vertOffset, itr2->V2 + vertOffset));
+ }
+
+ for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
+ {
+ if (!group->HasLiquidData)
+ continue;
+
+ for (uint32 y = 0; y < group->LiquidDataHeader.Height; y++)
+ {
+ for (uint32 x = 0; x < group->LiquidDataHeader.Width; x++)
+ {
+ if (!group->LiquidDataGeometry.ShouldRender(x, y))
+ continue;
+
+ uint32 vertOffset = verts.size();
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x][y], x, y));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x + 1][y], x + 1, y));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x][y + 1], x, y + 1));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1));
+
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset + 2, vertOffset + 1));
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
+
+ }
+ }
+ }
+ }
+}
+
+void WorldModelHandler::ReadDefinitions()
+{
+ Chunk* chunk = Source->ObjectData->GetChunkByName("MODF");
+ if (!chunk)
+ return;
+
+ const int32 definitionSize = 64;
+ uint32 definitionCount = chunk->Length / definitionSize;
+ _definitions = new std::vector<WorldModelDefinition>;
+ _definitions->reserve(definitionCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < definitionCount; i++)
+ _definitions->push_back(WorldModelDefinition::Read(stream));
+}
+
+void WorldModelHandler::ReadModelPaths()
+{
+ Chunk* mwid = Source->ObjectData->GetChunkByName("MWID");
+ Chunk* mwmo = Source->ObjectData->GetChunkByName("MWMO");
+ if (!mwid || !mwmo)
+ return;
+
+ uint32 paths = mwid->Length / 4;
+ _paths = new std::vector<std::string>;
+ _paths->reserve(paths);
+ for (uint32 i = 0; i < paths; i++)
+ {
+ FILE* stream = mwid->GetStream();
+ fseek(stream, i * 4, SEEK_CUR);
+ uint32 offset;
+ if (fread(&offset, sizeof(uint32), 1, stream) != 1)
+ printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n");
+ FILE* dataStream = mwmo->GetStream();
+ fseek(dataStream, offset + mwmo->Offset, SEEK_SET);
+ _paths->push_back(Utils::ReadString(dataStream));
+ }
+}
+
+WorldModelHandler::~WorldModelHandler()
+{
+ delete _definitions;
+ delete _paths;
+}
diff --git a/src/tools/mesh_extractor/WorldModelHandler.h b/src/tools/mesh_extractor/WorldModelHandler.h
new file mode 100644
index 00000000000..29715ded696
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelHandler.h
@@ -0,0 +1,47 @@
+#ifndef WMODEL_HNDL_H
+#define WMODEL_HNDL_H
+#include "Define.h"
+#include "Utils.h"
+#include "WorldModelRoot.h"
+#include "ObjectDataHandler.h"
+
+#include <set>
+#include <vector>
+
+class ADT;
+
+struct WorldModelDefinition : public IDefinition
+{
+public:
+ WorldModelDefinition() {}
+
+ uint32 MwidIndex;
+ uint32 UniqueId;
+ Vector3 UpperExtents;
+ Vector3 LowerExtents;
+ uint16 Flags;
+ uint16 DoodadSet;
+
+ static WorldModelDefinition Read(FILE* file);
+};
+
+class WorldModelHandler : public ObjectDataHandler
+{
+public:
+ WorldModelHandler(ADT* adt);
+ ~WorldModelHandler();
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool IsSane() { return _definitions && _paths; }
+ void InsertModelGeometry(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root);
+protected:
+ void ProcessInternal(ChunkedData* data);
+private:
+ void ReadDefinitions();
+ void ReadModelPaths();
+ std::set<uint32> _drawn;
+ std::vector<WorldModelDefinition>* _definitions;
+ std::vector<std::string>* _paths;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelRoot.cpp b/src/tools/mesh_extractor/WorldModelRoot.cpp
new file mode 100644
index 00000000000..c34a77e4531
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelRoot.cpp
@@ -0,0 +1,74 @@
+#include "WorldModelRoot.h"
+#include "ChunkedData.h"
+#include "Utils.h"
+
+WorldModelRoot::WorldModelRoot( std::string path )
+{
+ Data = new ChunkedData(path);
+ Path = path;
+ ReadHeader();
+ ReadGroups();
+ ReadDoodadInstances();
+ ReadDoodadSets();
+}
+
+void WorldModelRoot::ReadGroups()
+{
+ std::string pathBase = Utils::GetPathBase(Path);
+ Groups.reserve(Header.CountGroups);
+ for (uint32 i = 0; i < Header.CountGroups; i++)
+ {
+ char name[200];
+ sprintf(name, "%s_%03u.wmo", pathBase.c_str(), i);
+ WorldModelGroup group(name, i);
+ if (!group.IsBad)
+ Groups.push_back(group);
+ }
+}
+
+void WorldModelRoot::ReadDoodadSets()
+{
+ Chunk* chunk = Data->GetChunkByName("MODS");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ ASSERT(chunk->Length / 32 == Header.CountSets && "chunk.Length / 32 == Header.CountSets");
+ DoodadSets.reserve(Header.CountSets);
+ for (uint32 i = 0; i < Header.CountSets; i++)
+ DoodadSets.push_back(DoodadSet::Read(stream));
+}
+
+void WorldModelRoot::ReadDoodadInstances()
+{
+ Chunk* chunk = Data->GetChunkByName("MODD");
+ Chunk* nameChunk = Data->GetChunkByName("MODN");
+ if (!chunk || !nameChunk)
+ return;
+
+ const uint32 instanceSize = 40;
+ uint32 countInstances = chunk->Length / instanceSize;
+ DoodadInstances.reserve(countInstances);
+ for (uint32 i = 0; i < countInstances; i++)
+ {
+ FILE* stream = chunk->GetStream();
+ fseek(stream, instanceSize * i, SEEK_CUR);
+ DoodadInstance instance = DoodadInstance::Read(stream);
+ FILE* nameStream = nameChunk->GetStream();
+ if (instance.FileOffset >= nameChunk->Length)
+ continue;
+ fseek(nameStream, instance.FileOffset, SEEK_CUR);
+ instance.File = Utils::ReadString(nameStream);
+ DoodadInstances.push_back(instance);
+ }
+}
+
+void WorldModelRoot::ReadHeader()
+{
+ Chunk* chunk = Data->GetChunkByName("MOHD");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ Header = WorldModelHeader::Read(stream);
+}
diff --git a/src/tools/mesh_extractor/WorldModelRoot.h b/src/tools/mesh_extractor/WorldModelRoot.h
new file mode 100644
index 00000000000..c06ff3d5d2b
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelRoot.h
@@ -0,0 +1,26 @@
+#ifndef WMOROOT_H
+#define WMOROOT_H
+#include <string>
+#include <vector>
+
+#include "ChunkedData.h"
+#include "Utils.h"
+#include "WorldModelGroup.h"
+
+class WorldModelRoot
+{
+public:
+ WorldModelRoot(std::string path);
+ std::string Path;
+ ChunkedData* Data;
+ WorldModelHeader Header;
+ std::vector<DoodadInstance> DoodadInstances;
+ std::vector<DoodadSet> DoodadSets;
+ std::vector<WorldModelGroup> Groups;
+private:
+ void ReadGroups();
+ void ReadDoodadSets();
+ void ReadDoodadInstances();
+ void ReadHeader();
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/readme b/src/tools/mesh_extractor/readme
new file mode 100644
index 00000000000..85cd7cfc975
--- /dev/null
+++ b/src/tools/mesh_extractor/readme
@@ -0,0 +1,6 @@
+Experimental mesh extractor.
+Original work in C# by stschake
+Thanks to:
+Subv
+~
+For helping in the porting to C++ \ No newline at end of file
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
new file mode 100644
index 00000000000..b168691c994
--- /dev/null
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -0,0 +1,55 @@
+# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE mmap_gen_sources *.cpp *.h)
+
+set(mmap_gen_Includes
+ ${CMAKE_BINARY_DIR}
+ ${ACE_INCLUDE_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ ${CMAKE_SOURCE_DIR}/dep/bzip2
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/game/Conditions
+ ${CMAKE_SOURCE_DIR}/src/server/collision
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Management
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Models
+)
+
+if( WIN32 )
+ set(mmap_gen_Includes
+ ${mmap_gen_Includes}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq/win
+ )
+endif()
+
+include_directories(${mmap_gen_Includes})
+
+add_executable(mmaps_generator ${mmap_gen_sources})
+
+target_link_libraries(mmaps_generator
+ collision
+ g3dlib
+ Recast
+ Detour
+ ${ACE_LIBRARY}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+)
+
+if( UNIX )
+ install(TARGETS mmaps_generator DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS mmaps_generator DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mmaps_generator/Info/readme.txt b/src/tools/mmaps_generator/Info/readme.txt
new file mode 100644
index 00000000000..8d7c4f9d2e0
--- /dev/null
+++ b/src/tools/mmaps_generator/Info/readme.txt
@@ -0,0 +1,66 @@
+Generator command line args
+
+--offMeshInput [file.*] Path to file containing off mesh connections data.
+ Format must be: (see offmesh_example.txt)
+ "map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments"
+ Single mesh connection per line.
+
+--silent Make us script friendly. Do not wait for user input
+ on error or completion.
+
+--bigBaseUnit [true|false] Generate tile/map using bigger basic unit.
+ Use this option only if you have unexpected gaps.
+
+ false: use normal metrics (default)
+
+--maxAngle [#] Max walkable inclination angle
+
+ float between 45 and 90 degrees (default 60)
+
+--skipLiquid liquid data for maps
+
+ false: include liquid data (default)
+
+--skipContinents [true|false] continents are maps 0 (Eastern Kingdoms),
+ 1 (Kalimdor), 530 (Outlands), 571 (Northrend)
+
+ false: build continents (default)
+
+--skipJunkMaps [true|false] junk maps include some unused
+ maps, transport maps, and some other
+
+ true: skip junk maps (default)
+
+--skipBattlegrounds [true|false] does not include PVP arenas
+
+ false: skip battlegrounds (default)
+
+--debugOutput [true|false] create debugging files for use with RecastDemo
+ if you are only creating mmaps for use with MaNGOS,
+ you don't want debugging files
+
+ false: don't create debugging files (default)
+
+--tile [#,#] Build the specified tile
+ seperate number with a comma ','
+ must specify a map number (see below)
+ if this option is not used, all tiles are built
+
+ [#] Build only the map specified by #
+ this command will build the map regardless of --skip* option settings
+ if you do not specify a map number, builds all maps that pass the filters specified by --skip* options
+
+
+examples:
+
+movement_extractor
+builds maps using the default settings (see above for defaults)
+
+movement_extractor --skipContinents true
+builds the default maps, except continents
+
+movement_extractor 0
+builds all tiles of map 0
+
+movement_extractor 0 --tile 34,46
+builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain) \ No newline at end of file
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp
new file mode 100644
index 00000000000..a490273ad80
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "IntermediateValues.h"
+
+namespace MMAP
+{
+ IntermediateValues::~IntermediateValues()
+ {
+ rcFreeCompactHeightfield(compactHeightfield);
+ rcFreeHeightField(heightfield);
+ rcFreeContourSet(contours);
+ rcFreePolyMesh(polyMesh);
+ rcFreePolyMeshDetail(polyMeshDetail);
+ }
+
+ void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileX, tileY);
+
+ printf("%sWriting debug output... \r", tileString);
+
+ std::string name("meshes/%03u%02i%02i.");
+
+#define DEBUG_WRITE(fileExtension,data) \
+ do { \
+ sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \
+ FILE* file = fopen(fileName, "wb"); \
+ if (!file) \
+ { \
+ char message[1024]; \
+ sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \
+ perror(message); \
+ } \
+ else \
+ debugWrite(file, data); \
+ if (file) fclose(file); \
+ printf("%sWriting debug output... \r", tileString); \
+ } while (false)
+
+ if (heightfield)
+ DEBUG_WRITE("hf", heightfield);
+ if (compactHeightfield)
+ DEBUG_WRITE("chf", compactHeightfield);
+ if (contours)
+ DEBUG_WRITE("cs", contours);
+ if (polyMesh)
+ DEBUG_WRITE("pmesh", polyMesh);
+ if (polyMeshDetail)
+ DEBUG_WRITE("dmesh", polyMeshDetail);
+
+#undef DEBUG_WRITE
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->width), sizeof(int), 1, file);
+ fwrite(&(mesh->height), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+
+ for (int y = 0; y < mesh->height; ++y)
+ for (int x = 0; x < mesh->width; ++x)
+ {
+ rcSpan* span = mesh->spans[x+y*mesh->width];
+
+ // first, count the number of spans
+ int spanCount = 0;
+ while (span)
+ {
+ spanCount++;
+ span = span->next;
+ }
+
+ // write the span count
+ fwrite(&spanCount, sizeof(int), 1, file);
+
+ // write the spans
+ span = mesh->spans[x+y*mesh->width];
+ while (span)
+ {
+ fwrite(span, sizeof(rcSpan), 1, file);
+ span = span->next;
+ }
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcCompactHeightfield* chf)
+ {
+ if (!file | !chf)
+ return;
+
+ fwrite(&(chf->width), sizeof(chf->width), 1, file);
+ fwrite(&(chf->height), sizeof(chf->height), 1, file);
+ fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
+
+ fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
+ fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
+
+ fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
+ fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
+
+ fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
+ fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
+
+ fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
+ fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
+
+ int tmp = 0;
+ if (chf->cells) tmp |= 1;
+ if (chf->spans) tmp |= 2;
+ if (chf->dist) tmp |= 4;
+ if (chf->areas) tmp |= 8;
+
+ fwrite(&tmp, sizeof(tmp), 1, file);
+
+ if (chf->cells)
+ fwrite(chf->cells, sizeof(rcCompactCell), chf->width*chf->height, file);
+ if (chf->spans)
+ fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
+ if (chf->dist)
+ fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
+ if (chf->areas)
+ fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcContourSet* cs)
+ {
+ if (!file || !cs)
+ return;
+
+ fwrite(&(cs->cs), sizeof(float), 1, file);
+ fwrite(&(cs->ch), sizeof(float), 1, file);
+ fwrite(cs->bmin, sizeof(float), 3, file);
+ fwrite(cs->bmax, sizeof(float), 3, file);
+ fwrite(&(cs->nconts), sizeof(int), 1, file);
+ for (int i = 0; i < cs->nconts; ++i)
+ {
+ fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
+ fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
+ fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts*4, file);
+ fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts*4, file);
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMesh* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->nvp), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts*3, file);
+ fwrite(&(mesh->npolys), sizeof(int), 1, file);
+ fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys*mesh->nvp*2, file);
+ fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
+ fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
+ fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMeshDetail* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(float), mesh->nverts*3, file);
+ fwrite(&(mesh->ntris), sizeof(int), 1, file);
+ fwrite(mesh->tris, sizeof(char), mesh->ntris*4, file);
+ fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
+ fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
+ }
+
+ void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ char objFileName[255];
+ sprintf(objFileName, "meshes/map%03u%02u%02u.obj", mapID, tileY, tileX);
+
+ FILE* objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ G3D::Array<float> allVerts;
+ G3D::Array<int> allTris;
+
+ allTris.append(meshData.liquidTris);
+ allVerts.append(meshData.liquidVerts);
+ TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
+ allVerts.append(meshData.solidVerts);
+
+ float* verts = allVerts.getCArray();
+ int vertCount = allVerts.size() / 3;
+ int* tris = allTris.getCArray();
+ int triCount = allTris.size() / 3;
+
+ for (int i = 0; i < allVerts.size() / 3; i++)
+ fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]);
+
+ for (int i = 0; i < allTris.size() / 3; i++)
+ fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1);
+
+ fclose(objFile);
+
+
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileY, tileX);
+ printf("%sWriting debug output... \r", tileString);
+
+ sprintf(objFileName, "meshes/%03u.map", mapID);
+
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ char b = '\0';
+ fwrite(&b, sizeof(char), 1, objFile);
+ fclose(objFile);
+
+ sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX);
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ fwrite(&vertCount, sizeof(int), 1, objFile);
+ fwrite(verts, sizeof(float), vertCount*3, objFile);
+ fflush(objFile);
+
+ fwrite(&triCount, sizeof(int), 1, objFile);
+ fwrite(tris, sizeof(int), triCount*3, objFile);
+ fflush(objFile);
+
+ fclose(objFile);
+ }
+}
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
new file mode 100644
index 00000000000..89a5c3ae4c2
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INTERMEDIATE_VALUES_H
+#define _INTERMEDIATE_VALUES_H
+
+#include "PathCommon.h"
+#include "TerrainBuilder.h"
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+namespace MMAP
+{
+ // this class gathers all debug info holding and output
+ struct IntermediateValues
+ {
+ rcHeightfield* heightfield;
+ rcCompactHeightfield* compactHeightfield;
+ rcContourSet* contours;
+ rcPolyMesh* polyMesh;
+ rcPolyMeshDetail* polyMeshDetail;
+
+ IntermediateValues() : heightfield(NULL), compactHeightfield(NULL),
+ contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {}
+ ~IntermediateValues();
+
+ void writeIV(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ void debugWrite(FILE* file, const rcHeightfield* mesh);
+ void debugWrite(FILE* file, const rcCompactHeightfield* chf);
+ void debugWrite(FILE* file, const rcContourSet* cs);
+ void debugWrite(FILE* file, const rcPolyMesh* mesh);
+ void debugWrite(FILE* file, const rcPolyMeshDetail* mesh);
+
+ void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ };
+}
+#endif
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
new file mode 100644
index 00000000000..cd85d926125
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "MapTree.h"
+#include "ModelInstance.h"
+
+#include "DetourNavMeshBuilder.h"
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+
+#include "DisableMgr.h"
+#include <ace/OS_NS_unistd.h>
+
+uint32 GetLiquidFlags(uint32 /*liquidType*/) { return 0; }
+namespace DisableMgr
+{
+ bool IsDisabledFor(DisableType /*type*/, uint32 /*entry*/, Unit const* /*unit*/, uint8 /*flags*/ /*= 0*/) { return false; }
+}
+
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+namespace MMAP
+{
+ MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
+ bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
+ bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
+ m_terrainBuilder (NULL),
+ m_debugOutput (debugOutput),
+ m_offMeshFilePath (offMeshFilePath),
+ m_skipContinents (skipContinents),
+ m_skipJunkMaps (skipJunkMaps),
+ m_skipBattlegrounds (skipBattlegrounds),
+ m_maxWalkableAngle (maxWalkableAngle),
+ m_bigBaseUnit (bigBaseUnit),
+ m_rcContext (NULL)
+ {
+ m_terrainBuilder = new TerrainBuilder(skipLiquid);
+
+ m_rcContext = new rcContext(false);
+
+ discoverTiles();
+ }
+
+ /**************************************************************************/
+ MapBuilder::~MapBuilder()
+ {
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ (*it).second->clear();
+ delete (*it).second;
+ }
+
+ delete m_terrainBuilder;
+ delete m_rcContext;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::discoverTiles()
+ {
+ std::vector<std::string> files;
+ uint32 mapID, tileX, tileY, tileID, count = 0;
+ char filter[12];
+
+ printf("Discovering maps... ");
+ getDirContents(files, "maps");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ if (m_tiles.find(mapID) == m_tiles.end())
+ {
+ m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
+ count++;
+ }
+ }
+
+ files.clear();
+ getDirContents(files, "vmaps", "*.vmtree");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
+ count++;
+ }
+ printf("found %u.\n", count);
+
+ count = 0;
+ printf("Discovering tiles... ");
+ for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
+ {
+ std::set<uint32>* tiles = (*itr).second;
+ mapID = (*itr).first;
+
+ sprintf(filter, "%03u*.vmtile", mapID);
+ files.clear();
+ getDirContents(files, "vmaps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileX = uint32(atoi(files[i].substr(7,2).c_str()));
+ tileY = uint32(atoi(files[i].substr(4,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileY, tileX);
+
+ tiles->insert(tileID);
+ count++;
+ }
+
+ sprintf(filter, "%03u*", mapID);
+ files.clear();
+ getDirContents(files, "maps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileY = uint32(atoi(files[i].substr(3,2).c_str()));
+ tileX = uint32(atoi(files[i].substr(5,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileX, tileY);
+
+ if (tiles->insert(tileID).second)
+ count++;
+ }
+ }
+ printf("found %u.\n\n", count);
+ }
+
+ /**************************************************************************/
+ std::set<uint32>* MapBuilder::getTileList(uint32 mapID)
+ {
+ TileList::iterator itr = m_tiles.find(mapID);
+ if (itr != m_tiles.end())
+ return (*itr).second;
+
+ std::set<uint32>* tiles = new std::set<uint32>();
+ m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, tiles));
+ return tiles;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildAllMaps(int threads)
+ {
+ std::vector<BuilderThread*> _threads;
+
+ BuilderThreadPool* pool = threads > 0 ? new BuilderThreadPool() : NULL;
+
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ uint32 mapID = it->first;
+ if (!shouldSkipMap(mapID))
+ {
+ if (threads > 0)
+ pool->Enqueue(new MapBuildRequest(mapID));
+ else
+ buildMap(mapID);
+ }
+ }
+
+ for (int i = 0; i < threads; ++i)
+ _threads.push_back(new BuilderThread(this, pool->Queue()));
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+
+ delete pool;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
+ {
+ maxX = INT_MAX;
+ maxY = INT_MAX;
+ minX = INT_MIN;
+ minY = INT_MIN;
+
+ float bmin[3], bmax[3], lmin[3], lmax[3];
+ MeshData meshData;
+
+ // make sure we process maps which don't have tiles
+ // initialize the static tree, which loads WDT models
+ if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() && meshData.liquidVerts.size())
+ {
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+ rcVmin(bmin, lmin);
+ rcVmax(bmax, lmax);
+ }
+ else if (meshData.solidVerts.size())
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ else
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+
+ // convert coord bounds to grid bounds
+ maxX = 32 - bmin[0] / GRID_SIZE;
+ maxY = 32 - bmin[2] / GRID_SIZE;
+ minX = 32 - bmax[0] / GRID_SIZE;
+ minY = 32 - bmax[2] / GRID_SIZE;
+ }
+
+ void MapBuilder::buildMeshFromFile(char* name)
+ {
+ FILE* file = fopen(name, "rb");
+ if (!file)
+ return;
+
+ printf("Building mesh from file\n");
+ int tileX, tileY, mapId;
+ if (fread(&mapId, sizeof(int), 1, file) != 1)
+ return;
+ if (fread(&tileX, sizeof(int), 1, file) != 1)
+ return;
+ if (fread(&tileY, sizeof(int), 1, file) != 1)
+ return;
+
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapId, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ fclose(file);
+ return;
+ }
+
+ uint32 verticesCount, indicesCount;
+ if (fread(&verticesCount, sizeof(uint32), 1, file) != 1)
+ return;
+ if (fread(&indicesCount, sizeof(uint32), 1, file) != 1)
+ return;
+
+ float* verts = new float[verticesCount];
+ int* inds = new int[indicesCount];
+
+ if (fread(verts, sizeof(float), verticesCount, file) != verticesCount)
+ return;
+ if (fread(inds, sizeof(int), indicesCount, file) != indicesCount)
+ return;
+
+ MeshData data;
+
+ for (uint32 i = 0; i < verticesCount; ++i)
+ data.solidVerts.append(verts[i]);
+
+ for (uint32 i = 0; i < indicesCount; ++i)
+ data.solidTris.append(inds[i]);
+
+ TerrainBuilder::cleanVertices(data.solidVerts, data.solidTris);
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, data.solidVerts.getCArray(), data.solidVerts.size() / 3, bmin, bmax);
+
+ // build navmesh tile
+ buildMoveMapTile(mapId, tileX, tileY, data, bmin, bmax, navMesh);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ dtFreeNavMesh(navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMap(uint32 mapID)
+ {
+ printf("[Thread %u] Building map %03u:\n", uint32(ACE_Thread::self()), mapID);
+
+ std::set<uint32>* tiles = getTileList(mapID);
+
+ // make sure we process maps which don't have tiles
+ if (!tiles->size())
+ {
+ // convert coord bounds to grid bounds
+ uint32 minX, minY, maxX, maxY;
+ getGridBounds(mapID, minX, minY, maxX, maxY);
+
+ // add all tiles within bounds to tile list.
+ for (uint32 i = minX; i <= maxX; ++i)
+ for (uint32 j = minY; j <= maxY; ++j)
+ tiles->insert(StaticMapTree::packTileID(i, j));
+ }
+
+ if (!tiles->empty())
+ {
+ // build navMesh
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("[Map %i] Failed creating navmesh!\n", mapID);
+ return;
+ }
+
+ // now start building mmtiles for each tile
+ printf("[Map %i] We have %u tiles. \n", mapID, (unsigned int)tiles->size());
+ for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ uint32 tileX, tileY;
+
+ // unpack tile coords
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (shouldSkipTile(mapID, tileX, tileY))
+ continue;
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ }
+
+ dtFreeNavMesh(navMesh);
+ }
+
+ printf("[Map %i] Complete!\n", mapID);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
+ {
+ printf("[Map %i] Building tile [%02u,%02u]\n", mapID, tileX, tileY);
+
+ MeshData meshData;
+
+ // get heightmap data
+ m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
+
+ // get model data
+ m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
+
+ // if there is no data, give up now
+ if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
+ return;
+
+ // remove unused vertices
+ TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
+ TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
+
+ // gather all mesh data for final data check, and bounds calculation
+ G3D::Array<float> allVerts;
+ allVerts.append(meshData.liquidVerts);
+ allVerts.append(meshData.solidVerts);
+
+ if (!allVerts.size())
+ return;
+
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
+
+ m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
+
+ // build navmesh tile
+ buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh)
+ {
+ std::set<uint32>* tiles = getTileList(mapID);
+
+ // old code for non-statically assigned bitmask sizes:
+ ///*** calculate number of bits needed to store tiles & polys ***/
+ //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
+ //if (tileBits < 1) tileBits = 1; // need at least one bit!
+ //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
+
+ int polyBits = STATIC_POLY_BITS;
+
+ int maxTiles = tiles->size();
+ int maxPolysPerTile = 1 << polyBits;
+
+ /*** calculate bounds of map ***/
+
+ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
+ for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ StaticMapTree::unpackTileID(*it, tileX, tileY);
+
+ if (tileX > tileXMax)
+ tileXMax = tileX;
+ else if (tileX < tileXMin)
+ tileXMin = tileX;
+
+ if (tileY > tileYMax)
+ tileYMax = tileY;
+ else if (tileY < tileYMin)
+ tileYMin = tileY;
+ }
+
+ // use Max because '32 - tileX' is negative for values over 32
+ float bmin[3], bmax[3];
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+
+ /*** now create the navmesh ***/
+
+ // navmesh creation params
+ dtNavMeshParams navMeshParams;
+ memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
+ navMeshParams.tileWidth = GRID_SIZE;
+ navMeshParams.tileHeight = GRID_SIZE;
+ rcVcopy(navMeshParams.orig, bmin);
+ navMeshParams.maxTiles = maxTiles;
+ navMeshParams.maxPolys = maxPolysPerTile;
+
+ navMesh = dtAllocNavMesh();
+ printf("[Map %i] Creating navMesh...\n", mapID);
+ if (!navMesh->init(&navMeshParams))
+ {
+ printf("[Map %i] Failed creating navmesh! \n", mapID);
+ return;
+ }
+
+ char fileName[25];
+ sprintf(fileName, "mmaps/%03u.mmap", mapID);
+
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ dtFreeNavMesh(navMesh);
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ return;
+ }
+
+ // now that we know navMesh params are valid, we can write them to file
+ fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
+ MeshData &meshData, float bmin[3], float bmax[3],
+ dtNavMesh* navMesh)
+ {
+ // console output
+ char tileString[20];
+ sprintf(tileString, "[Map %03i] [%02i,%02i]: ", mapID, tileX, tileY);
+ printf("%s Building movemap tiles...\n", tileString);
+
+ IntermediateValues iv;
+
+ float* tVerts = meshData.solidVerts.getCArray();
+ int tVertCount = meshData.solidVerts.size() / 3;
+ int* tTris = meshData.solidTris.getCArray();
+ int tTriCount = meshData.solidTris.size() / 3;
+
+ float* lVerts = meshData.liquidVerts.getCArray();
+ int lVertCount = meshData.liquidVerts.size() / 3;
+ int* lTris = meshData.liquidTris.getCArray();
+ int lTriCount = meshData.liquidTris.size() / 3;
+ uint8* lTriFlags = meshData.liquidType.getCArray();
+
+ // these are WORLD UNIT based metrics
+ // this are basic unit dimentions
+ // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
+ const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
+
+ // All are in UNIT metrics!
+ const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f);
+ const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
+ const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE;
+
+ rcConfig config;
+ memset(&config, 0, sizeof(rcConfig));
+
+ rcVcopy(config.bmin, bmin);
+ rcVcopy(config.bmax, bmax);
+
+ config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ config.cs = BASE_UNIT_DIM;
+ config.ch = BASE_UNIT_DIM;
+ config.walkableSlopeAngle = m_maxWalkableAngle;
+ config.tileSize = VERTEX_PER_TILE;
+ config.walkableRadius = m_bigBaseUnit ? 1 : 2;
+ config.borderSize = config.walkableRadius + 3;
+ config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
+ config.walkableHeight = m_bigBaseUnit ? 3 : 6;
+ config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
+ config.minRegionArea = rcSqr(60);
+ config.mergeRegionArea = rcSqr(50);
+ config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ config.detailSampleDist = config.cs * 64;
+ config.detailSampleMaxError = config.ch * 2;
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
+
+ // allocate subregions : tiles
+ Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
+
+ // Initialize per tile config.
+ rcConfig tileCfg = config;
+ tileCfg.width = config.tileSize + config.borderSize*2;
+ tileCfg.height = config.tileSize + config.borderSize*2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!pmmerge)
+ {
+ printf("%s alloc pmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!dmmerge)
+ {
+ printf("%s alloc dmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ int nmerge = 0;
+ // build all tiles
+ for (int y = 0; y < TILES_PER_MAP; ++y)
+ {
+ for (int x = 0; x < TILES_PER_MAP; ++x)
+ {
+ Tile& tile = tiles[x + y * TILES_PER_MAP];
+
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = config.bmin[0] + float(x*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmin[2] = config.bmin[2] + float(y*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmax[0] = config.bmin[0] + float((x+1)*config.tileSize + config.borderSize)*config.cs;
+ tileCfg.bmax[2] = config.bmin[2] + float((y+1)*config.tileSize + config.borderSize)*config.cs;
+
+ // build heightfield
+ tile.solid = rcAllocHeightfield();
+ if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
+ {
+ printf("%s Failed building heightfield! \n", tileString);
+ continue;
+ }
+
+ // mark all walkable tiles, both liquids and solids
+ unsigned char* triFlags = new unsigned char[tTriCount];
+ memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char));
+ rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
+ rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
+ delete[] triFlags;
+
+ rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
+ rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
+ rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
+
+ rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
+
+ // compact heightfield spans
+ tile.chf = rcAllocCompactHeightfield();
+ if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
+ {
+ printf("%s Failed compacting heightfield! \n", tileString);
+ continue;
+ }
+
+ // build polymesh intermediates
+ if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
+ {
+ printf("%s Failed eroding area! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildDistanceField(m_rcContext, *tile.chf))
+ {
+ printf("%s Failed building distance field! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
+ {
+ printf("%s Failed building regions! \n", tileString);
+ continue;
+ }
+
+ tile.cset = rcAllocContourSet();
+ if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
+ {
+ printf("%s Failed building contours! \n", tileString);
+ continue;
+ }
+
+ // build polymesh
+ tile.pmesh = rcAllocPolyMesh();
+ if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
+ {
+ printf("%s Failed building polymesh! \n", tileString);
+ continue;
+ }
+
+ tile.dmesh = rcAllocPolyMeshDetail();
+ if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh))
+ {
+ printf("%s Failed building polymesh detail! \n", tileString);
+ continue;
+ }
+
+ // free those up
+ // we may want to keep them in the future for debug
+ // but right now, we don't have the code to merge them
+ rcFreeHeightField(tile.solid);
+ tile.solid = NULL;
+ rcFreeCompactHeightfield(tile.chf);
+ tile.chf = NULL;
+ rcFreeContourSet(tile.cset);
+ tile.cset = NULL;
+
+ if (tile.pmesh)
+ {
+ pmmerge[nmerge] = tile.pmesh;
+ dmmerge[nmerge] = tile.dmesh;
+ nmerge++;
+ }
+ }
+ }
+
+ iv.polyMesh = rcAllocPolyMesh();
+ if (!iv.polyMesh)
+ {
+ printf("%s alloc iv.polyMesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
+
+ iv.polyMeshDetail = rcAllocPolyMeshDetail();
+ if (!iv.polyMeshDetail)
+ {
+ printf("%s alloc m_dmesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
+
+ // free things up
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ delete[] tiles;
+
+ // remove padding for extraction
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] -= (unsigned short)config.borderSize;
+ v[2] -= (unsigned short)config.borderSize;
+ }
+
+ // set polygons as walkable
+ // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
+ for (int i = 0; i < iv.polyMesh->npolys; ++i)
+ if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
+ iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
+
+ // setup mesh parameters
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ params.verts = iv.polyMesh->verts;
+ params.vertCount = iv.polyMesh->nverts;
+ params.polys = iv.polyMesh->polys;
+ params.polyAreas = iv.polyMesh->areas;
+ params.polyFlags = iv.polyMesh->flags;
+ params.polyCount = iv.polyMesh->npolys;
+ params.nvp = iv.polyMesh->nvp;
+ params.detailMeshes = iv.polyMeshDetail->meshes;
+ params.detailVerts = iv.polyMeshDetail->verts;
+ params.detailVertsCount = iv.polyMeshDetail->nverts;
+ params.detailTris = iv.polyMeshDetail->tris;
+ params.detailTriCount = iv.polyMeshDetail->ntris;
+
+ params.offMeshConVerts = meshData.offMeshConnections.getCArray();
+ params.offMeshConCount = meshData.offMeshConnections.size()/6;
+ params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
+ params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
+ params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
+ params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
+
+ params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height
+ params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius
+ params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)!
+ params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
+ params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
+ rcVcopy(params.bmin, bmin);
+ rcVcopy(params.bmax, bmax);
+ params.cs = config.cs;
+ params.ch = config.ch;
+ params.tileSize = VERTEX_PER_MAP;
+
+ // will hold final navmesh
+ unsigned char* navData = NULL;
+ int navDataSize = 0;
+
+ do
+ {
+ // these values are checked within dtCreateNavMeshData - handle them here
+ // so we have a clear error message
+ if (params.nvp > DT_VERTS_PER_POLYGON)
+ {
+ printf("%s Invalid verts-per-polygon value! \n", tileString);
+ continue;
+ }
+ if (params.vertCount >= 0xffff)
+ {
+ printf("%s Too many vertices! \n", tileString);
+ continue;
+ }
+ if (!params.vertCount || !params.verts)
+ {
+ // occurs mostly when adjacent tiles have models
+ // loaded but those models don't span into this tile
+
+ // message is an annoyance
+ //printf("%sNo vertices to build tile! \n", tileString);
+ continue;
+ }
+ if (!params.polyCount || !params.polys ||
+ TILES_PER_MAP*TILES_PER_MAP == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("%s No polygons to build on tile! \n", tileString);
+ continue;
+ }
+ if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
+ {
+ printf("%s No detail mesh to build tile! \n", tileString);
+ continue;
+ }
+
+ printf("%s Building navmesh tile...\n", tileString);
+ if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+ {
+ printf("%s Failed building navmesh tile! \n", tileString);
+ continue;
+ }
+
+ dtTileRef tileRef = 0;
+ printf("%s Adding tile to navmesh...\n", tileString);
+ // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
+ // is removed via removeTile()
+ dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
+ if (!tileRef || dtResult != DT_SUCCESS)
+ {
+ printf("%s Failed adding tile to navmesh! \n", tileString);
+ continue;
+ }
+
+ // file output
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ navMesh->removeTile(tileRef, NULL, NULL);
+ continue;
+ }
+
+ printf("%s Writing to file...\n", tileString);
+
+ // write header
+ MmapTileHeader header;
+ header.usesLiquids = m_terrainBuilder->usesLiquids();
+ header.size = uint32(navDataSize);
+ fwrite(&header, sizeof(MmapTileHeader), 1, file);
+
+ // write data
+ fwrite(navData, sizeof(unsigned char), navDataSize, file);
+ fclose(file);
+
+ // now that tile is written to disk, we can unload it
+ navMesh->removeTile(tileRef, NULL, NULL);
+ }
+ while (0);
+
+ if (m_debugOutput)
+ {
+ // restore padding so that the debug visualization is correct
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] += (unsigned short)config.borderSize;
+ v[2] += (unsigned short)config.borderSize;
+ }
+
+ iv.generateObjFile(mapID, tileX, tileY, meshData);
+ iv.writeIV(mapID, tileX, tileY);
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+ {
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * GRID_SIZE;
+ bmax[2] = (32 - int(tileY)) * GRID_SIZE;
+ bmin[0] = bmax[0] - GRID_SIZE;
+ bmin[2] = bmax[2] - GRID_SIZE;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipMap(uint32 mapID)
+ {
+ if (m_skipContinents)
+ switch (mapID)
+ {
+ case 0:
+ case 1:
+ case 530:
+ case 571:
+ return true;
+ default:
+ break;
+ }
+
+ if (m_skipJunkMaps)
+ switch (mapID)
+ {
+ case 13: // test.wdt
+ case 25: // ScottTest.wdt
+ case 29: // Test.wdt
+ case 42: // Colin.wdt
+ case 169: // EmeraldDream.wdt (unused, and very large)
+ case 451: // development.wdt
+ case 573: // ExteriorTest.wdt
+ case 597: // CraigTest.wdt
+ case 605: // development_nonweighted.wdt
+ case 606: // QA_DVD.wdt
+ return true;
+ default:
+ if (isTransportMap(mapID))
+ return true;
+ break;
+ }
+
+ if (m_skipBattlegrounds)
+ switch (mapID)
+ {
+ case 30: // AV
+ case 37: // ?
+ case 489: // WSG
+ case 529: // AB
+ case 566: // EotS
+ case 607: // SotA
+ case 628: // IoC
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::isTransportMap(uint32 mapID)
+ {
+ switch (mapID)
+ {
+ // transport maps
+ case 582:
+ case 584:
+ case 586:
+ case 587:
+ case 588:
+ case 589:
+ case 590:
+ case 591:
+ case 592:
+ case 593:
+ case 594:
+ case 596:
+ case 610:
+ case 612:
+ case 613:
+ case 614:
+ case 620:
+ case 621:
+ case 622:
+ case 623:
+ case 641:
+ case 642:
+ case 647:
+ case 672:
+ case 673:
+ case 712:
+ case 713:
+ case 718:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ return false;
+
+ MmapTileHeader header;
+ int count = fread(&header, sizeof(MmapTileHeader), 1, file);
+ fclose(file);
+ if (count != 1)
+ return false;
+
+ if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != uint32(DT_NAVMESH_VERSION))
+ return false;
+
+ if (header.mmapVersion != MMAP_VERSION)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
new file mode 100644
index 00000000000..3ffaea0ab66
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MAP_BUILDER_H
+#define _MAP_BUILDER_H
+
+#include <vector>
+#include <set>
+#include <map>
+
+#include "TerrainBuilder.h"
+#include "IntermediateValues.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+#include <ace/Task.h>
+#include <ace/Activation_Queue.h>
+#include <ace/Method_Request.h>
+
+using namespace VMAP;
+
+// G3D namespace typedefs conflicts with ACE typedefs
+
+namespace MMAP
+{
+ typedef std::map<uint32, std::set<uint32>*> TileList;
+ struct Tile
+ {
+ Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}
+ ~Tile()
+ {
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+ rcFreeHeightField(solid);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ }
+ rcCompactHeightfield* chf;
+ rcHeightfield* solid;
+ rcContourSet* cset;
+ rcPolyMesh* pmesh;
+ rcPolyMeshDetail* dmesh;
+ };
+
+ class MapBuilder
+ {
+ public:
+ MapBuilder(float maxWalkableAngle = 60.f,
+ bool skipLiquid = false,
+ bool skipContinents = false,
+ bool skipJunkMaps = true,
+ bool skipBattlegrounds = false,
+ bool debugOutput = false,
+ bool bigBaseUnit = false,
+ const char* offMeshFilePath = NULL);
+
+ ~MapBuilder();
+
+ // builds all mmap tiles for the specified map id (ignores skip settings)
+ void buildMap(uint32 mapID);
+ void buildMeshFromFile(char* name);
+
+ // builds an mmap tile for the specified map and its mesh
+ void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ // builds list of maps, then builds all of mmap tiles (based on the skip settings)
+ void buildAllMaps(int threads);
+
+ private:
+ // detect maps and tiles
+ void discoverTiles();
+ std::set<uint32>* getTileList(uint32 mapID);
+
+ void buildNavMesh(uint32 mapID, dtNavMesh* &navMesh);
+
+ void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh);
+
+ // move map building
+ void buildMoveMapTile(uint32 mapID,
+ uint32 tileX,
+ uint32 tileY,
+ MeshData &meshData,
+ float bmin[3],
+ float bmax[3],
+ dtNavMesh* navMesh);
+
+ void getTileBounds(uint32 tileX, uint32 tileY,
+ float* verts, int vertCount,
+ float* bmin, float* bmax);
+ void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY);
+
+ bool shouldSkipMap(uint32 mapID);
+ bool isTransportMap(uint32 mapID);
+ bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ TerrainBuilder* m_terrainBuilder;
+ TileList m_tiles;
+
+ bool m_debugOutput;
+
+ const char* m_offMeshFilePath;
+ bool m_skipContinents;
+ bool m_skipJunkMaps;
+ bool m_skipBattlegrounds;
+
+ float m_maxWalkableAngle;
+ bool m_bigBaseUnit;
+
+ // build performance - not really used for now
+ rcContext* m_rcContext;
+ };
+
+ class MapBuildRequest : public ACE_Method_Request
+ {
+ public:
+ MapBuildRequest(uint32 mapId) : _mapId(mapId) {}
+
+ virtual int call()
+ {
+ /// @ Actually a creative way of unabstracting the class and returning a member variable
+ return (int)_mapId;
+ }
+
+ private:
+ uint32 _mapId;
+ };
+
+ class BuilderThread : public ACE_Task_Base
+ {
+ private:
+ MapBuilder* _builder;
+ ACE_Activation_Queue* _queue;
+
+ public:
+ BuilderThread(MapBuilder* builder, ACE_Activation_Queue* queue) : _builder(builder), _queue(queue) { activate(); }
+
+ int svc()
+ {
+ /// @ Set a timeout for dequeue attempts (only used when the queue is empty) as it will never get populated after thread starts
+ ACE_Time_Value timeout(5);
+ ACE_Method_Request* request = NULL;
+ while ((request = _queue->dequeue(&timeout)) != NULL)
+ {
+ _builder->buildMap(request->call());
+ delete request;
+ request = NULL;
+ }
+
+ return 0;
+ }
+ };
+
+ class BuilderThreadPool
+ {
+ public:
+ BuilderThreadPool() : _queue(new ACE_Activation_Queue()) {}
+ ~BuilderThreadPool() { _queue->queue()->close(); delete _queue; }
+
+ void Enqueue(MapBuildRequest* request)
+ {
+ _queue->enqueue(request);
+ }
+
+ ACE_Activation_Queue* Queue() { return _queue; }
+
+ private:
+ ACE_Activation_Queue* _queue;
+ };
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
new file mode 100644
index 00000000000..3e06ff58410
--- /dev/null
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_COMMON_H
+#define _MMAP_COMMON_H
+
+#include <string>
+#include <vector>
+#include <ace/OS_NS_sys_time.h>
+
+#include "Define.h"
+
+#ifndef _WIN32
+ #include <stddef.h>
+ #include <dirent.h>
+#endif
+
+#ifdef __linux__
+ #include <errno.h>
+#endif
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
+namespace MMAP
+{
+ inline bool matchWildcardFilter(const char* filter, const char* str)
+ {
+ if (!filter || !str)
+ return false;
+
+ // end on null character
+ while (*filter && *str)
+ {
+ if (*filter == '*')
+ {
+ if (*++filter == '\0') // wildcard at end of filter means all remaing chars match
+ return true;
+
+ while (true)
+ {
+ if (*filter == *str)
+ break;
+ if (*str == '\0')
+ return false; // reached end of string without matching next filter character
+ str++;
+ }
+ }
+ else if (*filter != *str)
+ return false; // mismatch
+
+ filter++;
+ str++;
+ }
+
+ return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0');
+ }
+
+ enum ListFilesResult
+ {
+ LISTFILE_DIRECTORY_NOT_FOUND = 0,
+ LISTFILE_OK = 1
+ };
+
+ inline ListFilesResult getDirContents(std::vector<std::string> &fileList, std::string dirpath = ".", std::string filter = "*")
+ {
+ #ifdef WIN32
+ HANDLE hFind;
+ WIN32_FIND_DATA findFileInfo;
+ std::string directory;
+
+ directory = dirpath + "/" + filter;
+
+ hFind = FindFirstFile(directory.c_str(), &findFileInfo);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ do
+ {
+ if ((findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ fileList.push_back(std::string(findFileInfo.cFileName));
+ }
+ while (FindNextFile(hFind, &findFileInfo));
+
+ FindClose(hFind);
+
+ #else
+ const char *p = dirpath.c_str();
+ DIR * dirp = opendir(p);
+ struct dirent * dp;
+ dirp = opendir(p);
+
+ while (dirp)
+ {
+ errno = 0;
+ if ((dp = readdir(dirp)) != NULL)
+ {
+ if (matchWildcardFilter(filter.c_str(), dp->d_name))
+ fileList.push_back(std::string(dp->d_name));
+ }
+ else
+ break;
+ }
+
+ if (dirp)
+ closedir(dirp);
+ else
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ #endif
+
+ return LISTFILE_OK;
+ }
+
+ inline uint32 getMSTime()
+ {
+ static const ACE_Time_Value ApplicationStartTime = ACE_OS::gettimeofday();
+ return (ACE_OS::gettimeofday() - ApplicationStartTime).msec();
+ }
+
+ inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
+ {
+ // getMSTime() have limited data range and this is case when it overflow in this tick
+ if (oldMSTime > newMSTime)
+ return (0xFFFFFFFF - oldMSTime) + newMSTime;
+ else
+ return newMSTime - oldMSTime;
+ }
+
+ inline uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
+ {
+ return getMSTimeDiff(oldMSTime, getMSTime());
+ }
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
new file mode 100644
index 00000000000..47d35b517d5
--- /dev/null
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+using namespace MMAP;
+
+bool checkDirectories(bool debugOutput)
+{
+ std::vector<std::string> dirFiles;
+
+ if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
+ {
+ printf("'maps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
+ {
+ printf("'vmaps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'mmaps' directory does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (debugOutput)
+ {
+ if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'meshes' directory does not exist (no place to put debugOutput files)\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool handleArgs(int argc, char** argv,
+ int &mapnum,
+ int &tileX,
+ int &tileY,
+ float &maxAngle,
+ bool &skipLiquid,
+ bool &skipContinents,
+ bool &skipJunkMaps,
+ bool &skipBattlegrounds,
+ bool &debugOutput,
+ bool &silent,
+ bool &bigBaseUnit,
+ char* &offMeshInputPath,
+ char* &file,
+ int& threads)
+{
+ char* param = NULL;
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--maxAngle") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ float maxangle = atof(param);
+ if (maxangle <= 90.f && maxangle >= 45.f)
+ maxAngle = maxangle;
+ else
+ printf("invalid option for '--maxAngle', using default\n");
+ }
+ else if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ threads = atoi(param);
+ printf("Using %i threads to extract mmaps\n", threads);
+ }
+ else if (strcmp(argv[i], "--file") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ file = param;
+ }
+ else if (strcmp(argv[i], "--tile") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* stileX = strtok(param, ",");
+ char* stileY = strtok(NULL, ",");
+ int tilex = atoi(stileX);
+ int tiley = atoi(stileY);
+
+ if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
+ tileX = tilex;
+ if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
+ tileY = tiley;
+
+ if (tileX < 0 || tileY < 0)
+ {
+ printf("invalid tile coords.\n");
+ return false;
+ }
+ }
+ else if (strcmp(argv[i], "--skipLiquid") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipLiquid = true;
+ else if (strcmp(param, "false") == 0)
+ skipLiquid = false;
+ else
+ printf("invalid option for '--skipLiquid', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipContinents") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipContinents = true;
+ else if (strcmp(param, "false") == 0)
+ skipContinents = false;
+ else
+ printf("invalid option for '--skipContinents', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipJunkMaps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipJunkMaps = true;
+ else if (strcmp(param, "false") == 0)
+ skipJunkMaps = false;
+ else
+ printf("invalid option for '--skipJunkMaps', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipBattlegrounds = true;
+ else if (strcmp(param, "false") == 0)
+ skipBattlegrounds = false;
+ else
+ printf("invalid option for '--skipBattlegrounds', using default\n");
+ }
+ else if (strcmp(argv[i], "--debugOutput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ debugOutput = true;
+ else if (strcmp(param, "false") == 0)
+ debugOutput = false;
+ else
+ printf("invalid option for '--debugOutput', using default true\n");
+ }
+ else if (strcmp(argv[i], "--silent") == 0)
+ {
+ silent = true;
+ }
+ else if (strcmp(argv[i], "--bigBaseUnit") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ bigBaseUnit = true;
+ else if (strcmp(param, "false") == 0)
+ bigBaseUnit = false;
+ else
+ printf("invalid option for '--bigBaseUnit', using default false\n");
+ }
+ else if (strcmp(argv[i], "--offMeshInput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ offMeshInputPath = param;
+ }
+ else
+ {
+ int map = atoi(argv[i]);
+ if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
+ mapnum = map;
+ else
+ {
+ printf("invalid map id\n");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+int finish(const char* message, int returnValue)
+{
+ printf("%s", message);
+ getchar();
+ return returnValue;
+}
+
+int main(int argc, char** argv)
+{
+ int threads = 3, mapnum = -1;
+ float maxAngle = 60.0f;
+ int tileX = -1, tileY = -1;
+ bool skipLiquid = false,
+ skipContinents = false,
+ skipJunkMaps = true,
+ skipBattlegrounds = false,
+ debugOutput = false,
+ silent = false,
+ bigBaseUnit = false;
+ char* offMeshInputPath = NULL;
+ char* file = NULL;
+
+ bool validParam = handleArgs(argc, argv, mapnum,
+ tileX, tileY, maxAngle,
+ skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
+ debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads);
+
+ if (!validParam)
+ return silent ? -1 : finish("You have specified invalid parameters", -1);
+
+ if (mapnum == -1 && debugOutput)
+ {
+ if (silent)
+ return -2;
+
+ printf("You have specifed debug output, but didn't specify a map to generate.\n");
+ printf("This will generate debug output for ALL maps.\n");
+ printf("Are you sure you want to continue? (y/n) ");
+ if (getchar() != 'y')
+ return 0;
+ }
+
+ if (!checkDirectories(debugOutput))
+ return silent ? -3 : finish("Press any key to close...", -3);
+
+ MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps,
+ skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath);
+
+ uint32 start = getMSTime();
+ if (file)
+ builder.buildMeshFromFile(file);
+ else if (tileX > -1 && tileY > -1 && mapnum >= 0)
+ builder.buildSingleTile(mapnum, tileX, tileY);
+ else if (mapnum >= 0)
+ builder.buildMap(uint32(mapnum));
+ else
+ builder.buildAllMaps(threads);
+
+ if (!silent)
+ printf("Finished. MMAPS were built in %u ms!\n", GetMSTimeDiffToNow(start));
+ return 0;
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
new file mode 100644
index 00000000000..3a87da3d4f1
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.cpp
@@ -0,0 +1,933 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TerrainBuilder.h"
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "VMapManager2.h"
+#include "MapTree.h"
+#include "ModelInstance.h"
+#include <vector>
+
+// ******************************************
+// Map file format defines
+// ******************************************
+struct map_fileheader
+{
+ uint32 mapMagic;
+ uint32 versionMagic;
+ uint32 buildMagic;
+ uint32 areaMapOffset;
+ uint32 areaMapSize;
+ uint32 heightMapOffset;
+ uint32 heightMapSize;
+ uint32 liquidMapOffset;
+ uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
+};
+
+#define MAP_HEIGHT_NO_HEIGHT 0x0001
+#define MAP_HEIGHT_AS_INT16 0x0002
+#define MAP_HEIGHT_AS_INT8 0x0004
+
+struct map_heightHeader
+{
+ uint32 fourcc;
+ uint32 flags;
+ float gridHeight;
+ float gridMaxHeight;
+};
+
+#define MAP_LIQUID_NO_TYPE 0x0001
+#define MAP_LIQUID_NO_HEIGHT 0x0002
+
+struct map_liquidHeader
+{
+ uint32 fourcc;
+ uint16 flags;
+ uint16 liquidType;
+ uint8 offsetX;
+ uint8 offsetY;
+ uint8 width;
+ uint8 height;
+ float liquidLevel;
+};
+
+#define MAP_LIQUID_TYPE_NO_WATER 0x00
+#define MAP_LIQUID_TYPE_WATER 0x01
+#define MAP_LIQUID_TYPE_OCEAN 0x02
+#define MAP_LIQUID_TYPE_MAGMA 0x04
+#define MAP_LIQUID_TYPE_SLIME 0x08
+#define MAP_LIQUID_TYPE_DARK_WATER 0x10
+#define MAP_LIQUID_TYPE_WMO_WATER 0x20
+
+namespace MMAP
+{
+
+ char const* MAP_VERSION_MAGIC = "v1.3";
+
+ TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ }
+ TerrainBuilder::~TerrainBuilder() { }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
+ {
+ switch (portion)
+ {
+ case ENTIRE:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ case TOP:
+ loopStart = 0;
+ loopEnd = V8_SIZE;
+ loopInc = 1;
+ break;
+ case LEFT:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
+ loopInc = V8_SIZE;
+ break;
+ case RIGHT:
+ loopStart = V8_SIZE - 1;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = V8_SIZE;
+ break;
+ case BOTTOM:
+ loopStart = V8_SIZE_SQ - V8_SIZE;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
+ {
+ loadMap(mapID, tileX+1, tileY, meshData, LEFT);
+ loadMap(mapID, tileX-1, tileY, meshData, RIGHT);
+ loadMap(mapID, tileX, tileY+1, meshData, TOP);
+ loadMap(mapID, tileX, tileY-1, meshData, BOTTOM);
+ }
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
+ {
+ char mapFileName[255];
+ sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
+
+ FILE* mapFile = fopen(mapFileName, "rb");
+ if (!mapFile)
+ return false;
+
+ map_fileheader fheader;
+ if (fread(&fheader, sizeof(map_fileheader), 1, mapFile) != 1 ||
+ fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)))
+ {
+ fclose(mapFile);
+ printf("%s is the wrong version, please extract new .map files\n", mapFileName);
+ return false;
+ }
+
+ map_heightHeader hheader;
+ fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
+
+ bool haveTerrain = false;
+ bool haveLiquid = false;
+ if (fread(&hheader, sizeof(map_heightHeader), 1, mapFile) == 1)
+ {
+ haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
+ haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
+ }
+
+ // no data in this map file
+ if (!haveTerrain && !haveLiquid)
+ {
+ fclose(mapFile);
+ return false;
+ }
+
+ // data used later
+ uint16 holes[16][16];
+ memset(holes, 0, sizeof(holes));
+ uint8 liquid_type[16][16];
+ memset(liquid_type, 0, sizeof(liquid_type));
+ G3D::Array<int> ltriangles;
+ G3D::Array<int> ttriangles;
+
+ // terrain data
+ if (haveTerrain)
+ {
+ float heightMultiplier;
+ float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
+ int expected = V9_SIZE_SQ + V8_SIZE_SQ;
+
+ if (hheader.flags & MAP_HEIGHT_AS_INT8)
+ {
+ uint8 v9[V9_SIZE_SQ];
+ uint8 v8[V8_SIZE_SQ];
+ int count = 0;
+ count += fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
+ count += fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
+ if (count != expected)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
+
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
+
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (int i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else if (hheader.flags & MAP_HEIGHT_AS_INT16)
+ {
+ uint16 v9[V9_SIZE_SQ];
+ uint16 v8[V8_SIZE_SQ];
+ int count = 0;
+ count += fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
+ count += fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
+ if (count != expected)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
+
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
+
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (int i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else
+ {
+ int count = 0;
+ count += fread(V9, sizeof(float), V9_SIZE_SQ, mapFile);
+ count += fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
+ if (count != expected)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
+ }
+
+ // hole data
+ if (fheader.holesSize != 0)
+ {
+ memset(holes, 0, fheader.holesSize);
+ fseek(mapFile, fheader.holesOffset, SEEK_SET);
+ if (fread(holes, fheader.holesSize, 1, mapFile) != 1)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+ }
+
+ int count = meshData.solidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ for (int i = 0; i < V8_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ int indices[3], loopStart = 0, loopEnd = 0, loopInc = 0;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ for (int j = TOP; j <= BOTTOM; j+=1)
+ {
+ getHeightTriangle(i, Spot(j), indices);
+ ttriangles.append(indices[2] + count);
+ ttriangles.append(indices[1] + count);
+ ttriangles.append(indices[0] + count);
+ }
+ }
+
+ // liquid data
+ if (haveLiquid)
+ {
+ map_liquidHeader lheader;
+ fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
+ if (fread(&lheader, sizeof(map_liquidHeader), 1, mapFile) != 1)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+
+
+ float* liquid_map = NULL;
+
+ if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
+ if (fread(liquid_type, sizeof(liquid_type), 1, mapFile) != 1)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ uint32 toRead = lheader.width * lheader.height;
+ liquid_map = new float [toRead];
+ if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+ }
+
+ if (liquid_map)
+ {
+ int count = meshData.liquidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+ int row, col;
+
+ // generate coordinates
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ int j = 0;
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+
+ if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
+ col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
+ {
+ // dummy vert using invalid height
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
+ continue;
+ }
+
+ getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
+ meshData.liquidVerts.append(coord[0]);
+ meshData.liquidVerts.append(coord[2]);
+ meshData.liquidVerts.append(coord[1]);
+ j++;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
+ }
+ }
+
+ delete [] liquid_map;
+
+ int indices[3], loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+
+ // generate triangles
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ for (int j = TOP; j <= BOTTOM; j+= triInc)
+ {
+ getHeightTriangle(i, Spot(j), indices, true);
+ ltriangles.append(indices[2] + count);
+ ltriangles.append(indices[1] + count);
+ ltriangles.append(indices[0] + count);
+ }
+ }
+ }
+
+ fclose(mapFile);
+
+ // now that we have gathered the data, we can figure out which parts to keep:
+ // liquid above ground, ground above liquid
+ int loopStart = 0, loopEnd = 0, loopInc = 0, tTriCount = 4;
+ bool useTerrain, useLiquid;
+
+ float* lverts = meshData.liquidVerts.getCArray();
+ int* ltris = ltriangles.getCArray();
+
+ float* tverts = meshData.solidVerts.getCArray();
+ int* ttris = ttriangles.getCArray();
+
+ if ((ltriangles.size() + ttriangles.size()) == 0)
+ return false;
+
+ // make a copy of liquid vertices
+ // used to pad right-bottom frame due to lost vertex data at extraction
+ float* lverts_copy = NULL;
+ if (meshData.liquidVerts.size())
+ {
+ lverts_copy = new float[meshData.liquidVerts.size()];
+ memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
+ }
+
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ {
+ for (int j = 0; j < 2; ++j)
+ {
+ // default is true, will change to false if needed
+ useTerrain = true;
+ useLiquid = true;
+ uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER;
+ // FIXME: "warning: the address of ‘liquid_type’ will always evaluate as ‘true’"
+
+ // if there is no liquid, don't use liquid
+ if (!meshData.liquidVerts.size() || !ltriangles.size())
+ useLiquid = false;
+ else
+ {
+ liquidType = getLiquidType(i, liquid_type);
+ switch (liquidType)
+ {
+ default:
+ useLiquid = false;
+ break;
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ // merge different types of water
+ liquidType = NAV_WATER;
+ break;
+ case MAP_LIQUID_TYPE_MAGMA:
+ liquidType = NAV_MAGMA;
+ break;
+ case MAP_LIQUID_TYPE_SLIME:
+ liquidType = NAV_SLIME;
+ break;
+ case MAP_LIQUID_TYPE_DARK_WATER:
+ // players should not be here, so logically neither should creatures
+ useTerrain = false;
+ useLiquid = false;
+ break;
+ }
+ }
+
+ // if there is no terrain, don't use terrain
+ if (!ttriangles.size())
+ useTerrain = false;
+
+ // while extracting ADT data we are losing right-bottom vertices
+ // this code adds fair approximation of lost data
+ if (useLiquid)
+ {
+ float quadHeight = 0;
+ uint32 validCount = 0;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts_copy[ltris[idx]*3 + 1];
+ if (h != INVALID_MAP_LIQ_HEIGHT && h < INVALID_MAP_LIQ_HEIGHT_MAX)
+ {
+ quadHeight += h;
+ validCount++;
+ }
+ }
+
+ // update vertex height data
+ if (validCount > 0 && validCount < 3)
+ {
+ quadHeight /= validCount;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts[ltris[idx]*3 + 1];
+ if (h == INVALID_MAP_LIQ_HEIGHT || h > INVALID_MAP_LIQ_HEIGHT_MAX)
+ lverts[ltris[idx]*3 + 1] = quadHeight;
+ }
+ }
+
+ // no valid vertexes - don't use this poly at all
+ if (validCount == 0)
+ useLiquid = false;
+ }
+
+ // if there is a hole here, don't use the terrain
+ if (useTerrain && fheader.holesSize != 0)
+ useTerrain = !isHole(i, holes);
+
+ // we use only one terrain kind per quad - pick higher one
+ if (useTerrain && useLiquid)
+ {
+ float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
+ for(uint32 x = 0; x < 3; x++)
+ {
+ float h = lverts[ltris[x]*3 + 1];
+ if (minLLevel > h)
+ minLLevel = h;
+
+ if (maxLLevel < h)
+ maxLLevel = h;
+ }
+
+ float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
+ float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ for(uint32 x = 0; x < 6; x++)
+ {
+ float h = tverts[ttris[x]*3 + 1];
+ if (maxTLevel < h)
+ maxTLevel = h;
+
+ if (minTLevel > h)
+ minTLevel = h;
+ }
+
+ // terrain under the liquid?
+ if (minLLevel > maxTLevel)
+ useTerrain = false;
+
+ //liquid under the terrain?
+ if (minTLevel > maxLLevel)
+ useLiquid = false;
+ }
+
+ // store the result
+ if (useLiquid)
+ {
+ meshData.liquidType.append(liquidType);
+ for (int k = 0; k < 3; ++k)
+ meshData.liquidTris.append(ltris[k]);
+ }
+
+ if (useTerrain)
+ for (int k = 0; k < 3*tTriCount/2; ++k)
+ meshData.solidTris.append(ttris[k]);
+
+ // advance to next set of triangles
+ ltris += 3;
+ ttris += 3*tTriCount/2;
+ }
+ }
+
+ if (lverts_copy)
+ delete [] lverts_copy;
+
+ return meshData.solidTris.size() || meshData.liquidTris.size();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ switch (grid)
+ {
+ case GRID_V9:
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index];
+ break;
+ case GRID_V8:
+ coord[0] = (xOffset + index%(V8_SIZE)*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V8_SIZE))*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[2] = v[index];
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/)
+ {
+ int rowOffset = square/V8_SIZE;
+ if (!liquid)
+ switch (triangle)
+ {
+ case TOP:
+ indices[0] = square+rowOffset; // 0-----1 .... 128
+ indices[1] = square+1+rowOffset; // |\ T /|
+ indices[2] = (V9_SIZE_SQ)+square; // | \ / |
+ break; // |L 0 R| .. 127
+ case LEFT: // | / \ |
+ indices[0] = square+rowOffset; // |/ B \|
+ indices[1] = (V9_SIZE_SQ)+square; // 129---130 ... 386
+ indices[2] = square+V9_SIZE+rowOffset; // |\ /|
+ break; // | \ / |
+ case RIGHT: // | 128 | .. 255
+ indices[0] = square+1+rowOffset; // | / \ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // |/ \|
+ indices[2] = (V9_SIZE_SQ)+square; // 258---259 ... 515
+ break;
+ case BOTTOM:
+ indices[0] = (V9_SIZE_SQ)+square;
+ indices[1] = square+V9_SIZE+1+rowOffset;
+ indices[2] = square+V9_SIZE+rowOffset;
+ break;
+ default: break;
+ }
+ else
+ switch (triangle)
+ { // 0-----1 .... 128
+ case TOP: // |\ |
+ indices[0] = square+rowOffset; // | \ T |
+ indices[1] = square+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+1+rowOffset; // | B \ |
+ break; // | \|
+ case BOTTOM: // 129---130 ... 386
+ indices[0] = square+rowOffset; // |\ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+rowOffset; // | \ |
+ break; // | \ |
+ default: break; // | \|
+ } // 258---259 ... 515
+
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index2];
+ }
+
+ static uint16 holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
+ static uint16 holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
+
+ /**************************************************************************/
+ bool TerrainBuilder::isHole(int square, const uint16 holes[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+ int holeRow = row % 8 / 2;
+ int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
+
+ uint16 hole = holes[cellRow][cellCol];
+
+ return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
+ }
+
+ /**************************************************************************/
+ uint8 TerrainBuilder::getLiquidType(int square, const uint8 liquid_type[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+
+ return liquid_type[cellRow][cellCol];
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ IVMapManager* vmapManager = new VMapManager2();
+ int result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
+ bool retval = false;
+
+ do
+ {
+ if (result == VMAP_LOAD_RESULT_ERROR)
+ break;
+
+ InstanceTreeMap instanceTrees;
+ ((VMapManager2*)vmapManager)->getInstanceMapTree(instanceTrees);
+
+ if (!instanceTrees[mapID])
+ break;
+
+ ModelInstance* models = NULL;
+ uint32 count = 0;
+ instanceTrees[mapID]->getModelInstances(models, count);
+
+ if (!models)
+ break;
+
+ for (uint32 i = 0; i < count; ++i)
+ {
+ ModelInstance instance = models[i];
+
+ // model instances exist in tree even though there are instances of that model in this tile
+ WorldModel* worldModel = instance.getWorldModel();
+ if (!worldModel)
+ continue;
+
+ // now we have a model to add to the meshdata
+ retval = true;
+
+ std::vector<GroupModel> groupModels;
+ worldModel->getGroupModels(groupModels);
+
+ // all M2s need to have triangle indices reversed
+ bool isM2 = instance.name.find(".m2") != std::string::npos || instance.name.find(".M2") != std::string::npos;
+
+ // transform data
+ float scale = instance.iScale;
+ G3D::Matrix3 rotation = G3D::Matrix3::fromEulerAnglesXYZ(G3D::pi()*instance.iRot.z/-180.f, G3D::pi()*instance.iRot.x/-180.f, G3D::pi()*instance.iRot.y/-180.f);
+ G3D::Vector3 position = instance.iPos;
+ position.x -= 32*GRID_SIZE;
+ position.y -= 32*GRID_SIZE;
+
+ for (std::vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it)
+ {
+ std::vector<G3D::Vector3> tempVertices;
+ std::vector<G3D::Vector3> transformedVertices;
+ std::vector<MeshTriangle> tempTriangles;
+ WmoLiquid* liquid = NULL;
+
+ it->getMeshData(tempVertices, tempTriangles, liquid);
+
+ // first handle collision mesh
+ transform(tempVertices, transformedVertices, scale, rotation, position);
+
+ int offset = meshData.solidVerts.size() / 3;
+
+ copyVertices(transformedVertices, meshData.solidVerts);
+ copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
+
+ // now handle liquid data
+ if (liquid)
+ {
+ std::vector<G3D::Vector3> liqVerts;
+ std::vector<int> liqTris;
+ uint32 tilesX, tilesY, vertsX, vertsY;
+ G3D::Vector3 corner;
+ liquid->getPosInfo(tilesX, tilesY, corner);
+ vertsX = tilesX + 1;
+ vertsY = tilesY + 1;
+ uint8* flags = liquid->GetFlagsStorage();
+ float* data = liquid->GetHeightStorage();
+ uint8 type = NAV_EMPTY;
+
+ // convert liquid type to NavTerrain
+ switch (liquid->GetType())
+ {
+ case 0:
+ case 1:
+ type = NAV_WATER;
+ break;
+ case 2:
+ type = NAV_MAGMA;
+ break;
+ case 3:
+ type = NAV_SLIME;
+ break;
+ }
+
+ // indexing is weird...
+ // after a lot of trial and error, this is what works:
+ // vertex = y*vertsX+x
+ // tile = x*tilesY+y
+ // flag = y*tilesY+x
+
+ G3D::Vector3 vert;
+ for (uint32 x = 0; x < vertsX; ++x)
+ for (uint32 y = 0; y < vertsY; ++y)
+ {
+ vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y*vertsX + x]);
+ vert = vert * rotation * scale + position;
+ vert.x *= -1.f;
+ vert.y *= -1.f;
+ liqVerts.push_back(vert);
+ }
+
+ int idx1, idx2, idx3, idx4;
+ uint32 square;
+ for (uint32 x = 0; x < tilesX; ++x)
+ for (uint32 y = 0; y < tilesY; ++y)
+ if ((flags[x+y*tilesX] & 0x0f) != 0x0f)
+ {
+ square = x * tilesY + y;
+ idx1 = square+x;
+ idx2 = square+1+x;
+ idx3 = square+tilesY+1+1+x;
+ idx4 = square+tilesY+1+x;
+
+ // top triangle
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx2);
+ liqTris.push_back(idx1);
+ // bottom triangle
+ liqTris.push_back(idx4);
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx1);
+ }
+
+ uint32 liqOffset = meshData.liquidVerts.size() / 3;
+ for (uint32 i = 0; i < liqVerts.size(); ++i)
+ meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
+
+ for (uint32 i = 0; i < liqTris.size() / 3; ++i)
+ {
+ meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset);
+ meshData.liquidType.append(type);
+ }
+ }
+ }
+ }
+ }
+ while (false);
+
+ vmapManager->unloadMap(mapID, tileX, tileY);
+ delete vmapManager;
+
+ return retval;
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::transform(std::vector<G3D::Vector3> &source, std::vector<G3D::Vector3> &transformedVertices, float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position)
+ {
+ for (std::vector<G3D::Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ // apply tranform, then mirror along the horizontal axes
+ G3D::Vector3 v((*it) * rotation * scale + position);
+ v.x *= -1.f;
+ v.y *= -1.f;
+ transformedVertices.push_back(v);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyVertices(std::vector<G3D::Vector3> &source, G3D::Array<float> &dest)
+ {
+ for (std::vector<G3D::Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).y);
+ dest.push_back((*it).z);
+ dest.push_back((*it).x);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(std::vector<MeshTriangle> &source, G3D::Array<int> &dest, int offset, bool flip)
+ {
+ if (flip)
+ {
+ for (std::vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx2+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx0+offset);
+ }
+ }
+ else
+ {
+ for (std::vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx0+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx2+offset);
+ }
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(G3D::Array<int> &source, G3D::Array<int> &dest, int offset)
+ {
+ int* src = source.getCArray();
+ for (int32 i = 0; i < source.size(); ++i)
+ dest.append(src[i] + offset);
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris)
+ {
+ std::map<int, int> vertMap;
+
+ int* t = tris.getCArray();
+ float* v = verts.getCArray();
+
+ G3D::Array<float> cleanVerts;
+ int index, count = 0;
+ // collect all the vertex indices from triangle
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ if (vertMap.find(t[i]) != vertMap.end())
+ continue;
+ std::pair<int, int> val;
+ val.first = t[i];
+
+ index = val.first;
+ val.second = count;
+
+ vertMap.insert(val);
+ cleanVerts.append(v[index * 3], v[index * 3 + 1], v[index * 3 + 2]);
+ count++;
+ }
+
+ verts.fastClear();
+ verts.append(cleanVerts);
+ cleanVerts.clear();
+
+ // update triangles to use new indices
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ std::map<int, int>::iterator it;
+ if ((it = vertMap.find(t[i])) == vertMap.end())
+ continue;
+
+ t[i] = (*it).second;
+ }
+
+ vertMap.clear();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath)
+ {
+ // no meshfile input given?
+ if (offMeshFilePath == NULL)
+ return;
+
+ FILE* fp = fopen(offMeshFilePath, "rb");
+ if (!fp)
+ {
+ printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath);
+ return;
+ }
+
+ // pretty silly thing, as we parse entire file and load only the tile we need
+ // but we don't expect this file to be too large
+ char* buf = new char[512];
+ while(fgets(buf, 512, fp))
+ {
+ float p0[3], p1[3];
+ uint32 mid, tx, ty;
+ float size;
+ if (sscanf(buf, "%d %d,%d (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty,
+ &p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size) != 10)
+ continue;
+
+ if (mapID == mid && tileX == tx && tileY == ty)
+ {
+ meshData.offMeshConnections.append(p0[1]);
+ meshData.offMeshConnections.append(p0[2]);
+ meshData.offMeshConnections.append(p0[0]);
+
+ meshData.offMeshConnections.append(p1[1]);
+ meshData.offMeshConnections.append(p1[2]);
+ meshData.offMeshConnections.append(p1[0]);
+
+ meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided
+ meshData.offMeshConnectionRads.append(size); // agent size equivalent
+ // can be used same way as polygon flags
+ meshData.offMeshConnectionsAreas.append((unsigned char)0xFF);
+ meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path
+ }
+
+ }
+
+ delete [] buf;
+ fclose(fp);
+ }
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h
new file mode 100644
index 00000000000..069a5a94c84
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_TERRAIN_BUILDER_H
+#define _MMAP_TERRAIN_BUILDER_H
+
+#include "PathCommon.h"
+#include "WorldModel.h"
+
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+
+namespace MMAP
+{
+ enum Spot
+ {
+ TOP = 1,
+ RIGHT = 2,
+ LEFT = 3,
+ BOTTOM = 4,
+ ENTIRE = 5
+ };
+
+ enum Grid
+ {
+ GRID_V8,
+ GRID_V9
+ };
+
+ static const int V9_SIZE = 129;
+ static const int V9_SIZE_SQ = V9_SIZE*V9_SIZE;
+ static const int V8_SIZE = 128;
+ static const int V8_SIZE_SQ = V8_SIZE*V8_SIZE;
+ static const float GRID_SIZE = 533.33333f;
+ static const float GRID_PART_SIZE = GRID_SIZE/V8_SIZE;
+
+ // see contrib/extractor/system.cpp, CONF_use_minHeight
+ static const float INVALID_MAP_LIQ_HEIGHT = -500.f;
+ static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f;
+
+ // see following files:
+ // contrib/extractor/system.cpp
+ // src/game/Map.cpp
+
+ struct MeshData
+ {
+ G3D::Array<float> solidVerts;
+ G3D::Array<int> solidTris;
+
+ G3D::Array<float> liquidVerts;
+ G3D::Array<int> liquidTris;
+ G3D::Array<uint8> liquidType;
+
+ // offmesh connection data
+ G3D::Array<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection
+ G3D::Array<float> offMeshConnectionRads;
+ G3D::Array<unsigned char> offMeshConnectionDirs;
+ G3D::Array<unsigned char> offMeshConnectionsAreas;
+ G3D::Array<unsigned short> offMeshConnectionsFlags;
+ };
+
+ class TerrainBuilder
+ {
+ public:
+ TerrainBuilder(bool skipLiquid);
+ ~TerrainBuilder();
+
+ void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath);
+
+ bool usesLiquids() { return !m_skipLiquid; }
+
+ // vert and triangle methods
+ static void transform(std::vector<G3D::Vector3> &original, std::vector<G3D::Vector3> &transformed,
+ float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position);
+ static void copyVertices(std::vector<G3D::Vector3> &source, G3D::Array<float> &dest);
+ static void copyIndices(std::vector<VMAP::MeshTriangle> &source, G3D::Array<int> &dest, int offest, bool flip);
+ static void copyIndices(G3D::Array<int> &src, G3D::Array<int> &dest, int offset);
+ static void cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris);
+ private:
+ /// Loads a portion of a map's terrain
+ bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion);
+
+ /// Sets loop variables for selecting only certain parts of a map's terrain
+ void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc);
+
+ /// Controls whether liquids are loaded
+ bool m_skipLiquid;
+
+ /// Load the map terrain from file
+ bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float> &vertices, G3D::Array<int> &triangles, Spot portion);
+
+ /// Get the vector coordinate for a specific position
+ void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the triangle's vector indices for a specific position
+ void getHeightTriangle(int square, Spot triangle, int* indices, bool liquid = false);
+
+ /// Determines if the specific position's triangles should be rendered
+ bool isHole(int square, const uint16 holes[16][16]);
+
+ /// Get the liquid vector coordinate for a specific position
+ void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the liquid type for a specific position
+ uint8 getLiquidType(int square, const uint8 liquid_type[16][16]);
+
+ // hide parameterless and copy constructor
+ TerrainBuilder();
+ TerrainBuilder(const TerrainBuilder &tb);
+ };
+}
+
+#endif
+
diff --git a/src/tools/mmaps_generator/VMapExtensions.cpp b/src/tools/mmaps_generator/VMapExtensions.cpp
new file mode 100644
index 00000000000..08929e5259d
--- /dev/null
+++ b/src/tools/mmaps_generator/VMapExtensions.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <vector>
+#include "MapTree.h"
+#include "VMapManager2.h"
+#include "WorldModel.h"
+#include "ModelInstance.h"
+
+namespace VMAP
+{
+ // Need direct access to encapsulated VMAP data, so we add functions for MMAP generator
+ // maybe add MapBuilder as friend to all of the below classes would be better?
+
+ // declared in src/shared/vmap/MapTree.h
+ void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
+ {
+ models = iTreeValues;
+ count = iNTreeValues;
+ }
+
+ // declared in src/shared/vmap/VMapManager2.h
+ void VMapManager2::getInstanceMapTree(InstanceTreeMap &instanceMapTree)
+ {
+ instanceMapTree = iInstanceMapTrees;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WorldModel::getGroupModels(std::vector<GroupModel> &groupModels)
+ {
+ groupModels = this->groupModels;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void GroupModel::getMeshData(std::vector<G3D::Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid)
+ {
+ vertices = this->vertices;
+ triangles = this->triangles;
+ liquid = iLiquid;
+ }
+
+ // declared in src/shared/vmap/ModelInstance.h
+ WorldModel* ModelInstance::getWorldModel()
+ {
+ return iModel;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WmoLiquid::getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const
+ {
+ tilesX = iTilesX;
+ tilesY = iTilesY;
+ corner = iCorner;
+ }
+}
diff --git a/src/tools/vmap4_assembler/CMakeLists.txt b/src/tools/vmap4_assembler/CMakeLists.txt
index 00eb8b76fd6..97a83c4c482 100644
--- a/src/tools/vmap4_assembler/CMakeLists.txt
+++ b/src/tools/vmap4_assembler/CMakeLists.txt
@@ -20,7 +20,6 @@ include_directories(
${ZLIB_INCLUDE_DIR}
)
-add_definitions(-DNO_CORE_FUNCS)
add_executable(vmap4assembler VMapAssembler.cpp)
add_dependencies(vmap4assembler storm)
diff --git a/src/tools/vmap4_extractor/adtfile.cpp b/src/tools/vmap4_extractor/adtfile.cpp
index d2ad71b8179..a5193739440 100644
--- a/src/tools/vmap4_extractor/adtfile.cpp
+++ b/src/tools/vmap4_extractor/adtfile.cpp
@@ -69,8 +69,7 @@ void fixname2(char* name, size_t len)
char* GetExtension(char* FileName)
{
- char* szTemp;
- if (szTemp = strrchr(FileName, '.'))
+ if (char* szTemp = strrchr(FileName, '.'))
return szTemp;
return NULL;
}
diff --git a/src/tools/vmap4_extractor/model.cpp b/src/tools/vmap4_extractor/model.cpp
index 9523071b6d4..68b839c4a6d 100644
--- a/src/tools/vmap4_extractor/model.cpp
+++ b/src/tools/vmap4_extractor/model.cpp
@@ -154,10 +154,10 @@ ModelInstance::ModelInstance(MPQFile& f, char const* ModelInstName, uint32 mapID
fseek(input, 8, SEEK_SET); // get the correct no of vertices
int nVertices;
- fread(&nVertices, sizeof (int), 1, input);
+ int count = fread(&nVertices, sizeof (int), 1, input);
fclose(input);
- if(nVertices == 0)
+ if (count != 1 || nVertices == 0)
return;
uint16 adtId = 0;// not used for models
diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp
index 3c7b433e125..bcbd705f834 100644
--- a/src/tools/vmap4_extractor/vmapexport.cpp
+++ b/src/tools/vmap4_extractor/vmapexport.cpp
@@ -577,8 +577,7 @@ int main(int argc, char ** argv)
printf("Your output directory seems to be polluted, please use an empty directory!\n");
printf("<press return to exit>");
char garbage[2];
- scanf("%c", garbage);
- return 1;
+ return scanf("%c", garbage);
}
}
diff --git a/src/tools/vmap4_extractor/wmo.cpp b/src/tools/vmap4_extractor/wmo.cpp
index 85cae02e41c..63187963550 100644
--- a/src/tools/vmap4_extractor/wmo.cpp
+++ b/src/tools/vmap4_extractor/wmo.cpp
@@ -523,10 +523,10 @@ WMOInstance::WMOInstance(MPQFile& f, char const* WmoInstName, uint32 mapID, uint
fseek(input, 8, SEEK_SET); // get the correct no of vertices
int nVertices;
- fread(&nVertices, sizeof (int), 1, input);
+ int count = fread(&nVertices, sizeof (int), 1, input);
fclose(input);
- if(nVertices == 0)
+ if (count != 1 || nVertices == 0)
return;
float x,z;