From 5e83cd2230e3dc9dfa70f15ade0a07f0acfe9cc8 Mon Sep 17 00:00:00 2001 From: abnerfs Date: Sat, 16 Mar 2024 21:58:40 -0300 Subject: [PATCH] Add map cooldown and hud menu for votemap --- Config.cs | 8 +++++++ Core/EndMapVoteManager.cs | 7 ++++-- Core/MapCooldown.cs | 42 +++++++++++++++++++++++++++++++++++ Features/NominationCommand.cs | 16 +++++++++---- Features/VotemapCommand.cs | 34 ++++++++++++++++++++++++---- Plugin.cs | 2 +- README.md | 5 +++++ lang/en.json | 3 ++- lang/fr.json | 7 +++--- lang/hu.json | 3 ++- lang/lv.json | 3 ++- lang/pl.json | 3 ++- lang/pt-BR.json | 3 ++- lang/ru.json | 3 ++- lang/tr.json | 3 ++- lang/ua.json | 3 ++- lang/zh-Hans.json | 3 ++- 17 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 Core/MapCooldown.cs diff --git a/Config.cs b/Config.cs index 85ec767..5e06bb3 100644 --- a/Config.cs +++ b/Config.cs @@ -1,4 +1,5 @@ using CounterStrikeSharp.API.Core; +using System.Text.Json.Serialization; namespace cs2_rockthevote { @@ -60,6 +61,7 @@ public class VotemapConfig : ICommandConfig, IVoteConfig public bool EnabledInWarmup { get; set; } = true; public int MinPlayers { get; set; } = 0; public int MinRounds { get; set; } = 0; + public bool HudMenu { get; set; } = false; } public class TimeleftConfig @@ -73,6 +75,11 @@ public class NextmapConfig } + public class MapCoolDownConfig + { + + } + public class Config : IBasePluginConfig { public int Version { get; set; } = 9; @@ -81,5 +88,6 @@ public class Config : IBasePluginConfig public EndOfMapConfig EndOfMapVote { get; set; } = new(); public TimeleftConfig Timeleft { get; set; } = new(); public NextmapConfig Nextmap { get; set; } = new(); + public ushort MapsInCoolDown { get; set; } = 3; } } diff --git a/Core/EndMapVoteManager.cs b/Core/EndMapVoteManager.cs index a8ef2b3..bb5f766 100644 --- a/Core/EndMapVoteManager.cs +++ b/Core/EndMapVoteManager.cs @@ -2,6 +2,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Timers; +using cs2_rockthevote.Core; using System.Data; using System.Text; using static CounterStrikeSharp.API.Core.Listeners; @@ -26,13 +27,14 @@ namespace cs2_rockthevote public class EndMapVoteManager : IPluginDependency { const int MAX_OPTIONS_HUD_MENU = 6; - public EndMapVoteManager(MapLister mapLister, ChangeMapManager changeMapManager, NominationCommand nominationManager, StringLocalizer localizer, PluginState pluginState) + public EndMapVoteManager(MapLister mapLister, ChangeMapManager changeMapManager, NominationCommand nominationManager, StringLocalizer localizer, PluginState pluginState, MapCooldown mapCooldown) { _mapLister = mapLister; _changeMapManager = changeMapManager; _nominationManager = nominationManager; _localizer = localizer; _pluginState = pluginState; + _mapCooldown = mapCooldown; } private readonly MapLister _mapLister; @@ -40,6 +42,7 @@ public EndMapVoteManager(MapLister mapLister, ChangeMapManager changeMapManager, private readonly NominationCommand _nominationManager; private readonly StringLocalizer _localizer; private PluginState _pluginState; + private MapCooldown _mapCooldown; private Timer? Timer; Dictionary Votes = new(); @@ -182,7 +185,7 @@ public void StartVote(IEndOfMapConfig config) if (config.HudMenu && mapsToShow > MAX_OPTIONS_HUD_MENU) mapsToShow = MAX_OPTIONS_HUD_MENU; - var mapsScrambled = Shuffle(new Random(), _mapLister.Maps!.Select(x => x.Name).Where(x => x != Server.MapName).ToList()); + var mapsScrambled = Shuffle(new Random(), _mapLister.Maps!.Select(x => x.Name).Where(x => x != Server.MapName && !_mapCooldown.IsMapInCooldown(x)).ToList()); mapsEllected = _nominationManager.NominationWinners().Concat(mapsScrambled).Distinct().ToList(); _canVote = ServerManager.ValidPlayerCount(); diff --git a/Core/MapCooldown.cs b/Core/MapCooldown.cs new file mode 100644 index 0000000..b1f1e5b --- /dev/null +++ b/Core/MapCooldown.cs @@ -0,0 +1,42 @@ +using CounterStrikeSharp.API; + +namespace cs2_rockthevote.Core +{ + public class MapCooldown : IPluginDependency + { + List mapsOnCoolDown = new(); + private ushort InCoolDown = 0; + + public event EventHandler? EventCooldownRefreshed; + + public MapCooldown(MapLister mapLister) + { + //this is called on map start + mapLister.EventMapsLoaded += (e, maps) => + { + var map = Server.MapName; + if (InCoolDown == 0) + { + mapsOnCoolDown.Clear(); + return; + } + + mapsOnCoolDown.Add(map.Trim().ToLower()); + if (mapsOnCoolDown.Count > InCoolDown) + mapsOnCoolDown.RemoveAt(0); + + EventCooldownRefreshed?.Invoke(this, maps); + }; + } + + public void OnConfigParsed(Config config) + { + InCoolDown = config.MapsInCoolDown; + } + + public bool IsMapInCooldown(string map) + { + return mapsOnCoolDown.IndexOf(map) > -1; + } + } +} diff --git a/Features/NominationCommand.cs b/Features/NominationCommand.cs index b5f58f1..fc0b05d 100644 --- a/Features/NominationCommand.cs +++ b/Features/NominationCommand.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Menu; +using cs2_rockthevote.Core; namespace cs2_rockthevote { @@ -33,15 +34,17 @@ public class NominationCommand : IPluginDependency private GameRules _gamerules; private StringLocalizer _localizer; private PluginState _pluginState; + private MapCooldown _mapCooldown; private MapLister _mapLister; - public NominationCommand(MapLister mapLister, GameRules gamerules, StringLocalizer localizer, PluginState pluginState) + public NominationCommand(MapLister mapLister, GameRules gamerules, StringLocalizer localizer, PluginState pluginState, MapCooldown mapCooldown) { _mapLister = mapLister; - _mapLister.EventMapsLoaded += OnMapsLoaded; _gamerules = gamerules; _localizer = localizer; _pluginState = pluginState; + _mapCooldown = mapCooldown; + _mapCooldown.EventCooldownRefreshed += OnMapsLoaded; } @@ -55,7 +58,6 @@ public void OnConfigParsed(Config config) _config = config.Rtv; } - public void OnMapsLoaded(object? sender, Map[] maps) { nominationMenu = new("Nomination"); @@ -64,7 +66,7 @@ public void OnMapsLoaded(object? sender, Map[] maps) nominationMenu.AddMenuOption(map.Name, (CCSPlayerController player, ChatMenuOption option) => { Nominate(player, option.Text); - }); + }, _mapCooldown.IsMapInCooldown(map.Name)); } } @@ -123,6 +125,12 @@ void Nominate(CCSPlayerController player, string map) return; } + if (_mapCooldown.IsMapInCooldown(map)) + { + player!.PrintToChat(_localizer.LocalizeWithPrefix("general.validation.map-played-recently")); + return; + } + if (_mapLister.Maps!.Select(x => x.Name).FirstOrDefault(x => x.ToLower() == map) is null) { player!.PrintToChat(_localizer.LocalizeWithPrefix("general.invalid-map")); diff --git a/Features/VotemapCommand.cs b/Features/VotemapCommand.cs index ba79d86..be1dcff 100644 --- a/Features/VotemapCommand.cs +++ b/Features/VotemapCommand.cs @@ -1,8 +1,10 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Core.Logging; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Menu; +using cs2_rockthevote.Core; using Microsoft.Extensions.Localization; namespace cs2_rockthevote @@ -29,21 +31,25 @@ public class VotemapCommand : IPluginDependency { Dictionary VotedMaps = new(); ChatMenu? votemapMenu = null; + CenterHtmlMenu? votemapMenuHud = null; private VotemapConfig _config = new(); private GameRules _gamerules; private StringLocalizer _localizer; private ChangeMapManager _changeMapManager; private PluginState _pluginState; + private MapCooldown _mapCooldown; private MapLister _mapLister; + private Plugin? _plugin; - public VotemapCommand(MapLister mapLister, GameRules gamerules, IStringLocalizer stringLocalizer, ChangeMapManager changeMapManager, PluginState pluginState) + public VotemapCommand(MapLister mapLister, GameRules gamerules, IStringLocalizer stringLocalizer, ChangeMapManager changeMapManager, PluginState pluginState, MapCooldown mapCooldown) { _mapLister = mapLister; - _mapLister.EventMapsLoaded += OnMapsLoaded; _gamerules = gamerules; _localizer = new StringLocalizer(stringLocalizer, "votemap.prefix"); _changeMapManager = changeMapManager; _pluginState = pluginState; + _mapCooldown = mapCooldown; + _mapCooldown.EventCooldownRefreshed += OnMapsLoaded; } public void OnMapStart(string map) @@ -60,12 +66,18 @@ public void OnConfigParsed(Config config) public void OnMapsLoaded(object? sender, Map[] maps) { votemapMenu = new("Votemap"); + votemapMenuHud = new("VoteMap"); foreach (var map in _mapLister.Maps!.Where(x => x.Name != Server.MapName)) { votemapMenu.AddMenuOption(map.Name, (CCSPlayerController player, ChatMenuOption option) => { AddVote(player, option.Text); - }); + }, _mapCooldown.IsMapInCooldown(map.Name)); + + votemapMenuHud.AddMenuOption(map.Name, (CCSPlayerController player, ChatMenuOption option) => + { + AddVote(player, option.Text); + }, _mapCooldown.IsMapInCooldown(map.Name)); } } @@ -113,7 +125,10 @@ public void CommandHandler(CCSPlayerController? player, string map) public void OpenVotemapMenu(CCSPlayerController player) { - MenuManager.OpenChatMenu(player!, votemapMenu!); + if (_config.HudMenu) + MenuManager.OpenCenterHtmlMenu(_plugin, player, votemapMenuHud!); + else + MenuManager.OpenChatMenu(player, votemapMenu!); } void AddVote(CCSPlayerController player, string map) @@ -124,6 +139,12 @@ void AddVote(CCSPlayerController player, string map) return; } + if (_mapCooldown.IsMapInCooldown(map)) + { + player!.PrintToChat(_localizer.LocalizeWithPrefix("general.validation.map-played-recently")); + return; + } + if (_mapLister.Maps!.FirstOrDefault(x => x.Name.ToLower() == map) is null) { player!.PrintToChat(_localizer.LocalizeWithPrefix("general.invalid-map")); @@ -164,5 +185,10 @@ public void PlayerDisconnected(CCSPlayerController player) foreach (var map in VotedMaps) map.Value.RemoveVote(userId); } + + public void OnLoad(Plugin plugin) + { + _plugin = plugin; + } } } diff --git a/Plugin.cs b/Plugin.cs index 861f357..a5f5762 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -21,7 +21,7 @@ public void ConfigureServices(IServiceCollection serviceCollection) public partial class Plugin : BasePlugin, IPluginConfig { public override string ModuleName => "RockTheVote"; - public override string ModuleVersion => "1.7.7"; + public override string ModuleVersion => "1.8.0"; public override string ModuleAuthor => "abnerfs"; public override string ModuleDescription => "General purpose map voting plugin"; diff --git a/README.md b/README.md index 4e88085..29c1502 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,11 @@ Feeling like paying me a ☕? Go ahead and donate: - Changes in the config file will require you to reload the plugin or restart the server (change the map won't work). - Maps that will be used in RTV/nominate/votemap/end of map vote are located in addons/counterstrikesharp/configs/plugins/RockTheVote/maplist.txt +## General config +| Config | Description | Default Value | Min | Max | +| -------------- | -------------------------------------------------------------------------------- | ------------- | --- | --- | +| MapsInCoolDown | Number of maps that can't be used in vote because they have been played recently | 3 | 0 | | + ## RockTheVote Players can type rtv to request the map to be changed, once a number of votes is reached (by default 60% of players in the server) a vote will start for the next map, this vote lasts up to 30 seconds (hardcoded for now), in the end server changes to the winner map. diff --git a/lang/en.json b/lang/en.json index 65debcb..37461b8 100644 --- a/lang/en.json +++ b/lang/en.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Vote for the next map: {0}s", "emv.hud.finished": "Vote finished, next map: {0}", "nextmap": "Next map will be {green}{0}", - "nextmap.decided-by-vote": "Next map will be decided by vote" + "nextmap.decided-by-vote": "Next map will be decided by vote", + "general.validation.map-played-recently": "Map has been played recently" } diff --git a/lang/fr.json b/lang/fr.json index 5732c73..abf41f8 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -27,14 +27,15 @@ "rtv.already-rocked-the-vote": "Vous avez déjà lancé le vote", "rtv.votes-reached": "Nombre de votes atteints, lancement du vote...", "rtv.disabled": "Rtv est désactivé pour le moment", - "emv.you-voted": "Vous avez voté pour {0}", + "emv.you-voted": "Vous avez voté pour {0}", "emv.vote-ended": "Le vote est terminé, la prochaine carte sera {green}{0}{default} ({1:N2}% de {2} vote(s))", - "emv.vote-ended-no-votes": "Pas de votes, la prochaine carte sera {green}{0}", + "emv.vote-ended-no-votes": "Pas de votes, la prochaine carte sera {green}{0}", "general.changing-map": "Changement de carte vers {green}{0}", "general.changing-map-next-round": "La carte changera pour {green}{0}{default} au prochain tour...", "emv.hud.menu-title": "Votez pour la prochaine carte :", "emv.hud.hud-timer": "Votez pour la prochaine carte : {0}s", "emv.hud.finished": "Vote terminé, prochaine carte : {0}", "nextmap": "La prochaine carte sera {green}{0}", - "nextmap.decided-by-vote": "La prochaine carte sera choisie par vote" + "nextmap.decided-by-vote": "La prochaine carte sera choisie par vote", + "general.validation.map-played-recently": "La carte a été récemment jouée" } diff --git a/lang/hu.json b/lang/hu.json index 90ae91c..cf73690 100644 --- a/lang/hu.json +++ b/lang/hu.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Szavazz a következő pályára: {0}mp", "emv.hud.finished": "A szavazás végetért, a köv. pálya: {0}", "nextmap": "A köv. pálya ez lesz: {green}{0}", - "nextmap.decided-by-vote": "A következő pálya majd az automatikus szavazás útján fog eldőlni" + "nextmap.decided-by-vote": "A következő pálya majd az automatikus szavazás útján fog eldőlni", + "general.validation.map-played-recently": "A térkép nemrégiben játszották" } diff --git a/lang/lv.json b/lang/lv.json index 85645be..8815f70 100644 --- a/lang/lv.json +++ b/lang/lv.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Balso par nākošo karti: {0}s", "emv.hud.finished": "Balsošana beidzās, nākošā karte: {0}", "nextmap": "Nākošā karte būs {green}{0}", - "nextmap.decided-by-vote": "Nākošā karte tiks izvēlētā pēc balsošanas" + "nextmap.decided-by-vote": "Nākošā karte tiks izvēlētā pēc balsošanas", + "general.validation.map-played-recently": "A térkép nemrégiben játszották" } diff --git a/lang/pl.json b/lang/pl.json index 5158721..ed8a63a 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Głosuj na następną mapę: {0}s", "emv.hud.finished": "Głosowanie zakończone, następna mapa: {0}", "nextmap": "Następną mapą będzie {green}{0}", - "nextmap.decided-by-vote": "Następna mapa zostanie wybrana głosowaniem" + "nextmap.decided-by-vote": "Następna mapa zostanie wybrana głosowaniem", + "general.validation.map-played-recently": "Mapa została niedawno rozegrana" } diff --git a/lang/pt-BR.json b/lang/pt-BR.json index 83a5bce..becb92b 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Vote para o próximo mapa: {0}s", "emv.hud.finished": "Votação encerrada, próximo mapa: {0}", "nextmap": "O próximo mapa será {green}{0}", - "nextmap.decided-by-vote": "O próximo mapa será decidido pelo voto" + "nextmap.decided-by-vote": "O próximo mapa será decidido pelo voto", + "general.validation.map-played-recently": "O mapa foi jogado recentemente" } diff --git a/lang/ru.json b/lang/ru.json index 0499bde..32ce0b8 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Голосование за следующую карту: {0}s", "emv.hud.finished": "Голосование завершено. Следующая карта: {0}", "nextmap": "Следующей картой будет {green}{0}", - "nextmap.decided-by-vote": "Следующая карта будет определена голосованием" + "nextmap.decided-by-vote": "Следующая карта будет определена голосованием", + "general.validation.map-played-recently": "Карта была недавно сыграна" } diff --git a/lang/tr.json b/lang/tr.json index af8adae..d8f7035 100644 --- a/lang/tr.json +++ b/lang/tr.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Sonraki haritaya oy verin: {0}", "emv.hud.finished": "Oylama tamamlandı, sonraki harita: {0}", "nextmap": "Sonraki harita {green}{0} olacak", - "nextmap.decided-by-vote": "Sonraki haritaya oylamayla karar verilecek" + "nextmap.decided-by-vote": "Sonraki haritaya oylamayla karar verilecek", + "general.validation.map-played-recently": "Harita yakın zamanda oynandı" } diff --git a/lang/ua.json b/lang/ua.json index d17f233..760225f 100644 --- a/lang/ua.json +++ b/lang/ua.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "Голосуйте за наступну карту: {0}с", "emv.hud.finished": "Голосування завершено, наступна карта: {0}", "nextmap": "Наступна карта буде {green}{0}", - "nextmap.decided-by-vote": "Наступна карта буде визначена голосуванням" + "nextmap.decided-by-vote": "Наступна карта буде визначена голосуванням", + "general.validation.map-played-recently": "Карта була недавно зіграна" } diff --git a/lang/zh-Hans.json b/lang/zh-Hans.json index e323a99..9553226 100644 --- a/lang/zh-Hans.json +++ b/lang/zh-Hans.json @@ -36,5 +36,6 @@ "emv.hud.hud-timer": "投票下一张地图: {0}s", "emv.hud.finished": "投票完成,下一张地图为: {0}", "nextmap": "下一张地图为 {green}{0}", - "nextmap.decided-by-vote": "下一张地图将由投票决定" + "nextmap.decided-by-vote": "下一张地图将由投票决定", + "general.validation.map-played-recently": "地图最近已经被玩过" }