Иконка ресурса

FREE EventManager : 4.0.7 4.0.7

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Requires: EMInterface

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Game.Rust.Cui;
using Oxide.Core.Plugins;
using UnityEngine;
using System.Linq;
using Network;
using Facepunch;
using UI = Oxide.Plugins.EMInterface.UI;
using UI4 = Oxide.Plugins.EMInterface.UI4;

namespace Oxide.Plugins
{
    using EventManagerEx;    

    [Info("EventManager", "k1lly0u", "4.0.7")]
    [Description("The core mechanics for arena combat games")]
    public class EventManager : RustPlugin
    {
        #region Fields        
        private DynamicConfigFile restorationData, eventData;

        [PluginReference]
        private Plugin Economics, Kits, NoEscape, ServerRewards, Spawns, ZoneManager;


        private Timer _autoEventTimer;

        private RewardType rewardType;

        private int scrapItemId;

        private static Regex hexFilter;


        public Hash<string, IEventPlugin> EventModes { get; set; } = new Hash<string, IEventPlugin>();

        public EventData Events { get; private set; }

        private RestoreData Restore { get; set; }

        public static EventManager Instance { get; private set; }

        public static BaseEventGame BaseManager { get; internal set; }

        public static ConfigData Configuration { get; set; }

        public static EventResults LastEventResult { get; private set; }

        public static bool IsUnloading { get; private set; }


        internal const string ADMIN_PERMISSION = "eventmanager.admin";
        #endregion
        
        #region Oxide Hooks
        private void Loaded()
        {
            restorationData = Interface.Oxide.DataFileSystem.GetFile("EventManager/restoration_data");

            eventData = Interface.Oxide.DataFileSystem.GetFile("EventManager/event_data");

            permission.RegisterPermission(ADMIN_PERMISSION, this);

            Instance = this;
            IsUnloading = false;
            LastEventResult = new EventResults();

            LoadData();
        }

        protected override void LoadDefaultMessages() => lang.RegisterMessages(Messages, this);

        private void OnServerInitialized()
        {
            if (!CheckDependencies())
                return;            

            rewardType = ParseType<RewardType>(Configuration.Reward.Type);

            scrapItemId = ItemManager.FindItemDefinition("scrap")?.itemid ?? 0;

            hexFilter = new Regex("^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");

            UnsubscribeAll();

            foreach (BasePlayer player in BasePlayer.activePlayerList)
                OnPlayerConnected(player);

            QueueAutoEvent();
        }

        private void Unload()
        {
            IsUnloading = true;

            SaveRestoreData();
            
            BaseEventPlayer[] eventPlayers = UnityEngine.Object.FindObjectsOfType<BaseEventPlayer>();
            for (int i = 0; i < eventPlayers?.Length; i++)            
                UnityEngine.Object.DestroyImmediate(eventPlayers[i]);
            
            if (BaseManager != null)
                UnityEngine.Object.DestroyImmediate(BaseManager.gameObject);

            hexFilter = null;

            LastEventResult = null;
            BaseManager = null;
            Configuration = null;
            Instance = null;            
        }

        private void OnServerSave() => SaveRestoreData();

        private void OnPlayerConnected(BasePlayer player)
        {
            if (player.IsSleeping() || player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot))
            {
                timer.Once(1f, () => OnPlayerConnected(player));
                return;
            }

            UnlockInventory(player);
            
            if (Restore.HasRestoreData(player.userID))
                Restore.RestorePlayer(player);
        }
       
        private void OnPlayerDisconnected(BasePlayer player)
        {
            BaseEventPlayer eventPlayer = GetUser(player);
            if (eventPlayer != null)
            {
                if (BaseManager != null)
                    BaseManager.LeaveEvent(player);
                else UnityEngine.Object.DestroyImmediate(eventPlayer);

                if (player.IsAlive())
                    player.DieInstantly();
            }
        }

        private void OnEntityTakeDamage(BaseEntity entity, HitInfo hitInfo)
        {
            if (entity == null || hitInfo == null)
                return;

            BasePlayer player = entity.ToPlayer();

            if (player != null)
            {
                BaseEventPlayer eventPlayer = GetUser(player);
                if (eventPlayer != null)
                {
                    if (BaseManager == null)
                        return;
                    
                    BaseManager.OnPlayerTakeDamage(eventPlayer, hitInfo);
                }
            }
            else
            {
                BaseEventPlayer attacker = GetUser(hitInfo.InitiatorPlayer);
                if (attacker != null)
                {
                    if (BaseManager != null)
                    {
                        if (BaseManager.CanDealEntityDamage(attacker, entity, hitInfo))
                            return;
                    }
                    ClearDamage(hitInfo);
                }
            }
        }

        private object CanBeWounded(BasePlayer player, HitInfo hitInfo)
        {
            BaseEventPlayer eventPlayer = GetUser(player);
            if (eventPlayer != null && BaseManager != null)
                return false;
            return null;
        }

