using System; using System.Collections.Generic; using System.Linq; using System.Text; using Oxide.Core.Libraries.Covalence; using Oxide.Plugins.TreePlanterEx; using Oxide.Core.Plugins; using Newtonsoft.Json; using UnityEngine; namespace Oxide.Plugins { [Info("Tree Planter", "Bazz3l", "1.2.2")] [Description("Buy and plant trees in building authed areas using in-game currency.")] public class TreePlanter : CovalencePlugin { [PluginReference] Plugin ServerRewards, Economics, Clans; #region Fields private const string PERM_USE = "treeplanter.use"; private PluginConfig _config; #endregion #region Config protected override void LoadDefaultConfig() => _config = PluginConfig.DefaultConfig(); protected override void LoadConfig() { base.LoadConfig(); try { _config = Config.ReadObject(); if (_config == null) { throw new JsonException(); } if (_config.ToDictionary().Keys .SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) return; PrintWarning("Loaded updated config."); SaveConfig(); } catch { PrintWarning("Loaded default config."); LoadDefaultConfig(); } } protected override void SaveConfig() => Config.WriteObject(_config, true); private class PluginConfig { [JsonProperty("UseServerRewards (use server rewards as currency)")] public bool UseServerRewards; [JsonProperty("UseEconomics (use economics as currency)")] public bool UseEconomics; [JsonProperty("UseCurrency (use custom items as currency, by specifying the CurrencyItem)")] public bool UseCurrency; [JsonProperty("CurrencyItem (set an item id to use as currency, default is set to scrap)")] public int CurrencyItem; [JsonProperty("CurrencySkinID (set an skin id to use as currency, default is set to 0)")] public ulong CurrencySkinID; [JsonProperty("EnableOwner (enables owners to chop down trees)")] public bool EnableOwner; [JsonProperty("EnableClan (enables clan members to chop down trees)")] public bool EnableClan; [JsonProperty("BlockedAgriItems (specify which items should only be placed in a planter box)")] public Dictionary AgriBlocked; [JsonProperty("TreeConfigs (list of available trees to purchase)")] public List TreeConfigs; [JsonIgnore] private Dictionary _availableConfigs; [JsonIgnore] public Dictionary AvailableConfigs { get { if (_availableConfigs != null) return _availableConfigs; _availableConfigs = new Dictionary(StringComparer.InvariantCultureIgnoreCase); TreeConfigs.ForEach(x => _availableConfigs[x.Name] = x); return _availableConfigs; } } public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary ToDictionary() => JsonConvert.DeserializeObject>(ToJson()); public static PluginConfig DefaultConfig() { return new PluginConfig { UseServerRewards = false, UseEconomics = false, UseCurrency = true, EnableOwner = false, EnableClan = false, CurrencyItem = -932201673, AgriBlocked = new Dictionary { {"seed.black.berry", true}, {"seed.blue.berry", true}, {"seed.green.berry", true}, {"seed.yellow.berry", true}, {"seed.white.berry", true}, {"seed.red.berry", true}, {"seed.corn", true}, {"clone.corn", true}, {"seed.pumpkin", true}, {"clone.pumpkin", true}, {"seed.hemp", true}, {"clone.hemp", true} }, TreeConfigs = new List { new TreeConfig("oak a", "assets/bundled/prefabs/autospawn/resource/v3_temp_field/oak_e.prefab"), new TreeConfig("oak b", "assets/bundled/prefabs/autospawn/resource/v3_temp_field_large/oak_b.prefab"), new TreeConfig("oak c", "assets/bundled/prefabs/autospawn/resource/v3_temp_field_large/oak_c.prefab"), new TreeConfig("birch a", "assets/bundled/prefabs/autospawn/resource/v3_temp_forest/birch_large_temp.prefab"), new TreeConfig("birch b", "assets/bundled/prefabs/autospawn/resource/v3_temp_beachside/birch_small_temp.prefab"), new TreeConfig("birch c", "assets/bundled/prefabs/autospawn/resource/v2_temp_forest/birch_large_temp.prefab"), new TreeConfig("douglas a", "assets/bundled/prefabs/autospawn/resource/v3_temp_forest_pine/douglas_fir_a.prefab"), new TreeConfig("douglas b", "assets/bundled/prefabs/autospawn/resource/v3_temp_forest_pine/douglas_fir_b.prefab"), new TreeConfig("douglas c", "assets/bundled/prefabs/autospawn/resource/v3_temp_forest_pine/douglas_fir_c.prefab"), new TreeConfig("palm a", "assets/bundled/prefabs/autospawn/resource/v3_arid_beachside/palm_tree_small_a_entity.prefab"), new TreeConfig("palm b", "assets/bundled/prefabs/autospawn/resource/v3_arid_beachside/palm_tree_small_b_entity.prefab"), new TreeConfig("palm c", "assets/bundled/prefabs/autospawn/resource/v3_arid_beachside/palm_tree_small_c_entity.prefab"), new TreeConfig("swamp a", "assets/bundled/prefabs/autospawn/resource/swamp-trees/swamp_tree_a.prefab"), new TreeConfig("swamp b", "assets/bundled/prefabs/autospawn/resource/swamp-trees/swamp_tree_b.prefab"), new TreeConfig("swamp c", "assets/bundled/prefabs/autospawn/resource/swamp-trees/swamp_tree_c.prefab"), new TreeConfig("pine a", "assets/bundled/prefabs/autospawn/resource/v3_temp_forest_pine/pine_a.prefab"), new TreeConfig("pine b", "assets/bundled/prefabs/autospawn/resource/v3_temp_field/pine_b.prefab"), new TreeConfig("pine c", "assets/bundled/prefabs/autospawn/resource/v3_temp_forest_pine/pine_c.prefab"), } }; } public TreeConfig FindAvailableConfig(string name) { if (string.IsNullOrEmpty(name)) return null; TreeConfig treeItem; return AvailableConfigs.TryGetValue(name, out treeItem) ? treeItem : null; } } private class TreeConfig { [JsonProperty("Name", Order = 0)] public string Name; [JsonProperty("Cost", Order = 1)] public int Cost = 10; [JsonProperty("Amount", Order = 2)] public int Amount = 1; [JsonProperty("Prefab", Order = 3)] public string Prefab; public TreeConfig(string name, string prefab) { Name = name; Prefab = prefab; } public void GiveItem(BasePlayer player) { Item item = ItemManager.CreateByName("clone.hemp", Amount); item.name = Name; item.text = Name; item.info.stackable = 1; player.GiveItem(item); } public static void RefundItem(BasePlayer player, Item item, GrowableGenes growableGenes) { Item refund = ItemManager.CreateByName(item.info.shortname, 1); refund.instanceData = new ProtoBuf.Item.InstanceData { }; refund.instanceData.dataInt = GrowableGeneEncoding.EncodeGenesToInt(growableGenes); player.GiveItem(refund); } } #endregion #region Lang protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { {"Message.Prefix", "Tree Planter: "}, {"Message.Info", "Please select one of the following."}, {"Message.Item", "{0} | ${1}"}, {"Message.Authed", "You must have build privilege."}, {"Message.Balance", "You don't have enough for that."}, {"Message.Planter", "Must be planted in a planter."}, {"Message.Ground", "Must be planted in the ground."}, {"Message.Planted", "{0} was successfully planted."}, {"Message.Received", "You've purchased {0}x {1}."}, {"Error.NotFound", "No item found by that name."} }, this); } #endregion #region Oxide private void OnServerInitialized() => AddCovalenceCommand("tree", nameof(TreeCommand), PERM_USE); private object OnMeleeAttack(BasePlayer player, HitInfo info) { if (player == null || info.HitEntity == null) return null; TreeEntity entity = info.HitEntity as TreeEntity; if (entity == null || entity.OwnerID == 0) return null; if (!IsEntityOwner(entity.OwnerID, player.userID)) return true; return null; } private void OnEntityBuilt(Planner plan, GameObject gameObject) { BasePlayer player = plan.GetOwnerPlayer(); if (player == null || !HasPermission(player, PERM_USE)) return; GrowableEntity growableEntity = gameObject.GetComponent(); Item item = player.GetActiveItem(); if (item == null) return; NextFrame(() => { TreeConfig treeConfig = _config.FindAvailableConfig(item.name); if (treeConfig != null && growableEntity != null && growableEntity.GetParentEntity() is PlanterBox) TryPlantTree(player, item, growableEntity, treeConfig); else if (treeConfig != null && growableEntity != null) TryPlantTree(player, item, growableEntity, treeConfig); else if (growableEntity == null || !(growableEntity.GetParentEntity() is PlanterBox)) TryPlantSeed(player, item, growableEntity); }); } #endregion #region Core private void TryPlantTree(BasePlayer player, Item item, GrowableEntity entity, TreeConfig treeItem) { if (player == null || item == null) return; if (entity.GetParentEntity() is PlanterBox) { treeItem.GiveItem(player); KillSeed(entity); player.ChatMessage(Lang("Message.Ground", player.UserIDString)); return; } if (!player.IsBuildingAuthed()) { treeItem.GiveItem(player); KillSeed(entity); player.ChatMessage(Lang("Message.Authed", player.UserIDString)); return; } KillSeed(entity); PlantTree(player, entity, treeItem.Prefab); player.ChatMessage(Lang("Message.Planted", player.UserIDString, item.name.TitleCase())); } private void TryPlantSeed(BasePlayer player, Item item, GrowableEntity entity) { if (player == null || item == null || IsBlockedAgri(item.info.shortname)) return; TreeConfig.RefundItem(player, item, entity.Genes); KillSeed(entity); player.ChatMessage(Lang("Message.Planter", player.UserIDString)); } private void PlantTree(BasePlayer player, GrowableEntity plant, string prefabName) { BaseEntity entity = GameManager.server.CreateEntity(prefabName, plant.ServerPosition, Quaternion.identity); if (entity == null) return; entity.OwnerID = player.userID; entity.Spawn(); } private void ListTrees(BasePlayer player) { StringBuilder stringBuilder = new StringBuilder(Lang("Message.Prefix", player.UserIDString)); stringBuilder.Append(Lang("Message.Info", player.UserIDString)); stringBuilder.AppendLine(); _config.TreeConfigs.ForEach(x => { stringBuilder.Append(Lang("Message.Item", player.UserIDString, x.Name, x.Cost)); stringBuilder.AppendLine(); }); player.ChatMessage(stringBuilder.ToString()); } private void KillSeed(GrowableEntity plant) { if (!IsValid(plant)) return; plant.Kill(); } private bool TakeCurrency(BasePlayer player, int treeCost) { if (treeCost == 0) return true; if (_config.UseServerRewards && ServerRewards != null) return ServerRewards.Call("TakePoints", player.userID, treeCost) != null; if (_config.UseEconomics && Economics != null) return Economics.Call("Withdraw", player.userID, (double) treeCost); if (_config.UseCurrency && HasAmount(player, treeCost)) { TakeAmount(player, treeCost); return true; } return false; } #endregion #region Command private void TreeCommand(IPlayer player, string command, string[] args) { if (args.Length == 0) { ListTrees(player.ToBasePlayer()); return; } TreeConfig treeConfig = _config.FindAvailableConfig(string.Join(" ", args)); if (treeConfig == null) { player.Message(Lang("Error.NotFound", player.Id)); return; } if (!TakeCurrency(player.ToBasePlayer(), treeConfig.Cost)) { player.Message(Lang("Message.Balance", player.Id)); return; } treeConfig.GiveItem(player.ToBasePlayer()); player.Message(Lang("Message.Received", player.Id, treeConfig.Amount, treeConfig.Name)); } #endregion #region Helpers private bool HasPermission(BasePlayer player, string permName) => permission.UserHasPermission(player.UserIDString, permName); private bool IsValid(BaseEntity entity) => entity.IsValid() && !entity.IsDestroyed; private bool SameClan(ulong userID, ulong targetID) { string playerClan = Clans?.Call("GetClanOf", userID); if (string.IsNullOrEmpty(playerClan)) return false; string targetClan = Clans?.Call("GetClanOf", targetID); if (string.IsNullOrEmpty(targetClan)) return false; return playerClan == targetClan; } private bool IsBlockedAgri(string shortname) => !(_config.AgriBlocked.ContainsKey(shortname) && _config.AgriBlocked[shortname]); private bool IsEntityOwner(ulong userID, ulong ownerID) { if (_config.EnableOwner && userID == ownerID) return true; if (_config.EnableClan && !SameClan(userID, ownerID)) return false; return true; } private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args); #endregion #region Inventory Methods | Needed To Check For Skinned Scrap bool HasAmount(BasePlayer player, int amount) => GetAmount(player.inventory, _config.CurrencyItem, _config.CurrencySkinID) >= amount; void TakeAmount(BasePlayer player, int amount) => TakeAmount(player.inventory, _config.CurrencyItem, _config.CurrencySkinID, amount); int GetAmount(PlayerInventory inventory, int itemid, ulong skinID = 0UL) { if (itemid == 0) return 0; int num = 0; if (inventory.containerMain != null) num += GetAmount(inventory.containerMain, itemid, skinID, true); if (inventory.containerBelt != null) num += GetAmount(inventory.containerBelt, itemid, skinID, true); return num; } int GetAmount(ItemContainer container, int itemid, ulong skinID = 0UL, bool usable = false) { int num = 0; foreach (Item obj in container.itemList) { if (obj.info.itemid == itemid && obj.skin == skinID && (!usable || !obj.IsBusy())) num += obj.amount; } return num; } int TakeAmount(PlayerInventory inventory, int itemid, ulong skinID, int amount) { int num1 = 0; if (inventory.containerMain != null) { int num2 = TakeAmount(inventory.containerMain, itemid, amount, skinID); num1 += num2; amount -= num2; } if (amount <= 0) return num1; if (inventory.containerBelt != null) { int num2 = TakeAmount(inventory.containerBelt, itemid, amount, skinID); num1 += num2; } return num1; } int TakeAmount(ItemContainer container, int itemid, int amount, ulong skinID) { int num1 = 0; if (amount == 0) return num1; List list = Facepunch.Pool.GetList(); foreach (Item obj in container.itemList) { if (obj.info.itemid != itemid || obj.skin != skinID) continue; int num2 = amount - num1; if (num2 <= 0) continue; if (obj.amount > num2) { obj.MarkDirty(); obj.amount -= num2; num1 += num2; Item byItemId = ItemManager.CreateByItemID(itemid); byItemId.amount = num2; byItemId.CollectedForCrafting(container.playerOwner); break; } if (obj.amount <= num2) { num1 += obj.amount; list.Add(obj); } if (num1 == amount) break; } list.ForEach(obj => { if (obj == null) return; obj.RemoveFromContainer(); obj.Remove(); }); ItemManager.DoRemoves(); Facepunch.Pool.FreeList(ref list); return num1; } #endregion } namespace TreePlanterEx { public static class PlayerEx { public static BasePlayer ToBasePlayer(this IPlayer player) => player?.Object as BasePlayer; } } }