From 54e371e0e7fa2911eb02ff8a3c042d51e7d4ef5b Mon Sep 17 00:00:00 2001 From: UltraNix <80540499+UltraNix@users.noreply.github.com> Date: Thu, 8 Apr 2021 13:00:45 +0200 Subject: [PATCH] feat(Core/PvP): Set 30 sec timer before turn off FFA PvP flag. (#5090) --- src/server/game/Entities/Player/Player.cpp | 137 +++++++++++++------ src/server/game/Entities/Player/Player.h | 7 +- src/server/game/World/IWorld.h | 1 + src/server/game/World/World.cpp | 2 + src/server/worldserver/worldserver.conf.dist | 7 + 5 files changed, 109 insertions(+), 45 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 314f818df..a7542f65b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1617,6 +1617,7 @@ void Player::Update(uint32 p_time) time_t now = time(nullptr); UpdatePvPFlag(now); + UpdateFFAPvPFlag(now); UpdateContestedPvP(p_time); @@ -7730,28 +7731,8 @@ void Player::UpdateArea(uint32 newArea) m_areaUpdateId = newArea; AreaTableEntry const* area = sAreaTableStore.LookupEntry(newArea); - bool oldFFAPvPArea = pvpInfo.IsInFFAPvPArea; pvpInfo.IsInFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA); - UpdatePvPState(true); - - // xinef: check if we were in ffa arena and we left - if (oldFFAPvPArea && !pvpInfo.IsInFFAPvPArea) - { - // xinef: iterate attackers - AttackerSet toRemove; - AttackerSet const& attackers = getAttackers(); - for (AttackerSet::const_iterator itr = attackers.begin(); itr != attackers.end(); ++itr) - if (!(*itr)->IsValidAttackTarget(this)) - toRemove.insert(*itr); - - for (AttackerSet::const_iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) - (*itr)->AttackStop(); - - // xinef: remove our own victim - if (Unit* victim = GetVictim()) - if (!IsValidAttackTarget(victim)) - AttackStop(); - } + UpdateFFAPvPState(false); UpdateAreaDependentAuras(newArea); @@ -21158,18 +21139,50 @@ void Player::UpdatePvPFlag(time_t currTime) { if (!IsPvP()) return; + if (pvpInfo.EndTimer == 0 || pvpInfo.IsHostile) return; + if (currTime < (pvpInfo.EndTimer + 300 + 5)) { if (currTime > (pvpInfo.EndTimer + 4) && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER)) SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER); + return; } UpdatePvP(false); } +void Player::UpdateFFAPvPFlag(time_t currTime) +{ + if (!IsFFAPvP() || sWorld->IsFFAPvPRealm() || !pvpInfo.FFAPvPEndTimer || currTime < pvpInfo.FFAPvPEndTimer + 30) + { + return; + } + + pvpInfo.FFAPvPEndTimer = time_t(0); + + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + + // xinef: iterate attackers + AttackerSet toRemove; + AttackerSet const& attackers = getAttackers(); + for (AttackerSet::const_iterator itr = attackers.begin(); itr != attackers.end(); ++itr) + if (!(*itr)->IsValidAttackTarget(this)) + toRemove.insert(*itr); + + for (AttackerSet::const_iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) + (*itr)->AttackStop(); + + // xinef: remove our own victim + if (Unit* victim = GetVictim()) + if (!IsValidAttackTarget(victim)) + AttackStop(); +} + void Player::UpdateDuelFlag(time_t currTime) { if (!duel || duel->startTimer == 0 || currTime < duel->startTimer + 3) @@ -22747,29 +22760,9 @@ void Player::UpdateHomebindTime(uint32 time) } } -void Player::UpdatePvPState(bool onlyFFA) +void Player::UpdatePvPState() { - // TODO: should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled? - // no, we shouldn't, those are checked for affecting player by client - if (!pvpInfo.IsInNoPvPArea && !IsGameMaster() - && (pvpInfo.IsInFFAPvPArea || sWorld->IsFFAPvPRealm())) - { - if (!IsFFAPvP()) - { - SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); - for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); - } - } - else if (IsFFAPvP()) - { - RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); - for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); - } - - if (onlyFFA) - return; + UpdateFFAPvPState(); if (pvpInfo.IsHostile) // in hostile area { @@ -22783,6 +22776,63 @@ void Player::UpdatePvPState(bool onlyFFA) } } +void Player::UpdateFFAPvPState(bool reset /*= true*/) +{ + // TODO: should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled? + // no, we shouldn't, those are checked for affecting player by client + if (!pvpInfo.IsInNoPvPArea && !IsGameMaster() && (pvpInfo.IsInFFAPvPArea || sWorld->IsFFAPvPRealm())) + { + if (!IsFFAPvP()) + { + SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + (*itr)->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + } + + if (pvpInfo.IsInFFAPvPArea) + { + pvpInfo.FFAPvPEndTimer = time_t(0); + } + } + else if (IsFFAPvP()) + { + if ((pvpInfo.IsInNoPvPArea || IsGameMaster()) || reset || !pvpInfo.EndTimer) + { + pvpInfo.FFAPvPEndTimer = time_t(0); + + RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + + // xinef: iterate attackers + AttackerSet toRemove; + AttackerSet const& attackers = getAttackers(); + for (AttackerSet::const_iterator itr = attackers.begin(); itr != attackers.end(); ++itr) + if (!(*itr)->IsValidAttackTarget(this)) + toRemove.insert(*itr); + + for (AttackerSet::const_iterator itr = toRemove.begin(); itr != toRemove.end(); ++itr) + (*itr)->AttackStop(); + + // xinef: remove our own victim + if (Unit* victim = GetVictim()) + if (!IsValidAttackTarget(victim)) + AttackStop(); + } + else + { + // Not in FFA PvP Area + // Not FFA PvP realm + // Not FFA PvP timer already set + // Being recently in PvP combat + if (!pvpInfo.IsInFFAPvPArea && !sWorld->IsFFAPvPRealm() && !pvpInfo.FFAPvPEndTimer) + { + pvpInfo.FFAPvPEndTimer = sWorld->GetGameTime() + sWorld->getIntConfig(CONFIG_FFA_PVP_TIMER); + } + } + } +} + void Player::UpdatePvP(bool state, bool _override) { if (!state || _override) @@ -22795,6 +22845,7 @@ void Player::UpdatePvP(bool state, bool _override) pvpInfo.EndTimer = time(nullptr); SetPvP(state); } + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 56c2031ac..366e12633 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -309,7 +309,8 @@ struct PvPInfo bool IsInHostileArea{false}; ///> Marks if player is in an area which forces PvP flag bool IsInNoPvPArea{false}; ///> Marks if player is in a sanctuary or friendly capital city bool IsInFFAPvPArea{false}; ///> Marks if player is in an FFAPvP area (such as Gurubashi Arena) - time_t EndTimer{0}; ///> Time when player unflags himself for PvP (flag removed after 5 minutes) + time_t EndTimer{0}; ///> Time when player unflags himself for PvP (flag removed after 5 minutes) + time_t FFAPvPEndTimer{0}; ///> Time when player unflags himself for FFA PvP (flag removed after 30 sec) }; struct DuelInfo @@ -1843,7 +1844,8 @@ public: bool IsActionButtonDataValid(uint8 button, uint32 action, uint8 type); PvPInfo pvpInfo; - void UpdatePvPState(bool onlyFFA = false); + void UpdatePvPState(); + void UpdateFFAPvPState(bool reset = true); void SetPvP(bool state) { Unit::SetPvP(state); @@ -1864,6 +1866,7 @@ public: void UpdateAfkReport(time_t currTime); void UpdatePvPFlag(time_t currTime); + void UpdateFFAPvPFlag(time_t currTime); void UpdateContestedPvP(uint32 currTime); void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;} void ResetContestedPvP() diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 3b5eecbac..51cd34538 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -378,6 +378,7 @@ enum WorldIntConfigs CONFIG_TOGGLE_XP_COST, CONFIG_NPC_EVADE_IF_NOT_REACHABLE, CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID, + CONFIG_FFA_PVP_TIMER, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 1e9293212..92577472d 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1240,6 +1240,8 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_ITEMDELETE_QUALITY] = sConfigMgr->GetOption("ItemDelete.Quality", 3); m_int_configs[CONFIG_ITEMDELETE_ITEM_LEVEL] = sConfigMgr->GetOption("ItemDelete.ItemLevel", 80); + m_int_configs[CONFIG_FFA_PVP_TIMER] = sConfigMgr->GetOption("FFAPvPTimer", 30); + ///- Read the "Data" directory from the config file std::string dataPath = sConfigMgr->GetOption("DataDir", "./"); if (dataPath.empty() || (dataPath.at(dataPath.length() - 1) != '/' && dataPath.at(dataPath.length() - 1) != '\\')) diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index e9dcd62db..4e7cd8b6f 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3680,6 +3680,13 @@ ICC.Buff.Alliance = 73828 Item.SetItemTradeable = 1 +# +# FFAPvPTimer +# Description: Specify time offset when player unset FFAPvP flag when leaving FFAPvP area. (e.g. Gurubashi Arena) +# Default: 30 sec + +FFAPvPTimer = 30 + # ###################################################################################################