        private object OnPlayerDeath(BasePlayer player, HitInfo hitInfo)
        {
            if (player != null)
            {
                BaseEventPlayer eventPlayer = GetUser(player);
                if (eventPlayer != null && BaseManager != null)
                { 
                    if (!eventPlayer.IsDead)
                        BaseManager.PrePlayerDeath(eventPlayer, hitInfo);
                    return false;                    
                }
            }
            return null;
        }


        private object CanSpectateTarget(BasePlayer player, string name)
        {
            BaseEventPlayer eventPlayer = player.GetComponent<BaseEventPlayer>();
            if (eventPlayer != null && eventPlayer.Player.IsSpectating())
            {
                eventPlayer.UpdateSpectateTarget();
                return false;
            }
            return null;
        }

        private void OnEntityBuilt(Planner planner, GameObject gameObject)
        {
            BasePlayer player = planner?.GetOwnerPlayer();
            if (player == null)
                return;

            BaseCombatEntity baseCombatEntity = gameObject?.ToBaseEntity() as BaseCombatEntity;
            if (baseCombatEntity == null)
                return;

            BaseEventPlayer eventPlayer = GetUser(player);
            if (eventPlayer != null && BaseManager != null)
                BaseManager.OnEntityDeployed(baseCombatEntity);
        }

        private void OnItemDeployed(Deployer deployer, BaseCombatEntity baseCombatEntity)
        {
            BasePlayer player = deployer.GetOwnerPlayer();
            if (player == null)
                return;

            BaseEventPlayer eventPlayer = GetUser(player);
            if (eventPlayer != null && BaseManager != null)
                BaseManager.OnEntityDeployed(baseCombatEntity);
        }

        private object OnCreateWorldProjectile(HitInfo hitInfo, Item item)
        {
            if (hitInfo == null)
                return null;

            if (hitInfo.InitiatorPlayer != null)
            {
                BaseEventPlayer eventPlayer = GetUser(hitInfo.InitiatorPlayer);
                if (eventPlayer != null)
                    return false;
            }

            if (hitInfo.HitEntity?.ToPlayer() != null)
            {
                BaseEventPlayer eventPlayer = GetUser(hitInfo.HitEntity.ToPlayer());
                if (eventPlayer != null)
                    return false;
            }

            return null;
        }

        private object CanDropActiveItem(BasePlayer player)
        {
            BaseEventPlayer eventPlayer = GetUser(player);
            if (eventPlayer != null)                           
                return false;            
            return null;
        }

        private object OnPlayerCommand(BasePlayer player, string command, string[] args)
        {
            BaseEventPlayer eventPlayer = GetUser(player);

            if (player == null || player.IsAdmin || eventPlayer == null)
                return null;

            if (Configuration.Event.CommandBlacklist.Any(x => x.StartsWith("/") ? x.Substring(1).ToLower() == command : x.ToLower() == command))
            {
                SendReply(player, Message("Error.CommandBlacklisted", player.userID));
                return false;
            }
            return null;
        }

        private object OnServerCommand(ConsoleSystem.Arg arg)
        {
            BasePlayer player = arg.Player();
            BaseEventPlayer eventPlayer = GetUser(player);

            if (player == null || player.IsAdmin || eventPlayer == null || arg.Args == null)
                return null;

            if (Configuration.Event.CommandBlacklist.Any(x => arg.cmd.FullName.Equals(x, StringComparison.OrdinalIgnoreCase)))
            {
                SendReply(player, Message("Error.CommandBlacklisted", player.userID));
                return false;
            }
            return null;
        }
        #endregion

        #region AutoEvents
        private int _nextEventIndex = -1;

        private void QueueAutoEvent()
        {
            if (!Configuration.AutoEvents.Enabled || Configuration.AutoEvents.Events.Length == 0)
                return;

            _autoEventTimer?.Destroy();

            _autoEventTimer = timer.In(Configuration.AutoEvents.Interval, InitializeAutoEvent);
        }

        private void InitializeAutoEvent()
        {            
            string eventName;
            if (Configuration.AutoEvents.Randomize)
                eventName = Configuration.AutoEvents.Events.GetRandom();
            else
            {
                _nextEventIndex += 1;

                if (_nextEventIndex >= Configuration.AutoEvents.Events.Length)
                    _nextEventIndex = 0;

                eventName = Configuration.AutoEvents.Events[_nextEventIndex];
            }

            object success = OpenEvent(eventName);
            if (success != null)
                QueueAutoEvent();
        }
        #endregion

        #region Event Construction
        public static void RegisterEvent(string eventName, IEventPlugin plugin) => Instance.EventModes[eventName] = plugin;

        public static void UnregisterEvent(string eventName) => Instance.EventModes.Remove(eventName);

        public object OpenEvent(string eventName)
        {
            if (Events.events.TryGetValue(eventName, out EventConfig eventConfig))
            {
                if (!EventModes.TryGetValue(eventConfig.EventType, out IEventPlugin plugin))                
                    return $"Unable to find event plugin for game mode: {eventConfig.EventType}";
                
                if (plugin == null)                
                    return $"Unable to initialize event plugin: {eventConfig.EventType}.Plugin is either unloaded or the class does not derive from IEventGame";

                object success = ValidateEventConfig(eventConfig);
                if (success is string)
                    return $"Failed to open event : {(string)success}";

                if (!plugin.InitializeEvent(eventConfig))
                    return $"There was a error initializing the event : {eventConfig.EventType}";

                _autoEventTimer?.Destroy();

                return null;
            }
            else return "Failed to find a event with the specified name";
        }

