using Oxide.Core; using Oxide.Core.Configuration; using Oxide.Game.Rust.Libraries; using System.Collections.Generic; using UnityEngine; using Newtonsoft.Json; namespace Oxide.Plugins { /// /// Blueprint Only Plugin /// Compatible with both Oxide and Carbon /// /// Features: /// - Blueprint learning notifications (Multi-language support) /// - Research table usage restrictions /// - Workbench tech tree unlock restrictions /// /// Permissions: /// - blueprintonly.notify.bypass: Bypass notifications /// - blueprintonly.research.use: Allow research table usage /// - blueprintonly.workbench.use: Allow workbench tech tree usage /// [Info("Blueprint Only", "MiaygawaYuu", "0.6.0")] [Description("Blueprint Only Plugin - Compatible with Oxide and Carbon")] public class BlueprintOnly : RustPlugin { #region Fields private readonly Dictionary> _translations = new Dictionary>(); private bool _translationsLoaded = false; #endregion protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["BP Notify"] = "{0} learned {1}.", ["Workbench Block Notify"] = "The tech tree cannot be opened at the workbench.", ["ResearchTable Block Notify"] = "Research tables are not available." }, this); lang.RegisterMessages(new Dictionary { ["BP Notify"] = "{0} さんが {1} をおぼえました。", ["Workbench Block Notify"] = "ワークベンチでのテックツリーのカイホウはできません。", ["ResearchTable Block Notify"] = "リサーチテーブルはつかえません。" }, this, "ja"); } private void Init() { // Register permissions permission.RegisterPermission("blueprintonly.notify.bypass", this); permission.RegisterPermission("blueprintonly.research.use", this); permission.RegisterPermission("blueprintonly.workbench.use", this); } private void OnServerInitialized() { LoadTranslations(); } #region Translation System /// /// Load translation data from Rust asset bundles /// private void LoadTranslations() { try { AssetBundleBackend assets = (AssetBundleBackend)FileSystem.Backend; Dictionary files = assets.files; foreach (var kvp in files) { string path = kvp.Key; AssetBundle bundle = kvp.Value; if (!path.EndsWith(".json") || !path.StartsWith("assets/localization/")) continue; TextAsset textAsset = bundle.LoadAsset(path) as TextAsset; if (textAsset == null) continue; int lastIndex = path.LastIndexOf('/'); int secondLastIndex = path.LastIndexOf('/', lastIndex - 1) + 1; string language = path.Substring(secondLastIndex, lastIndex - secondLastIndex); if (language.Equals("editor", System.StringComparison.OrdinalIgnoreCase)) continue; if (!_translations.ContainsKey(language)) { _translations[language] = new Dictionary(); } Dictionary tokens = JsonConvert.DeserializeObject>(textAsset.text); if (tokens != null) { foreach (var token in tokens) { _translations[language][token.Key] = token.Value; } } } _translationsLoaded = true; Puts($"Translation system initialized: {_translations.Count} languages loaded"); } catch (System.Exception ex) { PrintError($"Failed to load translations: {ex.Message}"); _translationsLoaded = false; } } /// /// Get player's language setting /// private string GetPlayerLanguage(BasePlayer player) { return player?.net?.connection?.info?.GetString("global.language", "en") ?? "en"; } /// /// Get translation for specified language and token /// private string GetTranslation(string language, string token) { if (!_translationsLoaded || string.IsNullOrEmpty(token)) return null; if (_translations.TryGetValue(language, out Dictionary tokens)) { if (tokens.TryGetValue(token, out string translation)) { return translation; } } return null; } /// /// Get translated item name /// private string GetItemTranslation(BasePlayer player, Item item) { if (item?.blueprintTargetDef?.displayName == null) return "Unknown Item"; string language = GetPlayerLanguage(player); string token = item.blueprintTargetDef.displayName.token; // Get translation in player's language string translation = GetTranslation(language, token); // Fallback to English name if translation not found return translation ?? item.blueprintTargetDef.displayName.english; } #endregion // Hook: Block research table usage object CanResearchItem(BasePlayer player, Item targetItem) { if (!permission.UserHasPermission(player.UserIDString, "blueprintonly.research.use")) { string _message = lang.GetMessage("ResearchTable Block Notify", this, player.UserIDString); PrintToChat(player,_message); return false; }else{ return null; } } // Hook: Block workbench tech tree unlocking object CanUnlockTechTreeNode(BasePlayer player, TechTreeData.NodeInstance node, TechTreeData techTree) { if (!permission.UserHasPermission(player.UserIDString, "blueprintonly.workbench.use")) { string _message = lang.GetMessage("Workbench Block Notify", this, player.UserIDString); PrintToChat(player,_message); return false; }else{ return null; } } // Hook: Blueprint learning notification object OnItemAction(Item item, string action, BasePlayer player) { if (action == "study") { if (!player.PersistantPlayerInfo.unlockedItems.Contains(item.blueprintTargetDef.itemid)) { if (!permission.UserHasPermission(player.UserIDString, "blueprintonly.notify.bypass")) { // Send translated message to each online player foreach (BasePlayer onlinePlayer in BasePlayer.activePlayerList) { string _message = lang.GetMessage("BP Notify", this, onlinePlayer.UserIDString); string itemName = GetItemTranslation(onlinePlayer, item); string message = string.Format(_message, player.displayName, itemName); PrintToChat(onlinePlayer, message); } } } } return null; } } }