using System.Collections.Generic; using System; using Oxide.Core.Libraries.Covalence; using System.Linq; using UnityEngine; using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Plugins; ///////////////////////////////// UPDATES /////////////////////////////////// // * v1.1.3 - Cleaned up some more code fot efficiency and more bug fixes, removed command for enabled/disable // * v1.1.2 - Fixed a few oxide bugs for messages // * v1.1.1 - Refactored player counts and bug fix for player connect msg (cleaned code as well) & Rewrote config/Data and update config/Data functions // * v1.1.0 - Added support for Toastify and Notify Plugins. Refactoring for how votes work // * v1.0.3 - Changed KillHeliCmd to have console command support for admins // * v1.0.2 - Changed the way enable/disable works (returns in functions rather than unloading plugin) // * v1.0.1 - Added Plugin Enabled Option // * v1.0.0 - Release namespace Oxide.Plugins { [Info("Kill Heli Vote", "Snaplatack", "1.1.3")] [Description("Players can vote to kill all Patrol Helicopters")] class KillHeliVote : CovalencePlugin { #region Variables [PluginReference] private readonly Plugin Toastify, Notify; bool Debug = true; const string UsePerm = "killhelivote.use", isAdmin = "killhelivote.admin"; public static KillHeliVote Instance; private bool HeliIsOut = false, TimerStarted = false; private Timer reminderMessage; private double votes = 0; private float activePlayersCount = 0; #endregion #region LoadUnload private void OnServerInitialized() { Instance = this; LoadData(); InitPlayerCount(); InitHelis(); AddCovalenceCommand(config.commands.VoteCommand, nameof(HeliVoteCmd), UsePerm); AddCovalenceCommand(config.commands.KillCommand, nameof(HeliKillCmd), isAdmin); if(!config.Settings.PluginEnabled) { Puts("PLUGIN UNLOADED! Ensure its enabled in the config!"); Interface.Oxide.UnloadPlugin(Instance.Name); } } private void Unload() { SaveData(); } #endregion #region InitVariables private void InitPlayerCount() { activePlayersCount = BasePlayer.activePlayerList.Count; if (Debug) Puts($"Active Players Currently = {activePlayersCount}"); if (config.Settings.BlacklistedVoters == null) return; foreach (var bPlayer in BasePlayer.activePlayerList) { if (config.Settings.BlacklistedVoters.Contains(bPlayer.userID)) activePlayersCount--; if (Debug) Puts($"Active Players Currently - Blacklisted Voters = {activePlayersCount}"); } } private void InitHelis() { foreach(var entity in BaseNetworkable.serverEntities.OfType()) { if (entity is PatrolHelicopter) { SetupHeli(entity); HeliIsOut = true; } } } #endregion #region Hooks private void OnPlayerConnected(BasePlayer player) { if(!config.Settings.PluginEnabled) return; if(config.Settings.BlacklistedVoters.Contains(player.userID)) return; if(HeliIsOut && !Data.Lists.PlayerVotes.Contains(player.userID)) { NextFrame(() => { NotifyPlayer(player, config.NotifSetting.NotifyPosMsgID, config.NotifSetting.ToastPosMsgID, lang.GetMessage("heli_announcement", this, player.UserIDString) + lang.GetMessage("pending_votes", this), config.NotifSetting.ToastDur); }); } activePlayersCount++; if(Debug) Puts($"Active Players Currently + 1 Player connected = {activePlayersCount}"); } private void OnPlayerDisconnected(BasePlayer player) { if(!config.Settings.PluginEnabled) return; if(player == null) return; if(Data.Lists.PlayerVotes.Contains(player.userID)) { votes--; if (Debug) Puts($"Votes = {votes}"); Data.Lists.PlayerVotes.Remove(player.userID); if (Debug) SaveData(); } if(config.Settings.BlacklistedVoters.Contains(player.userID)) return; activePlayersCount--; if(activePlayersCount < 0) activePlayersCount = 0; if(Debug) Puts($"Active Players Currently - 1 Player Left = {activePlayersCount}"); } private void OnEntitySpawned(PatrolHelicopter heli) { if(!config.Settings.PluginEnabled) return; SetupHeli(heli); } private void OnEntityKill(PatrolHelicopter heli) { if(TimerStarted) { reminderMessage.Destroy(); TimerStarted = false; } Data.Lists.ActiveHelis.Remove(heli.net.ID.Value); if (Data.Lists.ActiveHelis.Count == 0); HeliIsOut = false; } #endregion #region Commands private void HeliKillCmd(IPlayer iPlayer, string command, string[] args) { var IsServer = iPlayer.IsServer; var player = iPlayer.Object as BasePlayer; if (player == null && !IsServer) return; if (Data.Lists.ActiveHelis.Count == 0) { if(!IsServer) { NotifyPlayer(player, config.NotifSetting.NotifyNegMsgID, config.NotifSetting.ToastNegMsgID, lang.GetMessage("heli_not_out", this, player.UserIDString), config.NotifSetting.ToastDur); return; } Puts(lang.GetMessage("heli_not_out", this)); return; } if(!IsServer) { if(!permission.UserHasPermission(player.UserIDString, isAdmin)) { NotifyPlayer(player, config.NotifSetting.NotifyNegMsgID, config.NotifSetting.ToastNegMsgID, lang.GetMessage("no_perms", this, player.UserIDString), config.NotifSetting.ToastDur); return; } } Puts(lang.GetMessage("heli_killed", this)); KillHelis(); } private void HeliVoteCmd(IPlayer iPlayer, string command, string[] args) { if (iPlayer == null || command == null) return; var player = iPlayer.Object as BasePlayer; if(Data.Lists.ActiveHelis.Count == 0) { NotifyPlayer(player, config.NotifSetting.NotifyNegMsgID, config.NotifSetting.ToastNegMsgID, lang.GetMessage("heli_not_out", this, player.UserIDString), config.NotifSetting.ToastDur); return; } if(config.Settings.BlacklistedVoters.Contains(player.userID)) { NotifyPlayer(player, config.NotifSetting.NotifyNegMsgID, config.NotifSetting.ToastNegMsgID, lang.GetMessage("player_banned", this, player.UserIDString), config.NotifSetting.ToastDur); return; } if(!permission.UserHasPermission(player.UserIDString, UsePerm)) { NotifyPlayer(player, config.NotifSetting.NotifyNegMsgID, config.NotifSetting.ToastNegMsgID, lang.GetMessage("no_perms", this, player.UserIDString), config.NotifSetting.ToastDur); return; } if (Data.Lists.PlayerVotes.Contains(player.userID)) { NotifyPlayer(player, config.NotifSetting.NotifyNegMsgID, config.NotifSetting.ToastNegMsgID, lang.GetMessage("player_voted", this, player.UserIDString), config.NotifSetting.ToastDur); if (Debug) Puts($"Has already voted = true"); } else { if (Debug) Puts($"Has already voted = false"); Data.Lists.PlayerVotes.Add(player.userID); if (Debug) SaveData(); votes++; if (Debug) Puts($"Votes = {votes}"); NotifyPlayer(player, config.NotifSetting.NotifyPosMsgID, config.NotifSetting.ToastPosMsgID, lang.GetMessage("player_vote", this, player.UserIDString), config.NotifSetting.ToastDur); } if (Debug) Puts($" Current Votes = Voters: {votes} / Active Players: {activePlayersCount}"); if (config.Settings.PercentVotesRequired <= (votes / activePlayersCount) * 100) { KillHelis(); } } #endregion #region Functions private void SetupHeli(PatrolHelicopter heli) { if (!Data.Lists.ActiveHelis.Contains(heli.net.ID.Value)) Data.Lists.ActiveHelis.Add(heli.net.ID.Value); if (Debug) SaveData(); StartTimer(); } private void StartTimer() { if(TimerStarted) reminderMessage.Destroy(); TimerStarted = true; NotifyServer(config.NotifSetting.NotifyPosMsgID, config.NotifSetting.ToastPosMsgID, lang.GetMessage("heli_announcement", this), config.NotifSetting.ToastDur); reminderMessage = timer.Every(config.Settings.AnnounceTime, () => { NotifyServer(config.NotifSetting.NotifyPosMsgID, config.NotifSetting.ToastPosMsgID, lang.GetMessage("heli_announcement", this) + lang.GetMessage("pending_votes", this), config.NotifSetting.ToastDur); }); } private void KillHelis() { for (int i = Data.Lists.ActiveHelis.Count - 1; i >= 0; i--) { var heliId = new NetworkableId(Data.Lists.ActiveHelis.ToList()[i]); var heliEntity = (BaseEntity)BaseNetworkable.serverEntities.Find(heliId); if (heliEntity != null) heliEntity.Kill(); } Data.Lists.ActiveHelis.Clear(); NotifyServer(config.NotifSetting.NotifyPosMsgID, config.NotifSetting.ToastPosMsgID, lang.GetMessage("heli_killed", this), config.NotifSetting.ToastDur); votes = 0; if(Debug) Puts($"Votes = {votes}"); HeliIsOut = false; if(TimerStarted) { reminderMessage.Destroy(); TimerStarted = false; } Data.Lists.PlayerVotes.Clear(); } #endregion #region Messages private void NotifyPlayer(BasePlayer player, int? msgNum, string toastId, string message, float? duration) { var percentage = config.Settings.PercentVotesRequired / 100f; var totalVoters = (int)(activePlayersCount * percentage); if (totalVoters == 0) totalVoters = 1; if (config.UseMessage.UseToastify && Toastify != null) Toastify?.CallHook("SendToast", player, toastId, null, string.Format(message, votes, totalVoters, config.commands.VoteCommand), duration); if (config.UseMessage.UseNotify && Notify != null) Notify?.CallHook("SendNotify", player, msgNum, string.Format(message, votes, totalVoters, config.commands.VoteCommand)); if (config.UseMessage.UseChat || (config.UseMessage.UseToastify && Toastify == null) || (config.UseMessage.UseNotify && Notify == null)) player.ChatMessage(string.Format(message, votes, totalVoters, config.commands.VoteCommand)); } private void NotifyServer(int? msgNum, string toastId, string message, float? duration) { var ust = config.UseMessage.UseServerToastify && Toastify != null; var usn = config.UseMessage.UseServerNotify && Notify != null; var percentage = config.Settings.PercentVotesRequired / 100f; var totalVoters = (int)(activePlayersCount * percentage); if (totalVoters == 0) totalVoters = 1; if(config.UseMessage.UseServerChat || (config.UseMessage.UseServerToastify && Toastify == null) || (config.UseMessage.UseServerNotify && Notify == null)) server.Broadcast(string.Format(message, votes, totalVoters, config.commands.VoteCommand)); if(ust || usn) { foreach(var playerInList in BasePlayer.activePlayerList) { if(ust) Toastify?.CallHook("SendToast", playerInList, toastId, null, string.Format(message, votes, totalVoters, config.commands.VoteCommand), duration); if(usn) Notify?.CallHook("SendNotify", playerInList, msgNum, string.Format(message, votes, totalVoters, config.commands.VoteCommand)); } } } #endregion #region Configuration private Configuration config; public class Configuration { public GeneralSettings Settings = new GeneralSettings() { PluginEnabled = true, AnnounceTime = 300, PercentVotesRequired = 80, BlacklistedVoters = new List() }; [JsonProperty(PropertyName = "Commands")] public Commands commands = new Commands() { VoteCommand = "voteheli", KillCommand = "killhelis" }; [JsonProperty(PropertyName = "Messages")] public UseMessages UseMessage = new UseMessages() { UseChat = true, UseServerChat = true, UseToastify = false, UseServerToastify = false, UseNotify = false, UseServerNotify = false }; [JsonProperty(PropertyName = "Notifications")] public NotificationSettings NotifSetting = new NotificationSettings() { ToastPosMsgID = "success", ToastNegMsgID = "error", ToastDur = 10, NotifyPosMsgID = 0, NotifyNegMsgID = 1 }; public Core.VersionNumber Version = new Core.VersionNumber(0, 0, 0); } protected override void LoadDefaultConfig() { config = new Configuration(); } protected override void LoadConfig() { try { base.LoadConfig(); config = Config.ReadObject(); if (config == null) { LoadDefaultConfig(); } if(config.Version != Version) { UpdateConfig(); } } catch { Puts($"\n////////////////////////\n\n\n// Configuration file {Name}.json is invalid!\n// Resetting the config now!\n\n\n////////////////////////"); LoadDefaultConfig(); SaveConfig(); } } protected override void SaveConfig() { Puts($"Configuration changes saved to {Name}.json"); Config.WriteObject(config, true); } private void UpdateConfig() { //current version = 1.1.2 if (config.Version >= Version) return; if(config.Version <= new VersionNumber(1, 1, 3)) { LoadDefaultConfig(); } var configUpdateStr = "[CONFIG UPDATE] Updating to Version {0}"; PrintWarning(string.Format(configUpdateStr, Version)); config.Version = this.Version; SaveConfig(); } #endregion #region DataFile public PluginData Data = new PluginData(); public class PluginData { public DataLists Lists = new DataLists() { ActiveHelis = new HashSet(), PlayerVotes = new HashSet() }; } private void LoadData() { try { Data = Interface.Oxide.DataFileSystem.ReadObject($"{Name}/Data"); } catch (Exception) { Data = new PluginData(); } } private void SaveData() { Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Data", Data); } void OnNewSave() { Data = new PluginData(); } #endregion #region LangFile protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary() { ["Variables"] = "{0} = Players Voted | {1} = Players Active | {2} = Vote Command", ["no_perms"] = "You don't have permission to use this command!", ["plugin_enable"] = "Plugin is enabled!", ["plugin_enabled"] = "Plugin is already enabled!", ["plugin_disable"] = "Plugin is disabled!", ["plugin_disabled"] = "Plugin is already disabled!", ["player_banned"] = "You are not allowed to vote!", ["heli_announcement"] = "A Patrol Helicopter has appeared!\nCast your vote with /{2} to kill it!", ["pending_votes"] = "\nWe still need {0}/{1} votes!", ["heli_not_out"] = "Patrol Heli is not out!", ["invalid_syntax_toggle"] = "Invalid Syntax!\nCmd: /khv ", ["heli_killed"] = "All Patrol Helis have been KILLED!!", ["player_voted"] = "You have already voted!\nWe are still waiting for {0}/{1} players to vote!", ["player_vote"] = "You have voted!\nWe are still waiting for {0}/{1} players to vote!" }, this); } #endregion } public class GeneralSettings { [JsonProperty(PropertyName = "Plugin Enabled?")] public bool PluginEnabled; [JsonProperty(PropertyName = "Time to announce heli vote's [in seconds]")] public int AnnounceTime; [JsonProperty(PropertyName = "Percentage of vote's required [0 - 100]")] public float PercentVotesRequired; [JsonProperty(PropertyName = "Blacklisted voter's [by SteamID]", ObjectCreationHandling = ObjectCreationHandling.Replace)] public List BlacklistedVoters; } public class Commands { [JsonProperty(PropertyName = "Heli vote Command")] public string VoteCommand; [JsonProperty(PropertyName = "Heli kill command [Requires admin perm]")] public string KillCommand; } public class UseMessages { [JsonProperty(PropertyName = "Use Chat Messages?")] public bool UseChat; [JsonProperty(PropertyName = "Use chat for Server Messages?")] public bool UseServerChat; [JsonProperty(PropertyName = "Use Toastify Messages?")] public bool UseToastify; [JsonProperty(PropertyName = "Use Toastify for Server Messages?")] public bool UseServerToastify; [JsonProperty(PropertyName = "Use Notify Messages?")] public bool UseNotify; [JsonProperty(PropertyName = "Use Notify for Server Messages?")] public bool UseServerNotify; } public class NotificationSettings { [JsonProperty(PropertyName = "Toastify Positive Msg ID")] public string ToastPosMsgID; [JsonProperty(PropertyName = "Toastify Negative Msg ID")] public string ToastNegMsgID; [JsonProperty(PropertyName = "Toastify Msg Duration")] public float ToastDur; [JsonProperty(PropertyName = "Notify Positive Msg ID")] public int NotifyPosMsgID; [JsonProperty(PropertyName = "Notify Negative Msg ID")] public int NotifyNegMsgID; } public class DataLists { public HashSet ActiveHelis; public HashSet PlayerVotes; } }