        public static bool InitializeEvent<T>(IEventPlugin plugin, EventConfig config) where T : BaseEventGame
        {
            if (BaseManager != null)
                return false;

            BaseManager = new GameObject(config.EventName).AddComponent<T>();
            BaseManager.InitializeEvent(plugin, config);           

            return true;
        }
        #endregion

        #region Functions
        public IEventPlugin GetPlugin(string name)
        {
            if (EventModes.TryGetValue(name, out IEventPlugin eventPlugin))
                return eventPlugin;

            return null;
        }

        private bool CheckDependencies()
        {
            if (!Spawns)
            {
                PrintError("Unable to load EventManager - Spawns database not found.Please download Spawns database to continue");
                rust.RunServerCommand("oxide.unload", "EventManager");
                return false;
            }

            if (!ZoneManager)            
                PrintError("ZoneManager is not installed! Unable to restrict event players to zones");
               
            if (!Kits)
                PrintError("Kits is not installed! Unable to issue any weapon kits");

            return true;
        }

        private void UnsubscribeAll()
        {
            Unsubscribe(nameof(OnEntityTakeDamage));
            Unsubscribe(nameof(CanBeWounded));
            Unsubscribe(nameof(OnPlayerDeath));
            Unsubscribe(nameof(OnEntityBuilt));
            Unsubscribe(nameof(OnItemDeployed));
            Unsubscribe(nameof(OnCreateWorldProjectile));
            Unsubscribe(nameof(CanDropActiveItem));
            Unsubscribe(nameof(OnPlayerCommand));
            Unsubscribe(nameof(OnServerCommand));
        }

        private void SubscribeAll()
        {
            Subscribe(nameof(OnEntityTakeDamage));
            Subscribe(nameof(CanBeWounded));
            Subscribe(nameof(OnPlayerDeath));
            Subscribe(nameof(OnEntityBuilt));
            Subscribe(nameof(OnItemDeployed));
            Subscribe(nameof(OnCreateWorldProjectile));
            Subscribe(nameof(CanDropActiveItem));
            Subscribe(nameof(OnPlayerCommand));
            Subscribe(nameof(OnServerCommand));
        }

