using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using Oxide.Core; using Oxide.Core.Configuration; using Oxide.Core.Plugins; using Oxide.Game.Rust.Cui; using UnityEngine; namespace Oxide.Plugins { [Info("TeamShare", "Uzumi", "1.0.0")] [Description("Allows automatic team auth on codelocks, turrets, and tool cupboards via the /share command, including guest code functionality and NoEscape integration.")] public class TeamShare : RustPlugin { [PluginReference] private Plugin WelcomeController; [PluginReference] private Plugin NoEscape; // Plugin configuration settings private class Configuration { [JsonProperty("Use Permissions")] public bool UsePermissions = true; [JsonProperty("Using WelcomeController")] public bool UsingWelcomeController = false; [JsonProperty("Debug Mode")] public bool DebugMode = false; [JsonProperty("Messages")] public Dictionary Messages = new Dictionary { ["NoPermission"] = "You don't have permission to use this command.", ["InvalidCode"] = "Invalid code. Please enter a 4-digit numerical code.", ["CodeSet"] = "Code set to: {0}", ["GuestCodeSet"] = "Guest code set to: {0}", ["NoCodeSet"] = "You have not set any code. Use the UI to set a code.", ["CodeAutoLocked"] = "Code lock placed with code {0}.", ["CodeAutoLockedWithGuest"] = "Code lock placed with code {0} and guest code {1}.", ["CupboardAuthorized"] = "{0} authorized on the Tool Cupboard", ["NoEscape.RaidBlocked"] = "Cannot set codes during raid block.", ["NoEscape.CombatBlocked"] = "Cannot set codes during combat block." }; [JsonProperty("UI Settings")] public UISettings UISettings = new UISettings(); [JsonProperty("Commands Settings")] public CommandsSettings CommandsSettings = new CommandsSettings(); [JsonProperty("AddonName for WelcomeController")] public string AddonName = "TeamShare"; [JsonProperty("NoEscape Integration")] public NoEscapeIntegrationSettings NoEscapeIntegration = new NoEscapeIntegrationSettings(); } // Commands configuration settings public class CommandsSettings { [JsonProperty("Open UI Command")] public string OpenUI = "teamui"; [JsonProperty("Share Command")] public string Share = "share"; } // Simplified UI configuration settings public class UISettings { public string UIBackgroundColor { get; set; } = "0 0 0 0.6"; public string UIPrimaryColor { get; set; } = "1 0.4 0 0.5"; public string UIDangerButtonColor { get; set; } = "1 0 0 1"; } // NoEscape integration settings public class NoEscapeIntegrationSettings { [JsonProperty("Block During Raid Block")] public bool BlockDuringRaidBlock = true; [JsonProperty("Block During Combat Block")] public bool BlockDuringCombatBlock = true; } // Player-specific data private class PlayerData { public bool enabledCodelock { get; set; } = true; public bool enabledCupboard { get; set; } = true; public bool enabledTurret { get; set; } = true; public bool enabledQuiet { get; set; } = false; public bool enabledGuestCode { get; set; } = false; // Guest code toggle } private const string PERMISSION_USE = "teamshare.use"; // Data files for storing player information private DynamicConfigFile _playerCodesData; private DynamicConfigFile _playerGuestCodesData; private DynamicConfigFile _playerDataFile; private static Configuration _pluginConfig; // Data dictionaries private Dictionary _playerCodes = new Dictionary(); private Dictionary _playerGuestCodes = new Dictionary(); // Guest codes private Dictionary _playerData = new Dictionary(); // UI tracking private Dictionary playerUIs = new Dictionary(); // Temporary storage for code inputs private Dictionary playerTempCodes = new Dictionary(); private Dictionary playerTempGuestCodes = new Dictionary(); // Guest code input // Timers for code change messages private Dictionary codeChangedTimers = new Dictionary(); private Dictionary guestCodeChangedTimers = new Dictionary(); private UISettings uiSettings; #region Hooks and Initialization // Plugin initialization void Init() { LoadConfigData(); LoadPlayerData(); permission.RegisterPermission(PERMISSION_USE, this); lang.RegisterMessages(_pluginConfig.Messages, this); uiSettings = _pluginConfig.UISettings; HandleCommands(); Interface.CallHook("OnWCRequestColors", _pluginConfig.AddonName); if (_pluginConfig.DebugMode) { Puts("TeamShare plugin initialized."); } } // Load configuration private void LoadConfigData() { try { _pluginConfig = Config.ReadObject(); if (_pluginConfig == null) throw new Exception(); if (_pluginConfig.DebugMode) { Puts("Configuration loaded successfully."); } } catch { PrintError("Configuration file is corrupt or missing. Creating new configuration."); LoadDefaultConfig(); } } protected override void LoadDefaultConfig() { _pluginConfig = new Configuration(); SaveConfig(); if (_pluginConfig.DebugMode) { Puts("Default configuration loaded."); } } protected override void SaveConfig() { Config.WriteObject(_pluginConfig, true); if (_pluginConfig.DebugMode) { Puts("Configuration saved."); } } // Load player data from data files private void LoadPlayerData() { _playerCodesData = Interface.Oxide.DataFileSystem.GetDatafile("TeamShare/PlayerCodes"); _playerGuestCodesData = Interface.Oxide.DataFileSystem.GetDatafile("TeamShare/PlayerGuestCodes"); _playerDataFile = Interface.Oxide.DataFileSystem.GetDatafile("TeamShare/PlayerData"); try { _playerCodes = _playerCodesData.ReadObject>(); if (_pluginConfig.DebugMode) { Puts("Player codes loaded."); } } catch { _playerCodes = new Dictionary(); PrintError("Failed to load PlayerCodes data. Initialized empty dictionary."); } try { _playerGuestCodes = _playerGuestCodesData.ReadObject>(); if (_pluginConfig.DebugMode) { Puts("Player guest codes loaded."); } } catch { _playerGuestCodes = new Dictionary(); PrintError("Failed to load PlayerGuestCodes data. Initialized empty dictionary."); } try { _playerData = _playerDataFile.ReadObject>(); if (_pluginConfig.DebugMode) { Puts("Player data loaded."); } } catch { _playerData = new Dictionary(); PrintError("Failed to load PlayerData. Initialized empty dictionary."); } } // Save player data to data files private void SavePlayerData() { _playerCodesData.WriteObject(_playerCodes); _playerGuestCodesData.WriteObject(_playerGuestCodes); _playerDataFile.WriteObject(_playerData); if (_pluginConfig.DebugMode) { Puts("Player data saved."); } } // Retrieve or create PlayerData for a user private PlayerData GetPlayerData(ulong userID) { if (!_playerData.ContainsKey(userID)) { _playerData.Add(userID, new PlayerData()); SavePlayerData(); if (_pluginConfig.DebugMode) { Puts($"Created new PlayerData for UserID: {userID}"); } } return _playerData[userID]; } // Check if a player has the required permission private bool isPermitted(BasePlayer player) { if (_pluginConfig.UsePermissions && !permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) { if (_pluginConfig.DebugMode) { Puts($"Player {player.displayName} does not have permission to use TeamShare."); } return false; } return true; } // Validate if the code is a 4-digit number private bool IsValidCode(string code) { bool isValid = code.Length == 4 && int.TryParse(code, out _); if (_pluginConfig.DebugMode) { Puts($"Code validation for '{code}': {isValid}"); } return isValid; } // Set player's main code private void SetPlayerCode(ulong playerId, string code) { _playerCodes[playerId] = code; SavePlayerData(); if (_pluginConfig.DebugMode) { Puts($"Set main code for UserID {playerId} to {code}"); } } // Retrieve player's main code private string GetPlayerCode(ulong playerId) { string code = _playerCodes.TryGetValue(playerId, out string value) ? value : null; if (_pluginConfig.DebugMode) { Puts($"Retrieved main code for UserID {playerId}: {code}"); } return code; } // Set player's guest code private void SetPlayerGuestCode(ulong playerId, string code) { _playerGuestCodes[playerId] = code; SavePlayerData(); if (_pluginConfig.DebugMode) { Puts($"Set guest code for UserID {playerId} to {code}"); } } // Retrieve player's guest code private string GetPlayerGuestCode(ulong playerId) { string code = _playerGuestCodes.TryGetValue(playerId, out string value) ? value : null; if (_pluginConfig.DebugMode) { Puts($"Retrieved guest code for UserID {playerId}: {code}"); } return code; } // Check if a specific plugin is being used [HookMethod("IsUsingPlugin")] private bool IsUsingPlugin(string pluginName) { bool isUsing = pluginName.Equals(_pluginConfig.AddonName, StringComparison.OrdinalIgnoreCase); if (_pluginConfig.DebugMode) { Puts($"IsUsingPlugin check for '{pluginName}': {isUsing}"); } return isUsing; } // Handle UI panel requests from WelcomeController [HookMethod("OnWCRequestedUIPanel")] private void OnWCRequestedUIPanel(BasePlayer player, string panelName, string neededPlugin) { if (!neededPlugin.Equals(_pluginConfig.AddonName, StringComparison.OrdinalIgnoreCase)) return; if (_pluginConfig.DebugMode) { Puts($"Received UI panel request '{panelName}' from player {player.displayName}."); } CreateUI(player, panelName); } // Request theme colors from WelcomeController void OnWCRequestColors(string pluginName) { if (!pluginName.Equals(_pluginConfig.AddonName, StringComparison.OrdinalIgnoreCase)) return; if (_pluginConfig.DebugMode) { Puts($"Requesting theme colors for plugin '{pluginName}'."); } Interface.CallHook("WCSendColors", ConvertToDictionary(uiSettings), pluginName); } // Receive and apply theme colors from WelcomeController void OnWCSentThemeColors(List pluginNames, Dictionary themeColors) { if (!pluginNames.Contains(_pluginConfig.AddonName)) return; if (_pluginConfig.DebugMode) { Puts($"Received theme colors for plugin '{_pluginConfig.AddonName}'."); } uiSettings.UIBackgroundColor = themeColors.ContainsKey("BackgroundColor") ? themeColors["BackgroundColor"] : uiSettings.UIBackgroundColor; uiSettings.UIPrimaryColor = themeColors.ContainsKey("PrimaryButtonColor") ? themeColors["PrimaryButtonColor"] : uiSettings.UIPrimaryColor; uiSettings.UIDangerButtonColor = themeColors.ContainsKey("SecondaryButtonColor") ? themeColors["SecondaryButtonColor"] : uiSettings.UIDangerButtonColor; SaveConfig(); } // Convert UISettings to dictionary for WelcomeController private static Dictionary ConvertToDictionary(UISettings uiSettings) { var dictionary = new Dictionary(); foreach (var property in typeof(UISettings).GetProperties()) { string key = property.Name; string value = property.GetValue(uiSettings)?.ToString(); dictionary[key] = value; } return dictionary; } // Register chat commands based on configuration private void HandleCommands() { cmd.AddChatCommand(_pluginConfig.CommandsSettings.OpenUI, this, OpenTeamShareUI); cmd.AddChatCommand(_pluginConfig.CommandsSettings.Share, this, OpenTeamShareUI); if (_pluginConfig.DebugMode) { Puts($"Chat commands registered: /{_pluginConfig.CommandsSettings.OpenUI}, /{_pluginConfig.CommandsSettings.Share}"); } } // Cleanup UI on plugin unload private void Unload() { foreach (BasePlayer player in BasePlayer.activePlayerList) { DestroyUI(player); } if (_pluginConfig.DebugMode) { Puts("TeamShare plugin unloaded. All UIs destroyed."); } } #endregion #region Entity Deployment Hooks // Handle entity spawn events void OnEntitySpawned(BaseNetworkable entity) { if (entity == null) return; if (entity is CodeLock codelock) { HandleCodeLockDeployment(codelock); } else if (entity is BuildingPrivlidge cupboard) { HandleCupboardDeployment(cupboard); } else if (entity is AutoTurret turret) { HandleAutoTurretDeployment(turret); } } // Handle CodeLock deployment private void HandleCodeLockDeployment(CodeLock codelock) { BasePlayer player = getPermittedOwner(codelock); if (player == null || !isPermitted(player)) return; if (_pluginConfig.DebugMode) { Puts($"Handling CodeLock deployment by {player.displayName} (UserID: {player.UserIDString})"); } // Integrate with NoEscape plugin if (NoEscape != null) { if (_pluginConfig.NoEscapeIntegration.BlockDuringRaidBlock && (bool)(NoEscape.Call("IsRaidBlocked", player) ?? false)) { player.ChatMessage(Lang("NoEscape.RaidBlocked", player.UserIDString)); if (_pluginConfig.DebugMode) { Puts($"Raid block active. Deployment of CodeLock by {player.displayName} blocked."); } return; } if (_pluginConfig.NoEscapeIntegration.BlockDuringCombatBlock && (bool)(NoEscape.Call("IsCombatBlocked", player) ?? false)) { player.ChatMessage(Lang("NoEscape.CombatBlocked", player.UserIDString)); if (_pluginConfig.DebugMode) { Puts($"Combat block active. Deployment of CodeLock by {player.displayName} blocked."); } return; } } PlayerData pData = GetPlayerData(player.userID); if (pData.enabledCodelock) { string code = GetPlayerCode(player.userID); if (!string.IsNullOrEmpty(code)) { codelock.code = code; codelock.hasCode = true; codelock.SetFlag(BaseEntity.Flags.Locked, true); codelock.whitelistPlayers.Clear(); // Add team members to whitelist SetEntityWhitelist(player, codelock); // Handle guest code if enabled if (pData.enabledGuestCode) { string guestCode = GetPlayerGuestCode(player.userID); if (!string.IsNullOrEmpty(guestCode)) { codelock.guestCode = guestCode; codelock.hasGuestCode = true; codelock.guestPlayers.Clear(); } else { codelock.hasGuestCode = false; } } else { codelock.hasGuestCode = false; } codelock.SendNetworkUpdateImmediate(); if (!pData.enabledQuiet) { List authorizedNames = GetAuthorizedNames(GetTeamMembers(player)); string message = pData.enabledGuestCode && codelock.hasGuestCode ? $"Code lock placed with code \"{code}\" and guest code \"{codelock.guestCode}\" authed {string.Join(", ", authorizedNames)}" : $"Code lock placed with code \"{code}\" authed {string.Join(", ", authorizedNames)}"; SendChatMessage(player, message); } } else { if (!pData.enabledQuiet) SendChatMessage(player, Lang("NoCodeSet", player.UserIDString)); } } if (_pluginConfig.DebugMode) { Puts($"CodeLock deployment handled for {player.displayName}."); } } // Handle Tool Cupboard deployment private void HandleCupboardDeployment(BuildingPrivlidge cupboard) { BasePlayer player = getPermittedOwner(cupboard); if (player == null || !isPermitted(player)) return; if (_pluginConfig.DebugMode) { Puts($"Handling Tool Cupboard deployment by {player.displayName} (UserID: {player.UserIDString})"); } PlayerData pData = GetPlayerData(player.userID); if (pData.enabledCupboard) { SetEntityWhitelist(player, cupboard); cupboard.SendNetworkUpdateImmediate(); if (!pData.enabledQuiet) { List authorizedNames = GetAuthorizedNames(GetTeamMembers(player)); string authedMessage = $"Authorized {string.Join(", ", authorizedNames)} on the Tool Cupboard"; SendChatMessage(player, authedMessage); } } if (_pluginConfig.DebugMode) { Puts($"Tool Cupboard deployment handled for {player.displayName}."); } } // Handle Auto Turret deployment private void HandleAutoTurretDeployment(AutoTurret turret) { BasePlayer player = getPermittedOwner(turret); if (player == null || !isPermitted(player)) return; if (_pluginConfig.DebugMode) { Puts($"Handling Auto Turret deployment by {player.displayName} (UserID: {player.UserIDString})"); } PlayerData pData = GetPlayerData(player.userID); if (pData.enabledTurret) { SetEntityWhitelist(player, turret); turret.SendNetworkUpdateImmediate(); if (!pData.enabledQuiet) { List authorizedNames = GetAuthorizedNames(GetTeamMembers(player)); string authedMessage = $"Authorized {string.Join(", ", authorizedNames)} on the Auto Turret"; SendChatMessage(player, authedMessage); } } if (_pluginConfig.DebugMode) { Puts($"Auto Turret deployment handled for {player.displayName}."); } } // Retrieve the permitted owner of an entity private BasePlayer getPermittedOwner(BaseEntity entity) { if (entity == null) return null; BasePlayer player = BasePlayer.FindByID(entity.OwnerID); if (player == null || !isPermitted(player)) { if (_pluginConfig.DebugMode) { Puts($"Entity owner not found or lacks permission."); } return null; } if (_pluginConfig.DebugMode) { Puts($"Permitted owner found: {player.displayName} (UserID: {player.UserIDString})"); } return player; } // Get team members' UserIDs private List GetTeamMembers(BasePlayer player) { List members = new List { player.userID }; if (player.currentTeam != 0) { RelationshipManager.PlayerTeam team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam); if (team != null) { foreach (ulong memberId in team.members) { if (memberId != player.userID) members.Add(memberId); } } } if (_pluginConfig.DebugMode) { Puts($"Team members for {player.displayName}: {string.Join(", ", members)}"); } return members; } // Get authorized player names from UserIDs private List GetAuthorizedNames(List memberIds) { List names = new List(); foreach (ulong id in memberIds) { string name = covalence.Players.FindPlayerById(id.ToString())?.Name ?? "Unknown"; names.Add($"\"{name}\""); } return names; } // Set whitelist for CodeLock private void SetEntityWhitelist(BasePlayer player, CodeLock codelock) { List teamMembers = GetTeamMembers(player); foreach (ulong memberId in teamMembers) { codelock.whitelistPlayers.Add(memberId); } if (_pluginConfig.DebugMode) { foreach (ulong memberId in teamMembers) { Puts($"UserID {memberId} authorized on CodeLock by {player.displayName}."); } } } // Set whitelist for Tool Cupboard private void SetEntityWhitelist(BasePlayer player, BuildingPrivlidge cupboard) { List teamMembers = GetTeamMembers(player); foreach (ulong memberId in teamMembers) { cupboard.authorizedPlayers.Add(new ProtoBuf.PlayerNameID() { userid = memberId, username = covalence.Players.FindPlayerById(memberId.ToString())?.Name ?? "Unknown" }); } if (_pluginConfig.DebugMode) { foreach (ulong memberId in teamMembers) { Puts($"UserID {memberId} authorized on Tool Cupboard by {player.displayName}."); } } } // Set whitelist for Auto Turret private void SetEntityWhitelist(BasePlayer player, AutoTurret turret) { List teamMembers = GetTeamMembers(player); foreach (ulong memberId in teamMembers) { turret.authorizedPlayers.Add(new ProtoBuf.PlayerNameID() { userid = memberId, username = covalence.Players.FindPlayerById(memberId.ToString())?.Name ?? "Unknown" }); } if (_pluginConfig.DebugMode) { foreach (ulong memberId in teamMembers) { Puts($"UserID {memberId} authorized on Auto Turret by {player.displayName}."); } } } #endregion #region UI Methods // Open the TeamShare UI [ChatCommand("teamui")] private void OpenTeamShareUI(BasePlayer player, string command, string[] args) { if (!isPermitted(player)) { SendChatMessage(player, Lang("NoPermission", player.UserIDString)); return; } CreateUI(player); } // Create the TeamShare UI private void CreateUI(BasePlayer player, string parentPanel = "Overlay") { DestroyUI(player); CuiElementContainer container = new CuiElementContainer(); string panelName = CuiHelper.GetGuid(); playerUIs[player.userID] = panelName; string anchorMin = !_pluginConfig.UsingWelcomeController ? "0.2 0.2" : "0 0"; string anchorMax = !_pluginConfig.UsingWelcomeController ? "0.8 0.8" : "1 1"; int fontSize = !_pluginConfig.UsingWelcomeController ? 16 : 20; int buttonFontSize = !_pluginConfig.UsingWelcomeController ? 14 : 18; string fieldBackgroundColor = !_pluginConfig.UsingWelcomeController ? "0 0 0 0.7" : uiSettings.UIBackgroundColor; // Main background panel CreatePanel(ref container, anchorMin, anchorMax, uiSettings.UIBackgroundColor, parentPanel, panelName, isMainPanel: true); // Top Panel string topPanel = CreatePanel(ref container, "0.1 0.5", "0.9 0.9", uiSettings.UIBackgroundColor, panelName, "TopPanel"); // Title Label CreateLabel(ref container, "0 0.85", "1 1", "1 1 1 1", "0 0 0 0", "Team Auth Options", fontSize, TextAnchor.MiddleCenter, topPanel); // Toggle options var toggles = new[] { new { Label = "Codelock Sharing", Command = "toggle_codelock", IsOn = GetPlayerData(player.userID).enabledCodelock }, new { Label = "Cupboard Sharing", Command = "toggle_cupboard", IsOn = GetPlayerData(player.userID).enabledCupboard }, new { Label = "Turret Sharing", Command = "toggle_turret", IsOn = GetPlayerData(player.userID).enabledTurret }, new { Label = "Enable Guest Code", Command = "toggle_guestcode", IsOn = GetPlayerData(player.userID).enabledGuestCode }, new { Label = "Quiet Mode", Command = "toggle_quiet", IsOn = GetPlayerData(player.userID).enabledQuiet } }; float startY = 0.7f; float spacing = 0.12f; foreach (var toggle in toggles) { float yMin = startY - toggles.ToList().IndexOf(toggle) * spacing; float yMax = yMin + 0.1f; string toggleName = toggle.Command.Substring(7); string toggleGroupName = $"ToggleGroup_{toggleName}"; string toggleButtonName = $"ToggleButton_{toggleName}"; string toggleGroup = CreatePanel(ref container, $"0.05 {yMin}", $"0.95 {yMax}", fieldBackgroundColor, topPanel, toggleGroupName); CreateLabel(ref container, $"0.05 0", $"0.6 1", "1 1 1 1", "0 0 0 0", toggle.Label, fontSize, TextAnchor.MiddleLeft, toggleGroup); string buttonColor = toggle.IsOn ? uiSettings.UIPrimaryColor : uiSettings.UIDangerButtonColor; string buttonText = toggle.IsOn ? "On" : "Off"; // Assign chat commands to buttons string buttonCommand = $"teamshare_toggle_{toggleName}"; CreateButton(ref container, $"0.75 0.1", $"0.95 0.9", buttonColor, "1 1 1 1", buttonText, buttonFontSize, buttonCommand, toggleGroup, labelAnchor: TextAnchor.MiddleCenter, buttonName: toggleButtonName); } // Left Panel: Set Your Code string leftPanel = CreatePanel(ref container, "0.1 0.1", "0.4 0.45", uiSettings.UIBackgroundColor, panelName, "LeftPanel"); CreateLabel(ref container, "0.05 0.75", "0.95 0.95", "1 1 1 1", "0 0 0 0", "Set Your Code (0000-9999):", fontSize, TextAnchor.MiddleCenter, leftPanel); CreatePanel(ref container, "0.1 0.5", "0.9 0.7", fieldBackgroundColor, leftPanel, "InputFieldBackground_MainCode"); // Input field for main code container.Add(new CuiElement { Parent = "InputFieldBackground_MainCode", Components = { new CuiInputFieldComponent { Command = "teamshare_set_temp_code", FontSize = fontSize, Align = TextAnchor.MiddleCenter, Text = playerTempCodes.ContainsKey(player.userID) ? playerTempCodes[player.userID] : GetPlayerCode(player.userID) ?? "", Color = "1 1 1 1", NeedsKeyboard = true }, new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } } }); // Set Code Button CreateButton(ref container, "0.1 0.15", "0.9 0.3", uiSettings.UIPrimaryColor, "1 1 1 1", "Set Code", buttonFontSize, "teamshare_confirm_code", leftPanel); // Right Panel: Set Guest Code string rightPanel = CreatePanel(ref container, "0.6 0.1", "0.9 0.45", uiSettings.UIBackgroundColor, panelName, "RightPanel"); CreateLabel(ref container, "0.05 0.75", "0.95 0.95", "1 1 1 1", "0 0 0 0", "Set Guest Code (0000-9999):", fontSize, TextAnchor.MiddleCenter, rightPanel); CreatePanel(ref container, "0.1 0.5", "0.9 0.7", fieldBackgroundColor, rightPanel, "InputFieldBackground_GuestCode"); // Input field for guest code container.Add(new CuiElement { Parent = "InputFieldBackground_GuestCode", Components = { new CuiInputFieldComponent { Command = "teamshare_set_temp_guest_code", FontSize = fontSize, Align = TextAnchor.MiddleCenter, Text = playerTempGuestCodes.ContainsKey(player.userID) ? playerTempGuestCodes[player.userID] : GetPlayerGuestCode(player.userID) ?? "", Color = "1 1 1 1", NeedsKeyboard = true }, new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" } } }); // Set Guest Code Button CreateButton(ref container, "0.1 0.15", "0.9 0.3", uiSettings.UIPrimaryColor, "1 1 1 1", "Set Guest Code", buttonFontSize, "teamshare_confirm_guest_code", rightPanel); // Close Button if not using WelcomeController if (!_pluginConfig.UsingWelcomeController) { CreateButton(ref container, "0.4 0.15", "0.6 0.21", uiSettings.UIPrimaryColor, "1 1 1 1", "Close", buttonFontSize, "teamshare_close_ui", panelName, TextAnchor.MiddleCenter); } CuiHelper.AddUi(player, container); if (_pluginConfig.DebugMode) { Puts($"UI created for player {player.displayName}."); } } // Destroy the TeamShare UI private void DestroyUI(BasePlayer player) { if (playerUIs.TryGetValue(player.userID, out string panelName)) { CuiHelper.DestroyUi(player, panelName); playerUIs.Remove(player.userID); playerTempCodes.Remove(player.userID); playerTempGuestCodes.Remove(player.userID); // Cancel any running timers if (codeChangedTimers.TryGetValue(player.userID, out var codeTimer)) { codeTimer.Destroy(); codeChangedTimers.Remove(player.userID); } if (guestCodeChangedTimers.TryGetValue(player.userID, out var guestCodeTimer)) { guestCodeTimer.Destroy(); guestCodeChangedTimers.Remove(player.userID); } // Close the entire WelcomeController UI if (WelcomeController != null) { WelcomeController.Call("DestroyUI", player); } if (_pluginConfig.DebugMode) { Puts($"UI destroyed for player {player.displayName}."); } } } // Update toggle button state in UI private void UpdateToggleButton(BasePlayer player, string toggleName, bool isOn) { var container = new CuiElementContainer(); string buttonColor = isOn ? uiSettings.UIPrimaryColor : uiSettings.UIDangerButtonColor; string buttonText = isOn ? "On" : "Off"; int buttonFontSize = !_pluginConfig.UsingWelcomeController ? 14 : 18; string toggleButtonName = $"ToggleButton_{toggleName}"; string toggleGroupName = $"ToggleGroup_{toggleName}"; // Recreate the button with the updated state string buttonCommand = $"teamshare_toggle_{toggleName}"; CreateButton(ref container, "0.75 0.1", "0.95 0.9", buttonColor, "1 1 1 1", buttonText, buttonFontSize, buttonCommand, toggleGroupName, labelAnchor: TextAnchor.MiddleCenter, buttonName: toggleButtonName); // Replace the old button with the updated one CuiHelper.DestroyUi(player, toggleButtonName); CuiHelper.AddUi(player, container); if (_pluginConfig.DebugMode) { Puts($"Toggle '{toggleName}' updated to {(isOn ? "On" : "Off")} for player {player.displayName}."); } } #endregion #region Command Handlers // Close the UI [ConsoleCommand("teamshare_close_ui")] private void CloseUI(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; DestroyUI(player); } // Toggle Codelock Sharing [ConsoleCommand("teamshare_toggle_codelock")] private void ToggleCodelock(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; PlayerData pData = GetPlayerData(player.userID); pData.enabledCodelock = !pData.enabledCodelock; SavePlayerData(); UpdateToggleButton(player, "codelock", pData.enabledCodelock); } // Toggle Cupboard Sharing [ConsoleCommand("teamshare_toggle_cupboard")] private void ToggleCupboard(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; PlayerData pData = GetPlayerData(player.userID); pData.enabledCupboard = !pData.enabledCupboard; SavePlayerData(); UpdateToggleButton(player, "cupboard", pData.enabledCupboard); } // Toggle Turret Sharing [ConsoleCommand("teamshare_toggle_turret")] private void ToggleTurret(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; PlayerData pData = GetPlayerData(player.userID); pData.enabledTurret = !pData.enabledTurret; SavePlayerData(); UpdateToggleButton(player, "turret", pData.enabledTurret); } // Toggle Quiet Mode [ConsoleCommand("teamshare_toggle_quiet")] private void ToggleQuiet(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; PlayerData pData = GetPlayerData(player.userID); pData.enabledQuiet = !pData.enabledQuiet; SavePlayerData(); UpdateToggleButton(player, "quiet", pData.enabledQuiet); } // Toggle Guest Code [ConsoleCommand("teamshare_toggle_guestcode")] private void ToggleGuestCode(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; PlayerData pData = GetPlayerData(player.userID); pData.enabledGuestCode = !pData.enabledGuestCode; SavePlayerData(); UpdateToggleButton(player, "guestcode", pData.enabledGuestCode); } // Set temporary main code from UI input [ConsoleCommand("teamshare_set_temp_code")] private void SetTempCode(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; string code = arg.GetString(0, ""); playerTempCodes[player.userID] = code; if (_pluginConfig.DebugMode) { Puts($"Temporary main code set to '{code}' for player {player.displayName}."); } } // Set temporary guest code from UI input [ConsoleCommand("teamshare_set_temp_guest_code")] private void SetTempGuestCode(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; string code = arg.GetString(0, ""); playerTempGuestCodes[player.userID] = code; if (_pluginConfig.DebugMode) { Puts($"Temporary guest code set to '{code}' for player {player.displayName}."); } } // Confirm and set main code [ConsoleCommand("teamshare_confirm_code")] private void ConfirmCode(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; if (!playerTempCodes.TryGetValue(player.userID, out string code)) { SendChatMessage(player, Lang("NoCodeSet", player.UserIDString)); return; } // Ensure the code is 4 digits code = code.PadLeft(4, '0'); if (!IsValidCode(code)) { SendChatMessage(player, Lang("InvalidCode", player.UserIDString)); ShowCodeChangedMessage(player, "LeftPanel", "CodeChangedLabel_MainCode", isGuestCode: false, isValid: false); return; } SetPlayerCode(player.userID, code); SendChatMessage(player, string.Format(Lang("CodeSet", player.UserIDString), code)); ShowCodeChangedMessage(player, "LeftPanel", "CodeChangedLabel_MainCode", isGuestCode: false, isValid: true); playerTempCodes.Remove(player.userID); } // Confirm and set guest code [ConsoleCommand("teamshare_confirm_guest_code")] private void ConfirmGuestCode(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null) return; if (!playerTempGuestCodes.TryGetValue(player.userID, out string code)) { SendChatMessage(player, "Please enter a guest code first."); return; } code = code.PadLeft(4, '0'); if (!IsValidCode(code)) { SendChatMessage(player, Lang("InvalidCode", player.UserIDString)); ShowCodeChangedMessage(player, "RightPanel", "CodeChangedLabel_GuestCode", isGuestCode: true, isValid: false); return; } SetPlayerGuestCode(player.userID, code); SendChatMessage(player, string.Format(Lang("GuestCodeSet", player.UserIDString), code)); ShowCodeChangedMessage(player, "RightPanel", "CodeChangedLabel_GuestCode", isGuestCode: true, isValid: true); playerTempGuestCodes.Remove(player.userID); } #endregion #region Helper Methods // Send a chat message to the player private void SendChatMessage(BasePlayer player, string message, params object[] args) { if (GetPlayerData(player.userID).enabledQuiet) { if (_pluginConfig.DebugMode) { Puts($"Quiet mode enabled. Message '{message}' not sent to player {player.displayName}."); } return; } string formattedMessage = args.Length > 0 ? string.Format(message, args) : message; player.ChatMessage(formattedMessage); } // Retrieve localized message private string Lang(string key, string userId = null) => lang.GetMessage(key, this, userId); // Create a UI panel private string CreatePanel(ref CuiElementContainer container, string anchorMin, string anchorMax, string panelColor, string parent = "Overlay", string panelName = null, bool isMainPanel = false) { CuiPanel panel = new CuiPanel { RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax }, Image = { Color = panelColor }, CursorEnabled = isMainPanel }; string name = container.Add(panel, parent, panelName); return name; } // Create a UI label private void CreateLabel(ref CuiElementContainer container, string anchorMin, string anchorMax, string textColor, string backgroundColor, string text, int fontSize, TextAnchor alignment, string parent = "Overlay") { string panel = CreatePanel(ref container, anchorMin, anchorMax, backgroundColor, parent); container.Add(new CuiLabel { Text = { Color = textColor, Text = text, Align = alignment, FontSize = fontSize, Font = "robotocondensed-bold.ttf" }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, panel); } // Create a UI button private void CreateButton(ref CuiElementContainer container, string anchorMin, string anchorMax, string buttonColor, string textColor, string buttonText, int fontSize, string buttonCommand, string parent = "Overlay", TextAnchor labelAnchor = TextAnchor.MiddleCenter, string buttonName = null) { container.Add(new CuiButton { Button = { Color = buttonColor, Command = $"{buttonCommand}", Close = "" }, RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax }, Text = { Align = labelAnchor, Color = textColor, FontSize = fontSize, Text = buttonText, Font = "robotocondensed-bold.ttf" } }, parent, buttonName); } // Display code change messages with fade effects private void ShowCodeChangedMessage(BasePlayer player, string parentPanel, string labelName, bool isGuestCode = false, bool isValid = true) { string messageText = isValid ? "Code Changed" : "Invalid Code"; int fontSize = 14; // Fixed font size float fadeDuration = 1.0f; // 1 second for fade-in and fade-out int steps = 10; // Steps for the fade effect float interval = fadeDuration / steps; float alphaIncrement = 1f / steps; float alphaDecrement = 1f / steps; // Fade out existing message float currentAlpha = 1f; Timer fadeOutTimer = null; fadeOutTimer = timer.Repeat(interval, steps, () => { currentAlpha -= alphaDecrement; if (currentAlpha < 0f) currentAlpha = 0f; CuiHelper.DestroyUi(player, labelName); var container = new CuiElementContainer(); container.Add(CreateLabel(player, parentPanel, labelName, $"1 1 1 {currentAlpha}", fontSize, "")); CuiHelper.AddUi(player, container); if (currentAlpha <= 0) { fadeOutTimer.Destroy(); // Fade in new message float newAlpha = 0f; Timer fadeInTimer = null; fadeInTimer = timer.Repeat(interval, steps, () => { newAlpha += alphaIncrement; if (newAlpha > 1f) newAlpha = 1f; CuiHelper.DestroyUi(player, labelName); var containerFadeIn = new CuiElementContainer(); containerFadeIn.Add(CreateLabel(player, parentPanel, labelName, $"1 1 1 {newAlpha}", fontSize, messageText)); CuiHelper.AddUi(player, containerFadeIn); if (newAlpha >= 1f) { fadeInTimer.Destroy(); // After displaying, fade out again after 7 seconds timer.Once(7f, () => { Timer fadeOutAfterTimer = null; currentAlpha = 1f; fadeOutAfterTimer = timer.Repeat(interval, steps, () => { currentAlpha -= alphaDecrement; if (currentAlpha < 0f) currentAlpha = 0f; CuiHelper.DestroyUi(player, labelName); var containerFadeOut = new CuiElementContainer(); containerFadeOut.Add(CreateLabel(player, parentPanel, labelName, $"1 1 1 {currentAlpha}", fontSize, messageText)); CuiHelper.AddUi(player, containerFadeOut); if (currentAlpha <= 0) { fadeOutAfterTimer.Destroy(); } }); }); } }); } }); if (_pluginConfig.DebugMode) { Puts($"Displayed '{messageText}' message for player {player.displayName}."); } } // Helper to create a label with dynamic alpha and message private CuiElement CreateLabel(BasePlayer player, string parentPanel, string labelName, string color, int fontSize, string messageText) { var labelElement = new CuiElement { Name = labelName, Parent = parentPanel, Components = { new CuiRectTransformComponent { AnchorMin = "0.1 0.01", AnchorMax = "0.9 0.13" }, new CuiTextComponent { Text = messageText, FontSize = fontSize, Align = TextAnchor.MiddleCenter, Color = color, Font = "robotocondensed-bold.ttf" } } }; return labelElement; } #endregion } }