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

FREE LoyaltyRewards : 0.5.3 0.5.3

Нет прав для скачивания
C#:
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Oxide.Plugins
{
    [Info("Loyalty Rewards", "Corpse", "0.5.3")]
    [Description("Rewards players with permission groups based on their activity on the server.")]
    public class LoyaltyRewards : CovalencePlugin
    {
        private bool debugMode = false;
        private Configuration config;
        private PersistentData persistentData;
        private Dictionary<string, PlayerData> playerData;

        private class Configuration
        {
            public bool UseWipeCycle { get; set; }
            public int Period { get; set; }
            public int RequiredDays { get; set; }
            public double DemotionThreshold { get; set; }
            public int MinimumPeriod { get; set; }
            public bool PromoteImmediately { get; set; }
            public bool PermissionRequired { get; set; }
            public string RequiredPermissionGroup { get; set; }
            public List<string> PromotionGroups { get; set; }
        }

        private class PlayerData
        {
            public string PlayerName { get; set; }
            public HashSet<DateTime> ActiveDays { get; set; } = new HashSet<DateTime>();
            public string CurrentGroup { get; set; }
            public bool PromotedThisPeriod { get; set; }
            public bool PendingPromotionNotification { get; set; }
        }

        private class PersistentData
        {
            public DateTime PeriodStartTime { get; set; }
            public DateTime LastWipeDate { get; set; }
            public Dictionary<string, PlayerData> PlayerData { get; set; }
            public bool IsMigrated { get; set; }
        }

        protected override void LoadDefaultConfig()
        {
            config = new Configuration
            {
                UseWipeCycle = true,
                Period = 30,
                RequiredDays = 7,
                DemotionThreshold = 0.5,
                MinimumPeriod = 2,
                PromoteImmediately = true,
                PermissionRequired = false,
                RequiredPermissionGroup = "linked",
                PromotionGroups = new List<string> { "endurer", "veteran", "legend" }
            };
            SaveConfig();
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            config = Config.ReadObject<Configuration>();
            bool saveNeeded = false;
            if (config.PromotionGroups == null || config.PromotionGroups.Count == 0)
            {
                config.PromotionGroups = new List<string> { "endurer", "veteran", "legend" };
                saveNeeded = true;
            }
            if (saveNeeded) SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        private void Init()
        {
            LoadPersistentData();
            RegisterPermissions();
            ReconcilePlayerData();
            if (config.PromoteImmediately) ScheduleDailyPromotionCheck();
        }

        private void LoadPersistentData()
        {
            persistentData = Interface.Oxide.DataFileSystem.ReadObject<PersistentData>("LoyaltyRewardsData");
            playerData = persistentData.PlayerData ?? new Dictionary<string, PlayerData>();
            if (!persistentData.IsMigrated)
            {
                MigrateOldDataFormat();
                persistentData.IsMigrated = true;
                SavePersistentData();
            }
        }

        private void RegisterPermissions()
        {
            permission.RegisterPermission("loyaltyrewards.admin", this);
        }

        private void MigrateOldDataFormat()
        {
            var newData = new Dictionary<string, PlayerData>();
            foreach (var entry in playerData)
            {
                string key = entry.Key;
                var data = entry.Value;
                string userId = ExtractUserIdFromKey(key);
                if (string.IsNullOrEmpty(userId)) continue;
                if (newData.TryGetValue(userId, out var exist))
                {
                    exist.ActiveDays.UnionWith(data.ActiveDays);
                    exist.PromotedThisPeriod |= data.PromotedThisPeriod;
                    exist.PendingPromotionNotification |= data.PendingPromotionNotification;
                    if (GetPromotionIndex(data.CurrentGroup) > GetPromotionIndex(exist.CurrentGroup)) exist.CurrentGroup = data.CurrentGroup;
                    if (!string.IsNullOrEmpty(data.PlayerName)) exist.PlayerName = data.PlayerName;
                }
                else
                {
                    data.PlayerName = data.PlayerName ?? ExtractPlayerNameFromKey(key);
                    newData[userId] = data;
                }
            }
            playerData = newData;
            persistentData.PlayerData = playerData;
        }

        private string ExtractUserIdFromKey(string key)
        {
            int bracketIndex = key.IndexOf('(');
            if (bracketIndex > 0) return key.Substring(0, bracketIndex).Trim();
            return key.Trim();
        }

        private string ExtractPlayerNameFromKey(string key)
        {
            int startIndex = key.IndexOf('(');
            int endIndex = key.IndexOf(')', startIndex + 1);
            if (startIndex >= 0 && endIndex > startIndex) return key.Substring(startIndex + 1, endIndex - startIndex - 1).Trim();
            return "Unknown";
        }

        private int GetPromotionIndex(string group) => config.PromotionGroups.IndexOf(group);

        private void ReconcilePlayerData()
        {
            foreach (var group in config.PromotionGroups)
            {
                if (!permission.GroupExists(group)) continue;
                var rawUsers = permission.GetUsersInGroup(group);
                if (debugMode) Puts($"[Reconcile] Processing group '{group}' with {rawUsers.Length} users.");
                foreach (var rawId in rawUsers)
                {
                    string userId = ExtractUserIdFromKey(rawId);
                    if (debugMode) Puts($"[Reconcile] RawUserId='{rawId}', ExtractedUserId='{userId}'");
                    var ply = players.FindPlayerById(userId);
                    string plyName = ply?.Name ?? permission.GetUserData(userId)?.LastSeenNickname ?? "Unknown";
                    if (playerData.TryGetValue(userId, out var d))
                    {
                        if (debugMode) Puts($"[Reconcile] Found existing data for '{userId}'. CurrentGroup='{d.CurrentGroup}'.");
                        string oldGroup = d.CurrentGroup;
                        d.CurrentGroup = ValidateCurrentGroup(d.CurrentGroup);
                        d.PlayerName = plyName;
                        if (d.CurrentGroup != group)
                        {
                            string prevGroup = d.CurrentGroup;
                            d.CurrentGroup = group;
                            Puts($"[Reconcile] Player '{plyName}' (ID: {userId}) CurrentGroup changed from '{prevGroup}' to '{group}' during reconciliation.");
                        }
                    }
                    else
                    {
                        playerData[userId] = new PlayerData
                        {
                            PlayerName = plyName,
                            CurrentGroup = group,
                            ActiveDays = new HashSet<DateTime>(),
                            PromotedThisPeriod = false
                        };
                        Puts($"[Reconcile] Player '{plyName}' (ID: {userId}) assigned to group '{group}' during reconciliation.");
                    }
                }
            }
            foreach (var data in playerData.Values) data.CurrentGroup = ValidateCurrentGroup(data.CurrentGroup);
            SavePersistentData();
        }

        private void SavePersistentData()
        {
            persistentData.PlayerData = playerData;
            Interface.Oxide.DataFileSystem.WriteObject("LoyaltyRewardsData", persistentData);
        }

        private void OnServerSave() => SavePersistentData();

        private void OnUserConnected(IPlayer player)
        {
            string key = GetPlayerKey(player);
            if (!playerData.TryGetValue(key, out var d))
            {
                string grp = config.PromotionGroups.FirstOrDefault(g => permission.UserHasGroup(player.Id, g)) ?? "";
                d = new PlayerData
                {
                    PlayerName = player.Name,
                    CurrentGroup = grp,
                    ActiveDays = new HashSet<DateTime>(),
                    PromotedThisPeriod = false
                };
                playerData[key] = d;
                SavePersistentData();
                Puts($"[OnUserConnected] Player '{player.Name}' (ID: {player.Id}) connected and assigned to group '{grp}'.");
            }
            else
            {
                d.PlayerName = player.Name;
            }
            string valGroup = ValidateCurrentGroup(d.CurrentGroup);
            if (d.CurrentGroup != valGroup)
            {
                string oldGroup = d.CurrentGroup;
                d.CurrentGroup = valGroup;
                Puts($"[OnUserConnected] Player '{player.Name}' (ID: {player.Id}) CurrentGroup changed from '{oldGroup}' to '{valGroup}' during connection validation.");
            }
            var today = DateTime.Now.Date;
            if (d.ActiveDays.Add(today))
            {
                SavePersistentData();
                Puts($"[OnUserConnected] Player '{player.Name}' (ID: {player.Id}) active on {today.ToShortDateString()}.");
            }
            if (config.PromoteImmediately && !d.PromotedThisPeriod) CheckPlayerPromotion(player, true, false);
        }

        private void OnPlayerSpawned(BasePlayer player)
        {
            IPlayer pl = covalence.Players.FindPlayerById(player.UserIDString);
            if (pl != null && playerData.TryGetValue(GetPlayerKey(pl), out var data))
            {
                if (data.PendingPromotionNotification)
                {
                    pl.Message($"Congratulations! Loyalty Rewards has promoted you to {data.CurrentGroup}.");
                    data.PendingPromotionNotification = false;
                    SavePersistentData();
                    Puts($"[OnPlayerSpawned] Sent promotion notification to '{pl.Name}' (ID: {pl.Id}) for group '{data.CurrentGroup}'.");
                }
            }
        }

        private void OnNewSave(string filename)
        {
            if (config.UseWipeCycle)
            {
                Puts("Detected new save file. Processing promotions and demotions.");
                ProcessPromotionsAndDemotions();
                ResetPeriod(false);
                Puts("Period reset. All players' active days and promotion flags have been cleared.");
            }
        }

        private void ScheduleDailyPromotionCheck()
        {
            var now = DateTime.Now;
            var midnight = new DateTime(now.Year, now.Month, now.Day).AddDays(1);
            var timeUntilMidnight = midnight - now;
            timer.Once((float)timeUntilMidnight.TotalSeconds, () =>
            {
                RunMidnightCheck();
                ScheduleDailyPromotionCheck();
            });
        }

        private void RunMidnightCheck()
        {
            foreach (var p in players.Connected) OnUserConnected(p);
        }

        private void CheckPlayerPromotion(IPlayer player, bool suppressOutput, bool allowDemotions)
        {
            string key = GetPlayerKey(player);
            if (!playerData.TryGetValue(key, out var d)) return;
            if (d.PromotedThisPeriod) return;
            if (config.PermissionRequired && !permission.UserHasGroup(player.Id, config.RequiredPermissionGroup)) return;
            int activeDays = d.ActiveDays.Count;
            int promIndex = config.PromotionGroups.IndexOf(d.CurrentGroup) + 1;
            if (activeDays >= config.RequiredDays && promIndex < config.PromotionGroups.Count)
            {
                PromotePlayer(player, d, promIndex);
                d.PromotedThisPeriod = true; 
                SavePersistentData();
            }
            else if (allowDemotions)
            {
                HandleDemotionOrRetention(player, d, suppressOutput);
            }
        }

        private void PromotePlayer(IPlayer player, PlayerData data, int promotionIndex)
        {
            string newGroup = config.PromotionGroups[promotionIndex];
            string oldGroup = data.CurrentGroup;
            AddPlayerToGroup(player, newGroup);
            RemoveOnlyManagedGroups(player, newGroup);
            data.CurrentGroup = newGroup;
            Puts($"[PromotePlayer] Player '{data.PlayerName}' (ID: {player.Id}) promoted from '{oldGroup}' to '{newGroup}'.");
            if (!player.IsConnected)
            {
                data.PendingPromotionNotification = true;
            }
            else
            {
                player.Message($"Congratulations! Loyalty Rewards has promoted you to {newGroup}.");
            }
            SavePersistentData();
        }

        private void HandleDemotionOrRetention(IPlayer player, PlayerData data, bool suppressOutput)
        {
            int activeDays = data.ActiveDays.Count;
            int pIndex = config.PromotionGroups.IndexOf(data.CurrentGroup);
            if ((DateTime.Now - persistentData.PeriodStartTime).TotalDays < config.MinimumPeriod) return;
            if (activeDays < config.RequiredDays * config.DemotionThreshold && pIndex >= 0) DemotePlayer(player, data, pIndex);
        }

        private void DemotePlayer(IPlayer player, PlayerData data, int promotionIndex)
        {
            string oldGroup = data.CurrentGroup;
            string pName = data.PlayerName ?? player?.Name ?? "Unknown";
            if (!string.IsNullOrEmpty(data.CurrentGroup) && permission.UserHasGroup(player.Id, data.CurrentGroup))
            {
                permission.RemoveUserGroup(player.Id, data.CurrentGroup);
                Puts($"[DemotePlayer] Removed group '{data.CurrentGroup}' from player '{pName}' (ID: {player.Id}).");
            }
            if (promotionIndex == 0)
            {
                DemoteToBaseGroup(player, data);
            }
            else
            {
                string prevGroup = config.PromotionGroups[promotionIndex - 1];
                AddPlayerToGroup(player, prevGroup);
                data.CurrentGroup = prevGroup;
                Puts($"[DemotePlayer] Player '{pName}' (ID: {player.Id}) demoted to '{prevGroup}' from '{oldGroup}'.");
            }
            SavePersistentData();
        }

        private void DemoteToBaseGroup(IPlayer player, PlayerData data)
        {
            var usrGroups = permission.GetUserGroups(player.Id);
            var promoGroups = config.PromotionGroups.Where(g => usrGroups.Contains(g)).ToList();
            string oldGroup = data.CurrentGroup;
            string pName = data.PlayerName ?? player?.Name ?? "Unknown";
            if (!promoGroups.Any())
            {
                data.CurrentGroup = "";
                Puts($"[DemoteToBaseGroup] Player '{pName}' (ID: {player.Id}) demoted to base group from '{oldGroup}'.");
            }
            else
            {
                foreach (var g in promoGroups)
                {
                    permission.RemoveUserGroup(player.Id, g);
                    Puts($"[DemoteToBaseGroup] Removed group '{g}' from player '{pName}' (ID: {player.Id}).");
                }
                data.CurrentGroup = "";
                Puts($"[DemoteToBaseGroup] Player '{pName}' (ID: {player.Id}) demoted to base group.");
            }
        }

        private void AddPlayerToGroup(IPlayer player, string group)
        {
            if (!permission.UserHasGroup(player.Id, group))
            {
                permission.AddUserGroup(player.Id, group);
                Puts($"[AddPlayerToGroup] Added group '{group}' to player '{player.Name}' (ID: {player.Id}).");
            }
        }

        private void RemoveOnlyManagedGroups(IPlayer player, string currentGroup)
        {
            var usrGroups = permission.GetUserGroups(player.Id);
            foreach (var g in usrGroups)
            {
                if (config.PromotionGroups.Contains(g) && g != currentGroup)
                {
                    permission.RemoveUserGroup(player.Id, g);
                    Puts($"[RemoveOnlyManagedGroups] Removed group '{g}' from player '{player.Name}' (ID: {player.Id}) to maintain single group assignment.");
                }
            }
        }

        private void ProcessPromotionsAndDemotions()
        {
            foreach (var p in players.All) CheckPlayerPromotion(p, true, true);
        }

        [Command("lr.resetperiod", "loyaltyrewards.admin")]
        private void ResetPeriodCommand(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission("loyaltyrewards.admin"))
            {
                player.Reply("You do not have permission to use this command.");
                return;
            }
            if (args.Length == 0 || (args[0].ToLower() != "withpromotions" && args[0].ToLower() != "withoutpromotions"))
            {
                player.Reply("Usage: /lr.resetperiod <withpromotions|withoutpromotions>");
                return;
            }
            bool withPromotions = args[0].ToLower() == "withpromotions";
            ResetPeriod(withPromotions);
            if (withPromotions) player.Reply("Loyalty Rewards period has been manually reset with promotions and demotions.");
            else player.Reply("Loyalty Rewards period has been manually reset without promotions and demotions.");
        }

        private void ResetPeriod(bool withPromotions)
        {
            if (withPromotions) ProcessPromotionsAndDemotions();
            persistentData.PeriodStartTime = DateTime.Now;
            foreach (var d in playerData.Values)
            {
                d.ActiveDays.Clear();
                d.PromotedThisPeriod = false;
            }
            SavePersistentData();
            Puts($"[ResetPeriod] Period reset at {DateTime.Now}. Promotions applied: {withPromotions}.");
        }

        private string ValidateCurrentGroup(string currentGroup)
        {
            if (string.IsNullOrEmpty(currentGroup) || config.PromotionGroups.Contains(currentGroup)) return currentGroup;
            Puts($"[ValidateCurrentGroup] Invalid group '{currentGroup}' detected. Resetting to base group.");
            return "";
        }

        private string GetPlayerKey(IPlayer player) => player.Id;
        private string GetPlayerKey(string userId) => ExtractUserIdFromKey(userId);

        [Command("lr.promote", "loyaltyrewards.admin")]
        private void PromoteCommand(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission("loyaltyrewards.admin"))
            {
                player.Reply("You do not have permission to use this command.");
                return;
            }
            if (args.Length < 1)
            {
                player.Reply("Usage: /lr.promote <player> [levels] [keeppromotioneligibility]");
                return;
            }
            var target = players.FindPlayer(args[0]);
            if (target == null)
            {
                player.Reply($"Player '{args[0]}' not found.");
                return;
            }
            string key = GetPlayerKey(target);
            if (!playerData.TryGetValue(key, out var data))
            {
                player.Reply($"No data found for player '{target.Name}'.");
                return;
            }
            int levels = 1;
            bool keepPromotionEligibility = false;
            if (args.Length > 1)
            {
                if (int.TryParse(args[1], out int parsedLevels)) levels = parsedLevels;
                else if (args[1].ToLower() == "keeppromotioneligibility") keepPromotionEligibility = true;
            }
            if (args.Length > 2 && args[2].ToLower() == "keeppromotioneligibility") keepPromotionEligibility = true;
            int promotionIndex = config.PromotionGroups.IndexOf(data.CurrentGroup) + 1;
            int targetIndex = promotionIndex + levels - 1;
            if (targetIndex >= config.PromotionGroups.Count)
            {
                targetIndex = config.PromotionGroups.Count - 1;
                Puts($"[PromoteCommand] Player {target.Name} specified promotion levels exceed available groups. Promoting to highest group: {config.PromotionGroups[targetIndex]}.");
            }
            PromotePlayer(target, data, targetIndex);
            if (!keepPromotionEligibility) data.PromotedThisPeriod = true;
            player.Reply($"{target.Name} has been promoted to {data.CurrentGroup}.");
            Puts($"[PromoteCommand] Admin '{player.Name}' (ID: {player.Id}) promoted player '{target.Name}' (ID: {target.Id}) to '{data.CurrentGroup}'. Levels: {levels}, Keep Eligibility: {keepPromotionEligibility}.");
        }

        [Command("lr.demote", "loyaltyrewards.admin")]
        private void DemoteCommand(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission("loyaltyrewards.admin"))
            {
                player.Reply("You do not have permission to use this command.");
                return;
            }
            if (args.Length < 1)
            {
                player.Reply("Usage: /lr.demote <player>");
                return;
            }
            var target = players.FindPlayer(args[0]);
            if (target == null)
            {
                player.Reply($"Player '{args[0]}' not found.");
                return;
            }
            string key = GetPlayerKey(target);
            if (!playerData.TryGetValue(key, out var data))
            {
                player.Reply($"No data found for player '{target.Name}'.");
                return;
            }
            int index = config.PromotionGroups.IndexOf(data.CurrentGroup);
            if (index < 0)
            {
                player.Reply($"{target.Name} is already at the base group.");
                return;
            }
            DemotePlayer(target, data, index);
            if (index > 0) player.Reply($"{target.Name} has been demoted to {data.CurrentGroup}.");
            Puts($"[DemoteCommand] Admin '{player.Name}' (ID: {player.Id}) demoted player '{target.Name}' (ID: {target.Id}) to '{data.CurrentGroup}'.");
        }

        [Command("lr.playtime")]
        private void PlaytimeCommand(IPlayer player, string command, string[] args)
        {
            string key = GetPlayerKey(player);
            if (!playerData.TryGetValue(key, out var data))
            {
                player.Reply("Your playtime data is not available.");
                return;
            }
            if (config.PermissionRequired && !permission.UserHasGroup(player.Id, config.RequiredPermissionGroup))
            {
                player.Reply($"You are not eligible for promotions because you are not a member of the required group '{config.RequiredPermissionGroup}'.");
                return;
            }
            int activeDays = data.ActiveDays.Count;
            double demoThreshold = config.RequiredDays * config.DemotionThreshold;
            bool aboveThreshold = activeDays >= Math.Ceiling(demoThreshold);
            string periodLabel = config.UseWipeCycle ? "wipe" : "period";
            if (data.PromotedThisPeriod)
            {
                player.Reply($"You have already been promoted this {periodLabel} and are not eligible for further promotions, but you are safe from demotion.");
                return;
            }
            if (activeDays >= config.RequiredDays)
            {
                player.Reply($"You have played for {activeDays} days, are eligible for promotion, and are safe from demotion.");
            }
            else
            {
                int needed = config.RequiredDays - activeDays;
                if (aboveThreshold)
                {
                    player.Reply($"You have played for {activeDays} days, need {needed} more days for promotion, and are safe from demotion.");
                }
                else
                {
                    int below = (int)Math.Ceiling(demoThreshold - activeDays);
                    player.Reply($"You have played for {activeDays} days, need {needed} more days for promotion, and are {below} days below the threshold, putting you at risk of demotion.");
                }
            }
        }

        [Command("lr.cleanup", "loyaltyrewards.admin")]
        private void CleanupCommand(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission("loyaltyrewards.admin"))
            {
                player.Reply("You do not have permission to use this command.");
                return;
            }
            if (args.Length != 1 || args[0].ToLower() != "ihaveabackup")
            {
                player.Reply("Usage: /lr.cleanup ihaveabackup");
                return;
            }
            int removed = 0;
            foreach (var entry in playerData.ToList())
            {
                var d = entry.Value;
                if (!config.PromotionGroups.Contains(d.CurrentGroup) && d.ActiveDays.Count == 0)
                {
                    playerData.Remove(entry.Key);
                    removed++;
                }
            }
            SavePersistentData();
            player.Reply($"Loyalty Rewards data cleaned up. {removed} players removed.");
        }
    }
}

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