        private static void Broadcast(string key, params object[] args)
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player != null && player.IsConnected)
                    player.SendConsoleCommand("chat.add", 0, Configuration.Message.ChatIcon, string.Format(Message(key, player.userID), args));
            }
        }

        internal static bool IsValidHex(string s) => hexFilter.IsMatch(s);
        #endregion

        #region Classes and Components  
        public class BaseEventGame : MonoBehaviour
        {
            internal IEventPlugin Plugin { get; private set; }

            internal EventConfig Config { get; private set; }

            public EventStatus Status { get; protected set; }

            protected GameTimer Timer { get; set; }


            internal SpawnSelector _spawnSelectorA;

            internal SpawnSelector _spawnSelectorB;

            protected CuiElementContainer scoreContainer = null;

            internal List<BasePlayer> joiningPlayers = Pool.Get<List<BasePlayer>>();

            internal List<BaseEventPlayer> eventPlayers = Pool.Get<List<BaseEventPlayer>>();

            internal List<ScoreEntry> scoreData = Pool.Get<List<ScoreEntry>>();

            private List<BaseCombatEntity> _deployedObjects = Pool.Get<List<BaseCombatEntity>>();
                        
            private bool _isClosed = false;

            private double _startsAtTime;


            internal string TeamAColor { get; set; }

            internal string TeamBColor { get; set; }

            internal string TeamAClothing { get; set; }

            internal string TeamBClothing { get; set; }

            public bool GodmodeEnabled { get; protected set; } = true;

            internal string EventInformation
            {
                get
                {
                    string str = string.Format(Message("Info.Event.Current"), Config.EventName, Config.EventType);
                    str += string.Format(Message("Info.Event.Player"), eventPlayers.Count, Config.MaximumPlayers);
                    return str;
                }
            }

            internal string EventStatus => string.Format(Message("Info.Event.Status"), Status);
            
            #region Initialization and Destruction 
            /// <summary>
            /// Called when the event GameObject is destroyed
            /// </summary>
            protected virtual void OnDestroy()
            {
                CleanupEntities();

                for (int i = eventPlayers.Count - 1; i >= 0; i--)
                {
                    BaseEventPlayer eventPlayer = eventPlayers[i];

                    if (eventPlayer.IsDead)
                        ResetPlayer(eventPlayer.Player);

                    LeaveEvent(eventPlayer);
                }
                
                Pool.FreeUnmanaged(ref scoreData);
                Pool.FreeUnmanaged(ref joiningPlayers);
                Pool.FreeUnmanaged(ref _deployedObjects);
                Pool.FreeUnmanaged(ref eventPlayers);

                _spawnSelectorA?.Destroy();
                _spawnSelectorB?.Destroy();

                Timer?.StopTimer();

                Instance?.UnsubscribeAll();

                Instance?.QueueAutoEvent();

                Destroy(gameObject);
            }

            /// <summary>
            /// The first function called when an event is being opened
            /// </summary>
            /// <param name="plugin">The plugin the event game belongs to</param>
            /// <param name="config">The event config</param>
            internal virtual void InitializeEvent(IEventPlugin plugin, EventConfig config)
            {
                this.Plugin = plugin;
                this.Config = config;

                _spawnSelectorA = new SpawnSelector(config.EventName, config.TeamConfigA.Spawnfile);

                if (plugin.IsTeamEvent)
                {                    
                    TeamAColor = config.TeamConfigA.Color;
                    TeamBColor = config.TeamConfigB.Color;

                    if (string.IsNullOrEmpty(TeamAColor) || TeamAColor.Length < 6 || TeamAColor.Length > 6 || !hexFilter.IsMatch(TeamAColor))
                        TeamAColor = "#EA3232";
                    else TeamAColor = "#" + TeamAColor;

                    if (string.IsNullOrEmpty(TeamBColor) || TeamBColor.Length < 6 || TeamBColor.Length > 6 || !hexFilter.IsMatch(TeamBColor))
                        TeamBColor = "#3232EA";
                    else TeamBColor = "#" + TeamBColor;

                    _spawnSelectorB = new SpawnSelector(config.EventName, config.TeamConfigB.Spawnfile);
                }

                Timer = new GameTimer(this);

                GodmodeEnabled = true;

                OpenEvent();
            }
            #endregion

            #region Event Management 
            /// <summary>
            /// Opens the event for players to join
            /// </summary>
            internal virtual void OpenEvent()
            {
                _isClosed = false;
                Status = EventManager.EventStatus.Open;

                if (Configuration.Message.Announce)
                {
                    _startsAtTime = Time.time + Configuration.Timer.Start;

                    InvokeHandler.InvokeRepeating(this, BroadcastOpenEvent, 0f, Configuration.Message.AnnounceInterval);
                }

                InvokeHandler.Invoke(this, PrestartEvent, Configuration.Timer.Start);
            }

            /// <summary>
            /// Closes the event and prevent's more players from joining
            /// </summary>
            internal virtual void CloseEvent()
            {
                _isClosed = true;
                Broadcast("Notification.EventClosed");

                InvokeHandler.CancelInvoke(this, BroadcastOpenEvent);
            }

            /// <summary>
            /// The event prestart where players are created and sent to the arena
            /// </summary>
            internal virtual void PrestartEvent()
            {
                if (!HasMinimumRequiredPlayers())
                {
                    Broadcast("Notification.NotEnoughToStart");
                    EndEvent();
                    return;
                }

                InvokeHandler.CancelInvoke(this, BroadcastOpenEvent);

                Instance.SubscribeAll();

                Status = EventManager.EventStatus.Prestarting;

                StartCoroutine(CreateEventPlayers());                
            }
            
            /// <summary>
            /// Start's the event
            /// </summary>
            protected virtual void StartEvent()
            {
                InvokeHandler.CancelInvoke(this, PrestartEvent);

                if (!HasMinimumRequiredPlayers())
                {
                    Broadcast("Notification.NotEnoughToStart");
                    EndEvent();
                    return;
                }

                Timer.StopTimer();

                Status = EventManager.EventStatus.Started;

                if (Config.TimeLimit > 0)
                    Timer.StartTimer(Config.TimeLimit, string.Empty, EndEvent);

                GodmodeEnabled = false;

                eventPlayers.ForEach((BaseEventPlayer eventPlayer) =>
                {
                    if (eventPlayer?.Player == null)
                        return;

                    if (eventPlayer.IsDead)
                        RespawnPlayer(eventPlayer);
                    else
                    {
                        ResetPlayer(eventPlayer.Player);
                        OnPlayerRespawn(eventPlayer);
                    }
                });
            }

            /// <summary>
            /// End's the event and restore's all player's back to the state they were in prior to the event starting
            /// </summary>
            internal virtual void EndEvent()
            {
                InvokeHandler.CancelInvoke(this, BroadcastOpenEvent);

                InvokeHandler.CancelInvoke(this, PrestartEvent);

                Timer.StopTimer();

                Status = EventManager.EventStatus.Finished;

                GodmodeEnabled = true;

                LastEventResult.UpdateFromEvent(this);

                ProcessWinners();

                eventPlayers.ForEach((BaseEventPlayer eventPlayer) =>
                {
                    if (eventPlayer?.Player == null)
                        return;

                    if (eventPlayer.IsDead)  
                        ResetPlayer(eventPlayer.Player);
                    
                    EventStatistics.Data.OnGamePlayed(eventPlayer.Player, Config.EventType);
                });

                EventStatistics.Data.OnGamePlayed(Config.EventType);

                EjectAllPlayers();

                DestroyImmediate(this);
            }
            #endregion

            #region Player Management
            internal bool IsOpen()
            {
                if (_isClosed || Status == EventManager.EventStatus.Finished)
                    return false;

                if (((int)Status < 2 && joiningPlayers.Count >= Config.MaximumPlayers) || eventPlayers.Count >= Config.MaximumPlayers)
                    return false;

                if (!string.IsNullOrEmpty(CanJoinEvent()))
                    return false;

                return true;
            }

            internal bool CanJoinEvent(BasePlayer player)
            {
                if (_isClosed)
                {
                    player.ChatMessage(Message("Notification.EventClosed", player.userID));
                    return false;
                }

                if (Status == EventManager.EventStatus.Finished)
                {
                    player.ChatMessage(Message("Notification.EventFinished", player.userID));
                    return false;
                }

                if (((int)Status < 2 && joiningPlayers.Count >= Config.MaximumPlayers) || eventPlayers.Count >= Config.MaximumPlayers)
                {
                    player.ChatMessage(Message("Notification.MaximumPlayers", player.userID));
                    return false;
                }

                string str = CanJoinEvent();

                if (!string.IsNullOrEmpty(str))
                {
                    player.ChatMessage(str);
                    return false;
                }

                return true;
            }

            /// <summary>
            /// Allow or disallow players to join the event
            /// </summary>
            /// <returns>Supply a (string) reason to disallow, or a empty string to allow</returns>
            protected virtual string CanJoinEvent()
            {
                return string.Empty;
            }

            /// <summary>
            /// Override to perform additional logic when a player joins an event
            /// </summary>
            /// <param name="player">The BasePlayer object of the player joining the event</param>
            /// <param name="team">The team the player should be placed in</param>
            internal virtual void JoinEvent(BasePlayer player, Team team = Team.None)
            {
                if (Status == EventManager.EventStatus.Started)
                    CreateEventPlayer(player, team);
                else joiningPlayers.Add(player);

                if (Configuration.Message.BroadcastJoiners)
                    Broadcast("Notification.PlayerJoined", player.displayName, Config.EventName);                
            }

            /// <summary>
            /// Override to perform additional logic when a player leaves an event.This is called when the player uses the leave chat command prior to destroying the BaseEventPlayer
            /// </summary>
            /// <param name="player">The BasePlayer object of the player leaving the event</param>
            internal virtual void LeaveEvent(BasePlayer player)
            {
                if (joiningPlayers.Contains(player))
                {
                    joiningPlayers.Remove(player);

                    if (Configuration.Message.BroadcastLeavers)
                        Broadcast("Notification.PlayerLeft", player.displayName, Config.EventName);

                    return;
                }

                BaseEventPlayer eventPlayer = GetUser(player);
                if (eventPlayer == null)
                    return;

                LeaveEvent(eventPlayer);
            }

            /// <summary>
            /// Override to perform additional logic when a event player leaves an event
            /// </summary>
            /// <param name="eventPlayer">The BaseEventPlayer object of the player leaving the event</param>
            internal virtual void LeaveEvent(BaseEventPlayer eventPlayer)
            {
                BasePlayer player = eventPlayer.Player;

                if (!string.IsNullOrEmpty(Config.ZoneID))
                    Instance.ZoneManager?.Call("RemovePlayerFromZoneWhitelist", Config.ZoneID, player);

                eventPlayers.Remove(eventPlayer);

                DestroyImmediate(eventPlayer);

                if (!player.IsConnected || player.IsSleeping() || IsUnloading)
                    player.Die();
                else Instance.Restore.RestorePlayer(player);                

                if (Status != EventManager.EventStatus.Finished && !HasMinimumRequiredPlayers())
                {
                    BroadcastToPlayers("Notification.NotEnoughToContinue");
                    EndEvent();
                }
            }

            private IEnumerator CreateEventPlayers()
            {
                for (int i = joiningPlayers.Count - 1; i >= 0; i--)
                {
                    BasePlayer joiner = joiningPlayers[i];

                    EMInterface.DestroyAllUI(joiner);

                    CreateEventPlayer(joiner, GetPlayerTeam(joiner));

                    yield return CoroutineEx.waitForEndOfFrame;
                    yield return CoroutineEx.waitForEndOfFrame;
                }

                UpdateScoreboard();

                Timer.StartTimer(Configuration.Timer.Prestart, Message("Notification.RoundStartsIn"), StartEvent);
            }

            /// <summary>
            /// Override to perform additional logic when initializing the BaseEventPlayer component
            /// </summary>
            /// <param name="player">The BasePlayer object of the player joining the event</param>
            /// <param name="team">The team this player is on</param>
            protected virtual void CreateEventPlayer(BasePlayer player, Team team = Team.None)
            {
                if (player == null)
                    return;

                joiningPlayers.Remove(player);

                BaseEventPlayer eventPlayer = AddPlayerComponent(player);

                eventPlayer.ResetPlayer();

                eventPlayer.Event = this;

                eventPlayer.Team = team;

                eventPlayers.Add(eventPlayer);

                if (!Config.AllowClassSelection || GetAvailableKits(eventPlayer.Team).Count <= 1)
                    eventPlayer.Kit = GetAvailableKits(team).First();

                SpawnPlayer(eventPlayer, Status == EventManager.EventStatus.Started, true);

                if (!string.IsNullOrEmpty(Config.ZoneID))                
                    Instance.ZoneManager?.Call("AddPlayerToZoneWhitelist", Config.ZoneID, player);                
            }

            /// <summary>
            /// Override to assign players to teams
            /// </summary>
            /// <param name="player"></param>
            /// <returns>The team the player will be assigned to</returns>
            protected virtual Team GetPlayerTeam(BasePlayer player) => Team.None;

            /// <summary>
            /// Add's the BaseEventPlayer component to the player.Override with your own component if you want to extend the BaseEventPlayer class
            /// </summary>
            /// <param name="player"></param>
            /// <param name="team"></param>
            /// <returns>The BaseEventPlayer component</returns>
            protected virtual BaseEventPlayer AddPlayerComponent(BasePlayer player) => player.gameObject.GetComponent<BaseEventPlayer>() ?? player.gameObject.AddComponent<BaseEventPlayer>();                
            
            /// <summary>
            /// Called prior to a event player respawning
            /// </summary>
            /// <param name="baseEventPlayer"></param>
            internal virtual void OnPlayerRespawn(BaseEventPlayer baseEventPlayer)
            {
                SpawnPlayer(baseEventPlayer, Status == EventManager.EventStatus.Started);
            }

            /// <summary>
            /// Spawn's the specified player
            /// </summary>
            /// <param name="eventPlayer"></param>
            /// <param name="giveKit">Should this player recieve a kit?</param>
            /// <param name="sleep">Should this player be put to sleep before teleporting?</param>
            internal void SpawnPlayer(BaseEventPlayer eventPlayer, bool giveKit = true, bool sleep = false)
            {
                BasePlayer player = eventPlayer?.Player;
                if (player == null)
                    return;

                eventPlayer.Player.GetMounted()?.AttemptDismount(eventPlayer.Player);

                if (eventPlayer.Player.HasParent())
                    eventPlayer.Player.SetParent(null, true);

                StripInventory(player);

                ResetMetabolism(player);

                MovePosition(player, eventPlayer.Team == Team.B ? _spawnSelectorB.GetSpawnPoint() : _spawnSelectorA.GetSpawnPoint(), sleep);

                if (string.IsNullOrEmpty(eventPlayer.Kit))
                {
                    eventPlayer.ForceSelectClass();
                    EMInterface.DisplayDeathScreen(eventPlayer, Message("UI.SelectClass", eventPlayer.Player.userID), true);
                    return;
                }
                
                UpdateScoreboard(eventPlayer);

                if (giveKit)
                {
                    Instance.NextTick(() =>
                    {
                        if (!CanGiveKit(eventPlayer))
                            return;

                        GiveKit(player, eventPlayer.Kit);

                        OnKitGiven(eventPlayer);                        
                    });
                }

                eventPlayer.ApplyInvincibility();

                OnPlayerSpawned(eventPlayer);
            }

            /// <summary>
            /// Called after a player has spawned/respawned
            /// </summary>
            /// <param name="eventPlayer">The player that has spawned</param>
            protected virtual void OnPlayerSpawned(BaseEventPlayer eventPlayer) { }

            /// <summary>
            /// Kicks all players out of the event
            /// </summary>
            protected void EjectAllPlayers()
            {
                for (int i = eventPlayers.Count - 1; i >= 0; i--)
                    LeaveEvent(eventPlayers[i].Player);
                eventPlayers.Clear();
            }

            /// <summary>
            /// Reset's all players that are currently dead and respawn's them
            /// </summary>
            protected void RespawnAllPlayers()
            {
                for (int i = eventPlayers.Count - 1; i >= 0; i--)
                    RespawnPlayer(eventPlayers[i]);                
            }

            private bool HasMinimumRequiredPlayers()
            {
                if (Status == EventManager.EventStatus.Open)
                    return joiningPlayers.Count >= Config.MinimumPlayers;
                else return eventPlayers.Count >= Config.MinimumPlayers;
            }
            #endregion

            #region Damage and Death
            /// <summary>
            /// Called when a player deals damage to a entity that is not another event player
            /// </summary>
            /// <param name="attacker">The player dealing the damage</param>
            /// <param name="entity">The entity that was hit</param>
            /// <param name="hitInfo">The HitInfo</param>
            /// <returns>True allows damage, false prevents damage</returns>
            internal virtual bool CanDealEntityDamage(BaseEventPlayer attacker, BaseEntity entity, HitInfo hitInfo)
            {
                return false;
            }

            /// <summary>
            /// Scale's player-to-player damage
            /// </summary>
            /// <param name="eventPlayer">The player that is attacking</param>
            /// <returns>1.0f is normal damage</returns>
            protected virtual float GetDamageModifier(BaseEventPlayer eventPlayer) => 1f;

            /// <summary>
            /// Calculates and applies damage to the player
            /// </summary>
            /// <param name="eventPlayer"></param>
            /// <param name="hitInfo"></param>
            internal virtual void OnPlayerTakeDamage(BaseEventPlayer eventPlayer, HitInfo hitInfo)
            {
                BaseEventPlayer attacker = GetUser(hitInfo.InitiatorPlayer);

                if (GodmodeEnabled || eventPlayer.IsDead || eventPlayer.IsInvincible)
                {
                    ClearDamage(hitInfo);
                    return;
                }
                
                float damageModifier = GetDamageModifier(attacker);
                if (damageModifier != 1f)
                    hitInfo.damageTypes.ScaleAll(damageModifier);

                eventPlayer.OnTakeDamage(attacker?.Player.userID ?? 0U);
            }

            /// <summary>
            /// Called prior to event player death logic.Prepares the player for the death cycle by hiding them from other players
            /// </summary>
            /// <param name="eventPlayer"></param>
            /// <param name="hitInfo"></param>
            internal virtual void PrePlayerDeath(BaseEventPlayer eventPlayer, HitInfo hitInfo)
            {
                if (CanDropBackpack())
                    eventPlayer.DropInventory();

                if (eventPlayer.Player.isMounted)
                {
                    BaseMountable baseMountable = eventPlayer.Player.GetMounted();
                    if (baseMountable != null)
                    {
                        baseMountable.DismountPlayer(eventPlayer.Player);
                        eventPlayer.Player.EnsureDismounted();
                    }
                }

                eventPlayer.IsDead = true;

                UpdateDeadSpectateTargets(eventPlayer);

                eventPlayer.Player.limitNetworking = true;

                eventPlayer.Player.DisablePlayerCollider();

                eventPlayer.Player.RemoveFromTriggers();

                eventPlayer.RemoveFromNetwork();                

                OnEventPlayerDeath(eventPlayer, GetUser(hitInfo?.InitiatorPlayer), hitInfo);

                ClearDamage(hitInfo);
            }

            internal virtual void OnEventPlayerDeath(BaseEventPlayer victim, BaseEventPlayer attacker = null, HitInfo hitInfo = null)
            {
                if (victim == null || victim.Player == null)
                    return;

                StripInventory(victim.Player);

                if (Configuration.Message.BroadcastKills)
                    DisplayKillToChat(victim, attacker?.Player != null ? attacker.Player.displayName : string.Empty);
            }

            /// <summary>
            /// Display's the death message in chat
            /// </summary>
            /// <param name="victim"></param>
            /// <param name="attackerName"></param>
            protected virtual void DisplayKillToChat(BaseEventPlayer victim, string attackerName)
            {
                if (string.IsNullOrEmpty(attackerName))
                {
                    if (victim.IsOutOfBounds)
                        BroadcastToPlayers("Notification.Death.OOB", victim.Player.displayName);
                    else BroadcastToPlayers("Notification.Death.Suicide", victim.Player.displayName);
                }
                else BroadcastToPlayers("Notification.Death.Killed", victim.Player.displayName, attackerName);                
            }
            #endregion

            #region Winners
            /// <summary>
            /// Applies winner statistics, give's rewards and print's winner information to chat
            /// </summary>
            protected void ProcessWinners()
            {
                List<BaseEventPlayer> winners = Pool.Get<List<BaseEventPlayer>>();
                GetWinningPlayers(ref winners);

                for (int i = 0; i < eventPlayers.Count; i++)
                {
                    BaseEventPlayer eventPlayer = eventPlayers[i];
                    if (eventPlayer == null)
                        continue;

                    if (winners.Contains(eventPlayer))
                    {
                        EventStatistics.Data.AddStatistic(eventPlayer.Player, "Wins");
                        Instance.GiveReward(eventPlayer, Configuration.Reward.WinAmount);
                    }
                    else EventStatistics.Data.AddStatistic(eventPlayer.Player, "Losses");

                    EventStatistics.Data.AddStatistic(eventPlayer.Player, "Played");
                }

                if (Configuration.Message.BroadcastWinners && winners.Count > 0)
                {
                    if (Plugin.IsTeamEvent)
                    {
                        Team team = winners[0].Team;
                        Broadcast("Notification.EventWin.Multiple.Team", team == Team.B ? TeamBColor : TeamAColor, team, winners.Select(x => x.Player.displayName).ToSentence());
                    }
                    else
                    {
                        if (winners.Count > 1)
                            Broadcast("Notification.EventWin.Multiple", winners.Select(x => x.Player.displayName).ToSentence());
                        else Broadcast("Notification.EventWin", winners[0].Player.displayName);
                    }
                }

                Pool.FreeUnmanaged(ref winners);
            }

            /// <summary>
            /// Override to calculate the winning player(s).This should done done on a per event basis
            /// </summary>
            /// <param name="list"></param>
            protected virtual void GetWinningPlayers(ref List<BaseEventPlayer> list) { }
            #endregion

            #region Kits and Items
            /// <summary>
            /// Drop's the players belt and main containers in to a bag on death
            /// </summary>
            /// <returns>Return false to disable this feature</returns>
            protected virtual bool CanDropBackpack() => true;

            /// <summary>
            /// Override to prevent players being given kits
            /// </summary>
            /// <param name="eventPlayer"></param>
            /// <returns></returns>
            protected virtual bool CanGiveKit(BaseEventPlayer eventPlayer) => true;

            /// <summary>
            /// Called after a player has been given a kit.If the event is team based and team attire kits have been set team attire will be given
            /// </summary>
            /// <param name="eventPlayer"></param>
            protected virtual void OnKitGiven(BaseEventPlayer eventPlayer)
            {
                if (Plugin.IsTeamEvent)
                {
                    string kit = eventPlayer.Team == Team.B ? Config.TeamConfigB.Clothing : Config.TeamConfigA.Clothing;
                    if (!string.IsNullOrEmpty(kit))
                    {
                        List<Item> items = eventPlayer.Player.inventory.containerWear.itemList;
                        for (int i = 0; i < items.Count; i++)
                        {
                            Item item = items[i];
                            item.RemoveFromContainer();
                            item.Remove();
                        }

                        GiveKit(eventPlayer.Player, kit);
                    }
                }
            }

            /// <summary>
            /// Get's the list of Kits available for the specified team
            /// </summary>
            /// <param name="team"></param>
            /// <returns></returns>
            internal List<string> GetAvailableKits(Team team) => team == Team.B ? Config.TeamConfigB.Kits : Config.TeamConfigA.Kits;
            #endregion

            #region Overrides
            /// <summary>
            /// Allows you to display additional event details in the event menu.The key should be a localized message for the target player
            /// </summary>
            /// <param name="list"></param>
            /// <param name="playerId">The user's ID for localization purposes</param>
            internal virtual void GetAdditionalEventDetails(ref List<KeyValuePair<string, object>> list, ulong playerId) { }
            #endregion

            #region Spectating
            /// <summary>
            /// Fill's a list with valid spectate targets
            /// </summary>
            /// <param name="list"></param>
            internal virtual void GetSpectateTargets(ref List<BaseEventPlayer> list)
            {
                list.Clear();
                list.AddRange(eventPlayers);
            }

            /// <summary>
            /// Checks all spectating event players and updates their spectate target if the target has just died
            /// </summary>
            /// <param name="victim"></param>
            private void UpdateDeadSpectateTargets(BaseEventPlayer victim)
            {
                List<BaseEventPlayer> list = Pool.Get<List<BaseEventPlayer>>();
                GetSpectateTargets(ref list);

                bool hasValidSpectateTargets = list.Count > 0;

                for (int i = 0; i < eventPlayers.Count; i++)
                {
                    BaseEventPlayer eventPlayer = eventPlayers[i];

                    if (eventPlayer.Player.IsSpectating() && eventPlayer.SpectateTarget == victim)
                    {
                        if (hasValidSpectateTargets)
                            eventPlayer.UpdateSpectateTarget();
                        else eventPlayer.FinishSpectating();
                    }
                }
            }
            #endregion

            #region Player Counts
            /// <summary>
            /// Count the amount of player's that are alive
            /// </summary>
            /// <returns></returns>
            internal int GetAlivePlayerCount()
            {
                int count = 0;
                for (int i = 0; i < eventPlayers.Count; i++)
                {
                    if (!eventPlayers[i]?.IsDead ?? false)
                        count++;
                }
                return count;
            }

            /// <summary>
            /// Count the amount of player's on the specified team
            /// </summary>
            /// <param name="team"></param>
            /// <returns></returns>
            internal int GetTeamCount(Team team)
            {
                int count = 0;
                for (int i = 0; i < eventPlayers.Count; i++)
                {
                    if (eventPlayers[i]?.Team == team)
                        count++;
                }
                return count;
            }

            /// <summary>
            /// Count the amount of player's that are alive on the specified team
            /// </summary>
            /// <param name="team"></param>
            /// <returns></returns>
            internal int GetTeamAliveCount(Team team)
            {
                int count = 0;
                for (int i = 0; i < eventPlayers.Count; i++)
                {
                    BaseEventPlayer eventPlayer = eventPlayers[i];
                    if (eventPlayer != null && eventPlayer.Team == team && !eventPlayer.IsDead)
                        count++;
                }
                return count;               
            }
            #endregion

            #region Teams
            /// <summary>
            /// Get the score for the specified team
            /// </summary>
            /// <param name="team"></param>
            /// <returns></returns>
            internal virtual int GetTeamScore(Team team) => 0;

            /// <summary>
            /// Balance the team's if one team has > 2 more player's on it
            /// </summary>
            protected void BalanceTeams()
            {
                int aCount = GetTeamCount(Team.A);
                int bCount = GetTeamCount(Team.B);

                int difference = aCount > bCount + 1 ? aCount - bCount : bCount > aCount + 1 ? bCount - aCount : 0;
                Team moveFrom = aCount > bCount + 1 ? Team.A : bCount > aCount + 1 ? Team.B : Team.None;

                if (difference > 1 && moveFrom != Team.None)
                {
                    BroadcastToPlayers("Notification.Teams.Unbalanced");

                    List<BaseEventPlayer> teamPlayers = Pool.Get<List<BaseEventPlayer>>();

                    eventPlayers.ForEach(x =>
                    {
                        if (x.Team == moveFrom)
           
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху