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

FREE EventControlUI : 1.0.0 1.0.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using System;
using UnityEngine;



namespace Oxide.Plugins
{
    [Info("EventControlUI", "Naked", "1.0.0")]
    [Description("Plugin to manage teams, permissions, and teleportation for events in Rust via a UI.")]
    public class EventControlUI : RustPlugin
    {
        private const string PermissionManageTeams = "eventcontrolui.manage.teams";
        private const string PermissionManagePerms = "eventcontrolui.manage.perms";
        private const string PermissionManageTp = "eventcontrolui.manage.tp";
        private const string PermissionHideHud = "eventcontrolui.hidehud";
        private const string PermissionBypass = "kits.bypass";
        

        private Dictionary<string, string> playerTeams = new Dictionary<string, string>(); // Player ID -> Team

         private Dictionary<string, string> teamColors = new Dictionary<string, string>
        {
            { "TeamOne", "0.2 0.8 0.2 1.0" },
            { "TeamTwo", "0.2 0.5 0.8 1.0" },
            { "TeamThree", "0.8 0.2 0.2 1.0" },
            { "TeamFour", "0.8 0.8 0.2 1.0" }
        };

        // --- UI Tracking ---
        
       
        // Global loot flag – true means players are allowed to loot by default.private bool lootAllowed = true;

        // Dictionary to store per-team loot settings.private Dictionary<string, bool> teamLootStatus = new Dictionary<string, bool>();

        private Dictionary<string, List<string>> playerPerms = new Dictionary<string, List<string>>(); // Player ID -> Permissions
        private Dictionary<string, ulong> teamMapping = new Dictionary<string, ulong>();
        private Vector3? worldSpawnLocation = null;
        private List<string> teamNames = new List<string> { "TeamOne", "TeamTwo", "TeamThree", "TeamFour" };
        private List<string> availablePerms = new List<string> { "none" }; // Configurable

        private bool allowTeamLeave = true; // Toggle for allowing players to leave teams
        private string defaultTeam = null; // Default team players join automatically
private Harmony harmony; // Add this at the top of the class
public static EventControlUI Instance;
private bool friendlyFire = true; // Default value
 private List<string> defaultTeams = new List<string>(); // Allow multiple default teams

private void Init()
{
    Instance = this; // Set the static reference

    // Register permissions
    permission.RegisterPermission(PermissionManageTeams, this);
    permission.RegisterPermission(PermissionManagePerms, this);
    permission.RegisterPermission(PermissionManageTp, this);
    permission.RegisterPermission(PermissionHideHud, this);
    

        // Initialize Kits plugin reference
    Kits = plugins.Find("Kits"); // Ensure the plugin name matches exactly

    if (Kits == null)
    {
        PrintError("Kits plugin not found! Ensure that the Kits plugin is installed and loaded before EventControlUI.");
    }

    // Load the configuration
    LoadConfig();

    // Initialize Harmony for patching
    harmony = new Harmony("com.yourname.eventcontrolui");
    harmony.PatchAll();

    // Initialize team-specific states if not already set
    foreach (var team in teamNames)
    {
        if (!teamFreezeStatus.ContainsKey(team))
            teamFreezeStatus[team] = false;

        if (!teamRaidingStatus.ContainsKey(team))
            teamRaidingStatus[team] = raidingEnabled;

        if (!teamPvPStatus.ContainsKey(team))
            teamPvPStatus[team] = pvpEnabled;

        if (!teamGodModeStatus.ContainsKey(team))
            teamGodModeStatus[team] = godModeEnabled;

        if (!teamBuildingStatus.ContainsKey(team))
            teamBuildingStatus[team] = buildingEnabled;
    }

    // Initialize timers for Global and each team
    timers["Global"] = new TimerData
    {
        TeamName = "Global",
        RemainingTime = 0f,
        IsRunning = false,
        BuildingOnZero = false,
        RaidingOnZero = false,
        PvPOnZero = false,
        CustomText = ""
    };

    foreach (var team in teamNames)
    {
        timers[team] = new TimerData
        {
            TeamName = team,
            RemainingTime = 0f,
            IsRunning = false,
            BuildingOnZero = false,
            RaidingOnZero = false,
            PvPOnZero = false,
            CustomText = ""
        };
    }
}




protected override void LoadDefaultConfig()
{
    Config["AvailablePerms"] = new List<string> {  "atlasvanish.use",
  "bgrade.all"};
    SaveConfig();
    Config["WorldSpawnLocation"] = null;
    Config["AvailablePerms"] = availablePerms;
    Config["AllowTeamLeave"] = allowTeamLeave;
    Config["DefaultTeams"] = new List<string>();
    Config["FriendlyFire"] = true;
    Config["RaidingEnabled"] = true;
    Config["BuildingEnabled"] = true; // Default state
    Config["GodModeEnabled"] = false; // Default state
    Config["Timers"] = new Dictionary<string, object>();
    
    SaveConfig();


}

private void LoadConfig()
{
    // Load Building Enabled setting
    buildingEnabled = Config["BuildingEnabled"] != null && (bool)Config["BuildingEnabled"];
     godModeEnabled = Config["GodModeEnabled"] != null && (bool)Config["GodModeEnabled"];
    
    // Load Friendly Fire setting
    friendlyFire = Config["FriendlyFire"] != null && (bool)Config["FriendlyFire"];

    // Load Available Permissions
    if (Config["AvailablePerms"] is List<object> permsFromConfig)
    {
        availablePerms = permsFromConfig.Cast<string>().ToList();
    }
    else
    {
        availablePerms = new List<string>(); // Default empty list if not in config
    }
    // Load Allow Team Leave setting
    allowTeamLeave = Config["AllowTeamLeave"] != null && (bool)Config["AllowTeamLeave"];

    // Load Default Teams (multiple teams)
    if (Config["DefaultTeams"] is List<object> teamsFromConfig)
    {
        defaultTeams = teamsFromConfig.Cast<string>().ToList();
    }
    else
    {
        defaultTeams = new List<string>(); // Ensure it initializes even if config is missing
    }

     if (Config["WorldSpawnLocation"] != null)
    {
        worldSpawnLocation = Config["WorldSpawnLocation"] as Vector3?;
    }

    // Load Team-Specific Freeze Status
    if (Config["TeamFreezeStatus"] is Dictionary<string, object> freezeStatusConfig)
    {
        foreach (var kvp in freezeStatusConfig)
        {
            teamFreezeStatus[kvp.Key] = Convert.ToBoolean(kvp.Value);
        }
    }

    // Load Team-Specific Raiding Status
    if (Config["TeamRaidingStatus"] is Dictionary<string, object> raidingStatusConfig)
    {
        foreach (var kvp in raidingStatusConfig)
        {
            teamRaidingStatus[kvp.Key] = Convert.ToBoolean(kvp.Value);
        }
    }

    // Load Team-Specific PvP Status
    if (Config["TeamPvPStatus"] is Dictionary<string, object> pvpStatusConfig)
    {
        foreach (var kvp in pvpStatusConfig)
        {
            teamPvPStatus[kvp.Key] = Convert.ToBoolean(kvp.Value);
        }
    }

    // Load Team-Specific God Mode Status
    if (Config["TeamGodModeStatus"] is Dictionary<string, object> godModeStatusConfig)
    {
        foreach (var kvp in godModeStatusConfig)
        {
            teamGodModeStatus[kvp.Key] = Convert.ToBoolean(kvp.Value);
        }
    }

    // Load Team-Specific Building Status
    if (Config["TeamBuildingStatus"] is Dictionary<string, object> buildingStatusConfig)
    {
        foreach (var kvp in buildingStatusConfig)
        {
            teamBuildingStatus[kvp.Key] = Convert.ToBoolean(kvp.Value);
        }
    }

    // Ensure all teams have an entry
    foreach (var team in teamNames)
    {
        if (!teamFreezeStatus.ContainsKey(team)) teamFreezeStatus[team] = false;
        if (!teamRaidingStatus.ContainsKey(team)) teamRaidingStatus[team] = raidingEnabled;
        if (!teamPvPStatus.ContainsKey(team)) teamPvPStatus[team] = pvpEnabled;
        if (!teamGodModeStatus.ContainsKey(team)) teamGodModeStatus[team] = godModeEnabled;
        if (!teamBuildingStatus.ContainsKey(team)) teamBuildingStatus[team] = buildingEnabled;
    }

        if (Config["Timers"] is Dictionary<string, object> timersConfig)
    {
        foreach (var kvp in timersConfig)
        {
            string teamName = kvp.Key;
            var timerObj = kvp.Value as Dictionary<string, object>;
            if (timerObj == null) continue;

            if (!timers.ContainsKey(teamName))
                timers[teamName] = new TimerData { TeamName = teamName };

            TimerData timerData = timers[teamName];
            timerData.RemainingTime = timerObj.ContainsKey("RemainingTime") ? Convert.ToSingle(timerObj["RemainingTime"]) : 0f;
            timerData.IsRunning = timerObj.ContainsKey("IsRunning") ? Convert.ToBoolean(timerObj["IsRunning"]) : false;
            timerData.BuildingOnZero = timerObj.ContainsKey("BuildingOnZero") ? Convert.ToBoolean(timerObj["BuildingOnZero"]) : false;
            timerData.RaidingOnZero = timerObj.ContainsKey("RaidingOnZero") ? Convert.ToBoolean(timerObj["RaidingOnZero"]) : false;
            timerData.PvPOnZero = timerObj.ContainsKey("PvPOnZero") ? Convert.ToBoolean(timerObj["PvPOnZero"]) : false;
            timerData.CustomText = timerObj.ContainsKey("CustomText") ? timerObj["CustomText"].ToString() : "";

            if (timerData.IsRunning && timerData.RemainingTime > 0f)
            {
                timerData.TimerInstance = timer.Once(timerData.RemainingTime, () => OnTimerEnd(teamName));
            }
        }
    }

    SaveConfig();
}

private HashSet<ulong> uiOpenPlayers = new HashSet<ulong>();
// HashSet to track players with the Timers tab open
private HashSet<ulong> timersTabOpenPlayers = new HashSet<ulong>();

