diff --git a/Source/Framework/Constants/GroupConst.cs b/Source/Framework/Constants/GroupConst.cs index fc0a6357b..12950b38b 100644 --- a/Source/Framework/Constants/GroupConst.cs +++ b/Source/Framework/Constants/GroupConst.cs @@ -166,4 +166,11 @@ namespace Framework.Constants Max } + + public enum CountdownTimerType + { + Pvp = 0, + ChallengeMode = 1, + PlayerCountdown = 2 + } } diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 0f59ad47b..79db682b4 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -1888,13 +1888,6 @@ namespace Framework.Constants Max } - public enum TimerType - { - Pvp = 0, - ChallengerMode = 1, - PlayerCountdown = 2 - } - public enum GameError : uint { System = 0, diff --git a/Source/Game/BattleGrounds/BattleGround.cs b/Source/Game/BattleGrounds/BattleGround.cs index 6950820c4..a6c9db210 100644 --- a/Source/Game/BattleGrounds/BattleGround.cs +++ b/Source/Game/BattleGrounds/BattleGround.cs @@ -130,10 +130,7 @@ namespace Game.BattleGrounds // Update start time and reset stats timer SetElapsedTime(GetElapsedTime() + diff); if (GetStatus() == BattlegroundStatus.WaitJoin) - { m_ResetStatTimer += diff; - m_CountdownTimer += diff; - } PostUpdateImpl(diff); } @@ -278,26 +275,6 @@ namespace Game.BattleGrounds } } - // Send packet every 10 seconds until the 2nd field reach 0 - if (m_CountdownTimer >= 10000) - { - uint countdownMaxForBGType = IsArena() ? BattlegroundConst.ArenaCountdownMax : BattlegroundConst.BattlegroundCountdownMax; - - StartTimer timer = new(); - timer.Type = TimerType.Pvp; - timer.TimeLeft = countdownMaxForBGType - (GetElapsedTime() / 1000); - timer.TotalTime = countdownMaxForBGType; - - foreach (var guid in GetPlayers().Keys) - { - Player player = Global.ObjAccessor.FindPlayer(guid); - if (player != null) - player.SendPacket(timer); - } - - m_CountdownTimer = 0; - } - if (!m_Events.HasAnyFlag(BattlegroundEventFlags.Event1)) { m_Events |= BattlegroundEventFlags.Event1; @@ -316,6 +293,11 @@ namespace Game.BattleGrounds return; } + _preparationStartTime = GameTime.GetGameTime(); + foreach (Group group in m_BgRaids) + if (group != null) + group.StartCountdown(CountdownTimerType.Pvp, TimeSpan.FromSeconds((int)StartDelayTimes[BattlegroundConst.EventIdFirst] / 1000), _preparationStartTime); + StartingEventCloseDoors(); SetStartDelayTime(StartDelayTimes[BattlegroundConst.EventIdFirst]); // First start warning - 2 or 1 Minute @@ -1017,18 +999,8 @@ namespace Game.BattleGrounds else { if (GetStatus() == BattlegroundStatus.WaitJoin) // not started yet - { player.CastSpell(player, BattlegroundConst.SpellPreparation, true); // reduces all mana cost of spells. - uint countdownMaxForBGType = IsArena() ? BattlegroundConst.ArenaCountdownMax : BattlegroundConst.BattlegroundCountdownMax; - StartTimer timer = new(); - timer.Type = TimerType.Pvp; - timer.TimeLeft = countdownMaxForBGType - (GetElapsedTime() / 1000); - timer.TotalTime = countdownMaxForBGType; - - player.SendPacket(timer); - } - if (bp.Mercenary) { if (bp.Team == Team.Horde) @@ -1062,6 +1034,11 @@ namespace Game.BattleGrounds group = new Group(); SetBgRaid(team, group); group.Create(player); + TimeSpan countdownMaxForBGType = TimeSpan.FromSeconds((int)StartDelayTimes[BattlegroundConst.EventIdFirst] / 1000); + if (_preparationStartTime != 0) + group.StartCountdown(CountdownTimerType.Pvp, countdownMaxForBGType, _preparationStartTime); + else + group.StartCountdown(CountdownTimerType.Pvp, countdownMaxForBGType); } else // raid already exist { @@ -1952,7 +1929,6 @@ namespace Game.BattleGrounds BattlegroundStatus m_Status; uint m_ClientInstanceID; // the instance-id which is sent to the client and without any other internal use uint m_StartTime; - uint m_CountdownTimer; uint m_ResetStatTimer; uint m_ValidStartPositionTimer; int m_EndTime; // it is set to 120000 when bg is ending and it decreases itself @@ -1994,6 +1970,9 @@ namespace Game.BattleGrounds List _pvpStatIds = new(); List _playerPositions = new(); + + // Time when the first message "the battle will begin in 2minutes" is send (or 1m for arenas) + long _preparationStartTime; #endregion } diff --git a/Source/Game/Groups/Group.cs b/Source/Game/Groups/Group.cs index 2270f160b..48202581d 100644 --- a/Source/Game/Groups/Group.cs +++ b/Source/Game/Groups/Group.cs @@ -1394,6 +1394,25 @@ namespace Game.Groups } } + public void StartCountdown(CountdownTimerType timerType, TimeSpan duration, long? startTime = null) + { + if (timerType < 0 || (int)timerType >= _countdowns.Length) + return; + + if (_countdowns[(int)timerType] == null) + _countdowns[(int)timerType] = new(); + + _countdowns[(int)timerType].StartCountdown(duration, startTime); + } + + public CountdownInfo GetCountdownInfo(CountdownTimerType timerType) + { + if (timerType < 0 || (int)timerType >= _countdowns.Length) + return null; + + return _countdowns[(int)timerType]; + } + public void SetLootMethod(LootMethod method) { m_lootMethod = method; @@ -1927,7 +1946,7 @@ namespace Game.Groups { m_recentInstances[mapId] = Tuple.Create(instanceOwner, instanceId); } - + List m_memberSlots = new(); GroupRefManager m_memberMgr = new(); List m_invitees = new(); @@ -1961,6 +1980,8 @@ namespace Game.Groups // Raid markers RaidMarker[] m_markers = new RaidMarker[MapConst.RaidMarkersCount]; uint m_activeMarkers; + + CountdownInfo[] _countdowns = new CountdownInfo[3]; } public class MemberSlot @@ -1986,4 +2007,32 @@ namespace Game.Groups public WorldLocation Location; public ObjectGuid TransportGUID; } + + public class CountdownInfo + { + long _startTime; + long _endTime; + + public long GetTimeLeft() + { + return Math.Max(_endTime - GameTime.GetGameTime(), 0); + } + + public void StartCountdown(TimeSpan duration, long? startTime) + { + _startTime = startTime.HasValue ? startTime.Value : GameTime.GetGameTime(); + _endTime = (long)(_startTime + duration.TotalSeconds); + } + + public bool IsRunning() + { + return _endTime > GameTime.GetGameTime(); + } + + public long GetTotalTime() + { + return _endTime - _startTime; + } + } + } diff --git a/Source/Game/Handlers/MiscHandler.cs b/Source/Game/Handlers/MiscHandler.cs index 25893b222..a7eab1aeb 100644 --- a/Source/Game/Handlers/MiscHandler.cs +++ b/Source/Game/Handlers/MiscHandler.cs @@ -464,7 +464,26 @@ namespace Game splashScreenShowLatest.UISplashScreenID = splashScreen != null ? splashScreen.Id : 0; SendPacket(splashScreenShowLatest); } - + + [WorldPacketHandler(ClientOpcodes.QueryCountdownTimer, Processing = PacketProcessing.Inplace)] + void HandleQueryCountdownTimer(QueryCountdownTimer queryCountdownTimer) + { + Group group = _player.GetGroup(); + if (group == null) + return; + + CountdownInfo info = group.GetCountdownInfo(queryCountdownTimer.TimerType); + if (info == null) + return; + + StartTimer startTimer = new(); + startTimer.Type = queryCountdownTimer.TimerType; + startTimer.TimeLeft = info.GetTimeLeft(); + startTimer.TotalTime = info.GetTotalTime(); + + _player.SendPacket(startTimer); + } + [WorldPacketHandler(ClientOpcodes.ChatUnregisterAllAddonPrefixes)] void HandleUnregisterAllAddonPrefixes(ChatUnregisterAllAddonPrefixes packet) { diff --git a/Source/Game/Networking/Packets/MiscPackets.cs b/Source/Game/Networking/Packets/MiscPackets.cs index cde414b93..426be76c3 100644 --- a/Source/Game/Networking/Packets/MiscPackets.cs +++ b/Source/Game/Networking/Packets/MiscPackets.cs @@ -394,7 +394,7 @@ namespace Game.Networking.Packets public bool IsTournamentRealm; public bool XRealmPvpAlert; public bool BlockExitingLoadingScreen; // when set to true, sending SMSG_UPDATE_OBJECT with CreateObject Self bit = true will not hide loading screen - // instead it will be done after this packet is sent again with false in this bit and SMSG_UPDATE_OBJECT Values for player + // instead it will be done after this packet is sent again with false in this bit and SMSG_UPDATE_OBJECT Values for player public uint? RestrictedAccountMaxLevel; public ulong? RestrictedAccountMaxMoney; public uint? InstanceGroupSize; @@ -797,7 +797,7 @@ namespace Game.Networking.Packets public EnableBarberShop() : base(ServerOpcodes.EnableBarberShop) { } - public override void Write() + public override void Write() { _worldPacket.WriteUInt8(CustomizationScope); } @@ -1156,9 +1156,9 @@ namespace Game.Networking.Packets Enable = _worldPacket.HasBit(); } - public bool Enable; + public bool Enable; } - + class AccountHeirloomUpdate : ServerPacket { public AccountHeirloomUpdate() : base(ServerOpcodes.AccountHeirloomUpdate, ConnectionType.Instance) { } @@ -1277,7 +1277,19 @@ namespace Game.Networking.Packets public long TotalTime; public long TimeLeft; - public TimerType Type; + public CountdownTimerType Type; + } + + class QueryCountdownTimer : ClientPacket + { + public CountdownTimerType TimerType; + + public QueryCountdownTimer(WorldPacket packet) : base(packet) { } + + public override void Read() + { + TimerType = (CountdownTimerType)_worldPacket.ReadInt32(); + } } class ConversationLineStarted : ClientPacket @@ -1357,7 +1369,7 @@ namespace Game.Networking.Packets _worldPacket.FlushBits(); } } - + class DisplayGameError : ServerPacket { public DisplayGameError(GameError error) : base(ServerOpcodes.DisplayGameError) @@ -1441,7 +1453,7 @@ namespace Game.Networking.Packets public ObjectGuid SourceGuid; } - + //Structs struct PhaseShiftDataPhase {