aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml12
-rw-r--r--sql/updates/world/3.3.5/2016_05_30_09_world.sql5
-rw-r--r--sql/updates/world/3.3.5/2016_05_30_10_world.sql26
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_00_world.sql6
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_01_world_335.sql9
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_02_world.sql19
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_03_world.sql3
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_04_world.sql15
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_05_world.sql12
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_06_world.sql10
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_07_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_08_world.sql13
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_09_world.sql3
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_10_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_11_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_12_world_335.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_13_world.sql23
-rw-r--r--sql/updates/world/3.3.5/2016_05_31_14_world_335.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_00_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_01_world.sql5
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_02_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_03_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_04_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_05_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_06_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_07_world.sql4
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_08_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_09_world.sql4
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_10_world.sql2
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_11_world.sql81
-rw-r--r--sql/updates/world/3.3.5/2016_06_01_12_world.sql4
-rw-r--r--src/server/database/Updater/DBUpdater.cpp14
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp1189
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.h64
-rw-r--r--src/server/game/Entities/Player/Player.cpp7
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp12
-rw-r--r--src/server/game/Spells/Spell.cpp35
-rw-r--r--src/server/game/Spells/SpellMgr.cpp2
-rw-r--r--src/server/game/World/World.cpp3
-rw-r--r--src/server/game/World/World.h1
40 files changed, 1549 insertions, 58 deletions
diff --git a/.travis.yml b/.travis.yml
index cddb98492a2..ab2b1a0998c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,6 @@
+sudo: required
+dist: trusty
+
language: cpp
compiler:
- clang
@@ -6,16 +9,11 @@ git:
depth: 1
before_install:
- - echo "yes" | sudo add-apt-repository ppa:george-edison55/precise-backports
- - echo "yes" | sudo add-apt-repository ppa:boost-latest/ppa
- - echo "yes" | sudo add-apt-repository ppa:ubuntu-toolchain-r/test
- - echo "yes" | sudo add-apt-repository 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main'
- - wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-get -qq update
- - sudo apt-get -qq install build-essential libtool gcc-4.8 g++-4.8 make cmake cmake-data openssl clang-3.5
+ - sudo apt-get -qq install build-essential libtool make cmake cmake-data openssl
- sudo apt-get -qq install libssl-dev libmysqlclient-dev libmysql++-dev libreadline6-dev zlib1g-dev libbz2-dev
- sudo apt-get -qq install libboost1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-system1.55-dev libboost-program-options1.55-dev libboost-iostreams1.55-dev
- - export CC=clang-3.5 CXX=clang++-3.5
+ - sudo apt-get -qq install mysql-server
- git config user.email "travis@build.bot" && git config user.name "Travis CI"
- git tag -a -m "Travis build" init
diff --git a/sql/updates/world/3.3.5/2016_05_30_09_world.sql b/sql/updates/world/3.3.5/2016_05_30_09_world.sql
new file mode 100644
index 00000000000..3ec588d0982
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_30_09_world.sql
@@ -0,0 +1,5 @@
+UPDATE `quest_template_addon` SET `SpecialFlags`=2 WHERE `ID`=1144;
+UPDATE `creature_template` SET `flags_extra`=64 WHERE `entry` IN(28111,28112,28078,28079,23667, 23668, 32669,28844,28843,24199,24198,23564);
+UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34203 AND `Item`=43959;
+UPDATE `smart_scripts` SET `target_type`=12, `target_param1`=1 WHERE `entryorguid`=2735500 AND `source_type`=9 AND `id`=5 AND `link`=0;
+UPDATE `gameobject` SET `position_x`=5445.057129, `position_y`=4908.831543, `position_z`=-189.508224 WHERE `guid`=99748;
diff --git a/sql/updates/world/3.3.5/2016_05_30_10_world.sql b/sql/updates/world/3.3.5/2016_05_30_10_world.sql
new file mode 100644
index 00000000000..b8dbe0b94a6
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_30_10_world.sql
@@ -0,0 +1,26 @@
+DELETE FROM `gameobject_loot_template` WHERE `Entry`=26782;
+INSERT INTO `gameobject_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES
+(26782, 44724, 0, 100, 0, 1, 1, 1, 1, NULL), -- Everfrost Chip
+(26782, 44725, 0, 19, 0, 1, 2, 1, 1, NULL), -- Everfrost Chip (Quest Starter)
+(26782, 44729, 0, 81, 0, 1, 2, 1, 1, NULL); -- Everfrost Powder
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=4 AND `SourceGroup`=26782;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(4, 26782, 44724, 0, 1, 8, 0, 13420, 0, 0, 0, 0, 0, '', NULL), -- Everfrost Chip only drops if player is rewarded for ever frost,
+(4, 26782, 44729, 0, 0, 9, 0, 13420, 0, 0, 1, 0, 0, '', NULL), -- Everfrost powder only drops if player has not taken or completed Everfrost,
+(4, 26782, 44729, 0, 0, 8, 0, 13420, 0, 0, 1, 0, 0, '', NULL), -- Everfrost powder only drops if player has not taken or completed Everfrost,
+(4, 26782, 44725, 0, 0, 9, 0, 13420, 0, 0, 1, 0, 0, '', NULL), -- Everfrost Chip (Quest Starter) only drops if player has not taken or completed Everfrost,
+(4, 26782, 44725, 0, 0, 8, 0, 13420, 0, 0, 1, 0, 0, '', NULL), -- Everfrost Chip (Quest Starter) only drops if player has not taken or completed Everfrost,
+(4, 26782, 44725, 0, 0, 5, 0, 1119, 8, 0, 0, 0, 0, '', NULL); -- Everfrost Chip (Quest Starter) only drops if player is friendly with Sons of Hodir,
+
+UPDATE `quest_template_addon` SET `SpecialFlags`=1 WHERE `ID`=13421;
+
+DELETE FROM `creature_questender` WHERE `quest`=13421;
+INSERT INTO `creature_questender` (`id`, `quest`) VALUES
+(32594, 13421);
+
+DELETE FROM `creature_queststarter` WHERE `quest`=13421;
+INSERT INTO `creature_queststarter` (`id`, `quest`) VALUES
+(32594, 13421);
+
+UPDATE `quest_template_addon` SET `PrevQuestID`=13420 WHERE `ID`=13421;
diff --git a/sql/updates/world/3.3.5/2016_05_31_00_world.sql b/sql/updates/world/3.3.5/2016_05_31_00_world.sql
new file mode 100644
index 00000000000..29a29413167
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_00_world.sql
@@ -0,0 +1,6 @@
+--
+UPDATE `creature` SET `position_x`= -3739.09, `position_y`= 5405.21, `position_z`=-3.33350, `orientation`=2.70103, `MovementType`=0, `spawndist`=0 WHERE `guid`=78837 AND `id`=22464;
+UPDATE `creature` SET `position_x`= -3743.46, `position_y`= 5403.87, `position_z`=-3.33635, `orientation`=1.16950, `MovementType`=0, `spawndist`=0 WHERE `guid`=78838 AND `id`=22464;
+UPDATE `creature` SET `position_x`= -3746.42, `position_y`= 5405.33, `position_z`=-3.33737, `orientation`=0.39587, `MovementType`=0, `spawndist`=0 WHERE `guid`=78839 AND `id`=22464;
+UPDATE `creature` SET `position_x`= -3750.65, `position_y`= 5408.94, `position_z`=-3.33391, `orientation`=2.54786, `MovementType`=0, `spawndist`=0 WHERE `guid`=78840 AND `id`=22464;
+UPDATE `creature` SET `position_x`= -3741.11, `position_y`= 5403.61, `position_z`=-3.33493, `orientation`=1.9117 WHERE `guid`=78818 AND `id`=22458;
diff --git a/sql/updates/world/3.3.5/2016_05_31_01_world_335.sql b/sql/updates/world/3.3.5/2016_05_31_01_world_335.sql
new file mode 100644
index 00000000000..2d92a9fc537
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_01_world_335.sql
@@ -0,0 +1,9 @@
+--
+DELETE FROM `conditions` WHERE `SourceEntry` IN (6545, 6546, 6547) AND `SourceTypeOrReferenceId` IN (19, 20);
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(19, 0, 6545, 0, 0, 9, 0, 6543, 0, 0, 0, 0, 0, "", "Quest 'Warsong Runner Update' - Can accept if player has quest 'The Warsong Reports' in quest log"),
+(20, 0, 6545, 0, 0, 9, 0, 6543, 0, 0, 0, 0, 0, "", "Quest 'Warsong Runner Update' - Can accept if player has quest 'The Warsong Reports' in quest log"),
+(19, 0, 6546, 0, 0, 9, 0, 6543, 0, 0, 0, 0, 0, "", "Quest 'Warsong Outrider Update' - Can accept if player has quest 'The Warsong Reports' in quest log"),
+(20, 0, 6546, 0, 0, 9, 0, 6543, 0, 0, 0, 0, 0, "", "Quest 'Warsong Outrider Update' - Can accept if player has quest 'The Warsong Reports' in quest log"),
+(19, 0, 6547, 0, 0, 9, 0, 6543, 0, 0, 0, 0, 0, "", "Quest 'Warsong Scout Update' - Can accept if player has quest 'The Warsong Reports' in quest log"),
+(20, 0, 6547, 0, 0, 9, 0, 6543, 0, 0, 0, 0, 0, "", "Quest 'Warsong Scout Update' - Can accept if player has quest 'The Warsong Reports' in quest log");
diff --git a/sql/updates/world/3.3.5/2016_05_31_02_world.sql b/sql/updates/world/3.3.5/2016_05_31_02_world.sql
new file mode 100644
index 00000000000..09674385ab4
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_02_world.sql
@@ -0,0 +1,19 @@
+--
+UPDATE `creature` SET `spawndist`=0, `MovementType`=0, `unit_flags`=537133824 WHERE `guid` IN (103024, 103025, 103026, 103027, 103028);
+
+DELETE FROM `creature_addon` WHERE `guid` IN (103024, 103025, 103026, 103027, 103028);
+INSERT INTO `creature_addon` (`guid`, `mount`, `bytes1`, `bytes2`, `auras`) VALUES
+(103024, 0, 0x0, 0x1, '29266'), -- 25680 - 29266 - 29266
+(103025, 0, 0x0, 0x1, '29266'), -- 25680 - 29266 - 29266
+(103026, 0, 0x0, 0x1, '29266'), -- 25680 - 29266 - 29266
+(103027, 0, 0x0, 0x1, '29266'), -- 25680 - 29266 - 29266
+(103028, 0, 0x0, 0x1, '29266'); -- 25680 - 29266 - 29266
+
+UPDATE `creature` SET `spawndist`=0, `MovementType`=0 WHERE `id`=29546;
+
+DELETE FROM `creature_template_addon` WHERE `entry` IN (24196, 29546);
+INSERT INTO `creature_template_addon` (`entry`, `mount`, `bytes1`, `bytes2`, `auras`) VALUES
+(24196, 0, 0x0, 0x1, '29266'), -- 24196 - 29266
+(29546, 0, 0x0, 0x1, '29266'); -- 29546 - 29266
+
+DELETE FROM `creature_addon` WHERE `guid`=105491;
diff --git a/sql/updates/world/3.3.5/2016_05_31_03_world.sql b/sql/updates/world/3.3.5/2016_05_31_03_world.sql
new file mode 100644
index 00000000000..60c07d15735
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_03_world.sql
@@ -0,0 +1,3 @@
+--
+UPDATE `conditions` SET `ConditionValue1`=11248, `Comment`='Winterskorn mobs drop item 33314 only if Quest 11248 has been rewarded' WHERE `SourceTypeOrReferenceId`=1 AND `SourceEntry`=33314;
+UPDATE `conditions` SET `ConditionValue1`=11256, `Comment`='Winterskorn mobs drop item 33345 only if Quest 11256 has been rewarded' WHERE `SourceTypeOrReferenceId`=1 AND `SourceEntry`=33345;
diff --git a/sql/updates/world/3.3.5/2016_05_31_04_world.sql b/sql/updates/world/3.3.5/2016_05_31_04_world.sql
new file mode 100644
index 00000000000..d3ddfcfa05c
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_04_world.sql
@@ -0,0 +1,15 @@
+--
+DELETE FROM `areatrigger_scripts` WHERE `entry` IN (5187, 5190);
+INSERT INTO `areatrigger_scripts` VALUES
+(5187, "SmartTrigger"),
+(5190, "SmartTrigger");
+
+DELETE FROM `smart_scripts` WHERE `entryorguid` IN (5187, 5190) AND `source_type`=2;
+INSERT INTO `smart_scripts` VALUES
+(5187, 2, 0, 0, 46, 0, 100, 0, 0, 0, 0, 0, 62, 571, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6150.7563, -1071.3817, 402.7154, 2.1916, "Areatrigger - On Trigger - Teleport"),
+(5190, 2, 0, 0, 46, 0, 100, 0, 0, 0, 0, 0, 62, 571, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6314.1860, -1758.2946, 457.0714, 1.6787, "Areatrigger - On Trigger - Teleport");
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=22 AND `SourceId`=2 AND SourceEntry IN (5187, 5190);
+INSERT INTO `conditions` VALUES
+(22, 1, 5187, 2, 0, 8, 0, 12821, 0, 0, 0, 0, 0, "", "Event requires quest rewarded"),
+(22, 1, 5190, 2, 0, 8, 0, 12821, 0, 0, 0, 0, 0, "", "Event requires quest rewarded");
diff --git a/sql/updates/world/3.3.5/2016_05_31_05_world.sql b/sql/updates/world/3.3.5/2016_05_31_05_world.sql
new file mode 100644
index 00000000000..18bbf61baa4
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_05_world.sql
@@ -0,0 +1,12 @@
+--
+UPDATE `quest_template_addon` SET `PrevQuestID`=0 WHERE `Id`=12836;
+
+DELETE FROM `conditions` WHERE `SourceEntry`=12828 AND `SourceTypeOrReferenceId` IN (19, 20);
+INSERT INTO `conditions` VALUES
+(19, 0, 12828, 0, 0, 8, 0, 12827, 0, 0, 0, 0, 0, "", "Show mark for quest 'Ample Inspiration' only if quest 'Reclaimed Rations' is rewarded"),
+(20, 0, 12828, 0, 0, 8, 0, 12827, 0, 0, 0, 0, 0, "", "Can accept quest 'Ample Inspiration' only if quest 'Reclaimed Rations' is rewarded");
+
+DELETE FROM `quest_template_addon` WHERE `ID` IN (12862, 13060);
+INSERT INTO `quest_template_addon` (`ID`, `PrevQuestId`) VALUES
+(12862, 12824),
+(13060, 12824);
diff --git a/sql/updates/world/3.3.5/2016_05_31_06_world.sql b/sql/updates/world/3.3.5/2016_05_31_06_world.sql
new file mode 100644
index 00000000000..46d8b875c82
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_06_world.sql
@@ -0,0 +1,10 @@
+--
+-- remove mis-spawned Mistbat (entry: 16353)
+DELETE FROM `creature` WHERE `guid`=57016;
+
+-- remove one too many spawned Elder Springpaw (entry: 15651)
+DELETE FROM `creature` WHERE `guid`=55946;
+
+-- set random movement and spawn distance to the common value for that creature
+UPDATE `creature` SET `spawndist`=5, `MovementType`=1 WHERE `guid`=55942;
+UPDATE `creature` SET `spawndist`=5, `MovementType`=1 WHERE `guid`=55945;
diff --git a/sql/updates/world/3.3.5/2016_05_31_07_world.sql b/sql/updates/world/3.3.5/2016_05_31_07_world.sql
new file mode 100644
index 00000000000..7ed017ff1cd
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_07_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 WHERE `Item`=11509;
diff --git a/sql/updates/world/3.3.5/2016_05_31_08_world.sql b/sql/updates/world/3.3.5/2016_05_31_08_world.sql
new file mode 100644
index 00000000000..2cf3fbcdfc4
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_08_world.sql
@@ -0,0 +1,13 @@
+--
+DELETE FROM `conditions` WHERE `SourceEntry`=23359 AND `SourceTypeOrReferenceId`=17;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`, `ErrorType`, `ErrorTextId`,`ScriptName`,`Comment`) VALUES
+(17, 0, 23359, 0, 0, 31, 1, 3, 5357, 0, 0, 0, 0, "", "Transmogrify! target can be Land Walker"),
+(17, 0, 23359, 0, 0, 36, 1, 0, 0, 0, 0, 0, 0, "", "Transmogrify! target must be alive"),
+(17, 0, 23359, 0, 1, 31, 1, 3, 5358, 0, 0, 0, 0, "", "Transmogrify! target can be Cliff Giant"),
+(17, 0, 23359, 0, 1, 36, 1, 0, 0, 0, 0, 0, 0, "", "Transmogrify! target must be alive"),
+(17, 0, 23359, 0, 2, 31, 1, 3, 5359, 0, 0, 0, 0, "", "Transmogrify! target can be Shore Strider"),
+(17, 0, 23359, 0, 2, 36, 1, 0, 0, 0, 0, 0, 0, "", "Transmogrify! target must be alive"),
+(17, 0, 23359, 0, 3, 31, 1, 3, 5360, 0, 0, 0, 0, "", "Transmogrify! target can be Deep Strider"),
+(17, 0, 23359, 0, 3, 36, 1, 0, 0, 0, 0, 0, 0, "", "Transmogrify! target must be alive"),
+(17, 0, 23359, 0, 4, 31, 1, 3, 5361, 0, 0, 0, 0, "", "Transmogrify! target can be Wave Strider"),
+(17, 0, 23359, 0, 4, 36, 1, 0, 0, 0, 0, 0, 0, "", "Transmogrify! target must be alive");
diff --git a/sql/updates/world/3.3.5/2016_05_31_09_world.sql b/sql/updates/world/3.3.5/2016_05_31_09_world.sql
new file mode 100644
index 00000000000..73d62a92772
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_09_world.sql
@@ -0,0 +1,3 @@
+--
+UPDATE `creature_template` SET `speed_run`=0.42857 WHERE `entry` IN (16027);
+UPDATE `smart_scripts` SET `action_param1`=22 WHERE `entryorguid`=16027 AND `source_type`=0 AND `id`=3 AND `link`=4;
diff --git a/sql/updates/world/3.3.5/2016_05_31_10_world.sql b/sql/updates/world/3.3.5/2016_05_31_10_world.sql
new file mode 100644
index 00000000000..e7536b3a51a
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_10_world.sql
@@ -0,0 +1,2 @@
+--
+DELETE FROM `creature_loot_template` WHERE `Item`=31245;
diff --git a/sql/updates/world/3.3.5/2016_05_31_11_world.sql b/sql/updates/world/3.3.5/2016_05_31_11_world.sql
new file mode 100644
index 00000000000..e5d35f5eb42
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_11_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `reference_loot_template` SET `Item`=49667, `Chance`=10 WHERE `Item`=48679;
diff --git a/sql/updates/world/3.3.5/2016_05_31_12_world_335.sql b/sql/updates/world/3.3.5/2016_05_31_12_world_335.sql
new file mode 100644
index 00000000000..b20c38b36ba
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_12_world_335.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template_addon` SET `PrevQuestID`=155 WHERE `ID`=214;
diff --git a/sql/updates/world/3.3.5/2016_05_31_13_world.sql b/sql/updates/world/3.3.5/2016_05_31_13_world.sql
new file mode 100644
index 00000000000..6dd3d984c1c
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_13_world.sql
@@ -0,0 +1,23 @@
+--
+UPDATE `quest_template_addon` SET `ExclusiveGroup`=8747 WHERE `Id` IN (8747, 8752, 8757);
+
+UPDATE `quest_template_addon` SET `PrevQuestId`=0, `SpecialFlags`=1 WHERE `Id` IN (8764, 8765, 8766);
+
+DELETE FROM `conditions` WHERE `SourceEntry` IN (8764, 8765, 8766);
+INSERT INTO `conditions` VALUES
+(19, 0, 8764, 0, 0, 2, 0, 21200, 1, 1, 0, 0, 0, "", "Quest 8764 is available only if player has item 21200 in bags or bank"),
+(20, 0, 8764, 0, 0, 2, 0, 21200, 1, 1, 0, 0, 0, "", "Show question mark for quest 8764 only if player has item 21200 in bags or bank"),
+
+(19, 0, 8765, 0, 0, 2, 0, 21210, 1, 1, 0, 0, 0, "", "Quest 8765 is available only if player has item 21210 in bags or bank"),
+(20, 0, 8765, 0, 0, 2, 0, 21210, 1, 1, 0, 0, 0, "", "Show question mark for quest 8765 only if player has item 21210 in bags or bank"),
+
+(19, 0, 8766, 0, 0, 2, 0, 21205, 1, 1, 0, 0, 0, "", "Quest 8766 is available only if player has item 21205 in bags or bank"),
+(20, 0, 8766, 0, 0, 2, 0, 21205, 1, 1, 0, 0, 0, "", "Show question mark for quest 8766 only if player has item 21205 in bags or bank");
+
+DELETE FROM `quest_offer_reward` WHERE `ID` IN (8747, 8752, 8757);
+INSERT INTO `quest_offer_reward` (`ID`,`Emote1`,`Emote2`,`Emote3`,`Emote4`,`EmoteDelay1`,`EmoteDelay2`,`EmoteDelay3`,`EmoteDelay4`,`RewardText`,`VerifiedBuild`) VALUES
+(8747,1,0,0,0,0,0,0,0,(SELECT `CompletionText` FROM `quest_request_items` WHERE `ID`=8747),12340),
+(8752,1,0,0,0,0,0,0,0,(SELECT `CompletionText` FROM `quest_request_items` WHERE `ID`=8752),12340),
+(8757,1,0,0,0,0,0,0,0,(SELECT `CompletionText` FROM `quest_request_items` WHERE `ID`=8757),12340);
+
+UPDATE `quest_request_items` SET `CompletionText` = "Never have I seen such tenacity! The Bronze Flight grants you one final enchantment. The Timeless One himself has requested it so!$B$BHand me your signet ring so that I may make the necessary adjustments." WHERE `ID` IN (8751, 8756, 8761);
diff --git a/sql/updates/world/3.3.5/2016_05_31_14_world_335.sql b/sql/updates/world/3.3.5/2016_05_31_14_world_335.sql
new file mode 100644
index 00000000000..496a9645bda
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_05_31_14_world_335.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `creature_template` SET `unit_flags`=33536 WHERE `entry`=24117;
diff --git a/sql/updates/world/3.3.5/2016_06_01_00_world.sql b/sql/updates/world/3.3.5/2016_06_01_00_world.sql
new file mode 100644
index 00000000000..5f4e7dbb01c
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_00_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template` SET `AllowableRaces`=1024 WHERE `ID`=9429;
diff --git a/sql/updates/world/3.3.5/2016_06_01_01_world.sql b/sql/updates/world/3.3.5/2016_06_01_01_world.sql
new file mode 100644
index 00000000000..80ecabe34aa
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_01_world.sql
@@ -0,0 +1,5 @@
+--
+UPDATE `creature` SET `position_x`=3154.581, `position_y`=-3126.18, `position_z`=293.5911, `orientation`=4.430199 WHERE `guid`=76311;
+UPDATE `creature` SET `position_x`=3128.622, `position_y`=-3119.604, `position_z`=293.4113, `orientation`=4.738929 WHERE `guid`=76312;
+UPDATE `creature` SET `position_x`=3175.281, `position_y`=-3134.764, `position_z`=293.4368, `orientation`=4.244924 WHERE `guid`=76313;
+UPDATE `smart_scripts` SET `action_param1`=34 WHERE `entryorguid`=16027 AND `source_type`=0 AND `id`=3 AND `link`=4;
diff --git a/sql/updates/world/3.3.5/2016_06_01_02_world.sql b/sql/updates/world/3.3.5/2016_06_01_02_world.sql
new file mode 100644
index 00000000000..e2c27b15339
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_02_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template_addon` SET `PrevQuestID`=985 WHERE `ID`=986;
diff --git a/sql/updates/world/3.3.5/2016_06_01_03_world.sql b/sql/updates/world/3.3.5/2016_06_01_03_world.sql
new file mode 100644
index 00000000000..54beb26b15c
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_03_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template_addon` SET `AllowableClasses`=256 WHERE `ID`=397;
diff --git a/sql/updates/world/3.3.5/2016_06_01_04_world.sql b/sql/updates/world/3.3.5/2016_06_01_04_world.sql
new file mode 100644
index 00000000000..1de1716102b
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_04_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template` SET `AllowableRaces`=512 WHERE `ID`=9425;
diff --git a/sql/updates/world/3.3.5/2016_06_01_05_world.sql b/sql/updates/world/3.3.5/2016_06_01_05_world.sql
new file mode 100644
index 00000000000..3d7a32ac308
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_05_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `item_loot_template` SET `Chance`=100 WHERE `Entry`=24336;
diff --git a/sql/updates/world/3.3.5/2016_06_01_06_world.sql b/sql/updates/world/3.3.5/2016_06_01_06_world.sql
new file mode 100644
index 00000000000..9344147e067
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_06_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template` SET `AllowableRaces`=0 WHERE `ID`=1486;
diff --git a/sql/updates/world/3.3.5/2016_06_01_07_world.sql b/sql/updates/world/3.3.5/2016_06_01_07_world.sql
new file mode 100644
index 00000000000..9c159c54cb6
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_07_world.sql
@@ -0,0 +1,4 @@
+--
+DELETE FROM `creature_queststarter` WHERE `quest`=249;
+INSERT INTO `creature_queststarter` (`id`, `quest`) VALUES
+(313, 249);
diff --git a/sql/updates/world/3.3.5/2016_06_01_08_world.sql b/sql/updates/world/3.3.5/2016_06_01_08_world.sql
new file mode 100644
index 00000000000..356c0561bf2
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_08_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `creature_loot_template` SET `Chance`=100 WHERE `Item`=29795;
diff --git a/sql/updates/world/3.3.5/2016_06_01_09_world.sql b/sql/updates/world/3.3.5/2016_06_01_09_world.sql
new file mode 100644
index 00000000000..5cf06521a19
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_09_world.sql
@@ -0,0 +1,4 @@
+--
+DELETE FROM `quest_template_addon` WHERE `ID`=768;
+INSERT INTO `quest_template_addon` (`ID`, `RequiredSkillID`, `RequiredSkillPoints`) VALUES
+(768, 393, 1);
diff --git a/sql/updates/world/3.3.5/2016_06_01_10_world.sql b/sql/updates/world/3.3.5/2016_06_01_10_world.sql
new file mode 100644
index 00000000000..abc358431e3
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_10_world.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template_addon` SET `SpecialFlags`=`SpecialFlags`|1 WHERE `ID` IN (961, 3375, 3382, 3483);
diff --git a/sql/updates/world/3.3.5/2016_06_01_11_world.sql b/sql/updates/world/3.3.5/2016_06_01_11_world.sql
new file mode 100644
index 00000000000..dc0b7b05a33
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_11_world.sql
@@ -0,0 +1,81 @@
+--
+DELETE FROM `conditions` WHERE `SourceEntry`=254 AND `SourceTypeOrReferenceId`=20 AND `ConditionTypeOrReference` IN (8, 28);
+INSERT INTO `conditions` VALUES
+(20, 0, 254, 0, 0, 8, 0, 253, 0, 0, 1, 0, 0, "", "Show question mark for quest 'Digging Through the Dirt' only if quest 'Bride of the Embalmer' is not rewarded"),
+(20, 0, 254, 0, 0, 28, 0, 253, 0, 0, 1, 0, 0, "", "Show question mark for quest 'Digging Through the Dirt' only if quest 'Bride of the Embalmer' is not completed");
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` IN (19, 20) AND `SourceEntry` IN (349, 410, 431, 593, 779, 795, 813, 926, 961, 1191, 1462, 1463, 1464, 1714, 1789, 1790, 4041, 6581, 7645, 8508, 8732, 9483, 10850, 10919);
+INSERT INTO `conditions` VALUES
+(19, 0, 349, 0, 0, 9, 0, 348, 0, 0, 0, 0, 0, "", "Show quest 'Stranglethorn Fever' only if quest 'Stranglethorn Fever' is accepted but not completed"),
+(20, 0, 349, 0, 0, 9, 0, 348, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Stranglethorn Fever' only if quest 'Stranglethorn Fever' is accepted but not completed"),
+
+(19, 0, 410, 0, 0, 9, 0, 409, 0, 0, 0, 0, 0, "", "Show quest 'The Dormant Shade' only if quest 'Proving Allegiance' is accepted but not completed"),
+(20, 0, 410, 0, 0, 9, 0, 409, 0, 0, 0, 0, 0, "", "Show question mark for quest 'The Dormant Shade' only if quest 'Proving Allegiance' is accepted but not completed"),
+
+(19, 0, 431, 0, 0, 9, 0, 409, 0, 0, 0, 0, 0, "", "Show quest 'Candles of Beckoning' only if quest 'Proving Allegiance' is accepted but not completed"),
+(20, 0, 431, 0, 0, 9, 0, 409, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Candles of Beckoning' only if quest 'Proving Allegiance' is accepted but not completed"),
+
+(19, 0, 593, 0, 0, 9, 0, 592, 0, 0, 0, 0, 0, "", "Show quest 'Filling the Soul Gem' only if quest 'Saving Yenniku' is accepted but not completed"),
+(20, 0, 593, 0, 0, 9, 0, 592, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Filling the Soul Gem' only if quest 'Saving Yenniku' is accepted but not completed"),
+
+(19, 0, 779, 0, 0, 9, 0, 717, 0, 0, 0, 0, 0, "", "Show quest 'Seal of the Earth' only if quest 'Tremors of the Earth' is accepted but not completed"),
+(20, 0, 779, 0, 0, 9, 0, 717, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Seal of the Earth' only if quest 'Tremors of the Earth' is accepted but not completed"),
+
+(19, 0, 795, 0, 0, 9, 0, 793, 0, 0, 0, 0, 0, "", "Show quest 'Seal of the Earth' only if quest 'Broken Alliances' is accepted but not completed"),
+(20, 0, 795, 0, 0, 9, 0, 793, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Seal of the Earth' only if quest 'Broken Alliances' is accepted but not completed"),
+
+(19, 0, 813, 0, 0, 9, 0, 812, 0, 0, 0, 0, 0, "", "Show quest 'Finding the Antidote' only if quest 'Need for a Cure' is accepted"),
+(20, 0, 813, 0, 0, 9, 0, 812, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Finding the Antidote' only if quest 'Need for a Cure' is accepted but not completed"),
+
+(19, 0, 926, 0, 0, 9, 0, 924, 0, 0, 0, 0, 0, "", "Show quest 'Flawed Power Stone' only if quest 'The Demon Seed' is accepted but not completed"),
+(20, 0, 926, 0, 0, 9, 0, 924, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Flawed Power Stone' only if quest 'The Demon Seed' is accepted but not completed"),
+
+(19, 0, 961, 0, 0, 9, 0, 944, 0, 0, 0, 0, 0, "", "Show quest 'Onu is Meditating' only if quest 'The Demon Seed' is accepted but not completed OR"),
+(20, 0, 961, 0, 0, 9, 0, 944, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Onu is Meditating' only if quest 'The Demon Seed' is accepted but not completed OR"),
+(19, 0, 961, 0, 1, 9, 0, 949, 0, 0, 0, 0, 0, "", "Show quest 'Onu is Meditating' only if quest 'The Twilight Camp' is accepted but not completed"),
+(20, 0, 961, 0, 1, 9, 0, 949, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Onu is Meditating' only if quest 'The Twilight Camp' is accepted but not completed"),
+
+(19, 0, 1191, 0, 0, 9, 0, 1190, 0, 0, 0, 0, 0, "", "Show quest 'Zamek's Distraction' only if quest 'Keeping Pace' is accepted but not completed"),
+(20, 0, 1191, 0, 0, 9, 0, 1190, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Zamek's Distraction' only if quest 'Keeping Pace' is accepted but not completed"),
+
+(19, 0, 1462, 0, 0, 28, 0, 1520, 0, 0, 0, 0, 0, "", "Show quest 'Earth Sapta' only if quest 'Call of Earth' is completed"),
+(20, 0, 1462, 0, 0, 28, 0, 1520, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Earth Sapta' only if quest 'Call of Earth' is completed"),
+
+(19, 0, 1463, 0, 0, 28, 0, 1517, 0, 0, 0, 0, 0, "", "Show quest 'Earth Sapta' only if quest 'Call of Earth' is ompleted"),
+(20, 0, 1463, 0, 0, 28, 0, 1517, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Earth Sapta' only if quest 'Call of Earth' is completed"),
+
+(19, 0, 1464, 0, 0, 9, 0, 1526, 0, 0, 0, 0, 0, "", "Show quest 'Fire Sapta' only if quest 'Call of Fire' is accepted but not completed"),
+(20, 0, 1464, 0, 0, 9, 0, 1526, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Fire Sapta' only if quest 'Call of Fire' is accepted but not completed"),
+
+(19, 0, 1714, 0, 0, 9, 0, 1712, 0, 0, 0, 0, 0, "", "Show quest 'Essence of the Exile' only if quest 'Cyclonian' is accepted but not completed"),
+(20, 0, 1714, 0, 0, 9, 0, 1712, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Essence of the Exile' only if quest 'Cyclonian' is accepted but not completed"),
+
+(19, 0, 1789, 0, 0, 9, 0, 1783, 0, 0, 0, 0, 0, "", "Show quest 'The Symbol of Life' only if quest 'The Tome of Divinity' is accepted but not completed"),
+(20, 0, 1789, 0, 0, 9, 0, 1783, 0, 0, 0, 0, 0, "", "Show question mark for quest 'The Symbol of Life' only if quest 'The Tome of Divinity' is accepted but not completed"),
+
+(19, 0, 1790, 0, 0, 9, 0, 1786, 0, 0, 0, 0, 0, "", "Show quest 'The Symbol of Life' only if quest 'The Tome of Divinity' is accepted but not completed"),
+(20, 0, 1790, 0, 0, 9, 0, 1786, 0, 0, 0, 0, 0, "", "Show question mark for quest 'The Symbol of Life' only if quest 'The Tome of Divinity' is accepted but not completed"),
+
+(19, 0, 4041, 0, 0, 9, 0, 3909, 0, 0, 0, 0, 0, "", "Show quest 'The Videre Elixir' only if quest 'The Videre Elixir' is accepted but not completed"),
+(20, 0, 4041, 0, 0, 9, 0, 3909, 0, 0, 0, 0, 0, "", "Show question mark for quest 'The Videre Elixir' only if quest 'The Videre Elixir' is accepted but not completed"),
+
+(19, 0, 6581, 0, 0, 9, 0, 6571, 0, 0, 0, 0, 0, "", "Show quest 'Warsong Saw Blades' only if quest 'Warsong Supplies' is accepted but not completed"),
+(20, 0, 6581, 0, 0, 9, 0, 6571, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Warsong Saw Blades' only if quest 'Warsong Supplies' is accepted but not completed"),
+
+(19, 0, 7645, 0, 0, 9, 0, 7643, 0, 0, 0, 0, 0, "", "Show quest 'Manna-Enriched Horse Feed' only if quest 'Ancient Equine Spirit' is accepted but not completed"),
+(20, 0, 7645, 0, 0, 9, 0, 7643, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Manna-Enriched Horse Feed' only if quest 'Ancient Equine Spirit' is accepted but not completed"),
+
+(19, 0, 8508, 0, 0, 9, 0, 8507, 0, 0, 0, 0, 0, "", "Show quest 'Field Duty Papers' only if quest 'Field Duty' is accepted but not completed"),
+(20, 0, 8508, 0, 0, 9, 0, 8507, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Field Duty Papers' only if quest 'Field Duty' is accepted but not completed"),
+
+(19, 0, 8732, 0, 0, 9, 0, 8731, 0, 0, 0, 0, 0, "", "Show quest 'Field Duty Papers' only if quest 'Field Duty' is accepted but not completed"),
+(20, 0, 8732, 0, 0, 9, 0, 8731, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Field Duty Papers' only if quest 'Field Duty' is accepted but not completed"),
+
+(19, 0, 9483, 0, 0, 9, 0, 9472, 0, 0, 0, 0, 0, "", "Show quest 'Life's Finer Pleasures' only if quest 'Arelion's Mistress' is accepted but not completed"),
+(20, 0, 9483, 0, 0, 9, 0, 9472, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Life's Finer Pleasures' only if quest 'Arelion's Mistress' is accepted but not completed"),
+
+(19, 0, 10850, 0, 0, 9, 0, 10855, 0, 0, 0, 0, 0, "", "Show quest 'Nether Gas In a Fel Fire Engine' only if quest 'Fel Reavers, No Thanks!' is accepted but not completed"),
+(20, 0, 10850, 0, 0, 9, 0, 10855, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Nether Gas In a Fel Fire Engine' only if quest 'Fel Reavers, No Thanks!' is accepted but not completed"),
+
+(19, 0, 10919, 0, 0, 9, 0, 10916, 0, 0, 0, 0, 0, "", "Show quest 'Fei Fei's Treat' only if quest 'Digging for Prayer Beads' is accepted but not completed"),
+(20, 0, 10919, 0, 0, 9, 0, 10916, 0, 0, 0, 0, 0, "", "Show question mark for quest 'Fei Fei's Treat' only if quest 'Digging for Prayer Beads' is accepted but not completed");
diff --git a/sql/updates/world/3.3.5/2016_06_01_12_world.sql b/sql/updates/world/3.3.5/2016_06_01_12_world.sql
new file mode 100644
index 00000000000..f6fb906bb70
--- /dev/null
+++ b/sql/updates/world/3.3.5/2016_06_01_12_world.sql
@@ -0,0 +1,4 @@
+--
+UPDATE `conditions` SET `SourceEntry`=49667 WHERE `SourceEntry`=48679 AND `SourceTypeOrReferenceId`=10 AND `SourceGroup`=10016;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=22137 AND `source_type`=0 AND `id`=7;
+UPDATE `creature_text` SET `emote`=0 WHERE `entry`=19666 AND `groupid`=2 AND `id`=0;
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index 0d51f8ed060..85213a7f709 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -55,7 +55,7 @@ bool DBUpdaterUtil::CheckExecutable()
}
}
- TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
+ TC_LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
absolute(exe).generic_string().c_str());
return false;
@@ -178,7 +178,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
// Path of temp file
static Path const temp("create_table.sql");
- // Create temporary query to use external mysql cli
+ // Create temporary query to use external MySQL CLi
std::ofstream file(temp.generic_string());
if (!file.is_open())
{
@@ -197,7 +197,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
}
catch (UpdateException&)
{
- TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str());
+ TC_LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str());
boost::filesystem::remove(temp);
return false;
}
@@ -280,7 +280,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
{
case LOCATION_REPOSITORY:
{
- TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.",
+ TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing. Try fixing it by cloning the source again.",
base.generic_string().c_str());
break;
@@ -288,7 +288,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
case LOCATION_DOWNLOAD:
{
TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"https://github.com/TrinityCore/TrinityCore/releases\"" \
- " and place it in your server directory.", base.filename().generic_string().c_str());
+ " and place it in your worldserver directory.", base.filename().generic_string().c_str());
break;
}
}
@@ -355,7 +355,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!std::isdigit(port_or_socket[0]))
{
- // We can't check here if host == "." because is named localhost if socket option is enabled
+ // We can't check if host == "." here, because it is named localhost if socket option is enabled
args.push_back("-P0");
args.push_back("--protocol=SOCKET");
args.push_back("-S" + port_or_socket);
@@ -383,7 +383,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (ret != EXIT_SUCCESS)
{
TC_LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \
- " If you are an user pull the latest revision from the repository. If you are a developer fix your sql query.",
+ " If you are a user, pull the latest revision from the repository. If you are a developer, fix your sql query.",
path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str());
throw UpdateException("update failed");
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 12bfb86251b..68cb268ac6c 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -19,6 +19,51 @@
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
+static const uint8 NUM_TALENT_TREES = 3;
+static const uint8 NUM_SPEC_ICONICS = 3;
+
+enum Specs
+{
+ SPEC_WARRIOR_ARMS = 0,
+ SPEC_WARRIOR_FURY = 1,
+ SPEC_WARRIOR_PROTECTION = 2,
+
+ SPEC_PALADIN_HOLY = 0,
+ SPEC_PALADIN_PROTECTION = 1,
+ SPEC_PALADIN_RETRIBUTION = 2,
+
+ SPEC_HUNTER_BEAST_MASTERY = 0,
+ SPEC_HUNTER_MARKSMANSHIP = 1,
+ SPEC_HUNTER_SURVIVAL = 2,
+
+ SPEC_ROGUE_ASSASSINATION = 0,
+ SPEC_ROGUE_COMBAT = 1,
+ SPEC_ROGUE_SUBLETY = 2,
+
+ SPEC_PRIEST_DISCIPLINE = 0,
+ SPEC_PRIEST_HOLY = 1,
+ SPEC_PRIEST_SHADOW = 2,
+
+ SPEC_DEATH_KNIGHT_BLOOD = 0,
+ SPEC_DEATH_KNIGHT_FROST = 1,
+ SPEC_DEATH_KNIGHT_UNHOLY = 2,
+
+ SPEC_SHAMAN_ELEMENTAL = 0,
+ SPEC_SHAMAN_ENHANCEMENT = 1,
+ SPEC_SHAMAN_RESTORATION = 2,
+
+ SPEC_MAGE_ARCANE = 0,
+ SPEC_MAGE_FIRE = 1,
+ SPEC_MAGE_FROST = 2,
+
+ SPEC_WARLOCK_AFFLICTION = 0,
+ SPEC_WARLOCK_DEMONOLOGY = 1,
+ SPEC_WARLOCK_DESTRUCTION = 2,
+
+ SPEC_DRUID_BALANCE = 0,
+ SPEC_DRUID_FERAL = 1,
+ SPEC_DRUID_RESTORATION = 2
+};
enum Spells
{
/* Generic */
@@ -27,29 +72,449 @@ enum Spells
SPELL_THROW = 2764,
SPELL_SHOOT_WAND = 5019,
- /* Paladin */
- PASSIVE_ILLUMINATION = 20215,
+ /* Warrior - Generic */
+ SPELL_BATTLE_STANCE = 2457,
+ SPELL_BERSERKER_STANCE = 2458,
+ SPELL_DEFENSIVE_STANCE = 71,
+ SPELL_CHARGE = 11578,
+ SPELL_INTERCEPT = 20252,
+ SPELL_ENRAGED_REGEN = 55694,
+ SPELL_INTIMIDATING_SHOUT= 5246,
+ SPELL_PUMMEL = 6552,
+ SPELL_SHIELD_BASH = 72,
+ SPELL_BLOODRAGE = 2687,
+
+ /* Warrior - Arms */
+ SPELL_SWEEPING_STRIKES = 12328,
+ SPELL_MORTAL_STRIKE = 12294,
+ SPELL_BLADESTORM = 46924,
+ SPELL_REND = 47465,
+ SPELL_RETALIATION = 20230,
+ SPELL_SHATTERING_THROW = 64382,
+ SPELL_THUNDER_CLAP = 47502,
+
+ /* Warrior - Fury */
+ SPELL_DEATH_WISH = 12292,
+ SPELL_BLOODTHIRST = 23881,
+ PASSIVE_TITANS_GRIP = 46917,
+ SPELL_DEMO_SHOUT = 47437,
+ SPELL_EXECUTE = 47471,
+ SPELL_HEROIC_FURY = 60970,
+ SPELL_RECKLESSNESS = 1719,
+ SPELL_PIERCING_HOWL = 12323,
+
+ /* Warrior - Protection */
+ SPELL_VIGILANCE = 50720,
+ SPELL_DEVASTATE = 20243,
+ SPELL_SHOCKWAVE = 46968,
+ SPELL_CONCUSSION_BLOW = 12809,
+ SPELL_DISARM = 676,
+ SPELL_LAST_STAND = 12975,
+ SPELL_SHIELD_BLOCK = 2565,
+ SPELL_SHIELD_SLAM = 47488,
+ SPELL_SHIELD_WALL = 871,
+ SPELL_SPELL_REFLECTION = 23920,
+
+ /* Paladin - Generic */
+ SPELL_AURA_MASTERY = 31821,
+ SPELL_LAY_ON_HANDS = 48788,
+ SPELL_BLESSING_OF_MIGHT = 48932,
+ SPELL_AVENGING_WRATH = 31884,
+ SPELL_DIVINE_PROTECTION = 498,
+ SPELL_DIVINE_SHIELD = 642,
+ SPELL_HAMMER_OF_JUSTICE = 10308,
+ SPELL_HAND_OF_FREEDOM = 1044,
+ SPELL_HAND_OF_PROTECTION = 10278,
+ SPELL_HAND_OF_SACRIFICE = 6940,
+
+ /* Paladin - Holy*/
+ PASSIVE_ILLUMINATION = 20215,
+ SPELL_HOLY_SHOCK = 20473,
+ SPELL_BEACON_OF_LIGHT = 53563,
+ SPELL_CONSECRATION = 48819,
+ SPELL_FLASH_OF_LIGHT = 48785,
+ SPELL_HOLY_LIGHT = 48782,
+ SPELL_DIVINE_FAVOR = 20216,
+ SPELL_DIVINE_ILLUMINATION = 31842,
+
+ /* Paladin - Protection */
+ SPELL_BLESS_OF_SANC = 20911,
+ SPELL_HOLY_SHIELD = 20925,
+ SPELL_AVENGERS_SHIELD = 48827,
+ SPELL_DIVINE_SACRIFICE = 64205,
+ SPELL_HAMMER_OF_RIGHTEOUS = 53595,
+ SPELL_RIGHTEOUS_FURY = 25780,
+ SPELL_SHIELD_OF_RIGHTEOUS = 61411,
+
+ /* Paladin - Retribution */
+ SPELL_SEAL_OF_COMMAND = 20375,
+ SPELL_CRUSADER_STRIKE = 35395,
+ SPELL_DIVINE_STORM = 53385,
+ SPELL_JUDGEMENT = 20271,
+ SPELL_HAMMER_OF_WRATH = 48806,
+
+ /* Hunter - Generic */
+ SPELL_DETERRENCE = 19263,
+ SPELL_EXPLOSIVE_TRAP = 49067,
+ SPELL_FREEZING_ARROW = 60192,
+ SPELL_RAPID_FIRE = 3045,
+ SPELL_KILL_SHOT = 61006,
+ SPELL_MULTI_SHOT = 49048,
+ SPELL_VIPER_STING = 3034,
+
+ /* Hunter - Beast Mastery */
+ SPELL_BESTIAL_WRATH = 19574,
+ PASSIVE_BEAST_WITHIN = 34692,
+ PASSIVE_BEAST_MASTERY = 53270,
+
+ /* Hunter - Marksmanship */
+ SPELL_AIMED_SHOT = 19434,
+ PASSIVE_TRUESHOT_AURA = 19506,
+ SPELL_CHIMERA_SHOT = 53209,
+ SPELL_ARCANE_SHOT = 49045,
+ SPELL_STEADY_SHOT = 49052,
+ SPELL_READINESS = 23989,
+ SPELL_SILENCING_SHOT = 34490,
- /* Priest */
- SPELL_SOUL_WARDING = 63574,
- SPELL_SPIRIT_REDEMPTION = 20711,
- SPELL_SHADOWFORM = 15473,
+ /* Hunter - Survival */
+ PASSIVE_LOCK_AND_LOAD = 56344,
+ SPELL_WYVERN_STING = 19386,
+ SPELL_EXPLOSIVE_SHOT = 53301,
+ SPELL_BLACK_ARROW = 3674,
- /* Shaman */
- SPELL_TIDAL_FORCE = 582,
- SPELL_MANA_TIDE_TOTEM = 590,
- SPELL_SHA_NATURE_SWIFT = 591,
+ /* Rogue - Generic */
+ SPELL_DISMANTLE = 51722,
+ SPELL_EVASION = 26669,
+ SPELL_KICK = 1766,
+ SPELL_VANISH = 26889,
+ SPELL_BLIND = 2094,
+ SPELL_CLOAK_OF_SHADOWS = 31224,
+
+ /* Rogue - Assassination */
+ SPELL_COLD_BLOOD = 14177,
+ SPELL_MUTILATE = 1329,
+ SPELL_HUNGER_FOR_BLOOD = 51662,
+ SPELL_ENVENOM = 57993,
+
+ /* Rogue - Combat */
+ SPELL_SINISTER_STRIKE = 48637,
+ SPELL_BLADE_FLURRY = 13877,
+ SPELL_ADRENALINE_RUSH = 13750,
+ SPELL_KILLING_SPREE = 51690,
+ SPELL_EVISCERATE = 48668,
+
+ /* Rogue - Sublety */
+ SPELL_HEMORRHAGE = 16511,
+ SPELL_PREMEDITATION = 14183,
+ SPELL_SHADOW_DANCE = 51713,
+ SPELL_PREPARATION = 14185,
+ SPELL_SHADOWSTEP = 36554,
+
+ /* Priest - Generic */
+ SPELL_FEAR_WARD = 6346,
+ SPELL_POWER_WORD_FORT = 48161,
+ SPELL_DIVINE_SPIRIT = 48073,
+ SPELL_SHADOW_PROTECTION = 48169,
+ SPELL_DIVINE_HYMN = 64843,
+ SPELL_HYMN_OF_HOPE = 64901,
+ SPELL_SHADOW_WORD_DEATH = 48158,
+ SPELL_PSYCHIC_SCREAM = 10890,
+
+ /* Priest - Discipline */
+ PASSIVE_SOUL_WARDING = 63574,
+ SPELL_POWER_INFUSION = 10060,
+ SPELL_PENANCE = 47540,
+ SPELL_PAIN_SUPPRESSION = 33206,
+ SPELL_INNER_FOCUS = 14751,
+ SPELL_POWER_WORD_SHIELD = 48066,
+
+ /* Priest - Holy */
+ PASSIVE_SPIRIT_REDEMPTION = 20711,
+ SPELL_DESPERATE_PRAYER = 19236,
+ SPELL_GUARDIAN_SPIRIT = 47788,
+ SPELL_FLASH_HEAL = 48071,
+ SPELL_RENEW = 48068,
+
+ /* Priest - Shadow */
+ SPELL_VAMPIRIC_EMBRACE = 15286,
+ SPELL_SHADOWFORM = 15473,
+ SPELL_VAMPIRIC_TOUCH = 34914,
+ SPELL_MIND_FLAY = 15407,
+ SPELL_MIND_BLAST = 48127,
+ SPELL_SHADOW_WORD_PAIN = 48125,
+ SPELL_DEVOURING_PLAGUE = 48300,
+ SPELL_DISPERSION = 47585,
+
+ /* Death Knight - Generic */
+ SPELL_DEATH_GRIP = 49576,
+ SPELL_STRANGULATE = 47476,
+ SPELL_EMPOWER_RUNE_WEAP = 47568,
+ SPELL_ICEBORN_FORTITUDE = 48792,
+ SPELL_ANTI_MAGIC_SHELL = 48707,
+ SPELL_DEATH_COIL_DK = 49895,
+ SPELL_MIND_FREEZE = 47528,
+ SPELL_ICY_TOUCH = 49909,
+ AURA_FROST_FEVER = 55095,
+ SPELL_PLAGUE_STRIKE = 49921,
+ AURA_BLOOD_PLAGUE = 55078,
+ SPELL_PESTILENCE = 50842,
+
+ /* Death Knight - Blood */
+ SPELL_RUNE_TAP = 48982,
+ SPELL_HYSTERIA = 49016,
+ SPELL_HEART_STRIKE = 55050,
+ SPELL_DEATH_STRIKE = 49924,
+ SPELL_BLOOD_STRIKE = 49930,
+ SPELL_MARK_OF_BLOOD = 49005,
+ SPELL_VAMPIRIC_BLOOD = 55233,
+
+ /* Death Knight - Frost */
+ PASSIVE_ICY_TALONS = 50887,
+ SPELL_FROST_STRIKE = 49143,
+ SPELL_HOWLING_BLAST = 49184,
+ SPELL_UNBREAKABLE_ARMOR = 51271,
+ SPELL_OBLITERATE = 51425,
+ SPELL_DEATHCHILL = 49796,
+
+ /* Death Knight - Unholy */
+ PASSIVE_UNHOLY_BLIGHT = 49194,
+ PASSIVE_MASTER_OF_GHOUL = 52143,
+ SPELL_SCOURGE_STRIKE = 55090,
+ SPELL_DEATH_AND_DECAY = 49938,
+ SPELL_ANTI_MAGIC_ZONE = 51052,
+ SPELL_SUMMON_GARGOYLE = 49206,
+
+ /* Shaman - Generic */
+ SPELL_HEROISM = 32182,
+ SPELL_BLOODLUST = 2825,
+ SPELL_GROUNDING_TOTEM = 8177,
+
+ /* Shaman - Elemental*/
+ PASSIVE_ELEMENTAL_FOCUS = 16164,
+ SPELL_TOTEM_OF_WRATH = 30706,
+ SPELL_THUNDERSTORM = 51490,
+ SPELL_LIGHTNING_BOLT = 49238,
+ SPELL_EARTH_SHOCK = 49231,
+ SPELL_FLAME_SHOCK = 49233,
+ SPELL_LAVA_BURST = 60043,
+ SPELL_CHAIN_LIGHTNING = 49271,
+ SPELL_ELEMENTAL_MASTERY = 16166,
+
+ /* Shaman - Enhancement */
+ PASSIVE_SPIRIT_WEAPONS = 16268,
+ SPELL_LAVA_LASH = 60103,
+ SPELL_FERAL_SPIRIT = 51533,
+ AURA_MAELSTROM_WEAPON = 53817,
SPELL_STORMSTRIKE = 17364,
+ SPELL_SHAMANISTIC_RAGE = 30823,
+
+ /* Shaman - Restoration*/
+ SPELL_SHA_NATURE_SWIFT = 591,
+ SPELL_MANA_TIDE_TOTEM = 590,
+ SPELL_EARTH_SHIELD = 49284,
+ SPELL_RIPTIDE = 61295,
+ SPELL_HEALING_WAVE = 49273,
+ SPELL_LESSER_HEAL_WAVE = 49276,
+ SPELL_TIDAL_FORCE = 55198,
+
+ /* Mage - Generic */
+ SPELL_DAMPEN_MAGIC = 43015,
+ SPELL_EVOCATION = 12051,
+ SPELL_MANA_SHIELD = 43020,
+ SPELL_MIRROR_IMAGE = 55342,
+ SPELL_SPELLSTEAL = 30449,
+ SPELL_COUNTERSPELL = 2139,
+ SPELL_ICE_BLOCK = 45438,
+
+ /* Mage - Arcane */
+ SPELL_FOCUS_MAGIC = 54646,
+ SPELL_ARCANE_POWER = 12042,
+ SPELL_ARCANE_BARRAGE = 44425,
+ SPELL_ARCANE_BLAST = 42897,
+ AURA_ARCANE_BLAST = 36032,
+ SPELL_ARCANE_MISSILES = 42846,
+ SPELL_PRESENCE_OF_MIND = 12043,
+
+ /* Mage - Fire */
+ SPELL_PYROBLAST = 11366,
+ SPELL_COMBUSTION = 11129,
+ SPELL_LIVING_BOMB = 44457,
+ SPELL_FIREBALL = 42833,
+ SPELL_FIRE_BLAST = 42873,
+ SPELL_DRAGONS_BREATH = 31661,
+ SPELL_BLAST_WAVE = 11113,
+
+ /* Mage - Frost */
+ SPELL_ICY_VEINS = 12472,
+ SPELL_ICE_BARRIER = 11426,
+ SPELL_DEEP_FREEZE = 44572,
+ SPELL_FROST_NOVA = 42917,
+ SPELL_FROSTBOLT = 42842,
+ SPELL_COLD_SNAP = 11958,
+ SPELL_ICE_LANCE = 42914,
+
+ /* Warlock - Generic */
+ SPELL_FEAR = 6215,
+ SPELL_HOWL_OF_TERROR = 17928,
+ SPELL_CORRUPTION = 47813,
+ SPELL_DEATH_COIL_W = 47860,
+ SPELL_SHADOW_BOLT = 47809,
+ SPELL_INCINERATE = 47838,
+ SPELL_IMMOLATE = 47811,
+ SPELL_SEED_OF_CORRUPTION = 47836,
+
+ /* Warlock - Affliction */
+ PASSIVE_SIPHON_LIFE = 63108,
+ SPELL_UNSTABLE_AFFLICTION = 30108,
+ SPELL_HAUNT = 48181,
+ SPELL_CURSE_OF_AGONY = 47864,
+ SPELL_DRAIN_SOUL = 47855,
- /* Druid */
- SPELL_MOONKIN_FORM = 24858,
- SPELL_SWIFTMEND = 18562,
- SPELL_DRU_NATURE_SWIFT = 17116,
- SPELL_TREE_OF_LIFE = 33891
+ /* Warlock - Demonology */
+ SPELL_SOUL_LINK = 19028,
+ SPELL_DEMONIC_EMPOWERMENT = 47193,
+ SPELL_METAMORPHOSIS = 59672,
+ SPELL_IMMOLATION_AURA = 50589,
+ SPELL_DEMON_CHARGE = 54785,
+ AURA_DECIMATION = 63167,
+ AURA_MOLTEN_CORE = 71165,
+ SPELL_SOUL_FIRE = 47825,
+
+ /* Warlock - Destruction */
+ SPELL_SHADOWBURN = 17877,
+ SPELL_CONFLAGRATE = 17962,
+ SPELL_CHAOS_BOLT = 50796,
+ SPELL_SHADOWFURY = 47847,
+
+ /* Druid - Generic */
+ SPELL_BARKSKIN = 22812,
+ SPELL_INNERVATE = 29166,
+
+ /* Druid - Balance */
+ SPELL_INSECT_SWARM = 5570,
+ SPELL_MOONKIN_FORM = 24858,
+ SPELL_STARFALL = 48505,
+ SPELL_TYPHOON = 61384,
+ AURA_ECLIPSE_LUNAR = 48518,
+ SPELL_MOONFIRE = 48463,
+ SPELL_STARFIRE = 48465,
+ SPELL_WRATH = 48461,
+
+ /* Druid - Feral */
+ SPELL_CAT_FORM = 768,
+ SPELL_SURVIVAL_INSTINCTS = 61336,
+ SPELL_MANGLE = 33917,
+ SPELL_BERSERK = 50334,
+ SPELL_MANGLE_CAT = 48566,
+ SPELL_FERAL_CHARGE_CAT = 49376,
+ SPELL_RAKE = 48574,
+ SPELL_RIP = 49800,
+ SPELL_SAVAGE_ROAR = 52610,
+ SPELL_TIGER_FURY = 50213,
+ SPELL_CLAW = 48570,
+ SPELL_DASH = 33357,
+ SPELL_MAIM = 49802,
+
+ /* Druid - Restoration */
+ SPELL_SWIFTMEND = 18562,
+ SPELL_TREE_OF_LIFE = 33891,
+ SPELL_WILD_GROWTH = 48438,
+ SPELL_NATURE_SWIFTNESS = 17116,
+ SPELL_TRANQUILITY = 48447,
+ SPELL_NOURISH = 50464,
+ SPELL_HEALING_TOUCH = 48378,
+ SPELL_REJUVENATION = 48441,
+ SPELL_REGROWTH = 48443,
+ SPELL_LIFEBLOOM = 48451
};
+// As it turns out, finding out "how many points does the player have in spec X" is actually really expensive to do frequently
+// So instead, we just check for a handful of spells that, realistically, no spec is gonna go without (and "has spell" is cheap!)
+// Can players deliberately trick this check? Yes.
+// Is it worth doing? No.
+// Close enough.
+static const uint32 SPEC_ICONICS[MAX_CLASSES][NUM_TALENT_TREES][NUM_SPEC_ICONICS] = {
+ { // CLASS_NONE
+ {0,0,0},
+ {0,0,0},
+ {0,0,0}
+ },
+ { // CLASS_WARRIOR
+ {SPELL_BLADESTORM, SPELL_MORTAL_STRIKE, SPELL_SWEEPING_STRIKES}, // Arms
+ {PASSIVE_TITANS_GRIP, SPELL_BLOODTHIRST, SPELL_DEATH_WISH}, // Fury
+ {SPELL_SHOCKWAVE, SPELL_DEVASTATE, SPELL_VIGILANCE} // Protection
+ },
+ { // CLASS_PALADIN
+ {SPELL_BEACON_OF_LIGHT, SPELL_HOLY_SHOCK, PASSIVE_ILLUMINATION}, // Holy
+ {SPELL_HAMMER_OF_RIGHTEOUS, SPELL_HOLY_SHIELD, SPELL_BLESS_OF_SANC}, // Protection
+ {SPELL_DIVINE_STORM, SPELL_CRUSADER_STRIKE, SPELL_SEAL_OF_COMMAND} // Retribution
+ },
+ { // CLASS_HUNTER
+ {PASSIVE_BEAST_MASTERY, PASSIVE_BEAST_WITHIN, SPELL_BESTIAL_WRATH}, // Beast Mastery
+ {SPELL_CHIMERA_SHOT, PASSIVE_TRUESHOT_AURA, SPELL_AIMED_SHOT}, // Marksmanship
+ {SPELL_EXPLOSIVE_SHOT, SPELL_WYVERN_STING, PASSIVE_LOCK_AND_LOAD} // Survival
+ },
+ { // CLASS_ROGUE
+ {SPELL_HUNGER_FOR_BLOOD, SPELL_MUTILATE, SPELL_COLD_BLOOD}, // Assassination
+ {SPELL_KILLING_SPREE, SPELL_ADRENALINE_RUSH, SPELL_BLADE_FLURRY}, // Combat
+ {SPELL_SHADOW_DANCE, SPELL_PREMEDITATION, SPELL_HEMORRHAGE} // Sublety
+ },
+ { // CLASS_PRIEST
+ {SPELL_PENANCE, SPELL_POWER_INFUSION, PASSIVE_SOUL_WARDING}, // Discipline
+ {SPELL_GUARDIAN_SPIRIT, PASSIVE_SPIRIT_REDEMPTION, SPELL_DESPERATE_PRAYER}, // Holy
+ {SPELL_VAMPIRIC_TOUCH, SPELL_SHADOWFORM, SPELL_VAMPIRIC_EMBRACE} // Shadow
+ },
+ { // CLASS_DEATH_KNIGHT
+ {SPELL_HEART_STRIKE, SPELL_HYSTERIA, SPELL_RUNE_TAP}, // Blood
+ {SPELL_HOWLING_BLAST, SPELL_FROST_STRIKE, PASSIVE_ICY_TALONS}, // Frost
+ {SPELL_SCOURGE_STRIKE, PASSIVE_MASTER_OF_GHOUL, PASSIVE_UNHOLY_BLIGHT} // Unholy
+ },
+ { // CLASS_SHAMAN
+ {SPELL_THUNDERSTORM, SPELL_TOTEM_OF_WRATH, PASSIVE_ELEMENTAL_FOCUS}, // Elemental
+ {SPELL_FERAL_SPIRIT, SPELL_LAVA_LASH, PASSIVE_SPIRIT_WEAPONS}, // Enhancement
+ {SPELL_RIPTIDE, SPELL_MANA_TIDE_TOTEM, SPELL_SHA_NATURE_SWIFT} // Restoration
+ },
+ { // CLASS_MAGE
+ {SPELL_ARCANE_BARRAGE, SPELL_ARCANE_POWER, SPELL_FOCUS_MAGIC}, // Arcane
+ {SPELL_LIVING_BOMB, SPELL_COMBUSTION, SPELL_PYROBLAST}, // Fire
+ {SPELL_DEEP_FREEZE, SPELL_ICE_BARRIER, SPELL_ICY_VEINS} // Frost
+ },
+ { // CLASS_WARLOCK
+ {SPELL_HAUNT, SPELL_UNSTABLE_AFFLICTION, PASSIVE_SIPHON_LIFE}, // Affliction
+ {SPELL_METAMORPHOSIS, SPELL_DEMONIC_EMPOWERMENT, SPELL_SOUL_LINK}, // Demonology
+ {SPELL_CHAOS_BOLT, SPELL_CONFLAGRATE, SPELL_SHADOWBURN} // Destruction
+ },
+ { // CLASS_UNK
+ {0,0,0},
+ {0,0,0},
+ {0,0,0}
+ },
+ { // CLASS_DRUID
+ {SPELL_STARFALL, SPELL_MOONKIN_FORM, SPELL_INSECT_SWARM}, // Balance
+ {SPELL_BERSERK, SPELL_MANGLE, SPELL_SURVIVAL_INSTINCTS}, // Feral
+ {SPELL_WILD_GROWTH, SPELL_TREE_OF_LIFE, SPELL_SWIFTMEND} // Restoration
+ }
+};
+
+uint8 PlayerAI::GetPlayerSpec(Player const* who)
+{
+ if (!who)
+ return 0;
+
+ uint8 wClass = who->getClass();
+ for (uint8 tier = 0; tier < NUM_SPEC_ICONICS; ++tier)
+ for (uint8 tree = 0; tree < NUM_TALENT_TREES; ++tree)
+ if (SPEC_ICONICS[wClass][tree][tier] && who->HasSpell(SPEC_ICONICS[wClass][tree][tier]))
+ return tree;
+
+ return 0;
+}
+
bool PlayerAI::IsPlayerHealer(Player const* who)
{
+ if (!who)
+ return false;
+
switch (who->getClass())
{
case CLASS_WARRIOR:
@@ -61,18 +526,21 @@ bool PlayerAI::IsPlayerHealer(Player const* who)
default:
return false;
case CLASS_PALADIN:
- return who->HasSpell(PASSIVE_ILLUMINATION);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_PALADIN_HOLY);
case CLASS_PRIEST:
- return who->HasSpell(SPELL_SOUL_WARDING) || who->HasSpell(SPELL_SPIRIT_REDEMPTION);
+ return (PlayerAI::GetPlayerSpec(who) != SPEC_PRIEST_SHADOW);
case CLASS_SHAMAN:
- return who->HasSpell(SPELL_MANA_TIDE_TOTEM) || who->HasSpell(SPELL_SHA_NATURE_SWIFT) || who->HasSpell(SPELL_TIDAL_FORCE);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_RESTORATION);
case CLASS_DRUID:
- return who->HasSpell(SPELL_SWIFTMEND) || who->HasSpell(SPELL_DRU_NATURE_SWIFT) || who->HasSpell(SPELL_TREE_OF_LIFE);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_RESTORATION);
}
}
bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
{
+ if (!who)
+ return false;
+
switch (who->getClass())
{
case CLASS_WARRIOR:
@@ -94,12 +562,106 @@ bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
return false;
}
case CLASS_PRIEST:
- return who->HasSpell(SPELL_SHADOWFORM);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_PRIEST_SHADOW);
case CLASS_SHAMAN:
- return !who->HasSpell(SPELL_STORMSTRIKE);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_ELEMENTAL);
case CLASS_DRUID:
- return who->HasSpell(SPELL_MOONKIN_FORM);
+ return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_BALANCE);
+ }
+}
+
+PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, Unit* target)
+{
+ // Find highest spell rank that we know
+ uint32 knownRank, nextRank;
+ if (me->HasSpell(spellId))
+ {
+ // this will save us some lookups if the player has the highest rank (expected case)
+ knownRank = spellId;
+ nextRank = sSpellMgr->GetNextSpellInChain(spellId);
+ }
+ else
+ {
+ knownRank = 0;
+ nextRank = sSpellMgr->GetFirstSpellInChain(spellId);
+ }
+
+ while (nextRank && me->HasSpell(nextRank))
+ {
+ knownRank = nextRank;
+ nextRank = sSpellMgr->GetNextSpellInChain(knownRank);
+ }
+
+ if (!knownRank)
+ return {};
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(knownRank);
+ if (!spellInfo)
+ return {};
+
+ if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo))
+ return {};
+
+ Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE);
+ if (spell->CanAutoCast(target))
+ return{ spell, target };
+
+ delete spell;
+ return {};
+}
+
+PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, SpellTarget target)
+{
+ Unit* pTarget = nullptr;
+ switch (target)
+ {
+ case TARGET_NONE:
+ break;
+ case TARGET_VICTIM:
+ pTarget = me->GetVictim();
+ if (!pTarget)
+ return {};
+ break;
+ case TARGET_CHARMER:
+ pTarget = me->GetCharmer();
+ if (!pTarget)
+ return {};
+ break;
+ case TARGET_SELF:
+ pTarget = me;
+ break;
+ }
+
+ return VerifySpellCast(spellId, pTarget);
+}
+
+PlayerAI::TargetedSpell PlayerAI::SelectSpellCast(PossibleSpellVector& spells)
+{
+ uint32 totalWeights = 0;
+ for (PossibleSpell const& wSpell : spells)
+ totalWeights += wSpell.second;
+
+ TargetedSpell selected;
+ uint32 randNum = urand(0, totalWeights - 1);
+ for (PossibleSpell const& wSpell : spells)
+ {
+ if (selected)
+ {
+ delete wSpell.first.first;
+ continue;
+ }
+
+ if (randNum < wSpell.second)
+ selected = wSpell.first;
+ else
+ {
+ randNum -= wSpell.second;
+ delete wSpell.first.first;
+ }
}
+
+ spells.clear();
+ return selected;
}
void PlayerAI::DoRangedAttackIfReady()
@@ -150,6 +712,29 @@ void PlayerAI::DoAutoAttackIfReady()
DoMeleeAttackIfReady();
}
+void PlayerAI::CancelAllShapeshifts()
+{
+ std::list<AuraEffect*> const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT);
+ std::set<Aura*> removableShapeshifts;
+ for (AuraEffect* auraEff : shapeshiftAuras)
+ {
+ Aura* aura = auraEff->GetBase();
+ if (!aura)
+ continue;
+ SpellInfo const* auraInfo = aura->GetSpellInfo();
+ if (!auraInfo)
+ continue;
+ if (auraInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL))
+ continue;
+ if (!auraInfo->IsPositive() || auraInfo->IsPassive())
+ continue;
+ removableShapeshifts.insert(aura);
+ }
+
+ for (Aura* aura : removableShapeshifts)
+ me->RemoveOwnedAura(aura, AURA_REMOVE_BY_CANCEL);
+}
+
struct UncontrolledTargetSelectPredicate : public std::unary_function<Unit*, bool>
{
bool operator()(Unit const* target) const
@@ -164,7 +749,516 @@ Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const
return nullptr;
}
-void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
+PlayerAI::TargetedSpell SimpleCharmedPlayerAI::SelectAppropriateCastForSpec()
+{
+ PossibleSpellVector spells;
+
+ switch (me->getClass())
+ {
+ case CLASS_WARRIOR:
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_CHARGE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_INTERCEPT, TARGET_VICTIM, 10);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_ENRAGED_REGEN, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INTIMIDATING_SHOUT, TARGET_VICTIM, 4);
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_PUMMEL, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_BASH, TARGET_VICTIM, 15);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_BLOODRAGE, TARGET_NONE, 5);
+ switch (GetSpec())
+ {
+ case SPEC_WARRIOR_PROTECTION:
+ VerifyAndPushSpellCast(spells, SPELL_SHOCKWAVE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_CONCUSSION_BLOW, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DISARM, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LAST_STAND, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_BLOCK, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_SLAM, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_WALL, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SPELL_REFLECTION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DEVASTATE, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 1);
+ break;
+ case SPEC_WARRIOR_ARMS:
+ VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_MORTAL_STRIKE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_BLADESTORM, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_RETALIATION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SHATTERING_THROW, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15);
+ break;
+ case SPEC_WARRIOR_FURY:
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_WISH, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_BLOODTHIRST, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_HEROIC_FURY, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_RECKLESSNESS, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_PIERCING_HOWL, TARGET_VICTIM, 2);
+ break;
+ }
+ break;
+ case CLASS_PALADIN:
+ VerifyAndPushSpellCast(spells, SPELL_AURA_MASTERY, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_LAY_ON_HANDS, TARGET_CHARMER, 8);
+ VerifyAndPushSpellCast(spells, SPELL_BLESSING_OF_MIGHT, TARGET_CHARMER, 8);
+ VerifyAndPushSpellCast(spells, SPELL_AVENGING_WRATH, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_PROTECTION, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SHIELD, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_JUSTICE, TARGET_VICTIM, 6);
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_FREEDOM, TARGET_SELF, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, TARGET_SELF, 1);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ {
+ if (creatureCharmer->IsDungeonBoss() || creatureCharmer->isWorldBoss())
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_SACRIFICE, creatureCharmer, 10);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, creatureCharmer, 3);
+ }
+
+ switch (GetSpec())
+ {
+ case SPEC_PALADIN_PROTECTION:
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_RIGHTEOUS, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SACRIFICE, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_OF_RIGHTEOUS, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CONSECRATION, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHIELD, TARGET_NONE, 1);
+ break;
+ case SPEC_PALADIN_HOLY:
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_OF_LIGHT, TARGET_CHARMER, 4);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_LIGHT, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_FAVOR, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_ILLUMINATION, TARGET_NONE, 3);
+ break;
+ case SPEC_PALADIN_RETRIBUTION:
+ VerifyAndPushSpellCast(spells, SPELL_CRUSADER_STRIKE, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_STORM, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_WRATH, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_RIGHTEOUS_FURY, TARGET_NONE, 2);
+ break;
+ }
+ break;
+ case CLASS_HUNTER:
+ VerifyAndPushSpellCast(spells, SPELL_DETERRENCE, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_TRAP, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_FREEZING_ARROW, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_RAPID_FIRE, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_KILL_SHOT, TARGET_VICTIM, 10);
+ if (me->GetVictim() && me->GetVictim()->getPowerType() == POWER_MANA && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_VIPER_STING, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_VIPER_STING, TARGET_VICTIM, 5);
+
+ switch (GetSpec())
+ {
+ case SPEC_HUNTER_BEAST_MASTERY:
+ VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 2);
+ break;
+ case SPEC_HUNTER_MARKSMANSHIP:
+ VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CHIMERA_SHOT, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_READINESS, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_SILENCING_SHOT, TARGET_VICTIM, 5);
+ break;
+ case SPEC_HUNTER_SURVIVAL:
+ VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_SHOT, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_BLACK_ARROW, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 1);
+ break;
+ }
+ break;
+ case CLASS_ROGUE:
+ {
+ VerifyAndPushSpellCast(spells, SPELL_DISMANTLE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_EVASION, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_VANISH, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_BLIND, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CLOAK_OF_SHADOWS, TARGET_NONE, 2);
+
+ uint32 builder, finisher;
+ switch (GetSpec())
+ {
+ case SPEC_ROGUE_ASSASSINATION:
+ builder = SPELL_MUTILATE, finisher = SPELL_ENVENOM;
+ VerifyAndPushSpellCast(spells, SPELL_COLD_BLOOD, TARGET_NONE, 20);
+ break;
+ case SPEC_ROGUE_COMBAT:
+ builder = SPELL_SINISTER_STRIKE, finisher = SPELL_EVISCERATE;
+ VerifyAndPushSpellCast(spells, SPELL_ADRENALINE_RUSH, TARGET_NONE, 6);
+ VerifyAndPushSpellCast(spells, SPELL_BLADE_FLURRY, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_KILLING_SPREE, TARGET_NONE, 25);
+ break;
+ case SPEC_ROGUE_SUBLETY:
+ builder = SPELL_HEMORRHAGE, finisher = SPELL_EVISCERATE;
+ VerifyAndPushSpellCast(spells, SPELL_PREPARATION, TARGET_NONE, 10);
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWSTEP, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_DANCE, TARGET_NONE, 10);
+ break;
+ }
+
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_KICK, TARGET_VICTIM, 25);
+
+ uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0;
+ if (cp >= 4)
+ VerifyAndPushSpellCast(spells, finisher, TARGET_VICTIM, 10);
+ if (cp <= 4)
+ VerifyAndPushSpellCast(spells, builder, TARGET_VICTIM, 5);
+ }
+ break;
+ }
+ case CLASS_PRIEST:
+ VerifyAndPushSpellCast(spells, SPELL_FEAR_WARD, TARGET_SELF, 2);
+ VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_FORT, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SPIRIT, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_PROTECTION, TARGET_CHARMER, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_HYMN, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_HYMN_OF_HOPE, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_DEATH, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_PSYCHIC_SCREAM, TARGET_VICTIM, 3);
+ switch (GetSpec())
+ {
+ case SPEC_PRIEST_DISCIPLINE:
+ VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_SHIELD, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INNER_FOCUS, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_PAIN_SUPPRESSION, TARGET_CHARMER, 15);
+ VerifyAndPushSpellCast(spells, SPELL_POWER_INFUSION, TARGET_CHARMER, 10);
+ VerifyAndPushSpellCast(spells, SPELL_PENANCE, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1);
+ break;
+ case SPEC_PRIEST_HOLY:
+ VerifyAndPushSpellCast(spells, SPELL_DESPERATE_PRAYER, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_GUARDIAN_SPIRIT, TARGET_CHARMER, 5);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_RENEW, TARGET_CHARMER, 3);
+ break;
+ case SPEC_PRIEST_SHADOW:
+ if (!me->HasAura(SPELL_SHADOWFORM))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWFORM, TARGET_NONE, 100);
+ break;
+ }
+ if (Unit* victim = me->GetVictim())
+ {
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_VAMPIRIC_TOUCH, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_TOUCH, TARGET_VICTIM, 4);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_SHADOW_WORD_PAIN, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_PAIN, TARGET_VICTIM, 3);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_DEVOURING_PLAGUE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_DEVOURING_PLAGUE, TARGET_VICTIM, 4);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_MIND_BLAST, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_MIND_FLAY, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DISPERSION, TARGET_NONE, 10);
+ break;
+ }
+ break;
+ case CLASS_DEATH_KNIGHT:
+ {
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_GRIP, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_STRANGULATE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_EMPOWER_RUNE_WEAP, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ICEBORN_FORTITUDE, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_SHELL, TARGET_NONE, 10);
+
+ bool hasFF = false, hasBP = false;
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_MIND_FREEZE, TARGET_VICTIM, 25);
+
+ hasFF = !!victim->GetAuraApplicationOfRankedSpell(AURA_FROST_FEVER, me->GetGUID()), hasBP = !!victim->GetAuraApplicationOfRankedSpell(AURA_BLOOD_PLAGUE, me->GetGUID());
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_PESTILENCE, TARGET_VICTIM, 3);
+ if (!hasFF)
+ VerifyAndPushSpellCast(spells, SPELL_ICY_TOUCH, TARGET_VICTIM, 4);
+ if (!hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_PLAGUE_STRIKE, TARGET_VICTIM, 4);
+ }
+ switch (GetSpec())
+ {
+ case SPEC_DEATH_KNIGHT_BLOOD:
+ VerifyAndPushSpellCast(spells, SPELL_RUNE_TAP, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, TARGET_SELF, 5);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ if (!creatureCharmer->IsDungeonBoss() && !creatureCharmer->isWorldBoss())
+ VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, creatureCharmer, 15);
+ VerifyAndPushSpellCast(spells, SPELL_HEART_STRIKE, TARGET_VICTIM, 2);
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_STRIKE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_MARK_OF_BLOOD, TARGET_VICTIM, 20);
+ VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_BLOOD, TARGET_NONE, 10);
+ break;
+ case SPEC_DEATH_KNIGHT_FROST:
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_OBLITERATE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_HOWLING_BLAST, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_UNBREAKABLE_ARMOR, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_DEATHCHILL, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_FROST_STRIKE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1);
+ break;
+ case SPEC_DEATH_KNIGHT_UNHOLY:
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_SCOURGE_STRIKE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_AND_DECAY, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_ZONE, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_SUMMON_GARGOYLE, TARGET_VICTIM, 7);
+ VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 3);
+ break;
+ }
+ break;
+ }
+ case CLASS_SHAMAN:
+ VerifyAndPushSpellCast(spells, SPELL_HEROISM, TARGET_NONE, 25);
+ VerifyAndPushSpellCast(spells, SPELL_BLOODLUST, TARGET_NONE, 25);
+ VerifyAndPushSpellCast(spells, SPELL_GROUNDING_TOTEM, TARGET_NONE, 2);
+ switch (GetSpec())
+ {
+ case SPEC_SHAMAN_RESTORATION:
+ if (Unit* charmer = me->GetCharmer())
+ if (!charmer->GetAuraApplicationOfRankedSpell(SPELL_EARTH_SHIELD, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_EARTH_SHIELD, charmer, 2);
+ if (me->HasAura(SPELL_SHA_NATURE_SWIFT))
+ VerifyAndPushSpellCast(spells, SPELL_HEALING_WAVE, TARGET_CHARMER, 20);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_LESSER_HEAL_WAVE, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_TIDAL_FORCE, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_SHA_NATURE_SWIFT, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_MANA_TIDE_TOTEM, TARGET_NONE, 3);
+ break;
+ case SPEC_SHAMAN_ELEMENTAL:
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->GetAuraOfRankedSpell(SPELL_FLAME_SHOCK, GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_LAVA_BURST, TARGET_VICTIM, 5);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_FLAME_SHOCK, TARGET_VICTIM, 3);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_CHAIN_LIGHTNING, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ELEMENTAL_MASTERY, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDERSTORM, TARGET_NONE, 3);
+ break;
+ case SPEC_SHAMAN_ENHANCEMENT:
+ if (Aura const* maelstrom = me->GetAura(AURA_MAELSTROM_WEAPON))
+ if (maelstrom->GetStackAmount() == 5)
+ VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_STORMSTRIKE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_EARTH_SHOCK, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LAVA_LASH, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHAMANISTIC_RAGE, TARGET_NONE, 10);
+ break;
+ }
+ break;
+ case CLASS_MAGE:
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_COUNTERSPELL, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_DAMPEN_MAGIC, TARGET_CHARMER, 2);
+ VerifyAndPushSpellCast(spells, SPELL_EVOCATION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_MANA_SHIELD, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_MIRROR_IMAGE, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SPELLSTEAL, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ICE_BLOCK, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ICY_VEINS, TARGET_NONE, 3);
+ switch (GetSpec())
+ {
+ case SPEC_MAGE_ARCANE:
+ if (Aura* abAura = me->GetAura(AURA_ARCANE_BLAST))
+ if (abAura->GetStackAmount() >= 3)
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_MISSILES, TARGET_VICTIM, 7);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_BLAST, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_BARRAGE, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_POWER, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_PRESENCE_OF_MIND, TARGET_NONE, 7);
+ break;
+ case SPEC_MAGE_FIRE:
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_LIVING_BOMB))
+ VerifyAndPushSpellCast(spells, SPELL_LIVING_BOMB, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_COMBUSTION, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FIREBALL, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_FIRE_BLAST, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DRAGONS_BREATH, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_BLAST_WAVE, TARGET_VICTIM, 1);
+ break;
+ case SPEC_MAGE_FROST:
+ VerifyAndPushSpellCast(spells, SPELL_DEEP_FREEZE, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_FROST_NOVA, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FROSTBOLT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_COLD_SNAP, TARGET_VICTIM, 5);
+ if (me->GetVictim() && me->GetVictim()->HasAuraState(AURA_STATE_FROZEN, nullptr, me))
+ VerifyAndPushSpellCast(spells, SPELL_ICE_LANCE, TARGET_VICTIM, 5);
+ break;
+ }
+ break;
+ case CLASS_WARLOCK:
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_W, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_FEAR, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_SEED_OF_CORRUPTION, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_HOWL_OF_TERROR, TARGET_NONE, 2);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_CORRUPTION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CORRUPTION, TARGET_VICTIM, 10);
+ switch (GetSpec())
+ {
+ case SPEC_WARLOCK_AFFLICTION:
+ if (Unit* victim = me->GetVictim())
+ {
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_UNSTABLE_AFFLICTION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_UNSTABLE_AFFLICTION, TARGET_VICTIM, 8);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_HAUNT, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_HAUNT, TARGET_VICTIM, 8);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_CURSE_OF_AGONY, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CURSE_OF_AGONY, TARGET_VICTIM, 4);
+ if (victim->HealthBelowPct(25))
+ VerifyAndPushSpellCast(spells, SPELL_DRAIN_SOUL, TARGET_VICTIM, 100);
+ }
+ break;
+ case SPEC_WARLOCK_DEMONOLOGY:
+ VerifyAndPushSpellCast(spells, SPELL_METAMORPHOSIS, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7);
+ if (me->HasAura(AURA_DECIMATION))
+ VerifyAndPushSpellCast(spells, SPELL_SOUL_FIRE, TARGET_VICTIM, 100);
+ if (me->HasAura(SPELL_METAMORPHOSIS))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATION_AURA, TARGET_NONE, 30);
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_DEMON_CHARGE, TARGET_VICTIM, 20);
+ }
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 5);
+ if (me->HasAura(AURA_MOLTEN_CORE))
+ VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 10);
+ break;
+ case SPEC_WARLOCK_DESTRUCTION:
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 8);
+ if (me->GetVictim() && me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CONFLAGRATE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWFURY, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_CHAOS_BOLT, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWBURN, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 7);
+ break;
+ }
+ break;
+ case CLASS_DRUID:
+ VerifyAndPushSpellCast(spells, SPELL_INNERVATE, TARGET_CHARMER, 5);
+ VerifyAndPushSpellCast(spells, SPELL_BARKSKIN, TARGET_NONE, 5);
+ switch (GetSpec())
+ {
+ case SPEC_DRUID_RESTORATION:
+ if (!me->HasAura(SPELL_TREE_OF_LIFE))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_TREE_OF_LIFE, TARGET_NONE, 100);
+ break;
+ }
+ VerifyAndPushSpellCast(spells, SPELL_TRANQUILITY, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_NATURE_SWIFTNESS, TARGET_NONE, 7);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_NOURISH, creatureCharmer, 5);
+ VerifyAndPushSpellCast(spells, SPELL_WILD_GROWTH, creatureCharmer, 5);
+ if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_REJUVENATION, creatureCharmer, 8);
+ if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_REGROWTH, creatureCharmer, 8);
+ uint8 lifebloomStacks = 0;
+ if (Aura const* lifebloom = creatureCharmer->GetAura(SPELL_LIFEBLOOM, me->GetGUID()))
+ lifebloomStacks = lifebloom->GetStackAmount();
+ if (lifebloomStacks < 3)
+ VerifyAndPushSpellCast(spells, SPELL_LIFEBLOOM, creatureCharmer, 5);
+ if (creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION) ||
+ creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH))
+ VerifyAndPushSpellCast(spells, SPELL_SWIFTMEND, creatureCharmer, 10);
+ if (me->HasAura(SPELL_NATURE_SWIFTNESS))
+ VerifyAndPushSpellCast(spells, SPELL_HEALING_TOUCH, creatureCharmer, 100);
+ }
+ break;
+ case SPEC_DRUID_BALANCE:
+ {
+ if (!me->HasAura(SPELL_MOONKIN_FORM))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_MOONKIN_FORM, TARGET_NONE, 100);
+ break;
+ }
+ uint32 const mainAttackSpell = me->HasAura(AURA_ECLIPSE_LUNAR) ? SPELL_STARFIRE : SPELL_WRATH;
+ VerifyAndPushSpellCast(spells, SPELL_STARFALL, TARGET_NONE, 20);
+ VerifyAndPushSpellCast(spells, mainAttackSpell, TARGET_VICTIM, 10);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_INSECT_SWARM, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_INSECT_SWARM, TARGET_VICTIM, 7);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_MOONFIRE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_MOONFIRE, TARGET_VICTIM, 5);
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_TYPHOON, TARGET_NONE, 15);
+ break;
+ }
+ case SPEC_DRUID_FERAL:
+ if (!me->HasAura(SPELL_CAT_FORM))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_CAT_FORM, TARGET_NONE, 100);
+ break;
+ }
+ VerifyAndPushSpellCast(spells, SPELL_BERSERK, TARGET_NONE, 20);
+ VerifyAndPushSpellCast(spells, SPELL_SURVIVAL_INSTINCTS, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_TIGER_FURY, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_DASH, TARGET_NONE, 5);
+ if (Unit* victim = me->GetVictim())
+ {
+ uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0;
+ if (victim->HasUnitState(UNIT_STATE_CASTING) && cp >= 1)
+ VerifyAndPushSpellCast(spells, SPELL_MAIM, TARGET_VICTIM, 25);
+ if (!me->IsWithinMeleeRange(victim))
+ VerifyAndPushSpellCast(spells, SPELL_FERAL_CHARGE_CAT, TARGET_VICTIM, 25);
+ if (cp >= 4)
+ VerifyAndPushSpellCast(spells, SPELL_RIP, TARGET_VICTIM, 50);
+ if (cp <= 4)
+ {
+ VerifyAndPushSpellCast(spells, SPELL_MANGLE_CAT, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_CLAW, TARGET_VICTIM, 5);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_RAKE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_RAKE, TARGET_VICTIM, 8);
+ if (!me->HasAura(SPELL_SAVAGE_ROAR))
+ VerifyAndPushSpellCast(spells, SPELL_SAVAGE_ROAR, TARGET_NONE, 15);
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ return SelectSpellCast(spells);
+}
+
+static const float CASTER_CHASE_DISTANCE = 28.0f;
+void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff)
{
Creature* charmer = me->GetCharmer() ? me->GetCharmer()->ToCreature() : nullptr;
if (!charmer)
@@ -192,10 +1286,54 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
return;
if (IsRangedAttacker())
- AttackStartCaster(target, 28.0f);
+ {
+ _chaseCloser = !me->IsWithinLOSInMap(target);
+ if (_chaseCloser)
+ AttackStart(target);
+ else
+ AttackStartCaster(target, CASTER_CHASE_DISTANCE);
+ }
else
AttackStart(target);
+ _forceFacing = true;
}
+
+ if (me->IsStopped() && !me->HasUnitState(UNIT_STATE_CANNOT_TURN))
+ {
+ float targetAngle = me->GetAngle(target);
+ if (_forceFacing || fabs(me->GetOrientation() - targetAngle) > 0.4f)
+ {
+ me->SetFacingTo(targetAngle);
+ _forceFacing = false;
+ }
+ }
+
+ if (_castCheckTimer <= diff)
+ {
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ _castCheckTimer = 0;
+ else
+ {
+ if (IsRangedAttacker())
+ { // chase to zero if the target isn't in line of sight
+ bool inLOS = me->IsWithinLOSInMap(target);
+ if (_chaseCloser != !inLOS)
+ {
+ _chaseCloser = !inLOS;
+ if (_chaseCloser)
+ AttackStart(target);
+ else
+ AttackStartCaster(target, CASTER_CHASE_DISTANCE);
+ }
+ }
+ if (TargetedSpell shouldCast = SelectAppropriateCastForSpec())
+ DoCastAtTarget(shouldCast);
+ _castCheckTimer = 500;
+ }
+ }
+ else
+ _castCheckTimer -= diff;
+
DoAutoAttackIfReady();
}
else
@@ -214,6 +1352,9 @@ void SimpleCharmedPlayerAI::OnCharmed(bool apply)
{
me->CastStop();
me->AttackStop();
+ me->StopMoving();
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MovePoint(0, me->GetPosition(), false); // force re-sync of current position for all clients
}
else
{
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h
index b717816f9a3..18f65485161 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.h
+++ b/src/server/game/AI/PlayerAI/PlayerAI.h
@@ -20,41 +20,99 @@
#include "UnitAI.h"
#include "Player.h"
+#include "Spell.h"
#include "Creature.h"
class TC_GAME_API PlayerAI : public UnitAI
{
public:
- explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
+ explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _selfSpec(PlayerAI::GetPlayerSpec(player)), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
void OnCharmed(bool /*apply*/) override { } // charm AI application for players is handled by Unit::SetCharmedBy / Unit::RemoveCharmedBy
// helper functions to determine player info
+ // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned.
+ static uint8 GetPlayerSpec(Player const* who);
+ // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned.
+ uint8 GetSpec(Player const* who = nullptr) const { return (!who || who == me) ? _selfSpec : GetPlayerSpec(who); }
static bool IsPlayerHealer(Player const* who);
bool IsHealer(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who); }
static bool IsPlayerRangedAttacker(Player const* who);
bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); }
protected:
+ struct TargetedSpell : public std::pair<Spell*, Unit*>
+ {
+ TargetedSpell() : pair<Spell*, Unit*>() { }
+ TargetedSpell(Spell* first, Unit* second) : pair<Spell*, Unit*>(first, second) { }
+ explicit operator bool() { return !!first; }
+ };
+ typedef std::pair<TargetedSpell, uint32> PossibleSpell;
+ typedef std::vector<PossibleSpell> PossibleSpellVector;
+
Player* const me;
void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection
+ enum SpellTarget
+ {
+ TARGET_NONE,
+ TARGET_VICTIM,
+ TARGET_CHARMER,
+ TARGET_SELF
+ };
+ /* Check if the specified spell can be cast on that target.
+ Caller is responsible for cleaning up created Spell object from pointer. */
+ TargetedSpell VerifySpellCast(uint32 spellId, Unit* target);
+ /* Check if the specified spell can be cast on that target.
+ Caller is responsible for cleaning up created Spell object from pointer. */
+ TargetedSpell VerifySpellCast(uint32 spellId, SpellTarget target);
+
+ /* Helper method - checks spell cast, then pushes it onto provided vector if valid. */
+ template<typename T> inline void VerifyAndPushSpellCast(PossibleSpellVector& spells, uint32 spellId, T target, uint32 weight)
+ {
+ if (TargetedSpell spell = VerifySpellCast(spellId, target))
+ spells.push_back({ spell,weight });
+ }
+
+ /* Helper method - selects one spell from the vector and returns it, while deleting everything else.
+ This invalidates the vector, and empties it to prevent accidental misuse. */
+ TargetedSpell SelectSpellCast(PossibleSpellVector& spells);
+ /* Helper method - casts the included spell at the included target */
+ inline void DoCastAtTarget(TargetedSpell spell)
+ {
+ SpellCastTargets targets;
+ targets.SetUnitTarget(spell.second);
+ spell.first->prepare(&targets);
+ }
+
virtual Unit* SelectAttackTarget() const { return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; }
void DoRangedAttackIfReady();
void DoAutoAttackIfReady();
+ // Cancels all shapeshifts that the player could voluntarily cancel
+ void CancelAllShapeshifts();
+
private:
- bool _isSelfHealer;
+ uint8 const _selfSpec;
+ bool const _isSelfHealer;
bool _isSelfRangedAttacker;
};
class SimpleCharmedPlayerAI : public PlayerAI
{
public:
- SimpleCharmedPlayerAI(Player* player) : PlayerAI(player) { }
+ SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { }
void UpdateAI(uint32 diff) override;
void OnCharmed(bool apply) override;
+
+ protected:
Unit* SelectAttackTarget() const override;
+
+ private:
+ TargetedSpell SelectAppropriateCastForSpec();
+ uint32 _castCheckTimer;
+ bool _chaseCloser;
+ bool _forceFacing;
};
#endif
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index d2cbe482547..ec86e8fe4c3 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2794,6 +2794,8 @@ void Player::GiveLevel(uint8 level)
if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
UpdateSkillsToMaxSkillsForLevel();
+ _ApplyAllLevelScaleItemMods(true);
+
// set current level health and mana/energy to maximum after applying all mods.
SetFullHealth();
SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
@@ -2803,8 +2805,6 @@ void Player::GiveLevel(uint8 level)
SetPower(POWER_FOCUS, 0);
SetPower(POWER_HAPPINESS, 0);
- _ApplyAllLevelScaleItemMods(true);
-
// update level to hunter/summon pet
if (Pet* pet = GetPet())
pet->SynchronizeLevelWithOwner();
@@ -3202,10 +3202,9 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
}
}
- PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
PlayerTalent* newtalent = new PlayerTalent();
- newtalent->state = state;
+ newtalent->state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
newtalent->spec = spec;
(*m_talents[spec])[spellId] = newtalent;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 96bf207c274..2aa32374087 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -11653,8 +11653,11 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
&& (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)))
return false;
+ // Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction)
+ if (GetTypeId() == TYPEID_PLAYER && IsCharmed() && GetCharmerGUID().IsCreature())
+ return true;
// PvP case
- if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
+ else if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
{
Player const* targetPlayerOwner = target->GetAffectingPlayer();
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
@@ -13281,6 +13284,7 @@ void Unit::UpdateCharmAI()
if (!newAI) // otherwise, we default to the generic one
newAI = new SimpleCharmedPlayerAI(ToPlayer());
i_AI = newAI;
+ newAI->OnCharmed(true);
}
else
{
@@ -15574,7 +15578,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
{
// change AI to charmed AI on next Update tick
NeedChangeAI = true;
- IsAIEnabled = false;
+ if (IsAIEnabled)
+ {
+ IsAIEnabled = false;
+ player->AI()->OnCharmed(true);
+ }
}
player->SetClientControl(this, false);
}
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 883f051b700..5d96de1def9 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2914,7 +2914,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetSpellModTakingSpell(this, true);
- // Fill cost data (not use power for item casts
+ // Fill cost data (do not use power for item casts)
m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask);
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
@@ -2992,7 +2992,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
// don't allow channeled spells / spells with cast time to be cast while moving
// exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
- if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)
+ if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)
{
// 1. Is a channel spell, 2. Has no casttime, 3. And has flag to allow movement during channel
if (!(m_spellInfo->IsChanneled() && !m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING)))
@@ -3546,7 +3546,13 @@ void Spell::update(uint32 difftime)
{
// don't cancel for melee, autorepeat, triggered and instant spells
if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING)))
- cancel();
+ {
+ // if charmed by creature, trust the AI not to cheat and allow the cast to proceed
+ // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now
+ // however, checking what type of movement the spline is for every single spline would be really expensive
+ if (!m_caster->GetCharmerGUID().IsCreature())
+ cancel();
+ }
}
switch (m_spellState)
@@ -4842,7 +4848,7 @@ SpellCastResult Spell::CheckCast(bool strict)
// cancel autorepeat spells if cast start when moving
// (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving())
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()))
{
// skip stuck spell to allow use it in falling case and apply spell limitations at movement
if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) &&
@@ -5603,7 +5609,14 @@ SpellCastResult Spell::CheckPetCast(Unit* target)
m_targets.SetUnitTarget(target);
}
- // cooldown
+ // check power requirement
+ // this would be zero until ::prepare normally, we set it here (it gets reset in ::prepare)
+ m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask);
+ SpellCastResult failReason = CheckPower();
+ if (failReason != SPELL_CAST_OK)
+ return failReason;
+
+ // check cooldown
if (Creature* creatureCaster = m_caster->ToCreature())
if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo))
return SPELL_FAILED_NOT_READY;
@@ -5746,6 +5759,9 @@ SpellCastResult Spell::CheckCasterAuras() const
bool Spell::CanAutoCast(Unit* target)
{
+ if (!target)
+ return (CheckPetCast(target) == SPELL_CAST_OK);
+
ObjectGuid targetguid = target->GetGUID();
// check if target already has the same or a more powerful aura
@@ -5782,16 +5798,19 @@ bool Spell::CanAutoCast(Unit* target)
}
SpellCastResult result = CheckPetCast(target);
-
if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
{
+ // do not check targets for ground-targeted spells (we target them on top of the intended target anyway)
+ if (GetSpellInfo()->ExplicitTargetMask & TARGET_FLAG_DEST_LOCATION)
+ return true;
SelectSpellTargets();
//check if among target units, our WANTED target is as well (->only self cast spells return false)
- for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
+ for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
if (ihit->targetGUID == targetguid)
return true;
}
- return false; //target invalid
+ // either the cast failed or the intended target wouldn't be hit
+ return false;
}
SpellCastResult Spell::CheckRange(bool strict)
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 56042e05257..70eb7ad76bf 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3750,6 +3750,8 @@ void SpellMgr::LoadSpellInfoCorrections()
case 45257: // Using Steam Tonk Controller
case 45440: // Steam Tonk Controller
case 60256: // Collect Sample
+ case 45634: // Neural Needle
+ case 54897: // Flaming Arrow
// Crashes client on pressing ESC
spellInfo->AttributesEx4 &= ~SPELL_ATTR4_CAN_CAST_WHILE_CASTING;
break;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index d84fe11383f..37457f36145 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -659,9 +659,8 @@ void World::LoadConfigSettings(bool reload)
m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f);
m_float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetFloatDefault("MaxRecruitAFriendBonusDistance", 100.0f);
- /// @todo Add MonsterSight and GuarderSight (with meaning) in worldserver.conf or put them as define
+ /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define
m_float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetFloatDefault("MonsterSight", 50.0f);
- m_float_configs[CONFIG_SIGHT_GUARDER] = sConfigMgr->GetFloatDefault("GuarderSight", 50.0f);
if (reload)
{
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 330e78cf510..4456b7a553e 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -183,7 +183,6 @@ enum WorldFloatConfigs
CONFIG_GROUP_XP_DISTANCE = 0,
CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE,
CONFIG_SIGHT_MONSTER,
- CONFIG_SIGHT_GUARDER,
CONFIG_LISTEN_RANGE_SAY,
CONFIG_LISTEN_RANGE_TEXTEMOTE,
CONFIG_LISTEN_RANGE_YELL,