        [ChatCommand("eventui")]
        private void EventUICommand(BasePlayer player, string command, string[] args)
        {
            if (!permission.UserHasPermission(player.UserIDString, PermissionManageTeams))
            {
                SendReply(player, "You don't have permission to use this command.");
                return;
            }

            OpenMainMenu(player, "teams");
            uiOpenPlayers.Add(player.userID);
        }

void OnPlayerDisconnected(BasePlayer player, string reason)
{
    uiOpenPlayers.Remove(player.userID);
    timersTabOpenPlayers.Remove(player.userID);
}

private void OpenMainMenu(BasePlayer player, string activeTab, string selectedPlayerId = null, string selectedTeam = null, int page = 0)
{



    // Destroy existing UI
    CuiHelper.DestroyUi(player, "EventControlUI");

    CuiElementContainer container = new CuiElementContainer();

    // Background panel
    string panelName = "EventControlUI";
    container.Add(new CuiPanel
    {
        RectTransform = { AnchorMin = "0.05 0.05", AnchorMax = "0.95 0.95" },
        Image = { Color = "0.1 0.1 0.1 0.8" }
    }, "Overlay", panelName);

    // Tabs including the new "Timers" tab
    string[] tabs = { "Teams", "Permissions", "Global", "Teleport", "Timers", "Kits" };
    float tabWidth = 0.16f; // Adjusted width to accommodate 5 tabs within the panel
    for (int i = 0; i < tabs.Length; i++)
    {
        container.Add(new CuiButton
        {
            RectTransform = { AnchorMin = $"{0.05f + i * tabWidth} 0.95", AnchorMax = $"{0.05f + (i + 1) * tabWidth - 0.02f} 1.0" },
            Button = { Command = $"eventui.tab {tabs[i].ToLower()}", Color = activeTab == tabs[i].ToLower() ? "0.8 0.8 0.8 1.0" : "0.2 0.5 0.8 1.0" },
            Text = { Text = tabs[i], FontSize = 20, Align = UnityEngine.TextAnchor.MiddleCenter }
        }, panelName);
    }

    // Main Content based on the active tab
    switch (activeTab)
    {
        case "teams":
            AddTeamTabContent(container, panelName, selectedPlayerId, selectedTeam, page);
            break;
        case "permissions":
            AddPermissionsTabContent(container, panelName, page);
            break;
        case "global":
            AddGlobalTabContent(container, panelName);
            break;
        case "teleport":
            AddTeleportTabContent(container, panelName, page);
            break;
        case "timers":
            AddTimersTabContent(container, panelName);
            break;
        case "kits":
            AddKitsTabContent(container, panelName, page);
            break;
        default:
            // Default to Teams tab if an unknown tab is specified
            AddTeamTabContent(container, panelName, selectedPlayerId, selectedTeam, page);
            break;
    }

    
    // Close button
    container.Add(new CuiButton
    {
        RectTransform = { AnchorMin = "0.85 0.05", AnchorMax = "0.95 0.1" },
        Button = { Command = "eventui.close", Color = "0.8 0.2 0.2 1.0" },
        Text = { Text = "Close", FontSize = 20, Align = UnityEngine.TextAnchor.MiddleCenter }
    }, panelName);

    // Add the constructed UI to the player
    CuiHelper.AddUi(player, container);
}


[ConsoleCommand("team.page")]
private void HandleTeamPageCommand(ConsoleSystem.Arg args)
{
    BasePlayer player = args.Connection?.player as BasePlayer;
    if (player == null) return;

    int page = args.Args.Length > 0 ? int.Parse(args.Args[0]) : 0;
    OpenMainMenu(player, "teams", null, null, page);
}


