diff options
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(); |