using Newtonsoft.Json.Linq; using Oxide.Core; using Oxide.Core.Plugins; using Oxide.Game.Rust; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using UnityEngine; namespace Oxide.Plugins { [Info("Blueprint Share", "c_creep", "1.2.4")] [Description("Allows players to share researched blueprints with their friends, clan or team")] class BlueprintShare : RustPlugin { #region Fields [PluginReference] private Plugin Clans, ClansReborn, Friends; private StoredData storedData; private bool clansEnabled = true, friendsEnabled = true, teamsEnabled = true, recycleEnabled = false; #endregion #region Localization protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["Prefix"] = "[Blueprint Share] ", ["ArgumentsError"] = "Error, incorrect arguments. Try /bs help.", ["Help"] = "Blueprint Share Help:\n\n/bs toggle - Toggles the sharing of blueprints.\n/bs share - Shares your blueprints with other player.", ["ToggleOn"] = "You have enabled sharing blueprints.", ["ToggleOff"] = "You have disabled sharing blueprints.", ["NoPermission"] = "You don't have permission to use this command!", ["CannotShare"] = "You cannot share blueprints with this player because they aren't a friend or in the same clan or team!", ["NoTarget"] = "You didn't specifiy a player to share with!", ["TargetEqualsPlayer"] = "You cannot share blueprints with your self!", ["PlayerNotFound"] = "Couldn't find a player with that name!", ["MultiplePlayersFound"] = "Found multiple players with a similar name: {0}", ["ShareSuccess"] = "You shared {0} blueprints with {1}.", ["ShareFailure"] = "You don't have any new blueprints to share with {0}", ["ShareReceieve"] = "{0} has shared {1} blueprints with you.", ["Recycle"] = "You have kept the blueprint because no one learnt the blueprint.", ["ShareError"] = "An error occured while attempting to share items with another player" }, this); } private string GetLangValue(string key, string id = null, params object[] args) { var msg = lang.GetMessage(key, this, id); return args.Length > 0 ? string.Format(msg, args) : msg; } #endregion #region Oxide Hooks private void Init() { LoadData(); LoadDefaultConfig(); permission.RegisterPermission("blueprintshare.toggle", this); permission.RegisterPermission("blueprintshare.share", this); } private void OnPlayerConnected(BasePlayer player) { var playerUID = player.UserIDString; if (!PlayerDataExists(playerUID)) { CreateNewPlayerData(playerUID); } } private void OnItemAction(Item item, string action, BasePlayer player) { if (player != null) { if (action == "study") { var itemShortName = item.blueprintTargetDef.shortname; if (CanShareBlueprint(itemShortName, player)) { item.Remove(); } } } } private void OnTechTreeNodeUnlocked(Workbench workbench, TechTreeData.NodeInstance node, BasePlayer player) { if (workbench != null) { if (node != null) { if (player != null) { var itemShortName = node.itemDef.shortname; CanShareBlueprint(itemShortName, player); } } } } #endregion #region General Methods private bool CanShareBlueprint(string itemShortName, BasePlayer player) { if (player == null) return false; if (string.IsNullOrEmpty(itemShortName)) return false; var playerUID = player.UserIDString; if (SharingEnabled(playerUID)) { if (InTeam(player.userID) || InClan(player.userID) || HasFriends(player.userID)) { if (SomeoneWillLearnBlueprint(player, itemShortName)) { TryInsertBlueprint(player, itemShortName); ShareWithPlayers(player, itemShortName); HandleAdditionalBlueprints(player, itemShortName); return true; } else { if (recycleEnabled) { player.ChatMessage(GetLangValue("Recycle", playerUID)); return true; } return false; } } else { return false; } } else { TryInsertBlueprint(player, itemShortName); return true; } } private void HandleAdditionalBlueprints(BasePlayer player, string itemShortName) { var additionalBlueprints = GetItemDefinition(itemShortName).Blueprint.additionalUnlocks; if (additionalBlueprints.Count > 0) { foreach (var blueprint in additionalBlueprints) { var additionalItemShortName = blueprint.shortname; if (!string.IsNullOrEmpty(additionalItemShortName)) { if (SomeoneWillLearnBlueprint(player, additionalItemShortName)) { ShareWithPlayers(player, additionalItemShortName); } } } } } private void ShareWithPlayers(BasePlayer sharer, string itemShortName) { if (sharer == null || string.IsNullOrEmpty(itemShortName)) return; var recipients = SelectSharePlayers(sharer); foreach (var recipient in recipients) { if (recipient != null) { if (UnlockBlueprint(recipient, itemShortName)) { TryInsertBlueprint(recipient, itemShortName); } } } } private void ShareWithPlayer(BasePlayer sharer, BasePlayer recipient) { var sharerUID = sharer.UserIDString; var recipientUID = recipient.UserIDString; if (SameTeam(sharer, recipient) || SameClan(sharerUID, recipientUID) || AreFriends(sharerUID, recipientUID)) { var itemShortNames = LoadBlueprints(sharerUID); if (itemShortNames != null) { if (itemShortNames.Count > 0) { var learnedBlueprints = 0; foreach (var itemShortName in itemShortNames) { if (recipient == null || string.IsNullOrEmpty(itemShortName)) return; if (UnlockBlueprint(recipient, itemShortName)) { TryInsertBlueprint(recipient, itemShortName); learnedBlueprints++; } } if (learnedBlueprints > 0) { sharer.ChatMessage(GetLangValue("Prefix", sharerUID) + GetLangValue("ShareSuccess", sharerUID, learnedBlueprints, recipient.displayName)); recipient.ChatMessage(GetLangValue("Prefix", recipientUID) + GetLangValue("ShareReceieve", recipientUID, sharer.displayName, learnedBlueprints)); } else { sharer.ChatMessage(GetLangValue("Prefix", sharerUID) + GetLangValue("ShareFailure", sharerUID, recipient.displayName)); } } else { sharer.ChatMessage(GetLangValue("Prefix", sharerUID) + GetLangValue("ShareFailure", sharerUID, recipient.displayName)); } } else { sharer.ChatMessage(GetLangValue("ShareError", sharerUID)); } } } private bool UnlockBlueprint(BasePlayer player, string itemShortName) { if (player == null) return false; if (string.IsNullOrEmpty(itemShortName)) return false; var blueprintComponent = player.blueprints; if (blueprintComponent == null) return false; var itemDefinition = GetItemDefinition(itemShortName); if (itemDefinition == null) return false; if (blueprintComponent.HasUnlocked(itemDefinition)) return false; var soundEffect = new Effect("assets/prefabs/deployable/research table/effects/research-success.prefab", player.transform.position, Vector3.zero); if (soundEffect == null) return false; EffectNetwork.Send(soundEffect, player.net.connection); blueprintComponent.Unlock(itemDefinition); return true; } private bool SomeoneWillLearnBlueprint(BasePlayer sharer, string itemShortName) { if (sharer == null || string.IsNullOrEmpty(itemShortName)) return false; var players = SelectSharePlayers(sharer); if (players.Count > 0) { var blueprintItem = GetItemDefinition(itemShortName); if (blueprintItem != null) { var counter = 0; foreach (var player in players) { if (player != null) { if (!player.blueprints.HasUnlocked(blueprintItem)) { counter++; } } } return counter > 0; } else { return false; } } else { return false; } } private List SelectSharePlayers(BasePlayer player) { var playersToShareWith = new List(); var playerUID = player.userID; if (clansEnabled && (Clans != null || ClansReborn != null) && InClan(playerUID)) { playersToShareWith.AddRange(GetClanMembers(playerUID)); } if (friendsEnabled && Friends != null && HasFriends(playerUID)) { playersToShareWith.AddRange(GetFriends(playerUID)); } if (teamsEnabled && InTeam(playerUID)) { playersToShareWith.AddRange(GetTeamMembers(playerUID)); } return playersToShareWith; } private List LoadBlueprints(string playerUID) { if (PlayerDataExists(playerUID)) { return storedData.players[playerUID].learntBlueprints; } else { CreateNewPlayerData(playerUID); if (storedData.players[playerUID].learntBlueprints == null) { return storedData.players[playerUID].learntBlueprints = new List(); } else { return storedData.players[playerUID].learntBlueprints; } } } private void TryInsertBlueprint(BasePlayer player, string itemShortName) { var playerUID = player.UserIDString; if (PlayerDataExists(playerUID)) { InsertBlueprint(playerUID, itemShortName); } else { CreateNewPlayerData(playerUID); InsertBlueprint(playerUID, itemShortName); } } private void InsertBlueprint(string playerUID, string itemShortName) { if (string.IsNullOrEmpty(playerUID) || string.IsNullOrEmpty(itemShortName)) return; if (!storedData.players[playerUID].learntBlueprints.Contains(itemShortName)) { storedData.players[playerUID].learntBlueprints.Add(itemShortName); SaveData(); } } #endregion #region Config protected override void LoadDefaultConfig() { Config["ClansEnabled"] = clansEnabled = GetConfigValue("ClansEnabled", true); Config["FriendsEnabled"] = friendsEnabled = GetConfigValue("FriendsEnabled", true); Config["TeamsEnabled"] = teamsEnabled = GetConfigValue("TeamsEnabled", true); Config["RecycleBlueprints"] = recycleEnabled = GetConfigValue("RecycleBlueprints", false); SaveConfig(); } private T GetConfigValue(string name, T defaultValue) { return Config[name] == null ? defaultValue : (T)Convert.ChangeType(Config[name], typeof(T)); } #endregion #region Friends Methods private bool HasFriends(ulong playerUID) { if (Friends == null) return false; var friendsList = Friends.Call("GetFriends", playerUID); return friendsList != null && friendsList.Length != 0; } private List GetFriends(ulong playerUID) { var friendsList = new List(); var friends = Friends.Call("GetFriends", playerUID); foreach (var friendUID in friends) { var friend = RustCore.FindPlayerById(friendUID); friendsList.Add(friend); } return friendsList; } private bool AreFriends(string sharerUID, string playerUID) => Friends == null ? false : Friends.Call("AreFriends", sharerUID, playerUID); #endregion #region Clan Methods private bool InClan(ulong playerUID) { if (ClansReborn == null && Clans == null) return false; var clanName = Clans?.Call("GetClanOf", playerUID); return clanName != null; } private List GetClanMembers(ulong playerUID) { var membersList = new List(); var clanName = Clans?.Call("GetClanOf", playerUID); if (!string.IsNullOrEmpty(clanName)) { var clan = Clans?.Call("GetClan", clanName); if (clan != null && clan is JObject) { var members = clan.GetValue("members"); if (members != null) { foreach (var member in members) { ulong clanMemberUID; if (!ulong.TryParse(member.ToString(), out clanMemberUID)) continue; BasePlayer clanMember = RustCore.FindPlayerById(clanMemberUID); membersList.Add(clanMember); } } } } return membersList; } private bool SameClan(string sharerUID, string playerUID) => ClansReborn == null && Clans == null ? false : (bool)Clans?.Call("IsClanMember", sharerUID, playerUID); #endregion #region Team Methods private bool InTeam(ulong playerUID) { var player = RustCore.FindPlayerById(playerUID); return player.currentTeam != 0; } private List GetTeamMembers(ulong playerUID) { var membersList = new List(); var player = RustCore.FindPlayerById(playerUID); var teamMembers = player.Team.members; foreach (var teamMemberUID in teamMembers) { var teamMember = RustCore.FindPlayerById(teamMemberUID); if (teamMember != null) { membersList.Add(teamMember); } } return membersList; } private bool SameTeam(BasePlayer sharer, BasePlayer player) => sharer.currentTeam == player.currentTeam; #endregion #region Utility Methods private BasePlayer FindPlayer(string playerName, BasePlayer player, string playerUID) { var targets = FindPlayers(playerName); if (targets.Count <= 0) { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("PlayerNotFound", playerUID)); return null; } if (targets.Count > 1) { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("MultiplePlayersFound", playerUID)); return null; } return targets.First(); } private List FindPlayers(string playerName) { if (string.IsNullOrEmpty(playerName)) return null; return BasePlayer.allPlayerList.Where(p => p && p.UserIDString == playerName || p.displayName.Contains(playerName, CompareOptions.OrdinalIgnoreCase)).ToList(); } private ItemDefinition GetItemDefinition(string itemShortName) { if (string.IsNullOrEmpty(itemShortName)) return null; var itemDefinition = ItemManager.FindItemDefinition(itemShortName.ToLower()); return itemDefinition; } #endregion #region Data private class StoredData { public Dictionary players = new Dictionary(); } private class PlayerData { public bool sharingEnabled; public List learntBlueprints; } private void CreateData() { storedData = new StoredData(); SaveData(); } private void LoadData() { if (Interface.Oxide.DataFileSystem.ExistsDatafile(Name)) { storedData = Interface.Oxide.DataFileSystem.ReadObject(Name); } else { CreateData(); } } private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, storedData); private bool PlayerDataExists(string playerUID) => storedData.players.ContainsKey(playerUID); private void CreateNewPlayerData(string playerUID) { storedData.players.Add(playerUID, new PlayerData { sharingEnabled = true, learntBlueprints = new List() }); SaveData(); } #endregion #region Chat Commands [ChatCommand("bs")] private void ToggleCommand(BasePlayer player, string command, string[] args) { var playerUID = player.UserIDString; if (args.Length < 1) { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("Help", playerUID)); return; } switch (args[0].ToLower()) { case "help": { player.ChatMessage(GetLangValue("Help", playerUID)); break; } case "toggle": { if (permission.UserHasPermission(playerUID, "blueprintshare.toggle")) { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue(SharingEnabled(playerUID) ? "ToggleOff" : "ToggleOn", playerUID)); if (storedData.players.ContainsKey(playerUID)) { storedData.players[playerUID].sharingEnabled = !storedData.players[playerUID].sharingEnabled; SaveData(); } } else { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("NoPermission", playerUID)); } break; } case "share": { if (permission.UserHasPermission(playerUID, "blueprintshare.share")) { if (args.Length == 2) { var target = FindPlayer(args[1], player, playerUID); if (target == null) return; if (target == player) { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("TargetEqualsPlayer", playerUID)); return; } ShareWithPlayer(player, target); } else { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("NoTarget", playerUID)); return; } } else { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("NoPermission", playerUID)); } break; } default: { player.ChatMessage(GetLangValue("Prefix", playerUID) + GetLangValue("ArgumentsError", playerUID)); break; } } } #endregion #region API private bool SharingEnabled(string playerUID) => storedData.players.ContainsKey(playerUID) ? storedData.players[playerUID].sharingEnabled : true; #endregion } }