        private void AddTeamTabContent(CuiElementContainer container, string parent, string selectedPlayerId = null, string selectedTeam = null, int page = 0)
{
    // --- Allow Leave Toggle ---
    container.Add(new CuiLabel {
        RectTransform = { AnchorMin = "0.1 0.88", AnchorMax = "0.3 0.93" },
        Text = { Text = "Allow Leave", FontSize = 24, Align = TextAnchor.MiddleLeft }
    }, parent);
    container.Add(new CuiButton {
        RectTransform = { AnchorMin = "0.3 0.88", AnchorMax = "0.4 0.93" },
        Button = { Command = "team.toggleleave", Color = allowTeamLeave ? "0.2 0.8 0.2 1.0" : "0.8 0.2 0.2 1.0" },
        Text = { Text = allowTeamLeave ? "True" : "False", FontSize = 24, Align = TextAnchor.MiddleCenter }
    }, parent);

    // --- Friendly Fire Toggle ---
    container.Add(new CuiLabel {
        RectTransform = { AnchorMin = "0.5 0.88", AnchorMax = "0.7 0.93" },
        Text = { Text = "Friendly Fire", FontSize = 24, Align = TextAnchor.MiddleLeft }
    }, parent);
    container.Add(new CuiButton {
        RectTransform = { AnchorMin = "0.7 0.88", AnchorMax = "0.8 0.93" },
        Button = { Command = "team.togglefriendlyfire", Color = friendlyFire ? "0.2 0.8 0.2 1.0" : "0.8 0.2 0.2 1.0" },
        Text = { Text = friendlyFire ? "True" : "False", FontSize = 24, Align = TextAnchor.MiddleCenter }
    }, parent);

    // --- Default Teams Row ---
    container.Add(new CuiLabel {
        RectTransform = { AnchorMin = "0.1 0.82", AnchorMax = "0.3 0.87" },
        Text = { Text = "Default Teams", FontSize = 24, Align = TextAnchor.MiddleLeft }
    }, parent);

    int index = 0;
    foreach (var teamName in teamNames)
    {
        container.Add(new CuiButton {
            RectTransform = { AnchorMin = $"{0.35f + index * 0.12f} 0.82", AnchorMax = $"{0.45f + index * 0.12f} 0.87" },
            Button = { Command = $"team.toggleddefault {teamName}", Color = defaultTeams.Contains(teamName) ? "0.2 0.8 0.2 1.0" : "0.8 0.2 0.2 1.0" },
            Text = { Text = teamName, FontSize = 24, Align = TextAnchor.MiddleCenter }
        }, parent);
        index++;
    }

    // --- Player Grid (Team Assignment) ---
    int playersPerPage = 6 * 10; // 60 players per page
    var players = BasePlayer.activePlayerList.Skip(page * playersPerPage).Take(playersPerPage).ToList();

    // Adjust grid starting position to be below the default teams row.float gridStartX = 0.1f;
    float gridStartY = 0.75f;
    float buttonWidth = 0.13f;
    float buttonHeight = 0.06f;
    float hSpacing = 0.02f;
    float vSpacing = 0.01f;
    int cols = 6;

    for (int i = 0; i < players.Count; i++)
    {
        int col = i % cols;
        int row = i / cols;
        float xMin = gridStartX + col * (buttonWidth + hSpacing);
        float xMax = xMin + buttonWidth;
        float yMax = gridStartY - row * (buttonHeight + vSpacing);
        float yMin = yMax - buttonHeight;
        BasePlayer p = players[i];
        string playerId = p.UserIDString;
        // Set button color based on assigned team (gray if none)
        string boxColor = "0.5 0.5 0.5 1.0";
        if (playerTeams.ContainsKey(playerId))
        {
            string team = playerTeams[playerId];
            if (teamColors.ContainsKey(team))
                boxColor = teamColors[team];
        }
        container.Add(new CuiButton
        {
            RectTransform = { AnchorMin = $"{xMin} {yMin}", AnchorMax = $"{xMax} {yMax}" },
            Button = { Command = $"team.openselect {playerId} {page}", Color = boxColor },
            Text = { Text = p.displayName, FontSize = 14, Align = TextAnchor.MiddleCenter }
        }, parent);
    }

    // --- Pagination Arrows ---
    int totalPlayers = BasePlayer.activePlayerList.Count;
    int totalPages = (int)Math.Ceiling((double)totalPlayers / playersPerPage);
    if (totalPages > 1)
    {
        if (page > 0)
        {
            container.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.1 0.05", AnchorMax = "0.2 0.1" },
                Button = { Command = $"team.paginate {page - 1}", Color = "0.2 0.5 0.8 1.0" },
                Text = { Text = "<< Previous", FontSize = 20, Align = TextAnchor.MiddleCenter }
            }, parent);
        }
        if (page < totalPages - 1)
        {
            container.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.8 0.05", AnchorMax = "0.9 0.1" },
                Button = { Command = $"team.paginate {page + 1}", Color = "0.2 0.5 0.8 1.0" },
                Text = { Text = "Next >>", FontSize = 20, Align = TextAnchor.MiddleCenter }
            }, parent);
        }
    }
}


        private void OpenTeamSelectionMenu(BasePlayer admin, string targetPlayerId, int page)
        {
            // Destroy any existing team selection UI
            CuiHelper.DestroyUi(admin, "TeamSelectionUI");

            var container = new CuiElementContainer();
            // Create a centered panel for team selection
            container.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0.3 0.3", AnchorMax = "0.7 0.7" },
                Image = { Color = "0.1 0.1 0.1 0.9" }
            }, "Overlay", "TeamSelectionUI");

            // Title label
            container.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = "0.32 0.65", AnchorMax = "0.68 0.75" },
                Text = { Text = "Select a Team", FontSize = 24, Align = TextAnchor.MiddleCenter }
            }, "TeamSelectionUI");

            // Show one button per team from teamNames.int teamCount = teamNames.Count;
            float buttonWidth = 0.15f;
            float spacing = 0.05f;
            float totalWidth = teamCount * buttonWidth + (teamCount - 1) * spacing;
            float startX = 0.5f - totalWidth / 2;
            for (int i = 0; i < teamCount; i++)
            {
                string teamName = teamNames[i];
                float xMin = startX + i * (buttonWidth + spacing);
                float xMax = xMin + buttonWidth;
                container.Add(new CuiButton
                {
                    RectTransform = { AnchorMin = $"{xMin} 0.45", AnchorMax = $"{xMax} 0.55" },
                    Button = { Command = $"team.assignplayer {targetPlayerId} {teamName} {page}", Color = teamColors.ContainsKey(teamName) ? teamColors[teamName] : "0.2 0.2 0.2 1.0" },
                    Text = { Text = teamName, FontSize = 18, Align = TextAnchor.MiddleCenter }
                }, "TeamSelectionUI");
            }

            // A close button to cancel team selection and return to the teams tab.container.Add(new CuiButton
            {
                RectTransform = { AnchorMin = "0.45 0.35", AnchorMax = "0.55 0.4" },
                Button = { Command = $"team.closeSelect {page}", Color = "0.8 0.2 0.2 1.0" },
                Text = { Text = "Close", FontSize = 18, Align = TextAnchor.MiddleCenter }
            }, "TeamSelectionUI");

            CuiHelper.AddUi(admin, container);
        }


        // Pagination command for teams tab.[ConsoleCommand("team.paginate")]
        private void HandleTeamPagination(ConsoleSystem.Arg args)
        {
            BasePlayer admin = args.Connection?.player as BasePlayer;
            if (admin == null) return;
            int page = 0;
            if (args.Args != null && args.Args.Length > 0)
                int.TryParse(args.Args[0], out page);
            OpenMainMenu(admin, "teams");
        }

        // Command triggered when a player name button is clicked.// It opens the team selection menu for that player.[ConsoleCommand("team.openselect")]
        private void OpenTeamSelectCommand(ConsoleSystem.Arg args)
        {
            BasePlayer admin = args.Connection?.player as BasePlayer;
            if (admin == null) return;
            if (args.Args == null || args.Args.Length < 2) return;
            string targetPlayerId = args.Args[0];
            int page = 0;
            int.TryParse(args.Args[1], out page);
            OpenTeamSelectionMenu(admin, targetPlayerId, page);
        }

        // Command to close the team selection menu and return to the teams tab.[ConsoleCommand("team.closeSelect")]
        private void CloseTeamSelectCommand(ConsoleSystem.Arg args)
        {
            BasePlayer admin = args.Connection?.player as BasePlayer;
            if (admin == null) return;
            int page = 0;
            if (args.Args != null && args.Args.Length > 0)
                int.TryParse(args.Args[0], out page);
            CuiHelper.DestroyUi(admin, "TeamSelectionUI");
            OpenMainMenu(admin, "teams");
        }

        // Command that is run when a team button is clicked in the team selection menu.// It assigns the target player to the chosen team.[ConsoleCommand("team.assignplayer")]
