diff --git a/Source/Framework/Constants/PlayerConst.cs b/Source/Framework/Constants/PlayerConst.cs index c4031cac6..18f503f30 100644 --- a/Source/Framework/Constants/PlayerConst.cs +++ b/Source/Framework/Constants/PlayerConst.cs @@ -790,4 +790,12 @@ namespace Framework.Constants Normal = 0, NPE = 1 } + + public enum DuelState + { + Challenged, + Countdown, + InProgress, + Completed + } } diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 36db6a4eb..bdc980504 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -2080,7 +2080,7 @@ namespace Game.Entities return ReputationRank.Friendly; // duel - always hostile to opponent - if (selfPlayerOwner.duel != null && selfPlayerOwner.duel.opponent == targetPlayerOwner && selfPlayerOwner.duel.startTime != 0) + if (selfPlayerOwner.duel != null && selfPlayerOwner.duel.Opponent == targetPlayerOwner && selfPlayerOwner.duel.State == DuelState.InProgress) return ReputationRank.Hostile; // same group - checks dependant only on our faction - skip FFA_PVP for example @@ -2387,7 +2387,7 @@ namespace Game.Entities return false; if (playerAffectingAttacker && playerAffectingTarget) - if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.opponent == playerAffectingTarget && playerAffectingAttacker.duel.startTime != 0) + if (playerAffectingAttacker.duel != null && playerAffectingAttacker.duel.Opponent == playerAffectingTarget && playerAffectingAttacker.duel.State == DuelState.InProgress) return true; // PvP case - can't attack when attacker or target are in sanctuary diff --git a/Source/Game/Entities/Player/Player.Combat.cs b/Source/Game/Entities/Player/Player.Combat.cs index 665afdc80..eed907c1d 100644 --- a/Source/Game/Entities/Player/Player.Combat.cs +++ b/Source/Game/Entities/Player/Player.Combat.cs @@ -390,18 +390,16 @@ namespace Game.Entities void UpdateDuelFlag(long currTime) { - if (duel == null || duel.startTimer == 0 || currTime < duel.startTimer + 3) - return; + if (duel != null && duel.State == DuelState.Countdown && duel.StartTime <= currTime) + { + Global.ScriptMgr.OnPlayerDuelStart(this, duel.Opponent); - Global.ScriptMgr.OnPlayerDuelStart(this, duel.opponent); + SetDuelTeam(1); + duel.Opponent.SetDuelTeam(2); - SetDuelTeam(1); - duel.opponent.SetDuelTeam(2); - - duel.startTimer = 0; - duel.startTime = currTime; - duel.opponent.duel.startTimer = 0; - duel.opponent.duel.startTime = currTime; + duel.State = DuelState.InProgress; + duel.Opponent.duel.State = DuelState.InProgress; + } } void CheckDuelDistance(long currTime) @@ -414,11 +412,11 @@ namespace Game.Entities if (!obj) return; - if (duel.outOfBound == 0) + if (duel.OutOfBoundsTime == 0) { if (!IsWithinDistInMap(obj, 50)) { - duel.outOfBound = currTime; + duel.OutOfBoundsTime = currTime + 10; SendPacket(new DuelOutOfBounds()); } } @@ -426,10 +424,10 @@ namespace Game.Entities { if (IsWithinDistInMap(obj, 40)) { - duel.outOfBound = 0; + duel.OutOfBoundsTime = 0; SendPacket(new DuelInBounds()); } - else if (currTime >= (duel.outOfBound + 10)) + else if (currTime >= duel.OutOfBoundsTime) DuelComplete(DuelCompleteType.Fled); } } @@ -440,26 +438,27 @@ namespace Game.Entities return; // Check if DuelComplete() has been called already up in the stack and in that case don't do anything else here - if (duel.isCompleted || duel.opponent.duel.isCompleted) + if (duel.State == DuelState.Completed) return; - duel.isCompleted = true; - duel.opponent.duel.isCompleted = true; + Player opponent = duel.Opponent; + duel.State = DuelState.Completed; + opponent.duel.State = DuelState.Completed; - Log.outDebug(LogFilter.Player, "Duel Complete {0} {1}", GetName(), duel.opponent.GetName()); + Log.outDebug(LogFilter.Player, $"Duel Complete {GetName()} {opponent.GetName()}"); DuelComplete duelCompleted = new(); duelCompleted.Started = type != DuelCompleteType.Interrupted; SendPacket(duelCompleted); - if (duel.opponent.GetSession() != null) - duel.opponent.SendPacket(duelCompleted); + if (opponent.GetSession() != null) + opponent.SendPacket(duelCompleted); if (type != DuelCompleteType.Interrupted) { DuelWinner duelWinner = new(); - duelWinner.BeatenName = (type == DuelCompleteType.Won ? duel.opponent.GetName() : GetName()); - duelWinner.WinnerName = (type == DuelCompleteType.Won ? GetName() : duel.opponent.GetName()); + duelWinner.BeatenName = (type == DuelCompleteType.Won ? opponent.GetName() : GetName()); + duelWinner.WinnerName = (type == DuelCompleteType.Won ? GetName() : opponent.GetName()); duelWinner.BeatenVirtualRealmAddress = Global.WorldMgr.GetVirtualRealmAddress(); duelWinner.WinnerVirtualRealmAddress = Global.WorldMgr.GetVirtualRealmAddress(); duelWinner.Fled = type != DuelCompleteType.Won; @@ -467,41 +466,41 @@ namespace Game.Entities SendMessageToSet(duelWinner, true); } - duel.opponent.DisablePvpRules(); + opponent.DisablePvpRules(); DisablePvpRules(); - Global.ScriptMgr.OnPlayerDuelEnd(duel.opponent, this, type); + Global.ScriptMgr.OnPlayerDuelEnd(opponent, this, type); switch (type) { case DuelCompleteType.Fled: // if initiator and opponent are on the same team // or initiator and opponent are not PvP enabled, forcibly stop attacking - if (duel.initiator.GetTeam() == duel.opponent.GetTeam()) + if (GetTeam() == opponent.GetTeam()) { - duel.initiator.AttackStop(); - duel.opponent.AttackStop(); + AttackStop(); + opponent.AttackStop(); } else { - if (!duel.initiator.IsPvP()) - duel.initiator.AttackStop(); - if (!duel.opponent.IsPvP()) - duel.opponent.AttackStop(); + if (!IsPvP()) + AttackStop(); + if (!opponent.IsPvP()) + opponent.AttackStop(); } break; case DuelCompleteType.Won: UpdateCriteria(CriteriaType.LoseDuel, 1); - duel.opponent.UpdateCriteria(CriteriaType.WinDuel, 1); + opponent.UpdateCriteria(CriteriaType.WinDuel, 1); // Credit for quest Death's Challenge - if (GetClass() == Class.Deathknight && duel.opponent.GetQuestStatus(12733) == QuestStatus.Incomplete) - duel.opponent.CastSpell(duel.opponent, 52994, true); + if (GetClass() == Class.Deathknight && opponent.GetQuestStatus(12733) == QuestStatus.Incomplete) + opponent.CastSpell(duel.Opponent, 52994, true); // Honor points after duel (the winner) - ImpConfig int amount = WorldConfig.GetIntValue(WorldCfg.HonorAfterDuel); if (amount != 0) - duel.opponent.RewardHonor(null, 1, amount); + opponent.RewardHonor(null, 1, amount); break; default: @@ -510,41 +509,41 @@ namespace Game.Entities // Victory emote spell if (type != DuelCompleteType.Interrupted) - duel.opponent.CastSpell(duel.opponent, 52852, true); + opponent.CastSpell(duel.Opponent, 52852, true); //Remove Duel Flag object GameObject obj = GetMap().GetGameObject(m_playerData.DuelArbiter); if (obj) - duel.initiator.RemoveGameObject(obj, true); + duel.Initiator.RemoveGameObject(obj, true); //remove auras - var itsAuras = duel.opponent.GetAppliedAuras(); + var itsAuras = opponent.GetAppliedAuras(); foreach (var pair in itsAuras) { Aura aura = pair.Value.GetBase(); - if (!pair.Value.IsPositive() && aura.GetCasterGUID() == GetGUID() && aura.GetApplyTime() >= duel.startTime) - duel.opponent.RemoveAura(pair); + if (!pair.Value.IsPositive() && aura.GetCasterGUID() == GetGUID() && aura.GetApplyTime() >= duel.StartTime) + opponent.RemoveAura(pair); } var myAuras = GetAppliedAuras(); foreach (var pair in myAuras) { Aura aura = pair.Value.GetBase(); - if (!pair.Value.IsPositive() && aura.GetCasterGUID() == duel.opponent.GetGUID() && aura.GetApplyTime() >= duel.startTime) + if (!pair.Value.IsPositive() && aura.GetCasterGUID() == opponent.GetGUID() && aura.GetApplyTime() >= duel.StartTime) RemoveAura(pair); } // cleanup combo points ClearComboPoints(); - duel.opponent.ClearComboPoints(); + opponent.ClearComboPoints(); //cleanups SetDuelArbiter(ObjectGuid.Empty); SetDuelTeam(0); - duel.opponent.SetDuelArbiter(ObjectGuid.Empty); - duel.opponent.SetDuelTeam(0); + opponent.SetDuelArbiter(ObjectGuid.Empty); + opponent.SetDuelTeam(0); - duel.opponent.duel = null; + opponent.duel = null; duel = null; } public void SetDuelArbiter(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.DuelArbiter), guid); } @@ -578,7 +577,7 @@ namespace Game.Entities public void SetContestedPvP(Player attackedPlayer = null) { - if (attackedPlayer != null && (attackedPlayer == this || (duel != null && duel.opponent == attackedPlayer))) + if (attackedPlayer != null && (attackedPlayer == this || (duel != null && duel.Opponent == attackedPlayer))) return; SetContestedPvPTimer(30000); diff --git a/Source/Game/Entities/Player/Player.Fields.cs b/Source/Game/Entities/Player/Player.Fields.cs index a9f974796..4b5b816ef 100644 --- a/Source/Game/Entities/Player/Player.Fields.cs +++ b/Source/Game/Entities/Player/Player.Fields.cs @@ -421,15 +421,19 @@ namespace Game.Entities public class DuelInfo { - public Player initiator; - public Player opponent; - public long startTimer; - public long startTime; - public long outOfBound; - public bool isMounted; - public bool isCompleted; + public Player Opponent; + public Player Initiator; + public bool IsMounted; + public DuelState State; + public long StartTime; + public long OutOfBoundsTime; - public bool IsDueling() { return opponent != null; } + public DuelInfo(Player opponent, Player initiator, bool isMounted) + { + Opponent = opponent; + Initiator = initiator; + IsMounted = isMounted; + } } public class AccessRequirement diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 8f98bb056..8ddf68eef 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -4047,10 +4047,13 @@ namespace Game.Entities if (base.IsAlwaysDetectableFor(seer)) return true; + if (duel != null && duel.State != DuelState.Challenged && duel.Opponent == seer) + return false; + Player seerPlayer = seer.ToPlayer(); if (seerPlayer != null) if (IsGroupVisibleFor(seerPlayer)) - return !(seerPlayer.duel != null && seerPlayer.duel.startTime != 0 && seerPlayer.duel.opponent == this); + return true; return false; } diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index c157bc596..f561a1275 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -699,7 +699,7 @@ namespace Game.Entities Player myPlayerOwner = GetCharmerOrOwnerPlayerOrPlayerItself(); Player targetPlayerOwner = target.GetCharmerOrOwnerPlayerOrPlayerItself(); - if (myPlayerOwner && targetPlayerOwner && !(myPlayerOwner.duel != null && myPlayerOwner.duel.opponent == targetPlayerOwner)) + if (myPlayerOwner && targetPlayerOwner && !(myPlayerOwner.duel != null && myPlayerOwner.duel.Opponent == targetPlayerOwner)) { myPlayerOwner.UpdatePvP(true); myPlayerOwner.SetContestedPvP(targetPlayerOwner); @@ -879,7 +879,7 @@ namespace Game.Entities // last damage from non duel opponent or opponent controlled creature if (plrVictim.duel != null) { - plrVictim.duel.opponent.CombatStopWithPets(true); + plrVictim.duel.Opponent.CombatStopWithPets(true); plrVictim.CombatStopWithPets(true); plrVictim.DuelComplete(DuelCompleteType.Interrupted); } diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index f9167c9b7..71cbc5e3e 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -982,7 +982,7 @@ namespace Game.Entities Player player = ToPlayer(); // If the player is on mounted duel and exits the mount, he should immediatly lose the duel - if (player && player.duel != null && player.duel.isMounted) + if (player && player.duel != null && player.duel.IsMounted) player.DuelComplete(DuelCompleteType.Fled); SetControlled(false, UnitState.Root); // SMSG_MOVE_FORCE_UNROOT, ~MOVEMENTFLAG_ROOT @@ -2422,7 +2422,7 @@ namespace Game.Entities return 0; // prevent kill only if killed in duel and killed by opponent or opponent controlled creature - if (victim.ToPlayer().duel.opponent == attacker.GetControllingPlayer()) + if (victim.ToPlayer().duel.Opponent == attacker.GetControllingPlayer()) damage = health - 1; duel_hasEnded = true; @@ -2430,13 +2430,13 @@ namespace Game.Entities else if (victim.IsVehicle() && damage >= (health - 1) && victim.GetCharmer() != null && victim.GetCharmer().IsTypeId(TypeId.Player)) { Player victimRider = victim.GetCharmer().ToPlayer(); - if (victimRider != null && victimRider.duel != null && victimRider.duel.isMounted) + if (victimRider != null && victimRider.duel != null && victimRider.duel.IsMounted) { if (!attacker) return 0; // prevent kill only if killed in duel and killed by opponent or opponent controlled creature - if (victimRider.duel.opponent == attacker.GetControllingPlayer()) + if (victimRider.duel.Opponent == attacker.GetControllingPlayer()) damage = health - 1; duel_wasMounted = true; @@ -2657,7 +2657,7 @@ namespace Game.Entities else he.SetHealth(1); - he.duel.opponent.CombatStopWithPets(true); + he.duel.Opponent.CombatStopWithPets(true); he.CombatStopWithPets(true); he.CastSpell(he, 7267, true); // beg diff --git a/Source/Game/Handlers/DuelHandler.cs b/Source/Game/Handlers/DuelHandler.cs index 1ac0b20d3..5f74687ab 100644 --- a/Source/Game/Handlers/DuelHandler.cs +++ b/Source/Game/Handlers/DuelHandler.cs @@ -50,57 +50,57 @@ namespace Game void HandleDuelResponse(DuelResponse duelResponse) { if (duelResponse.Accepted && !duelResponse.Forfeited) - HandleDuelAccepted(); + HandleDuelAccepted(duelResponse.ArbiterGUID); else HandleDuelCancelled(); } - void HandleDuelAccepted() + void HandleDuelAccepted(ObjectGuid arbiterGuid) { - if (GetPlayer().duel == null) // ignore accept from duel-sender + Player player = GetPlayer(); + if (player.duel == null || player == player.duel.Initiator || player.duel.State != DuelState.Challenged) return; - Player player = GetPlayer(); - Player plTarget = player.duel.opponent; - - if (player == player.duel.initiator || !plTarget || player == plTarget || player.duel.startTime != 0 || plTarget.duel.startTime != 0) + Player target = player.duel.Opponent; + if (target.m_playerData.DuelArbiter != arbiterGuid) return; Log.outDebug(LogFilter.Network, "Player 1 is: {0} ({1})", player.GetGUID().ToString(), player.GetName()); - Log.outDebug(LogFilter.Network, "Player 2 is: {0} ({1})", plTarget.GetGUID().ToString(), plTarget.GetName()); + Log.outDebug(LogFilter.Network, "Player 2 is: {0} ({1})", target.GetGUID().ToString(), target.GetName()); long now = GameTime.GetGameTime(); - player.duel.startTimer = now; - plTarget.duel.startTimer = now; + player.duel.StartTime = now + 3; + target.duel.StartTime = now + 3; DuelCountdown packet = new(3000); player.SendPacket(packet); - plTarget.SendPacket(packet); + target.SendPacket(packet); player.EnablePvpRules(); - plTarget.EnablePvpRules(); + target.EnablePvpRules(); } void HandleDuelCancelled() { + Player player = GetPlayer(); + // no duel requested - if (GetPlayer().duel == null) + if (player.duel == null || player.duel.State == DuelState.Completed) return; // player surrendered in a duel using /forfeit - if (GetPlayer().duel.startTime != 0) + if (player.duel.State == DuelState.InProgress) { - GetPlayer().CombatStopWithPets(true); - if (GetPlayer().duel.opponent) - GetPlayer().duel.opponent.CombatStopWithPets(true); + player.CombatStopWithPets(true); + player.duel.Opponent.CombatStopWithPets(true); - GetPlayer().CastSpell(GetPlayer(), 7267, true); // beg - GetPlayer().DuelComplete(DuelCompleteType.Won); + player.CastSpell(GetPlayer(), 7267, true); // beg + player.DuelComplete(DuelCompleteType.Won); return; } - GetPlayer().DuelComplete(DuelCompleteType.Interrupted); + player.DuelComplete(DuelCompleteType.Interrupted); } } } diff --git a/Source/Game/Handlers/QueryHandler.cs b/Source/Game/Handlers/QueryHandler.cs index d6976426e..ff841ba01 100644 --- a/Source/Game/Handlers/QueryHandler.cs +++ b/Source/Game/Handlers/QueryHandler.cs @@ -16,7 +16,6 @@ */ using Framework.Constants; -using Framework.GameMath; using Framework.Realm; using Game.DataStorage; using Game.Entities; @@ -25,6 +24,7 @@ using Game.Misc; using Game.Networking; using Game.Networking.Packets; using System.Collections.Generic; +using System.Numerics; namespace Game { diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index b8c62eb16..040cbc80b 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -3299,21 +3299,9 @@ namespace Game.Spells target.SendPacket(packet); // create duel-info - DuelInfo duel = new(); - duel.initiator = caster; - duel.opponent = target; - duel.startTime = 0; - duel.startTimer = 0; - duel.isMounted = (GetSpellInfo().Id == 62875); // Mounted Duel - caster.duel = duel; - - DuelInfo duel2 = new(); - duel2.initiator = caster; - duel2.opponent = caster; - duel2.startTime = 0; - duel2.startTimer = 0; - duel2.isMounted = (GetSpellInfo().Id == 62875); // Mounted Duel - target.duel = duel2; + bool isMounted = (GetSpellInfo().Id == 62875); + caster.duel = new(target, caster, isMounted); + target.duel = new(caster, caster, isMounted); caster.SetDuelArbiter(go.GetGUID()); target.SetDuelArbiter(go.GetGUID()); diff --git a/Source/Scripts/World/Achievements.cs b/Source/Scripts/World/Achievements.cs index fa0dbb9c4..713f5576f 100644 --- a/Source/Scripts/World/Achievements.cs +++ b/Source/Scripts/World/Achievements.cs @@ -264,7 +264,7 @@ namespace Scripts.World.Achievements player.GetAreaId() == AreaIds.AreaRingOfHordeValiants || player.GetAreaId() == AreaIds.AreaRingOfChampions; - return checkArea && player.duel != null && player.duel.isMounted; + return checkArea && player.duel != null && player.duel.IsMounted; } }