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

FREE StudyBuddy : 1.1.1 1.1.1

Нет прав для скачивания
C#:
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("StudyBuddy", "Orangemart", "1.1.1")]
    [Description("A lightweight blueprint sharing plugin that allows online teammates to copy your homework and unlock blueprints")]
    class StudyBuddy : RustPlugin
    {
        #region Fields & Config

        private const string usePermission = "studybuddy.use";
        private Configuration config;

        private class Configuration
        {
            [JsonProperty("Share Tech Tree Blueprints")] public bool TechTreeSharingEnabled = true;
            [JsonProperty("Items Blocked from Sharing")] public HashSet<string> BlockedItems = new HashSet<string>();
        }

        #endregion

        #region Hooks

        private void Init()
        {
            permission.RegisterPermission(usePermission, this);
        }

        private void OnServerInitialized()
        {
            Puts($"StudyBuddy has been initialized and is ready! TechTreeSharingEnabled: {config?.TechTreeSharingEnabled}");
        }

        // Hook: Research Table
        private void OnItemAction(Item item, string action, BasePlayer player)
        {
            if (player == null || item == null || action != "study") return;
            TryShareBlueprint(item.blueprintTargetDef, player);
        }

        // Hook: Tech Tree (Pre-Unlock)
        private void OnTechTreeNodeUnlock(Workbench workbench, object nodeOrItemDef, BasePlayer player)
        {
            HandleTechTreeUnlock(workbench, nodeOrItemDef, player);
        }

        // Hook: Tech Tree (Post-Unlock)
        private void OnTechTreeNodeUnlocked(Workbench workbench, object nodeOrItemDef, BasePlayer player)
        {
            HandleTechTreeUnlock(workbench, nodeOrItemDef, player);
        }

        private void HandleTechTreeUnlock(Workbench workbench, object nodeOrItemDef, BasePlayer player)
        {
            if (nodeOrItemDef == null || player == null || !config.TechTreeSharingEnabled) return;

            ItemDefinition itemDef = nodeOrItemDef as ItemDefinition;
            if (itemDef == null)
            {
                // Reflection to extract ItemDefinition from the node
                var fields = nodeOrItemDef.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                foreach (var field in fields)
                {
                    if (field.FieldType == typeof(ItemDefinition))
                    {
                        itemDef = field.GetValue(nodeOrItemDef) as ItemDefinition;
                        if (itemDef != null)
                        {
                            break;
                        }
                    }
                }

                if (itemDef == null)
                {
                    var props = nodeOrItemDef.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    foreach (var prop in props)
                    {
                        if (prop.PropertyType == typeof(ItemDefinition))
                        {
                            itemDef = prop.GetValue(nodeOrItemDef) as ItemDefinition;
                            if (itemDef != null)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (itemDef == null)
            {
                return;
            }

            List<ItemDefinition> prerequisites = new List<ItemDefinition>();
            if (workbench != null)
            {
                var techTrees = GetTechTrees(workbench);
                foreach (var techTree in techTrees)
                {
                    object nodeInstance = FindNodeInstance(techTree, itemDef);
                    if (nodeInstance != null)
                    {
                        AddParentsFromNode(nodeInstance, techTree, new HashSet<object>(), prerequisites);
                        break;
                    }
                }
            }

            // Schedule sharing in the next frame so the server has finished adding the path unlocks to the player's profile
            NextFrame(() =>
            {
                if (player == null || !player.IsConnected) return;

                // Only share if the player successfully unlocked the target item (not failed/cancelled)
                if (player.PersistantPlayerInfo != null && player.PersistantPlayerInfo.unlockedItems.Contains(itemDef.itemid))
                {
                    TryShareBlueprint(itemDef, player, prerequisites);
                }
            });
        }

        private List<object> GetTechTrees(object workbench)
        {
            var list = new List<object>();
            if (workbench == null) return list;

            // Try to get "techTrees" array first
            var techTreesField = workbench.GetType().GetField("techTrees", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                                 ?? workbench.GetType().GetField("TechTrees", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if (techTreesField != null)
            {
                var val = techTreesField.GetValue(workbench);
                if (val is System.Collections.IEnumerable enumerable)
                {
                    foreach (var tree in enumerable)
                    {
                        if (tree != null)
                        {
                            list.Add(tree);
                        }
                    }
                }
            }

            // Fallback to single "techTree" field/property
            if (list.Count == 0)
            {
                var field = workbench.GetType().GetField("techTree", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                            ?? workbench.GetType().GetField("TechTree", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                if (field != null)
                {
                    var val = field.GetValue(workbench);
                    if (val != null) list.Add(val);
                }
                else
                {
                    var prop = workbench.GetType().GetProperty("techTree", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                               ?? workbench.GetType().GetProperty("TechTree", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                    if (prop != null)
                    {
                        var val = prop.GetValue(workbench);
                        if (val != null) list.Add(val);
                    }
                }
            }

            return list;
        }

        private object FindNodeInstance(object techTree, ItemDefinition itemDef)
        {
            if (techTree == null || itemDef == null) return null;

            var nodesField = techTree.GetType().GetField("nodes", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                             ?? techTree.GetType().GetField("Nodes", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            if (nodesField == null) return null;

            var nodesEnumerable = nodesField.GetValue(techTree) as System.Collections.IEnumerable;
            if (nodesEnumerable == null) return null;

            int count = 0;
            foreach (object node in nodesEnumerable)
            {
                if (node == null) continue;
                count++;
                var nodeItem = GetItemDefFromNode(node);
                if (nodeItem == itemDef)
                {
                    return node;
                }
            }

            return null;
        }

        private ItemDefinition GetItemDefFromNode(object node)
        {
            if (node == null) return null;
            ItemDefinition itemDef = node as ItemDefinition;
            if (itemDef != null) return itemDef;

            var fields = node.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            foreach (var field in fields)
            {
                if (field.FieldType == typeof(ItemDefinition))
                {
                    var val = field.GetValue(node) as ItemDefinition;
                    if (val != null) return val;
                }
            }

            var props = node.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            foreach (var prop in props)
            {
                if (prop.PropertyType == typeof(ItemDefinition))
                {
                    var val = prop.GetValue(node) as ItemDefinition;
                    if (val != null) return val;
                }
            }

            return null;
        }

        private int GetNodeId(object node)
        {
            if (node == null) return -1;
            var fields = node.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            foreach (var field in fields)
            {
                if ((field.Name.Equals("id", System.StringComparison.OrdinalIgnoreCase) || field.Name.Equals("nodeid", System.StringComparison.OrdinalIgnoreCase)) && field.FieldType == typeof(int))
                {
                    return (int)field.GetValue(node);
                }
            }

            var props = node.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            foreach (var prop in props)
            {
                if ((prop.Name.Equals("id", System.StringComparison.OrdinalIgnoreCase) || prop.Name.Equals("nodeid", System.StringComparison.OrdinalIgnoreCase)) && prop.PropertyType == typeof(int))
                {
                    return (int)prop.GetValue(node);
                }
            }

            return -1;
        }

        private void AddParentsFromNode(object node, object techTree, HashSet<object> visitedNodes, List<ItemDefinition> result)
        {
            if (node == null || techTree == null || !visitedNodes.Add(node)) return;

            var nodesField = techTree.GetType().GetField("nodes", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                             ?? techTree.GetType().GetField("Nodes", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            System.Collections.IEnumerable allNodes = null;
            if (nodesField != null)
            {
                allNodes = nodesField.GetValue(techTree) as System.Collections.IEnumerable;
            }

            var inputsField = node.GetType().GetField("inputs", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                              ?? node.GetType().GetField("Inputs", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            System.Collections.IEnumerable inputsEnumerable = null;
            if (inputsField != null)
            {
                inputsEnumerable = inputsField.GetValue(node) as System.Collections.IEnumerable;
            }
            else
            {
                var inputsProp = node.GetType().GetProperty("inputs", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                                 ?? node.GetType().GetProperty("Inputs", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                if (inputsProp != null)
                {
                    inputsEnumerable = inputsProp.GetValue(node) as System.Collections.IEnumerable;
                }
            }

            if (inputsEnumerable != null)
            {
                foreach (var item in inputsEnumerable)
                {
                    if (item is int parentId)
                    {
                        object parentNode = FindNodeById(allNodes, parentId);
                        if (parentNode != null)
                        {
                            ItemDefinition parentItem = GetItemDefFromNode(parentNode);
                            if (parentItem != null && !result.Contains(parentItem))
                            {
                                result.Add(parentItem);
                            }
                            AddParentsFromNode(parentNode, techTree, visitedNodes, result);
                        }
                    }
                }
            }
        }

        private object FindNodeById(System.Collections.IEnumerable allNodes, int id)
        {
            if (allNodes == null || id < 0) return null;
            foreach (var node in allNodes)
            {
                if (node == null) continue;
                if (GetNodeId(node) == id)
                {
                    return node;
                }
            }
            return null;
        }

        #endregion

        #region Core Logic

        private void TryShareBlueprint(ItemDefinition itemDef, BasePlayer sharer, List<ItemDefinition> prerequisites = null)
        {
            if (itemDef == null || sharer == null) return;

            if (!permission.UserHasPermission(sharer.UserIDString, usePermission))
            {
                return;
            }

            if (config.BlockedItems.Contains(itemDef.shortname))
            {
                return;
            }

            var targets = new HashSet<ulong>();

            // 1.Get Team Members
            var team = RelationshipManager.ServerInstance.FindPlayersTeam(sharer.userID);
            if (team != null)
            {
                foreach (ulong memberId in team.members)
                {
                    if (memberId != sharer.userID)
                    {
                        targets.Add(memberId);
                    }
                }
            }

            // 2.Get Clan Members (Official Clan System)
            if (sharer.clanId != 0 && ClanManager.ServerInstance != null)
            {
                IClan clan = null;
                if (ClanManager.ServerInstance.Backend?.TryGet(sharer.clanId, out clan) ?? false)
                {
                    if (clan != null && clan.Members != null)
                    {
                        foreach (ClanMember member in clan.Members)
                        {
                            if (member.SteamId != sharer.userID)
                            {
                                targets.Add(member.SteamId);
                            }
                        }
                    }
                }
            }

            if (targets.Count == 0) return;

            // 3.Identify Blueprint IDs (Main item + any sub-items + prerequisites)
            var blueprintsToShare = new List<int> { itemDef.itemid };
            if (itemDef.Blueprint?.additionalUnlocks != null)
            {
                foreach (var subItem in itemDef.Blueprint.additionalUnlocks)
                    blueprintsToShare.Add(subItem.itemid);
            }

            if (prerequisites != null)
            {
                foreach (var prereq in prerequisites)
                {
                    if (prereq != null && !blueprintsToShare.Contains(prereq.itemid))
                    {
                        // Only share prerequisite if the sharer actually has this blueprint unlocked
                        if (sharer.PersistantPlayerInfo != null && sharer.PersistantPlayerInfo.unlockedItems.Contains(prereq.itemid))
                        {
                            blueprintsToShare.Add(prereq.itemid);
                        }
                    }
                }
            }

            int sharedPlayersCount = 0;

            // 4.Loop through targets
            foreach (ulong targetId in targets)
            {
                BasePlayer onlinePlayer = RelationshipManager.FindByID(targetId);
                if (onlinePlayer != null && onlinePlayer.IsConnected)
                {
                    var learned = UnlockForOnlinePlayer(onlinePlayer, blueprintsToShare);
                    if (learned.Count > 0)
                    {
                        sharedPlayersCount++;
                        if (learned.Count == 1)
                        {
                            Message(onlinePlayer, $"<color=#ffff00>{sharer.displayName}</color> shared {learned[0].displayName.translated} with you.");
                        }
                        else
                        {
                            bool learnedMain = learned.Any(i => i.itemid == itemDef.itemid);
                            if (learnedMain)
                            {
                                int extraCount = learned.Count - 1;
                                string extraSuffix = extraCount == 1 ? "prerequisite blueprint" : "prerequisite blueprints";
                                Message(onlinePlayer, $"<color=#ffff00>{sharer.displayName}</color> shared {itemDef.displayName.translated} (and {extraCount} {extraSuffix}) with you.");
                            }
                            else
                            {
                                string suffix = learned.Count == 1 ? "prerequisite blueprint" : "prerequisite blueprints";
                                Message(onlinePlayer, $"<color=#ffff00>{sharer.displayName}</color> shared {learned.Count} {suffix} with you.");
                            }
                        }
                    }
                }
            }

            if (sharedPlayersCount > 0)
            {
                if (prerequisites != null && prerequisites.Count > 0)
                {
                    Message(sharer, $"Shared <color=#ffff00>{itemDef.displayName.translated}</color> (and prerequisites) with {sharedPlayersCount} online team mate(s).");
                }
                else
                {
                    Message(sharer, $"Shared <color=#ffff00>{itemDef.displayName.translated}</color> with {sharedPlayersCount} online team mate(s).");
                }
            }
        }

        private List<ItemDefinition> UnlockForOnlinePlayer(BasePlayer player, List<int> blueprintIds)
        {
            var learnedList = new List<ItemDefinition>();
            var playerInfo = player.PersistantPlayerInfo;
            if (playerInfo == null) return learnedList;

            foreach (int id in blueprintIds)
            {
                if (!playerInfo.unlockedItems.Contains(id))
                {
                    playerInfo.unlockedItems.Add(id);
                    
                    // Send RPC so the client UI updates immediately (green checkmark appears)
                    player.ClientRPC(RpcTarget.Player("UnlockedBlueprint", player), id);
                    
                    // Update stats just for fun
                    player.stats.Add("blueprint_studied", 1);
                    
                    var def = ItemManager.FindItemDefinition(id);
                    if (def != null)
                    {
                        learnedList.Add(def);
                    }
                }
            }

            if (learnedList.Count > 0)
            {
                player.SendNetworkUpdateImmediate();
            }

            return learnedList;
        }

        #endregion

        #region Helpers

        private void Message(BasePlayer player, string msg)
        {
            if (player == null) return;
            player.ChatMessage($"<color=#D85540>[StudyBuddy]</color> {msg}");
        }

        protected override void LoadDefaultConfig() => config = new Configuration();
        protected override void LoadConfig()
        {
            base.LoadConfig();
            try { config = Config.ReadObject<Configuration>(); }
            catch { LoadDefaultConfig(); }
        }
        protected override void SaveConfig() => Config.WriteObject(config, true);

        #endregion
    }
}

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