private void AssignPlayerToTeamCommand(ConsoleSystem.Arg args)
{
    BasePlayer admin = args.Connection?.player as BasePlayer;
    if (admin == null) return;
    if (args.Args == null || args.Args.Length < 3) return;
    
    string targetPlayerId = args.Args[0];
    string newTeamName = args.Args[1];
    int page = 0;
    int.TryParse(args.Args[2], out page);

    // Validate team name.if (!teamNames.Contains(newTeamName))
    {
        SendReply(admin, $"Team {newTeamName} does not exist.");
        return;
    }

    // Update our internal dictionary.if (playerTeams.ContainsKey(targetPlayerId))
        playerTeams[targetPlayerId] = newTeamName;
    else
        playerTeams.Add(targetPlayerId, newTeamName);

    // Find the target player.var targetPlayer = BasePlayer.FindByID(ulong.Parse(targetPlayerId));
    if (targetPlayer == null)
    {
        SendReply(admin, "Player not found.");
        return;
    }

    // If the player is already in a team, remove them from it.if (targetPlayer.currentTeam != 0)
    {
        var oldTeam = RelationshipManager.ServerInstance.FindTeam(targetPlayer.currentTeam);
        if (oldTeam != null)
        {
            oldTeam.RemovePlayer(targetPlayer.userID);
            UpdateTeamMembers(oldTeam);
        }
    }

    // Find or create the new team.RelationshipManager.PlayerTeam newTeam;
    if (!teamMapping.ContainsKey(newTeamName))
    {
        newTeam = RelationshipManager.ServerInstance.CreateTeam();
        newTeam.teamName = newTeamName;
        teamMapping[newTeamName] = newTeam.teamID;
    }
    else
    {
        newTeam = RelationshipManager.ServerInstance.FindTeam(teamMapping[newTeamName]);
    }

    // Add the player to the new team.newTeam.AddPlayer(targetPlayer);
    targetPlayer.currentTeam = newTeam.teamID;
    UpdateTeamMembers(newTeam);

    // Notify the player.targetPlayer.ChatMessage($"You have been added to the team: {newTeamName}");

    // Close the team selection UI and refresh the Teams tab.CuiHelper.DestroyUi(admin, "TeamSelectionUI");
    OpenMainMenu(admin, "teams", page: page);
}




