using Facepunch; using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Configuration; using Oxide.Core.Plugins; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; /* 1.0.6 * Added a voting system to allow players to vote for games manually. * Added temperature restrictions. * Added hook: EMWasManuallyStarted(string eventName); */ namespace Oxide.Plugins { [Info("EventHelper", "imthenewguy", "1.0.6")] [Description("A plugin that devs can use to help get players into events.")] class EventHelper : RustPlugin { #region Config private Configuration config; public class Configuration { [JsonProperty("A list of items that cannot be taken into an event")] public List black_listed_items = new List() { "cassette", " cassette.medium", "cassette.short", "fun.casetterecorder", "boombox" }; [JsonProperty("The command to recover items")] public string redeem_items_command = "recoveritems"; [JsonProperty("How often should we cycle through events")] public float event_cycle_timer = 7200f; [JsonProperty("Set to true if the server is giving players a kit on respawn and you want players to receive their saved items back automatically")] public bool auto_rekit = false; [JsonProperty("Unban players on server wipe?")] public bool unban_players = true; [JsonProperty("Allow players to vote for games manually?")] public bool voting_enabled = true; [JsonProperty("Command to initiate a vote for an event")] public string vote_start_cmd = "eventvote"; [JsonProperty("Command to vote for an event")] public string vote_cmd = "vote"; [JsonProperty("Max vote time (seconds)")] public int max_vote_time = 300; [JsonProperty("Minimum temperature while at an event")] public float temperature_min = 25f; [JsonProperty("Maximum temperature while at an event")] public float temperature_max = 25f; [JsonProperty("How often should players be reminded of the voting feature (seconds)? [0 = off]")] public float voting_reminder_time = 1200f; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary ToDictionary() => JsonConvert.DeserializeObject>(ToJson()); } protected override void LoadDefaultConfig() => config = new Configuration(); protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) { throw new JsonException(); } if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) { PrintToConsole("Configuration appears to be outdated; updating and saving"); SaveConfig(); } } catch { PrintToConsole($"Configuration file {Name}.json is invalid; using defaults"); LoadDefaultConfig(); } } protected override void SaveConfig() { PrintToConsole($"Configuration changes saved to {Name}.json"); Config.WriteObject(config, true); } #endregion #region classes public Dictionary Events = new Dictionary(); public Dictionary EventRunning = new Dictionary(); List FirstRespawnAfterGame; List BagPreventDropList = new List(); public class EventInfo { public bool automatic_start; public bool strip_items; public bool leaves_event_on_death; public bool full_health_on_join; public bool give_items_back_automatically; public bool full_metabolism_on_join; public bool restricted_temp; public Vector3 teleport_destination; public List participants = new List(); public bool manually_started; public string[] black_listed_commands; public ExternalPluginSettings external_plugin_settings = new ExternalPluginSettings(false, false, false, false, false, false); public EventInfo(bool automatic_start, bool strip_items, bool leaves_event_on_death, bool full_health_on_join, bool give_items_back_automatically, bool full_metabolism_on_join, Vector3 teleport_destination, bool restricted_temp = true) { this.automatic_start = automatic_start; this.strip_items = strip_items; this.leaves_event_on_death = leaves_event_on_death; this.full_health_on_join = full_health_on_join; this.give_items_back_automatically = give_items_back_automatically; this.full_metabolism_on_join = full_metabolism_on_join; this.teleport_destination = teleport_destination; this.restricted_temp = restricted_temp; } public class ExternalPluginSettings { public bool canDropBackpack; public bool canEraseBackpack; public bool canOpenBackpack; public bool canBackpackAcceptItem; public bool canRedeemKit; public bool CanLoseXP; public ExternalPluginSettings(bool canDropBackpack, bool canEraseBackpack, bool canOpenBackpack, bool canBackpackAcceptItem, bool canRedeemKit, bool CanLoseXP) { this.canDropBackpack = canDropBackpack; this.canEraseBackpack = canEraseBackpack; this.canOpenBackpack = canOpenBackpack; this.canBackpackAcceptItem = canBackpackAcceptItem; this.canRedeemKit = canRedeemKit; this.CanLoseXP = CanLoseXP; } } } public enum ContainerType { Main, Belt, Wear } public class ItemInfo { public string shortname; public ulong skin; public int amount; public float condition; public float maxCondition; public int ammo; public string ammotype; public ContainerType container; public int position; public int frequency; public KeyInfo instanceData; public class KeyInfo { public bool ShouldPool; public int dataInt; public int blueprintTarget; public int blueprintAmount; public uint subEntity; } public ItemInfo[] contents; public string text; public string name; public string description; } #endregion #region Data PlayerEntity pcdData; private DynamicConfigFile PCDDATA; const string perm_admin = "eventhelper.admin"; void Init() { PCDDATA = Interface.Oxide.DataFileSystem.GetFile(this.Name); LoadData(); permission.RegisterPermission(perm_admin, this); cmd.AddChatCommand(config.vote_cmd, this, "VoteForEvent"); cmd.AddChatCommand(config.vote_start_cmd, this, "StartEventVote"); Temperature_Min = config.temperature_min; Temperature_Max = config.temperature_max; if (config.voting_reminder_time > 0) { timer.Every(config.voting_reminder_time, () => { PrintToChat(string.Format(lang.GetMessage("VotingReminder", this), config.vote_start_cmd)); }); } } void Unload() { cmd.RemoveChatCommand(config.redeem_items_command, this); cmd.RemoveChatCommand(config.vote_cmd, this); cmd.RemoveChatCommand(config.vote_start_cmd, this); foreach (var kvp in pcdData.pEntity) { kvp.Value.AtEvent = false; kvp.Value.Event = null; } foreach (var e in Events.ToList()) { EMRemoveEvent(e.Key); } SaveData(); foreach (var player in BasePlayer.activePlayerList) { DestroyTempRestriction(player); } } void SaveData() { PCDDATA.WriteObject(pcdData); } void LoadData() { try { pcdData = Interface.Oxide.DataFileSystem.ReadObject(this.Name); } catch { Puts(lang.GetMessage("NoPlayerDataLoading", this)); pcdData = new PlayerEntity(); } } class PlayerEntity { public Dictionary pEntity = new Dictionary(); public Dictionary> banned_players = new Dictionary>(); } class PCDInfo { public string Event; public float Health; public float Food; public float Water; public bool AtEvent; public Vector3 Location; public List Items = new List(); } [PluginReference] private Plugin Backpacks, NoEscape, RestoreUponDeath; #endregion; #region Localization protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["AtEvent"] = "You are already at an event.", ["ItemsStored"] = "You have items stored that must be claimed before joining another event. Type /{0} to recover your items (doing this will delete your current inventory).", ["NoCrafting"] = "You must stop crafting in order to join this event.", ["EscapeBlocked"] = "You cannot join the game while you are escape blocked.", ["BlackListedItem"] = "You cannot bring a {0} into a minigame.", ["BlackListedItemSub"] = "You cannot bring a {0} into a minigame. This was found inside of your {1}", ["AlreadyEnrolled"] = "You are already enrolled in this event.", ["FailedToStrip"] = "Failed to strip your items.", ["ManualItemRecovery"] = "You must type /{0} to recover them (doing this will delete your current inventory).", ["NoRecovery"] = "You cannot recover your items until you leave the event.", ["NoCommand"] = "You cannot use the {0} command at this event.", ["UnclaimedItems"] = "You have unclaimed items. Type /{0} to recover them.", ["SkipStart"] = "Skipping event start due to manual start.", ["ManualStart"] = "Manually started event {0}", ["RemovedEvent"] = "Removed Event: {0}", ["SetupEvent"] = "Setup Event: {0}", ["EndedEvent"] = "Ended the event", ["NotSetup"] = "{0} has not been setup.", ["EventNotSetupExternalPluginSettings"] = "Event: {0} has not been setup. Must call EMCreateEvent before EMExternalPluginSettings.", ["EventNotSetupBlackList"] = "Event: {eventName} has not been setup. Must call EMCreateEvent before EMBlackListCommands.", ["RegisteredCommands"] = "Registered {0} black listed commands.", ["NoPlayerDataLoading"] = "Couldn't load player data, creating new Playerfile", ["NoEventData"] = "You have no event data stored.", ["MorePlayersFound"] = "More than one player found: {0}", ["NoMatch"] = "No player was found that matched: {0}", ["EHBanUsage"] = "Usage: /ehban ", ["BannedPlayer"] = "Banned {0} from all EventHelper events.", ["EHUnbanUsage"] = "Usage: /ehunban ", ["UnbannedPlayer"] = "Unbanned {0} from all EventHelper events.", ["NoBan"] = "{0} does not have any bans.", ["VotingReminder"] = "You can vote to start your favourite event by typing /{0} " }, this); } #endregion #region Methods bool IsPlayerBanned(BasePlayer player, string eventName) { if (pcdData.banned_players.ContainsKey(eventName) && pcdData.banned_players[eventName].Contains(player.userID)) return true; if (pcdData.banned_players.ContainsKey("global") && pcdData.banned_players["global"].Contains(player.userID)) return true; return false; } bool CanJoinEvent(BasePlayer player, string eventName) { if (IsPlayerBanned(player, eventName)) { PrintToChat(player, $"You are banned from the event and cannot join."); return false; } var playerData = GetPlayer(player.userID); if (playerData.AtEvent) { PrintToChat(player, lang.GetMessage("AtEvent", this, player.UserIDString)); return false; } if (HasItemsStored(player.userID) && !playerData.AtEvent) { PrintToChat(player, string.Format(lang.GetMessage("ItemsStored", this, player.UserIDString), config.redeem_items_command)); return false; } if (player.inventory.crafting.queue.Count != 0) { PrintToChat(player, lang.GetMessage("NoCrafting", this, player.UserIDString)); return false; } if (NoEscape != null && Convert.ToBoolean(NoEscape.Call("IsEscapeBlocked", player.UserIDString))) { PrintToChat(player, lang.GetMessage("EscapeBlocked", this, player.UserIDString)); return false; } if (HasProhibitedItems(player)) return false; return true; } bool StorePlayerItems(BasePlayer player) { var playerData = GetPlayer(player.userID); foreach (var item in player.inventory.AllItems()) { var item_save = new ItemInfo() { shortname = item.info.shortname, container = GetContainer(player, item.parent), position = item.position, amount = item.amount, ammo = item.GetHeldEntity() is BaseProjectile ? (item.GetHeldEntity() as BaseProjectile).primaryMagazine.contents : item.GetHeldEntity() is FlameThrower ? (item.GetHeldEntity() as FlameThrower).ammo : 0, ammotype = (item.GetHeldEntity() as BaseProjectile)?.primaryMagazine.ammoType.shortname ?? null, skin = item.skin, condition = item.condition, maxCondition = item.maxCondition, contents = item.contents?.itemList.Select(item1 => new ItemInfo { shortname = item1.info.shortname, amount = item1.amount, condition = item1.condition, maxCondition = item1.maxCondition }).ToArray(), }; if (item.instanceData != null) { item_save.instanceData = new ItemInfo.KeyInfo() { ShouldPool = item.instanceData.ShouldPool, dataInt = item.instanceData.dataInt, blueprintTarget = item.instanceData.blueprintTarget, blueprintAmount = item.instanceData.blueprintAmount, subEntity = item.instanceData.subEntity }; } if (item.text != null) item_save.text = item.text; if (item.name != null) item_save.name = item.name; if (item.info.displayDescription.english != null) item_save.description = item.info.displayDescription.english; playerData.Items.Add(item_save); } player.inventory.Strip(); return true; } bool RestoreItems(BasePlayer player, bool strip = true) { if (player.IsDead() || !player.IsConnected) return false; var playerData = GetPlayer(player.userID); if (strip) player.inventory.Strip(); if (playerData.Items == null || playerData.Items.Count == 0) return true; List unpositioned_items = Pool.GetList(); foreach (var saved_item in playerData.Items) { try { var item = ItemManager.CreateByName(saved_item.shortname, saved_item.amount, saved_item.skin); if (saved_item.name != null) item.name = saved_item.name; if (saved_item.description != null) item.info.displayDescription.english = saved_item.description; item.condition = saved_item.condition; item.maxCondition = saved_item.maxCondition; BaseProjectile weapon = item.GetHeldEntity() as BaseProjectile; if (weapon != null) { if (!string.IsNullOrEmpty(saved_item.ammotype)) weapon.primaryMagazine.ammoType = ItemManager.FindItemDefinition(saved_item.ammotype); weapon.primaryMagazine.contents = saved_item.ammo; } FlameThrower flameThrower = item.GetHeldEntity() as FlameThrower; if (flameThrower != null) flameThrower.ammo = saved_item.ammo; if (saved_item.contents != null) { foreach (ItemInfo contentData in saved_item.contents) { Item newContent = ItemManager.CreateByName(contentData.shortname, contentData.amount); if (newContent != null) { newContent.condition = contentData.condition; newContent.MoveToContainer(item.contents); } } } if (saved_item.instanceData != null) { item.instanceData = new ProtoBuf.Item.InstanceData(); item.instanceData.ShouldPool = saved_item.instanceData.ShouldPool; item.instanceData.dataInt = saved_item.instanceData.dataInt; item.instanceData.blueprintTarget = saved_item.instanceData.blueprintTarget; item.instanceData.blueprintAmount = saved_item.instanceData.blueprintAmount; item.instanceData.subEntity = saved_item.instanceData.subEntity; } if (saved_item.text != null) item.text = saved_item.text; switch (saved_item.container) { case ContainerType.Belt: item.MoveToContainer(player.inventory.containerBelt, saved_item.position); break; case ContainerType.Wear: item.MoveToContainer(player.inventory.containerWear, saved_item.position); break; case ContainerType.Main: item.MoveToContainer(player.inventory.containerMain, saved_item.position); break; default: unpositioned_items.Add(item); break; } } catch { Puts($"Failed to return {saved_item?.name ?? saved_item?.shortname}"); } } if (unpositioned_items.Count > 0) { foreach (var item in unpositioned_items) { player.GiveItem(item); } } playerData.Items.Clear(); Pool.FreeList(ref unpositioned_items); return true; } ContainerType GetContainer(BasePlayer player, ItemContainer container) { if (container.uid == player.inventory.containerBelt.uid) return ContainerType.Belt; else if (container.uid == player.inventory.containerWear.uid) return ContainerType.Wear; else return ContainerType.Main; } bool HasProhibitedItems(BasePlayer player) { if (config.black_listed_items == null || config.black_listed_items.Count == 0 || player.inventory.AllItems().Length == 0) return false; foreach (var item in player.inventory.AllItems()) { if (config.black_listed_items.Contains(item.info.shortname)) { PrintToChat(player, string.Format(lang.GetMessage("BlackListedItem", this, player.UserIDString), item.info.displayName.english)); return true; } if (item.contents != null && item.contents.itemList != null && item.contents.itemList.Count > 1) { foreach (var sub_item in item.contents.itemList) { if (config.black_listed_items.Contains(sub_item.info.shortname)) { PrintToChat(player, string.Format(lang.GetMessage("BlackListedItemSub", this, player.UserIDString), item.info.displayName.english, item.info.displayName.english)); return true; } } } } return false; } private bool HasItemsStored(ulong id) { var playerData = GetPlayer(id); if (playerData.Items.Count == 0) return false; return true; } bool IsPlayerSetup(ulong id) { return pcdData.pEntity.ContainsKey(id); } PCDInfo GetPlayer(ulong id) { if (!pcdData.pEntity.ContainsKey(id)) pcdData.pEntity.Add(id, new PCDInfo()); return pcdData.pEntity[id]; } void RestoreStats(BasePlayer player) { if (player.IsDead() || !player.IsConnected) return; var playerData = GetPlayer(player.userID); if (playerData.Food > 0) player.metabolism.calories.SetValue(playerData.Food); if (playerData.Water > 0) player.metabolism.hydration.SetValue(playerData.Water); if (playerData.Health > 0) player.SetHealth(playerData.Health); if (playerData.Location != Vector3.zero) TeleportToEvent(player, playerData.Location); playerData.Location = Vector3.zero; playerData.Food = 0; playerData.Water = 0; playerData.Health = 0; player.State.unHostileTimestamp = Network.TimeEx.currentTimestamp; player.MarkHostileFor(0); DestroyTempRestriction(player); } void StorePlayerInfo(BasePlayer player, bool StoreHealth, bool StoreStats, bool StoreTemp) { var playerData = GetPlayer(player.userID); if (StoreStats) { playerData.Food = player.metabolism.calories.value; playerData.Water = player.metabolism.hydration.value; player.metabolism.calories.SetValue(player.metabolism.calories.max); player.metabolism.hydration.SetValue(player.metabolism.hydration.max); } if (StoreHealth) { playerData.Health = player.health; player.SetHealth(100f); } if (StoreTemp) AddTempRestriction(player); playerData.Location = player.transform.position; } void TeleportToEvent(BasePlayer player, Vector3 loc) { if (loc == Vector3.zero) return; Player.Teleport(player, loc); player.StartSleeping(); player.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, true); player.ClientRPCPlayer(null, player, "StartLoading"); player.SendEntityUpdate(); player.UpdateNetworkGroup(); player.SendNetworkUpdateImmediate(false); } void RemoveFromEvent(BasePlayer player, string eventName) { if (!IsPlayerSetup(player.userID)) return; var playerData = GetPlayer(player.userID); playerData.AtEvent = false; EventInfo ei; if (eventName != null) Events.TryGetValue(eventName, out ei); else ei = null; ei.participants.Remove(player); } BasePlayer FindPlayerByID(string id, BasePlayer searchingPlayer = null) { if (!id.IsSteamId()) return null; var player = BasePlayer.activePlayerList.Where(x => x.UserIDString == id).FirstOrDefault(); if (player == null) { if (searchingPlayer != null) PrintToChat(searchingPlayer, string.Format(lang.GetMessage("NoMatch", this), id)); else Puts(string.Format(lang.GetMessage("NoMatch", this), id)); } return player ?? null; } private BasePlayer FindPlayerByName(string Playername, BasePlayer SearchingPlayer = null) { var lowered = Playername.ToLower(); var targetList = BasePlayer.allPlayerList.Where(x => x.displayName.ToLower().Contains(lowered)).OrderBy(x => x.displayName.Length); if (targetList.Count() == 1) { return targetList.First(); } if (targetList.Count() > 1) { if (targetList.First().displayName.Equals(Playername, StringComparison.OrdinalIgnoreCase)) { return targetList.First(); } if (SearchingPlayer != null) { PrintToChat(SearchingPlayer, string.Format(lang.GetMessage("MorePlayersFound", this, SearchingPlayer.UserIDString), String.Join(",", targetList.Select(x => x.displayName)))); } else Puts(string.Format(lang.GetMessage("MorePlayersFound", this), String.Join(",", targetList.Select(x => x.displayName)))); return null; } if (targetList.Count() == 0) { if (SearchingPlayer != null) { PrintToChat(SearchingPlayer, string.Format(lang.GetMessage("NoMatch", this, SearchingPlayer.UserIDString), Playername)); } else Puts(string.Format(lang.GetMessage("NoMatch", this), Playername)); return null; } return null; } public bool AddBan(BasePlayer player, string event_name) { if (!pcdData.banned_players.ContainsKey(event_name)) pcdData.banned_players.Add(event_name, new List()); if (!pcdData.banned_players[event_name].Contains(player.userID)) { pcdData.banned_players[event_name].Add(player.userID); SaveData(); return true; } return false; } public bool RemoveBan(BasePlayer player, string event_name) { if (!pcdData.banned_players.ContainsKey(event_name) || !pcdData.banned_players[event_name].Contains(player.userID)) return false; pcdData.banned_players[event_name].Remove(player.userID); SaveData(); return true; } #endregion #region Event Stuff string LastEvent; List automatic_Events = new List(); void StartNextEvent() { if (Events == null || Events.Count == 0 || automatic_Events.Count == 0) return; if (LastEvent != null && Events.ContainsKey(LastEvent) && !Events[LastEvent].manually_started) { Interface.CallHook("EMEndGame", LastEvent); EMEndEvent(LastEvent); } if (LastEvent == null) { Interface.CallHook("EMStartNextEvent", automatic_Events.First()); LastEvent = automatic_Events.First(); } else { var foundLastEvent = false; var setNewEvent = false; foreach (var e in automatic_Events) { if (Events[e].manually_started) continue; if (foundLastEvent) { Interface.CallHook("EMStartNextEvent", e); LastEvent = e; setNewEvent = true; break; } else if (e == LastEvent) foundLastEvent = true; } if (!setNewEvent) { var e = automatic_Events.First(); if (!Events[e].manually_started) { Interface.CallHook("EMStartNextEvent", e); LastEvent = e; } else Puts(lang.GetMessage("SkipStart", this)); } } } [HookMethod("EMWasManuallyStarted")] public bool EMWasManuallyStarted(string eventName) { EventInfo ei; if (Events.TryGetValue(eventName, out ei)) { return ei.manually_started; } return false; } [HookMethod("EMManuallyStarted")] public void EMManuallyStarted(string eventName) { EventInfo ei; if (Events.TryGetValue(eventName, out ei)) { ei.manually_started = true; Puts(string.Format(lang.GetMessage("ManualStart", this), eventName)); } } [HookMethod("EMRemoveEvent")] public void EMRemoveEvent(string eventName) { Interface.CallHook("EMEndGame", eventName); EventInfo ei; if (Events.TryGetValue(eventName, out ei)) { if (ei.participants.Count > 0) { foreach (var player in ei.participants.ToList()) { EMPlayerLeaveEvent(player, eventName); } } if (ei.automatic_start) automatic_Events.Remove(eventName); } Events.Remove(eventName); Puts(string.Format(lang.GetMessage("RemovedEvent", this), eventName)); } [HookMethod("EMCreateEvent")] private void EMCreateEvent(string eventName, bool automatic_start, bool stripItems, bool leaves_event_on_death, bool full_health_on_join, bool give_items_back_on_respawn, bool full_metabolism_on_join, Vector3 teleport_destination) { if (Events.ContainsKey(eventName)) EMRemoveEvent(eventName); EventInfo ei; Events.Add(eventName, ei = new EventInfo(automatic_start, stripItems, leaves_event_on_death, full_health_on_join, give_items_back_on_respawn, full_metabolism_on_join, teleport_destination)); Puts(lang.GetMessage("SetupEvent", this), eventName); if (automatic_start && !automatic_Events.Contains(eventName)) { automatic_Events.Add(eventName); } } [HookMethod("EMUpdateLobby")] private void EMUpdateLobby(string eventName, Vector3 pos) { EventInfo ei; if (!Events.TryGetValue(eventName, out ei)) return; ei.teleport_destination = pos; } [HookMethod("EMStartEvent")] private void EMStartEvent(string eventName) { EventInfo ei; if (!Events.TryGetValue(eventName, out ei)) return; if (!EventRunning.ContainsKey(eventName)) EventRunning.Add(eventName, true); else EventRunning[eventName] = true; } [HookMethod("EMEndEvent")] private void EMEndEvent(string eventName) { if (EventRunning.ContainsKey(eventName)) { EventInfo ei; if (eventName == null || !Events.TryGetValue(eventName, out ei)) return; if (ei.participants.Count > 0) { foreach (var player in ei.participants.ToList()) { EMPlayerLeaveEvent(player, eventName); } } ei.participants.Clear(); ei.manually_started = false; EventRunning.Remove(eventName); //Interface.CallHook("EMEndGame", eventName); Puts(lang.GetMessage("EndedEvent", this)); } } [HookMethod("EMEnrollPlayer")] private bool EMEnrollPlayer(BasePlayer player, string eventName) { EventInfo ei; if (!Events.TryGetValue(eventName, out ei)) { Puts(string.Format(lang.GetMessage("eventName", this), eventName)); return false; } if (ei.participants.Contains(player)) { PrintToChat(player, lang.GetMessage("AlreadyEnrolled", this, player.UserIDString)); return false; } if (!CanJoinEvent(player, eventName)) return false; if (ei.strip_items && !StorePlayerItems(player)) { RestoreItems(player); pcdData.pEntity.Remove(player.userID); PrintToChat(player, lang.GetMessage("FailedToStrip", this, player.UserIDString)); return false; } StorePlayerInfo(player, ei.full_health_on_join, ei.full_metabolism_on_join, ei.restricted_temp); ei.participants.Add(player); TeleportToEvent(player, ei.teleport_destination); var playerData = GetPlayer(player.userID); playerData.AtEvent = true; playerData.Event = eventName; return true; } [HookMethod("EMPlayerLeaveEvent")] private void EMPlayerLeaveEvent(BasePlayer player, string eventName = null, bool manually_left = false) { if (!IsPlayerSetup(player.userID)) { if (manually_left) PrintToChat(player, lang.GetMessage("NoEventData", this, player.UserIDString)); return; } var playerData = GetPlayer(player.userID); EventInfo ei; if (eventName != null) Events.TryGetValue(eventName, out ei); else ei = null; RestoreStats(player); if (playerData.Items.Count > 0 && (ei == null || ei.give_items_back_automatically || manually_left)) { if (RestoreItems(player)) { pcdData.pEntity.Remove(player.userID); } } else { player.inventory.Strip(); playerData.AtEvent = false; if (playerData.Items.Count > 0) PrintToChat(player, string.Format(lang.GetMessage("ManualItemRecovery", this, player.UserIDString), config.redeem_items_command)); } ei?.participants.Remove(player); } //Related to chat commandf void RestoreItemsCMD(BasePlayer player) { RestoreItemsAndStats(player, false); } void RestoreItemsAndStats(BasePlayer player, bool respawned = false, bool autokit = false) { if (!IsPlayerSetup(player.userID)) return; var playerData = GetPlayer(player.userID); if (playerData.AtEvent) { PrintToChat(player, lang.GetMessage("NoRecovery", this, player.UserIDString)); return; } RestoreStats(player); if (autokit) { NextTick(() => { if (RestoreItems(player, respawned ? true : false)) { pcdData.pEntity.Remove(player.userID); } }); } else if (RestoreItems(player, respawned ? true : false)) { pcdData.pEntity.Remove(player.userID); } } #endregion #region API Stuff [HookMethod("EMExternalPluginSettings")] void EMExternalPluginSettings(string eventName, bool canDropBackpack = false, bool canEraseBackpack = false, bool canOpenBackpack = false, bool canBackpackAcceptItem = false, bool canRedeemKit = false, bool CanLoseXP= false) { EventInfo ei; if (!Events.TryGetValue(eventName, out ei)) { Puts(string.Format(lang.GetMessage("EventNotSetupExternalPluginSettings", this), eventName)); return; } ei.external_plugin_settings = new EventInfo.ExternalPluginSettings(canDropBackpack, canEraseBackpack, canOpenBackpack, canBackpackAcceptItem, canRedeemKit, CanLoseXP); } [HookMethod("EMBlackListCommands")] void EMBlackListCommands(string eventName, string[] commands) { if (commands == null) return; EventInfo ei; if (!Events.TryGetValue(eventName, out ei)) { Puts(string.Format(lang.GetMessage("EventNotSetupBlackList", this), eventName)); return; } ei.black_listed_commands = commands; Puts(string.Format(lang.GetMessage("RegisteredCommands", this), ei.black_listed_commands.Length)); } [HookMethod("EMIsParticipating")] bool EMIsParticipating(BasePlayer player, string eventName) { EventInfo ei; if (!Events.TryGetValue(eventName, out ei)) return false; return ei.participants.Contains(player); } [HookMethod("EMAtEvent")] bool EMAtEvent(ulong id) { if (IsPlayerSetup(id) && pcdData.pEntity[id].AtEvent) return true; return false; } object canRedeemKit(BasePlayer player) { if (IsPlayerSetup(player.userID)) { var playerData = GetPlayer(player.userID); if (!playerData.AtEvent || string.IsNullOrEmpty(playerData.Event)) return null; if (!Events[playerData.Event].external_plugin_settings.canRedeemKit) return false; } return null; } object CanDropBackpack(ulong backpackOwnerID, Vector3 position) { if (BagPreventDropList.Contains(backpackOwnerID)) { BagPreventDropList.Remove(backpackOwnerID); return false; } return null; } object STOnPouchDrop(BasePlayer player) { if (IsPlayerSetup(player.userID)) { var playerData = GetPlayer(player.userID); if (!playerData.AtEvent || string.IsNullOrEmpty(playerData.Event)) return null; if (!Events[playerData.Event].external_plugin_settings.canDropBackpack) return true; } return null; } string CanOpenBackpack(BasePlayer player, ulong backpackOwnerID) { if (IsPlayerSetup(backpackOwnerID)) { var playerData = GetPlayer(backpackOwnerID); if (!playerData.AtEvent || string.IsNullOrEmpty(playerData.Event)) return null; if (!Events[playerData.Event].external_plugin_settings.canOpenBackpack) return "You cannot open your backpack during an event."; } return null; } object CanBackpackAcceptItem(ulong backpackOwnerID, ItemContainer backpackContainer, Item item) { if (IsPlayerSetup(backpackOwnerID)) { var playerData = GetPlayer(backpackOwnerID); if (!playerData.AtEvent || string.IsNullOrEmpty(playerData.Event)) return null; if (!Events[playerData.Event].external_plugin_settings.canBackpackAcceptItem) return false; } return null; } object CanEraseBackpack(ulong backpackOwnerID) { if (IsPlayerSetup(backpackOwnerID)) { var playerData = GetPlayer(backpackOwnerID); if (!playerData.AtEvent || string.IsNullOrEmpty(playerData.Event)) return null; if (!Events[playerData.Event].external_plugin_settings.canEraseBackpack) return false; } return null; } object STOnLoseXP(BasePlayer player) { if (IsPlayerSetup(player.userID)) { var playerData = GetPlayer(player.userID); if (!playerData.AtEvent || string.IsNullOrEmpty(playerData.Event)) return null; if (!Events[playerData.Event].external_plugin_settings.CanLoseXP) return false; } return null; } object OnRestoreUponDeath(BasePlayer player) { if (FirstRespawnAfterGame != null && FirstRespawnAfterGame.Contains(player)) { FirstRespawnAfterGame.Remove(player); return true; } return null; } #endregion #region Hooks void OnServerSave() { SaveData(); } void OnNewSave(string filename) { pcdData.pEntity.Clear(); if (config.unban_players) pcdData.pEntity.Clear(); SaveData(); } object OnPlayerCommand(BasePlayer player, string command, string[] args) { if (IsPlayerSetup(player.userID)) { var playerData = GetPlayer(player.userID); EventInfo ei; if (!string.IsNullOrEmpty(playerData.Event) && Events.TryGetValue(playerData.Event, out ei) && ei.participants.Contains(player) && ei.black_listed_commands.Contains(command)) { PrintToChat(player, string.Format(lang.GetMessage("NoCommand", this, player.UserIDString), command)); return false; } } return null; } object OnServerCommand(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null || arg.cmd.FullName == "chat.say") return null; if (IsPlayerSetup(player.userID)) { var playerData = GetPlayer(player.userID); EventInfo ei; if (!string.IsNullOrEmpty(playerData.Event) && Events.TryGetValue(playerData.Event, out ei) && ei.participants.Contains(player) && (ei.black_listed_commands.Contains(arg.cmd.Name) || ei.black_listed_commands.Contains(arg.cmd.FullName))) { PrintToChat(player, string.Format(lang.GetMessage("NoCommand", this, player.UserIDString), arg.cmd.FullName)); return false; } } return null; } void OnPlayerRespawned(BasePlayer player) { if (player.IsNpc || !player.userID.IsSteamId()) return; if (!IsPlayerSetup(player.userID)) return; var playerData = GetPlayer(player.userID); if (playerData.AtEvent || playerData.Items == null || playerData.Items.Count == 0) return; if (playerData.Event != null && Events.ContainsKey(playerData.Event) && (Events[playerData.Event].give_items_back_automatically || config.auto_rekit)) { RestoreItemsAndStats(player, true, config.auto_rekit); } else PrintToChat(player, string.Format(lang.GetMessage("UnclaimedItems", this, player.UserIDString), config.redeem_items_command)); } void OnServerInitialized(bool initial) { cmd.AddChatCommand(config.redeem_items_command, this, "RestoreItemsCMD"); timer.Every(config.event_cycle_timer, () => { try { StartNextEvent(); } catch { } }); if (RestoreUponDeath != null) FirstRespawnAfterGame = new List(); } void OnPlayerDeath(BasePlayer player, HitInfo info) { if (!IsPlayerSetup(player.userID)) return; var playerData = GetPlayer(player.userID); if (playerData.Event == null || !Events.ContainsKey(playerData.Event) || !Events[playerData.Event].leaves_event_on_death) return; if (RestoreUponDeath != null) { if (FirstRespawnAfterGame == null) FirstRespawnAfterGame = new List(); if (!FirstRespawnAfterGame.Contains(player)) FirstRespawnAfterGame.Add(player); } EventInfo ei; if (Events.TryGetValue(playerData.Event, out ei) && ei.external_plugin_settings != null) { if (!ei.external_plugin_settings.canDropBackpack && !BagPreventDropList.Contains(player.userID)) BagPreventDropList.Add(player.userID); } RemoveFromEvent(player, playerData.Event); } #endregion #region Chat commands [ChatCommand("ehban")] void BanPlayer(BasePlayer player, string command, string[] args) { if (!permission.UserHasPermission(player.UserIDString, perm_admin)) return; if (args.Length == 0) { PrintToChat(player, lang.GetMessage("EHBanUsage", this, player.UserIDString)); return; } var name = args.First(); var target = name.IsNumeric() ? FindPlayerByID(name, player ?? null) : FindPlayerByName(name, player ?? null); if (target == null) return; if (AddBan(target, "global")) PrintToChat(player, string.Format(lang.GetMessage("BannedPlayer", this, player.UserIDString), target.displayName)); } [ChatCommand("ehunban")] void UnbanPlayer(BasePlayer player, string command, string[] args) { if (!permission.UserHasPermission(player.UserIDString, perm_admin)) return; if (args.Length == 0) { PrintToChat(player, lang.GetMessage("EHUnbanUsage", this, player.UserIDString)); return; } var name = args.First(); var target = name.IsNumeric() ? FindPlayerByID(name, player ?? null) : FindPlayerByName(name, player ?? null); if (target == null) return; var foundBan = false; foreach (var cat in pcdData.banned_players) { if (cat.Value.Contains(player.userID)) { RemoveBan(target, cat.Key); foundBan = true; PrintToChat(player, string.Format(lang.GetMessage("UnbannedPlayer", this, player.UserIDString), target.displayName)); } } if (!foundBan) PrintToChat(player, string.Format(lang.GetMessage("NoBan", this, player.UserIDString), target.displayName)); } #endregion #region Event vote public Dictionary Voting = new Dictionary(); public class VoteInfo { public Timer _timer; public int votes; public int votes_required; public List voters = new List(); } // void StartEventVote(BasePlayer player, string command, string[] args) { if (!config.voting_enabled) return; if (args == null || args.Length == 0) { PrintToChat(player, $"You can initiate a vote for an arena event by typing /{config.vote_start_cmd} . Available events: \n- {string.Join("\n- ", automatic_Events)}"); return; } var eventName = automatic_Events.Where(x => x.Equals(string.Join(" ", args), StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (string.IsNullOrEmpty(eventName)) { PrintToChat(player, $"Invalid event. Available events: \n{string.Join("\n", automatic_Events)}"); return; } if (EventRunning.ContainsKey(eventName) && EventRunning[eventName]) { PrintToChat(player, $"Event {eventName} is already running. Please wait for it to finish before calling a vote."); return; } VoteInfo vi; if (Voting.TryGetValue(eventName, out vi) && vi._timer != null && !vi._timer.Destroyed) { PrintToChat(player, $"Vote for {eventName} is already running [{vi.votes}/{vi.votes_required}]"); return; } if (vi == null) Voting.Add(eventName, vi = new VoteInfo()); vi.voters.Add(player); vi.votes++; var maxTime = config.max_vote_time; vi.votes_required = BasePlayer.activePlayerList.Count > 10 ? Convert.ToInt32(BasePlayer.activePlayerList.Count * 0.3) : 2; PrintToChat($"A vote to start the event [{eventName}] has been initiated by {player.displayName}. Type /{config.vote_cmd} {eventName} to vote in favour [{vi.votes}/{vi.votes_required}]"); vi._timer = timer.Every(1, () => { maxTime--; if (maxTime <= 0) { PrintToChat($"The vote for {eventName} has failed [{vi.votes}/{vi.votes_required}]"); RemoveVote(eventName, vi); } if (EventRunning.ContainsKey(eventName) && EventRunning[eventName]) { PrintToChat($"Vote for {eventName} has been cancelled as the game has started."); RemoveVote(eventName, vi); } CheckVotes(eventName, vi); }); } void VoteForEvent(BasePlayer player, string command, string[] args) { if (!config.voting_enabled) return; if (args == null || args.Length == 0) { if (Voting.Count == 0 || !IsActiveVote()) PrintToChat(player, $"There are no active votes. To initiate a vote, type /{config.vote_start_cmd} . \nAvailable events: \n- {string.Join("\n- ", automatic_Events)}"); else { List activeVotes = Pool.GetList(); activeVotes.AddRange(Voting.Where(x => x.Value._timer != null && !x.Value._timer.Destroyed).Select(y => y.Key)); PrintToChat(player, string.Format("There {0} {1} active {2} running.", activeVotes.Count > 1 ? "are" : "is", activeVotes.Count, activeVotes.Count > 1 ? "votes" : "vote")); if (activeVotes.Count == 1) PrintToChat(player, $"Type /{config.vote_cmd} {activeVotes.First()} to vote in favour of the event."); else { var vote_text = $"Type /{config.vote_cmd} . Active event votes:"; foreach (var name in activeVotes) { vote_text += $"\n- {name}"; } PrintToChat(player, vote_text); } Pool.Free(ref activeVotes); } return; } var eventName = automatic_Events.Where(x => x.Equals(string.Join(" ", args), StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (string.IsNullOrEmpty(eventName)) { PrintToChat(player, $"Invalid event. Available events: \n- {string.Join("\n- ", automatic_Events)}"); return; } VoteInfo vi; if (Voting.TryGetValue(eventName, out vi)) { if (vi.voters.Contains(player)) { PrintToChat(player, "You have already voted for this event."); return; } vi.voters.Add(player); vi.votes++; PrintToChat($"{player.displayName} has voted to initiate {eventName} [{vi.votes}/{vi.votes_required}]."); CheckVotes(eventName, vi); } else PrintToChat(player, "Event vote not available."); } bool IsActiveVote() { var eventName = Voting.Where(x => x.Value._timer != null && !x.Value._timer.Destroyed).FirstOrDefault().Key; return !string.IsNullOrEmpty(eventName); } void RemoveVote(string eventName, VoteInfo vi = null) { if (vi == null) { if (!Voting.TryGetValue(eventName, out vi)) return; } if (vi._timer != null && !vi._timer.Destroyed) vi._timer.Destroy(); Voting.Remove(eventName); } void CheckVotes(string eventName, VoteInfo vi = null) { if (vi == null) { if (!Voting.TryGetValue(eventName, out vi)) return; } if (vi.votes >= vi.votes_required) { PrintToChat($"The vote for {eventName} has been successful. Starting a new game."); Interface.CallHook("EMStartNextEvent", eventName); EMManuallyStarted(eventName); RemoveVote(eventName, vi); } } #endregion #region monobehaviour void AddTempRestriction(BasePlayer player) { Puts("Adding temp restriction."); DestroyTempRestriction(player); player.gameObject.AddComponent(); } void DestroyTempRestriction(BasePlayer player) { var gameObject = player.GetComponent(); if (gameObject != null) GameObject.DestroyImmediate(gameObject); } public static float Temperature_Min; public static float Temperature_Max; public class Temperature : MonoBehaviour { private BasePlayer player; private float adjustmentDelay; private void Awake() { player = GetComponent(); adjustmentDelay = Time.time + 1f; } public void FixedUpdate() { if (player == null) return; if (adjustmentDelay < Time.time) { adjustmentDelay = Time.time + 1f; DoChange(); } } public void DoChange() { if (player == null || !player.IsConnected || !player.IsAlive()) return; if (player.metabolism.temperature.value < Temperature_Min) player.metabolism.temperature.value = Temperature_Min; else if (player.metabolism.temperature.value < Temperature_Max) player.metabolism.temperature.value = Temperature_Max; } private void OnDestroy() { enabled = false; CancelInvoke(); } } #endregion } }