using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Oxide.Plugins { [Info("SeedExtractor", "ChatGPT", "1.0.0")] [Description("Extract seeds and consume outputs from consumable items.")] class SeedExtractor : RustPlugin { const string PermUse = "SeedExtractor.Use"; private readonly Dictionary OpenWindows = new Dictionary(); #region Configuration Configuration config; class Configuration { [JsonProperty("Consume Input Item")] public bool ConsumeInput = true; [JsonProperty("Give Output To Inventory")] public bool GiveInventory = true; [JsonProperty("Drop Output If Inventory Full")] public bool DropOnGround = true; [JsonProperty("Close After Extraction")] public bool CloseAfterExtraction = true; [JsonProperty("Window Title")] public string WindowTitle = "Seed Extractor"; } protected override void LoadDefaultConfig() { config = new Configuration(); } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) throw new JsonException(); } catch { PrintWarning("Creating new config."); LoadDefaultConfig(); } SaveConfig(); } protected override void SaveConfig() { Config.WriteObject(config, true); } #endregion #region Language protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["NoPermission"] = "You don't have permission.", ["Nothing"] = "This item produces nothing.", ["Open"] = "Place one consumable into the window.", ["Invalid"] = "That item cannot be processed.", ["Busy"] = "Close your current loot window first." }, this); } string Msg(BasePlayer player, string key) { return lang.GetMessage(key, this, player.UserIDString); } #endregion #region Hooks void Init() { permission.RegisterPermission(PermUse, this); } void Unload() { foreach (var pair in OpenWindows) { var player = BasePlayer.FindByID(pair.Key); if (player != null) player.EndLooting(); if (pair.Value != null && !pair.Value.IsDestroyed) pair.Value.Kill(); } OpenWindows.Clear(); } void OnPlayerDisconnected(BasePlayer player, string reason) { DestroyWindow(player); } void OnPlayerLootEnd(PlayerLoot loot) { BasePlayer player = loot?.baseEntity; if (player == null) return; DestroyWindow(player); } #endregion #region Command [ChatCommand("seedextract")] void CmdSeed(BasePlayer player, string cmd, string[] args) { if (!permission.UserHasPermission(player.UserIDString, PermUse)) { player.ChatMessage(Msg(player, "NoPermission")); return; } if (player.inventory.loot.containers.Count > 0) { player.ChatMessage(Msg(player, "Busy")); return; } OpenExtractor(player); } #endregion #region Loot Window private void OpenExtractor(BasePlayer player) { DestroyWindow(player); LootableCorpse corpse = GameManager.server.CreateEntity( "assets/prefabs/misc/player_corpse/player_corpse.prefab", Vector3.zero) as LootableCorpse; corpse.enableSaving = false; corpse.syncPosition = false; corpse.limitNetworking = true; corpse.playerSteamID = 0; corpse.playerName = config.WindowTitle; corpse.Spawn(); corpse.CancelInvoke(corpse.RemoveCorpse); corpse.SetFlagLocal(BaseEntity.Flags.Locked, true); var rb = corpse.GetComponent(); if (rb != null) UnityEngine.Object.Destroy(rb); var buoyancy = corpse.GetComponent(); if (buoyancy != null) UnityEngine.Object.Destroy(buoyancy); corpse.containers = new ItemContainer[1]; ItemContainer container = new ItemContainer { entityOwner = corpse, allowedContents = ItemContainer.ContentsType.Generic, capacity = 1 }; container.ServerInitialize(null, 1); corpse.containers[0] = container; OpenWindows[player.userID] = corpse; NextTick(() => StartLoot(player, corpse)); player.ChatMessage(Msg(player, "Open")); } private void StartLoot(BasePlayer player, LootableCorpse corpse) { if (player == null || corpse == null || corpse.IsDestroyed) return; player.EndLooting(); player.inventory.loot.Clear(); player.inventory.loot.PositionChecks = false; player.inventory.loot.entitySource = corpse; foreach (ItemContainer c in corpse.containers) player.inventory.loot.AddContainer(c); player.inventory.loot.MarkDirty(); player.inventory.loot.SendImmediate(); player.ClientRPC(RpcTarget.Player("RPC_OpenLootPanel", player), "player_corpse"); } private void DestroyWindow(BasePlayer player) { if (player == null) return; LootableCorpse corpse; if (!OpenWindows.TryGetValue(player.userID, out corpse)) return; OpenWindows.Remove(player.userID); if (corpse == null) return; if (!corpse.IsDestroyed) { foreach (ItemContainer container in corpse.containers) { if (container == null) continue; foreach (Item item in container.itemList.ToList()) { item.Drop( player.transform.position + Vector3.up, Vector3.zero); } } corpse.Kill(); } } #endregion #region Extraction private object CanMoveItem(Item item, PlayerInventory inventory, ItemContainerId targetContainer, int slot, int amount) { BasePlayer player = inventory?.baseEntity; if (player == null) return null; LootableCorpse corpse; if (!OpenWindows.TryGetValue(player.userID, out corpse)) return null; if (inventory.loot.entitySource != corpse) return null; if (slot != 0 && slot != -1) return false; ExtractItem(player, item); return false; } private void ExtractItem(BasePlayer player, Item item) { if (item == null) return; ItemModConsume consume = item.info.GetComponent(); if (consume == null) { player.ChatMessage(Msg(player, "Nothing")); return; } if (consume.product == null || consume.product.Length == 0) { player.ChatMessage(Msg(player, "Nothing")); return; } foreach (ItemAmountRandom product in consume.product) { if (product == null) continue; int amount = product.RandomAmount(); if (amount <= 0) continue; Item output = ItemManager.Create( product.itemDef, amount, 0); if (output == null) continue; if (config.GiveInventory) GiveOrDrop(player, output); else output.Drop(player.transform.position + Vector3.up, Vector3.zero); } if (config.ConsumeInput) { item.UseItem(1); } if (config.CloseAfterExtraction) { NextTick(() => { if (player != null) player.EndLooting(); }); } } #endregion #region Protection Hooks // Prevent dropping items directly into the loot panel in odd ways private object OnItemAction(Item item, string action, BasePlayer player) { if (player == null) return null; if (!OpenWindows.ContainsKey(player.userID)) return null; if (action == "drop") return false; return null; } // Close the extractor if the player dies private void OnPlayerDeath(BasePlayer player, HitInfo info) { DestroyWindow(player); } // Close if sleeping private void OnPlayerSleep(BasePlayer player) { DestroyWindow(player); } #endregion #region Helpers private bool GiveOrDrop(BasePlayer player, Item item) { if (item == null) return false; if (player.inventory.GiveItem(item)) return true; if (config.DropOnGround) { item.Drop( player.transform.position + Vector3.up, Vector3.zero); return true; } item.Remove(); return false; } #endregion } }