[ConsoleCommand("team.togglefriendlyfire")]
private void ToggleFriendlyFire(ConsoleSystem.Arg args)
{
    BasePlayer player = args.Connection?.player as BasePlayer;
    if (player == null) return;

    // Toggle the friendly fire setting
    friendlyFire = !friendlyFire;
    Config["FriendlyFire"] = friendlyFire;
    SaveConfig();

    // Notify the admin
    player.ChatMessage($"Friendly Fire is now {(friendlyFire ? "Enabled" : "Disabled")}");

    // Refresh the UI
    OpenMainMenu(player, "teams");
}


[ConsoleCommand("team.toggleddefault")]
private void ToggleDefaultTeam(ConsoleSystem.Arg args)
{
    BasePlayer player = args.Connection?.player as BasePlayer;
    if (player == null || args.Args.Length < 1) return;

    string teamName = args.Args[0];

    if (defaultTeams.Contains(teamName))
        defaultTeams.Remove(teamName);
    else
        defaultTeams.Add(teamName);

    Config["DefaultTeams"] = defaultTeams;
    SaveConfig();

    player.ChatMessage($"Default teams updated: {string.Join(", ", defaultTeams)}");
    OpenMainMenu(player, "teams");
}





void OnPlayerConnected(BasePlayer player)
{
    // Check if the player is already assigned to a team
    if (playerTeams.ContainsKey(player.UserIDString))
    {
        string team = playerTeams[player.UserIDString];
        player.ChatMessage($"Welcome back! You are in team {team}.");
        return;
    }

    // Handle players who are excluded from auto-team assignment
    if (defaultTeams == null || defaultTeams.Count == 0)
    {
        player.ChatMessage("You are not assigned to any team.");
        return;
    }

    // Find the least populated default team
    string assignedTeam = defaultTeams.OrderBy(t =>
    {
        // Ensure the team exists in the teamMapping dictionary
        if (!teamMapping.ContainsKey(t))
        {
            CreateTeamIfNotExists(t);
        }

        return BasePlayer.activePlayerList.Count(p => playerTeams.ContainsKey(p.UserIDString) && playerTeams[p.UserIDString] == t);
    }).First();

    // Assign the player to the selected team
    playerTeams[player.UserIDString] = assignedTeam;

    var newTeam = RelationshipManager.ServerInstance.FindTeam(teamMapping[assignedTeam]);
    if (newTeam == null)
    {
        newTeam = CreateTeamIfNotExists(assignedTeam);
    }

    newTeam.AddPlayer(player);
    player.currentTeam = newTeam.teamID;

    player.ChatMessage($"You have been added to the default team: {assignedTeam}.");
}

// Helper Method to Create a Team If It Doesn't Exist
private RelationshipManager.PlayerTeam CreateTeamIfNotExists(string teamName)
{
    if (!teamMapping.ContainsKey(teamName))
    {
        var newTeam = RelationshipManager.ServerInstance.CreateTeam();
        newTeam.teamName = teamName;
        teamMapping[teamName] = newTeam.teamID;
        return newTeam;
    }

    return RelationshipManager.ServerInstance.FindTeam(teamMapping[teamName]);
}





private void ShowLeaveTeamBlockUI(BasePlayer player)
{
    // Destroy any existing blocking UI
    CuiHelper.DestroyUi(player, "LeaveTeamBlockUI");

    // Only show the UI if leaving teams is not allowed
    if (allowTeamLeave) return;

    // Create a blocking UI
    var container = new CuiElementContainer();
    string panelName = "LeaveTeamBlockUI";

    container.Add(new CuiPanel
    {
        RectTransform = { AnchorMin = "0.15 0.05", AnchorMax = "0.3.12" }, // Position over the in-game Leave Team button
        Image = { Color = "0 0 0 0" } // Semi-transparent black
    }, "Overlay", panelName);



    CuiHelper.AddUi(player, container);
}

private void RemoveLeaveTeamBlockUI(BasePlayer player)
{
    CuiHelper.DestroyUi(player, "LeaveTeamBlockUI");
}






[ConsoleCommand("team.toggleleave")]
private void ToggleAllowLeave(ConsoleSystem.Arg args)
{
    BasePlayer player = args.Connection?.player as BasePlayer;
    if (player == null) return;

    allowTeamLeave = !allowTeamLeave; // Toggle the value
    Config["AllowTeamLeave"] = allowTeamLeave;
    SaveConfig();

    // Notify the admin about the current status
    player.ChatMessage($"Allow Leave is now {(allowTeamLeave ? "enabled" : "disabled")}.");

    // Update the UI for all active players
    foreach (var activePlayer in BasePlayer.activePlayerList)
    {
        if (allowTeamLeave)
        {
            RemoveLeaveTeamBlockUI(activePlayer);
        }
        else
        {
            ShowLeaveTeamBlockUI(activePlayer);
        }
    }

    OpenMainMenu(player, "teams");
}




[ConsoleCommand("team.setdefault")]
private void SetDefaultTeam(ConsoleSystem.Arg args)
{
    BasePlayer admin = args.Connection?.player as BasePlayer;
    if (admin == null || args.Args.Length < 1) return;

    string teamName = args.Args[0];

    // Validate the team name
    if (!teamNames.Contains(teamName)) return;

    // Update the default team
    defaultTeam = teamName;

    // Save the default team in the configuration
    Config["DefaultTeam"] = defaultTeam;
    SaveConfig();

    // Refresh the UI for the admin
    OpenMainMenu(admin, "teams");
}


