summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/sql/updates/db_world/2025_10_04_00.sql13
-rw-r--r--data/sql/updates/db_world/2025_10_04_01.sql6
-rw-r--r--data/sql/updates/db_world/2025_10_04_02.sql45
-rw-r--r--data/sql/updates/db_world/2025_10_04_03.sql28
-rw-r--r--data/sql/updates/db_world/2025_10_04_04.sql7
-rw-r--r--data/sql/updates/db_world/2025_10_04_05.sql12
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp8
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp129
-rw-r--r--src/server/game/Entities/Creature/Creature.h4
-rw-r--r--src/server/game/Entities/Player/Player.cpp162
-rw-r--r--src/server/game/Entities/Player/Player.h5
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp397
-rw-r--r--src/server/game/Entities/Unit/Unit.h32
-rw-r--r--src/server/game/Entities/Unit/UnitDefines.h4
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp44
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp341
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp8
-rw-r--r--src/server/game/Server/WorldSession.cpp3
-rw-r--r--src/server/game/Server/WorldSession.h17
-rw-r--r--src/server/scripts/Northrend/Gundrak/boss_moorabi.cpp2
-rw-r--r--src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp6
-rw-r--r--src/server/scripts/Northrend/zone_storm_peaks.cpp124
22 files changed, 793 insertions, 604 deletions
diff --git a/data/sql/updates/db_world/2025_10_04_00.sql b/data/sql/updates/db_world/2025_10_04_00.sql
new file mode 100644
index 0000000000..8c69861799
--- /dev/null
+++ b/data/sql/updates/db_world/2025_10_04_00.sql
@@ -0,0 +1,13 @@
+-- DB update 2025_10_02_01 -> 2025_10_04_00
+-- Update gameobject 'Big Rancid Meat' with sniffed values
+-- updated spawns
+DELETE FROM `gameobject` WHERE (`id` IN (177665)) AND (`guid` IN (45717));
+INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES
+(45717, 177665, 0, 0, 0, 1, 1, 1598.3446044921875, -3246.51708984375, 66.82944488525390625, 5.480334281921386718, 0, 0, -0.39073085784912109, 0.920504987239837646, 120, 255, 1, "", 46248, NULL);
+
+-- new spawns
+DELETE FROM `gameobject` WHERE (`id` IN (191789)) AND (`guid` IN (178, 179, 180));
+INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES
+(178, 191789, 571, 0, 0, 1, 1, 8322.1416015625, 2812.73779296875, 655.9156494140625, 2.042035102844238281, 0, 0, 0.852640151977539062, 0.522498607635498046, 120, 255, 1, "", 46368, NULL),
+(179, 191789, 571, 0, 0, 1, 1, 8340.8603515625, 2739.64208984375, 655.246337890625, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 46368, NULL),
+(180, 191789, 571, 0, 0, 1, 1, 8347.0751953125, 2816.0400390625, 655.16448974609375, 6.248279094696044921, 0, 0, -0.01745223999023437, 0.999847710132598876, 120, 255, 1, "", 46368, NULL);
diff --git a/data/sql/updates/db_world/2025_10_04_01.sql b/data/sql/updates/db_world/2025_10_04_01.sql
new file mode 100644
index 0000000000..82575f58c8
--- /dev/null
+++ b/data/sql/updates/db_world/2025_10_04_01.sql
@@ -0,0 +1,6 @@
+-- DB update 2025_10_04_00 -> 2025_10_04_01
+-- Update gameobject 'Stolen Pack' with sniffed values
+-- new spawns
+DELETE FROM `gameobject` WHERE (`id` IN (191726)) AND (`guid` IN (42));
+INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES
+(42, 191726, 571, 0, 0, 1, 1, 7312.4150390625, -1610.486572265625, 944.2940673828125, 4.991643905639648437, 0, 0, -0.60181427001953125, 0.798636078834533691, 120, 255, 1, "", 47720, NULL);
diff --git a/data/sql/updates/db_world/2025_10_04_02.sql b/data/sql/updates/db_world/2025_10_04_02.sql
new file mode 100644
index 0000000000..d12c1542b1
--- /dev/null
+++ b/data/sql/updates/db_world/2025_10_04_02.sql
@@ -0,0 +1,45 @@
+-- DB update 2025_10_04_01 -> 2025_10_04_02
+--
+SET @PATH := 29677;
+DELETE FROM `script_waypoint` WHERE `entry`=@PATH;
+INSERT INTO `script_waypoint` (`entry`, `pointid`, `location_x`, `location_y`, `location_z`, `waittime`) VALUES
+(@PATH, 0, 7157.85, -749.79016, 891.31964, 0),
+(@PATH, 1, 7164.398, -764.5809, 892.379, 0),
+(@PATH, 2, 7173.0713, -777.02747, 899.1467, 0),
+(@PATH, 3, 7182.944, -781.4821, 904.5898, 0),
+(@PATH, 4, 7193.046, -787.17285, 910.80505, 0),
+(@PATH, 5, 7205.2866, -793.2994, 917.2018, 0),
+(@PATH, 6, 7218.268, -802.1098, 921.5436, 0),
+(@PATH, 7, 7230.444, -816.3606, 924.7782, 0),
+(@PATH, 8, 7232.167, -834.23175, 926.6067, 0),
+(@PATH, 9, 7247.4736, -848.1854, 925.8038, 0),
+(@PATH, 10, 7263.3325, -875.33746, 925.0643, 0),
+(@PATH, 11, 7275.5557, -895.33954, 926.41693, 0),
+(@PATH, 12, 7283.677, -922.1367, 922.6736, 0),
+(@PATH, 13, 7284.9517, -947.7603, 918.92914, 0),
+(@PATH, 14, 7294.8335, -985.7026, 915.495, 0),
+(@PATH, 15, 7310.106, -1018.042, 913.71466, 0),
+(@PATH, 16, 7321.158, -1040.3647, 912.26416, 0),
+(@PATH, 17, 7341.2275, -1082.5309, 905.9852, 0),
+(@PATH, 18, 7355.9634, -1127.9506, 907.5246, 0),
+(@PATH, 19, 7359.7407, -1156.9366, 910.2179, 0),
+(@PATH, 20, 7352.724, -1172.65, 912.25995, 0),
+(@PATH, 21, 7340.049, -1188.0729, 914.70184, 0),
+(@PATH, 22, 7330.8037, -1201.4724, 915.6217, 0),
+(@PATH, 23, 7323.1274, -1228.3346, 909.4873, 0),
+(@PATH, 24, 7316.8394, -1268.2141, 902.8047, 0),
+(@PATH, 25, 7316.317, -1300.7645, 904.0472, 0),
+(@PATH, 26, 7315.278, -1332.3418, 904.6077, 0),
+(@PATH, 27, 7313.0195, -1366.2754, 907.04065, 0),
+(@PATH, 28, 7312.0806, -1399.7428, 910.6963, 0),
+(@PATH, 29, 7312.83, -1434.0999, 912.7638, 0),
+(@PATH, 30, 7311.606, -1466.3434, 916.6626, 0),
+(@PATH, 31, 7309.7144, -1498.7023, 921.09863, 0),
+(@PATH, 32, 7306.8794, -1531.1903, 928.2958, 0),
+(@PATH, 33, 7305.797, -1558.9252, 939.31244, 0),
+(@PATH, 34, 7305.19, -1566.0189, 940.9037, 0);
+
+-- set StandState to sitting
+DELETE FROM `creature_template_addon` WHERE (`entry` = 29695);
+INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
+(29695, 0, 0, 1, 0, 0, 0, '');
diff --git a/data/sql/updates/db_world/2025_10_04_03.sql b/data/sql/updates/db_world/2025_10_04_03.sql
new file mode 100644
index 0000000000..bd4a85489b
--- /dev/null
+++ b/data/sql/updates/db_world/2025_10_04_03.sql
@@ -0,0 +1,28 @@
+-- DB update 2025_10_04_02 -> 2025_10_04_03
+
+-- Set MT and WD
+UPDATE `creature` SET `wander_distance` = 0, `MovementType` = 0 WHERE (`id1` = 27482) AND (`guid` IN (104181));
+
+-- Set byte1 (Kneel)
+UPDATE `creature_addon` SET `bytes1` = 8 WHERE (`guid` IN (104181, 104184));
+
+-- Update SmartAI
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 27482;
+
+DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 27482);
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(27482, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - On Reset - Set Reactstate Passive'),
+(27482, 0, 1, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - On Aggro - Set Reactstate Aggressive'),
+(27482, 0, 2, 0, 0, 0, 100, 0, 2000, 4000, 8000, 12000, 0, 0, 11, 32771, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - In Combat - Cast \'Holy Shock\''),
+(27482, 0, 3, 0, 0, 0, 100, 0, 3000, 6000, 32000, 36000, 0, 0, 11, 29385, 32, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - In Combat - Cast \'Seal of Command\''),
+(27482, 0, 4, 0, 8, 0, 100, 512, 48845, 0, 0, 0, 0, 0, 80, 2748200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - On Spellhit \'Renew Infantry\' - Run Script');
+
+-- Set Action List
+DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2748200);
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(2748200, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 48813, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - Actionlist - Cast \'Kill Credit\''),
+(2748200, 9, 1, 0, 0, 0, 100, 0, 500, 500, 0, 0, 0, 0, 91, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - Actionlist - Remove FlagStandstate Kneel'),
+(2748200, 9, 2, 0, 0, 0, 100, 0, 2500, 2500, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - Actionlist - Set Orientation Invoker'),
+(2748200, 9, 3, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 5, 113, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - Actionlist - Play Emote 113'),
+(2748200, 9, 4, 0, 0, 0, 100, 0, 100, 100, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - Actionlist - Say Line 0'),
+(2748200, 9, 5, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 53, 1, 27482, 0, 0, 2000, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wounded Westfall Infantry - Actionlist - Start Waypoint Path 27482');
diff --git a/data/sql/updates/db_world/2025_10_04_04.sql b/data/sql/updates/db_world/2025_10_04_04.sql
new file mode 100644
index 0000000000..6c37e61065
--- /dev/null
+++ b/data/sql/updates/db_world/2025_10_04_04.sql
@@ -0,0 +1,7 @@
+-- DB update 2025_10_04_03 -> 2025_10_04_04
+--
+DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 50674);
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(13, 1, 50674, 0, 0, 31, 0, 3, 25469, 0, 0, 0, 0, '', 'The Demoralizer only hits Mindless Aberration'),
+(13, 1, 50674, 0, 1, 31, 0, 3, 25332, 0, 0, 0, 0, '', 'The Demoralizer only hits Stitched Warsong Horror'),
+(13, 1, 50674, 0, 2, 31, 0, 3, 25333, 0, 0, 0, 0, '', 'The Demoralizer only hits Undying Aggressor');
diff --git a/data/sql/updates/db_world/2025_10_04_05.sql b/data/sql/updates/db_world/2025_10_04_05.sql
new file mode 100644
index 0000000000..a2f9d56acd
--- /dev/null
+++ b/data/sql/updates/db_world/2025_10_04_05.sql
@@ -0,0 +1,12 @@
+-- DB update 2025_10_04_04 -> 2025_10_04_05
+
+-- Update SmartAI (Horde Siege Tank and Barrels).
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (25334, 27064));
+
+DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (25334, 27064));
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(27064, 0, 0, 1, 103, 0, 100, 512, 0, 25334, 1, 2, 0, 0, 11, 47916, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Abandoned Fuel Tank - On 1 or More Units in Range - Cast \'Fuel\''),
+(27064, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 4000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Abandoned Fuel Tank - On 1 or More Units in Range - Despawn In 4000 ms'),
+(27064, 0, 2, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Abandoned Fuel Tank - On Respawn - Stop Attacking'),
+(25334, 0, 0, 0, 8, 0, 100, 512, 47916, 0, 2000, 2000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Horde Siege Tank - On Spellhit \'Fuel\' - Say Line 0'),
+(25334, 0, 1, 0, 28, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 1000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Horde Siege Tank - On Passenger Removed - Despawn In 1000 ms');
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 043fa814bd..30373f2f65 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -531,10 +531,6 @@ void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO
if (loadDefault)
{
me->LoadEquipment(me->GetOriginalEquipmentId(), true);
- if (me->HasWeapon(OFF_ATTACK))
- me->SetCanDualWield(true);
- else
- me->SetCanDualWield(false);
return;
}
@@ -547,10 +543,6 @@ void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO
if (offHand >= 0)
{
me->SetVirtualItem(1, uint32(offHand));
- if (offHand >= 1)
- me->SetCanDualWield(true);
- else
- me->SetCanDualWield(false);
}
if (ranged >= 0)
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 19fe38a3c9..877cfee68a 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -525,7 +525,8 @@ bool Creature::InitEntry(uint32 Entry, const CreatureData* data)
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight);
- SetCanDualWield(cinfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK));
+ if (cinfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK))
+ SetDualWieldMode(DualWieldMode::ENABLED);
// checked at loading
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
@@ -571,7 +572,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
ReplaceAllDynamicFlags(dynamicflags);
- SetCanDualWield(cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK));
+ if (cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK))
+ SetDualWieldMode(DualWieldMode::ENABLED);
SetAttackTime(BASE_ATTACK, cInfo->BaseAttackTime);
SetAttackTime(OFF_ATTACK, cInfo->BaseAttackTime);
@@ -1996,7 +1998,7 @@ void Creature::setDeathState(DeathState state, bool despawn)
m_formation->FormationReset(true, false);
bool needsFalling = !despawn && (IsFlying() || IsHovering()) && !IsUnderWater();
- SetHover(false, false, false);
+ SetHover(false);
SetDisableGravity(false, false, false);
if (needsFalling)
@@ -3195,10 +3197,23 @@ bool Creature::IsImmuneToKnockback() const
bool Creature::HasWeapon(WeaponAttackType type) const
{
+ if (type == OFF_ATTACK)
+ {
+ switch (_dualWieldMode)
+ {
+ case DualWieldMode::ENABLED:
+ return true;
+ case DualWieldMode::DISABLED:
+ return false;
+ case DualWieldMode::AUTO:
+ default:
+ break;
+ }
+ }
+
const uint8 slot = uint8(type);
ItemEntry const* item = sItemStore.LookupEntry(GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + slot));
-
- return ((item && item->ClassID == ITEM_CLASS_WEAPON) || (type == OFF_ATTACK && CanDualWield()));
+ return (item && item->ClassID == ITEM_CLASS_WEAPON);
}
/**
@@ -3229,8 +3244,9 @@ bool Creature::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool
{
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
data << GetPackGUID();
- data << uint32(0); //! movement counter
+ data << m_movedByPlayer->ToPlayer()->GetSession()->GetOrderCounter(); // movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
+ m_movedByPlayer->ToPlayer()->GetSession()->IncrementOrderCounter();
data.Initialize(MSG_MOVE_GRAVITY_CHNG, 64);
data << GetPackGUID();
@@ -3303,107 +3319,6 @@ void Creature::RefreshSwimmingFlag(bool recheck)
SetUnitFlag(UNIT_FLAG_SWIMMING);
}
-bool Creature::SetCanFly(bool enable, bool /*packetOnly*/ /* = false */)
-{
- if (!Unit::SetCanFly(enable))
- return false;
-
- if (m_movedByPlayer)
- {
- sScriptMgr->AnticheatSetCanFlybyServer(m_movedByPlayer->ToPlayer(), enable);
-
- if (!enable)
- m_movedByPlayer->ToPlayer()->SetFallInformation(GameTime::GetGameTime().count(), m_movedByPlayer->ToPlayer()->GetPositionZ());
-
- WorldPacket data(enable ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
- return true;
- }
-
- WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9);
- data << GetPackGUID();
- SendMessageToSet(&data, false);
- return true;
-}
-
-bool Creature::SetWaterWalking(bool enable, bool packetOnly /* = false */)
-{
- if (!packetOnly && !Unit::SetWaterWalking(enable))
- return false;
-
- if (m_movedByPlayer)
- {
- WorldPacket data(enable ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_WATER_WALK, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
- return true;
- }
-
- WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9);
- data << GetPackGUID();
- SendMessageToSet(&data, true);
- return true;
-}
-
-bool Creature::SetFeatherFall(bool enable, bool packetOnly /* = false */)
-{
- if (!packetOnly && !Unit::SetFeatherFall(enable))
- return false;
-
- if (m_movedByPlayer)
- {
- WorldPacket data(enable ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_FEATHER_FALL, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
- return true;
- }
-
- WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL, 9);
- data << GetPackGUID();
- SendMessageToSet(&data, true);
- return true;
-}
-
-bool Creature::SetHover(bool enable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/)
-{
- if (!packetOnly && !Unit::SetHover(enable))
- return false;
-
- if (updateAnimationTier && IsAlive() && !HasUnitState(UNIT_STATE_ROOT) && !IsRooted())
- {
- if (IsLevitating())
- SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
- else if (IsHovering())
- SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER);
- else
- SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
- }
-
- WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9);
- data << GetPackGUID();
- SendMessageToSet(&data, false);
- return true;
-}
-
float Creature::GetAggroRange(Unit const* target) const
{
// Determines the aggro range for creatures
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index a2c2947020..e5ebcd45a4 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -143,10 +143,6 @@ public:
bool SetWalk(bool enable) override;
bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true) override;
bool SetSwim(bool enable) override;
- bool SetCanFly(bool enable, bool packetOnly = false) override;
- bool SetWaterWalking(bool enable, bool packetOnly = false) override;
- bool SetFeatherFall(bool enable, bool packetOnly = false) override;
- bool SetHover(bool enable, bool packetOnly = false, bool updateAnimationTier = true) override;
bool HasSpellFocus(Spell const* focusSpell = nullptr) const;
struct
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 02adb9ef0c..05c93ca7e1 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1337,9 +1337,10 @@ void Player::SendTeleportAckPacket()
{
WorldPacket data(MSG_MOVE_TELEPORT_ACK, 41);
data << GetPackGUID();
- data << uint32(0); // this value increments every time
+ data << GetSession()->GetOrderCounter(); // movement counter
BuildMovementPacket(&data);
GetSession()->SendPacket(&data);
+ GetSession()->IncrementOrderCounter();
}
bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options /*= 0*/, Unit* target /*= nullptr*/, bool newInstance /*= false*/)
@@ -3800,7 +3801,7 @@ bool Player::resetTalents(bool noResetCost)
if (m_canTitanGrip)
SetCanTitanGrip(false);
// xinef: remove dual wield if player does not have dual wield spell (shamans)
- if (!HasSpell(674) && m_canDualWield)
+ if (!HasSpell(674) && CanDualWield())
SetCanDualWield(false);
AutoUnequipOffhandIfNeed();
@@ -4425,27 +4426,29 @@ void Player::DeleteOldRecoveryItems(uint32 keepDays)
void Player::SetMovement(PlayerMovementType pType)
{
WorldPacket data;
+ const PackedGuid& guid = GetPackGUID();
switch (pType)
{
case MOVE_ROOT:
- data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size() + 4);
+ data.Initialize(SMSG_FORCE_MOVE_ROOT, guid.size() + 4);
break;
case MOVE_UNROOT:
- data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size() + 4);
+ data.Initialize(SMSG_FORCE_MOVE_UNROOT, guid.size() + 4);
break;
case MOVE_WATER_WALK:
- data.Initialize(SMSG_MOVE_WATER_WALK, GetPackGUID().size() + 4);
+ data.Initialize(SMSG_MOVE_WATER_WALK, guid.size() + 4);
break;
case MOVE_LAND_WALK:
- data.Initialize(SMSG_MOVE_LAND_WALK, GetPackGUID().size() + 4);
+ data.Initialize(SMSG_MOVE_LAND_WALK, guid.size() + 4);
break;
default:
LOG_ERROR("entities.player", "Player::SetMovement: Unsupported move type ({}), data not sent to client.", pType);
return;
}
- data << GetPackGUID();
- data << uint32(0);
+ data << guid;
+ data << GetSession()->GetOrderCounter(); // movement counter
GetSession()->SendPacket(&data);
+ GetSession()->IncrementOrderCounter();
}
/* Preconditions:
@@ -11701,13 +11704,56 @@ void Player::SendInitialPacketsAfterAddToMap()
if (HasStunAura())
SetMovement(MOVE_ROOT);
+ WorldPacket setCompoundState(SMSG_MULTIPLE_MOVES, 100);
+ setCompoundState << uint32(0); // size placeholder
+
// manual send package (have code in HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true); that must not be re-applied.
- if (HasRootAura())
+ if (IsImmobilizedState())
+ {
+ auto const counter = GetSession()->GetOrderCounter();
+ setCompoundState << uint8(2 + GetPackGUID().size() + 4);
+ setCompoundState << uint16(SMSG_FORCE_MOVE_ROOT);
+ setCompoundState << GetPackGUID();
+ setCompoundState << uint32(counter);
+ GetSession()->IncrementOrderCounter();
+ }
+
+ if (HasAuraType(SPELL_AURA_FEATHER_FALL))
+ {
+ auto const counter = GetSession()->GetOrderCounter();
+ setCompoundState << uint8(2 + GetPackGUID().size() + 4);
+ setCompoundState << uint16(SMSG_MOVE_FEATHER_FALL);
+ setCompoundState << GetPackGUID();
+ setCompoundState << uint32(counter);
+ GetSession()->IncrementOrderCounter();
+ }
+
+ if (HasAuraType(SPELL_AURA_WATER_WALK))
{
- WorldPacket data2(SMSG_FORCE_MOVE_ROOT, 10);
- data2 << GetPackGUID();
- data2 << (uint32)2;
- SendMessageToSet(&data2, true);
+ auto const counter = GetSession()->GetOrderCounter();
+ setCompoundState << uint8(2 + GetPackGUID().size() + 4);
+ setCompoundState << uint16(SMSG_MOVE_WATER_WALK);
+ setCompoundState << GetPackGUID();
+ setCompoundState << uint32(counter);
+ GetSession()->IncrementOrderCounter();
+ }
+
+ if (HasAuraType(SPELL_AURA_HOVER))
+ {
+ auto const counter = GetSession()->GetOrderCounter();
+ setCompoundState << uint8(2 + GetPackGUID().size() + 4);
+ setCompoundState << uint16(SMSG_MOVE_SET_HOVER);
+ setCompoundState << GetPackGUID();
+ setCompoundState << uint32(counter);
+ GetSession()->IncrementOrderCounter();
+ }
+
+ // TODO: Pending mount protocol
+
+ if (setCompoundState.size() > 4)
+ {
+ setCompoundState.put<uint32>(0, setCompoundState.size() - 4);
+ SendDirectMessage(&setCompoundState);
}
SendEnchantmentDurations(); // must be after add to map
@@ -15270,7 +15316,7 @@ void Player::ActivateSpec(uint8 spec)
if (!HasTalent(46917, GetActiveSpec()) && m_canTitanGrip)
SetCanTitanGrip(false);
// xinef: remove dual wield if player does not have dual wield spell (shamans)
- if (!HasSpell(674) && m_canDualWield)
+ if (!HasSpell(674) && CanDualWield())
SetCanDualWield(false);
AutoUnequipOffhandIfNeed();
@@ -15932,8 +15978,9 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool /
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
data << GetPackGUID();
- data << uint32(0); //! movement counter
+ data << GetSession()->GetOrderCounter(); // movement counter
SendDirectMessage(&data);
+ GetSession()->IncrementOrderCounter();
data.Initialize(MSG_MOVE_GRAVITY_CHNG, 64);
data << GetPackGUID();
@@ -15942,91 +15989,6 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool /
return true;
}
-bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
-{
- sScriptMgr->AnticheatSetCanFlybyServer(this, apply);
-
- if (!packetOnly && !Unit::SetCanFly(apply))
- return false;
-
- if (!apply)
- SetFallInformation(GameTime::GetGameTime().count(), GetPositionZ());
-
- WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- SendMessageToSet(&data, false);
- return true;
-}
-
-bool Player::SetHover(bool apply, bool packetOnly /*= false*/, bool /*updateAnimationTier = true*/)
-{
- // moved inside, flag can be removed on landing and wont send appropriate packet to client when aura is removed
- if (!packetOnly /* && !Unit::SetHover(apply)*/)
- {
- Unit::SetHover(apply);
- // return false;
- }
-
- WorldPacket data(apply ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_HOVER, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- SendMessageToSet(&data, false);
- return true;
-}
-
-bool Player::SetWaterWalking(bool apply, bool packetOnly /*= false*/)
-{
- // moved inside, flag can be removed on landing and wont send appropriate packet to client when aura is removed
- if (!packetOnly /* && !Unit::SetWaterWalking(apply)*/)
- {
- Unit::SetWaterWalking(apply);
- // return false;
- }
-
- WorldPacket data(apply ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_WATER_WALK, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- SendMessageToSet(&data, false);
- return true;
-}
-
-bool Player::SetFeatherFall(bool apply, bool packetOnly /*= false*/)
-{
- // Xinef: moved inside, flag can be removed on landing and wont send appropriate packet to client when aura is removed
- if (!packetOnly/* && !Unit::SetFeatherFall(apply)*/)
- {
- Unit::SetFeatherFall(apply);
- //return false;
- }
-
- WorldPacket data(apply ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, 12);
- data << GetPackGUID();
- data << uint32(0); //! movement counter
- SendDirectMessage(&data);
-
- data.Initialize(MSG_MOVE_FEATHER_FALL, 64);
- data << GetPackGUID();
- BuildMovementPacket(&data);
- SendMessageToSet(&data, false);
- return true;
-}
-
Guild* Player::GetGuild() const
{
uint32 guildId = GetGuildId();
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index c8dd9fc7ab..74c89cc96d 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -2564,13 +2564,10 @@ public:
void RemoveFromWhisperWhiteList(ObjectGuid guid) { WhisperList.remove(guid); }
bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true) override;
- bool SetCanFly(bool apply, bool packetOnly = false) override;
- bool SetWaterWalking(bool apply, bool packetOnly = false) override;
- bool SetFeatherFall(bool apply, bool packetOnly = false) override;
- bool SetHover(bool enable, bool packetOnly = false, bool updateAnimationTier = true) override;
[[nodiscard]] bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
[[nodiscard]] bool CanEnterWater() const override { return true; }
+ bool IsFreeFlying() const { return HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || HasAuraType(SPELL_AURA_FLY); }
// saving
void AdditionalSavingAddMask(uint8 mask) { m_additionalSaveTimer = 2000; m_additionalSaveMask |= mask; }
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 7963773799..addcf5b2b8 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -242,9 +242,7 @@ Unit::Unit() : WorldObject(),
m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
- m_canDualWield = false;
-
- m_rootTimes = 0;
+ _dualWieldMode = DualWieldMode::AUTO;
m_state = 0;
m_deathState = DeathState::Alive;
@@ -868,6 +866,9 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
// interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras)
if (spellProto)
{
+ if (attacker && damagetype != DOT && spellProto->DmgClass == SPELL_DAMAGE_CLASS_MELEE && !(spellProto->GetSchoolMask() & SPELL_SCHOOL_MASK_HOLY))
+ attacker->DealDamageShieldDamage(victim);
+
if (!spellProto->HasAttribute(SPELL_ATTR4_REACTIVE_DAMAGE_PROC))
victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto->Id);
}
@@ -1975,57 +1976,60 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
// Do effect if any damage done to target
if (damageInfo->damages[0].damage + damageInfo->damages[1].damage)
+ DealDamageShieldDamage(victim);
+}
+
+void Unit::DealDamageShieldDamage(Unit* victim)
+{
+ // We're going to call functions which can modify content of the list during iteration over it's elements
+ // Let's copy the list so we can prevent iterator invalidation
+ AuraEffectList vDamageShieldsCopy(victim->GetAuraEffectsByType(SPELL_AURA_DAMAGE_SHIELD));
+ for (AuraEffectList::const_iterator dmgShieldItr = vDamageShieldsCopy.begin(); dmgShieldItr != vDamageShieldsCopy.end(); ++dmgShieldItr)
{
- // We're going to call functions which can modify content of the list during iteration over it's elements
- // Let's copy the list so we can prevent iterator invalidation
- AuraEffectList vDamageShieldsCopy(victim->GetAuraEffectsByType(SPELL_AURA_DAMAGE_SHIELD));
- for (AuraEffectList::const_iterator dmgShieldItr = vDamageShieldsCopy.begin(); dmgShieldItr != vDamageShieldsCopy.end(); ++dmgShieldItr)
+ SpellInfo const* i_spellProto = (*dmgShieldItr)->GetSpellInfo();
+ // Damage shield can be resisted...
+ if (SpellMissInfo missInfo = victim->SpellHitResult(this, i_spellProto, false))
{
- SpellInfo const* i_spellProto = (*dmgShieldItr)->GetSpellInfo();
- // Damage shield can be resisted...
- if (SpellMissInfo missInfo = victim->SpellHitResult(this, i_spellProto, false))
- {
- victim->SendSpellMiss(this, i_spellProto->Id, missInfo);
- continue;
- }
+ victim->SendSpellMiss(this, i_spellProto->Id, missInfo);
+ continue;
+ }
- // ...or immuned
- if (IsImmunedToDamageOrSchool(i_spellProto))
- {
- victim->SendSpellDamageImmune(this, i_spellProto->Id);
- continue;
- }
+ // ...or immuned
+ if (IsImmunedToDamageOrSchool(i_spellProto))
+ {
+ victim->SendSpellDamageImmune(this, i_spellProto->Id);
+ continue;
+ }
- uint32 damage = uint32(std::max(0, (*dmgShieldItr)->GetAmount())); // xinef: done calculated at amount calculation
+ uint32 damage = uint32(std::max(0, (*dmgShieldItr)->GetAmount())); // xinef: done calculated at amount calculation
- if (Unit* caster = (*dmgShieldItr)->GetCaster())
- {
- damage = caster->SpellDamageBonusDone(this, i_spellProto, damage, SPELL_DIRECT_DAMAGE, (*dmgShieldItr)->GetEffIndex());
- damage = this->SpellDamageBonusTaken(caster, i_spellProto, damage, SPELL_DIRECT_DAMAGE);
- }
+ if (Unit* caster = (*dmgShieldItr)->GetCaster())
+ {
+ damage = caster->SpellDamageBonusDone(this, i_spellProto, damage, SPELL_DIRECT_DAMAGE, (*dmgShieldItr)->GetEffIndex());
+ damage = this->SpellDamageBonusTaken(caster, i_spellProto, damage, SPELL_DIRECT_DAMAGE);
+ }
- uint32 absorb = 0;
+ uint32 absorb = 0;
- DamageInfo dmgInfo(victim, this, damage, i_spellProto, i_spellProto->GetSchoolMask(), SPELL_DIRECT_DAMAGE);
- Unit::CalcAbsorbResist(dmgInfo);
- absorb = dmgInfo.GetAbsorb();
- damage = dmgInfo.GetDamage();
+ DamageInfo dmgInfo(victim, this, damage, i_spellProto, i_spellProto->GetSchoolMask(), SPELL_DIRECT_DAMAGE);
+ Unit::CalcAbsorbResist(dmgInfo);
+ absorb = dmgInfo.GetAbsorb();
+ damage = dmgInfo.GetDamage();
- Unit::DealDamageMods(this, damage, &absorb);
+ Unit::DealDamageMods(this, damage, &absorb);
- /// @todo: Move this to a packet handler
- WorldPacket data(SMSG_SPELLDAMAGESHIELD, (8 + 8 + 4 + 4 + 4 + 4));
- data << victim->GetGUID();
- data << GetGUID();
- data << uint32(i_spellProto->Id);
- data << uint32(damage); // Damage
- int32 overkill = int32(damage) - int32(GetHealth());
- data << uint32(overkill > 0 ? overkill : 0); // Overkill
- data << uint32(i_spellProto->GetSchoolMask());
- victim->SendMessageToSet(&data, true);
+ /// @todo: Move this to a packet handler
+ WorldPacket data(SMSG_SPELLDAMAGESHIELD, (8 + 8 + 4 + 4 + 4 + 4));
+ data << victim->GetGUID();
+ data << GetGUID();
+ data << uint32(i_spellProto->Id);
+ data << uint32(damage); // Damage
+ int32 overkill = int32(damage) - int32(GetHealth());
+ data << uint32(overkill > 0 ? overkill : 0); // Overkill
+ data << uint32(i_spellProto->GetSchoolMask());
+ victim->SendMessageToSet(&data, true);
- Unit::DealDamage(victim, this, damage, 0, SPELL_DIRECT_DAMAGE, i_spellProto->GetSchoolMask(), i_spellProto, true);
- }
+ Unit::DealDamage(victim, this, damage, 0, SPELL_DIRECT_DAMAGE, i_spellProto->GetSchoolMask(), i_spellProto, true);
}
}
@@ -4332,10 +4336,10 @@ void Unit::ProcessTerrainStatusUpdate()
LiquidData const& liquidData = GetLiquidData();
- // remove appropriate auras if we are swimming/not swimming respectively
- if (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING)
+ // remove appropriate auras if we are swimming/not swimming respectively - exact mirror of client logic
+ if (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING && (liquidData.Level - GetPositionZ()) > GetCollisionHeight() * 0.75f) // Shallow water at ~75% of collision height)
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER);
- else if (!isSwimming())
+ else
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
// liquid aura handling
@@ -13607,9 +13611,10 @@ void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry)
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
data << GetPackGUID();
- data << uint32(GameTime::GetGameTime().count()); // Packet counter
+ data << player->GetSession()->GetOrderCounter(); // movement counter
data << player->GetCollisionHeight();
player->GetSession()->SendPacket(&data);
+ player->GetSession()->IncrementOrderCounter();
}
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNT);
@@ -13623,13 +13628,14 @@ void Unit::Dismount()
SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
RemoveUnitFlag(UNIT_FLAG_MOUNT);
- if (Player* thisPlayer = ToPlayer())
+ if (Player* player = ToPlayer())
{
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
data << GetPackGUID();
- data << uint32(GameTime::GetGameTime().count()); // Packet counter
- data << thisPlayer->GetCollisionHeight();
- thisPlayer->GetSession()->SendPacket(&data);
+ data << player->GetSession()->GetOrderCounter(); // movement counter
+ data << player->GetCollisionHeight();
+ player->GetSession()->SendPacket(&data);
+ player->GetSession()->IncrementOrderCounter();
}
WorldPacket data(SMSG_DISMOUNT, 8);
@@ -14643,11 +14649,13 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
return;
}
data << GetPackGUID();
- data << (uint32)0; // moveEvent, NUM_PMOVE_EVTS = 0x39
+ data << (IsPlayer() ? ToPlayer()->GetSession()->GetOrderCounter() : uint32(0)); // movement counter
if (mtype == MOVE_RUN)
data << uint8(0); // new 2.1.0
data << float(GetSpeed(mtype));
SendMessageToSet(&data, true);
+ if (IsPlayer()) // TODO: Resolve this mess
+ ToPlayer()->GetSession()->IncrementOrderCounter();
}
}
@@ -16806,6 +16814,15 @@ bool Unit::IsStandState() const
return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL;
}
+bool Unit::IsStandUpOnMovementState() const
+{
+ uint8 s = getStandState();
+ return
+ s == UNIT_STAND_STATE_SIT_CHAIR || s == UNIT_STAND_STATE_SIT_LOW_CHAIR ||
+ s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR ||
+ s == UNIT_STAND_STATE_SIT || s == UNIT_STAND_STATE_SLEEP;
+}
+
void Unit::SetStandState(uint8 state)
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, state);
@@ -18302,9 +18319,6 @@ void Unit::SetRooted(bool apply, bool isStun)
{
if (apply)
{
- if (m_rootTimes > 0) // blizzard internal check?
- m_rootTimes++;
-
// MOVEMENTFLAG_ROOT cannot be used in conjunction with MOVEMENTFLAG_MASK_MOVING (tested 3.3.5a)
// this will freeze clients. That's why we remove MOVEMENTFLAG_MASK_MOVING before
// setting MOVEMENTFLAG_ROOT
@@ -18336,8 +18350,9 @@ void Unit::SetRooted(bool apply, bool isStun)
{
WorldPacket data(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size() + 4);
data << GetPackGUID();
- data << m_rootTimes;
+ data << m_movedByPlayer->ToPlayer()->GetSession()->GetOrderCounter(); // movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
+ m_movedByPlayer->ToPlayer()->GetSession()->IncrementOrderCounter();
}
else
{
@@ -18356,8 +18371,9 @@ void Unit::SetRooted(bool apply, bool isStun)
{
WorldPacket data(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size() + 4);
data << GetPackGUID();
- data << m_rootTimes;
+ data << m_movedByPlayer->ToPlayer()->GetSession()->GetOrderCounter(); // movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
+ m_movedByPlayer->ToPlayer()->GetSession()->IncrementOrderCounter();
}
else
{
@@ -19261,16 +19277,14 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ)
WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8 + 4 + 4 + 4 + 4 + 4));
data << GetPackGUID();
- data << uint32(0); // counter
+ data << player->GetSession()->GetOrderCounter(); // movement counter
data << float(vcos); // x direction
data << float(vsin); // y direction
data << float(speedXY); // Horizontal speed
data << float(-speedZ); // Z Movement speed (vertical)
player->GetSession()->SendPacket(&data);
-
- if (player->HasIncreaseMountedFlightSpeedAura() || player->HasFlyAura())
- player->SetCanFly(true, true);
+ player->GetSession()->IncrementOrderCounter();
player->SetCanKnockback(true);
}
@@ -20464,98 +20478,186 @@ bool Unit::SetSwim(bool enable)
*
* Doesn't inform the client.
*/
-bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */)
+void Unit::SetCanFly(bool enable)
{
- if (enable == HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
- return false;
+ bool isClientControlled = IsClientControlled();
- if (enable)
+ if (!isClientControlled)
{
- AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
- RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
+ if (enable)
+ m_movementInfo.AddMovementFlag(MOVEMENTFLAG_CAN_FLY);
+ else
+ m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_CAN_FLY);
}
- else
+
+ if (!IsInWorld()) // is sent on add to map
+ return;
+
+ if (isClientControlled)
{
- RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_MASK_MOVING_FLY);
+ if (Player const* player = GetClientControlling())
+ {
+ auto const counter = player->GetSession()->GetOrderCounter();
+
+ WorldPacket data(enable ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, GetPackGUID().size() + 4);
+ data << GetPackGUID();
+ data << counter;
+ player->GetSession()->SendPacket(&data);
+ player->GetSession()->IncrementOrderCounter();
+ return;
+ }
}
- return true;
+ WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9);
+ data << GetPackGUID();
+ SendMessageToSet(&data, true);
}
-/**
- * @brief Allow to walk on water. Doesn't inform the client.
- * Need to use SendMovementWaterWalking() if it's for players.
- */
-bool Unit::SetWaterWalking(bool enable, bool /*packetOnly = false*/)
+void Unit::SetFeatherFall(bool enable)
{
- if (enable == HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING))
- return false;
-
- if (enable)
- AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
- else
- RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
+ bool isClientControlled = IsClientControlled();
- return true;
-}
+ if (!isClientControlled)
+ {
+ if (enable)
+ m_movementInfo.AddMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
+ else
+ m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
+ }
-void Unit::SendMovementWaterWalking(Player* sendTo)
-{
- if (!movespline->Initialized())
+ if (!IsInWorld()) // is sent on add to map
return;
- WorldPacket data(SMSG_SPLINE_MOVE_WATER_WALK, 9);
+
+ if (isClientControlled)
+ {
+ if (Player const* player = GetClientControlling())
+ {
+ auto const counter = player->GetSession()->GetOrderCounter();
+
+ WorldPacket data(enable ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, GetPackGUID().size() + 4);
+
+ data << GetPackGUID();
+ data << counter;
+ player->GetSession()->SendPacket(&data);
+ player->GetSession()->IncrementOrderCounter();
+
+ // start fall from current height
+ if (!enable)
+ const_cast<Player*>(player)->SetFallInformation(0, GetPositionZ());
+
+ return;
+ }
+ }
+
+ WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL);
data << GetPackGUID();
- sendTo->SendDirectMessage(&data);
+ SendMessageToSet(&data, true);
}
-bool Unit::SetFeatherFall(bool enable, bool /*packetOnly = false*/)
+void Unit::SetHover(bool enable)
{
- if (enable == HasUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW))
- return false;
+ bool isClientControlled = IsClientControlled();
+
+ if (!isClientControlled)
+ {
+ if (enable)
+ m_movementInfo.AddMovementFlag(MOVEMENTFLAG_HOVER);
+ else
+ m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_HOVER);
+ }
+
+ float hoverHeight = GetHoverHeight();
if (enable)
- AddUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
+ {
+ if (hoverHeight && GetPositionZ() - GetMap()->GetHeight(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ()) < hoverHeight)
+ Relocate(GetPositionX(), GetPositionY(), GetPositionZ() + hoverHeight);
+ }
else
- RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
-
- return true;
-}
+ {
+ if (IsAlive() || !IsUnit())
+ {
+ float newZ = std::max<float>(GetMap()->GetHeight(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ()), GetPositionZ() - hoverHeight);
+ UpdateAllowedPositionZ(GetPositionX(), GetPositionY(), newZ);
+ Relocate(GetPositionX(), GetPositionY(), newZ);
+ }
+ }
-void Unit::SendMovementFeatherFall(Player* sendTo)
-{
- if (!movespline->Initialized())
+ if (!IsInWorld()) // is sent on add to map
return;
- WorldPacket data(SMSG_SPLINE_MOVE_FEATHER_FALL, 9);
+
+ if (isClientControlled)
+ {
+ if (Player const* player = GetClientControlling())
+ {
+ WorldPacket data(enable ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, GetPackGUID().size() + 4);
+
+ auto const counter = player->GetSession()->GetOrderCounter();
+
+ data << GetPackGUID();
+ data << counter;
+ player->GetSession()->SendPacket(&data);
+ player->GetSession()->IncrementOrderCounter();
+ return;
+ }
+ }
+
+ WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9);
data << GetPackGUID();
- sendTo->SendDirectMessage(&data);
+ SendMessageToSet(&data, true);
}
-bool Unit::SetHover(bool enable, bool /*packetOnly = false*/, bool /*updateAnimationTier = true*/)
+void Unit::SetWaterWalking(bool enable)
{
- if (enable == HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
- return false;
+ bool isClientControlled = IsClientControlled();
- float hoverHeight = GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
-
- if (enable)
+ if (!isClientControlled)
{
- AddUnitMovementFlag(MOVEMENTFLAG_HOVER);
- if (hoverHeight && GetPositionZ() - GetFloorZ() < hoverHeight)
- UpdateHeight(GetPositionZ() + hoverHeight);
+ if (enable)
+ m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WATERWALKING);
+ else
+ m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_WATERWALKING);
}
- else
+
+ if (!IsInWorld()) // is sent on add to map
+ return;
+
+ if (isClientControlled)
{
- RemoveUnitMovementFlag(MOVEMENTFLAG_HOVER);
- if (hoverHeight && (!isDying() || !IsCreature()))
+ if (Player const* player = GetClientControlling())
{
- float newZ = std::max<float>(GetFloorZ(), GetPositionZ() - hoverHeight);
- UpdateAllowedPositionZ(GetPositionX(), GetPositionY(), newZ);
- UpdateHeight(newZ);
+ auto const counter = player->GetSession()->GetOrderCounter();
+
+ WorldPacket data(enable ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, GetPackGUID().size() + 4);
+ data << GetPackGUID();
+ data << counter;
+ player->GetSession()->SendPacket(&data);
+ player->GetSession()->IncrementOrderCounter();
+ return;
}
- SendMovementFlagUpdate(); // pussywizard: needed for falling after death (instead of falling onto air at hover height)
}
- return true;
+ WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9);
+ data << GetPackGUID();
+ SendMessageToSet(&data, true);
+}
+
+void Unit::SendMovementWaterWalking(Player* sendTo)
+{
+ if (!movespline->Initialized())
+ return;
+ WorldPacket data(SMSG_SPLINE_MOVE_WATER_WALK, 9);
+ data << GetPackGUID();
+ sendTo->SendDirectMessage(&data);
+}
+
+void Unit::SendMovementFeatherFall(Player* sendTo)
+{
+ if (!movespline->Initialized())
+ return;
+ WorldPacket data(SMSG_SPLINE_MOVE_FEATHER_FALL, 9);
+ data << GetPackGUID();
+ sendTo->SendDirectMessage(&data);
}
void Unit::SendMovementHover(Player* sendTo)
@@ -21203,3 +21305,58 @@ std::string Unit::GetDebugInfo() const
<< " Class: " << std::to_string(getClass());
return sstr.str();
}
+
+bool Unit::IsClientControlled(Player const* exactClient /*= nullptr*/) const
+{
+ // Severvide method to check if unit is client controlled (optionally check for specific client in control)
+
+ // Applies only to player controlled units
+ if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
+ return false;
+
+ // These flags are meant to be used when server controls this unit, client control is taken away
+ if (HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING)))
+ return false;
+
+ // If unit is possessed, it has lost original control...
+ if (ObjectGuid const& guid = GetCharmerGUID())
+ {
+ // ... but if it is a possessing charm, then we have to check if some other player controls it
+ if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) && guid.IsPlayer())
+ return (exactClient ? (exactClient->GetGUID() == guid) : true);
+ return false;
+ }
+
+ // By default: players have client control over themselves
+ if (IsPlayer())
+ return (exactClient ? (exactClient == this) : true);
+ return false;
+}
+
+Player const* Unit::GetClientControlling() const
+{
+ // Serverside reverse "mover" deduction logic at controlled unit
+
+ // Applies only to player controlled units
+ if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
+ {
+ // Charm always removes control from original client...
+ if (GetCharmerGUID())
+ {
+ // ... but if it is a possessing charm, some other client may have control
+ if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED))
+ {
+ Unit const* charmer = GetCharmer();
+ if (charmer && charmer->IsPlayer())
+ return static_cast<Player const*>(charmer);
+ }
+ }
+ else if (IsPlayer())
+ {
+ // Check if anything prevents original client from controlling
+ if (IsClientControlled(static_cast<Player const*>(this)))
+ return static_cast<Player const*>(this);
+ }
+ }
+ return nullptr;
+}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 2fcccdb905..77b56d5669 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -567,6 +567,13 @@ enum class SearchMethod
typedef std::list<Player*> SharedVisionList;
+enum class DualWieldMode : uint8
+{
+ AUTO = 0, // non-player units must have a valid offhand weapon to enable dual wield
+ DISABLED = 1,
+ ENABLED = 2,
+};
+
struct AttackPosition {
AttackPosition(Position pos) : _pos(std::move(pos)), _taken(false) {}
bool operator==(const int val)
@@ -737,6 +744,7 @@ public:
void SetExtraUnitMovementFlags(uint16 f) { m_movementInfo.flags2 = f; }
inline bool IsCrowdControlled() const { return HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_STUNNED)); }
+ inline bool IsImmobilizedState() const { return HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED); }
/*********************************************************/
/*** UNIT TYPES, CLASSES, RACES... ***/
@@ -819,6 +827,11 @@ public:
bool IsValidAssistTarget(Unit const* target) const;
bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const;
+ // Client controlled: check if unit currently is under client control (has active "mover"), optionally check for specific client (server-side)
+ bool IsClientControlled(Player const* exactClient = nullptr) const;
+ // Controlling client: server PoV on which client (player) controls movement of the unit at the moment, obtain "mover" (server-side)
+ Player const* GetClientControlling() const;
+
// Combat range
[[nodiscard]] float GetBoundaryRadius() const { return m_floatValues[UNIT_FIELD_BOUNDINGRADIUS]; }
[[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
@@ -917,8 +930,9 @@ public:
// Weapons systems
[[nodiscard]] bool haveOffhandWeapon() const;
- [[nodiscard]] bool CanDualWield() const { return m_canDualWield; }
- virtual void SetCanDualWield(bool value) { m_canDualWield = value; }
+ [[nodiscard]] bool CanDualWield() const { return _dualWieldMode == DualWieldMode::ENABLED; }
+ virtual void SetCanDualWield(bool value) { _dualWieldMode = value ? DualWieldMode::ENABLED : DualWieldMode::DISABLED; }
+ virtual void SetDualWieldMode(DualWieldMode mode) { _dualWieldMode = mode; }
virtual bool HasWeapon(WeaponAttackType type) const = 0;
inline bool HasMainhandWeapon() const { return HasWeapon(BASE_ATTACK); }
@@ -1157,6 +1171,7 @@ public:
static uint32 DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = nullptr, bool durabilityLoss = true, bool allowGM = false, Spell const* spell = nullptr);
void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss);
void DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss, Spell const* spell = nullptr);
+ void DealDamageShieldDamage(Unit* victim);
static void DealDamageMods(Unit const* victim, uint32& damage, uint32* absorb);
static void Kill(Unit* killer, Unit* victim, bool durabilityLoss = true, WeaponAttackType attackType = BASE_ATTACK, SpellInfo const* spellProto = nullptr, Spell const* spell = nullptr);
@@ -1668,10 +1683,10 @@ public:
virtual bool SetWalk(bool enable);
virtual bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true);
virtual bool SetSwim(bool enable);
- virtual bool SetCanFly(bool enable, bool packetOnly = false);
- virtual bool SetWaterWalking(bool enable, bool packetOnly = false);
- virtual bool SetFeatherFall(bool enable, bool packetOnly = false);
- virtual bool SetHover(bool enable, bool packetOnly = false, bool updateAnimationTier = true);
+ void SetCanFly(bool enable);
+ void SetWaterWalking(bool enable);
+ void SetFeatherFall(bool enable);
+ void SetHover(bool enable);
MotionMaster* GetMotionMaster() { return i_motionMaster; }
[[nodiscard]] const MotionMaster* GetMotionMaster() const { return i_motionMaster; }
@@ -1698,6 +1713,7 @@ public:
[[nodiscard]] uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
[[nodiscard]] bool IsSitState() const;
[[nodiscard]] bool IsStandState() const;
+ [[nodiscard]] bool IsStandUpOnMovementState() const;
void SetStandState(uint8 state);
void SetStandFlags(uint8 flags) { SetByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, flags); }
@@ -1988,7 +2004,7 @@ public:
//----------- Public variables ----------//
uint32 m_extraAttacks;
- bool m_canDualWield;
+ DualWieldMode _dualWieldMode;
ControlSet m_Controlled;
@@ -2121,8 +2137,6 @@ protected:
bool m_applyResilience;
bool _instantCast;
- uint32 m_rootTimes;
-
private:
bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo);
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, ProcEventInfo const& eventInfo);
diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h
index e6885fe162..1875a96aae 100644
--- a/src/server/game/Entities/Unit/UnitDefines.h
+++ b/src/server/game/Entities/Unit/UnitDefines.h
@@ -197,7 +197,7 @@ enum UnitState
UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_NO_ENVIRONMENT_UPD = 0x20000000,
- UNIT_STATE_NO_COMBAT_MOVEMENT, // serverside only - should never be changed outside of core and hence shouldnt have a defined static value and be at the end
+ UNIT_STATE_NO_COMBAT_MOVEMENT = 0x40000000, // serverside only - should not be changed outside the core and should be placed at the end
UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
| UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED
@@ -412,6 +412,8 @@ enum MovementFlags
MOVEMENTFLAG_MASK_PLAYER_ONLY =
MOVEMENTFLAG_FLYING,
+ MOVEMENTFLAG_MASK_MOVING_OR_TURN = MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING,
+
/// Movement flags that have change status opcodes associated for players
MOVEMENTFLAG_MASK_HAS_PLAYER_STATUS_OPCODE = MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_ROOT |
MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_WATERWALKING | MOVEMENTFLAG_FALLING_SLOW | MOVEMENTFLAG_HOVER
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 942eae9c54..64673eb3eb 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -949,12 +949,6 @@ void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recv_data*/)
GetPlayer()->GetCinematicMgr()->BeginCinematic();
}
-void WorldSession::HandleFeatherFallAck(WorldPacket& recv_data)
-{
- // no used
- recv_data.rfinish(); // prevent warnings spam
-}
-
void WorldSession::HandleSetActionBarToggles(WorldPacket& recv_data)
{
uint8 ActionBar;
@@ -1519,12 +1513,13 @@ void WorldSession::HandleCancelMountAuraOpcode(WorldPacket& /*recv_data*/)
_player->RemoveAurasByType(SPELL_AURA_MOUNTED);
}
-void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recv_data)
+void WorldSession::HandleMoveFlagChangeOpcode(WorldPacket& recv_data)
{
- // fly mode on/off
- LOG_DEBUG("network", "WORLD: CMSG_MOVE_SET_CAN_FLY_ACK");
+ LOG_DEBUG("network", "WORLD: {}", GetOpcodeNameForLogging((Opcodes)recv_data.GetOpcode()));
ObjectGuid guid;
+ uint32 counter;
+ uint32 isApplied;
recv_data >> guid.ReadAsPacked();
if (!_player)
@@ -1533,17 +1528,42 @@ void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recv_data)
return;
}
- recv_data.read_skip<uint32>(); // unk
+ recv_data >> counter;
MovementInfo movementInfo;
movementInfo.guid = guid;
ReadMovementInfo(recv_data, &movementInfo);
- recv_data.read_skip<float>(); // unk2
+ recv_data >> isApplied;
sScriptMgr->AnticheatSetCanFlybyServer(_player, movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY));
- _player->m_mover->m_movementInfo.flags = movementInfo.GetMovementFlags();
+ Unit* mover = _player->m_mover;
+ Player* plrMover = mover->ToPlayer();
+
+ mover->m_movementInfo.flags = movementInfo.GetMovementFlags();
+
+ Opcodes response;
+
+ switch (recv_data.GetOpcode())
+ {
+ case CMSG_MOVE_HOVER_ACK: response = MSG_MOVE_HOVER; break;
+ case CMSG_MOVE_FEATHER_FALL_ACK: response = MSG_MOVE_FEATHER_FALL; break;
+ case CMSG_MOVE_WATER_WALK_ACK: response = MSG_MOVE_WATER_WALK; break;
+ case CMSG_MOVE_SET_CAN_FLY_ACK: response = MSG_MOVE_UPDATE_CAN_FLY; break;
+ default: return;
+ }
+
+ if (!ProcessMovementInfo(movementInfo, mover, plrMover, recv_data))
+ {
+ recv_data.rfinish(); // prevent warnings spam
+ return;
+ }
+
+ WorldPacket data(response, 8);
+ data << guid.WriteAsPacked();
+ WriteMovementInfo(&data, &movementInfo);
+ _player->m_mover->SendMessageToSet(&data, _player);
}
void WorldSession::HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo& /*packet*/)
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 77b5bb43bd..443ac946c0 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -370,82 +370,44 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
movementInfo.guid = guid;
ReadMovementInfo(recvData, &movementInfo);
- // Stop emote on move
- if (Player* plrMover = mover->ToPlayer())
+ if (!ProcessMovementInfo(movementInfo, mover, plrMover, recvData))
{
- if (plrMover->GetUInt32Value(UNIT_NPC_EMOTESTATE) != EMOTE_ONESHOT_NONE && movementInfo.HasMovementFlag(MOVEMENTFLAG_MASK_MOVING))
- {
- plrMover->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- }
- }
-
- if (!movementInfo.pos.IsPositionValid())
- {
- if (plrMover)
- {
- sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
- }
-
recvData.rfinish(); // prevent warnings spam
return;
}
- if (!mover->movespline->Finalized())
- {
- recvData.rfinish(); // prevent warnings spam
- return;
- }
+ /* process position-change */
+ WorldPacket data(opcode, recvData.size());
+ movementInfo.guid = mover->GetGUID();
+ WriteMovementInfo(&data, &movementInfo);
+ mover->SendMessageToSet(&data, _player);
+}
- // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
- if (mover->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
+void WorldSession::SynchronizeMovement(MovementInfo& movementInfo)
+{
+ int64 movementTime = (int64)movementInfo.time + _timeSyncClockDelta;
+ if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF)
{
- // Xinef: skip moving packets
- if (movementInfo.HasMovementFlag(MOVEMENTFLAG_MASK_MOVING))
- {
- if (plrMover)
- {
- sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
- }
- return;
- }
- movementInfo.pos.Relocate(mover->GetPositionX(), mover->GetPositionY(), mover->GetPositionZ());
-
- if (mover->IsCreature())
- {
- movementInfo.transport.guid = mover->m_movementInfo.transport.guid;
- movementInfo.transport.pos.Relocate(mover->m_movementInfo.transport.pos.GetPositionX(), mover->m_movementInfo.transport.pos.GetPositionY(), mover->m_movementInfo.transport.pos.GetPositionZ());
- movementInfo.transport.seat = mover->m_movementInfo.transport.seat;
- }
+ LOG_INFO("misc", "The computed movement time using clockDelta is erronous. Using fallback instead");
+ movementInfo.time = getMSTime();
}
-
- if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
+ else
{
- // We were teleported, skip packets that were broadcast before teleport
- if (movementInfo.pos.GetExactDist2d(mover) > SIZE_OF_GRIDS)
- {
- if (plrMover)
- {
- sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
- //LOG_INFO("anticheat", "MovementHandler:: 2 We were teleported, skip packets that were broadcast before teleport");
- }
- recvData.rfinish(); // prevent warnings spam
- return;
- }
+ movementInfo.time = (uint32)movementTime;
+ }
+}
- if (!Acore::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(),
- movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation()))
- {
- if (plrMover)
- {
- sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
- }
+void WorldSession::HandleMoverRelocation(MovementInfo& movementInfo, Unit* mover)
+{
+ SynchronizeMovement(movementInfo);
- recvData.rfinish(); // prevent warnings spam
- return;
- }
+ mover->UpdatePosition(movementInfo.pos);
+ mover->m_movementInfo = movementInfo;
+ if (mover->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
+ {
// if we boarded a transport, add us to it
- if (plrMover)
+ if (Player* plrMover = mover->ToPlayer())
{
if (!plrMover->GetTransport())
{
@@ -483,44 +445,92 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
}
}
}
- else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave
+ else if (mover->IsPlayer())
{
- sScriptMgr->AnticheatSetUnderACKmount(plrMover); // just for safe
+ if (Player* plrMover = mover->ToPlayer())
+ {
+ if (plrMover->GetTransport()) // if we were on a transport, leave
+ {
+ sScriptMgr->AnticheatSetUnderACKmount(plrMover); // just for safe
- plrMover->m_transport->RemovePassenger(plrMover);
- plrMover->m_transport = nullptr;
- movementInfo.transport.Reset();
+ plrMover->m_transport->RemovePassenger(plrMover);
+ plrMover->m_transport = nullptr;
+ movementInfo.transport.Reset();
+ }
+ }
}
- // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
- if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight())
+ // Some vehicles allow the passenger to turn by himself
+ if (Vehicle* vehicle = mover->GetVehicle())
{
- plrMover->HandleFall(movementInfo);
-
- sScriptMgr->AnticheatSetJumpingbyOpcode(plrMover, false);
+ if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover))
+ {
+ if (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING && movementInfo.pos.GetOrientation() != mover->GetOrientation())
+ {
+ mover->SetOrientation(movementInfo.pos.GetOrientation());
+ mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
+ }
+ }
}
- // interrupt parachutes upon falling or landing in water
- if (opcode == MSG_MOVE_FALL_LAND || opcode == MSG_MOVE_START_SWIM)
+ if (Player* plrMover = mover->ToPlayer()) // nothing is charmed, or player charmed
{
- mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // Parachutes
+ if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING)))
+ plrMover->SetStandState(UNIT_STAND_STATE_STAND);
- if (plrMover)
+ if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()))
{
- sScriptMgr->AnticheatSetJumpingbyOpcode(plrMover, false);
+ if (!plrMover->GetBattleground() || !plrMover->GetBattleground()->HandlePlayerUnderMap(_player))
+ {
+ if (plrMover->IsAlive())
+ {
+ plrMover->SetPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS);
+ plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
+ // player can be alive if GM
+ if (plrMover->IsAlive())
+ plrMover->KillPlayer();
+ }
+ else if (!plrMover->HasPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS))
+ {
+ GraveyardStruct const* grave = sGraveyard->GetClosestGraveyard(plrMover, plrMover->GetTeamId());
+ if (grave)
+ {
+ plrMover->TeleportTo(grave->Map, grave->x, grave->y, grave->z, plrMover->GetOrientation());
+ plrMover->Relocate(grave->x, grave->y, grave->z, plrMover->GetOrientation());
+ }
+ }
+ }
}
}
+}
- if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater())
+bool WorldSession::VerifyMovementInfo(MovementInfo const& movementInfo, Player* plrMover, Unit* mover, Opcodes opcode) const
+{
+ if (!movementInfo.pos.IsPositionValid())
{
- // now client not include swimming flag in case jumping under water
- plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseMask(), movementInfo.pos.GetPositionX(),
- movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ(), plrMover->GetCollisionHeight()));
+ if (plrMover)
+ {
+ sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
+ }
+
+ return false;
}
- if (plrMover)//Hook for OnPlayerMove
+ if (!mover->movespline->Finalized())
+ return false;
+
+ // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
+ if (mover->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
{
- sScriptMgr->OnPlayerMove(plrMover, movementInfo, opcode);
+ // Xinef: skip moving packets
+ if (movementInfo.HasMovementFlag(MOVEMENTFLAG_MASK_MOVING))
+ {
+ if (plrMover)
+ {
+ sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
+ }
+ return false;
+ }
}
bool jumpopcode = false;
@@ -530,7 +540,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
if (plrMover && !sScriptMgr->AnticheatHandleDoubleJump(plrMover, mover))
{
plrMover->GetSession()->KickPlayer();
- return;
+ return false;
}
}
@@ -538,74 +548,98 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
if (plrMover && !sScriptMgr->AnticheatCheckMovementInfo(plrMover, movementInfo, mover, jumpopcode))
{
plrMover->GetSession()->KickPlayer();
- return;
+ return false;
}
- /* process position-change */
- WorldPacket data(opcode, recvData.size());
- int64 movementTime = (int64)movementInfo.time + _timeSyncClockDelta;
- if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF)
+ if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT))
{
- LOG_INFO("misc", "The computed movement time using clockDelta is erronous. Using fallback instead");
- movementInfo.time = getMSTime();
+ // We were teleported, skip packets that were broadcast before teleport
+ if (movementInfo.pos.GetExactDist2d(mover) > SIZE_OF_GRIDS)
+ {
+ if (plrMover)
+ {
+ sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
+ //LOG_INFO("anticheat", "MovementHandler:: 2 We were teleported, skip packets that were broadcast before teleport");
+ }
+ return false;
+ }
+
+ if (!Acore::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(),
+ movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation()))
+ {
+ if (plrMover)
+ {
+ sScriptMgr->AnticheatUpdateMovementInfo(plrMover, movementInfo);
+ }
+
+ return false;
+ }
}
- else
+ return true;
+}
+
+bool WorldSession::ProcessMovementInfo(MovementInfo& movementInfo, Unit* mover, Player* plrMover, WorldPacket& recvData)
+{
+ Opcodes opcode = (Opcodes)recvData.GetOpcode();
+ if (!VerifyMovementInfo(movementInfo, plrMover, mover, opcode))
+ return false;
+
+ if (mover->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
{
- movementInfo.time = (uint32) movementTime;
+ movementInfo.pos.Relocate(mover->GetPositionX(), mover->GetPositionY(), mover->GetPositionZ());
+
+ if (mover->IsCreature())
+ {
+ movementInfo.transport.guid = mover->m_movementInfo.transport.guid;
+ movementInfo.transport.pos.Relocate(mover->m_movementInfo.transport.pos.GetPositionX(), mover->m_movementInfo.transport.pos.GetPositionY(), mover->m_movementInfo.transport.pos.GetPositionZ());
+ movementInfo.transport.seat = mover->m_movementInfo.transport.seat;
+ }
}
- movementInfo.guid = mover->GetGUID();
- WriteMovementInfo(&data, &movementInfo);
- mover->SendMessageToSet(&data, _player);
+ // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
+ if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight())
+ {
+ plrMover->HandleFall(movementInfo);
- mover->m_movementInfo = movementInfo;
+ sScriptMgr->AnticheatSetJumpingbyOpcode(plrMover, false);
+ }
- // Some vehicles allow the passenger to turn by himself
- if (Vehicle* vehicle = mover->GetVehicle())
+ // interrupt parachutes upon falling or landing in water
+ if (opcode == MSG_MOVE_FALL_LAND || opcode == MSG_MOVE_START_SWIM)
{
- if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover))
+ mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // Parachutes
+
+ if (plrMover)
{
- if (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING && movementInfo.pos.GetOrientation() != mover->GetOrientation())
- {
- mover->SetOrientation(movementInfo.pos.GetOrientation());
- mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
- }
+ sScriptMgr->AnticheatSetJumpingbyOpcode(plrMover, false);
}
+ }
- return;
+ if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater())
+ {
+ // now client not include swimming flag in case jumping under water
+ plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseMask(), movementInfo.pos.GetPositionX(),
+ movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ(), plrMover->GetCollisionHeight()));
}
- mover->UpdatePosition(movementInfo.pos);
+ if (plrMover)//Hook for OnPlayerMove
+ {
+ sScriptMgr->OnPlayerMove(plrMover, movementInfo, opcode);
+ }
- if (plrMover) // nothing is charmed, or player charmed
+ if (movementInfo.GetMovementFlags() & MOVEMENTFLAG_MASK_MOVING_OR_TURN)
{
- if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING)))
- plrMover->SetStandState(UNIT_STAND_STATE_STAND);
+ if (mover->IsStandState())
+ mover->SetStandState(UNIT_STAND_STATE_STAND);
+ mover->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
+ }
+
+ HandleMoverRelocation(movementInfo, mover);
+ if (plrMover && opcode != CMSG_MOVE_KNOCK_BACK_ACK)
plrMover->UpdateFallInformationIfNeed(movementInfo, opcode);
- if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()))
- if (!plrMover->GetBattleground() || !plrMover->GetBattleground()->HandlePlayerUnderMap(_player))
- {
- if (plrMover->IsAlive())
- {
- plrMover->SetPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS);
- plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
- // player can be alive if GM
- if (plrMover->IsAlive())
- plrMover->KillPlayer();
- }
- else if (!plrMover->HasPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS))
- {
- GraveyardStruct const* grave = sGraveyard->GetClosestGraveyard(plrMover, plrMover->GetTeamId());
- if (grave)
- {
- plrMover->TeleportTo(grave->Map, grave->x, grave->y, grave->z, plrMover->GetOrientation());
- plrMover->Relocate(grave->x, grave->y, grave->z, plrMover->GetOrientation());
- }
- }
- }
- }
+ return true;
}
void WorldSession::HandleForceSpeedChangeAck(WorldPacket& recvData)
@@ -762,11 +796,13 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData)
{
LOG_DEBUG("network", "CMSG_MOVE_KNOCK_BACK_ACK");
+ Unit* mover = _player->m_mover;
+
ObjectGuid guid;
recvData >> guid.ReadAsPacked();
// pussywizard: typical check for incomming movement packets
- if (!_player->m_mover || !_player->m_mover->IsInWorld() || _player->m_mover->IsDuringRemoveFromWorld() || guid != _player->m_mover->GetGUID())
+ if (!mover || !mover->IsInWorld() || mover->IsDuringRemoveFromWorld() || guid != mover->GetGUID())
{
recvData.rfinish(); // prevent warnings spam
return;
@@ -778,7 +814,10 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData)
movementInfo.guid = guid;
ReadMovementInfo(recvData, &movementInfo);
- _player->m_mover->m_movementInfo = movementInfo;
+ mover->m_movementInfo = movementInfo;
+
+ if (mover->IsPlayer() && static_cast<Player*>(mover)->IsFreeFlying())
+ mover->SetCanFly(true);
WorldPacket data(MSG_MOVE_KNOCK_BACK, 66);
data << guid.WriteAsPacked();
@@ -793,38 +832,6 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData)
_player->SendMessageToSet(&data, false);
}
-void WorldSession::HandleMoveHoverAck(WorldPacket& recvData)
-{
- LOG_DEBUG("network", "CMSG_MOVE_HOVER_ACK");
-
- ObjectGuid guid;
- recvData >> guid.ReadAsPacked();
-
- recvData.read_skip<uint32>(); // unk
-
- MovementInfo movementInfo;
- movementInfo.guid = guid;
- ReadMovementInfo(recvData, &movementInfo);
-
- recvData.read_skip<uint32>(); // unk2
-}
-
-void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recvData)
-{
- LOG_DEBUG("network", "CMSG_MOVE_WATER_WALK_ACK");
-
- ObjectGuid guid;
- recvData >> guid.ReadAsPacked();
-
- recvData.read_skip<uint32>(); // unk
-
- MovementInfo movementInfo;
- movementInfo.guid = guid;
- ReadMovementInfo(recvData, &movementInfo);
-
- recvData.read_skip<uint32>(); // unk2
-}
-
void WorldSession::HandleSummonResponseOpcode(WorldPacket& recvData)
{
if (!_player->IsAlive() || _player->IsInCombat())
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index bc12f7764f..11b0a90057 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -374,7 +374,7 @@ void OpcodeTable::Initialize()
/*0x0F3*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_NORMAL_FALL, STATUS_NEVER);
/*0x0F4*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_HOVER, STATUS_NEVER);
/*0x0F5*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_UNSET_HOVER, STATUS_NEVER);
- /*0x0F6*/ DEFINE_HANDLER(CMSG_MOVE_HOVER_ACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMoveHoverAck );
+ /*0x0F6*/ DEFINE_HANDLER(CMSG_MOVE_HOVER_ACK, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMoveFlagChangeOpcode );
/*0x0F7*/ DEFINE_SERVER_OPCODE_HANDLER(MSG_MOVE_HOVER, STATUS_NEVER);
/*0x0F8*/ DEFINE_HANDLER(CMSG_TRIGGER_CINEMATIC_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
/*0x0F9*/ DEFINE_HANDLER(CMSG_OPENING_CINEMATIC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -847,8 +847,8 @@ void OpcodeTable::Initialize()
/*0x2CC*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_RAID_INSTANCE_INFO, STATUS_NEVER);
/*0x2CD*/ DEFINE_HANDLER(CMSG_REQUEST_RAID_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestRaidInfoOpcode );
/*0x2CE*/ DEFINE_HANDLER(CMSG_MOVE_TIME_SKIPPED, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveTimeSkippedOpcode );
- /*0x2CF*/ DEFINE_HANDLER(CMSG_MOVE_FEATHER_FALL_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleFeatherFallAck );
- /*0x2D0*/ DEFINE_HANDLER(CMSG_MOVE_WATER_WALK_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveWaterWalkAck );
+ /*0x2CF*/ DEFINE_HANDLER(CMSG_MOVE_FEATHER_FALL_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveFlagChangeOpcode );
+ /*0x2D0*/ DEFINE_HANDLER(CMSG_MOVE_WATER_WALK_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveFlagChangeOpcode );
/*0x2D1*/ DEFINE_HANDLER(CMSG_MOVE_NOT_ACTIVE_MOVER, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveNotActiveMover );
/*0x2D2*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAY_SOUND, STATUS_NEVER);
/*0x2D3*/ DEFINE_HANDLER(CMSG_BATTLEFIELD_STATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlefieldStatusOpcode );
@@ -965,7 +965,7 @@ void OpcodeTable::Initialize()
/*0x342*/ DEFINE_HANDLER(MSG_MOVE_STOP_SWIM_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
/*0x343*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_SET_CAN_FLY, STATUS_NEVER);
/*0x344*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOVE_UNSET_CAN_FLY, STATUS_NEVER);
- /*0x345*/ DEFINE_HANDLER(CMSG_MOVE_SET_CAN_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveSetCanFlyAckOpcode );
+ /*0x345*/ DEFINE_HANDLER(CMSG_MOVE_SET_CAN_FLY_ACK, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMoveFlagChangeOpcode );
/*0x346*/ DEFINE_HANDLER(CMSG_MOVE_SET_FLY, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes );
/*0x347*/ DEFINE_HANDLER(CMSG_SOCKET_GEMS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleSocketOpcode );
/*0x348*/ DEFINE_HANDLER(CMSG_ARENA_TEAM_CREATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index c8d336fe53..412d1a9f63 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -136,7 +136,8 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, s
_addonMessageReceiveCount(0),
_timeSyncClockDeltaQueue(6),
_timeSyncClockDelta(0),
- _pendingTimeSyncRequests()
+ _pendingTimeSyncRequests(),
+ _orderCounter(0)
{
memset(m_Tutorials, 0, sizeof(m_Tutorials));
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index c5fa3c8aee..8f0a8fd59d 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -379,6 +379,10 @@ public:
void ReadMovementInfo(WorldPacket& data, MovementInfo* mi);
void WriteMovementInfo(WorldPacket* data, MovementInfo* mi);
+ void SynchronizeMovement(MovementInfo& movementInfo);
+ void HandleMoverRelocation(MovementInfo& movementInfo, Unit* mover);
+ bool VerifyMovementInfo(MovementInfo const& movementInfo, Player* plrMover, Unit* mover, Opcodes opcode) const;
+ bool ProcessMovementInfo(MovementInfo& movementInfo, Unit* mover, Player* plrMover, WorldPacket& recvData);
void SendPacket(WorldPacket const* packet);
void SendPetNameInvalid(uint32 error, std::string const& name, DeclinedName* declinedName);
@@ -582,6 +586,10 @@ public:
// Time Synchronisation
void ResetTimeSync();
void SendTimeSync();
+
+ // Movement packet order
+ uint32 GetOrderCounter() const { return _orderCounter; }
+ void IncrementOrderCounter() { ++_orderCounter; }
public: // opcodes handlers
void Handle_NULL(WorldPacket& null); // not used
void Handle_EarlyProccess(WorldPacket& recvPacket); // just mark packets processed in WorldSocket::OnRead
@@ -620,11 +628,6 @@ public: // opcodes handlers
// new party stats
void HandleInspectHonorStatsOpcode(WorldPacket& recvPacket);
- void HandleMoveWaterWalkAck(WorldPacket& recvPacket);
- void HandleFeatherFallAck(WorldPacket& recvData);
-
- void HandleMoveHoverAck(WorldPacket& recvData);
-
void HandleMountSpecialAnimOpcode(WorldPacket& recvdata);
// character view
@@ -969,7 +972,7 @@ public: // opcodes handlers
void HandleFarSightOpcode(WorldPacket& recvData);
void HandleSetDungeonDifficultyOpcode(WorldPacket& recvData);
void HandleSetRaidDifficultyOpcode(WorldPacket& recvData);
- void HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData);
+ void HandleMoveFlagChangeOpcode(WorldPacket& recvData);
void HandleSetTitleOpcode(WorldPacket& recvData);
void HandleRealmSplitOpcode(WorldPacket& recvData);
void HandleTimeSyncResp(WorldPacket& recvData);
@@ -1237,6 +1240,8 @@ private:
uint32 _timeSyncNextCounter;
uint32 _timeSyncTimer;
+ uint32 _orderCounter;
+
WorldSession(WorldSession const& right) = delete;
WorldSession& operator=(WorldSession const& right) = delete;
};
diff --git a/src/server/scripts/Northrend/Gundrak/boss_moorabi.cpp b/src/server/scripts/Northrend/Gundrak/boss_moorabi.cpp
index a85172a2d6..e0be1dd9d9 100644
--- a/src/server/scripts/Northrend/Gundrak/boss_moorabi.cpp
+++ b/src/server/scripts/Northrend/Gundrak/boss_moorabi.cpp
@@ -83,6 +83,7 @@ public:
BossAI::Reset();
events2.Reset();
events2.ScheduleEvent(EVENT_PHANTOM, 21s);
+ me->SetCanDualWield(true);
}
void JustEngagedWith(Unit* who) override
@@ -104,6 +105,7 @@ public:
me->RemoveAurasDueToSpell(SPELL_MOJO_FRENZY);
events.CancelEvent(EVENT_TRANSFORMATION);
Talk(EMOTE_TRANSFORMED);
+ me->SetCanDualWield(false);
}
}
diff --git a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp
index 5e32259383..f8a87b2530 100644
--- a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp
+++ b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp
@@ -105,6 +105,9 @@ public:
break;
case GO_ECK_DOORS:
case GO_ECK_UNDERWATER_GATE:
+ if (instance->IsHeroic())
+ AddDoor(gameobject);
+ break;
case GO_GAL_DARAH_DOORS0:
case GO_GAL_DARAH_DOORS1:
case GO_GAL_DARAH_DOORS2:
@@ -119,6 +122,9 @@ public:
{
case GO_ECK_DOORS:
case GO_ECK_UNDERWATER_GATE:
+ if (instance->IsHeroic())
+ RemoveDoor(gameobject);
+ break;
case GO_GAL_DARAH_DOORS0:
case GO_GAL_DARAH_DOORS1:
case GO_GAL_DARAH_DOORS2:
diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp
index 35fd65e310..d43a29a5e9 100644
--- a/src/server/scripts/Northrend/zone_storm_peaks.cpp
+++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp
@@ -28,88 +28,90 @@
#include "Vehicle.h"
#include "WaypointMgr.h"
-enum qSniffing
+enum qSniffingOutThePerpetrator
{
+ NPC_FROSTHOUND = 29677,
SPELL_SUMMON_PURSUERS_PERIODIC = 54993,
SPELL_SNIFFING_CREDIT = 55477,
+ TALK_EMOTE_FROSTHOUND_SNIFF = 0,
+ TALK_SEEN = 1,
+ TALK_CONFRONT = 2,
+ TALK_EMOTE_TRACKED_COMPLETE = 3,
};
-class npc_frosthound : public CreatureScript
+struct npc_frosthound : public npc_escortAI
{
-public:
- npc_frosthound() : CreatureScript("npc_frosthound") { }
-
- struct npc_frosthoundAI : public npc_escortAI
- {
- npc_frosthoundAI(Creature* creature) : npc_escortAI(creature) {}
+ explicit npc_frosthound(Creature* creature) : npc_escortAI(creature), _summons(creature) {}
- void AttackStart(Unit* /*who*/) override {}
- void JustEngagedWith(Unit* /*who*/) override {}
- void EnterEvadeMode(EvadeReason /* why */) override {}
+ void AttackStart(Unit* /*who*/) override {}
+ void JustEngagedWith(Unit* /*who*/) override {}
+ void EnterEvadeMode(EvadeReason /* why */) override {}
+ void JustDied(Unit* /*killer*/) override { }
+ void OnCharmed(bool /*apply*/) override { }
- void PassengerBoarded(Unit* who, int8 /*seatId*/, bool apply) override
+ void PassengerBoarded(Unit* who, int8 /*seatId*/, bool apply) override
+ {
+ if (who->IsPlayer())
{
- if (who->IsPlayer())
+ if (apply)
{
- if (apply)
- {
- me->SetFaction(who->GetFaction());
- me->CastSpell(me, SPELL_SUMMON_PURSUERS_PERIODIC, true);
- Start(false, true, who->GetGUID());
- }
+ me->SetFaction(who->GetFaction());
+ me->CastSpell(me, SPELL_SUMMON_PURSUERS_PERIODIC, true);
+ Start(false, true, who->GetGUID());
+ Talk(TALK_EMOTE_FROSTHOUND_SNIFF, me);
}
}
+ }
- void JustDied(Unit* /*killer*/) override
- {
- }
-
- void OnCharmed(bool /*apply*/) override
- {
- }
+ void UpdateAI(uint32 diff) override
+ {
+ npc_escortAI::UpdateAI(diff);
- void UpdateAI(uint32 diff) override
- {
- npc_escortAI::UpdateAI(diff);
+ if (!UpdateVictim())
+ return;
+ }
- if (!UpdateVictim())
- return;
- }
+ void WaypointReached(uint32 waypointId) override
+ {
+ Player* player = GetPlayerForEscort();
+ if (!player)
+ return;
- void WaypointReached(uint32 waypointId) override
+ switch (waypointId)
{
- Player* player = GetPlayerForEscort();
- if (!player)
- return;
-
- switch (waypointId)
- {
- case 0:
- me->TextEmote("You've been seen! Use the net and Freezing elixir to keep the dwarves away!", nullptr, true);
- break;
- case 19:
- me->TextEmote("The frosthound has located the thief's hiding place. Confront him!", 0, true);
- if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
- summoner->ToPlayer()->KilledMonsterCredit(29677);
- break;
- }
+ case 0:
+ Talk(TALK_SEEN, player);
+ break;
+ case 34:
+ Talk(TALK_EMOTE_TRACKED_COMPLETE, me);
+ Talk(TALK_CONFRONT, player);
+ if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
+ summoner->ToPlayer()->KilledMonsterCredit(NPC_FROSTHOUND);
+ _summons.DespawnAll();
+ break;
+ default:
+ break;
}
+ }
- void JustSummoned(Creature* cr) override
- {
- cr->ToTempSummon()->SetTempSummonType(TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT);
- cr->ToTempSummon()->InitStats(20000);
- if (urand(0, 1))
- cr->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f);
- else if (cr->AI())
- cr->AI()->AttackStart(me);
- }
- };
+ void JustSummoned(Creature* cr) override
+ {
+ _summons.Summon(cr);
+ cr->ToTempSummon()->SetTempSummonType(TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT);
+ cr->ToTempSummon()->InitStats(20000);
+ if (urand(0, 1))
+ cr->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f);
+ else if (cr->AI())
+ cr->AI()->AttackStart(me);
+ }
- CreatureAI* GetAI(Creature* creature) const override
+ void Reset() override
{
- return new npc_frosthoundAI(creature);
+ _summons.DespawnAll();
}
+
+private:
+ SummonList _summons;
};
enum eIronWatcher
@@ -1194,7 +1196,7 @@ class spell_feed_stormcrest_eagle : public SpellScript
void AddSC_storm_peaks()
{
- new npc_frosthound();
+ RegisterCreatureAI(npc_frosthound);
new npc_iron_watcher();
new npc_time_lost_proto_drake();
new npc_wild_wyrm();