C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Requires: EMInterface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Reflection;
using UnityEngine;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Newtonsoft.Json.Linq;
using Rust;
using Network;
namespace Oxide.Plugins
{
[Info("Event Manager", "Reneb / k1lly0u", "3.0.73", ResourceId = 740)]
class EventManager : RustPlugin
{
#region Fields
[PluginReference] EMInterface EMInterface;
[PluginReference] Plugin Economics;
[PluginReference] Plugin FriendlyFire;
[PluginReference] Plugin Kits;
[PluginReference] Plugin ServerRewards;
[PluginReference] Plugin Spawns;
[PluginReference] Plugin ZoneManager;
MethodInfo killLifestory;
static EventManager ins;
static List<MessageData> popupQueue;
static List<BasePlayer> unnetworkedPlayers;
private Dictionary<ulong, Timer> KillTimers;
private RestorationManager Restoration;
private GameTimer _GameTimer;
private WaveTimer _WaveTimer;
private DynamicConfigFile P_Stats;
private DynamicConfigFile RestoreData;
private bool _GodEnabled;
private bool debugEnabled = false;
private bool ResetNoCorpse;
private static string ScoreUI = "EMUI_Scoreboard";
private static string ClockUI = "EMUI_Timer";
private static string DeathUI = "EMUI_Death";
private static string TimerUI = "EMUI_DeathTimer";
private FieldInfo spectateFilter;
public Dictionary<ulong, PlayerStatistics> StatsCache;
public List<EMInterface.AEConfig> ValidAutoEvents;
public Dictionary<string, Events> ValidEvents;
public Events _Event;
public string _EventName;
public string _CurrentEventConfig;
public int _AutoEventNum = 0;
public bool _ForceNextConfig;
public string _NextConfigName;
public int _NextEventNum;
public bool _Open;
public bool _Started;
public bool _Prestarting;
public bool _Ended;
public bool _Pending;
public bool _Destoyed;
public bool _Launched;
public bool _RandomizeAuto;
public bool _TimerStarted;
public List<Timer> EventTimers;
public Dictionary<ulong, Timer> RespawnTimers;
public Dictionary<string, EventSetting> EventGames;
public ScoreData GameScores;
public SpawnManager SpawnCount;
public List<EventPlayer> EventPlayers;
public List<BasePlayer> Joiners;
public Statistics GameStatistics;
public GameMode EventMode;
#endregion
#region UI
#region Popup Messages
private static string Popup = "EMUI_Popupmsg";
private List<string> PopupPanels = new List<string>();
private int popUpCount = 0;
public void PopupMessage(string message)
{
popupQueue.Add(new MessageData(message, 6, ""));
UpdateMessages();
}
private CuiElementContainer CreateMessageEntry(int number, string panelName, MessageData data)
{
PopupPanels.Add(panelName);
var pos = GetFeedPosition(number);
var Main = UI.CreateElementContainer(panelName, "0 0 0 0", $"{pos[0]} {pos[1]}", $"{pos[2]} {pos[3]}", false, "Hud");
UI.CreateOutLineLabel(ref Main, panelName, "0 0 0 1", data.message, 17, "1 1", "0 0", "1 1");
return Main;
}
private float[] GetFeedPosition(int number)
{
Vector2 initialPos = new Vector2(0.25f, 0.89f);
Vector2 dimensions = new Vector2(0.5f, 0.04f);
var yPos = initialPos.y - ((dimensions.y + 0.005f) * number);
return new float[] { initialPos.x, yPos, initialPos.x + dimensions.x, yPos + dimensions.y };
}
private void UpdateMessages(bool destroyed = false)
{
if (destroyed)
{
if (popupQueue.Count > 0)
{
for (int i = 0; i < popupQueue.Count; i++)
{
if (i >= 3)
return;
var feed = popupQueue[i];
var panelName = feed.elementID;
if (!feed.started)
{
panelName = Popup + popUpCount;
popUpCount++;
feed.Begin(panelName);
}
AddUI(CreateMessageEntry(i, panelName, popupQueue[i]));
}
}
}
else
{
if (popupQueue.Count > 0 && popupQueue.Count < 3)
{
var feed = popupQueue[popupQueue.Count - 1];
var panelName = Popup + popUpCount;
popUpCount++;
AddUI(CreateMessageEntry(popupQueue.Count - 1, panelName, feed));
feed.Begin(panelName);
}
}
}
private void AddUI(CuiElementContainer element)
{
foreach (var player in EventPlayers)
{
if (player.inEvent && player.enabled)
CuiHelper.AddUi(player.GetPlayer(), element);
}
}
private void DestroyUpdate()
{
for (int i = 0; i < popupQueue.Count; i++)
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, popupQueue[i].elementID);
}
}
UpdateMessages(true);
}
private void DestroyPopupUI(BasePlayer player)
{
for (int i = 0; i < popupQueue.Count; i++)
{
CuiHelper.DestroyUi(player, popupQueue[i].elementID);
}
}
private void DestroyPopupUI(MessageData element)
{
if (!string.IsNullOrEmpty(element.elementID))
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, element.elementID);
}
}
popupQueue.Remove(element);
DestroyUpdate();
}
private void DestroyAllPopups()
{
foreach (var player in BasePlayer.activePlayerList)
foreach (var message in PopupPanels)
CuiHelper.DestroyUi(player, message);
PopupPanels.Clear();
}
#endregion
#region Scoreboards
public class ScoreData
{
public Dictionary<ulong, Scoreboard> Scores = new Dictionary<ulong, Scoreboard>();
public string ScoreType;
public string Additional;
}
public class Scoreboard
{
public string Name;
public int Position;
public int Score;
}
public void CreateScoreboard(BasePlayer player)
{
if (GameScores == null) return;
var scores = GameScores.Scores;
var type = GameScores.ScoreType;
var additional = GameScores.Additional;
var Main = UI.CreateElementContainer(ScoreUI, "0.1 0.1 0.1 0.7", "0.82 0.55", "0.99 0.98", false, "Hud");
int count = 0;
if (!string.IsNullOrEmpty(additional))
{
UI.CreateLabel(ref Main, ScoreUI, "", additional, 12, $"0.05 {GetHeight(count)}", $"0.95 {GetHeight(count) + 0.06f}");
count++;
}
if (scores != null)
{
var topScores = scores.Take(11);
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MainColor}>Name</color>", 12, $"0.25 {GetHeight(count)}", $"0.7 {GetHeight(count) + 0.06f}");
if (type != null) UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MainColor}>{type}</color>", 12, $"0.7 {GetHeight(count)}", $"0.95 {GetHeight(count) + 0.06f}");
count++;
if (scores.ContainsKey(player.userID) && scores[player.userID].Position > 10)
{
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MainColor}>{scores[player.userID].Position + 1}</color>", 12, $"0.05 {GetHeight(count)}", $"0.25 {GetHeight(count) + 0.06f}");
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MsgColor}>{scores[player.userID].Name}</color>", 12, $"0.25 {GetHeight(count)}", $"0.7 {GetHeight(count) + 0.06f}");
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MsgColor}>{scores[player.userID].Score}</color>", 12, $"0.7 {GetHeight(count)}", $"0.95 {GetHeight(count) + 0.06f}");
count++;
}
foreach (var score in topScores)
{
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MainColor}>{score.Value.Position + 1}</color>", 12, $"0.05 {GetHeight(count)}", $"0.25 {GetHeight(count) + 0.06f}");
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MsgColor}>{score.Value.Name}</color>", 12, $"0.25 {GetHeight(count)}", $"0.7 {GetHeight(count) + 0.06f}");
UI.CreateLabel(ref Main, ScoreUI, "", $"<color={configData.Messaging.MsgColor}>{score.Value.Score}</color>", 12, $"0.7 {GetHeight(count)}", $"0.95 {GetHeight(count) + 0.06f}");
count++;
}
}
DestroyScoreboard(player);
CuiHelper.AddUi(player, Main);
}
public void UpdateScoreboard(ScoreData data)
{
GameScores = data;
foreach (var eventPlayer in EventPlayers)
{
CreateScoreboard(eventPlayer.GetPlayer());
}
}
float GetHeight(int num) => 0.89f - (0.06f * num);
void DestroyScoreboard(BasePlayer player) => CuiHelper.DestroyUi(player, ScoreUI);
#endregion
#endregion
#region Oxide Hooks
void Loaded()
{
Unsubscribe(nameof(OnEntityTakeDamage));
Unsubscribe(nameof(OnPlayerAttack));
Unsubscribe(nameof(OnItemPickup));
Unsubscribe(nameof(OnRunPlayerMetabolism));
Unsubscribe(nameof(CanNetworkTo));
Unsubscribe(nameof(CanLootPlayer));
Unsubscribe(nameof(OnCreateWorldProjectile));
P_Stats = Interface.Oxide.DataFileSystem.GetFile("EventManager/Statistics");
RestoreData = Interface.Oxide.DataFileSystem.GetFile("EventManager/Restoration_data");
Restoration = new RestorationManager();
killLifestory = typeof(BasePlayer).GetMethod("LifeStoryEnd", (BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic));
}
void OnServerInitialized()
{
lang.RegisterMessages(Messages, this);
if (!Spawns)
{
PrintError("Unable to load EventManager - Spawns database not found.Please download Spawns database to continue");
rust.RunServerCommand("oxide.unload", new object[] { "EventManager" });
return;
}
if (!ZoneManager)
{
PrintError("Unable to load EventManager - ZoneManager not found.Please download ZoneManager to continue");
rust.RunServerCommand("oxide.unload", new object[] { "EventManager" });
return;
}
if (!Kits)
PrintError("Kits is not installed! Unable to issue any weapon kits");
SetVars();
LoadVariables();
LoadData();
ins = this;
foreach(var player in BasePlayer.activePlayerList)
CheckForRestore(player);
timer.In(5, ()=>
{
Interface.CallHook("RegisterGame");
ValidateAllEvents();
});
}
void Unload()
{
SaveStatistics();
SaveRestoreInfo();
if (_Open) CloseEvent();
if (_Started)
{
foreach(var eventPlayer in EventPlayers)
{
eventPlayer?.GetPlayer().DieInstantly();
UnityEngine.Object.DestroyImmediate(eventPlayer);
}
}
else DestroyGame();
}
void OnPlayerInit(BasePlayer player)
{
CheckForRestore(player);
}
void OnPlayerDisconnected(BasePlayer player)
{
if (Joiners.Contains(player))
Joiners.Remove(player);
if (GetUser(player))
LeaveEvent(player);
}
void OnPlayerRespawned(BasePlayer player)
{
if (!_Started && !_Open)
CheckForRestore(player);
else
{
var eventPlayer = GetUser(player);
if (eventPlayer == null) return;
if (!EventPlayers.Contains(eventPlayer))
{
UnityEngine.Object.DestroyImmediate(eventPlayer);
return;
}
if (eventPlayer.inEvent && !eventPlayer.isLeaving)
TeleportPlayerToEvent(player);
else if (!eventPlayer.isLeaving)
Restoration.RestorePlayer(player);
}
}
void OnPlayerAttack(BasePlayer player, HitInfo hitinfo)
{
var eventPlayer = GetUser(player);
if (eventPlayer == null || !eventPlayer.inEvent)
return;
if (hitinfo.HitEntity != null)
Interface.Oxide.CallHook("OnEventPlayerAttack", player, hitinfo);
if (hitinfo.IsProjectile())
AddStats(player, StatType.Shots);
return;
}
void OnEntityTakeDamage(BaseEntity entity, HitInfo info)
{
if (entity.GetComponent<EventCorpse>())
{
NullifyDamage(info);
return;
}
var player = entity.ToPlayer();
var attacker = info.InitiatorPlayer;
if (player != null)
{
var eventPlayer = GetUser(player);
if (eventPlayer != null)
{
if (eventPlayer.isLeaving || eventPlayer.isDead)
{
NullifyDamage(info);
return;
}
if (_GodEnabled)
{
NullifyDamage(info);
return;
}
float damageAmount = info.damageTypes.Total();
if (attacker != null)
{
if (!GetUser(attacker))
{
NullifyDamage(info);
return;
}
else
{
object multiply = Interface.CallHook("HasDamageMultiplier", player, attacker);
if (multiply is float)
{
damageAmount *= (float)multiply;
}
}
}
if (info.isHeadshot)
damageAmount *= 2;
if (player.health - damageAmount < 1)
{
NullifyDamage(info);
OnPlayerDeath(player, info);
return;
}
}
else if (attacker != null)
{
if (GetUser(attacker))
{
NullifyDamage(info);
return;
}
}
}
}
object OnCreateWorldProjectile(HitInfo info, Item item)
{
if (info?.InitiatorPlayer != null)
{
var eventPlayer = GetUser(info.InitiatorPlayer);
if (eventPlayer != null)
return false;
}
if (info?.HitEntity?.ToPlayer() != null)
{
var eventPlayer = GetUser(info.HitEntity.ToPlayer());
if (eventPlayer != null)
return false;
}
return null;
}
private void OnRunPlayerMetabolism(PlayerMetabolism metabolism, BaseCombatEntity entity, float delta)
{
var eventPlayer = GetUser(entity?.ToPlayer());
if (eventPlayer == null) return;
if (eventPlayer.isDead)
{
metabolism.bleeding.value = 0;
metabolism.poison.value = 0;
metabolism.radiation_level.value = 0;
metabolism.radiation_poison.value = 0;
metabolism.wetness.value = 0;
}
else
metabolism.bleeding.value = 0f;
}
object CanNetworkTo(BaseEntity entity, BasePlayer target)
{
var player = entity as BasePlayer;
var eventPlayer = GetUser(player ?? (entity as HeldEntity)?.GetOwnerPlayer());
if (eventPlayer == null || target == null)
return null;
if (unnetworkedPlayers.Contains(player))
return false;
return null;
}
void OnItemPickup(Item item, BasePlayer player)
{
if (_Started && GetUser(player) != null && _Event.DisableItemPickup)
{
item.Remove(0.01f);
SendMsg(player, "ItemPickup");
}
}
object CanLootPlayer(BasePlayer target, BasePlayer looter)
{
if (_Started && GetUser(looter) != null && _Event.DisableItemPickup)
{
SendMsg(looter, "NoLooting");
return true;
}
return null;
}
void SetVars()
{
EventGames = new Dictionary<string, EventSetting>();
EventPlayers = new List<EventPlayer>();
Joiners = new List<BasePlayer>();
EventTimers = new List<Timer>();
RespawnTimers = new Dictionary<ulong, Timer>();
ValidAutoEvents = new List<EMInterface.AEConfig>();
ValidEvents = new Dictionary<string, Events>();
KillTimers = new Dictionary<ulong, Timer>();
StatsCache = new Dictionary<ulong, PlayerStatistics>();
GameScores = new ScoreData();
SpawnCount = new SpawnManager();
Restoration = new RestorationManager();
popupQueue = new List<MessageData>();
unnetworkedPlayers = new List<BasePlayer>();
spectateFilter = typeof(BasePlayer).GetField("spectateFilter", (BindingFlags.Instance | BindingFlags.NonPublic));
_Open = false;
_Started = false;
_Ended = true;
_Pending = false;
_Destoyed = true;
_Launched = false;
_GodEnabled = false;
_RandomizeAuto = false;
_ForceNextConfig = false;
_EventName = null;
_NextConfigName = null;
_NextEventNum = 0;
_CurrentEventConfig = null;
_Event = DefaultConfig;
EventMode = GameMode.Normal;
_TimerStarted = false;
_GameTimer = null;
}
void CheckForRestore(BasePlayer player)
{
if (player.IsSleeping() || player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot))
{
timer.Once(3, () => CheckForRestore(player));
return;
}
var eventPlayer = GetUser(player);
if (eventPlayer != null && eventPlayer.inEvent && _Started) return;
if (Restoration.HasPendingRestore(player.userID))
{
if (Restoration.ReadyToRestore(player))
{
if (!Restoration.RestorePlayer(player))
{
SendMsg(player, "failedRestore");
}
else SaveRestoreInfo();
}
}
}
#endregion
#region Config
private ConfigData configData;
class ConfigData
{
public EM_Messaging Messaging { get; set; }
public EM_Options Options { get; set; }
}
class EM_Options
{
public bool AllowClassSelectionOnDeath { get; set; }
public bool LaunchAutoEventsOnStartup { get; set; }
public int Battlefield_Timer { get; set; }
public int Required_AuthLevel { get; set; }
public bool DropCorpseOnDeath { get; set; }
public bool UseSpectateMode { get; set; }
public bool UseEconomicsAsTokens { get; set; }
public bool UseEventPrestart { get; set; }
public int EventPrestartTimer { get; set; }
}
class EM_Messaging
{
public bool AnnounceEvent { get; set; }
public bool AnnounceEvent_During { get; set; }
public int AnnounceEvent_Interval { get; set; }
public string MainColor { get; set; }
public string MsgColor { get; set; }
}
private void LoadVariables()
{
LoadConfigVariables();
SaveConfig();
}
private void LoadConfigVariables()
{
try
{
configData = Config.ReadObject<ConfigData>();
}
catch (Exception)
{
Puts("Invalid config file, restoring default");
LoadDefaultConfig();
}
}
protected override void LoadDefaultConfig()
{
var config = new ConfigData
{
Messaging = new EM_Messaging
{
AnnounceEvent_During = true,
AnnounceEvent_Interval = 120,
AnnounceEvent = true,
MainColor = "#FF8C00",
MsgColor = "#939393"
},
Options = new EM_Options
{
AllowClassSelectionOnDeath = true,
Battlefield_Timer = 1200,
DropCorpseOnDeath = true,
EventPrestartTimer = 30,
LaunchAutoEventsOnStartup = false,
Required_AuthLevel = 1,
UseEconomicsAsTokens = false,
UseSpectateMode = true,
UseEventPrestart = true
}
};
SaveConfig(config);
}
void SaveConfig(ConfigData config) => Config.WriteObject(config, true);
#endregion
#region Messaging
public void BroadcastEvent(string msg)
{
foreach (EventPlayer eventplayer in EventPlayers)
SendReply(eventplayer.GetPlayer(), $"<color={configData.Messaging.MainColor}>" + msg + "</color>");
}
public void BroadcastToChat(string message) => PrintToChat($"<color={configData.Messaging.MainColor}>{msg("Title")}</color><color={configData.Messaging.MsgColor}>{msg(message)}</color>");
static string msg(string key, BasePlayer player = null) => ins.lang.GetMessage(key, ins, player?.UserIDString);
private void SendMsg(BasePlayer player, string langkey, bool title = true)
{
string message = $"<color={configData.Messaging.MsgColor}>{msg(langkey)}</color>";
if (title) message = $"<color={configData.Messaging.MainColor}>{msg("Title")}</color>" + message;
SendReply(player, message);
}
void AnnounceEvent()
{
object success = Interface.Oxide.CallHook("OnEventAnnounce");
if (success is string)
BroadcastToChat((string)success);
else BroadcastToChat(string.Format(msg("eventOpen"), _Event.EventType));
}
void AnnounceDuringEvent()
{
if (configData.Messaging.AnnounceEvent_During)
{
if (_Open && _Started)
{
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
if (!GetUser(player))
SendMsg(player, string.Format(msg("stillOpen"), _Event.EventType));
}
}
}
}
#endregion
#region Information Classes
public class Events
{
public bool CloseOnStart;
public bool DisableItemPickup;
public bool UseClassSelector;
public int MinimumPlayers;
public int MaximumPlayers;
public int ScoreLimit;
public int RespawnTimer;
public int GameRounds;
public int EnemiesToSpawn;
public string EventType;
public string Kit;
public string WeaponSet;
public string Spawnfile;
public string Spawnfile2;
public string ZoneID;
public GameMode GameMode;
public RespawnType RespawnType;
public SpawnType SpawnType;
}
public class EventSetting
{
public bool LockClothing;
public bool RequiresKit;
public bool RequiresSpawns;
public bool RequiresMultipleSpawns;
public bool CanUseClassSelector;
public bool CanPlayBattlefield;
public bool CanChooseRespawn;
public bool ForceCloseOnStart;
public bool IsRoundBased;
public bool SpawnsEnemies;
public string ScoreType;
}
public class EventInvItem
{
public int itemid;
public ulong skin;
public int amount;
public float condition;
public int ammo;
public string ammotype;
public ProtoBuf.Item.InstanceData instanceData;
public EventInvItem[] contents;
}
public class RestoreInfo
{
//public Dictionary<InventoryType, List<EventInvItem>> inventory = new Dictionary<InventoryType, List<EventInvItem>>();
public Dictionary<InventoryType, byte[]> inventory = new Dictionary<InventoryType, byte[]>();
public float health, hydration, calories, x, y, z;
}
public class UI
{
static public CuiElementContainer CreateElementContainer(string panelName, string color, string aMin, string aMax, bool useCursor = false, string parent = "Overlay")
{
var NewElement = new CuiElementContainer()
{
{
new CuiPanel
{
Image = {Color = color},
RectTransform = {AnchorMin = aMin, AnchorMax = aMax},
CursorEnabled = useCursor
},
new CuiElement().Parent = parent,
panelName
}
};
return NewElement;
}
static public void CreatePanel(ref CuiElementContainer container, string panel, string color, string aMin, string aMax, bool cursor = false)
{
container.Add(new CuiPanel
{
Image = { Color = color },
RectTransform = { AnchorMin = aMin, AnchorMax = aMax },
CursorEnabled = cursor
},
panel);
}
static public void CreateLabel(ref CuiElementContainer container, string panel, string color, string text, int size, string aMin, string aMax, TextAnchor align = TextAnchor.MiddleCenter)
{
container.Add(new CuiLabel
{
Text = { Color = color, FontSize = size, Align = align, Text = text },
RectTransform = { AnchorMin = aMin, AnchorMax = aMax }
},
panel);
}
static public void CreateOutLineLabel(ref CuiElementContainer container, string panel, string color, string text, int size, string distance, string aMin, string aMax, TextAnchor align = TextAnchor.MiddleCenter, string parent = "Overlay")
{
CuiElement textElement = new CuiElement
{
Name = CuiHelper.GetGuid(),
Parent = panel,
FadeOut = 0.2f,
Components =
{
new CuiTextComponent
{
Text = text,
FontSize = size,
Align = TextAnchor.MiddleCenter,
FadeIn = 0.2f
},
new CuiOutlineComponent
{
Distance = distance,
Color = color
},
new CuiRectTransformComponent
{
AnchorMin = aMin,
AnchorMax = aMax
}
}
};
container.Add(textElement);
}
static public void CreateButton(ref CuiElementContainer container, string panel, string color, string text, int size, string aMin, string aMax, string command, TextAnchor align = TextAnchor.MiddleCenter)
{
container.Add(new CuiButton
{
Button = { Color = color, Command = command, FadeIn = 0f },
RectTransform = { AnchorMin = aMin, AnchorMax = aMax },
Text = { Text = text, FontSize = size, Align = align }
},
panel);
}
static public void ImageFromStorage(ref CuiElementContainer container, string panel, string png, string aMin, string aMax)
{
container.Add(new CuiElement
{
Name = CuiHelper.GetGuid(),
Parent = panel,
Components =
{
new CuiRawImageComponent {Png = png },
new CuiRectTransformComponent {AnchorMin = aMin, AnchorMax = aMax }
}
});
}
static public void ImageFromURL(ref CuiElementContainer container, string panel, string url, string aMin, string aMax)
{
container.Add(new CuiElement
{
Name = CuiHelper.GetGuid(),
Parent = panel,
Components =
{
new CuiRawImageComponent {Url = url },
new CuiRectTransformComponent {AnchorMin = aMin, AnchorMax = aMax }
}
});
}
static public string Color(string hexColor, float alpha)
{
if (hexColor.StartsWith("#"))
hexColor = hexColor.TrimStart('#');
int red = int.Parse(hexColor.Substring(0, 2), NumberStyles.AllowHexSpecifier);
int green = int.Parse(hexColor.Substring(2, 2), NumberStyles.AllowHexSpecifier);
int blue = int.Parse(hexColor.Substring(4, 2), NumberStyles.AllowHexSpecifier);
return $"{(double)red / 255} {(double)green / 255} {(double)blue / 255} {alpha}";
}
}
public enum InventoryType { Main, Wear, Belt };
public enum GameMode
{
Normal,
Battlefield
}
public enum SpawnType
{
Random,
Consecutive
}
public enum RespawnType
{
None,
Timer,
Waves
}
#endregion
#region Components
public class EventPlayer : MonoBehaviour
{
private BasePlayer player;
private EventCorpse corpse;
public bool inEvent, isDead, isRespawning, isSpectating, OOB, isLeaving;
public string currentClass;
public int restoreAttempts;
void Awake()
{
inEvent = true;
isDead = false;
isRespawning = false;
isSpectating = false;
isLeaving = false;
OOB = false;
currentClass = string.Empty;
restoreAttempts = 0;
player = GetComponent<BasePlayer>();
}
private void OnDestroy()
{
AddToNetwork();
}
public BasePlayer GetPlayer() => player;
public void SetCorpse(EventCorpse corpse) => this.corpse = corpse ?? null;
public EventCorpse GetCorpse() => corpse ?? null;
public void RemoveFromNetwork(float refreshIn = 0)
{
if (!unnetworkedPlayers.Contains(player))
{
if (Net.sv.write.Start())
{
Net.sv.write.PacketID(Message.Type.EntityDestroy);
Net.sv.write.EntityID(player.net.ID);
Net.sv.write.UInt8((byte)BaseNetworkable.DestroyMode.None);
Net.sv.write.Send(new SendInfo(player.net.group.subscribers.Where(x => x.userid != player.userID).ToList()));
}
unnetworkedPlayers.Add(player);
if (refreshIn > 0)
InvokeHandler.Invoke(this, AddToNetwork, refreshIn);
}
}
public void AddToNetwork()
{
if (unnetworkedPlayers.Contains(player))
{
unnetworkedPlayers.Remove(player);
player.SendFullSnapshot();
}
}
}
public class EventCorpse : MonoBehaviour
{
public LootableCorpse entity;
void Awake()
{
entity = GetComponent<LootableCorpse>();
}
void OnDestroy()
{
if (entity == null) return;
if (entity?.containers == null) return;
foreach (var container in entity.containers)
{
Item[] array = container.itemList.ToArray();
for (int i = 0; i < array.Length; i++)
array[i].Remove(0f);
}
ItemManager.DoRemoves();
entity.DieInstantly();
}
}
private class GameTimer : MonoBehaviour
{
int timeRemaining;
void Awake() => timeRemaining = 0;
public void StartTimer(int time)
{
timeRemaining = time;
InvokeRepeating("TimerTick", 1f, 1f);
ins._TimerStarted = true;
}
void OnDestroy()
{
CancelInvoke("TimerTick");
ins._TimerStarted = false;
Destroy(this);
}
internal void TimerTick()
{
timeRemaining--;
if (timeRemaining == 0)
{
DestroyUI();
ins.CancelEvent(msg("TimeLimit"));
CancelInvoke("TimerTick");
}
else UpdateUITimer();
}
internal void UpdateUITimer()
{
string clockTime = "";
TimeSpan dateDifference = TimeSpan.FromSeconds(timeRemaining);
var hours = dateDifference.Hours;
var mins = dateDifference.Minutes;
var secs = dateDifference.Seconds;
if (hours > 0)
clockTime = string.Format("{0:00}:{1:00}:{2:00}", hours, mins, secs);
else clockTime = string.Format("{0:00}:{1:00}", mins, secs);
var CUI = UI.CreateElementContainer(ClockUI, "0.1 0.1 0.1 0.7", "0.45 0.95", "0.55 0.99", false, "Hud");
UI.CreateLabel(ref CUI, ClockUI, "", clockTime, 16, "0 0", "1 1");
foreach (var ePlayer in ins.EventPlayers)
{
CuiHelper.DestroyUi(ePlayer.GetPlayer(), ClockUI);
CuiHelper.AddUi(ePlayer.GetPlayer(), CUI);
}
}
internal void DestroyUI()
{
foreach (var player in BasePlayer.activePlayerList)
CuiHelper.DestroyUi(player, ClockUI);
}
}
private class WaveTimer : MonoBehaviour
{
private int waveTimer;
void Awake()
{
waveTimer = ins._Event.RespawnTimer;
InvokeRepeating("RespawnTick", 1f, 1f);
}
void OnDestroy() => CancelInvoke("RespawnTick");
private void RespawnTick()
{
waveTimer--;
if (waveTimer <= 0)
waveTimer = ins._Event.RespawnTimer;
}
public int GetTime() => waveTimer;
}
public class SpawnManager
{
public int spawnsCountA = 0;
public int spawnsCountB = 0;
private int lastSpawnA = 0;
private int lastSpawnB = 0;
public object GetSpawnPoint(string file, bool team = true, int min = -1, int max = -1)
{
if (ins._Event.SpawnType == SpawnType.Consecutive)
{
if (min > 0)
{
if (team)
{
if (lastSpawnA < min || (max > 0 && lastSpawnA > max))
{
lastSpawnA = min - 1;
return GetNextSpawn(file, team);
}
}
else
{
if (lastSpawnB < min || (max > 0 && lastSpawnB > max))
{
lastSpawnB = min - 1;
return GetNextSpawn(file, team);
}
}
}
return GetNextSpawn(file, team);
}
else
{
if (min > 0)
{
if (max > 0)
return GetRandomRange(file, min, max);
else return GetRandomRange(file, min, 9999);
}
return GetRandomSpawn(file);
}
}
private object GetRandomSpawn(string file) => ins.Spawns.Call("GetRandomSpawn", file);
private object GetRandomRange(string file, int min, int max) => ins.Spawns.Call("GetRandomSpawnRange", file, min, max);
private object GetNextSpawn(string file, bool team)
{
int number;
if (team)
{
++lastSpawnA;
if (lastSpawnA >= spawnsCountA)
lastSpawnA = 0;
number = lastSpawnA;
}
else
{
++lastSpawnB;
if (lastSpawnB >= spawnsCountB)
lastSpawnB = 0;
number = lastSpawnB;
}
return ins.Spawns.Call("GetSpawn", file, number);
}
public void SetSpawnCount(bool team, int number)
{
if (team)
spawnsCountA = number;
else spawnsCountB = number;
}
}
private class MessageData
{
public int timecount;
public string elementID;
public string message;
public bool started;
public MessageData(string message, int timecount, string elementID)
{
this.timecount = timecount;
this.elementID = elementID;
this.message = message;
started = false;
}
public void Begin(string elementID)
{
this.elementID = elementID;
started = true;
StartDestroy();
}
void StartDestroy()
{
if (timecount > 0)
{
timecount--;
ins.timer.Once(1, () => StartDestroy());
}
else if (timecount == 0)
{
ins.DestroyPopupUI(this);
}
}
}
private EventPlayer GetUser(BasePlayer player)
{
if (player == null) return null;
var eventPlayer = player.GetComponent<EventPlayer>();
if (eventPlayer == null) return null;
return eventPlayer;
}
#endregion
#region Event Management
public object SelectEvent(string name)
{
if (!(EventGames.ContainsKey(name))) return string.Format(msg("noEvent"), name);
if (_Started || _Open) return msg("isAlreadyStarted");
Interface.CallHook("OnSelectEventGamePost", new object[] { name });
_Event.EventType = name;
return true;
}
object SetEventDetails()
{
var success = SelectEvent(_Event.EventType);
if (success is string) return (string)success;
if (EventGames.ContainsKey(_Event.EventType))
{
EventMode = _Event.GameMode;
if (EventGames[_Event.EventType].RequiresSpawns && !string.IsNullOrEmpty(_Event.Spawnfile))
{
Interface.CallHook("SetSpawnfile", true, _Event.Spawnfile);
object count = Spawns.Call("GetSpawnsCount", _Event.Spawnfile);
if (count is int)
SpawnCount.SetSpawnCount(true, (int)count);
}
if (EventGames[_Event.EventType].RequiresMultipleSpawns && !string.IsNullOrEmpty(_Event.Spawnfile2))
{
Interface.CallHook("SetSpawnfile", false, _Event.Spawnfile2);
object count = Spawns.Call("GetSpawnsCount", _Event.Spawnfile2);
if (count is int)
SpawnCount.SetSpawnCount(false, (int)count);
}
if (_Event.GameRounds > 0)
Interface.CallHook("SetGameRounds", _Event.GameRounds);
if (_Event.EnemiesToSpawn > 0)
Interface.CallHook("SetEnemyCount", _Event.EnemiesToSpawn);
if (!string.IsNullOrEmpty(EventGames[_Event.EventType].ScoreType))
Interface.CallHook("SetScoreLimit", _Event.ScoreLimit);
if (!string.IsNullOrEmpty(_Event.WeaponSet))
Interface.CallHook("ChangeWeaponSet", _Event.WeaponSet);
if (!string.IsNullOrEmpty(_Event.Kit))
Interface.CallHook("OnSelectKit", _Event.Kit);
if (!string.IsNullOrEmpty(_Event.ZoneID))
{
Interface.CallHook("SetEventZone", _Event.ZoneID);
if (configData.Options.DropCorpseOnDeath)
{
var hasFlag = ZoneManager?.Call("HasFlag", _Event.ZoneID, "NoCorpse");
if (hasFlag is bool && (bool)hasFlag)
{
ZoneManager?.Call("RemoveFlag", _Event.ZoneID, "NoCorpse");
ResetNoCorpse = true;
}
}
}
if (EventGames[_Event.EventType].ForceCloseOnStart)
_Event.CloseOnStart = true;
if (!EventGames[_Event.EventType].CanChooseRespawn)
{
var type = Interface.CallHook("GetRespawnType");
if (type != null)
_Event.RespawnType = (RespawnType)type;
var time = Interface.CallHook("GetRespawnTime");
if (time != null)
_Event.RespawnTimer = (int)time;
}
return null;
}
return "Error setting event details.Please check your settings";
}
public object OpenEvent()
{
if (_Event == null) return msg("noneSelected");
if (string.IsNullOrEmpty(_Event.EventType)) return msg("noTypeSelected");
if (_Open) return string.Format(msg("isAlreadyOpen"), _Event.EventType);
if (_Started && !_Open)
{
if (!EventGames[_Event.EventType].ForceCloseOnStart)
{
_Open = true;
return null;
}
else return msg("cantOpen");
}
var success = ValidateEvent(_Event);
if (success is string)
return (string)success;
success = SetEventDetails();
if (success is string)
return (string)success;
success = Interface.Oxide.CallHook("CanEventOpen");
if (success is string)
return (string)success;
EMInterface.EventVoting.OpenEventVoting(false);
_Open = true;
GameScores = new ScoreData();
EventPlayers = new List<EventPlayer>();
Joiners = new List<BasePlayer>();
_EventName = _Event.EventType;
if (_Event.GameMode == GameMode.Battlefield)
_EventName = $"{msg("Battlefield")} - {_EventName}";
if (!string.IsNullOrEmpty(_CurrentEventConfig))
// ... [Code preview truncated due to length. Full source in attached file] ...