[ConsoleCommand("team.assign")]
private void AssignPlayerToTeam(ConsoleSystem.Arg args)
{
    BasePlayer admin = args.Connection?.player as BasePlayer;
    if (admin == null || args.Args.Length < 2) return;

    string playerId = args.Args[0];
    string newTeamName = args.Args[1];

    // Validate the team name
    if (!teamNames.Contains(newTeamName)) return;

    // Find the target player
    var targetPlayer = BasePlayer.activePlayerList.FirstOrDefault(p => p.UserIDString == playerId);
    if (targetPlayer == null) return;

    // Update the player's team (admins bypass allowTeamLeave)
    if (playerTeams.ContainsKey(playerId))
    {
        playerTeams[playerId] = newTeamName;
    }
    else
    {
        playerTeams.Add(playerId, newTeamName);
    }

    // Check if the player is already in a team
    if (targetPlayer.currentTeam != 0)
    {
        var oldTeam = RelationshipManager.ServerInstance.FindTeam(targetPlayer.currentTeam);
        if (oldTeam != null)
        {
            oldTeam.RemovePlayer(targetPlayer.userID);
            UpdateTeamMembers(oldTeam);
        }
    }

    // Find or create the new team
    RelationshipManager.PlayerTeam newTeam;
    if (!teamMapping.ContainsKey(newTeamName))
    {
        newTeam = RelationshipManager.ServerInstance.CreateTeam();
        newTeam.teamName = newTeamName;
        teamMapping[newTeamName] = newTeam.teamID;
    }
    else
    {
        newTeam = RelationshipManager.ServerInstance.FindTeam(teamMapping[newTeamName]);
    }

    // Add the player to the new team
    newTeam.AddPlayer(targetPlayer);
    targetPlayer.currentTeam = newTeam.teamID;
    UpdateTeamMembers(newTeam);

    // Notify the player
    targetPlayer.ChatMessage($"You have been added to the team: {newTeamName}");

    // Refresh the UI for the admin
    OpenMainMenu(admin, "teams");
}







private void UpdateTeamMembers(RelationshipManager.PlayerTeam team)
{
    foreach (var memberId in team.members)
    {
        var member = BasePlayer.FindByID(memberId);
        if (member != null)
        {
            member.SendNetworkUpdateImmediate();
            SendTeamInfo(member, team);
        }
    }
}

private void SendTeamInfo(BasePlayer player, RelationshipManager.PlayerTeam team)
{
    var members = new List<object>();
    foreach (var memberId in team.members)
    {
        var member = BasePlayer.FindByID(memberId);
        if (member != null)
        {
            members.Add(new
            {
                userid = memberId,
                username = member.displayName,
                online = true,
                avatar = string.Empty // You can add avatar URL logic if needed
            });
        }
    }

    var teamInfo = new
    {
        teamID = team.teamID,
        teamName = team.teamName,
        members
    };

    player.ClientRPCPlayer(null, player, "CLIENT_ReceiveTeamInfo", teamInfo);
}





private void AddPermissionsTabContent(CuiElementContainer container, string parent, int currentPage = 0)
{
    const float buttonHeight = 0.05f;
    const float fullButtonWidth = 0.15f;  // Full width for individual player buttons
    const float halfButtonWidth = fullButtonWidth / 2;  // Half width for Grant/Revoke buttons
    const float spacingBetweenPairs = 0.02f;  // Space between revoke -> next grant
    const float rowSpacing = 0.06f;
    const float startY = 0.85f;
    const int rowsPerPage = 10;

    var entries = new List<string>();
    entries.AddRange(teamNames);  // Add teams
    entries.AddRange(BasePlayer.activePlayerList.Select(p => p.UserIDString));  // Add players

    int totalPages = (int)Math.Ceiling((double)entries.Count / rowsPerPage);
    int startRow = currentPage * rowsPerPage;

    // GLOBAL ROW (Handles all players)
    float yMinGlobal = startY;
    float yMaxGlobal = yMinGlobal + buttonHeight;

    container.Add(new CuiLabel
    {
        RectTransform = { AnchorMin = $"0.1 {yMinGlobal}", AnchorMax = $"0.3 {yMaxGlobal}" },
        Text = { Text = "Global", FontSize = 20, Align = UnityEngine.TextAnchor.MiddleLeft }
    }, parent);

    for (int j = 0; j < availablePerms.Count; j++)
    {
        float xStart = 0.35f + j * (fullButtonWidth + spacingBetweenPairs);

        container.Add(new CuiButton
        {
            RectTransform = { AnchorMin = $"{xStart} {yMinGlobal}", AnchorMax = $"{xStart + halfButtonWidth} {yMaxGlobal}" },
            Button = { Command = $"perm.globalgrant {availablePerms[j]}", Color = "0.2 0.8 0.2 1.0" },
            Text = { Text = $"Grant", FontSize = 14, Align = UnityEngine.TextAnchor.MiddleCenter }
        }, parent);

        container.Add(new CuiButton
        {
            RectTransform = { AnchorMin = $"{xStart + halfButtonWidth} {yMinGlobal}", AnchorMax = $"{xStart + fullButtonWidth} {yMaxGlobal}" },
            Button = { Command = $"perm.globalrevoke {availablePerms[j]}", Color = "0.8 0.2 0.2 1.0" },
            Text = { Text = $"Revoke", FontSize = 14, Align = UnityEngine.TextAnchor.MiddleCenter }
        }, parent);
    }

    // TEAM AND PLAYER ROWS
    for (int i = 0; i < rowsPerPage; i++)
    {
        int index = startRow + i;
        if (index >= entries.Count) break;

        string entry = entries[index];
        float yMin = startY - (i + 1) * rowSpacing;
        float yMax = yMin + buttonHeight;

        if (teamNames.Contains(entry))
        {
            // Team row with Grant/Revoke buttons
            container.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = $"0.1 {yMin}", AnchorMax = $"0.3 {yMax}" },
                Text = { Text = entry, FontSize = 20, Align = UnityEngine.TextAnchor.MiddleLeft }
            }, parent);

            for (int j = 0; j < availablePerms.Count; j++)
            {
                float xStart = 0.35f + j * (fullButtonWidth + spacingBetweenPairs);

                container.Add(new CuiButton
                {
                    RectTransform = { AnchorMin = $"{xStart} {yMin}", AnchorMax = $"{xStart + halfButtonWidth} {yMax}" },
                    Button = { Command = $"perm.teamgrant {entry} {availablePerms[j]}", Color = "0.2 0.8 0.2 1.0" },
                    Text = { Text = $"Grant", FontSize = 14, Align = UnityEngine.TextAnchor.MiddleCenter }
                }, parent);

                container.Add(new CuiButton
                {
                    RectTransform = { AnchorMin = $"{xStart + halfButtonWidth} {yMin}", AnchorMax = $"{xStart + fullButtonWidth} {yMax}" },
                    Button = { Command = $"perm.teamrevoke {entry} {availablePerms[j]}", Color = "0.8 0.2 0.2 1.0" },
                    Text = { Text = $"Revoke", FontSize = 14, Align = UnityEngine.TextAnchor.MiddleCenter }
                }, parent);
            }
        }
        else
        {
            // Individual player row with full-width permission buttons
            string displayName = BasePlayer.FindByID(ulong.Parse(entry))?.displayName ?? "Unknown Player";

            container.Add(new CuiLabel
            {
                RectTransform = { AnchorMin = $"0.1 {yMin}", AnchorMax = $"0.3 {yMax}" },
                Text = { Text = displayName, FontSize = 20, Align = UnityEngine.TextAnchor.MiddleLeft }
            }, parent);

            for (int j = 0; j < availablePerms.Count; j++)
            {
                float xStart = 0.35f + j * (fullButtonWidth + spacingBetweenPairs);
                bool hasPerm = permission.UserHasPermission(entry, availablePerms[j]);

                container.Add(new CuiButton
                {
                    RectTransform = { AnchorMin = $"{xStart} {yMin}", AnchorMax = $"{xStart + fullButtonWidth} {yMax}" },
                    Button = { Command = $"perm.toggle {entry} {availablePerms[j]}", Color = hasPerm ? "0.2 0.8 0.2 1.0" : "0.8 0.2 0.2 1.0" },
                    Text = { Text = availablePerms[j], FontSize = 18, Align = UnityEngine.TextAnchor.MiddleCenter }
                }, parent);
            }
        }
    }
}



private const string BypassPermission = "eventcontrolui.bypass";

[ConsoleCommand("perm.globalgrant")]
private void GrantGlobalPermission(ConsoleSystem.Arg args)
{
    if (args.Connection?.player is not BasePlayer admin || args.Args.Length < 1)
    {
        
        return;
    }

    string perm = args.Args[0].Trim();
    int grantedCount = 0;

    foreach (var player in BasePlayer.activePlayerList)
    {
        if (permission.UserHasPermission(player.UserIDString, BypassPermission))
        {
            SendReply(admin, $"Skipping {player.displayName} (bypass enabled)");
            continue;
        }

        permission.GrantUserPermission(player.UserIDString, perm, null);

        if (permission.UserHasPermission(player.UserIDString, perm))
        {
            grantedCount++;
        }
        else
        {
            Puts($"[DEBUG] Failed to grant {perm} to {player.displayName}");
        }
    }

    SendReply(admin, $"Granted {perm} to {grantedCount} players (excluding bypassed).");
    OpenMainMenu(admin, "permissions");
}

[ConsoleCommand("perm.globalrevoke")]
private void RevokeGlobalPermission(ConsoleSystem.Arg args)
{
    if (args.Connection?.player is not BasePlayer admin || args.Args.Length < 1)
    {
       
        return;
    }

    string perm = args.Args[0].Trim();
    int revokedCount = 0;

    foreach (var player in BasePlayer.activePlayerList)
    {
        if (permission.UserHasPermission(player.UserIDString, BypassPermission))
        {
            SendReply(admin, $"Skipping {player.displayName} (bypass enabled)");
            continue;
        }

        permission.RevokeUserPermission(player.UserIDString, perm);

        if (!permission.UserHasPermission(player.UserIDString, perm))
        {
            revokedCount++;
        }
        else
        {
            Puts($"[DEBUG] Failed to revoke {perm} from {player.displayName}");
        }
    }

    SendReply(admin, $"Revoked {perm} from {revokedCount} players (excluding bypassed).");
    OpenMainMenu(admin, "permissions");
}


private Dictionary<string, TimerGroup> timerGroups = new Dictionary<string, TimerGroup>();

public class TimerGroup
{
    public string GroupName { get; set; }
    public float RemainingTime { get; set; }
    public bool IsRunning { get; set; }
    public bool IsUIVisible { get; set; }
    public bool RaidingOnZero { get; set; }
    public bool BuildingOnZero { get; set; }
    public bool PvPOnZero { get; set; }

    public TimerGroup(string groupName)
    {
        GroupName = groupName;
        RemainingTime = 0f;
        IsRunning = false;
        IsUIVisible = true;
        RaidingOnZero = false;
        BuildingOnZero = false;
        PvPOnZero = false;
    }
}

private void AddGlobalTabContent(CuiElementContainer container, string parent)
{
    // Row 1: Set World Spawn, Heal All, Kill All
    float row1YMin = 0.85f;
    float row1YMax = 0.9f;
    float buttonWidth = 0.25f;
    float buttonHeight = 0.05f;
    float spacing = 0.02f;

    // Set World Spawn Button
    container.Add(new CuiButton
    {
        RectTransform = { AnchorMin = "0.1 0.85", AnchorMax = $"{0.1 + buttonWidth} {row1YMax}" },
        Button = { Command = "global.setworldspawn", Color = "0.2 0.5 0.8 1.0" },
        Text = { Text = "Set World Spawn", FontSize = 20, Align = TextAnchor.MiddleCenter }
    }, parent);

    // Heal All Button
    container.Add(new CuiButton
    {
        RectTransform = { AnchorMin = $"{0.1 + buttonWidth + spacing} 0.85", AnchorMax = $"{0.1 + 2 * buttonWidth + spacing} {row1YMax}" },
        Button = { Command = "global.healall", Color = "0.2 0.8 0.2 1.0" },
        Text = { Text = "Heal All", FontSize = 20, Align = TextAnchor.MiddleCenter }
    }, parent);

    // Kill All Button
    container.Add(new CuiButton
    {
        RectTransform = { AnchorMin = $"{0.1 + 2 * buttonWidth + 2 * spacing} 0.85", AnchorMax = $"{0.1 + 3 * buttonWidth + 2 * spacing} {row1YMax}" },
        Button = { Command = "global.killall", Color = "0.8 0.2 0.2 1.0" },
        Text = { Text = "Kill All", FontSize = 20, Align = TextAnchor.MiddleCenter }
    }, parent);

    // Define the actions for each row
    List<string> actionButtons = new List<string> { "Freeze", "Raiding", "PvP", "God Mode", "Building", "Loot" };
List<string> actionCommands = new List<string> { "freeze", "toggle.raiding", "toggle.pvp", "toggle.godmode", "toggle.building", "toggle.loot" };



    // Row 2: Global actions
    float currentY = row1YMin - 0.1f;
    AddActionRow(container, parent, "Global", actionButtons, actionCommands, 0.1f, currentY);

    // Rows 3-6: Team actions
    for (int i = 0; i < teamNames.Count; i++)
    {
        currentY -= 0.07f;
        AddActionRow(container, parent, teamNames[i], actionButtons, actionCommands, 0.1f, currentY, isTeam: true);
    }
}

private void AddActionRow(CuiElementContainer container, string parent, string rowLabel, List<string> actions, List<string> commands, float xStart, float y, bool isTeam = false)
{
    // Row Label
    container.Add(new CuiLabel
    {
        RectTransform = { AnchorMin = $"{xStart} {y + 0.04f}", AnchorMax = $"{xStart + 0.1f} {y + 0.09f}" },
        Text = { Text = $"[{rowLabel}]", FontSize = 20, Align = TextAnchor.MiddleLeft }
    }, parent);

    // Action Buttons
    float buttonWidth = 0.1f;
    float buttonHeight = 0.05f;
    float spacing = 0.02f;
    float currentX = xStart + 0.12f;

    for (int i = 0; i < actions.Count; i++)
    {
        string action = actions[i];
        string command = commands[i];

        // Determine the current state of the action
        bool isEnabled = false;
switch (action)
{
    case "Freeze":
         // Set isEnabled for Freeze, e.g.:
         isEnabled = isTeam ? (teamFreezeStatus.ContainsKey(rowLabel) && teamFreezeStatus[rowLabel])
                            : globalFreezeEnabled;
         break;  // Ensure break is present here
    case "Loot":
         isEnabled = isTeam ? (teamLootStatus.ContainsKey(rowLabel) && teamLootStatus[rowLabel])
                            : lootAllowed;
         break;
            case "Raiding":
                isEnabled = isTeam ? teamRaidingStatus.ContainsKey(rowLabel) && teamRaidingStatus[rowLabel]
                                   : raidingEnabled;
                break;
            case "PvP":
                isEnabled = isTeam ? teamPvPStatus.ContainsKey(rowLabel) && teamPvPStatus[rowLabel]
                                   : pvpEnabled;
                break;
            case "God Mode":
                isEnabled = isTeam ? teamGodModeStatus.ContainsKey(rowLabel) && teamGodModeStatus[rowLabel]
                                   : godModeEnabled;
                break;
            case "Building":
                isEnabled = isTeam ? teamBuildingStatus.ContainsKey(rowLabel) && teamBuildingStatus[rowLabel]
                                   : buildingEnabled;
                break;
            default:
                break;
        }

        // Set button color based on state
        string color = isEnabled ? "0.2 0.8 0.2 1.0" : "0.8 0.2 0.2 1.0"; // Green for on, Red for off

        // Define the command based on whether it's Global or Team
        string fullCommand = "";
        switch (action)
        {
            case "Freeze":
                fullCommand = isTeam ? $"global.freeze.team {rowLabel}" : $"global.freeze";
                break;
            case "Raiding":
                fullCommand = isTeam ? $"global.toggle.raiding.team {rowLabel}" : $"global.toggle.raiding";
                break;
            case "PvP":
                fullCommand = isTeam ? $"global.toggle.pvp.team {rowLabel}" : $"global.toggle.pvp";
                break;
            case "God Mode":
                fullCommand = isTeam ? $"global.toggle.godmode.team {rowLabel}" : $"global.toggle.godmode";
                break;
            case "Building":
                fullCommand = isTeam ? $"global.toggle.building.team {rowLabel}" : $"global.toggle.building";
                break;
            default:
                fullCommand = $"global.{command.ToLower()}";
                break;
        }

        container.Add(new CuiButton
        {
            RectTransform = { AnchorMin = $"{currentX} {y}", AnchorMax = $"{currentX + buttonWidth} {y + buttonHeight}" },
            Button = { Command = fullCommand, Color = color },
            Text = { Text = action, FontSize = 18, Align = TextAnchor.MiddleCenter }
        }, parent);

        currentX += buttonWidth + spacing;
    }
}

// Global Permission Flag
private bool globalPermEnabled;

// Global Settings
private bool globalFreezeEnabled;
private bool globalBuildingEnabled;
private bool globalRaidingEnabled;
private bool globalPvPEnabled;

// Global feature states
private bool raidingEnabled = true;
private bool pvpEnabled = true;
private bool godModeEnabled = false;
private bool buildingEnabled = true;

// Team-specific feature states
private Dictionary<string, bool> teamFreezeStatus = new Dictionary<string, bool>();
private Dictionary<string, bool> teamRaidingStatus = new Dictionary<string, bool>();
private Dictionary<string, bool> teamPvPStatus = new Dictionary<string, bool>();
private Dictionary<string, bool> teamGodModeStatus = new Dictionary<string, bool>();
private Dictionary<string, bool> teamBuildingStatus = new Dictionary<string, bool>();

private void LoadConfigValues()
{
    // Load global permissions
    globalPermEnabled = Convert.ToBoolean(Config["GlobalPermEnabled"] ?? false);
    globalFreezeEnabled = Convert.ToBoolean(Config["GlobalFreezeEnabled"] ?? false);
    globalBuildingEnabled = Convert.ToBoolean(Config["GlobalBuildingEnabled"] ?? true);
    globalRaidingEnabled = Convert.ToBoolean(Config["GlobalRaidingEnabled"] ?? true);
    globalPvPEnabled = Convert.ToBoolean(Config["GlobalPvPEnabled"] ?? true);

    // Load team-specific settings
    teamFreezeStatus = Config["TeamFreezeStatus"] as Dictionary<string, bool> ?? new Dictionary<string, bool>();
    teamBuildingStatus = Config["TeamBuildingStatus"] as Dictionary<string, bool> ?? new Dictionary<string, bool>();
    teamRaidingStatus = Config["TeamRaidingStatus"] as Dictionary<string, bool> ?? new Dictionary<string, bool>();
    teamPvPStatus = Config["TeamPvPStatus"] as Dictionary<string, bool> ?? new Dictionary<string, bool>();
}


[ConsoleCommand("global.toggle.loot")]
private void ToggleGlobalLoot(ConsoleSystem.Arg args)
{
    BasePlayer admin = args.Connection?.player as BasePlayer;
    if (admin == null || !permission.UserHasPermission(admin.UserIDString, PermissionManagePerms))
    {
         SendReply(admin, "You don't ha
// ... [Code preview truncated due to length. Full source in attached file] ...

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